/*
* Small and portable HTTP server, http://shttpd.sourceforge.net
- * $Id: shttpd.c,v 1.10 2007/06/01 17:59:32 drozd Exp $
+ * $Id: shttpd.c,v 1.28 2008/02/17 21:45:09 drozd Exp $
*/
#include "defs.h"
time_t current_time; /* Current UTC time */
int tz_offset; /* Time zone offset from UTC */
-static LL_HEAD(listeners); /* List of listening sockets */
-
const struct vec known_http_methods[] = {
{"GET", 3},
{"POST", 4},
};
struct shttpd_ctx *init_ctx(const char *config_file, int argc, char *argv[]);
+static void process_connection(struct conn *, int, int);
int
url_decode(const char *src, int src_len, char *dst, int dst_len)
dst[j] = '%';
}
break;
- case '+':
- dst[j] = ' ';
- break;
default:
dst[j] = src[i];
break;
return (j);
}
-void
-shttpd_add_mime_type(struct shttpd_ctx *ctx, const char *ext, const char *mime)
-{
- struct mime_type_link *e;
- const char *error_msg = "shttpd_add_mime_type: no memory";
-
- if ((e = malloc(sizeof(*e))) == NULL) {
- elog(E_FATAL, 0, error_msg);
- } else if ((e->ext= my_strdup(ext)) == NULL) {
- elog(E_FATAL, 0, error_msg);
- } else if ((e->mime = my_strdup(mime)) == NULL) {
- elog(E_FATAL, 0, error_msg);
- } else {
- e->ext_len = strlen(ext);
- LL_TAIL(&ctx->mime_types, &e->link);
- }
-}
-
-
static const char *
is_alias(struct shttpd_ctx *ctx, const char *uri,
struct vec *a_uri, struct vec *a_path)
{
- const char *p, *s = ctx->aliases;
+ const char *p, *s = ctx->options[OPT_ALIASES];
size_t len;
DBG(("is_alias: aliases [%s]", s == NULL ? "" : s));
FOR_EACH_WORD_IN_LIST(s, len) {
- if ((p = memchr(s, '=', len)) != NULL &&
- memcmp(uri, s, p - s) == 0) {
+
+ if ((p = memchr(s, '=', len)) == NULL || p >= s + len || p == s)
+ continue;
+
+ if (memcmp(uri, s, p - s) == 0) {
a_uri->ptr = s;
a_uri->len = p - s;
a_path->ptr = ++p;
for (s = buf, e = s + buflen - 1; len == 0 && s < e; s++)
/* Control characters are not allowed but >=128 is. */
- if (!isprint(*(unsigned char *)s) && *s != '\r' && *s != '\n' && *(unsigned char *)s < 128)
+ if (!isprint(* (unsigned char *) s) && *s != '\r' &&
+ *s != '\n' && * (unsigned char *) s < 128)
len = -1;
else if (s[0] == '\n' && s[1] == '\n')
len = s - buf + 2;
void
send_server_error(struct conn *c, int status, const char *reason)
{
-#ifdef EMBEDDED
struct llhead *lp;
struct error_handler *e;
c->loc.io_class->close != NULL)
c->loc.io_class->close(&c->loc);
io_clear(&c->loc.io);
- setup_embedded_stream(c, e->callback, NULL);
+ setup_embedded_stream(c, e->callback, e->callback_data);
return;
}
}
-#endif /* EMBEDDED */
io_clear(&c->loc.io);
- c->loc.headers_len = c->loc.io.head = my_snprintf(c->loc.io.buf,
- c->loc.io.size, "HTTP/1.1 %d %s\r\n\r\n%d %s",
- status, reason, status, reason);
+ c->loc.io.head = my_snprintf(c->loc.io.buf, c->loc.io.size,
+ "HTTP/1.1 %d %s\r\n"
+ "Content-Type: text/plain\r\n"
+ "Content-Length: 12\r\n"
+ "\r\n"
+ "Error: %03d\r\n",
+ status, reason, status);
+ c->loc.content_len = 10;
c->status = status;
stop_stream(&c->loc);
}
while (*s != '\0') {
*p++ = *s++;
- if (s[-1] == '/')
- while (*s == '.' || *s == '/')
+ if (s[-1] == '/' || s[-1] == '\\')
+ while (*s == '.' || *s == '/' || *s == '\\')
s++;
}
*p = '\0';
}
}
+static const struct {
+ const char *extension;
+ int ext_len;
+ const char *mime_type;
+} builtin_mime_types[] = {
+ {"html", 4, "text/html" },
+ {"htm", 3, "text/html" },
+ {"txt", 3, "text/plain" },
+ {"css", 3, "text/css" },
+ {"ico", 3, "image/x-icon" },
+ {"gif", 3, "image/gif" },
+ {"jpg", 3, "image/jpeg" },
+ {"jpeg", 4, "image/jpeg" },
+ {"png", 3, "image/png" },
+ {"svg", 3, "image/svg+xml" },
+ {"torrent", 7, "application/x-bittorrent" },
+ {"wav", 3, "audio/x-wav" },
+ {"mp3", 3, "audio/x-mp3" },
+ {"mid", 3, "audio/mid" },
+ {"m3u", 3, "audio/x-mpegurl" },
+ {"ram", 3, "audio/x-pn-realaudio" },
+ {"ra", 2, "audio/x-pn-realaudio" },
+ {"doc", 3, "application/msword", },
+ {"exe", 3, "application/octet-stream" },
+ {"zip", 3, "application/x-zip-compressed" },
+ {"xls", 3, "application/excel" },
+ {"tgz", 3, "application/x-tar-gz" },
+ {"tar.gz", 6, "application/x-tar-gz" },
+ {"tar", 3, "application/x-tar" },
+ {"gz", 2, "application/x-gunzip" },
+ {"arj", 3, "application/x-arj-compressed" },
+ {"rar", 3, "application/x-arj-compressed" },
+ {"rtf", 3, "application/rtf" },
+ {"pdf", 3, "application/pdf" },
+ {"swf", 3, "application/x-shockwave-flash" },
+ {"mpg", 3, "video/mpeg" },
+ {"mpeg", 4, "video/mpeg" },
+ {"asf", 3, "video/x-ms-asf" },
+ {"avi", 3, "video/x-msvideo" },
+ {"bmp", 3, "image/bmp" },
+ {NULL, 0, NULL }
+};
+
+void
+get_mime_type(struct shttpd_ctx *ctx, const char *uri, int len, struct vec *vec)
+{
+ const char *eq, *p = ctx->options[OPT_MIME_TYPES];
+ int i, n, ext_len;
+
+ /* Firt, loop through the custom mime types if any */
+ FOR_EACH_WORD_IN_LIST(p, n) {
+ if ((eq = memchr(p, '=', n)) == NULL || eq >= p + n || eq == p)
+ continue;
+ ext_len = eq - p;
+ if (len > ext_len && uri[len - ext_len - 1] == '.' &&
+ !my_strncasecmp(p, &uri[len - ext_len], ext_len)) {
+ vec->ptr = eq + 1;
+ vec->len = p + n - vec->ptr;
+ return;
+ }
+ }
+
+ /* If no luck, try built-in mime types */
+ for (i = 0; builtin_mime_types[i].extension != NULL; i++) {
+ ext_len = builtin_mime_types[i].ext_len;
+ if (len > ext_len && uri[len - ext_len - 1] == '.' &&
+ !my_strncasecmp(builtin_mime_types[i].extension,
+ &uri[len - ext_len], ext_len)) {
+ vec->ptr = builtin_mime_types[i].mime_type;
+ vec->len = strlen(vec->ptr);
+ return;
+ }
+ }
+
+ /* Oops. This extension is unknown to us. Fallback to text/plain */
+ vec->ptr = "text/plain";
+ vec->len = strlen(vec->ptr);
+}
+
/*
* For given directory path, substitute it to valid index file.
* Return 0 if index file has been found, -1 if not found
find_index_file(struct conn *c, char *path, size_t maxpath, struct stat *stp)
{
char buf[FILENAME_MAX];
- const char *s = c->ctx->index_files;
+ const char *s = c->ctx->options[OPT_INDEX_FILES];
size_t len;
FOR_EACH_WORD_IN_LIST(s, len) {
my_snprintf(buf, sizeof(buf), "%s%c%.*s",path, DIRSEP, len, s);
if (my_stat(buf, stp) == 0) {
my_strlcpy(path, buf, maxpath);
- c->mime_type = get_mime_type(c->ctx, s, len);
+ get_mime_type(c->ctx, s, len, &c->mime_type);
return (0);
}
}
return (0);
p = path + strlen(path);
- e = path + strlen(c->ctx->document_root) + 2;
+ e = path + strlen(c->ctx->options[OPT_ROOT]) + 2;
/* Strip directory parts of the path one by one */
for (; p > e; p--)
static void
decide_what_to_do(struct conn *c)
{
- char path[URI_MAX], buf[1024];
+ char path[URI_MAX], buf[1024], *root;
struct vec alias_uri, alias_path;
struct stat st;
int rc;
-#ifdef EMBEDDED
struct registered_uri *ruri;
-#endif /* EMBEDDED */
DBG(("decide_what_to_do: [%s]", c->uri));
url_decode(c->uri, strlen(c->uri), c->uri, strlen(c->uri) + 1);
remove_double_dots(c->uri);
- if (strlen(c->uri) + strlen(c->ctx->document_root) >= sizeof(path)) {
+ root = c->ctx->options[OPT_ROOT];
+ if (strlen(c->uri) + strlen(root) >= sizeof(path)) {
send_server_error(c, 400, "URI is too long");
return;
}
- (void) my_snprintf(path, sizeof(path), "%s%s",
- c->ctx->document_root, c->uri);
+ (void) my_snprintf(path, sizeof(path), "%s%s", root, c->uri);
/* User may use the aliases - check URI for mount point */
if (is_alias(c->ctx, c->uri, &alias_uri, &alias_path) != NULL) {
send_authorization_request(c);
} else
#endif /* NO_AUTH */
-#ifdef EMBEDDED
if ((ruri = is_registered_uri(c->ctx, c->uri)) != NULL) {
setup_embedded_stream(c, ruri->callback, ruri->callback_data);
} else
-#endif /* EMBEDDED */
if (strstr(path, HTPASSWD)) {
/* Do not allow to view passwords files */
send_server_error(c, 403, "Forbidden");
} else
#if !defined(NO_AUTH)
if ((c->method == METHOD_PUT || c->method == METHOD_DELETE) &&
- (c->ctx->put_auth_file == NULL || !is_authorized_for_put(c))) {
+ (c->ctx->options[OPT_AUTH_PUT] == NULL ||
+ !is_authorized_for_put(c))) {
send_authorization_request(c);
} else
#endif /* NO_AUTH */
send_server_error(c, 301, buf);
} else if (S_ISDIR(st.st_mode) &&
find_index_file(c, path, sizeof(path) - 1, &st) == -1 &&
- c->ctx->dirlist == 0) {
+ !IS_TRUE(c->ctx, OPT_DIR_LIST)) {
send_server_error(c, 403, "Directory Listing Denied");
- } else if (S_ISDIR(st.st_mode) && c->ctx->dirlist) {
+ } else if (S_ISDIR(st.st_mode) && IS_TRUE(c->ctx, OPT_DIR_LIST)) {
if ((c->loc.chan.dir.path = my_strdup(path)) != NULL)
get_dir(c);
else
send_server_error(c, 500, "GET Directory Error");
- } else if (S_ISDIR(st.st_mode) && c->ctx->dirlist == 0) {
+ } else if (S_ISDIR(st.st_mode) && !IS_TRUE(c->ctx, OPT_DIR_LIST)) {
send_server_error(c, 403, "Directory listing denied");
#if !defined(NO_CGI)
- } else if (match_extension(path, c->ctx->cgi_extensions)) {
+ } else if (match_extension(path, c->ctx->options[OPT_CGI_EXTENSIONS])) {
if (c->method != METHOD_POST && c->method != METHOD_GET) {
send_server_error(c, 501, "Bad method ");
} else if ((run_cgi(c, path)) == -1) {
}
#endif /* NO_CGI */
#if !defined(NO_SSI)
- } else if (match_extension(path, c->ctx->ssi_extensions)) {
+ } else if (match_extension(path, c->ctx->options[OPT_SSI_EXTENSIONS])) {
if ((c->loc.chan.fd = my_open(path,
O_RDONLY | O_BINARY, 0644)) == -1) {
send_server_error(c, 500, "SSI open error");
{
const struct vec *v;
- assert(c->rem.io.head >= MIN_REQ_LEN);
-
/* Set the request method */
for (v = known_http_methods; v->ptr != NULL; v++)
if (!memcmp(c->rem.io.buf, v->ptr, v->len)) {
parse_http_request(struct conn *c)
{
char *s, *e, *p, *start;
- char *end_number;
- int uri_len, req_len;
+ int uri_len, req_len, n;
- s = c->rem.io.buf;
- req_len = c->rem.headers_len = get_headers_len(s, c->rem.io.head);
+ s = io_data(&c->rem.io);;
+ req_len = c->rem.headers_len =
+ get_headers_len(s, io_data_len(&c->rem.io));
- if (req_len == 0 && io_space_len(&c->rem.io) == 0)
+ if (req_len == 0 && io_space_len(&c->rem.io) == 0) {
+ io_clear(&c->rem.io);
send_server_error(c, 400, "Request is too big");
+ }
+
+ io_inc_tail(&c->rem.io, req_len);
- if (req_len == 0)
+ if (req_len == 0) {
return;
- else if (req_len < MIN_REQ_LEN)
+ } else if (req_len < 16) { /* Minimal: "GET / HTTP/1.0\n\n" */
send_server_error(c, 400, "Bad request");
- else if (set_request_method(c))
+ } else if (set_request_method(c)) {
send_server_error(c, 501, "Method Not Implemented");
- else if ((c->request = my_strndup(s, req_len)) == NULL)
+ } else if ((c->request = my_strndup(s, req_len)) == NULL) {
send_server_error(c, 500, "Cannot allocate request");
+ }
if (c->loc.flags & FLAG_CLOSED)
return;
* First, we skip the REQUEST_METHOD and shift to the URI.
*/
for (p = c->request, e = p + req_len; *p != ' ' && p < e; p++);
- while (p < e && *p == ' ') p++;
+ while (p < e && *p == ' ')
+ p++;
/* Now remember where URI starts, and shift to the end of URI */
for (start = p; p < e && !isspace((unsigned char)*p); ) p++;
uri_len = p - start;
+
/* Skip space following the URI */
- while (p < e && *p == ' ') p++;
+ while (p < e && *p == ' ')
+ p++;
/* Now comes the HTTP-Version in the form HTTP/<major>.<minor> */
- if (strncmp(p, "HTTP/", 5) != 0) {
+ if (sscanf(p, "HTTP/%lu.%lu%n",
+ &c->major_version, &c->minor_version, &n) != 2 || p[n] != '\0') {
send_server_error(c, 400, "Bad HTTP version");
- return;
- }
- p += 5;
- /* Parse the HTTP major version number */
- c->major_version = strtoul(p, &end_number, 10);
- if (end_number == p || *end_number != '.') {
- send_server_error(c, 400, "Bad HTTP major version");
- return;
- }
- p = end_number + 1;
- /* Parse the minor version number */
- c->minor_version = strtoul(p, &end_number, 10);
- if (end_number == p || *end_number != '\0') {
- send_server_error(c, 400, "Bad HTTP minor version");
- return;
- }
- /* Version must be <=1.1 */
- if (c->major_version > 1 ||
+ } else if (c->major_version > 1 ||
(c->major_version == 1 && c->minor_version > 1)) {
send_server_error(c, 505, "HTTP version not supported");
- return;
- }
-
- if (uri_len <= 0) {
+ } else if (uri_len <= 0) {
send_server_error(c, 400, "Bad URI");
} else if ((c->uri = malloc(uri_len + 1)) == NULL) {
send_server_error(c, 500, "Cannot allocate URI");
/* Remove the length of request from total, count only data */
assert(c->rem.io.total >= (big_int_t) req_len);
c->rem.io.total -= req_len;
-
c->rem.content_len = c->ch.cl.v_big_int;
- io_inc_tail(&c->rem.io, req_len);
-
decide_what_to_do(c);
}
}
void
-shttpd_add_socket(struct shttpd_ctx *ctx, int sock)
+shttpd_add_socket(struct shttpd_ctx *ctx, int sock, int is_ssl)
{
struct conn *c;
struct usa sa;
- int l = ctx->inetd_mode ? E_FATAL : E_LOG;
+ int l = IS_TRUE(ctx, OPT_INETD) ? E_FATAL : E_LOG;
#if !defined(NO_SSL)
SSL *ssl = NULL;
#endif /* NO_SSL */
if (getpeername(sock, &sa.u.sa, &sa.len)) {
elog(l, NULL, "add_socket: %s", strerror(errno));
#if !defined(NO_SSL)
- } else if (ctx->ssl_ctx && (ssl = SSL_new(ctx->ssl_ctx)) == NULL) {
+ } else if (is_ssl && (ssl = SSL_new(ctx->ssl_ctx)) == NULL) {
elog(l, NULL, "add_socket: SSL_new: %s", strerror(ERRNO));
(void) closesocket(sock);
- } else if (ctx->ssl_ctx && SSL_set_fd(ssl, sock) == 0) {
+ } else if (is_ssl && SSL_set_fd(ssl, sock) == 0) {
elog(l, NULL, "add_socket: SSL_set_fd: %s", strerror(ERRNO));
(void) closesocket(sock);
SSL_free(ssl);
#endif /* NO_SSL */
- } else if ((c = calloc(1, sizeof(*c) + 2 * ctx->io_buf_size)) == NULL) {
-
+ } else if ((c = calloc(1, sizeof(*c) + 2 * URI_MAX)) == NULL) {
#if !defined(NO_SSL)
if (ssl)
SSL_free(ssl);
/* Set IO buffers */
c->loc.io.buf = (char *) (c + 1);
- c->rem.io.buf = c->loc.io.buf + ctx->io_buf_size;
- c->loc.io.size = c->rem.io.size = ctx->io_buf_size;
+ c->rem.io.buf = c->loc.io.buf + URI_MAX;
+ c->loc.io.size = c->rem.io.size = URI_MAX;
#if !defined(NO_SSL)
- if (ssl) {
+ if (is_ssl) {
c->rem.io_class = &io_ssl;
c->rem.chan.ssl.sock = sock;
c->rem.chan.ssl.ssl = ssl;
l->is_ssl = is_ssl;
l->sock = sock;
l->ctx = ctx;
- LL_TAIL(&listeners, &l->link);
+ LL_TAIL(&ctx->listeners, &l->link);
DBG(("shttpd_listen: added socket %d", sock));
}
static void
-disconnect(struct conn *c)
+disconnect(struct llhead *lp)
{
- static const struct vec ka = {"keep-alive", 10};
- int dont_close;
+ struct conn *c = LL_ENTRY(lp, struct conn, link);
+ static const struct vec vec = {"close", 5};
+ int do_close;
DBG(("Disconnecting %d (%.*s)", c->rem.chan.sock,
c->ch.connection.v_vec.len, c->ch.connection.v_vec.ptr));
-#if !defined(_WIN32) || defined(NO_GUI)
- if (c->ctx->access_log != NULL)
-#endif /* _WIN32 */
- log_access(c->ctx->access_log, c);
+ if (c->request != NULL && c->ctx->access_log != NULL)
+ log_access(c->ctx->access_log, c);
/* In inetd mode, exit if request is finished. */
- if (c->ctx->inetd_mode)
+ if (IS_TRUE(c->ctx, OPT_INETD))
exit(0);
if (c->loc.io_class != NULL && c->loc.io_class->close != NULL)
* Check the "Connection: " header before we free c->request
* If it its 'keep-alive', then do not close the connection
*/
- dont_close = c->ch.connection.v_vec.len >= ka.len &&
- !my_strncasecmp(ka.ptr, c->ch.connection.v_vec.ptr, ka.len);
+ do_close = (c->ch.connection.v_vec.len >= vec.len &&
+ !my_strncasecmp(vec.ptr, c->ch.connection.v_vec.ptr, vec.len)) ||
+ (c->major_version < 1 ||
+ (c->major_version >= 1 && c->minor_version < 1));
if (c->request)
free(c->request);
if (c->uri)
free(c->uri);
- /* Handle Keep-Alive */
- dont_close = 0;
- if (dont_close) {
+ /* Keep the connection open only if we have Content-Length set */
+ if (!do_close && c->loc.content_len > 0) {
c->loc.io_class = NULL;
- c->loc.flags = c->rem.flags = 0;
+ c->loc.flags = 0;
+ c->loc.content_len = 0;
+ c->rem.flags = FLAG_W | FLAG_R;
c->query = c->request = c->uri = c->path_info = NULL;
- c->mime_type = NULL;
+ c->mime_type.len = 0;
(void) memset(&c->ch, 0, sizeof(c->ch));
- io_clear(&c->rem.io);
io_clear(&c->loc.io);
- c->rem.io.total = c->loc.io.total = 0;
+ c->birth_time = current_time;
+ if (io_data_len(&c->rem.io) > 0)
+ process_connection(c, 0, 0);
} else {
if (c->rem.io_class != NULL)
c->rem.io_class->close(&c->rem);
}
}
+static int
+is_allowed(const struct shttpd_ctx *ctx, const struct usa *usa)
+{
+ const struct acl *acl;
+ const struct llhead *lp;
+ int allowed = '+';
+ uint32_t ip;
+
+ LL_FOREACH(&ctx->acl, lp) {
+ acl = LL_ENTRY(lp, struct acl, link);
+ (void) memcpy(&ip, &usa->u.sin.sin_addr, sizeof(ip));
+ if (acl->ip == (ntohl(ip) & acl->mask))
+ allowed = acl->flag;
+ }
+
+ return (allowed == '+');
+}
+
static void
add_to_set(int fd, fd_set *set, int *max_fd)
{
*max_fd = fd;
}
+static void
+process_connection(struct conn *c, int remote_ready, int local_ready)
+{
+ /* Read from remote end if it is ready */
+ if (remote_ready && io_space_len(&c->rem.io))
+ read_stream(&c->rem);
+
+ /* If the request is not parsed yet, do so */
+ if (!(c->rem.flags & FLAG_HEADERS_PARSED))
+ parse_http_request(c);
+
+ DBG(("loc: %u [%.*s]", io_data_len(&c->loc.io),
+ io_data_len(&c->loc.io), io_data(&c->loc.io)));
+ DBG(("rem: %u [%.*s]", io_data_len(&c->rem.io),
+ io_data_len(&c->rem.io), io_data(&c->rem.io)));
+
+ /* Read from the local end if it is ready */
+ if (local_ready && io_space_len(&c->loc.io))
+ read_stream(&c->loc);
+
+ if (io_data_len(&c->rem.io) > 0 && (c->loc.flags & FLAG_W) &&
+ c->loc.io_class != NULL && c->loc.io_class->write != NULL)
+ write_stream(&c->rem, &c->loc);
+
+ if (io_data_len(&c->loc.io) > 0 && c->rem.io_class != NULL)
+ write_stream(&c->loc, &c->rem);
+
+ if (c->rem.nread_last > 0)
+ c->ctx->in += c->rem.nread_last;
+ if (c->loc.nread_last > 0)
+ c->ctx->out += c->loc.nread_last;
+
+ /* Check whether we should close this connection */
+ if ((current_time > c->expire_time) ||
+ (c->rem.flags & FLAG_CLOSED) ||
+ ((c->loc.flags & FLAG_CLOSED) && !io_data_len(&c->loc.io)))
+ disconnect(&c->link);
+}
+
/*
* One iteration of server loop. This is the core of the data exchange.
*/
FD_ZERO(&write_set);
/* Add listening sockets to the read set */
- LL_FOREACH(&listeners, lp) {
+ LL_FOREACH(&ctx->listeners, lp) {
l = LL_ENTRY(lp, struct listener, link);
FD_SET(l->sock, &read_set);
if (l->sock > max_fd)
}
tv.tv_sec = msec / 1000;
- tv.tv_usec = msec % 1000;
+ tv.tv_usec = (msec % 1000) * 1000;
/* Check IO readiness */
if (select(max_fd + 1, &read_set, &write_set, NULL, &tv) < 0) {
}
/* Check for incoming connections on listener sockets */
- LL_FOREACH(&listeners, lp) {
+ LL_FOREACH(&ctx->listeners, lp) {
l = LL_ENTRY(lp, struct listener, link);
if (!FD_ISSET(l->sock, &read_set))
continue;
sa.len = sizeof(sa.u.sin);
if ((sock = accept(l->sock, &sa.u.sa, &sa.len)) != -1) {
#if defined(_WIN32)
- shttpd_add_socket(ctx, sock);
-#else
- if (sock < (int) FD_SETSIZE) {
- shttpd_add_socket(ctx, sock);
+ if (!is_allowed(ctx, &sa)) {
+ elog(E_LOG, NULL, "shttpd_poll: %s "
+ "is not allowed to connect",
+ inet_ntoa(sa.u.sin.sin_addr));
+ (void) closesocket(sock);
} else {
+ shttpd_add_socket(ctx, sock, l->is_ssl);
+ }
+#else
+ if (sock >= (int) FD_SETSIZE) {
elog(E_LOG, NULL,
"shttpd_poll: ctx %p: disarding "
"socket %d, too busy", ctx, sock);
(void) closesocket(sock);
+ } else if (!is_allowed(ctx, &sa)) {
+ elog(E_LOG, NULL, "shttpd_poll: %s "
+ "is not allowed to connect",
+ inet_ntoa(sa.u.sin.sin_addr));
+ (void) closesocket(sock);
+ } else {
+ shttpd_add_socket(ctx, sock, l->is_ssl);
}
#endif /* _WIN32 */
}
/* Process all connections */
LL_FOREACH_SAFE(&ctx->connections, lp, tmp) {
c = LL_ENTRY(lp, struct conn, link);
-
- /* Read from remote end if it is ready */
- if (FD_ISSET(c->rem.chan.fd, &read_set) &&
- io_space_len(&c->rem.io))
- read_stream(&c->rem);
-
- /* If the request is not parsed yet, do so */
- if (!(c->rem.flags & FLAG_HEADERS_PARSED))
- parse_http_request(c);
-
- DBG(("loc: %u [%.*s]", io_data_len(&c->loc.io),
- io_data_len(&c->loc.io), io_data(&c->loc.io)));
- DBG(("rem: %u [%.*s]", io_data_len(&c->rem.io),
- io_data_len(&c->rem.io), io_data(&c->rem.io)));
-
- /* Read from the local end if it is ready */
- if (io_space_len(&c->loc.io) &&
+ process_connection(c, FD_ISSET(c->rem.chan.fd, &read_set),
((c->loc.flags & FLAG_ALWAYS_READY)
-
#if !defined(NO_CGI)
- ||(c->loc.io_class == &io_cgi &&
+ || (c->loc.io_class == &io_cgi &&
FD_ISSET(c->loc.chan.fd, &read_set))
#endif /* NO_CGI */
- ))
- read_stream(&c->loc);
-
- if (io_data_len(&c->rem.io) > 0 && (c->loc.flags & FLAG_W) &&
- c->loc.io_class != NULL && c->loc.io_class->write != NULL)
- write_stream(&c->rem, &c->loc);
-
- if (io_data_len(&c->loc.io) > 0 && c->rem.io_class != NULL)
- write_stream(&c->loc, &c->rem);
-
- if (c->rem.nread_last > 0)
- c->ctx->in += c->rem.nread_last;
- if (c->loc.nread_last > 0)
- c->ctx->out += c->loc.nread_last;
-
- /* Check whether we should close this connection */
- if ((current_time > c->expire_time) ||
- (c->rem.flags & FLAG_CLOSED) ||
- ((c->loc.flags & FLAG_CLOSED) && !io_data_len(&c->loc.io)))
- disconnect(c);
+ ));
}
}
-/*
- * Deallocate shttpd object, free up the resources
- */
void
-shttpd_fini(struct shttpd_ctx *ctx)
+free_list(struct llhead *head, void (*dtor)(struct llhead *))
{
- struct llhead *lp, *tmp;
- struct mime_type_link *mtl;
- struct conn *c;
- struct listener *l;
- struct registered_uri *ruri;
+ struct llhead *lp, *tmp;
- /* Free configured mime types */
- LL_FOREACH_SAFE(&ctx->mime_types, lp, tmp) {
- mtl = LL_ENTRY(lp, struct mime_type_link, link);
- free(mtl->mime);
- free(mtl->ext);
- free(mtl);
+ LL_FOREACH_SAFE(head, lp, tmp) {
+ LL_DEL(lp);
+ dtor(lp);
}
+}
- /* Free all connections */
- LL_FOREACH_SAFE(&ctx->connections, lp, tmp) {
- c = LL_ENTRY(lp, struct conn, link);
- disconnect(c);
- }
+void
+listener_destructor(struct llhead *lp)
+{
+ struct listener *listener = LL_ENTRY(lp, struct listener, link);
- /* Free registered URIs (must be done after disconnect()) */
- LL_FOREACH_SAFE(&ctx->registered_uris, lp, tmp) {
- ruri = LL_ENTRY(lp, struct registered_uri, link);
- free((void *)ruri->uri);
- free(ruri);
- }
+ (void) closesocket(listener->sock);
+ free(listener);
+}
- /* Free listener sockets for this context */
- LL_FOREACH_SAFE(&listeners, lp, tmp) {
- l = LL_ENTRY(lp, struct listener, link);
- (void) closesocket(l->sock);
- LL_DEL(&l->link);
- free(l);
- }
+void
+registered_uri_destructor(struct llhead *lp)
+{
+ struct registered_uri *ruri = LL_ENTRY(lp, struct registered_uri, link);
-#if !defined(NO_SSI)
- free_ssi_funcs(ctx);
-#endif /* NO_SSI */
+ free((void *) ruri->uri);
+ free(ruri);
+}
+
+static void
+acl_destructor(struct llhead *lp)
+{
+ struct acl *acl = LL_ENTRY(lp, struct acl, link);
+ free(acl);
+}
+
+/*
+ * Deallocate shttpd object, free up the resources
+ */
+void
+shttpd_fini(struct shttpd_ctx *ctx)
+{
+ size_t i;
+
+ free_list(&ctx->connections, disconnect);
+ free_list(&ctx->registered_uris, registered_uri_destructor);
+ free_list(&ctx->acl, acl_destructor);
+ free_list(&ctx->listeners, listener_destructor);
+ free_list(&ctx->ssi_funcs, ssi_func_destructor);
+
+ for (i = 0; i < NELEMS(ctx->options); i++)
+ if (ctx->options[i] != NULL)
+ free(ctx->options[i]);
if (ctx->access_log) (void) fclose(ctx->access_log);
if (ctx->error_log) (void) fclose(ctx->error_log);
- if (ctx->put_auth_file) free(ctx->put_auth_file);
- if (ctx->document_root) free(ctx->document_root);
- if (ctx->index_files) free(ctx->index_files);
- if (ctx->aliases) free(ctx->aliases);
-#if !defined(NO_CGI)
- if (ctx->cgi_vars) free(ctx->cgi_vars);
- if (ctx->cgi_extensions) free(ctx->cgi_extensions);
- if (ctx->cgi_interpreter) free(ctx->cgi_interpreter);
-#endif /* NO_CGI */
- if (ctx->auth_realm) free(ctx->auth_realm);
- if (ctx->global_passwd_file) free(ctx->global_passwd_file);
- if (ctx->uid) free(ctx->uid);
/* TODO: free SSL context */
free(ctx);
}
-
-void
-open_listening_ports(struct shttpd_ctx *ctx)
-{
- const char *p = ctx->ports;
- int len, is_ssl;
-
- FOR_EACH_WORD_IN_LIST(p, len) {
- is_ssl = p[len - 1] == 's' ? 1 : 0;
- if (shttpd_listen(ctx, atoi(p), is_ssl) == -1)
- elog(E_FATAL, NULL,
- "Cannot open socket on port %d", atoi(p));
- }
-}