Special deamon function to receive error messages until startup completion.
[vde.git] / vdetelweb / telnet.c
blob087a735e1961269be316f2872a8cc346ef127642
1 /*
2 * VDETELWEB: VDE telnet and WEB interface
4 * telnet.c: telnet module
5 *
6 * Copyright 2005,2007 Renzo Davoli University of Bologna - Italy
7 *
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.
21 * $Id$
25 #define _GNU_SOURCE
26 #include <config.h>
27 #include <stdio.h>
28 #include <signal.h>
29 #include <stdarg.h>
30 #include <syslog.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <ctype.h>
34 #include <errno.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <sys/poll.h>
38 #include <linux/un.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
41 #include <arpa/telnet.h>
42 #include <string.h>
43 #include <getopt.h>
44 #include "vdetelweb.h"
45 #include <lwipv6.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;
55 struct telnetstat {
56 unsigned char status;
57 unsigned char echo;
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];
75 int i;
76 for (i=0;i<HISTORYSIZE;i++)
77 if(st->history[i])
78 free(st->history[i]);
79 delpfd(pfdsearch(st->lwipfd));
80 lwip_close(st->lwipfd);
81 if (st->vdemgmtfd >= 0) {
82 delpfd(pfdsearch(st->vdemgmtfd));
83 close(st->vdemgmtfd);
85 free(st);
88 #define CC_HEADER 0
89 #define CC_BODY 1
90 #define CC_TERM 2
91 #define MAX_KEYWORDS 128
93 static int commonprefix(char *x, char *y,int maxlen)
95 int len=0;
96 while (*(x++)==*(y++) && len<maxlen)
97 len++;
98 return len;
101 static void showexpand(char *linebuf,int bufindex, int fd)
103 char *buf;
104 size_t bufsize;
105 FILE *ms=open_memstream(&buf,&bufsize);
106 int nmatches=0;
107 if (ms) {
108 if (commandlist && bufindex>0) {
109 char **s=commandlist;
110 while (*s) {
111 if (strncmp(linebuf,*s,bufindex)==0) {
112 nmatches++;
113 fprintf(ms,"%s ",*s);
115 s++;
117 fprintf(ms,"\r\n");
119 fclose(ms);
120 if (nmatches > 1)
121 lwip_write(fd,buf,strlen(buf));
122 free(buf);
126 static int tabexpand(char *linebuf,int bufindex,int maxlength)
128 if (commandlist && bufindex>0) {
129 char **s=commandlist;
130 int nmatches=0;
131 int len=0;
132 char *match=NULL;
133 while (*s) {
134 if (strncmp(linebuf,*s,bufindex)==0) {
135 nmatches++;
136 if (nmatches == 1) {
137 match=*s;
138 len=strlen(match);
139 } else
140 len=commonprefix(match,*s,len);
142 s++;
144 if (len > 0) {
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);
154 linebuf[len]=' ';
155 len++;
157 bufindex=len;
161 return bufindex;
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];
173 int nkeywords=0;
174 int i,j;
175 if (vdefd) {
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)
182 status=CC_BODY;
183 } else {
184 if (strncmp(linebuf,".\n",2) == 0)
185 status=CC_TERM;
186 else {
187 char *s=linebuf;
188 while (*s!=' ' && *s != 0)
189 s++;
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*/
203 } else {
204 localclist[j]=localclist[i];
205 j++;
207 nkeywords=j;
208 write(vdefd,"logout\n",7);
209 close(vdefd);
211 nkeywords++;
212 commandlist=malloc(nkeywords*sizeof(char *));
213 if (commandlist) {
214 for (i=0;i<nkeywords;i++)
215 commandlist[i]=localclist[i];
216 commandlist[i]=NULL;
220 static void erase_line(struct telnetstat *st,int prompt_too)
222 int j;
223 int size=st->bufindex+(prompt_too != 0)*strlen(prompt);
224 char *buf;
225 size_t bufsize;
226 FILE *ms=open_memstream(&buf,&bufsize);
227 if (ms) {
228 for (j=0;j<size;j++)
229 fputc('\010',ms);
230 size=strlen(st->linebuf)+(prompt_too != 0)*strlen(prompt);
231 for (j=0;j<size;j++)
232 fputc(' ',ms);
233 for (j=0;j<size;j++)
234 fputc('\010',ms);
235 fclose(ms);
236 if (buf)
237 lwip_write(st->lwipfd,buf,bufsize);
238 free(buf);
242 static void redraw_line(struct telnetstat *st,int prompt_too)
244 int j;
245 int tail=strlen(st->linebuf)-st->bufindex;
246 char *buf;
247 size_t bufsize;
248 FILE *ms=open_memstream(&buf,&bufsize);
249 if (ms) {
250 if (prompt_too)
251 fprintf(ms,"%s%s",prompt,st->linebuf);
252 else
253 fprintf(ms,"%s",st->linebuf);
254 for (j=0;j<tail;j++)
255 fputc('\010',ms);
256 fclose(ms);
257 if (buf)
258 lwip_write(st->lwipfd,buf,bufsize);
259 free(buf);
263 void telnet_getanswer(struct telnetstat *st)
265 char buf[BUFSIZE+1];
266 int n=0,ib=0;
267 n=read(st->vdemgmtfd,buf,BUFSIZE);
268 buf[n]=0;
269 while (n>0) {
270 for(ib=0;ib<n;ib++)
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';
277 (st->vbufindex)++;
278 if (st->vindata) {
279 if (st->vlinebuf[0]=='.' && st->vlinebuf[1]=='\r')
280 st->vindata=0;
281 else
282 lwip_write(st->lwipfd,st->vlinebuf,(st->vbufindex));
283 } else {
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])))
291 message++;
292 //fprintf(stderr,"MSG2 \"%s\"\n",message);
293 if (strncmp(message,"0000",4)==0)
294 st->vindata=1;
295 else if(message[0]=='1' &&
296 isdigit(message[1]) &&
297 isdigit(message[2]) &&
298 isdigit(message[3])) {
299 message+=5;
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])) {
305 message+=5;
306 lwip_write(st->lwipfd,"** DBG MSG: ",12);
307 lwip_write(st->lwipfd,(message),strlen(message));
310 (st->vbufindex)=0;
313 n=read(st->vdemgmtfd,buf,BUFSIZE);
317 void vdedata(int fn,int fd,int vdefd)
319 struct telnetstat *st=status[fn];
320 erase_line(st,1);
321 if (st->vdemgmtfd)
322 telnet_getanswer(st);
323 redraw_line(st,1);
326 void telnet_core(int fn,int fd,int vdefd)
328 struct telnetstat *st=status[fn];
330 switch (st->status) {
331 case TELNET_LOGIN:
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);
336 } else {
337 lwip_write(fd,"Password: ",11);
338 st->status=TELNET_PASSWD;
340 break;
341 case 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)) {
348 st->status++;
349 if (st->status < TELNET_PASSWD + 3)
350 lwip_write(fd,"\r\nlogin incorrect\r\n\r\nPassword: ",30);
351 else
352 telnet_close(fn,fd);
353 } else {
354 int newfn;
355 int flags;
356 st->vdemgmtfd=openextravdem();
357 flags = fcntl(st->vdemgmtfd, F_GETFL);
358 flags |= O_NONBLOCK;
359 fcntl(st->vdemgmtfd, F_SETFL, flags);
360 if (st->vdemgmtfd >= 0) {
361 newfn=addpfd(st->vdemgmtfd,vdedata);
362 status[newfn]=st;
363 } else
364 telnet_close(fn,fd);
365 st->status=TELNET_COMMAND;
366 lwip_write(fd,"\r\n",2);
367 lwip_write(fd,prompt,strlen(prompt));
369 break;
370 case TELNET_COMMAND:
372 char *cmd=st->linebuf;
373 while (*cmd == ' ' || *cmd == '\t')
374 cmd++;
375 if (strncmp(cmd,"logout",6)==0)
376 telnet_close(fn,fd);
377 else {
378 if (*cmd != 0) {
379 write(st->vdemgmtfd,st->linebuf,st->bufindex);
380 if (strncmp(cmd,"shutdown",8)==0) {
381 telnet_close(fn,fd);
382 exit(0);
383 } /*else
384 telnet_getanswer(fd,st->vdemgmtfd);*/
386 lwip_write(fd,"\r\n",2);
387 lwip_write(fd,prompt,strlen(prompt));
389 break;
394 static void telnet_option_send3(int fd,int action,int object)
396 char opt[3];
397 opt[0]=0xff;
398 opt[1]=action;
399 opt[2]=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);
411 int skip=2;
412 s++;
413 action_n_object=((*s)<<8) + (*(s+1));
414 switch (action_n_object) {
415 case (DO<<8) + TELOPT_ECHO:
416 //printf("okay echo\n");
417 st->echo=1;
418 break;
419 case (WILL<<8) + TELOPT_ECHO:
420 telnet_option_send3(fd,DONT,TELOPT_ECHO);
421 telnet_option_send3(fd,WILL,TELOPT_ECHO);
422 break;
423 case (DO<<8) + TELOPT_SGA:
424 //printf("do sga -> okay will sga\n");
425 telnet_option_send3(fd,WILL,TELOPT_SGA);
426 break;
427 case (WILL<<8) + TELOPT_TTYPE:
428 //printf("will tty -> dont tty\n");
429 telnet_option_send3(fd,DONT,TELOPT_TTYPE);
430 break;
431 default:
432 //printf("not managed yet %x %x\n",*s,*(s+1));
433 if (*s == WILL)
434 telnet_option_send3(fd,DONT,*(s+1));
435 else if (*s == DO)
436 telnet_option_send3(fd,WONT,*(s+1));
438 return skip;
442 static void erase_line(int fd, struct telnetstat *st)
444 int j;
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 *));
474 st->history[0]=NULL;
477 void telnetdata(int fn,int fd,int vdefd)
479 unsigned char buf[BUFSIZE];
480 int n,i;
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]);
484 if (n==0)
485 telnet_close(fn,fd);
486 else if (n<0)
487 printlog(LOG_ERR,"telnet read err: %s",strerror(errno));
488 else {
489 for (i=0;i<n && strlen(st->linebuf)<BUFSIZE;i++) {
490 if (buf[i] == 0xff && buf[i+1] == 0xff)
491 i++;
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) {
496 /* ESCAPE! */
497 if (buf[i+1]=='[' && st->status == TELNET_COMMAND) {
498 st->edited=1;
499 switch (buf[i+2]) {
500 case 'A': //fprintf(stderr,"UP\n");
501 erase_line(st,0);
502 put_history(st);
503 get_history(1,st);
504 redraw_line(st,0);
505 //lwip_write(fd,st->linebuf,st->bufindex);
506 st->bufindex=strlen(st->linebuf);
507 break;
508 case 'B': //fprintf(stderr,"DOWN\n");
509 erase_line(st,0);
510 put_history(st);
511 get_history(-1,st);
512 redraw_line(st,0);
513 //lwip_write(fd,st->linebuf,st->bufindex);
514 break;
515 case 'C': //fprintf(stderr,"RIGHT\n");
516 if (st->linebuf[st->bufindex] != '\0') {
517 lwip_write(fd,"\033[C",3);
518 (st->bufindex)++;
520 break;
521 case 'D': //fprintf(stderr,"LEFT\n");
522 if (st->bufindex > 0) {
523 lwip_write(fd,"\033[D",3);
524 (st->bufindex)--;
526 break;
528 i+=3;
530 else
531 i+=2;/* ignored */
532 } else if(buf[i] < 0x20 && !(buf[i] == '\n' || buf[i] == '\r')) {
533 /*ctrl*/
534 if (buf[i] == 4) /*ctrl D is a shortcut for UNIX people! */ {
535 telnet_close(fn,fd);
536 break;
538 switch (buf[i]) {
539 case 3: /*ctrl C cleans the current buffer */
540 erase_line(st,0);
541 st->bufindex=0;
542 st->linebuf[(st->bufindex)]=0;
543 break;
544 case 12: /* ctrl L redraw */
545 erase_line(st,1);
546 redraw_line(st,1);
547 break;
548 case 1: /* ctrl A begin of line */
549 erase_line(st,0);
550 st->bufindex=0;
551 redraw_line(st,0);
552 break;
553 case 5: /* ctrl E endofline */
554 erase_line(st,0);
555 st->bufindex=strlen(st->linebuf);
556 redraw_line(st,0);
557 case '\t': /* tab */
558 if (st->lastchar== '\t') {
559 erase_line(st,1);
560 showexpand(st->linebuf,st->bufindex,fd);
561 redraw_line(st,1);
562 } else {
563 erase_line(st,0);
564 st->bufindex=tabexpand(st->linebuf,st->bufindex,BUFSIZE);
565 redraw_line(st,0);
567 break;
569 } else if(buf[i] == 0x7f) {
570 if(st->bufindex > 0) {
571 char *x;
572 (st->bufindex)--;
573 x=st->linebuf+st->bufindex;
574 memmove(x,x+1,strlen(x));
575 if (st->echo && st->status<TELNET_PASSWD) {
576 if (st->edited)
577 lwip_write(fd,"\010\033[P",4);
578 else
579 lwip_write(fd,"\010 \010",3);
582 } else {
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') {
589 if (buf[i]=='\n') {
590 if (st->status == TELNET_COMMAND) {
591 st->histindex=0;
592 put_history(st);
593 if (strlen(st->linebuf) > 0)
594 shift_history(st);
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;
600 } else {
601 char *x;
602 x=st->linebuf+st->bufindex;
603 memmove(x+1,x,strlen(x)+1);
604 st->linebuf[(st->bufindex)++]=buf[i];
608 st->lastchar=buf[i];
613 void telnetaccept(int fn,int fd,int vdefd)
615 struct sockaddr_in cli_addr;
616 int newsockfd;
617 unsigned int clilen;
618 struct telnetstat *st;
619 int newfn;
620 int i;
622 clilen = sizeof(cli_addr);
623 newsockfd = lwip_accept(fd, (struct sockaddr *) &cli_addr, &clilen);
625 if (newsockfd < 0) {
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;
632 st->echo=0;
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;
638 st->vdemgmtfd=-1;
639 for (i=0;i<HISTORYSIZE;i++)
640 st->history[i]=0;
641 lwip_write(newsockfd,banner,strlen(banner));
642 lwip_write(newsockfd,"\r\nLogin: ",9);
645 void telnet_init(int vdefd)
647 int sockfd;
648 struct sockaddr_in serv_addr;
649 sockfd=lwip_socket(AF_INET, SOCK_STREAM, 0);
651 if (!sockfd) {
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();