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.
35 #include <sys/types.h>
36 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
41 #include <arpa/telnet.h>
44 #include "vdetelweb.h"
47 #define TELNET_TCP_PORT 23
48 #define TELNET_LOGIN 0x0
49 #define TELNET_COMMAND 0x1
50 #define TELNET_PASSWD 0x80
51 #define HISTORYSIZE 32
53 static char **commandlist
;
58 unsigned char telnetprotocol
;
59 unsigned char edited
; /* the linebuf has been modified (left/right arrow)*/
60 unsigned char vindata
; /* 1 when in_data... (0000 end with .)*/
61 char lastchar
; /* for double tag*/
62 char linebuf
[BUFSIZE
]; /*line buffer from the user*/
63 int bufindex
; /*current editing position on the buf */
64 char vlinebuf
[BUFSIZE
+1]; /*line buffer from vde*/
65 int vbufindex
; /*current editing position on the buf */
66 char *history
[HISTORYSIZE
]; /*history of the previous commands*/
67 int histindex
; /* index on the history (changed with up/down arrows) */
68 int lwipfd
; /* fd to the network */
69 int vdemgmtfd
; /* mgmt fd to vde_switch */
72 void telnet_close(int fn
,int fd
)
74 struct telnetstat
*st
=status
[fn
];
76 for (i
=0;i
<HISTORYSIZE
;i
++)
79 delpfd(pfdsearch(st
->lwipfd
));
80 lwip_close(st
->lwipfd
);
81 if (st
->vdemgmtfd
>= 0) {
82 delpfd(pfdsearch(st
->vdemgmtfd
));
91 #define MAX_KEYWORDS 128
93 static int commonprefix(char *x
, char *y
,int maxlen
)
96 while (*(x
++)==*(y
++) && len
<maxlen
)
101 static void showexpand(char *linebuf
,int bufindex
, int fd
)
105 FILE *ms
=open_memstream(&buf
,&bufsize
);
108 if (commandlist
&& bufindex
>0) {
109 char **s
=commandlist
;
111 if (strncmp(linebuf
,*s
,bufindex
)==0) {
113 fprintf(ms
,"%s ",*s
);
121 lwip_write(fd
,buf
,strlen(buf
));
126 static int tabexpand(char *linebuf
,int bufindex
,int maxlength
)
128 if (commandlist
&& bufindex
>0) {
129 char **s
=commandlist
;
134 if (strncmp(linebuf
,*s
,bufindex
)==0) {
140 len
=commonprefix(match
,*s
,len
);
145 int alreadymatch
=commonprefix(linebuf
,match
,len
);
146 //fprintf(stderr,"TAB %s %d -> %s %d already %d\n",linebuf,bufindex,match,len,alreadymatch);
147 if ((len
-alreadymatch
)+strlen(linebuf
) < maxlength
) {
148 memmove(linebuf
+len
,linebuf
+alreadymatch
,
149 strlen(linebuf
+alreadymatch
)+1);
150 memcpy(linebuf
+alreadymatch
,match
+alreadymatch
,len
-alreadymatch
);
151 if (nmatches
== 1 && linebuf
[len
] != ' ' && strlen(linebuf
)+1 < maxlength
) {
152 memmove(linebuf
+len
+1,linebuf
+len
,
153 strlen(linebuf
+len
)+1);
164 static int qstrcmp(const void *a
,const void *b
)
166 return strcmp(*(char * const *)a
,*(char * const *)b
);
168 static void create_commandlist()
170 int vdefd
=openextravdem();
171 char linebuf
[BUFSIZE
];
172 char *localclist
[MAX_KEYWORDS
];
176 int status
=CC_HEADER
;
177 FILE *in
=fdopen(vdefd
,"r");
178 write(vdefd
,"help\n",5);
179 while (status
!= CC_TERM
&& fgets(linebuf
,BUFSIZE
,in
) != NULL
) {
180 if (status
== CC_HEADER
) {
181 if (strncmp(linebuf
,"------------",12) == 0)
184 if (strncmp(linebuf
,".\n",2) == 0)
188 while (*s
!=' ' && *s
!= 0)
190 *s
=0; /* take the first token */
191 //fprintf(stderr,"%s\n",linebuf);
192 localclist
[nkeywords
]=strdup(linebuf
);
193 if (nkeywords
<MAX_KEYWORDS
) nkeywords
++;
194 //char *thiskeyword=strdup(linebuf);
198 qsort(localclist
,nkeywords
,sizeof(char *),qstrcmp
);
199 for (i
=j
=0; i
<nkeywords
-1; i
++)
200 if (strncmp(localclist
[i
],localclist
[i
+1],strlen(localclist
[i
]))==0 &&
201 localclist
[i
+1][strlen(localclist
[i
])] == '/') {
202 free(localclist
[i
]); /*avoid menu*/
204 localclist
[j
]=localclist
[i
];
208 write(vdefd
,"logout\n",7);
212 commandlist
=malloc(nkeywords
*sizeof(char *));
214 for (i
=0;i
<nkeywords
;i
++)
215 commandlist
[i
]=localclist
[i
];
220 static void erase_line(struct telnetstat
*st
,int prompt_too
)
223 int size
=st
->bufindex
+(prompt_too
!= 0)*strlen(prompt
);
226 FILE *ms
=open_memstream(&buf
,&bufsize
);
230 size
=strlen(st
->linebuf
)+(prompt_too
!= 0)*strlen(prompt
);
237 lwip_write(st
->lwipfd
,buf
,bufsize
);
242 static void redraw_line(struct telnetstat
*st
,int prompt_too
)
245 int tail
=strlen(st
->linebuf
)-st
->bufindex
;
248 FILE *ms
=open_memstream(&buf
,&bufsize
);
251 fprintf(ms
,"%s%s",prompt
,st
->linebuf
);
253 fprintf(ms
,"%s",st
->linebuf
);
258 lwip_write(st
->lwipfd
,buf
,bufsize
);
263 void telnet_getanswer(struct telnetstat
*st
)
267 n
=read(st
->vdemgmtfd
,buf
,BUFSIZE
);
272 st
->vlinebuf
[(st
->vbufindex
)++]=buf
[ib
];
273 if (buf
[ib
] == '\n') {
274 st
->vlinebuf
[(st
->vbufindex
)-1]='\r';
275 st
->vlinebuf
[(st
->vbufindex
)]='\n';
276 st
->vlinebuf
[(st
->vbufindex
)+1]='\0';
279 if (st
->vlinebuf
[0]=='.' && st
->vlinebuf
[1]=='\r')
282 lwip_write(st
->lwipfd
,st
->vlinebuf
,(st
->vbufindex
));
284 char *message
=st
->vlinebuf
;
285 //fprintf(stderr,"MSG1 \"%s\"\n",message);
286 while (*message
!= '\0' &&
287 !(isdigit(message
[0]) &&
288 isdigit(message
[1]) &&
289 isdigit(message
[2]) &&
290 isdigit(message
[3])))
292 //fprintf(stderr,"MSG2 \"%s\"\n",message);
293 if (strncmp(message
,"0000",4)==0)
295 else if(message
[0]=='1' &&
296 isdigit(message
[1]) &&
297 isdigit(message
[2]) &&
298 isdigit(message
[3])) {
300 lwip_write(st
->lwipfd
,message
,strlen(message
));
301 } else if (message
[0]=='3' &&
302 isdigit(message
[1]) &&
303 isdigit(message
[2]) &&
304 isdigit(message
[3])) {
306 lwip_write(st
->lwipfd
,"** DBG MSG: ",12);
307 lwip_write(st
->lwipfd
,(message
),strlen(message
));
313 n
=read(st
->vdemgmtfd
,buf
,BUFSIZE
);
317 void vdedata(int fn
,int fd
,int vdefd
)
319 struct telnetstat
*st
=status
[fn
];
322 telnet_getanswer(st
);
326 void telnet_core(int fn
,int fd
,int vdefd
)
328 struct telnetstat
*st
=status
[fn
];
330 switch (st
->status
) {
332 while (st
->linebuf
[st
->bufindex
-1] == '\n')
333 st
->linebuf
[--st
->bufindex
]=0;
334 if (strcmp(st
->linebuf
,"admin") != 0) {
335 lwip_write(fd
,"login incorrect\r\n\r\nLogin: ",26);
337 lwip_write(fd
,"Password: ",11);
338 st
->status
=TELNET_PASSWD
;
342 case TELNET_PASSWD
+1:
343 case TELNET_PASSWD
+2:
344 while (st
->linebuf
[st
->bufindex
-1] == '\n')
345 st
->linebuf
[--st
->bufindex
]=0;
347 if (!sha1passwdok(st
->linebuf
)) {
349 if (st
->status
< TELNET_PASSWD
+ 3)
350 lwip_write(fd
,"\r\nlogin incorrect\r\n\r\nPassword: ",30);
356 st
->vdemgmtfd
=openextravdem();
357 flags
= fcntl(st
->vdemgmtfd
, F_GETFL
);
359 fcntl(st
->vdemgmtfd
, F_SETFL
, flags
);
360 if (st
->vdemgmtfd
>= 0) {
361 newfn
=addpfd(st
->vdemgmtfd
,vdedata
);
365 st
->status
=TELNET_COMMAND
;
366 lwip_write(fd
,"\r\n",2);
367 lwip_write(fd
,prompt
,strlen(prompt
));
372 char *cmd
=st
->linebuf
;
373 while (*cmd
== ' ' || *cmd
== '\t')
375 if (strncmp(cmd
,"logout",6)==0)
379 write(st
->vdemgmtfd
,st
->linebuf
,st
->bufindex
);
380 if (strncmp(cmd
,"shutdown",8)==0) {
384 telnet_getanswer(fd,st->vdemgmtfd);*/
386 lwip_write(fd
,"\r\n",2);
387 lwip_write(fd
,prompt
,strlen(prompt
));
394 static void telnet_option_send3(int fd
,int action
,int object
)
400 lwip_write(fd
,opt
,3);
403 static int telnet_options(int fn
,int fd
,unsigned char *s
)
405 struct telnetstat
*st
=status
[fn
];
406 register int action_n_object
;
407 if (st
->telnetprotocol
== 0) {
408 st
->telnetprotocol
=1;
409 telnet_option_send3(fd
,WILL
,TELOPT_ECHO
);
413 action_n_object
=((*s
)<<8) + (*(s
+1));
414 switch (action_n_object
) {
415 case (DO
<<8) + TELOPT_ECHO
:
416 //printf("okay echo\n");
419 case (WILL
<<8) + TELOPT_ECHO
:
420 telnet_option_send3(fd
,DONT
,TELOPT_ECHO
);
421 telnet_option_send3(fd
,WILL
,TELOPT_ECHO
);
423 case (DO
<<8) + TELOPT_SGA
:
424 //printf("do sga -> okay will sga\n");
425 telnet_option_send3(fd
,WILL
,TELOPT_SGA
);
427 case (WILL
<<8) + TELOPT_TTYPE
:
428 //printf("will tty -> dont tty\n");
429 telnet_option_send3(fd
,DONT
,TELOPT_TTYPE
);
432 //printf("not managed yet %x %x\n",*s,*(s+1));
434 telnet_option_send3(fd
,DONT
,*(s
+1));
436 telnet_option_send3(fd
,WONT
,*(s
+1));
442 static void erase_line(int fd, struct telnetstat *st)
445 for (j=0;j<st->bufindex;j++)
446 lwip_write(fd,"\033[D",3);
447 for (j=0;j<strlen(st->linebuf);j++)
448 lwip_write(fd,"\033[P",3);
452 static void put_history(struct telnetstat
*st
)
454 if(st
->history
[st
->histindex
])
455 free(st
->history
[st
->histindex
]);
456 st
->history
[st
->histindex
]=strdup(st
->linebuf
);
459 static void get_history(int change
,struct telnetstat
*st
)
461 st
->histindex
+= change
;
462 if(st
->histindex
< 0) st
->histindex
=0;
463 if(st
->histindex
>= HISTORYSIZE
) st
->histindex
=HISTORYSIZE
-1;
464 if(st
->history
[st
->histindex
] == NULL
) (st
->histindex
)--;
465 strcpy(st
->linebuf
,st
->history
[st
->histindex
]);
466 st
->bufindex
=strlen(st
->linebuf
);
469 static void shift_history(struct telnetstat
*st
)
471 if (st
->history
[HISTORYSIZE
-1] != NULL
)
472 free(st
->history
[HISTORYSIZE
-1]);
473 memmove(st
->history
+1,st
->history
,(HISTORYSIZE
-1)*sizeof(char *));
477 void telnetdata(int fn
,int fd
,int vdefd
)
479 unsigned char buf
[BUFSIZE
];
481 struct telnetstat
*st
=status
[fn
];
482 n
=lwip_read(fd
,buf
,BUFSIZE
);
483 //printf("N%d %x %x %x %x\n",n,buf[0],buf[1],buf[2],buf[3]);
487 printlog(LOG_ERR
,"telnet read err: %s",strerror(errno
));
489 for (i
=0;i
<n
&& strlen(st
->linebuf
)<BUFSIZE
;i
++) {
490 if (buf
[i
] == 0xff && buf
[i
+1] == 0xff)
492 if(buf
[i
]==0) buf
[i
]='\n'; /*telnet encode \n as a 0 when in raw mode*/
493 if (buf
[i
] == 0xff && buf
[i
+1] != 0xff) {
494 i
+=telnet_options(fn
,fd
,buf
+i
);
495 } else if(buf
[i
] == 0x1b) {
497 if (buf
[i
+1]=='[' && st
->status
== TELNET_COMMAND
) {
500 case 'A': //fprintf(stderr,"UP\n");
505 //lwip_write(fd,st->linebuf,st->bufindex);
506 st
->bufindex
=strlen(st
->linebuf
);
508 case 'B': //fprintf(stderr,"DOWN\n");
513 //lwip_write(fd,st->linebuf,st->bufindex);
515 case 'C': //fprintf(stderr,"RIGHT\n");
516 if (st
->linebuf
[st
->bufindex
] != '\0') {
517 lwip_write(fd
,"\033[C",3);
521 case 'D': //fprintf(stderr,"LEFT\n");
522 if (st
->bufindex
> 0) {
523 lwip_write(fd
,"\033[D",3);
532 } else if(buf
[i
] < 0x20 && !(buf
[i
] == '\n' || buf
[i
] == '\r')) {
534 if (buf
[i
] == 4) /*ctrl D is a shortcut for UNIX people! */ {
539 case 3: /*ctrl C cleans the current buffer */
542 st
->linebuf
[(st
->bufindex
)]=0;
544 case 12: /* ctrl L redraw */
548 case 1: /* ctrl A begin of line */
553 case 5: /* ctrl E endofline */
555 st
->bufindex
=strlen(st
->linebuf
);
558 if (st
->lastchar
== '\t') {
560 showexpand(st
->linebuf
,st
->bufindex
,fd
);
564 st
->bufindex
=tabexpand(st
->linebuf
,st
->bufindex
,BUFSIZE
);
569 } else if(buf
[i
] == 0x7f) {
570 if(st
->bufindex
> 0) {
573 x
=st
->linebuf
+st
->bufindex
;
574 memmove(x
,x
+1,strlen(x
));
575 if (st
->echo
&& st
->status
<TELNET_PASSWD
) {
577 lwip_write(fd
,"\010\033[P",4);
579 lwip_write(fd
,"\010 \010",3);
583 if (st
->echo
&& st
->status
<TELNET_PASSWD
) {
584 if (st
->edited
&& buf
[i
] >= ' ')
585 lwip_write(fd
,"\033[@",3);
586 lwip_write(fd
,&(buf
[i
]),1);
588 if (buf
[i
] != '\r') {
590 if (st
->status
== TELNET_COMMAND
) {
593 if (strlen(st
->linebuf
) > 0)
596 st
->bufindex
=strlen(st
->linebuf
);
597 telnet_core(fn
,fd
,vdefd
);
598 st
->bufindex
=st
->edited
=st
->histindex
=0;
599 st
->linebuf
[(st
->bufindex
)]=0;
602 x
=st
->linebuf
+st
->bufindex
;
603 memmove(x
+1,x
,strlen(x
)+1);
604 st
->linebuf
[(st
->bufindex
)++]=buf
[i
];
613 void telnetaccept(int fn
,int fd
,int vdefd
)
615 struct sockaddr_in cli_addr
;
618 struct telnetstat
*st
;
622 clilen
= sizeof(cli_addr
);
623 newsockfd
= lwip_accept(fd
, (struct sockaddr
*) &cli_addr
, &clilen
);
626 printlog(LOG_ERR
,"telnet accept err: %s",strerror(errno
));
629 newfn
=addpfd(newsockfd
,telnetdata
);
630 status
[newfn
]=st
=malloc(sizeof(struct telnetstat
));
631 st
->status
=TELNET_LOGIN
;
633 st
->telnetprotocol
=0;
634 st
->bufindex
=st
->edited
=st
->histindex
=st
->vbufindex
=st
->vindata
=st
->lastchar
=0;
635 st
->lwipfd
=newsockfd
;
636 st
->linebuf
[(st
->bufindex
)]=0;
637 st
->vlinebuf
[(st
->vbufindex
)]=0;
639 for (i
=0;i
<HISTORYSIZE
;i
++)
641 lwip_write(newsockfd
,banner
,strlen(banner
));
642 lwip_write(newsockfd
,"\r\nLogin: ",9);
645 void telnet_init(int vdefd
)
648 struct sockaddr_in serv_addr
;
649 sockfd
=lwip_socket(AF_INET
, SOCK_STREAM
, 0);
652 printlog(LOG_ERR
,"telnet socket err: %s",strerror(errno
));
655 bzero((char *) &serv_addr
, sizeof(serv_addr
));
656 serv_addr
.sin_family
= AF_INET
;
657 serv_addr
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
658 serv_addr
.sin_port
= htons(TELNET_TCP_PORT
);
660 if (lwip_bind(sockfd
, (struct sockaddr
*) &serv_addr
, sizeof(serv_addr
)) < 0) {
661 printlog(LOG_ERR
,"telnet bind err: %s",strerror(errno
));
664 lwip_listen(sockfd
, 5);
666 addpfd(sockfd
,telnetaccept
);
667 create_commandlist();