extremely small and simple HTTP GET/HEAD-only web server for static content
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

262 lines
4.8 KiB

  1. /* See LICENSE file for copyright and license details. */
  2. #include <errno.h>
  3. #include <grp.h>
  4. #include <netinet/in.h>
  5. #include <pwd.h>
  6. #include <regex.h>
  7. #include <signal.h>
  8. #include <sys/resource.h>
  9. #include <sys/socket.h>
  10. #include <sys/types.h>
  11. #include <sys/wait.h>
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <time.h>
  16. #include <unistd.h>
  17. #include "http.h"
  18. #include "sock.h"
  19. #include "util.h"
  20. #include "config.h"
  21. static char *udsname;
  22. static void
  23. serve(int insock)
  24. {
  25. struct request r;
  26. struct sockaddr_storage in_sa;
  27. pid_t p;
  28. socklen_t in_sa_len;
  29. time_t t;
  30. enum status status;
  31. int infd;
  32. char inaddr[INET6_ADDRSTRLEN /* > INET_ADDRSTRLEN */];
  33. char tstmp[25];
  34. while (1) {
  35. /* accept incoming connections */
  36. in_sa_len = sizeof(in_sa);
  37. if ((infd = accept(insock, (struct sockaddr *)&in_sa,
  38. &in_sa_len)) < 0) {
  39. warn("accept:");
  40. continue;
  41. }
  42. /* fork and handle */
  43. switch ((p = fork())) {
  44. case -1:
  45. warn("fork:");
  46. break;
  47. case 0:
  48. close(insock);
  49. /* set connection timeout */
  50. if (sock_set_timeout(infd, 30)) {
  51. goto cleanup;
  52. }
  53. /* handle request */
  54. if (!(status = http_get_request(infd, &r))) {
  55. status = http_send_response(infd, &r);
  56. }
  57. /* write output to log */
  58. t = time(NULL);
  59. if (!strftime(tstmp, sizeof(tstmp), "%Y-%m-%dT%H:%M:%S",
  60. gmtime(&t))) {
  61. warn("strftime: Exceeded buffer capacity");
  62. goto cleanup;
  63. }
  64. if (sock_get_inaddr_str(&in_sa, inaddr, LEN(inaddr))) {
  65. goto cleanup;
  66. }
  67. printf("%s\t%s\t%d\t%s\t%s\n", tstmp, inaddr, status,
  68. r.field[REQ_HOST], r.target);
  69. cleanup:
  70. /* clean up and finish */
  71. shutdown(infd, SHUT_RD);
  72. shutdown(infd, SHUT_WR);
  73. close(infd);
  74. exit(0);
  75. default:
  76. /* close the connection in the parent */
  77. close(infd);
  78. }
  79. }
  80. }
  81. static void
  82. cleanup(void)
  83. {
  84. if (udsname)
  85. sock_rem_uds(udsname);
  86. }
  87. static void
  88. sigcleanup(int sig)
  89. {
  90. cleanup();
  91. kill(0, sig);
  92. _exit(1);
  93. }
  94. static void
  95. handlesignals(void(*hdl)(int))
  96. {
  97. struct sigaction sa;
  98. memset(&sa, 0, sizeof(sa));
  99. sigemptyset(&sa.sa_mask);
  100. sa.sa_handler = hdl;
  101. sigaction(SIGTERM, &sa, NULL);
  102. sigaction(SIGHUP, &sa, NULL);
  103. sigaction(SIGINT, &sa, NULL);
  104. sigaction(SIGQUIT, &sa, NULL);
  105. }
  106. static void
  107. usage(void)
  108. {
  109. die("usage: %s [-l | -L] [-v | -V] [[[-h host] [-p port]] | [-U sockfile]] "
  110. "[-d dir] [-u user] [-g group]", argv0);
  111. }
  112. int
  113. main(int argc, char *argv[])
  114. {
  115. struct passwd *pwd = NULL;
  116. struct group *grp = NULL;
  117. struct rlimit rlim;
  118. pid_t cpid, wpid;
  119. int i, insock, status = 0;
  120. ARGBEGIN {
  121. case 'd':
  122. servedir = EARGF(usage());
  123. break;
  124. case 'g':
  125. group = EARGF(usage());
  126. break;
  127. case 'h':
  128. host = EARGF(usage());
  129. break;
  130. case 'l':
  131. listdirs = 0;
  132. break;
  133. case 'L':
  134. listdirs = 1;
  135. break;
  136. case 'p':
  137. port = EARGF(usage());
  138. break;
  139. case 'u':
  140. user = EARGF(usage());
  141. break;
  142. case 'U':
  143. udsname = EARGF(usage());
  144. break;
  145. case 'v':
  146. vhosts = 0;
  147. break;
  148. case 'V':
  149. vhosts = 1;
  150. break;
  151. default:
  152. usage();
  153. } ARGEND
  154. if (argc) {
  155. usage();
  156. }
  157. if (udsname && (!access(udsname, F_OK) || errno != ENOENT)) {
  158. die("UNIX-domain socket: %s", errno ?
  159. strerror(errno) : "file exists");
  160. }
  161. /* compile and check the supplied vhost regexes */
  162. if (vhosts) {
  163. for (i = 0; i < LEN(vhost); i++) {
  164. if (regcomp(&vhost[i].re, vhost[i].regex,
  165. REG_EXTENDED | REG_ICASE | REG_NOSUB)) {
  166. die("regcomp '%s': invalid regex",
  167. vhost[i].regex);
  168. }
  169. }
  170. }
  171. /* raise the process limit */
  172. rlim.rlim_cur = rlim.rlim_max = maxnprocs;
  173. if (setrlimit(RLIMIT_NPROC, &rlim) < 0) {
  174. die("setrlimit RLIMIT_NPROC:");
  175. }
  176. /* validate user and group */
  177. errno = 0;
  178. if (user && !(pwd = getpwnam(user))) {
  179. die("getpwnam '%s':", user);
  180. }
  181. errno = 0;
  182. if (group && !(grp = getgrnam(group))) {
  183. die("getgrnam '%s':", group);
  184. }
  185. handlesignals(sigcleanup);
  186. /* bind socket */
  187. insock = udsname ? sock_get_uds(udsname, pwd->pw_uid, grp->gr_gid) :
  188. sock_get_ips(host, port);
  189. switch (cpid = fork()) {
  190. case -1:
  191. warn("fork:");
  192. break;
  193. case 0:
  194. /* restore default handlers */
  195. handlesignals(SIG_DFL);
  196. /* reap children automatically */
  197. if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) {
  198. die("signal: Failed to set SIG_IGN on SIGCHLD");
  199. }
  200. /* chroot */
  201. if (chdir(servedir) < 0) {
  202. die("chdir '%s':", servedir);
  203. }
  204. if (chroot(".") < 0) {
  205. die("chroot .:");
  206. }
  207. /* drop root */
  208. if (grp && setgroups(1, &(grp->gr_gid)) < 0) {
  209. die("setgroups:");
  210. }
  211. if (grp && setgid(grp->gr_gid) < 0) {
  212. die("setgid:");
  213. }
  214. if (pwd && setuid(pwd->pw_uid) < 0) {
  215. die("setuid:");
  216. }
  217. if (getuid() == 0) {
  218. die("won't run as root user", argv0);
  219. }
  220. if (getgid() == 0) {
  221. die("won't run as root group", argv0);
  222. }
  223. serve(insock);
  224. exit(0);
  225. default:
  226. while ((wpid = wait(&status)) > 0)
  227. ;
  228. }
  229. cleanup();
  230. return status;
  231. }