bugfix:
[vde.git] / vdetelweb / web.c
blobe065617d1b32c00cd75b0b04242be4223c7a3c7a
1 /*
2 * VDETELWEB: VDE telnet and WEB interface
4 * web.c: http micro server for vde mgmt
5 *
6 * Copyright 2005 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; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 * $Id$
25 #include <config.h>
26 #include <stdio.h>
27 #define __USE_GNU
28 #include <signal.h>
29 #include <stdarg.h>
30 #include <syslog.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <sys/poll.h>
36 #include <sys/ioctl.h>
37 #include <linux/un.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40 #include <string.h>
41 #include <getopt.h>
42 #include "vdetelweb.h"
43 #include <lwipv6.h>
45 #define WEB_TCP_PORT 80
46 #define WEB_IDENTIFY 0x0
47 #define WEB_AUTHORIZED 0x1
48 #define WEB_UNAUTHORIZED 0x2
49 #define WEB_OP_GET 0x0
50 #define WEB_OP_POST 0x1
51 #define WEB_OP_POSTDATA 0x2
53 static char base64ab[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
54 static char *base64passwd;
55 typedef void (*voidfun)();
56 struct webstat {
57 unsigned char status;
58 unsigned char op;
59 unsigned int bodylen;
60 char linebuf[BUFSIZE];
61 char path[BUFSIZE];
62 int bufindex;
66 static void lowercase(char *s)
68 while (*s != 0) {
69 tolower(*s);
70 s++;
74 static convert2base64(char *from,char *to,int tosize)
76 int convbuf;
77 int n=strlen(from);
78 while (n>0 && tosize>3) {
79 convbuf=*from;
80 from++;n--;
81 convbuf<<=8;
82 if (n>0) convbuf|=*from;
83 from++;n--;
84 convbuf<<=8;
85 if (n>0) convbuf|=*from;
86 from++;n--;
87 *(to++)=base64ab[convbuf>>18];
88 *(to++)=base64ab[convbuf>>12 & 0x3f];
89 *(to++)=(n<-1)?'=':base64ab[convbuf>>6 & 0x3f];
90 *(to++)=(n<0)?'=':base64ab[convbuf & 0x3f];
91 tosize -= 4;
93 *to=0;
96 static void createbase64passwd()
98 char buf[BUFSIZE];
99 char buf64[BUFSIZE*4/3];
100 snprintf(buf,BUFSIZE,"admin:%s",passwd);
101 convert2base64(buf,buf64,BUFSIZE*4/3);
102 base64passwd=strdup(buf64);
105 static void lwip_printf(int fd, const char *format, ...)
107 char outbuf[BUFSIZE];
108 va_list arg;
109 va_start (arg, format);
110 vsnprintf(outbuf,BUFSIZE,format,arg);
111 lwip_write(fd,outbuf,strlen(outbuf));
114 static void web_close(int fn,int fd)
116 //printf("web_close %d %d\n",fn,fd);
117 free(status[fn]);
118 delpfd(fn);
119 lwip_close(fd);
122 static int vde_getanswer(voidfun f,void *arg,int vdefd)
124 char buf[BUFSIZE];
125 char linebuf[BUFSIZE+1];
126 int n=0,ib=0,il=0,indata=0,eoa=0;
127 do {
128 n=read(vdefd,buf,BUFSIZE);
129 for(ib=0;ib<n;ib++)
131 linebuf[il++]=buf[ib];
132 if (buf[ib] == '\n') {
133 linebuf[il-1]='\r';
134 linebuf[il]='\n';
135 linebuf[il+1]=0;
136 il++;
137 if (indata) {
138 if (linebuf[0]=='.' && linebuf[1]=='\r')
139 indata=0;
140 else
141 f(arg,linebuf,il,indata,0);
142 } else if (strncmp(linebuf,"0000",4)==0)
143 indata=1;
144 else {
145 if(linebuf[0]=='1' &&
146 linebuf[1] >= '0' && linebuf[1] <= '9' &&
147 linebuf[2] >= '0' && linebuf[2] <= '9' &&
148 linebuf[3] >= '0' && linebuf[3] <= '9') {
149 f(arg,linebuf+5,il-5,0,atoi(linebuf));
150 eoa=atoi(linebuf);
153 il=0;
156 } while (!eoa);
157 return(eoa);
160 struct vdesub {
161 char *name;
162 char *descr;
163 char *syntax;
164 struct vdesub *next;
167 struct vdemenu {
168 char *name;
169 char *descr;
170 struct vdesub *sub;
171 struct vdemenu *next;
174 static struct vdemenu *menuhead;
176 static struct vdemenu *vde_findmenu(struct vdemenu *head,char *name)
178 if (head == NULL)
179 return NULL;
180 else
181 if (strcmp(head->name,name)==0)
182 return head;
183 else
184 return vde_findmenu(head->next,name);
187 static void vde_addsub(struct vdesub **headp,char *name,char *syntax,char *help)
189 if (*headp == NULL) {
190 *headp=malloc(sizeof(struct vdesub));
191 if (*headp != NULL) {
192 (*headp)->name=name;
193 (*headp)->descr=help;
194 (*headp)->syntax=syntax;
195 (*headp)->next=NULL;
197 } else
198 vde_addsub(&((*headp)->next),name,syntax,help);
201 static void vde_addcmd(struct vdemenu *head,char *menu,char *name,char *syntax,char *help)
203 if (head != NULL) {
204 if (strcmp(head->name,menu) == 0)
205 vde_addsub(&(head->sub),name,syntax,help);
206 else
207 vde_addcmd(head->next,menu,name,syntax,help);
211 static void vde_addmenu(struct vdemenu **headp,char *name,char *help)
213 if (*headp == NULL) {
214 *headp=malloc(sizeof(struct vdemenu));
215 if (*headp != NULL) {
216 (*headp)->name=name;
217 (*headp)->descr=help;
218 (*headp)->sub=NULL;
219 (*headp)->next=NULL;
221 } else
222 vde_addmenu(&((*headp)->next),name,help);
225 static void vde_helpline(struct vdemenu **headp,char *buf,int len,int indata,int rv)
227 static int nl=0;
228 static int syntaxpos,helppos;
229 nl++;
230 if (nl==2) {
231 int i;
232 for (i=0;i<len && buf[i]=='-';i++) ;
233 for (;i<len && buf[i]==' ';i++) ;
234 syntaxpos=i;
235 for (;i<len && buf[i]=='-';i++) ;
236 for (;i<len && buf[i]==' ';i++) ;
237 helppos=i;
239 else if (nl > 2 && indata && (strncmp(buf,"debug",5) !=0 )) {
240 int i;
241 char *name;
242 char *syntax;
243 char *help;
244 int namelen;
245 for (namelen=0;namelen<syntaxpos && buf[namelen]!=' ';namelen++) ;
246 if (strncmp(buf+syntaxpos,"======",5) ==0) {
247 /* MENU */
248 name=strndup(buf,namelen);
249 help=strndup(buf+helppos,len-helppos-2);
250 vde_addmenu(headp,name,help);
251 } else {
252 int slash;
253 for (slash=0;slash<namelen && buf[slash]!='/';slash++) ;
254 if (slash<namelen) {
255 int synlen;
256 buf[slash]=0;slash++;
257 namelen-=slash;
258 for (synlen=helppos-syntaxpos; synlen>0 && buf[syntaxpos+synlen-1]==' ';synlen--) ;
259 name=strndup(buf+slash,namelen);
260 if (synlen>0)
261 syntax=strndup(buf+syntaxpos,synlen);
262 else
263 syntax="";
264 help=strndup(buf+helppos,len-helppos-2);
265 vde_addcmd(*headp,buf,name,syntax,help);
271 static struct vdemenu *vde_gethelp(int vdefd)
273 struct vdemenu *head=NULL;
274 write(vdefd,"help\n",5);
275 vde_getanswer(vde_helpline,&head,vdefd);
276 return head;
279 static void lwip_showline(int *fdp,char *buf,int len,int indata,int rv)
281 if (indata)
282 lwip_write(*fdp,buf,len);
285 static int lwip_showout(int fd, int vdefd)
287 return vde_getanswer(lwip_showline,&fd,vdefd);
290 static int hex2num(int c)
292 if (c>96) c-=32;
293 c -='0';
294 if (c>9)
295 c-=7;
296 return c;
299 static char *uriconv(char *in)
301 char *s=in;
302 char *t=in;
303 while ((*t=*s) != 0) {
304 if (*s=='+')
305 *t=' ';
306 if (*s=='%') {
307 *t=(hex2num(*(s+1))<<4)+hex2num(*(s+2));
308 s+=2;
310 s++;t++;
312 return in;
315 static void postdata_parse(int fd,int vdefd,char *menu,char *postdata)
317 char cmdbuf[BUFSIZE];
318 int cmdlen,arglen,rv;
319 char *postcmd,*cmd,*endcmd,*arg=NULL;
320 /*printf("PD **%s**\n",postdata);*/
321 if ((postcmd=strstr(postdata,"X="))!=NULL) {
322 /* enter in a text field (catched through the hidden button) */
323 cmd=NULL;
324 while(postdata)
326 char *token=strsep(&postdata,"&");
327 int l=strlen(token);
328 char *targ=index(token,'=');
329 if(strncmp("X=",token,2) != 0) {
330 if (targ+1 < token+l)
331 if(cmd==NULL) {
332 char *point;
333 if ((point=strstr(token,".arg")) != NULL)
334 *point=0;
335 cmd=token;
336 arg=targ+1;
337 } else
338 cmd="";
341 if(cmd!=NULL && *cmd != 0) {
342 strncpy(cmdbuf,menu,BUFSIZE);
343 strncat(cmdbuf,"/",BUFSIZE);
344 strncat(cmdbuf,cmd,BUFSIZE);
345 strncat(cmdbuf," ",BUFSIZE);
346 strncat(cmdbuf,uriconv(arg),BUFSIZE);
347 write(vdefd,cmdbuf,strlen(cmdbuf));
348 lwip_printf(fd,"<P> </P><B>%s %s</B><PRE>",prompt,cmdbuf);
349 rv=lwip_showout(fd,vdefd);
350 lwip_printf(fd,"</PRE><B>Result: %s</B>\r\n",strerror(rv-1000));
353 else if ((postcmd=strstr(postdata,"COMMAND="))!=NULL) {
354 /* accept button */
355 postcmd+=8;
356 for(cmdlen=0;postcmd[cmdlen] != '&' && postcmd[cmdlen] != 0; cmdlen++)
358 strncpy(cmdbuf,menu,BUFSIZE);
359 strncat(cmdbuf,"/",BUFSIZE);
360 cmd=cmdbuf+strlen(cmdbuf);
361 strncat(cmdbuf,postcmd,(BUFSIZE<cmdlen)?BUFSIZE:cmdlen);
362 endcmd=cmdbuf+strlen(cmdbuf);
363 strncat(cmdbuf,".arg",BUFSIZE);
364 if ((arg=strstr(postdata,cmd))!=NULL) {
365 arg+=strlen(cmd)+1;
366 for(arglen=0;arg[arglen] != '&' && arg[arglen] != 0; arglen++)
368 arg[arglen]=0;
369 *endcmd=0;
370 if (*arg != 0) {
371 strncat(cmdbuf," ",BUFSIZE);
372 strncat(cmdbuf,uriconv(arg),BUFSIZE);
374 } else
375 *endcmd=0;
376 write(vdefd,cmdbuf,strlen(cmdbuf));
377 lwip_printf(fd,"<P> </P><B>%s %s</B><PRE>",prompt,cmdbuf);
378 rv=lwip_showout(fd,vdefd);
379 lwip_printf(fd,"</PRE><B>Result: %s</B>\r\n",strerror(rv-1000));
383 static char css[]=
384 "<style type=\"text/CSS\"\r\n"
385 "<!--\r\n"
386 ".core {\r\n"
387 "font-family: Helvetica;\r\n"
388 "color: #0000FF;\r\n"
389 "background-color: #FFFFFF;\r\n"
390 "text-align: justify;\r\n"
391 "margin-left: 5pt;\r\n"
392 "margin-top: 5pt;\r\n"
393 "margin-right: 5pt;\r\n"
394 "margin-bottom: 5pt;\r\n"
395 "}\r\n"
396 ".sidebar {\r\n"
397 "font-family: Helvetica;\r\n"
398 "font-size: 12px;\r\n"
399 "color: #ff0000;\r\n"
400 "}\r\n"
401 "-->\r\n"
402 "</style>\r\n";
404 static char okmsg[]=
405 "HTTP/1.1 200 OK\r\n"
406 "Content-Type: text/html\r\n"
407 "\r\n";
409 static char errmsg[]=
410 "HTTP/1.1 404 Not Found\r\n"
411 "Content-Type: text/html\r\n"
412 "\r\n"
413 "<HTML><HEAD>\r\n"
414 "<TITLE>404 Not Found</TITLE>\r\n"
415 "</HEAD><BODY>\r\n"
416 "<H1>Not Found</H1>\r\n"
417 "The requested URL was not found on this server.\r\n"
418 "<hr>VDE 2.0 WEB MGMT INTERFACE\r\n"
419 "</BODY></HTML>\r\n";
421 static void web_this_form(int fd,struct vdemenu *this)
423 struct vdesub *sub;
424 for (sub=this->sub;sub!=NULL;sub=sub->next) {
425 if (*(sub->syntax) == 0) {
426 lwip_printf(fd,
427 "<TR><TD width=50><INPUT type=submit size=100 name=\"%s\" value=\"%s\"></TD>\r\n"
428 "<TD width=100></TD>\r\n"
429 "<TD width=100></TD>\r\n"
430 "<TD width=300>%s</TD></TR>\r\n",
431 "COMMAND",sub->name,sub->descr);
432 } else {
433 lwip_printf(fd,
434 "<TR><TD width=50><INPUT type=submit size=100 name=\"%s\" value=\"%s\"></TD>\r\n"
435 "<TD width=100>%s</TD>\r\n"
436 "<TD width=100><INPUT type=text name=\"%s.arg\"></TD>\r\n"
437 "<TD width=300>%s</TD></TR>\r\n",
438 "COMMAND",sub->name,sub->syntax,sub->name,sub->descr);
443 static void web_menu_index(int fd)
445 struct vdemenu *this;
446 lwip_printf(fd,"<P><A HREF=\"index.html\">Home Page</A></P>\r\n");
447 for (this=menuhead;this!=NULL;this=this->next)
448 lwip_printf(fd,"<P><A HREF=\"%s.html\">%s</A></P>\r\n",this->name,this->name);
451 static void web_create_page(char *path,int fd,int vdefd,char *postdata)
453 struct vdemenu *this;
454 char *tail;
455 if ((tail=strstr(path,".html")) != NULL)
456 *tail=0;
457 if (*path==0 || ((this=vde_findmenu(menuhead,path)) != NULL)) {
458 lwip_write(fd,okmsg,sizeof(okmsg));
459 lwip_printf(fd,
460 "<HTML><HEAD>\r\n"
461 "<TITLE>%s %s</TITLE>\r\n",
462 prompt, (*path==0)?"Home Page":path);
463 lwip_write(fd,css,sizeof(css));
464 lwip_printf(fd,
465 "</HEAD><BODY class=core>\r\n"
466 "<H1>%s %s</H1>\r\n"
467 "<TABLE BORDER=0><TD width=80 bgcolor=#aacbff valign=top class=sidebar>",
468 prompt, (*path==0)?"Home Page":this->descr);
469 web_menu_index(fd);
470 if (*path==0) {/* HOME PAGE */
471 int rv;
472 write(vdefd,"showinfo\r\n",10);
473 lwip_printf(fd,
474 "</TD><TD><PRE>\r\n");
475 rv=lwip_showout(fd,vdefd);
476 lwip_printf(fd,"</PRE>\r\n");
477 if (rv != 1000)
478 lwip_printf(fd,"<B>%s</B>\r\n",strerror(rv-1000));
479 } else {
480 lwip_printf(fd,
481 "</TD><TD><FORM action=\"%s.html\" method=post table-layout=fixed>\r\n<TABLE><THEAD><TR>\r\n"
482 "<TD><INPUT type=submit name=X style=\"visibility:hidden\" ></TD>\r\n"
483 "<TD><B>Syntax</B></TD><TD><B>Args</B>\r\n"
484 "</TD><TD><B>Description</B></TD></TR></THEAD>\r\n",path);
485 web_this_form(fd,this);
486 lwip_printf(fd,"</TABLE></FORM>\r\n");
487 if (postdata != NULL) {
488 postdata_parse(fd,vdefd,path,postdata);
491 lwip_printf(fd,
492 "</TD></TABLE>\r\n"
493 "<hr>VDE 2.0 WEB MGMT INTERFACE\r\n"
494 "</BODY></HTML>\r\n");
495 } else
496 lwip_write(fd,errmsg,sizeof(errmsg));
499 static char authmsg[]=
500 "HTTP/1.1 401 Authorization Required\r\n"
501 "WWW-Authenticate: Basic realm=\"";
503 //"Content-Length: 187\r\n"
504 //"Connection: close\r\n"
505 static char authmsg2[]= "\"\r\n"
506 "Content-Type: text/html\r\n"
507 "\r\n"
508 "<HTML><HEAD>\r\n"
509 "<TITLE>401 Authorization Required</TITLE>\r\n"
510 "</HEAD><BODY>\r\n"
511 "<H1>Authorization Required</H1>\r\n"
512 "Login and Password required\r\n"
513 "<hr>\r\nVDE 2.0 WEB MGMT INTERFACE</H1>\r\n"
514 "</BODY></HTML>\r\n";
517 int web_core(int fn,int fd,int vdefd)
519 struct webstat *st=status[fn];
520 //printf("CORE %s\n",st->linebuf);
521 if (st->op==WEB_OP_POSTDATA) {
522 //printf("POSTDATA %s\n",st->linebuf);
523 web_create_page(&(st->path[1]),fd,vdefd,st->linebuf);
524 return 1;
525 } else if (strncmp(st->linebuf,"GET",3) == 0) {
526 //printf("GET %s\n",st->linebuf);
527 sscanf(st->linebuf+4,"%s",st->path);
528 st->op=WEB_OP_GET;
529 return 0;
530 } else if (strncmp(st->linebuf,"POST",3) == 0) {
531 //printf("POST %s\n",st->linebuf);
532 sscanf(st->linebuf+5,"%s",st->path);
533 st->op=WEB_OP_POST;
534 return 0;
535 } else if (strncmp(st->linebuf,"Content-Length: ",16) == 0) {
536 st->bodylen=atoi(st->linebuf+16);
537 //printf("BODYLEN %d\n",st->bodylen);
538 return 0;
539 } else if (strncmp(st->linebuf,"Authorization: Basic",20) == 0) {
540 int k=20;
541 while (st->linebuf[k] == ' ') k++;
542 if (strncmp(st->linebuf+k,base64passwd,strlen(base64passwd))==0)
543 st->status=WEB_AUTHORIZED;
544 return 0;
545 } else if (st->linebuf[0]=='\n' || st->linebuf[0]=='\r') {
546 switch (st->status) {
547 case WEB_IDENTIFY:
548 lwip_write(fd,authmsg,sizeof(authmsg));
549 lwip_write(fd,prompt,strlen(prompt));
550 lwip_write(fd,authmsg2,sizeof(authmsg2));
551 return 1;
552 break;
553 case WEB_AUTHORIZED:
554 lowercase(st->path);
555 if (strcmp(st->path,"/index.html") == 0)
556 st->path[1]=0;
557 if (st->op == WEB_OP_GET) {
558 web_create_page(&(st->path[1]),fd,vdefd,NULL);
559 return 1;
560 } else {
561 st->op=WEB_OP_POSTDATA;
562 return 0;
565 } else
566 return 0;
569 int webdata(int fn,int fd,int vdefd)
571 char buf[BUFSIZE];
572 int n,i;
573 struct webstat *st=status[fn];
574 n=lwip_read(fd,buf,BUFSIZE);
575 if (n==0) {
576 web_close(fn,fd);
578 else if (n<0)
579 printlog(LOG_ERR,"web read err: %s",strerror(errno));
580 else {
581 for (i=0;i<n && st->bufindex<BUFSIZE;i++) {
582 st->linebuf[(st->bufindex)++]=buf[i];
583 if (buf[i]=='\n' || (st->op==WEB_OP_POSTDATA && st->bufindex==st->bodylen)) {
584 st->linebuf[(st->bufindex)]=0;
585 if (web_core(fn,fd,vdefd)) {
586 web_close(fn,fd);
587 break;
588 } else
589 st->bufindex=0;
595 int webaccept(int fn,int fd,int vdefd)
597 struct sockaddr_in cli_addr;
598 int newsockfd;
599 unsigned int clilen;
600 struct webstat *st;
601 int newfn;
603 clilen = sizeof(cli_addr);
604 newsockfd = lwip_accept(fd, (struct sockaddr *) &cli_addr, &clilen);
606 if (newsockfd < 0) {
607 printlog(LOG_ERR,"web accept err: %s",strerror(errno));
610 newfn=addpfd(newsockfd,webdata);
611 status[newfn]=st=malloc(sizeof(struct webstat));
612 st->status=WEB_IDENTIFY;
613 st->op=0;
614 st->bufindex=0;
615 return 0;
618 void web_init(int vdefd)
620 int sockfd;
621 int one=1;
622 struct sockaddr_in serv_addr;
623 sockfd=lwip_socket(AF_INET, SOCK_STREAM, 0);
625 if (!sockfd) {
626 printlog(LOG_ERR,"web socket err: %s",strerror(errno));
628 if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
629 sizeof(one)) < 0){
630 printlog(LOG_ERR,"web setsockopt: %s",strerror(errno));
631 return;
633 if(fcntl(sockfd, F_SETFL, O_NONBLOCK) < 0){
634 printlog(LOG_ERR,"Setting O_NONBLOCK web: %s",strerror(errno));
635 return;
638 bzero((char *) &serv_addr, sizeof(serv_addr));
639 serv_addr.sin_family = AF_INET;
640 serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
641 serv_addr.sin_port = htons(WEB_TCP_PORT);
643 if (lwip_bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
644 printlog(LOG_ERR,"web bind err: %s",strerror(errno));
647 lwip_listen(sockfd, 5);
649 createbase64passwd();
650 menuhead=vde_gethelp(vdefd);
651 addpfd(sockfd,webaccept);