14e794d5dSNick Mathewson /*
24e794d5dSNick Mathewson A trivial static http webserver using Libevent's evhttp.
34e794d5dSNick Mathewson
44e794d5dSNick Mathewson This is not the best code in the world, and it does some fairly stupid stuff
54e794d5dSNick Mathewson that you would never want to do in a production webserver. Caveat hackor!
64e794d5dSNick Mathewson
74e794d5dSNick Mathewson */
84e794d5dSNick Mathewson
995e2455cSNick Mathewson /* Compatibility for possible missing IPv6 declarations */
1095e2455cSNick Mathewson #include "../util-internal.h"
1195e2455cSNick Mathewson
124e794d5dSNick Mathewson #include <stdio.h>
134e794d5dSNick Mathewson #include <stdlib.h>
144e794d5dSNick Mathewson #include <string.h>
154e794d5dSNick Mathewson
164e794d5dSNick Mathewson #include <sys/types.h>
1718d03276SNick Mathewson #include <sys/stat.h>
184e794d5dSNick Mathewson
199f560bfaSNick Mathewson #ifdef _WIN32
204e794d5dSNick Mathewson #include <winsock2.h>
2118d03276SNick Mathewson #include <ws2tcpip.h>
224e794d5dSNick Mathewson #include <windows.h>
23b6309bccSAzat Khuzhin #include <getopt.h>
2418d03276SNick Mathewson #include <io.h>
2518d03276SNick Mathewson #include <fcntl.h>
2670be7d17SPeter Rosin #ifndef S_ISDIR
2770be7d17SPeter Rosin #define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
2870be7d17SPeter Rosin #endif
297c4da937SAzat Khuzhin #else /* !_WIN32 */
304e794d5dSNick Mathewson #include <sys/stat.h>
314e794d5dSNick Mathewson #include <sys/socket.h>
324e794d5dSNick Mathewson #include <fcntl.h>
334e794d5dSNick Mathewson #include <unistd.h>
344e794d5dSNick Mathewson #include <dirent.h>
357c4da937SAzat Khuzhin #endif /* _WIN32 */
36b6309bccSAzat Khuzhin #include <signal.h>
374e794d5dSNick Mathewson
387c4da937SAzat Khuzhin #ifdef EVENT__HAVE_SYS_UN_H
397c4da937SAzat Khuzhin #include <sys/un.h>
407c4da937SAzat Khuzhin #endif
417c4da937SAzat Khuzhin #ifdef EVENT__HAVE_AFUNIX_H
427c4da937SAzat Khuzhin #include <afunix.h>
437c4da937SAzat Khuzhin #endif
447c4da937SAzat Khuzhin
454e794d5dSNick Mathewson #include <event2/event.h>
464e794d5dSNick Mathewson #include <event2/http.h>
477c4da937SAzat Khuzhin #include <event2/listener.h>
484e794d5dSNick Mathewson #include <event2/buffer.h>
494e794d5dSNick Mathewson #include <event2/util.h>
504e794d5dSNick Mathewson #include <event2/keyvalq_struct.h>
514e794d5dSNick Mathewson
52b6309bccSAzat Khuzhin #ifdef _WIN32
53b6309bccSAzat Khuzhin #include <event2/thread.h>
547c4da937SAzat Khuzhin #endif /* _WIN32 */
55b6309bccSAzat Khuzhin
5668120d9bSNick Mathewson #ifdef EVENT__HAVE_NETINET_IN_H
57ad66dfd0SNick Mathewson #include <netinet/in.h>
5810c834c4SHarlan Stenn # ifdef _XOPEN_SOURCE_EXTENDED
5910c834c4SHarlan Stenn # include <arpa/inet.h>
6010c834c4SHarlan Stenn # endif
61ad66dfd0SNick Mathewson #endif
62ad66dfd0SNick Mathewson
639f560bfaSNick Mathewson #ifdef _WIN32
646810908aSNick Mathewson #ifndef stat
6518d03276SNick Mathewson #define stat _stat
666810908aSNick Mathewson #endif
676810908aSNick Mathewson #ifndef fstat
6818d03276SNick Mathewson #define fstat _fstat
696810908aSNick Mathewson #endif
706810908aSNick Mathewson #ifndef open
7118d03276SNick Mathewson #define open _open
726810908aSNick Mathewson #endif
736810908aSNick Mathewson #ifndef close
7418d03276SNick Mathewson #define close _close
756810908aSNick Mathewson #endif
766810908aSNick Mathewson #ifndef O_RDONLY
7718d03276SNick Mathewson #define O_RDONLY _O_RDONLY
7818d03276SNick Mathewson #endif
797c4da937SAzat Khuzhin #endif /* _WIN32 */
8018d03276SNick Mathewson
814e794d5dSNick Mathewson char uri_root[512];
824e794d5dSNick Mathewson
834e794d5dSNick Mathewson static const struct table_entry {
844e794d5dSNick Mathewson const char *extension;
854e794d5dSNick Mathewson const char *content_type;
864e794d5dSNick Mathewson } content_type_table[] = {
874e794d5dSNick Mathewson { "txt", "text/plain" },
884e794d5dSNick Mathewson { "c", "text/plain" },
894e794d5dSNick Mathewson { "h", "text/plain" },
904e794d5dSNick Mathewson { "html", "text/html" },
914e794d5dSNick Mathewson { "htm", "text/htm" },
924e794d5dSNick Mathewson { "css", "text/css" },
934e794d5dSNick Mathewson { "gif", "image/gif" },
944e794d5dSNick Mathewson { "jpg", "image/jpeg" },
954e794d5dSNick Mathewson { "jpeg", "image/jpeg" },
964e794d5dSNick Mathewson { "png", "image/png" },
974e794d5dSNick Mathewson { "pdf", "application/pdf" },
9823f9a20eSjohnsonlee { "ps", "application/postscript" },
994e794d5dSNick Mathewson { NULL, NULL },
1004e794d5dSNick Mathewson };
1014e794d5dSNick Mathewson
102*4a6f1ccfSDavid Disseldorp struct options {
103b6309bccSAzat Khuzhin int port;
104b6309bccSAzat Khuzhin int iocp;
105b6309bccSAzat Khuzhin int verbose;
1067c4da937SAzat Khuzhin
1077c4da937SAzat Khuzhin int unlink;
1087c4da937SAzat Khuzhin const char *unixsock;
109*4a6f1ccfSDavid Disseldorp const char *docroot;
110b6309bccSAzat Khuzhin };
111b6309bccSAzat Khuzhin
1124e794d5dSNick Mathewson /* Try to guess a good content-type for 'path' */
1134e794d5dSNick Mathewson static const char *
guess_content_type(const char * path)1144e794d5dSNick Mathewson guess_content_type(const char *path)
1154e794d5dSNick Mathewson {
1164e794d5dSNick Mathewson const char *last_period, *extension;
1174e794d5dSNick Mathewson const struct table_entry *ent;
1184e794d5dSNick Mathewson last_period = strrchr(path, '.');
1194e794d5dSNick Mathewson if (!last_period || strchr(last_period, '/'))
1204e794d5dSNick Mathewson goto not_found; /* no exension */
1214e794d5dSNick Mathewson extension = last_period + 1;
1224e794d5dSNick Mathewson for (ent = &content_type_table[0]; ent->extension; ++ent) {
1234e794d5dSNick Mathewson if (!evutil_ascii_strcasecmp(ent->extension, extension))
1244e794d5dSNick Mathewson return ent->content_type;
1254e794d5dSNick Mathewson }
1264e794d5dSNick Mathewson
1274e794d5dSNick Mathewson not_found:
1284e794d5dSNick Mathewson return "application/misc";
1294e794d5dSNick Mathewson }
1304e794d5dSNick Mathewson
1314e794d5dSNick Mathewson /* Callback used for the /dump URI, and for every non-GET request:
1324e794d5dSNick Mathewson * dumps all information to stdout and gives back a trivial 200 ok */
1334e794d5dSNick Mathewson static void
dump_request_cb(struct evhttp_request * req,void * arg)1344e794d5dSNick Mathewson dump_request_cb(struct evhttp_request *req, void *arg)
1354e794d5dSNick Mathewson {
1364e794d5dSNick Mathewson const char *cmdtype;
1374e794d5dSNick Mathewson struct evkeyvalq *headers;
1384e794d5dSNick Mathewson struct evkeyval *header;
1394e794d5dSNick Mathewson struct evbuffer *buf;
1404e794d5dSNick Mathewson
1414e794d5dSNick Mathewson switch (evhttp_request_get_command(req)) {
1424e794d5dSNick Mathewson case EVHTTP_REQ_GET: cmdtype = "GET"; break;
1434e794d5dSNick Mathewson case EVHTTP_REQ_POST: cmdtype = "POST"; break;
1444e794d5dSNick Mathewson case EVHTTP_REQ_HEAD: cmdtype = "HEAD"; break;
1454e794d5dSNick Mathewson case EVHTTP_REQ_PUT: cmdtype = "PUT"; break;
1464e794d5dSNick Mathewson case EVHTTP_REQ_DELETE: cmdtype = "DELETE"; break;
1474e794d5dSNick Mathewson case EVHTTP_REQ_OPTIONS: cmdtype = "OPTIONS"; break;
1484e794d5dSNick Mathewson case EVHTTP_REQ_TRACE: cmdtype = "TRACE"; break;
1494e794d5dSNick Mathewson case EVHTTP_REQ_CONNECT: cmdtype = "CONNECT"; break;
1504e794d5dSNick Mathewson case EVHTTP_REQ_PATCH: cmdtype = "PATCH"; break;
1514e794d5dSNick Mathewson default: cmdtype = "unknown"; break;
1524e794d5dSNick Mathewson }
1534e794d5dSNick Mathewson
1544e794d5dSNick Mathewson printf("Received a %s request for %s\nHeaders:\n",
1554e794d5dSNick Mathewson cmdtype, evhttp_request_get_uri(req));
1564e794d5dSNick Mathewson
1574e794d5dSNick Mathewson headers = evhttp_request_get_input_headers(req);
1584e794d5dSNick Mathewson for (header = headers->tqh_first; header;
1594e794d5dSNick Mathewson header = header->next.tqe_next) {
1604e794d5dSNick Mathewson printf(" %s: %s\n", header->key, header->value);
1614e794d5dSNick Mathewson }
1624e794d5dSNick Mathewson
1634e794d5dSNick Mathewson buf = evhttp_request_get_input_buffer(req);
1644e794d5dSNick Mathewson puts("Input data: <<<");
1654e794d5dSNick Mathewson while (evbuffer_get_length(buf)) {
1664e794d5dSNick Mathewson int n;
1674e794d5dSNick Mathewson char cbuf[128];
168c322c207SGyepi Sam n = evbuffer_remove(buf, cbuf, sizeof(cbuf));
1697c11e51eSHarlan Stenn if (n > 0)
1707206e8cdSNick Mathewson (void) fwrite(cbuf, 1, n, stdout);
1714e794d5dSNick Mathewson }
1724e794d5dSNick Mathewson puts(">>>");
1734e794d5dSNick Mathewson
1744e794d5dSNick Mathewson evhttp_send_reply(req, 200, "OK", NULL);
1754e794d5dSNick Mathewson }
1764e794d5dSNick Mathewson
1774e794d5dSNick Mathewson /* This callback gets invoked when we get any http request that doesn't match
1784e794d5dSNick Mathewson * any other callback. Like any evhttp server callback, it has a simple job:
1794e794d5dSNick Mathewson * it must eventually call evhttp_send_error() or evhttp_send_reply().
1804e794d5dSNick Mathewson */
1814e794d5dSNick Mathewson static void
send_document_cb(struct evhttp_request * req,void * arg)1824e794d5dSNick Mathewson send_document_cb(struct evhttp_request *req, void *arg)
1834e794d5dSNick Mathewson {
1842e9f6655SNick Mathewson struct evbuffer *evb = NULL;
185*4a6f1ccfSDavid Disseldorp struct options *o = arg;
1864e794d5dSNick Mathewson const char *uri = evhttp_request_get_uri(req);
1874e794d5dSNick Mathewson struct evhttp_uri *decoded = NULL;
1884e794d5dSNick Mathewson const char *path;
1894e794d5dSNick Mathewson char *decoded_path;
1904e794d5dSNick Mathewson char *whole_path = NULL;
1914e794d5dSNick Mathewson size_t len;
1924e794d5dSNick Mathewson int fd = -1;
1934e794d5dSNick Mathewson struct stat st;
1944e794d5dSNick Mathewson
1954e794d5dSNick Mathewson if (evhttp_request_get_command(req) != EVHTTP_REQ_GET) {
1964e794d5dSNick Mathewson dump_request_cb(req, arg);
1974e794d5dSNick Mathewson return;
1984e794d5dSNick Mathewson }
1994e794d5dSNick Mathewson
2004e794d5dSNick Mathewson printf("Got a GET request for <%s>\n", uri);
2014e794d5dSNick Mathewson
2024e794d5dSNick Mathewson /* Decode the URI */
2034e794d5dSNick Mathewson decoded = evhttp_uri_parse(uri);
2044e794d5dSNick Mathewson if (!decoded) {
2054e794d5dSNick Mathewson printf("It's not a good URI. Sending BADREQUEST\n");
2064e794d5dSNick Mathewson evhttp_send_error(req, HTTP_BADREQUEST, 0);
2074e794d5dSNick Mathewson return;
2084e794d5dSNick Mathewson }
2094e794d5dSNick Mathewson
2104e794d5dSNick Mathewson /* Let's see what path the user asked for. */
2114e794d5dSNick Mathewson path = evhttp_uri_get_path(decoded);
2124e794d5dSNick Mathewson if (!path) path = "/";
2134e794d5dSNick Mathewson
2144e794d5dSNick Mathewson /* We need to decode it, to see what path the user really wanted. */
2154e794d5dSNick Mathewson decoded_path = evhttp_uridecode(path, 0, NULL);
2167c11e51eSHarlan Stenn if (decoded_path == NULL)
2177c11e51eSHarlan Stenn goto err;
2184e794d5dSNick Mathewson /* Don't allow any ".."s in the path, to avoid exposing stuff outside
2194e794d5dSNick Mathewson * of the docroot. This test is both overzealous and underzealous:
2204e794d5dSNick Mathewson * it forbids aceptable paths like "/this/one..here", but it doesn't
2214e794d5dSNick Mathewson * do anything to prevent symlink following." */
2224e794d5dSNick Mathewson if (strstr(decoded_path, ".."))
2234e794d5dSNick Mathewson goto err;
2244e794d5dSNick Mathewson
225*4a6f1ccfSDavid Disseldorp len = strlen(decoded_path)+strlen(o->docroot)+2;
2264e794d5dSNick Mathewson if (!(whole_path = malloc(len))) {
2274e794d5dSNick Mathewson perror("malloc");
2284e794d5dSNick Mathewson goto err;
2294e794d5dSNick Mathewson }
230*4a6f1ccfSDavid Disseldorp evutil_snprintf(whole_path, len, "%s/%s", o->docroot, decoded_path);
2314e794d5dSNick Mathewson
2324e794d5dSNick Mathewson if (stat(whole_path, &st)<0) {
2334e794d5dSNick Mathewson goto err;
2344e794d5dSNick Mathewson }
2354e794d5dSNick Mathewson
2364e794d5dSNick Mathewson /* This holds the content we're sending. */
2374e794d5dSNick Mathewson evb = evbuffer_new();
2384e794d5dSNick Mathewson
2394e794d5dSNick Mathewson if (S_ISDIR(st.st_mode)) {
2404e794d5dSNick Mathewson /* If it's a directory, read the comments and make a little
2414e794d5dSNick Mathewson * index page */
2429f560bfaSNick Mathewson #ifdef _WIN32
24318d03276SNick Mathewson HANDLE d;
24418d03276SNick Mathewson WIN32_FIND_DATAA ent;
24518d03276SNick Mathewson char *pattern;
24618d03276SNick Mathewson size_t dirlen;
24718d03276SNick Mathewson #else
2484e794d5dSNick Mathewson DIR *d;
2494e794d5dSNick Mathewson struct dirent *ent;
25018d03276SNick Mathewson #endif
2514e794d5dSNick Mathewson const char *trailing_slash = "";
2524e794d5dSNick Mathewson
2534e794d5dSNick Mathewson if (!strlen(path) || path[strlen(path)-1] != '/')
2544e794d5dSNick Mathewson trailing_slash = "/";
2554e794d5dSNick Mathewson
2569f560bfaSNick Mathewson #ifdef _WIN32
25718d03276SNick Mathewson dirlen = strlen(whole_path);
25818d03276SNick Mathewson pattern = malloc(dirlen+3);
25918d03276SNick Mathewson memcpy(pattern, whole_path, dirlen);
26018d03276SNick Mathewson pattern[dirlen] = '\\';
26118d03276SNick Mathewson pattern[dirlen+1] = '*';
26218d03276SNick Mathewson pattern[dirlen+2] = '\0';
26318d03276SNick Mathewson d = FindFirstFileA(pattern, &ent);
26418d03276SNick Mathewson free(pattern);
26518d03276SNick Mathewson if (d == INVALID_HANDLE_VALUE)
26618d03276SNick Mathewson goto err;
26718d03276SNick Mathewson #else
2684e794d5dSNick Mathewson if (!(d = opendir(whole_path)))
2694e794d5dSNick Mathewson goto err;
27018d03276SNick Mathewson #endif
2714e794d5dSNick Mathewson
2726d72bdcaSNick Mathewson evbuffer_add_printf(evb,
2736d72bdcaSNick Mathewson "<!DOCTYPE html>\n"
2746d72bdcaSNick Mathewson "<html>\n <head>\n"
2756d72bdcaSNick Mathewson " <meta charset='utf-8'>\n"
2764e794d5dSNick Mathewson " <title>%s</title>\n"
2776171e1c2SAzat Khuzhin " <base href='%s%s'>\n"
2784e794d5dSNick Mathewson " </head>\n"
2794e794d5dSNick Mathewson " <body>\n"
2804e794d5dSNick Mathewson " <h1>%s</h1>\n"
2814e794d5dSNick Mathewson " <ul>\n",
2824e794d5dSNick Mathewson decoded_path, /* XXX html-escape this. */
2836171e1c2SAzat Khuzhin path, /* XXX html-escape this? */
2844e794d5dSNick Mathewson trailing_slash,
2854e794d5dSNick Mathewson decoded_path /* XXX html-escape this */);
2869f560bfaSNick Mathewson #ifdef _WIN32
28718d03276SNick Mathewson do {
28818d03276SNick Mathewson const char *name = ent.cFileName;
28918d03276SNick Mathewson #else
2904e794d5dSNick Mathewson while ((ent = readdir(d))) {
29118d03276SNick Mathewson const char *name = ent->d_name;
29218d03276SNick Mathewson #endif
2934e794d5dSNick Mathewson evbuffer_add_printf(evb,
2944e794d5dSNick Mathewson " <li><a href=\"%s\">%s</a>\n",
29518d03276SNick Mathewson name, name);/* XXX escape this */
2969f560bfaSNick Mathewson #ifdef _WIN32
29713fd242dSNick Mathewson } while (FindNextFileA(d, &ent));
29818d03276SNick Mathewson #else
2994e794d5dSNick Mathewson }
30018d03276SNick Mathewson #endif
3014e794d5dSNick Mathewson evbuffer_add_printf(evb, "</ul></body></html>\n");
3029f560bfaSNick Mathewson #ifdef _WIN32
3036466e88aSNick Mathewson FindClose(d);
30418d03276SNick Mathewson #else
3054e794d5dSNick Mathewson closedir(d);
30618d03276SNick Mathewson #endif
3074e794d5dSNick Mathewson evhttp_add_header(evhttp_request_get_output_headers(req),
3084e794d5dSNick Mathewson "Content-Type", "text/html");
3094e794d5dSNick Mathewson } else {
3104e794d5dSNick Mathewson /* Otherwise it's a file; add it to the buffer to get
3114e794d5dSNick Mathewson * sent via sendfile */
3124e794d5dSNick Mathewson const char *type = guess_content_type(decoded_path);
3134881778cSNick Mathewson if ((fd = open(whole_path, O_RDONLY)) < 0) {
3144e794d5dSNick Mathewson perror("open");
3154e794d5dSNick Mathewson goto err;
3164e794d5dSNick Mathewson }
3174e794d5dSNick Mathewson
3184e794d5dSNick Mathewson if (fstat(fd, &st)<0) {
3194e794d5dSNick Mathewson /* Make sure the length still matches, now that we
3204e794d5dSNick Mathewson * opened the file :/ */
3214e794d5dSNick Mathewson perror("fstat");
3224e794d5dSNick Mathewson goto err;
3234e794d5dSNick Mathewson }
3244e794d5dSNick Mathewson evhttp_add_header(evhttp_request_get_output_headers(req),
3254e794d5dSNick Mathewson "Content-Type", type);
3264e794d5dSNick Mathewson evbuffer_add_file(evb, fd, 0, st.st_size);
3274e794d5dSNick Mathewson }
3284e794d5dSNick Mathewson
3294e794d5dSNick Mathewson evhttp_send_reply(req, 200, "OK", evb);
3302e9f6655SNick Mathewson goto done;
3314e794d5dSNick Mathewson err:
3324e794d5dSNick Mathewson evhttp_send_error(req, 404, "Document was not found");
3334e794d5dSNick Mathewson if (fd>=0)
3344e794d5dSNick Mathewson close(fd);
3352e9f6655SNick Mathewson done:
3364e794d5dSNick Mathewson if (decoded)
3374e794d5dSNick Mathewson evhttp_uri_free(decoded);
3384e794d5dSNick Mathewson if (decoded_path)
3394e794d5dSNick Mathewson free(decoded_path);
3404e794d5dSNick Mathewson if (whole_path)
3414e794d5dSNick Mathewson free(whole_path);
3422e9f6655SNick Mathewson if (evb)
3432e9f6655SNick Mathewson evbuffer_free(evb);
3444e794d5dSNick Mathewson }
3454e794d5dSNick Mathewson
3467c4da937SAzat Khuzhin static void
print_usage(FILE * out,const char * prog,int exit_code)3477c4da937SAzat Khuzhin print_usage(FILE *out, const char *prog, int exit_code)
3487c4da937SAzat Khuzhin {
349*4a6f1ccfSDavid Disseldorp fprintf(out,
350*4a6f1ccfSDavid Disseldorp "Syntax: %s [ OPTS ] <docroot>\n"
351*4a6f1ccfSDavid Disseldorp " -p - port\n"
352*4a6f1ccfSDavid Disseldorp " -U - bind to unix socket\n"
353*4a6f1ccfSDavid Disseldorp " -u - unlink unix socket before bind\n"
354*4a6f1ccfSDavid Disseldorp " -I - IOCP\n"
355*4a6f1ccfSDavid Disseldorp " -v - verbosity, enables libevent debug logging too\n", prog);
3567c4da937SAzat Khuzhin exit(exit_code);
3577c4da937SAzat Khuzhin }
358b6309bccSAzat Khuzhin static struct options
parse_opts(int argc,char ** argv)359b6309bccSAzat Khuzhin parse_opts(int argc, char **argv)
3604e794d5dSNick Mathewson {
361b6309bccSAzat Khuzhin struct options o;
362b6309bccSAzat Khuzhin int opt;
363b6309bccSAzat Khuzhin
364b6309bccSAzat Khuzhin memset(&o, 0, sizeof(o));
365b6309bccSAzat Khuzhin
3667c4da937SAzat Khuzhin while ((opt = getopt(argc, argv, "hp:U:uIv")) != -1) {
367b6309bccSAzat Khuzhin switch (opt) {
368b6309bccSAzat Khuzhin case 'p': o.port = atoi(optarg); break;
3697c4da937SAzat Khuzhin case 'U': o.unixsock = optarg; break;
3707c4da937SAzat Khuzhin case 'u': o.unlink = 1; break;
371b6309bccSAzat Khuzhin case 'I': o.iocp = 1; break;
372b6309bccSAzat Khuzhin case 'v': ++o.verbose; break;
3737c4da937SAzat Khuzhin case 'h': print_usage(stdout, argv[0], 0); break;
374b6309bccSAzat Khuzhin default : fprintf(stderr, "Unknown option %c\n", opt); break;
375b6309bccSAzat Khuzhin }
376b6309bccSAzat Khuzhin }
377b6309bccSAzat Khuzhin
378b6309bccSAzat Khuzhin if (optind >= argc || (argc - optind) > 1) {
3797c4da937SAzat Khuzhin print_usage(stdout, argv[0], 1);
380b6309bccSAzat Khuzhin }
381*4a6f1ccfSDavid Disseldorp o.docroot = argv[optind];
382b6309bccSAzat Khuzhin
383b6309bccSAzat Khuzhin return o;
384b6309bccSAzat Khuzhin }
385b6309bccSAzat Khuzhin
386b6309bccSAzat Khuzhin static void
do_term(int sig,short events,void * arg)387b6309bccSAzat Khuzhin do_term(int sig, short events, void *arg)
388b6309bccSAzat Khuzhin {
389b6309bccSAzat Khuzhin struct event_base *base = arg;
390b6309bccSAzat Khuzhin event_base_loopbreak(base);
391b6309bccSAzat Khuzhin fprintf(stderr, "Got %i, Terminating\n", sig);
3924e794d5dSNick Mathewson }
3934e794d5dSNick Mathewson
3947c4da937SAzat Khuzhin static int
display_listen_sock(struct evhttp_bound_socket * handle)3957c4da937SAzat Khuzhin display_listen_sock(struct evhttp_bound_socket *handle)
3967c4da937SAzat Khuzhin {
3977c4da937SAzat Khuzhin struct sockaddr_storage ss;
3987c4da937SAzat Khuzhin evutil_socket_t fd;
3997c4da937SAzat Khuzhin ev_socklen_t socklen = sizeof(ss);
4007c4da937SAzat Khuzhin char addrbuf[128];
4017c4da937SAzat Khuzhin void *inaddr;
4027c4da937SAzat Khuzhin const char *addr;
4037c4da937SAzat Khuzhin int got_port = -1;
4047c4da937SAzat Khuzhin
4057c4da937SAzat Khuzhin fd = evhttp_bound_socket_get_fd(handle);
4067c4da937SAzat Khuzhin memset(&ss, 0, sizeof(ss));
4077c4da937SAzat Khuzhin if (getsockname(fd, (struct sockaddr *)&ss, &socklen)) {
4087c4da937SAzat Khuzhin perror("getsockname() failed");
4097c4da937SAzat Khuzhin return 1;
4107c4da937SAzat Khuzhin }
4117c4da937SAzat Khuzhin
4127c4da937SAzat Khuzhin if (ss.ss_family == AF_INET) {
4137c4da937SAzat Khuzhin got_port = ntohs(((struct sockaddr_in*)&ss)->sin_port);
4147c4da937SAzat Khuzhin inaddr = &((struct sockaddr_in*)&ss)->sin_addr;
4157c4da937SAzat Khuzhin } else if (ss.ss_family == AF_INET6) {
4167c4da937SAzat Khuzhin got_port = ntohs(((struct sockaddr_in6*)&ss)->sin6_port);
4177c4da937SAzat Khuzhin inaddr = &((struct sockaddr_in6*)&ss)->sin6_addr;
4187c4da937SAzat Khuzhin }
4197c4da937SAzat Khuzhin #ifdef EVENT__HAVE_STRUCT_SOCKADDR_UN
4207c4da937SAzat Khuzhin else if (ss.ss_family == AF_UNIX) {
4217c4da937SAzat Khuzhin printf("Listening on <%s>\n", ((struct sockaddr_un*)&ss)->sun_path);
4227c4da937SAzat Khuzhin return 0;
4237c4da937SAzat Khuzhin }
4247c4da937SAzat Khuzhin #endif
4257c4da937SAzat Khuzhin else {
4267c4da937SAzat Khuzhin fprintf(stderr, "Weird address family %d\n",
4277c4da937SAzat Khuzhin ss.ss_family);
4287c4da937SAzat Khuzhin return 1;
4297c4da937SAzat Khuzhin }
4307c4da937SAzat Khuzhin
4317c4da937SAzat Khuzhin addr = evutil_inet_ntop(ss.ss_family, inaddr, addrbuf,
4327c4da937SAzat Khuzhin sizeof(addrbuf));
4337c4da937SAzat Khuzhin if (addr) {
4347c4da937SAzat Khuzhin printf("Listening on %s:%d\n", addr, got_port);
4357c4da937SAzat Khuzhin evutil_snprintf(uri_root, sizeof(uri_root),
4367c4da937SAzat Khuzhin "http://%s:%d",addr,got_port);
4377c4da937SAzat Khuzhin } else {
4387c4da937SAzat Khuzhin fprintf(stderr, "evutil_inet_ntop failed\n");
4397c4da937SAzat Khuzhin return 1;
4407c4da937SAzat Khuzhin }
4417c4da937SAzat Khuzhin
4427c4da937SAzat Khuzhin return 0;
4437c4da937SAzat Khuzhin }
4447c4da937SAzat Khuzhin
4454e794d5dSNick Mathewson int
main(int argc,char ** argv)4464e794d5dSNick Mathewson main(int argc, char **argv)
4474e794d5dSNick Mathewson {
448b6309bccSAzat Khuzhin struct event_config *cfg = NULL;
449b6309bccSAzat Khuzhin struct event_base *base = NULL;
450b6309bccSAzat Khuzhin struct evhttp *http = NULL;
451b6309bccSAzat Khuzhin struct evhttp_bound_socket *handle = NULL;
4527c4da937SAzat Khuzhin struct evconnlistener *lev = NULL;
453b6309bccSAzat Khuzhin struct event *term = NULL;
454b6309bccSAzat Khuzhin struct options o = parse_opts(argc, argv);
455b6309bccSAzat Khuzhin int ret = 0;
4564e794d5dSNick Mathewson
4579f560bfaSNick Mathewson #ifdef _WIN32
458b6309bccSAzat Khuzhin {
459b6309bccSAzat Khuzhin WORD wVersionRequested;
460b6309bccSAzat Khuzhin WSADATA wsaData;
461b6309bccSAzat Khuzhin wVersionRequested = MAKEWORD(2, 2);
462b6309bccSAzat Khuzhin WSAStartup(wVersionRequested, &wsaData);
4634e794d5dSNick Mathewson }
464b6309bccSAzat Khuzhin #else
465b6309bccSAzat Khuzhin if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
466b6309bccSAzat Khuzhin ret = 1;
467b6309bccSAzat Khuzhin goto err;
468b6309bccSAzat Khuzhin }
469b6309bccSAzat Khuzhin #endif
4704e794d5dSNick Mathewson
471b6309bccSAzat Khuzhin setbuf(stdout, NULL);
472b6309bccSAzat Khuzhin setbuf(stderr, NULL);
473b6309bccSAzat Khuzhin
4747c4da937SAzat Khuzhin /** Read env like in regress */
475b6309bccSAzat Khuzhin if (o.verbose || getenv("EVENT_DEBUG_LOGGING_ALL"))
476b6309bccSAzat Khuzhin event_enable_debug_logging(EVENT_DBG_ALL);
477b6309bccSAzat Khuzhin
478b6309bccSAzat Khuzhin cfg = event_config_new();
479b6309bccSAzat Khuzhin #ifdef _WIN32
480b6309bccSAzat Khuzhin if (o.iocp) {
48184f6fb41SAzat Khuzhin #ifdef EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED
482b6309bccSAzat Khuzhin evthread_use_windows_threads();
483b6309bccSAzat Khuzhin event_config_set_num_cpus_hint(cfg, 8);
48484f6fb41SAzat Khuzhin #endif
48584f6fb41SAzat Khuzhin event_config_set_flag(cfg, EVENT_BASE_FLAG_STARTUP_IOCP);
486b6309bccSAzat Khuzhin }
487b6309bccSAzat Khuzhin #endif
488b6309bccSAzat Khuzhin
489b6309bccSAzat Khuzhin base = event_base_new_with_config(cfg);
4904e794d5dSNick Mathewson if (!base) {
4914e794d5dSNick Mathewson fprintf(stderr, "Couldn't create an event_base: exiting\n");
492b6309bccSAzat Khuzhin ret = 1;
4934e794d5dSNick Mathewson }
494b6309bccSAzat Khuzhin event_config_free(cfg);
495b6309bccSAzat Khuzhin cfg = NULL;
4964e794d5dSNick Mathewson
4974e794d5dSNick Mathewson /* Create a new evhttp object to handle requests. */
4984e794d5dSNick Mathewson http = evhttp_new(base);
4994e794d5dSNick Mathewson if (!http) {
5004e794d5dSNick Mathewson fprintf(stderr, "couldn't create evhttp. Exiting.\n");
501b6309bccSAzat Khuzhin ret = 1;
5024e794d5dSNick Mathewson }
5034e794d5dSNick Mathewson
5044e794d5dSNick Mathewson /* The /dump URI will dump all requests to stdout and say 200 ok. */
5054e794d5dSNick Mathewson evhttp_set_cb(http, "/dump", dump_request_cb, NULL);
5064e794d5dSNick Mathewson
5074e794d5dSNick Mathewson /* We want to accept arbitrary requests, so we need to set a "generic"
5084e794d5dSNick Mathewson * cb. We can also add callbacks for specific paths. */
509*4a6f1ccfSDavid Disseldorp evhttp_set_gencb(http, send_document_cb, &o);
5104e794d5dSNick Mathewson
5117c4da937SAzat Khuzhin if (o.unixsock) {
5127c4da937SAzat Khuzhin #ifdef EVENT__HAVE_STRUCT_SOCKADDR_UN
5137c4da937SAzat Khuzhin struct sockaddr_un addr;
5147c4da937SAzat Khuzhin
5157c4da937SAzat Khuzhin if (o.unlink && (unlink(o.unixsock) && errno != ENOENT)) {
5167c4da937SAzat Khuzhin perror(o.unixsock);
5177c4da937SAzat Khuzhin ret = 1;
5187c4da937SAzat Khuzhin goto err;
5197c4da937SAzat Khuzhin }
5207c4da937SAzat Khuzhin
5217c4da937SAzat Khuzhin addr.sun_family = AF_UNIX;
5227c4da937SAzat Khuzhin strcpy(addr.sun_path, o.unixsock);
5237c4da937SAzat Khuzhin
5247c4da937SAzat Khuzhin lev = evconnlistener_new_bind(base, NULL, NULL,
5257c4da937SAzat Khuzhin LEV_OPT_CLOSE_ON_FREE, -1,
5267c4da937SAzat Khuzhin (struct sockaddr *)&addr, sizeof(addr));
5277c4da937SAzat Khuzhin if (!lev) {
5287c4da937SAzat Khuzhin perror("Cannot create listener");
5297c4da937SAzat Khuzhin ret = 1;
5307c4da937SAzat Khuzhin goto err;
5317c4da937SAzat Khuzhin }
5327c4da937SAzat Khuzhin
5337c4da937SAzat Khuzhin handle = evhttp_bind_listener(http, lev);
5347c4da937SAzat Khuzhin if (!handle) {
5357c4da937SAzat Khuzhin fprintf(stderr, "couldn't bind to %s. Exiting.\n", o.unixsock);
5367c4da937SAzat Khuzhin ret = 1;
5377c4da937SAzat Khuzhin goto err;
5387c4da937SAzat Khuzhin }
5397c4da937SAzat Khuzhin #else /* !EVENT__HAVE_STRUCT_SOCKADDR_UN */
5407c4da937SAzat Khuzhin fprintf(stderr, "-U is not supported on this platform. Exiting.\n");
5417c4da937SAzat Khuzhin ret = 1;
5427c4da937SAzat Khuzhin goto err;
5437c4da937SAzat Khuzhin #endif /* EVENT__HAVE_STRUCT_SOCKADDR_UN */
5447c4da937SAzat Khuzhin }
5457c4da937SAzat Khuzhin else {
546b6309bccSAzat Khuzhin handle = evhttp_bind_socket_with_handle(http, "0.0.0.0", o.port);
5474e794d5dSNick Mathewson if (!handle) {
548b6309bccSAzat Khuzhin fprintf(stderr, "couldn't bind to port %d. Exiting.\n", o.port);
549b6309bccSAzat Khuzhin ret = 1;
5508eb00842SAzat Khuzhin goto err;
5514e794d5dSNick Mathewson }
5527c4da937SAzat Khuzhin }
5534e794d5dSNick Mathewson
5547c4da937SAzat Khuzhin if (display_listen_sock(handle)) {
555b6309bccSAzat Khuzhin ret = 1;
5568eb00842SAzat Khuzhin goto err;
5574e794d5dSNick Mathewson }
5584e794d5dSNick Mathewson
559b6309bccSAzat Khuzhin term = evsignal_new(base, SIGINT, do_term, base);
560b6309bccSAzat Khuzhin if (!term)
561b6309bccSAzat Khuzhin goto err;
562b6309bccSAzat Khuzhin if (event_add(term, NULL))
563b6309bccSAzat Khuzhin goto err;
564b6309bccSAzat Khuzhin
5654e794d5dSNick Mathewson event_base_dispatch(base);
5664e794d5dSNick Mathewson
567b6309bccSAzat Khuzhin #ifdef _WIN32
568b6309bccSAzat Khuzhin WSACleanup();
569b6309bccSAzat Khuzhin #endif
570b6309bccSAzat Khuzhin
571b6309bccSAzat Khuzhin err:
572b6309bccSAzat Khuzhin if (cfg)
573b6309bccSAzat Khuzhin event_config_free(cfg);
574b6309bccSAzat Khuzhin if (http)
575b6309bccSAzat Khuzhin evhttp_free(http);
576b6309bccSAzat Khuzhin if (term)
577b6309bccSAzat Khuzhin event_free(term);
578b6309bccSAzat Khuzhin if (base)
579b6309bccSAzat Khuzhin event_base_free(base);
580b6309bccSAzat Khuzhin
581b6309bccSAzat Khuzhin return ret;
5824e794d5dSNick Mathewson }
583