2 * Copyright (c) 2007, Cameron Rich
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
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.
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.
33 #include <sys/types.h>
40 struct serverstruct
*servers
;
41 struct connstruct
*usedconns
;
42 struct connstruct
*freeconns
;
43 const char * const server_version
= "axhttpd/"AXTLS_VERSION
;
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);
51 #if defined(CONFIG_HTTP_HAS_CGI)
52 struct cgiextstruct
*cgiexts
;
53 static void addcgiext(const char *tp
);
56 static void reaper(int sigtype
)
58 wait3(NULL
, WNOHANG
, NULL
);
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
)
67 struct serverstruct
*sp
;
68 struct connstruct
*tp
;
70 while (servers
!= NULL
)
73 ssl_ctx_free(servers
->ssl_ctx
);
80 while (freeconns
!= NULL
)
87 while (usedconns
!= NULL
)
94 #if defined(CONFIG_HTTP_HAS_CGI)
97 struct cgiextstruct
*cp
= cgiexts
->next
;
98 if (cp
== NULL
) /* last entry */
108 static void die(int sigtype
)
114 int main(int argc
, char *argv
[])
117 struct connstruct
*tp
, *to
;
118 struct serverstruct
*sp
;
119 int rnum
, wnum
, active
;
124 WORD wVersionRequested
= MAKEWORD(2, 2);
126 WSAStartup(wVersionRequested
,&wsaData
);
128 signal(SIGPIPE
, SIG_IGN
);
129 #if defined(CONFIG_HTTP_HAS_CGI)
130 signal(SIGCHLD
, reaper
);
132 #ifdef CONFIG_HTTP_VERBOSE
133 signal(SIGQUIT
, die
);
137 #ifdef CONFIG_HTTP_VERBOSE
138 signal(SIGTERM
, die
);
139 signal(SIGINT
, sigint_cleanup
);
143 for (i
= 0; i
< INITIAL_CONNECTION_SLOTS
; i
++)
146 freeconns
= (struct connstruct
*)calloc(1, sizeof(struct connstruct
));
147 freeconns
->next
= tp
;
150 if ((active
= openlistener(CONFIG_HTTP_PORT
)) == -1)
152 #ifdef CONFIG_HTTP_VERBOSE
153 fprintf(stderr
, "ERR: Couldn't bind to port %d\n",
159 addtoservers(active
);
161 if ((active
= openlistener(CONFIG_HTTP_HTTPS_PORT
)) == -1)
163 #ifdef CONFIG_HTTP_VERBOSE
164 fprintf(stderr
, "ERR: Couldn't bind to port %d\n",
165 CONFIG_HTTP_HTTPS_PORT
);
170 addtoservers(active
);
171 servers
->ssl_ctx
= ssl_ctx_new(CONFIG_HTTP_DEFAULT_SSL_OPTIONS
,
172 CONFIG_HTTP_SESSION_CACHE_SIZE
);
175 #if defined(CONFIG_HTTP_HAS_CGI)
176 addcgiext(CONFIG_HTTP_CGI_EXTENSIONS
);
179 #if defined(CONFIG_HTTP_VERBOSE)
180 #if defined(CONFIG_HTTP_HAS_CGI)
181 printf("addcgiext %s\n", CONFIG_HTTP_CGI_EXTENSIONS
);
183 printf("%s: listening on ports %d (http) and %d (https)\n",
184 server_version
, CONFIG_HTTP_PORT
, CONFIG_HTTP_HTTPS_PORT
);
190 #ifdef CONFIG_HTTP_ENABLE_DIFFERENT_USER
192 struct passwd
*pd
= getpwnam(CONFIG_HTTP_USER
);
196 int res
= setuid(pd
->pw_uid
);
197 res
|= setgid(pd
->pw_gid
);
199 #if defined(CONFIG_HTTP_VERBOSE)
202 printf("change to '%s' successful\n", CONFIG_HTTP_USER
);
213 #ifdef CONFIG_HTTP_IS_DAEMON
214 if (fork() > 0) /* parent will die */
229 while (sp
!= NULL
) /* read each server port */
231 FD_SET(sp
->sd
, &rfds
);
238 /* Add the established sockets */
240 currtime
= time(NULL
);
244 if (currtime
> tp
->timeout
) /* timed out? Kill it. */
248 removeconnection(to
);
252 if (tp
->state
== STATE_WANT_TO_READ_HEAD
)
254 FD_SET(tp
->networkdesc
, &rfds
);
255 if (tp
->networkdesc
> rnum
)
256 rnum
= tp
->networkdesc
;
259 if (tp
->state
== STATE_WANT_TO_SEND_HEAD
)
261 FD_SET(tp
->networkdesc
, &wfds
);
262 if (tp
->networkdesc
> wnum
)
263 wnum
= tp
->networkdesc
;
266 if (tp
->state
== STATE_WANT_TO_READ_FILE
)
268 FD_SET(tp
->filedesc
, &rfds
);
269 if (tp
->filedesc
> rnum
)
273 if (tp
->state
== STATE_WANT_TO_SEND_FILE
)
275 FD_SET(tp
->networkdesc
, &wfds
);
276 if (tp
->networkdesc
> wnum
)
277 wnum
= tp
->networkdesc
;
280 #if defined(CONFIG_HTTP_DIRECTORIES)
281 if (tp
->state
== STATE_DOING_DIR
)
283 FD_SET(tp
->networkdesc
, &wfds
);
284 if (tp
->networkdesc
> wnum
)
285 wnum
= tp
->networkdesc
;
291 active
= select(wnum
> rnum
? wnum
+1 : rnum
+1,
292 rnum
!= -1 ? &rfds
: NULL
,
293 wnum
!= -1 ? &wfds
: NULL
,
296 /* New connection? */
298 while (active
> 0 && sp
!= NULL
)
300 if (FD_ISSET(sp
->sd
, &rfds
))
302 handlenewconnection(sp
->sd
, sp
->is_ssl
);
309 /* Handle the established sockets */
312 while (active
> 0 && tp
!= NULL
)
317 if (to
->state
== STATE_WANT_TO_READ_HEAD
&&
318 FD_ISSET(to
->networkdesc
, &rfds
))
321 #if defined(CONFIG_HTTP_HAS_CGI)
329 if (to
->state
== STATE_WANT_TO_SEND_HEAD
&&
330 FD_ISSET(to
->networkdesc
, &wfds
))
336 if (to
->state
== STATE_WANT_TO_READ_FILE
&&
337 FD_ISSET(to
->filedesc
, &rfds
))
343 if (to
->state
== STATE_WANT_TO_SEND_FILE
&&
344 FD_ISSET(to
->networkdesc
, &wfds
))
350 #if defined(CONFIG_HTTP_DIRECTORIES)
351 if (to
->state
== STATE_DOING_DIR
&&
352 FD_ISSET(to
->networkdesc
, &wfds
))
364 #if defined(CONFIG_HTTP_HAS_CGI)
365 static void addcgiext(const char *cgi_exts
)
367 char *cp
= strdup(cgi_exts
);
369 /* extenstions are comma separated */
372 struct cgiextstruct
*ex
= (struct cgiextstruct
*)
373 malloc(sizeof(struct cgiextstruct
));
377 if ((cp
= strchr(cp
, ',')) != NULL
)
379 } while (cp
!= NULL
);
383 static void addtoservers(int sd
)
385 struct serverstruct
*tp
= (struct serverstruct
*)
386 calloc(1, sizeof(struct serverstruct
));
393 static void handlenewconnection(int listenfd
, int is_ssl
)
395 struct sockaddr_in6 their_addr
;
396 int tp
= sizeof(their_addr
);
398 int connfd
= accept(listenfd
, (struct sockaddr
*)&their_addr
, &tp
);
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
));
408 addconnection(connfd
, ipbuf
, is_ssl
);
412 static void handlenewconnection(int listenfd
, int is_ssl
)
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
);
421 static int openlistener(int port
)
430 struct sockaddr_in my_addr
;
432 if ((sd
= socket(AF_INET
, SOCK_STREAM
, 0)) == -1)
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
;
440 struct sockaddr_in6 my_addr
;
442 if ((sd
= socket(AF_INET6
, SOCK_STREAM
, 0)) == -1)
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
;
451 setsockopt(sd
, SOL_SOCKET
, SO_REUSEADDR
, &tp
, sizeof(tp
));
452 if (bind(sd
, (struct sockaddr
*)&my_addr
, sizeof(struct sockaddr
)) == -1)
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.
466 char *my_strncpy(char *dest
, const char *src
, size_t n
)
468 strncpy(dest
, src
, n
);
473 int isdir(const char *tpbuf
)
476 char path
[MAXREQUESTLENGTH
];
479 #ifdef WIN32 /* win32 stat() can't handle trailing '\' */
480 if (path
[strlen(path
)-1] == '\\')
481 path
[strlen(path
)-1] = 0;
484 if (stat(path
, &st
) == -1)
487 if ((st
.st_mode
& S_IFMT
) == S_IFDIR
)
493 static void addconnection(int sd
, char *ip
, int is_ssl
)
495 struct connstruct
*tp
;
497 /* Get ourselves a connstruct */
498 if (freeconns
== NULL
)
499 tp
= (struct connstruct
*)calloc(1, sizeof(struct connstruct
));
503 freeconns
= tp
->next
;
506 /* Attach it to the used list */
507 tp
->next
= usedconns
;
509 tp
->networkdesc
= sd
;
512 tp
->ssl
= ssl_server_new(servers
->ssl_ctx
, sd
);
516 #if defined(CONFIG_HTTP_HAS_DIRECTORIES)
519 *tp
->actualfile
= '\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
);
530 void removeconnection(struct connstruct
*cn
)
532 struct connstruct
*tp
;
537 if (tp
== NULL
|| cn
== NULL
)
540 usedconns
= tp
->next
;
547 tp
->next
= (tp
->next
)->next
;
560 /* If we did, add it to the free list */
561 cn
->next
= freeconns
;
564 /* Close it all down */
565 if (cn
->networkdesc
!= -1)
573 SOCKET_CLOSE(cn
->networkdesc
);
576 if (cn
->filedesc
!= -1)
579 #if defined(CONFIG_HTTP_HAS_DIRECTORIES)
580 if (cn
->dirp
!= NULL
)
590 * Change directories one way or the other.
592 static void ax_chdir(void)
594 static char *webroot
= CONFIG_HTTP_WEBROOT
;
598 #ifdef CONFIG_HTTP_VERBOSE
599 fprintf(stderr
, "'%s' is not a directory\n", webroot
);