2 * VDETELWEB: VDE telnet and WEB interface
4 * telnet.c: telnet module
6 * Copyright 2005,2007 Renzo Davoli University of Bologna - Italy
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, version 2 of the License.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
33 #include <sys/types.h>
34 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
39 #include <arpa/telnet.h>
42 #include "vdetelweb.h"
45 #define TELNET_TCP_PORT 23
46 #define TELNET_LOGIN 0x0
47 #define TELNET_COMMAND 0x1
48 #define TELNET_PASSWD 0x80
49 #define HISTORYSIZE 32
51 static char **commandlist
;
56 unsigned char telnetprotocol
;
57 unsigned char edited
; /* the linebuf has been modified (left/right arrow)*/
58 unsigned char vindata
; /* 1 when in_data... (0000 end with .)*/
59 char lastchar
; /* for double tag*/
60 char linebuf
[BUFSIZE
]; /*line buffer from the user*/
61 int bufindex
; /*current editing position on the buf */
62 char vlinebuf
[BUFSIZE
+1]; /*line buffer from vde*/
63 int vbufindex
; /*current editing position on the buf */
64 char *history
[HISTORYSIZE
]; /*history of the previous commands*/
65 int histindex
; /* index on the history (changed with up/down arrows) */
66 int lwipfd
; /* fd to the network */
67 int vdemgmtfd
; /* mgmt fd to vde_switch */
70 void telnet_close(int fn
,int fd
)
72 struct telnetstat
*st
=status
[fn
];
74 for (i
=0;i
<HISTORYSIZE
;i
++)
77 delpfd(pfdsearch(st
->lwipfd
));
78 lwip_close(st
->lwipfd
);
79 if (st
->vdemgmtfd
>= 0) {
80 delpfd(pfdsearch(st
->vdemgmtfd
));
89 #define MAX_KEYWORDS 128
91 static int commonprefix(char *x
, char *y
,int maxlen
)
94 while (*(x
++)==*(y
++) && len
<maxlen
)
99 static void showexpand(char *linebuf
,int bufindex
, int fd
)
103 FILE *ms
=open_memstream(&buf
,&bufsize
);
106 if (commandlist
&& bufindex
>0) {
107 char **s
=commandlist
;
110 if (strncmp(linebuf
,*s
,bufindex
)==0) {
112 fprintf(ms
,"%s ",*s
);
120 lwip_write(fd
,buf
,strlen(buf
));
125 static int tabexpand(char *linebuf
,int bufindex
,int maxlength
)
127 if (commandlist
&& bufindex
>0) {
128 char **s
=commandlist
;
132 if (strncmp(linebuf
,*s
,bufindex
)==0) {
138 len
=commonprefix(match
,*s
,len
);
143 int alreadymatch
=commonprefix(linebuf
,match
,len
);
144 //fprintf(stderr,"TAB %s %d -> %s %d already %d\n",linebuf,bufindex,match,len,alreadymatch);
145 if ((len
-alreadymatch
)+strlen(linebuf
) < maxlength
) {
146 memmove(linebuf
+len
,linebuf
+alreadymatch
,
147 strlen(linebuf
+alreadymatch
)+1);
148 memcpy(linebuf
+alreadymatch
,match
+alreadymatch
,len
-alreadymatch
);
149 if (nmatches
== 1 && linebuf
[len
] != ' ' && strlen(linebuf
)+1 < maxlength
) {
150 memmove(linebuf
+len
+1,linebuf
+len
,
151 strlen(linebuf
+len
)+1);
162 static int qstrcmp(const void *a
,const void *b
)
164 return strcmp(*(char * const *)a
,*(char * const *)b
);
166 static void create_commandlist()
168 int vdefd
=openextravdem();
170 char linebuf
[BUFSIZE
];
171 char *localclist
[MAX_KEYWORDS
];
175 int status
=CC_HEADER
;
176 FILE *in
=fdopen(vdefd
,"r");
177 write(vdefd
,"help\n",5);
178 while (status
!= CC_TERM
&& fgets(linebuf
,BUFSIZE
,in
) != NULL
) {
179 if (status
== CC_HEADER
) {
180 if (strncmp(linebuf
,"------------",12) == 0)
183 if (strncmp(linebuf
,".\n",2) == 0)
187 while (*s
!=' ' && *s
!= 0)
189 *s
=0; /* take the first token */
190 //fprintf(stderr,"%s\n",linebuf);
191 localclist
[nkeywords
]=strdup(linebuf
);
192 if (nkeywords
<MAX_KEYWORDS
) nkeywords
++;
193 char *thiskeyword
=strdup(linebuf
);
197 qsort(localclist
,nkeywords
,sizeof(char *),qstrcmp
);
198 for (i
=j
=0; i
<nkeywords
-1; i
++)
199 if (strncmp(localclist
[i
],localclist
[i
+1],strlen(localclist
[i
]))==0 &&
200 localclist
[i
+1][strlen(localclist
[i
])] == '/') {
201 free(localclist
[i
]); /*avoid menu*/
203 localclist
[j
]=localclist
[i
];
210 commandlist
=malloc(nkeywords
*sizeof(char *));
212 for (i
=0;i
<nkeywords
;i
++)
213 commandlist
[i
]=localclist
[i
];
218 static void erase_line(struct telnetstat
*st
,int prompt_too
)
221 int size
=st
->bufindex
+(prompt_too
!= 0)*strlen(prompt
);
224 FILE *ms
=open_memstream(&buf
,&bufsize
);
228 size
=strlen(st
->linebuf
)+(prompt_too
!= 0)*strlen(prompt
);
235 lwip_write(st
->lwipfd
,buf
,bufsize
);
240 static void redraw_line(struct telnetstat
*st
,int prompt_too
)
243 int tail
=strlen(st
->linebuf
)-st
->bufindex
;
246 FILE *ms
=open_memstream(&buf
,&bufsize
);
249 fprintf(ms
,"%s%s",prompt
,st
->linebuf
);
251 fprintf(ms
,"%s",st
->linebuf
);
256 lwip_write(st
->lwipfd
,buf
,bufsize
);
261 void telnet_getanswer(struct telnetstat
*st
)
265 n
=read(st
->vdemgmtfd
,buf
,BUFSIZE
);
270 st
->vlinebuf
[(st
->vbufindex
)++]=buf
[ib
];
271 if (buf
[ib
] == '\n') {
272 st
->vlinebuf
[(st
->vbufindex
)-1]='\r';
273 st
->vlinebuf
[(st
->vbufindex
)]='\n';
274 st
->vlinebuf
[(st
->vbufindex
)+1]='\0';
277 if (st
->vlinebuf
[0]=='.' && st
->vlinebuf
[1]=='\r')
280 lwip_write(st
->lwipfd
,st
->vlinebuf
,(st
->vbufindex
));
282 char *message
=st
->vlinebuf
;
283 //fprintf(stderr,"MSG1 \"%s\"\n",message);
284 while (*message
!= '\0' &&
285 !(isdigit(message
[0]) &&
286 isdigit(message
[1]) &&
287 isdigit(message
[2]) &&
288 isdigit(message
[3])))
290 //fprintf(stderr,"MSG2 \"%s\"\n",message);
291 if (strncmp(message
,"0000",4)==0)
293 else if(message
[0]=='1' &&
294 isdigit(message
[1]) &&
295 isdigit(message
[2]) &&
296 isdigit(message
[3])) {
298 lwip_write(st
->lwipfd
,message
,strlen(message
));
299 } else if (message
[0]=='3' &&
300 isdigit(message
[1]) &&
301 isdigit(message
[2]) &&
302 isdigit(message
[3])) {
304 lwip_write(st
->lwipfd
,"** DBG MSG: ",12);
305 lwip_write(st
->lwipfd
,(message
),strlen(message
));
311 n
=read(st
->vdemgmtfd
,buf
,BUFSIZE
);
315 int vdedata(int fn
,int fd
,int vdefd
)
317 struct telnetstat
*st
=status
[fn
];
320 telnet_getanswer(st
);
324 void telnet_core(int fn
,int fd
,int vdefd
)
326 struct telnetstat
*st
=status
[fn
];
328 switch (st
->status
) {
330 while (st
->linebuf
[st
->bufindex
-1] == '\n')
331 st
->linebuf
[--st
->bufindex
]=0;
332 if (strcmp(st
->linebuf
,"admin") != 0) {
333 lwip_write(fd
,"login incorrect\r\n\r\nLogin: ",26);
335 lwip_write(fd
,"Password: ",11);
336 st
->status
=TELNET_PASSWD
;
340 case TELNET_PASSWD
+1:
341 case TELNET_PASSWD
+2:
342 while (st
->linebuf
[st
->bufindex
-1] == '\n')
343 st
->linebuf
[--st
->bufindex
]=0;
344 if (strcmp(st
->linebuf
,passwd
) != 0) {
346 if (st
->status
< TELNET_PASSWD
+ 3)
347 lwip_write(fd
,"\r\nlogin incorrect\r\n\r\nPassword: ",30);
353 st
->vdemgmtfd
=openextravdem();
354 flags
= fcntl(st
->vdemgmtfd
, F_GETFL
);
356 fcntl(st
->vdemgmtfd
, F_SETFL
, flags
);
357 if (st
->vdemgmtfd
>= 0) {
358 newfn
=addpfd(st
->vdemgmtfd
,vdedata
);
362 st
->status
=TELNET_COMMAND
;
363 lwip_write(fd
,"\r\n",2);
364 lwip_write(fd
,prompt
,strlen(prompt
));
369 char *cmd
=st
->linebuf
;
370 while (*cmd
== ' ' || *cmd
== '\t')
372 if (strncmp(cmd
,"logout",6)==0)
376 write(st
->vdemgmtfd
,st
->linebuf
,st
->bufindex
);
377 if (strncmp(cmd
,"shutdown",8)==0) {
381 telnet_getanswer(fd,st->vdemgmtfd);*/
383 lwip_write(fd
,"\r\n",2);
384 lwip_write(fd
,prompt
,strlen(prompt
));
391 static void telnet_option_send3(int fd
,int action
,int object
)
397 lwip_write(fd
,opt
,3);
400 static int telnet_options(int fn
,int fd
,unsigned char *s
)
402 struct telnetstat
*st
=status
[fn
];
403 register int action_n_object
;
404 if (st
->telnetprotocol
== 0) {
405 st
->telnetprotocol
=1;
406 telnet_option_send3(fd
,WILL
,TELOPT_ECHO
);
410 action_n_object
=((*s
)<<8) + (*(s
+1));
411 switch (action_n_object
) {
412 case (DO
<<8) + TELOPT_ECHO
:
413 //printf("okay echo\n");
416 case (WILL
<<8) + TELOPT_ECHO
:
417 telnet_option_send3(fd
,DONT
,TELOPT_ECHO
);
418 telnet_option_send3(fd
,WILL
,TELOPT_ECHO
);
420 case (DO
<<8) + TELOPT_SGA
:
421 //printf("do sga -> okay will sga\n");
422 telnet_option_send3(fd
,WILL
,TELOPT_SGA
);
424 case (WILL
<<8) + TELOPT_TTYPE
:
425 //printf("will tty -> dont tty\n");
426 telnet_option_send3(fd
,DONT
,TELOPT_TTYPE
);
429 //printf("not managed yet %x %x\n",*s,*(s+1));
431 telnet_option_send3(fd
,DONT
,*(s
+1));
433 telnet_option_send3(fd
,WONT
,*(s
+1));
439 static void erase_line(int fd, struct telnetstat *st)
442 for (j=0;j<st->bufindex;j++)
443 lwip_write(fd,"\033[D",3);
444 for (j=0;j<strlen(st->linebuf);j++)
445 lwip_write(fd,"\033[P",3);
449 static void put_history(struct telnetstat
*st
)
451 if(st
->history
[st
->histindex
])
452 free(st
->history
[st
->histindex
]);
453 st
->history
[st
->histindex
]=strdup(st
->linebuf
);
456 static void get_history(int change
,struct telnetstat
*st
)
458 st
->histindex
+= change
;
459 if(st
->histindex
< 0) st
->histindex
=0;
460 if(st
->histindex
>= HISTORYSIZE
) st
->histindex
=HISTORYSIZE
-1;
461 if(st
->history
[st
->histindex
] == NULL
) (st
->histindex
)--;
462 strcpy(st
->linebuf
,st
->history
[st
->histindex
]);
463 st
->bufindex
=strlen(st
->linebuf
);
466 static void shift_history(struct telnetstat
*st
)
468 if (st
->history
[HISTORYSIZE
-1] != NULL
)
469 free(st
->history
[HISTORYSIZE
-1]);
470 memmove(st
->history
+1,st
->history
,(HISTORYSIZE
-1)*sizeof(char *));
474 int telnetdata(int fn
,int fd
,int vdefd
)
476 unsigned char buf
[BUFSIZE
];
478 struct telnetstat
*st
=status
[fn
];
479 n
=lwip_read(fd
,buf
,BUFSIZE
);
480 //printf("N%d %x %x %x %x\n",n,buf[0],buf[1],buf[2],buf[3]);
484 printlog(LOG_ERR
,"telnet read err: %s",strerror(errno
));
486 for (i
=0;i
<n
&& strlen(st
->linebuf
)<BUFSIZE
;i
++) {
487 if (buf
[i
] == 0xff && buf
[i
+1] == 0xff)
489 if(buf
[i
]==0) buf
[i
]='\n'; /*telnet encode \n as a 0 when in raw mode*/
490 if (buf
[i
] == 0xff && buf
[i
+1] != 0xff) {
491 i
+=telnet_options(fn
,fd
,buf
+i
);
492 } else if(buf
[i
] == 0x1b) {
494 if (buf
[i
+1]=='[' && st
->status
== TELNET_COMMAND
) {
497 case 'A': //fprintf(stderr,"UP\n");
502 //lwip_write(fd,st->linebuf,st->bufindex);
503 st
->bufindex
=strlen(st
->linebuf
);
505 case 'B': //fprintf(stderr,"DOWN\n");
510 //lwip_write(fd,st->linebuf,st->bufindex);
512 case 'C': //fprintf(stderr,"RIGHT\n");
513 if (st
->linebuf
[st
->bufindex
] != '\0') {
514 lwip_write(fd
,"\033[C",3);
518 case 'D': //fprintf(stderr,"LEFT\n");
519 if (st
->bufindex
> 0) {
520 lwip_write(fd
,"\033[D",3);
529 } else if(buf
[i
] < 0x20 && !(buf
[i
] == '\n' || buf
[i
] == '\r')) {
531 if (buf
[i
] == 4) /*ctrl D is a shortcut for UNIX people! */ {
536 case 3: /*ctrl C cleans the current buffer */
539 st
->linebuf
[(st
->bufindex
)]=0;
541 case 12: /* ctrl L redraw */
545 case 1: /* ctrl A begin of line */
550 case 5: /* ctrl E endofline */
552 st
->bufindex
=strlen(st
->linebuf
);
555 if (st
->lastchar
== '\t') {
557 showexpand(st
->linebuf
,st
->bufindex
,fd
);
561 st
->bufindex
=tabexpand(st
->linebuf
,st
->bufindex
,BUFSIZE
);
566 } else if(buf
[i
] == 0x7f) {
567 if(st
->bufindex
> 0) {
570 x
=st
->linebuf
+st
->bufindex
;
571 memmove(x
,x
+1,strlen(x
));
572 if (st
->echo
&& st
->status
<TELNET_PASSWD
) {
574 lwip_write(fd
,"\010\033[P",4);
576 lwip_write(fd
,"\010 \010",3);
580 if (st
->echo
&& st
->status
<TELNET_PASSWD
) {
581 if (st
->edited
&& buf
[i
] >= ' ')
582 lwip_write(fd
,"\033[@",3);
583 lwip_write(fd
,&(buf
[i
]),1);
585 if (buf
[i
] != '\r') {
587 if (st
->status
== TELNET_COMMAND
) {
590 if (strlen(st
->linebuf
) > 0)
593 st
->bufindex
=strlen(st
->linebuf
);
594 telnet_core(fn
,fd
,vdefd
);
595 st
->bufindex
=st
->edited
=st
->histindex
=0;
596 st
->linebuf
[(st
->bufindex
)]=0;
599 x
=st
->linebuf
+st
->bufindex
;
600 memmove(x
+1,x
,strlen(x
)+1);
601 st
->linebuf
[(st
->bufindex
)++]=buf
[i
];
610 int telnetaccept(int fn
,int fd
,int vdefd
)
612 struct sockaddr_in cli_addr
;
615 struct telnetstat
*st
;
619 clilen
= sizeof(cli_addr
);
620 newsockfd
= lwip_accept(fd
, (struct sockaddr
*) &cli_addr
, &clilen
);
623 printlog(LOG_ERR
,"telnet accept err: %s",strerror(errno
));
626 newfn
=addpfd(newsockfd
,telnetdata
);
627 status
[newfn
]=st
=malloc(sizeof(struct telnetstat
));
628 st
->status
=TELNET_LOGIN
;
630 st
->telnetprotocol
=0;
631 st
->bufindex
=st
->edited
=st
->histindex
=st
->vbufindex
=st
->vindata
=st
->lastchar
=0;
632 st
->lwipfd
=newsockfd
;
633 st
->linebuf
[(st
->bufindex
)]=0;
634 st
->vlinebuf
[(st
->vbufindex
)]=0;
636 for (i
=0;i
<HISTORYSIZE
;i
++)
638 lwip_write(newsockfd
,banner
,strlen(banner
));
639 lwip_write(newsockfd
,"\r\nLogin: ",9);
643 void telnet_init(int vdefd
)
647 struct sockaddr_in serv_addr
;
648 sockfd
=lwip_socket(AF_INET
, SOCK_STREAM
, 0);
651 printlog(LOG_ERR
,"telnet socket err: %s",strerror(errno
));
653 if(setsockopt(sockfd
, SOL_SOCKET
, SO_REUSEADDR
, (char *) &one
,
655 printlog(LOG_ERR
,"telnet setsockopt: %s",strerror(errno
));
658 if(fcntl(sockfd
, F_SETFL
, O_NONBLOCK
) < 0){
659 printlog(LOG_ERR
,"Setting O_NONBLOCK telnet: %s",strerror(errno
));
663 bzero((char *) &serv_addr
, sizeof(serv_addr
));
664 serv_addr
.sin_family
= AF_INET
;
665 serv_addr
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
666 serv_addr
.sin_port
= htons(TELNET_TCP_PORT
);
668 if (lwip_bind(sockfd
, (struct sockaddr
*) &serv_addr
, sizeof(serv_addr
)) < 0) {
669 printlog(LOG_ERR
,"telnet bind err: %s",strerror(errno
));
672 lwip_listen(sockfd
, 5);
674 addpfd(sockfd
,telnetaccept
);
675 create_commandlist();