Merge pull request #1818 from dibdot/lxc_fix
[project/luci.git] / libs / luci-lib-nixio / axTLS / httpd / axhttpd.c
1 /*
2  * Copyright (c) 2007, Cameron Rich
3  * 
4  * All rights reserved.
5  * 
6  * Redistribution and use in source and binary forms, with or without 
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * * Redistributions of source code must retain the above copyright notice, 
10  *   this list of conditions and the following disclaimer.
11  * * Redistributions in binary form must reproduce the above copyright notice, 
12  *   this list of conditions and the following disclaimer in the documentation 
13  *   and/or other materials provided with the distribution.
14  * * Neither the name of the axTLS project nor the names of its contributors 
15  *   may be used to endorse or promote products derived from this software 
16  *   without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include <stdio.h>
32 #include <string.h>
33 #include <sys/types.h>
34 #include <signal.h>
35 #include <stdlib.h>
36 #include <sys/stat.h>
37 #include <pwd.h>
38 #include "axhttp.h"
39
40 struct serverstruct *servers;
41 struct connstruct *usedconns;
42 struct connstruct *freeconns;
43 const char * const server_version = "axhttpd/"AXTLS_VERSION;
44
45 static void addtoservers(int sd);
46 static int openlistener(int port);
47 static void handlenewconnection(int listenfd, int is_ssl);
48 static void addconnection(int sd, char *ip, int is_ssl);
49 static void ax_chdir(void);
50
51 #if defined(CONFIG_HTTP_HAS_CGI)
52 struct cgiextstruct *cgiexts;
53 static void addcgiext(const char *tp);
54
55 #if !defined(WIN32)
56 static void reaper(int sigtype) 
57 {
58     wait3(NULL, WNOHANG, NULL);
59 }
60 #endif
61 #endif
62
63 #ifdef CONFIG_HTTP_VERBOSE  /* should really be in debug mode or something */
64 /* clean up memory for valgrind */
65 static void sigint_cleanup(int sig)
66 {
67     struct serverstruct *sp;
68     struct connstruct *tp;
69
70     while (servers != NULL) 
71     {
72         if (servers->is_ssl)
73             ssl_ctx_free(servers->ssl_ctx);
74
75         sp = servers->next;
76         free(servers);
77         servers = sp;
78     }
79
80     while (freeconns != NULL)
81     {
82         tp = freeconns->next;
83         free(freeconns);
84         freeconns = tp;
85     }
86
87     while (usedconns != NULL)
88     {
89         tp = usedconns->next;
90         free(usedconns);
91         usedconns = tp;
92     }
93
94 #if defined(CONFIG_HTTP_HAS_CGI)
95     while (cgiexts)
96     {
97         struct cgiextstruct *cp = cgiexts->next;
98         if (cp == NULL) /* last entry */
99             free(cgiexts->ext);
100         free(cgiexts);
101         cgiexts = cp;
102     }
103 #endif
104
105     exit(0);
106 }
107
108 static void die(int sigtype) 
109 {
110     exit(0);
111 }
112 #endif
113
114 int main(int argc, char *argv[]) 
115 {
116     fd_set rfds, wfds;
117     struct connstruct *tp, *to;
118     struct serverstruct *sp;
119     int rnum, wnum, active;
120     int i;
121     time_t currtime;
122
123 #ifdef WIN32
124     WORD wVersionRequested = MAKEWORD(2, 2);
125     WSADATA wsaData;
126     WSAStartup(wVersionRequested,&wsaData);
127 #else
128     signal(SIGPIPE, SIG_IGN);
129 #if defined(CONFIG_HTTP_HAS_CGI)
130     signal(SIGCHLD, reaper);
131 #endif
132 #ifdef CONFIG_HTTP_VERBOSE
133     signal(SIGQUIT, die);
134 #endif
135 #endif
136
137 #ifdef CONFIG_HTTP_VERBOSE
138     signal(SIGTERM, die);
139     signal(SIGINT, sigint_cleanup);
140 #endif
141     tdate_init();
142
143     for (i = 0; i < INITIAL_CONNECTION_SLOTS; i++) 
144     {
145         tp = freeconns;
146         freeconns = (struct connstruct *)calloc(1, sizeof(struct connstruct));
147         freeconns->next = tp;
148     }
149
150     if ((active = openlistener(CONFIG_HTTP_PORT)) == -1) 
151     {
152 #ifdef CONFIG_HTTP_VERBOSE
153         fprintf(stderr, "ERR: Couldn't bind to port %d\n",
154                 CONFIG_HTTP_PORT);
155 #endif
156         exit(1);
157     }
158
159     addtoservers(active);
160
161     if ((active = openlistener(CONFIG_HTTP_HTTPS_PORT)) == -1) 
162     {
163 #ifdef CONFIG_HTTP_VERBOSE
164         fprintf(stderr, "ERR: Couldn't bind to port %d\n", 
165                 CONFIG_HTTP_HTTPS_PORT);
166 #endif
167         exit(1);
168     }
169
170     addtoservers(active);
171     servers->ssl_ctx = ssl_ctx_new(CONFIG_HTTP_DEFAULT_SSL_OPTIONS, 
172                                 CONFIG_HTTP_SESSION_CACHE_SIZE);
173     servers->is_ssl = 1;
174
175 #if defined(CONFIG_HTTP_HAS_CGI)
176     addcgiext(CONFIG_HTTP_CGI_EXTENSIONS);
177 #endif
178
179 #if defined(CONFIG_HTTP_VERBOSE)
180 #if defined(CONFIG_HTTP_HAS_CGI)
181     printf("addcgiext %s\n", CONFIG_HTTP_CGI_EXTENSIONS); 
182 #endif
183     printf("%s: listening on ports %d (http) and %d (https)\n", 
184             server_version, CONFIG_HTTP_PORT, CONFIG_HTTP_HTTPS_PORT);
185     TTY_FLUSH();
186 #endif
187
188     ax_chdir();
189
190 #ifdef CONFIG_HTTP_ENABLE_DIFFERENT_USER
191     {
192         struct passwd *pd = getpwnam(CONFIG_HTTP_USER);
193
194         if (pd != NULL)
195         {
196             int res = setuid(pd->pw_uid);
197             res |= setgid(pd->pw_gid);
198
199 #if defined(CONFIG_HTTP_VERBOSE)
200             if (res == 0)
201             {
202                 printf("change to '%s' successful\n", CONFIG_HTTP_USER); 
203                 TTY_FLUSH();
204             }
205 #endif
206         }
207
208     }
209 #endif
210
211
212 #ifndef WIN32 
213 #ifdef CONFIG_HTTP_IS_DAEMON
214     if (fork() > 0)  /* parent will die */
215         exit(0);
216
217     setsid();
218 #endif
219 #endif
220
221     /* main loop */
222     while (1)
223     {
224         FD_ZERO(&rfds);
225         FD_ZERO(&wfds);
226         rnum = wnum = -1;
227         sp = servers;
228
229         while (sp != NULL)  /* read each server port */
230         {
231             FD_SET(sp->sd, &rfds);
232
233             if (sp->sd > rnum) 
234                 rnum = sp->sd;
235             sp = sp->next;
236         }
237
238         /* Add the established sockets */
239         tp = usedconns;
240         currtime = time(NULL);
241
242         while (tp != NULL) 
243         {
244             if (currtime > tp->timeout)     /* timed out? Kill it. */
245             {
246                 to = tp;
247                 tp = tp->next;
248                 removeconnection(to);
249                 continue;
250             }
251
252             if (tp->state == STATE_WANT_TO_READ_HEAD) 
253             {
254                 FD_SET(tp->networkdesc, &rfds);
255                 if (tp->networkdesc > rnum) 
256                     rnum = tp->networkdesc;
257             }
258
259             if (tp->state == STATE_WANT_TO_SEND_HEAD) 
260             {
261                 FD_SET(tp->networkdesc, &wfds);
262                 if (tp->networkdesc > wnum) 
263                     wnum = tp->networkdesc;
264             }
265
266             if (tp->state == STATE_WANT_TO_READ_FILE) 
267             {
268                 FD_SET(tp->filedesc, &rfds);
269                 if (tp->filedesc > rnum) 
270                     rnum = tp->filedesc;
271             }
272
273             if (tp->state == STATE_WANT_TO_SEND_FILE) 
274             {
275                 FD_SET(tp->networkdesc, &wfds);
276                 if (tp->networkdesc > wnum) 
277                     wnum = tp->networkdesc;
278             }
279
280 #if defined(CONFIG_HTTP_DIRECTORIES)
281             if (tp->state == STATE_DOING_DIR) 
282             {
283                 FD_SET(tp->networkdesc, &wfds);
284                 if (tp->networkdesc > wnum) 
285                     wnum = tp->networkdesc;
286             }
287 #endif
288             tp = tp->next;
289         }
290
291         active = select(wnum > rnum ? wnum+1 : rnum+1,
292                 rnum != -1 ? &rfds : NULL, 
293                 wnum != -1 ? &wfds : NULL,
294                 NULL, NULL);
295
296         /* New connection? */
297         sp = servers;
298         while (active > 0 && sp != NULL) 
299         {
300             if (FD_ISSET(sp->sd, &rfds)) 
301             {
302                 handlenewconnection(sp->sd, sp->is_ssl);
303                 active--;
304             }
305
306             sp = sp->next;
307         }
308
309         /* Handle the established sockets */
310         tp = usedconns;
311
312         while (active > 0 && tp != NULL) 
313         {
314             to = tp;
315             tp = tp->next;
316
317             if (to->state == STATE_WANT_TO_READ_HEAD &&
318                         FD_ISSET(to->networkdesc, &rfds)) 
319             {
320                 active--;
321 #if defined(CONFIG_HTTP_HAS_CGI)
322                 if (to->post_state)
323                     read_post_data(to);
324                 else
325 #endif
326                     procreadhead(to);
327             } 
328
329             if (to->state == STATE_WANT_TO_SEND_HEAD &&
330                         FD_ISSET(to->networkdesc, &wfds)) 
331             {
332                 active--;
333                 procsendhead(to);
334             } 
335
336             if (to->state == STATE_WANT_TO_READ_FILE && 
337                         FD_ISSET(to->filedesc, &rfds)) 
338             {
339                 active--;
340                 procreadfile(to);
341             } 
342
343             if (to->state == STATE_WANT_TO_SEND_FILE && 
344                         FD_ISSET(to->networkdesc, &wfds)) 
345             {
346                 active--;
347                 procsendfile(to);
348             }
349
350 #if defined(CONFIG_HTTP_DIRECTORIES)
351             if (to->state == STATE_DOING_DIR &&
352                         FD_ISSET(to->networkdesc, &wfds)) 
353             {
354                 active--;
355                 procdodir(to);
356             }
357 #endif
358         }
359     }
360
361     return 0;
362 }
363
364 #if defined(CONFIG_HTTP_HAS_CGI)
365 static void addcgiext(const char *cgi_exts)
366 {
367     char *cp = strdup(cgi_exts);
368
369     /* extenstions are comma separated */
370     do 
371     {
372         struct cgiextstruct *ex = (struct cgiextstruct *)
373                             malloc(sizeof(struct cgiextstruct));
374         ex->ext = cp;
375         ex->next = cgiexts;
376         cgiexts = ex;
377         if ((cp = strchr(cp, ',')) != NULL)
378             *cp++ = 0;
379     } while (cp != NULL);
380 }
381 #endif
382
383 static void addtoservers(int sd) 
384 {
385     struct serverstruct *tp = (struct serverstruct *)
386                             calloc(1, sizeof(struct serverstruct));
387     tp->next = servers;
388     tp->sd = sd;
389     servers = tp;
390 }
391
392 #ifdef HAVE_IPV6
393 static void handlenewconnection(int listenfd, int is_ssl) 
394 {
395     struct sockaddr_in6 their_addr;
396     int tp = sizeof(their_addr);
397     char ipbuf[100];
398     int connfd = accept(listenfd, (struct sockaddr *)&their_addr, &tp);
399
400     if (tp == sizeof(struct sockaddr_in6)) 
401         inet_ntop(AF_INET6, &their_addr.sin6_addr, ipbuf, sizeof(ipbuf));
402     else if (tp == sizeof(struct sockaddr_in)) 
403         inet_ntop(AF_INET, &(((struct sockaddr_in *)&their_addr)->sin_addr),
404                 ipbuf, sizeof(ipbuf));
405     else 
406         *ipbuf = '\0';
407
408     addconnection(connfd, ipbuf, is_ssl);
409 }
410
411 #else
412 static void handlenewconnection(int listenfd, int is_ssl) 
413 {
414     struct sockaddr_in their_addr;
415     socklen_t tp = sizeof(struct sockaddr_in);
416     int connfd = accept(listenfd, (struct sockaddr *)&their_addr, &tp);
417     addconnection(connfd, inet_ntoa(their_addr.sin_addr), is_ssl);
418 }
419 #endif
420
421 static int openlistener(int port) 
422 {
423     int sd;
424 #ifdef WIN32
425     char tp = 1;
426 #else
427     int tp = 1;
428 #endif
429 #ifndef HAVE_IPV6
430     struct sockaddr_in my_addr;
431
432     if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 
433         return -1;
434
435     memset(&my_addr, 0, sizeof(my_addr));
436     my_addr.sin_family = AF_INET;
437     my_addr.sin_port = htons((short)port);
438     my_addr.sin_addr.s_addr = INADDR_ANY;
439 #else
440     struct sockaddr_in6 my_addr;
441
442     if ((sd = socket(AF_INET6, SOCK_STREAM, 0)) == -1) 
443         return -1;
444
445     memset(&my_addr, 0, sizeof(my_addr));
446     my_addr.sin6_family = AF_INET6;
447     my_addr.sin6_port = htons(port);
448     my_addr.sin6_addr.s_addr = INADDR_ANY;
449 #endif
450
451     setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &tp, sizeof(tp));
452     if (bind(sd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1)
453     {
454         close(sd);
455         return -1;
456     }
457
458     listen(sd, BACKLOG);
459     return sd;
460 }
461
462 /* Wrapper function for strncpy() that guarantees
463    a null-terminated string. This is to avoid any possible
464    issues due to strncpy()'s behaviour.
465  */
466 char *my_strncpy(char *dest, const char *src, size_t n) 
467 {
468     strncpy(dest, src, n);
469     dest[n-1] = '\0';
470     return dest;
471 }
472
473 int isdir(const char *tpbuf) 
474 {
475     struct stat st;
476     char path[MAXREQUESTLENGTH];
477     strcpy(path, tpbuf);
478
479 #ifdef WIN32        /* win32 stat() can't handle trailing '\' */
480     if (path[strlen(path)-1] == '\\')
481         path[strlen(path)-1] = 0;
482 #endif
483
484     if (stat(path, &st) == -1) 
485         return 0;
486
487     if ((st.st_mode & S_IFMT) == S_IFDIR) 
488         return 1;
489
490     return 0;
491 }
492
493 static void addconnection(int sd, char *ip, int is_ssl) 
494 {
495     struct connstruct *tp;
496
497     /* Get ourselves a connstruct */
498     if (freeconns == NULL) 
499         tp = (struct connstruct *)calloc(1, sizeof(struct connstruct));
500     else 
501     {
502         tp = freeconns;
503         freeconns = tp->next;
504     }
505
506     /* Attach it to the used list */
507     tp->next = usedconns;
508     usedconns = tp;
509     tp->networkdesc = sd;
510
511     if (is_ssl)
512         tp->ssl = ssl_server_new(servers->ssl_ctx, sd);
513
514     tp->is_ssl = is_ssl;
515     tp->filedesc = -1;
516 #if defined(CONFIG_HTTP_HAS_DIRECTORIES)
517     tp->dirp = NULL;
518 #endif
519     *tp->actualfile = '\0';
520     *tp->filereq = '\0';
521     tp->state = STATE_WANT_TO_READ_HEAD;
522     tp->reqtype = TYPE_GET;
523     tp->close_when_done = 0;
524     tp->timeout = time(NULL) + CONFIG_HTTP_TIMEOUT;
525 #if defined(CONFIG_HTTP_HAS_CGI)
526     strcpy(tp->remote_addr, ip);
527 #endif
528 }
529
530 void removeconnection(struct connstruct *cn) 
531 {
532     struct connstruct *tp;
533     int shouldret = 0;
534
535     tp = usedconns;
536
537     if (tp == NULL || cn == NULL) 
538         shouldret = 1;
539     else if (tp == cn) 
540         usedconns = tp->next;
541     else 
542     {
543         while (tp != NULL) 
544         {
545             if (tp->next == cn) 
546             {
547                 tp->next = (tp->next)->next;
548                 shouldret = 0;
549                 break;
550             }
551
552             tp = tp->next;
553             shouldret = 1;
554         }
555     }
556
557     if (shouldret) 
558         return;
559
560     /* If we did, add it to the free list */
561     cn->next = freeconns;
562     freeconns = cn;
563
564     /* Close it all down */
565     if (cn->networkdesc != -1) 
566     {
567         if (cn->is_ssl) 
568         {
569             ssl_free(cn->ssl);
570             cn->ssl = NULL;
571         }
572
573         SOCKET_CLOSE(cn->networkdesc);
574     }
575
576     if (cn->filedesc != -1) 
577         close(cn->filedesc);
578
579 #if defined(CONFIG_HTTP_HAS_DIRECTORIES)
580     if (cn->dirp != NULL) 
581 #ifdef WIN32
582         FindClose(cn->dirp);
583 #else
584         closedir(cn->dirp);
585 #endif
586 #endif
587 }
588
589 /*
590  * Change directories one way or the other.
591  */
592 static void ax_chdir(void)
593 {
594     static char *webroot = CONFIG_HTTP_WEBROOT;
595
596     if (chdir(webroot))
597     {
598 #ifdef CONFIG_HTTP_VERBOSE
599         fprintf(stderr, "'%s' is not a directory\n", webroot);
600 #endif
601         exit(1);
602     }
603 }
604