Newer
Older
*port = 80;
proto[0] = host[0] = '\0';
snprintf(fmt1, sizeof(fmt1), "%%%zu[a-z]://%%%zu[^: ]:%%hu%%n", plen, hlen);
snprintf(fmt2, sizeof(fmt2), "%%%zu[a-z]://%%%zu[^/ ]%%n", plen, hlen);
snprintf(fmt3, sizeof(fmt3), "%%%zu[^: ]:%%hu%%n", hlen);
if (sscanf(url, fmt1, proto, host, port, &n) == 3 ||
sscanf(url, fmt2, proto, host, &n) == 2) {
return n;
} else if (sscanf(url, fmt3, host, port, &n) == 2) {
proto[0] = '\0';
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
static void proxy_request(struct ns_connection *pc, struct mg_connection *c) {
int i, sent_close_header = 0;
ns_printf(pc, "%s %s HTTP/%s\r\n", c->request_method, c->uri,
c->http_version);
for (i = 0; i < c->num_headers; i++) {
if (mg_strcasecmp(c->http_headers[i].name, "Connection") == 0) {
// Force connection close, cause we don't parse proxy replies
// therefore we don't know message boundaries
//ns_printf(pc, "%s: %s\r\n", "Connection", "close");
sent_close_header = 1;
//} else {
}
ns_printf(pc, "%s: %s\r\n", c->http_headers[i].name,
c->http_headers[i].value);
}
if (!sent_close_header) {
ns_printf(pc, "%s: %s\r\n", "Connection", "close");
}
ns_printf(pc, "%s", "\r\n");
ns_send(pc, c->content, c->content_len);
}
static void proxify_connection(struct connection *conn) {
char proto[10], host[500], cert[500];
struct mg_connection *c = &conn->mg_conn;
struct ns_server *server = &conn->server->ns_server;
struct ns_connection *pc;
proto[0] = host[0] = cert[0] = '\0';
n = parse_url(c->uri, proto, sizeof(proto), host, sizeof(host), &port);
#ifdef NS_ENABLE_SSL
// Find out whether we should be in the MITM mode
{
const char *certs = conn->server->config_options[SSL_MITM_CERTS];
int host_len = strlen(host);
struct vec a, b;
while ((certs = next_option(certs, &a, &b)) != NULL) {
if (a.len == host_len && mg_strncasecmp(a.ptr, host, a.len) == 0) {
snprintf(cert, sizeof(cert), "%.*s", b.len, b.ptr);
break;
}
}
}
#endif
(pc = ns_connect(server, host, port, use_ssl, conn)) != NULL) {
// Interlink two connections
pc->flags |= MG_PROXY_CONN;
conn->endpoint_type = EP_PROXY;
conn->endpoint.nc = pc;
DBG(("%p [%s] -> %p %d", conn, c->uri, pc, use_ssl));
if (strcmp(c->request_method, "CONNECT") == 0) {
// For CONNECT request, reply with 200 OK. Tunnel is established.
mg_printf(c, "%s", "HTTP/1.1 200 OK\r\n\r\n");
conn->request_len = 0;
free(conn->request);
conn->request = NULL;
DBG(("%s", "Triggering MITM mode: terminating SSL connection"));
SSL_library_init();
ctx = SSL_CTX_new(SSLv23_server_method());
if (ctx == NULL) {
pc->flags |= NSF_CLOSE_IMMEDIATELY;
} else {
SSL_CTX_use_certificate_file(ctx, cert, 1);
SSL_CTX_use_PrivateKey_file(ctx, cert, 1);
SSL_CTX_use_certificate_chain_file(ctx, cert);
// When clear-text reply is pushed to client, switch to SSL mode.
n = send(conn->ns_conn->sock, conn->ns_conn->send_iobuf.buf,
conn->ns_conn->send_iobuf.len, 0);
DBG(("%p %lu %d SEND", c, conn->ns_conn->send_iobuf.len, n));
conn->ns_conn->send_iobuf.len = 0;
if ((conn->ns_conn->ssl = SSL_new(ctx)) != NULL) {
//SSL_set_fd((SSL *) c->connection_param, conn->ns_conn->sock);
SSL_set_fd(conn->ns_conn->ssl, conn->ns_conn->sock);
}
SSL_CTX_free(ctx);
}
}
#endif
} else {
// For other methods, forward the request to the target host.
}
} else {
conn->ns_conn->flags |= NSF_CLOSE_IMMEDIATELY;
}
}
static void open_local_endpoint(struct connection *conn, int skip_user) {
#ifndef MONGOOSE_NO_FILESYSTEM
#ifndef MONGOOSE_NO_CGI
const char *cgi_pat = conn->server->config_options[CGI_PATTERN];
#else
const char *cgi_pat = DEFAULT_CGI_PATTERN;
#endif
#ifndef MONGOOSE_NO_DIRECTORY_LISTING
const char *dir_lst = conn->server->config_options[ENABLE_DIRECTORY_LISTING];
#else
const char *dir_lst = "yes";
#endif
// If EP_USER was set in a prev call, reset it
conn->endpoint_type = EP_NONE;
#ifndef MONGOOSE_NO_AUTH
if (conn->server->event_handler && call_user(conn, MG_AUTH) == MG_FALSE) {
mg_send_digest_auth_request(&conn->mg_conn);
return;
}
// Call URI handler if one is registered for this URI
if (skip_user == 0 && conn->server->event_handler != NULL) {
{
const char *cl = mg_get_header(&conn->mg_conn, "Content-Length");
if ((strcmp(conn->mg_conn.request_method, "POST") == 0 ||
strcmp(conn->mg_conn.request_method, "PUT") == 0) &&
(cl == NULL || to64(cl) > MONGOOSE_POST_SIZE_LIMIT)) {
send_http_error(conn, 500, "POST size > %zu",
if (strcmp(conn->mg_conn.request_method, "CONNECT") == 0 ||
memcmp(conn->mg_conn.uri, "http", 4) == 0) {
proxify_connection(conn);
return;
}
#ifdef MONGOOSE_NO_FILESYSTEM
if (!strcmp(conn->mg_conn.request_method, "OPTIONS")) {
send_options(conn);
} else {
send_http_error(conn, 404, NULL);
}
exists = convert_uri_to_file_name(conn, path, sizeof(path), &st);
is_directory = S_ISDIR(st.st_mode);
if (!strcmp(conn->mg_conn.request_method, "OPTIONS")) {
send_options(conn);
} else if (conn->server->config_options[DOCUMENT_ROOT] == NULL) {
send_http_error(conn, 404, NULL);
#ifndef MONGOOSE_NO_AUTH
} else if ((!is_dav_request(conn) && !is_authorized(conn, path)) ||
(is_dav_request(conn) && !is_authorized_for_dav(conn))) {
mg_send_digest_auth_request(&conn->mg_conn);
close_local_endpoint(conn);
} else if (!strcmp(conn->mg_conn.request_method, "PROPFIND")) {
handle_propfind(conn, path, &st, exists);
} else if (!strcmp(conn->mg_conn.request_method, "MKCOL")) {
handle_mkcol(conn, path);
} else if (!strcmp(conn->mg_conn.request_method, "DELETE")) {
handle_delete(conn, path);
} else if (!strcmp(conn->mg_conn.request_method, "PUT")) {
handle_put(conn, path);
} else if (!exists || must_hide_file(conn, path)) {
send_http_error(conn, 404, NULL);
} else if (is_directory &&
conn->mg_conn.uri[strlen(conn->mg_conn.uri) - 1] != '/') {
conn->mg_conn.status_code = 301;
mg_printf(&conn->mg_conn, "HTTP/1.1 301 Moved Permanently\r\n"
"Location: %s/\r\n\r\n", conn->mg_conn.uri);
close_local_endpoint(conn);
} else if (is_directory && !find_index_file(conn, path, sizeof(path), &st)) {
if (!mg_strcasecmp(dir_lst, "yes")) {
#ifndef MONGOOSE_NO_DIRECTORY_LISTING
send_directory_listing(conn, path);
#else
send_http_error(conn, 501, NULL);
send_http_error(conn, 403, NULL);
} else if (mg_match_prefix(cgi_pat, strlen(cgi_pat), path) > 0) {
#if !defined(MONGOOSE_NO_CGI)
#endif // !MONGOOSE_NO_CGI
} else if (mg_match_prefix(conn->server->config_options[SSI_PATTERN],
strlen(conn->server->config_options[SSI_PATTERN]),
path) > 0) {
} else if (is_not_modified(conn, &st)) {
send_http_error(conn, 304, NULL);
} else if ((conn->endpoint.fd = open(path, O_RDONLY | O_BINARY)) != -1) {
// O_BINARY is required for Windows, otherwise in default text mode
// two bytes \r\n will be read as one.
open_file_endpoint(conn, path, &st);
} else {
send_http_error(conn, 404, NULL);
#endif // MONGOOSE_NO_FILESYSTEM
static void send_continue_if_expected(struct connection *conn) {
static const char expect_response[] = "HTTP/1.1 100 Continue\r\n\r\n";
const char *expect_hdr = mg_get_header(&conn->mg_conn, "Expect");
if (expect_hdr != NULL && !mg_strcasecmp(expect_hdr, "100-continue")) {
ns_send(conn->ns_conn, expect_response, sizeof(expect_response) - 1);
// Conform to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
static int is_valid_uri(const char *uri) {
unsigned short n;
return uri[0] == '/' ||
strcmp(uri, "*") == 0 || // OPTIONS method can use asterisk URI
memcmp(uri, "http", 4) == 0 || // Naive check for the absolute URI
sscanf(uri, "%*[^ :]:%hu", &n) > 0; // CONNECT method can use host:port
static void try_parse(struct connection *conn) {
struct iobuf *io = &conn->ns_conn->recv_iobuf;
if (conn->request_len == 0 &&
(conn->request_len = get_request_len(io->buf, io->len)) > 0) {
// If request is buffered in, remove it from the iobuf. This is because
// iobuf could be reallocated, and pointers in parsed request could
// become invalid.
conn->request = (char *) malloc(conn->request_len);
memcpy(conn->request, io->buf, conn->request_len);
//DBG(("%p [%.*s]", conn, conn->request_len, conn->request));
conn->request_len = parse_http_message(conn->request, conn->request_len,
&conn->mg_conn);
if (conn->request_len > 0) {
const char *cl_hdr = mg_get_header(&conn->mg_conn, "Content-Length");
conn->cl = cl_hdr == NULL ? 0 : to64(cl_hdr);
conn->mg_conn.content_len = (size_t) conn->cl;
static void do_proxy(struct connection *conn) {
if (conn->request_len == 0) {
DBG(("%p parsing", conn));
try_parse(conn);
if (conn->request_len > 0 &&
call_user(conn, MG_REQUEST) == MG_FALSE) {
proxy_request(conn->endpoint.nc, &conn->mg_conn);
} else if (conn->request_len < 0) {
ns_forward(conn->ns_conn, conn->endpoint.nc);
}
} else {
DBG(("%p forwarding", conn));
ns_forward(conn->ns_conn, conn->endpoint.nc);
}
static void on_recv_data(struct connection *conn) {
if (conn->endpoint_type == EP_PROXY && conn->endpoint.nc != NULL) {
do_proxy(conn);
return;
}
DBG(("%p %d %zu %d", conn, conn->request_len, io->len, conn->ns_conn->flags));
if (conn->request_len < 0 ||
(conn->request_len > 0 && !is_valid_uri(conn->mg_conn.uri))) {
send_http_error(conn, 400, NULL);
} else if (conn->request_len == 0 && io->len > MAX_REQUEST_SIZE) {
send_http_error(conn, 413, NULL);
} else if (conn->request_len > 0 &&
strcmp(conn->mg_conn.http_version, "1.0") != 0 &&
strcmp(conn->mg_conn.http_version, "1.1") != 0) {
send_http_error(conn, 505, NULL);
} else if (conn->request_len > 0 && conn->endpoint_type == EP_NONE) {
#ifndef MONGOOSE_NO_WEBSOCKET
send_websocket_handshake_if_requested(&conn->mg_conn);
#endif
send_continue_if_expected(conn);
open_local_endpoint(conn, 0);
if (conn->endpoint_type == EP_CGI && conn->endpoint.nc != NULL) {
ns_forward(conn->ns_conn, conn->endpoint.nc);
}
#endif
if (conn->endpoint_type == EP_USER) {
call_request_handler_if_data_is_buffered(conn);
if (conn->endpoint_type == EP_PUT && io->len > 0) {
forward_put_data(conn);
Sergey Lyubka
committed
static void call_http_client_handler(struct connection *conn) {
//conn->mg_conn.status_code = code;
// For responses without Content-Lengh, use the whole buffer
Sergey Lyubka
committed
if (conn->cl == 0) {
conn->mg_conn.content_len = conn->ns_conn->recv_iobuf.len;
conn->mg_conn.content = conn->ns_conn->recv_iobuf.buf;
Sergey Lyubka
committed
if (call_user(conn, MG_REPLY) == MG_FALSE) {
iobuf_remove(&conn->ns_conn->recv_iobuf, conn->mg_conn.content_len);
conn->mg_conn.status_code = 0;
conn->cl = conn->num_bytes_sent = conn->request_len = 0;
free(conn->request);
conn->request = NULL;
}
static void process_response(struct connection *conn) {
DBG(("%p %d %zu", conn, conn->request_len, io->len));
if (conn->request_len < 0 ||
(conn->request_len == 0 && io->len > MAX_REQUEST_SIZE)) {
Sergey Lyubka
committed
call_http_client_handler(conn);
Sergey Lyubka
committed
call_http_client_handler(conn);
struct mg_connection *mg_connect(struct mg_server *server, const char *host,
int port, int use_ssl) {
struct ns_connection *nsconn;
struct connection *conn;
nsconn = ns_connect(&server->ns_server, host, port, use_ssl, NULL);
if ((conn = (struct connection *) calloc(1, sizeof(*conn))) == NULL) {
nsconn->flags |= NSF_CLOSE_IMMEDIATELY;
// Interlink two structs
conn->ns_conn = nsconn;
nsconn->connection_data = conn;
//conn->handler = handler;
conn->mg_conn.server_param = server->ns_server.server_data;
conn->ns_conn->flags = NSF_CONNECTING;
return &conn->mg_conn;
#ifndef MONGOOSE_NO_LOGGING
static void log_header(const struct mg_connection *conn, const char *header,
FILE *fp) {
const char *header_value;
if ((header_value = mg_get_header(conn, header)) == NULL) {
(void) fprintf(fp, "%s", " -");
} else {
(void) fprintf(fp, " \"%s\"", header_value);
static void log_access(const struct connection *conn, const char *path) {
const struct mg_connection *c = &conn->mg_conn;
FILE *fp = (path == NULL) ? NULL : fopen(path, "a+");
now = time(NULL);
strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z", localtime(&now));
flockfile(fp);
mg_parse_header(mg_get_header(&conn->mg_conn, "Authorization"), "username",
user, sizeof(user));
fprintf(fp, "%s - %s [%s] \"%s %s%s%s HTTP/%s\" %d %" INT64_FMT,
c->remote_ip, user[0] == '\0' ? "-" : user, date,
c->request_method ? c->request_method : "-",
c->uri ? c->uri : "-", c->query_string ? "?" : "",
c->query_string ? c->query_string : "",
c->http_version, c->status_code, conn->num_bytes_sent);
log_header(c, "Referer", fp);
log_header(c, "User-Agent", fp);
fputc('\n', fp);
fflush(fp);
static void close_local_endpoint(struct connection *conn) {
struct mg_connection *c = &conn->mg_conn;
// Must be done before free()
int keep_alive = should_keep_alive(&conn->mg_conn) &&
(conn->endpoint_type == EP_FILE || conn->endpoint_type == EP_USER);
DBG(("%p %d %d %d", conn, conn->endpoint_type, keep_alive,
conn->ns_conn->flags));
case EP_PUT:
case EP_FILE:
close(conn->endpoint.fd);
break;
case EP_CGI:
case EP_PROXY:
if (conn->endpoint.nc != NULL) {
DBG(("%p %p %p :-)", conn, conn->ns_conn, conn->endpoint.nc));
conn->endpoint.nc->flags |= NSF_CLOSE_IMMEDIATELY;
conn->endpoint.nc->connection_data = NULL;
#ifndef MONGOOSE_NO_LOGGING
if (c->status_code > 0 && conn->endpoint_type != EP_CLIENT &&
c->status_code != 400) {
log_access(conn, conn->server->config_options[ACCESS_LOG_FILE]);
}
#endif
// Gobble possible POST data sent to the URI handler
iobuf_free(&conn->ns_conn->recv_iobuf);
free(conn->request);
free(conn->path_info);
conn->cl = conn->num_bytes_sent = conn->request_len = 0;
conn->ns_conn->flags &= ~(NSF_FINISHED_SENDING_DATA |
NSF_BUFFER_BUT_DONT_SEND | NSF_CLOSE_IMMEDIATELY |
MG_HEADERS_SENT | MG_LONG_RUNNING);
c->num_headers = c->status_code = c->is_websocket = c->content_len = 0;
conn->endpoint.nc = NULL;
c->request_method = c->uri = c->http_version = c->query_string = NULL;
conn->request = conn->path_info = NULL;
on_recv_data(conn); // Can call us recursively if pipelining is used
conn->ns_conn->flags |= conn->ns_conn->send_iobuf.len == 0 ?
NSF_CLOSE_IMMEDIATELY : NSF_FINISHED_SENDING_DATA;
static void transfer_file_data(struct connection *conn) {
char buf[IOBUF_SIZE];
int n = read(conn->endpoint.fd, buf, conn->cl < (int64_t) sizeof(buf) ?
(int) conn->cl : (int) sizeof(buf));
close_local_endpoint(conn);
} else if (n > 0) {
conn->cl -= n;
if (conn->cl <= 0) {
close_local_endpoint(conn);
int mg_poll_server(struct mg_server *server, int milliseconds) {
return ns_server_poll(&server->ns_server, milliseconds);
void mg_destroy_server(struct mg_server **server) {
if (server != NULL && *server != NULL) {
for (i = 0; i < (int) ARRAY_SIZE(s->config_options); i++) {
free(s->config_options[i]); // It is OK to free(NULL)
struct mg_iterator {
mg_handler_t cb;
void *param;
};
static void iter(struct ns_connection *nsconn, enum ns_event ev, void *param) {
if (ev == NS_POLL) {
struct mg_iterator *it = (struct mg_iterator *) param;
struct connection *c = (struct connection *) nsconn->connection_data;
Sergey Lyubka
committed
if (c != NULL) c->mg_conn.callback_param = it->param;
it->cb(&c->mg_conn, MG_POLL);
Sergey Lyubka
committed
// Apply function to all active connections.
void mg_iterate_over_connections(struct mg_server *server, mg_handler_t cb,
void *param) {
struct mg_iterator it = { cb, param };
ns_iterate(&server->ns_server, iter, &it);
static int get_var(const char *data, size_t data_len, const char *name,
char *dst, size_t dst_len) {
const char *p, *e, *s;
size_t name_len;
int len;
if (dst == NULL || dst_len == 0) {
len = -2;
} else if (data == NULL || name == NULL || data_len == 0) {
len = -1;
dst[0] = '\0';
} else {
name_len = strlen(name);
e = data + data_len;
len = -1;
dst[0] = '\0';
// data is "var1=val1&var2=val2...". Find variable first
for (p = data; p + name_len < e; p++) {
if ((p == data || p[-1] == '&') && p[name_len] == '=' &&
!mg_strncasecmp(name, p, name_len)) {
// Point p to variable value
p += name_len + 1;
// Point s to the end of the value
s = (const char *) memchr(p, '&', (size_t)(e - p));
if (s == NULL) {
s = e;
}
assert(s >= p);
// Decode variable into destination buffer
len = mg_url_decode(p, (size_t)(s - p), dst, dst_len, 1);
// Redirect error code from -1 to -2 (destination buffer too small).
if (len == -1) {
len = -2;
}
break;
}
}
}
int mg_get_var(const struct mg_connection *conn, const char *name,
char *dst, size_t dst_len) {
int len = get_var(conn->query_string, conn->query_string == NULL ? 0 :
strlen(conn->query_string), name, dst, dst_len);
if (len < 0) {
len = get_var(conn->content, conn->content_len, name, dst, dst_len);
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
static int get_line_len(const char *buf, int buf_len) {
int len = 0;
while (len < buf_len && buf[len] != '\n') len++;
return buf[len] == '\n' ? len + 1: -1;
}
int mg_parse_multipart(const char *buf, int buf_len,
char *var_name, int var_name_len,
char *file_name, int file_name_len,
const char **data, int *data_len) {
static const char cd[] = "Content-Disposition: ";
//struct mg_connection c;
int hl, bl, n, ll, pos, cdl = sizeof(cd) - 1;
//char *p;
if (buf == NULL || buf_len <= 0) return 0;
if ((hl = get_request_len(buf, buf_len)) <= 0) return 0;
if (buf[0] != '-' || buf[1] != '-' || buf[2] == '\n') return 0;
// Get boundary length
bl = get_line_len(buf, buf_len);
// Loop through headers, fetch variable name and file name
var_name[0] = file_name[0] = '\0';
for (n = bl; (ll = get_line_len(buf + n, hl - n)) > 0; n += ll) {
if (mg_strncasecmp(cd, buf + n, cdl) == 0) {
parse_header(buf + n + cdl, ll - (cdl + 2), "name",
var_name, var_name_len);
parse_header(buf + n + cdl, ll - (cdl + 2), "filename",
file_name, file_name_len);
}
}
// Scan body, search for terminating boundary
for (pos = hl; pos + (bl - 2) < buf_len; pos++) {
if (buf[pos] == '-' && !memcmp(buf, &buf[pos], bl - 2)) {
if (data_len != NULL) *data_len = (pos - 2) - hl;
if (data != NULL) *data = buf + hl;
return pos;
}
}
return 0;
}
const char **mg_get_valid_option_names(void) {
return static_config_options;
static int get_option_index(const char *name) {
for (i = 0; static_config_options[i * 2] != NULL; i++) {
if (strcmp(static_config_options[i * 2], name) == 0) {
return i;
}
return -1;
}
static void set_default_option_values(char **opts) {
const char *value, **all_opts = mg_get_valid_option_names();
int i;
for (i = 0; all_opts[i * 2] != NULL; i++) {
value = all_opts[i * 2 + 1];
if (opts[i] == NULL && value != NULL) {
opts[i] = mg_strdup(value);
const char *mg_set_option(struct mg_server *server, const char *name,
const char *value) {
int ind = get_option_index(name);
const char *error_msg = NULL;
Sergey Lyubka
committed
char **v = NULL;
Sergey Lyubka
committed
if (ind < 0) return "No such option";
v = &server->config_options[ind];
Sergey Lyubka
committed
// Return success immediately if setting to the same value
if ((*v == NULL && value == NULL) ||
(value != NULL && *v != NULL && !strcmp(value, *v))) {
return NULL;
}
if (*v != NULL) {
free(*v);
*v = NULL;
}
if (value == NULL || value[0] == '\0') return NULL;
Sergey Lyubka
committed
*v = mg_strdup(value);
DBG(("%s [%s]", name, *v));
if (ind == LISTENING_PORT) {
int port = ns_bind(&server->ns_server, value);
if (port < 0) {
error_msg = "Cannot bind to port";
} else {
if (!strcmp(value, "0")) {
char buf[10];
mg_snprintf(buf, sizeof(buf), "%d", port);
free(*v);
*v = mg_strdup(buf);
Sergey Lyubka
committed
}
Sergey Lyubka
committed
} else if (ind == RUN_AS_USER) {
struct passwd *pw;
if ((pw = getpwnam(value)) == NULL) {
error_msg = "Unknown user";
} else if (setgid(pw->pw_gid) != 0) {
error_msg = "setgid() failed";
} else if (setuid(pw->pw_uid) != 0) {
error_msg = "setuid() failed";
}
Sergey Lyubka
committed
} else if (ind == SSL_CERTIFICATE) {
int res = ns_set_ssl_cert(&server->ns_server, value);
if (res == -2) {
error_msg = "Cannot load PEM";
} else if (res == -3) {
error_msg = "SSL not enabled";
} else if (res == -1) {
error_msg = "SSL_CTX_new() failed";
} else if (ind == SSL_CA_CERTIFICATE) {
if (ns_set_ssl_ca_cert(&server->ns_server, value) != 0) {
error_msg = "Error setting CA cert";
}
Sergey Lyubka
committed
#endif
static void set_ips(struct ns_connection *nc, int is_rem) {
struct connection *conn = (struct connection *) nc->connection_data;
struct mg_connection *c = &conn->mg_conn;
char buf[100];
ns_sock_to_str(nc->sock, buf, sizeof(buf), is_rem ? 7 : 3);
sscanf(buf, "%47[^:]:%hu",
is_rem ? c->remote_ip : c->local_ip,
is_rem ? &c->remote_port : &c->local_port);
//DBG(("%p %s %s", conn, is_rem ? "rem" : "loc", buf));
static void on_accept(struct ns_connection *nc, union socket_address *sa) {
struct mg_server *server = (struct mg_server *) nc->server;
struct connection *conn;
if (!check_acl(server->config_options[ACCESS_CONTROL_LIST],
ntohl(* (uint32_t *) &sa->sin.sin_addr)) ||
(conn = (struct connection *) calloc(1, sizeof(*conn))) == NULL) {
nc->flags |= NSF_CLOSE_IMMEDIATELY;
} else {
// Circularly link two connection structures
nc->connection_data = conn;
conn->ns_conn = nc;
// Initialize the rest of connection attributes
conn->server = server;
conn->mg_conn.server_param = nc->server->server_data;
#ifndef MONGOOSE_NO_FILESYSTEM
static void hexdump(struct ns_connection *nc, const char *path,
int num_bytes, int is_sent) {
const struct iobuf *io = is_sent ? &nc->send_iobuf : &nc->recv_iobuf;
FILE *fp;
if (path != NULL && (fp = fopen(path, "a")) != NULL) {
ns_sock_to_str(nc->sock, src, sizeof(src), 3);
ns_sock_to_str(nc->sock, dst, sizeof(dst), 7);
fprintf(fp, "%lu %p %s %s %s %d\n", (unsigned long) time(NULL),
nc->connection_data, src,
is_sent == 0 ? "<-" : is_sent == 1 ? "->" :
if (num_bytes > 0 && (buf = (char *) malloc(buf_size)) != NULL) {
ns_hexdump(io->buf + (is_sent ? 0 : io->len) - (is_sent ? 0 : num_bytes),
num_bytes, buf, buf_size);
fprintf(fp, "%s", buf);
free(buf);
}
fclose(fp);
}
}
static void mg_ev_handler(struct ns_connection *nc, enum ns_event ev, void *p) {
struct connection *conn = (struct connection *) nc->connection_data;
struct mg_server *server = (struct mg_server *) nc->server;
Sergey Lyubka
committed
// Send NS event to the handler. Note that call_user won't send an event
// if conn == NULL. Therefore, repeat this for NS_ACCEPT event as well.
#ifdef MONGOOSE_SEND_NS_EVENTS
{
struct connection *conn = (struct connection *) nc->connection_data;
if (conn != NULL) conn->mg_conn.callback_param = p;
call_user(conn, (enum mg_event) ev);
}
#endif
switch (ev) {
case NS_ACCEPT:
on_accept(nc, (union socket_address *) p);
#ifndef MONGOOSE_NO_FILESYSTEM
hexdump(nc, server->config_options[HEXDUMP_FILE], 0, 2);
#endif
Sergey Lyubka
committed
#ifdef MONGOOSE_SEND_NS_EVENTS
{
struct connection *conn = (struct connection *) nc->connection_data;
if (conn != NULL) conn->mg_conn.callback_param = p;
call_user(conn, (enum mg_event) ev);
}
#endif
if (nc->connection_data != NULL) {
set_ips(nc, 1);
set_ips(nc, 0);
}
#ifndef MONGOOSE_NO_FILESYSTEM
hexdump(nc, server->config_options[HEXDUMP_FILE], 0, 3);
#endif
conn->mg_conn.status_code = * (int *) p;
if (conn->mg_conn.status_code != 0 ||
(!(nc->flags & MG_PROXY_CONN) &&
call_user(conn, MG_CONNECT) == MG_FALSE)) {
#ifndef MONGOOSE_NO_FILESYSTEM
hexdump(nc, server->config_options[HEXDUMP_FILE], * (int *) p, 0);
#ifndef MONGOOSE_NO_CGI
} else if (nc->flags & MG_CGI_CONN) {
on_cgi_data(nc);
} else if (nc->flags & MG_PROXY_CONN) {
if (conn != NULL) {
ns_forward(nc, conn->ns_conn);
}
} else {
process_response(conn);
}
break;
case NS_SEND:
#ifndef MONGOOSE_NO_FILESYSTEM
hexdump(nc, server->config_options[HEXDUMP_FILE], * (int *) p, 1);
break;
case NS_CLOSE:
nc->connection_data = NULL;
if (nc->flags & (MG_CGI_CONN | MG_PROXY_CONN)) {
DBG(("%p %p closing cgi/proxy conn", conn, nc));
if (conn && conn->ns_conn) {
conn->ns_conn->flags &= ~NSF_BUFFER_BUT_DONT_SEND;
conn->ns_conn->flags |= conn->ns_conn->send_iobuf.len > 0 ?
NSF_FINISHED_SENDING_DATA : NSF_CLOSE_IMMEDIATELY;
conn->endpoint.nc = NULL;
}
DBG(("%p %p %d closing", conn, nc, conn->endpoint_type));
if (conn->endpoint_type == EP_CLIENT && nc->recv_iobuf.len > 0) {
call_user(conn, MG_CLOSE);
free(conn);
}
break;
case NS_POLL:
if (call_user(conn, MG_POLL) == MG_TRUE) {
nc->flags |= NSF_FINISHED_SENDING_DATA;
}
if (conn != NULL && conn->endpoint_type == EP_FILE) {
transfer_file_data(conn);
}
// Expire idle connections
{
time_t current_time = * (time_t *) p;
if (conn != NULL && conn->mg_conn.is_websocket) {
ping_idle_websocket_connection(conn, current_time);
}
if (nc->last_io_time + MONGOOSE_IDLE_TIMEOUT_SECONDS < current_time) {
mg_ev_handler(nc, NS_CLOSE, NULL);
nc->flags |= NSF_CLOSE_IMMEDIATELY;
}
}
break;
default:
break;
}
}
static void iter2(struct ns_connection *nc, enum ns_event ev, void *param) {
mg_handler_t func = NULL;
struct connection *conn = (struct connection *) nc->connection_data;
const char *msg = (const char *) param;
int n;
(void) ev;
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
if (sscanf(msg, "%p %n", &func, &n) && func != NULL) {
conn->mg_conn.callback_param = (void *) (msg + n);
func(&conn->mg_conn, MG_POLL);
}
}
void mg_wakeup_server_ex(struct mg_server *server, mg_handler_t cb,
const char *fmt, ...) {
va_list ap;
char buf[8 * 1024];
int len;
// Encode callback (cb) into a buffer
len = snprintf(buf, sizeof(buf), "%p ", cb);
va_start(ap, fmt);
len += vsnprintf(buf + len, sizeof(buf) - len, fmt, ap);
va_end(ap);
// "len + 1" is to include terminating \0 in the message
ns_server_wakeup_ex(&server->ns_server, iter2, buf, len + 1);
}
void mg_wakeup_server(struct mg_server *server) {
ns_server_wakeup_ex(&server->ns_server, NULL, (void *) "", 0);
Sergey Lyubka
committed
void mg_set_listening_socket(struct mg_server *server, int sock) {
if (server->ns_server.listening_sock != INVALID_SOCKET) {
closesocket(server->ns_server.listening_sock);
Sergey Lyubka
committed
}
server->ns_server.listening_sock = (sock_t) sock;
Sergey Lyubka
committed
}
int mg_get_listening_socket(struct mg_server *server) {
Sergey Lyubka
committed
}
const char *mg_get_option(const struct mg_server *server, const char *name) {
const char **opts = (const char **) server->config_options;
int i = get_option_index(name);
return i == -1 ? NULL : opts[i] == NULL ? "" : opts[i];
}
struct mg_server *mg_create_server(void *server_data, mg_handler_t handler) {
struct mg_server *server = (struct mg_server *) calloc(1, sizeof(*server));
ns_server_init(&server->ns_server, server_data, mg_ev_handler);
set_default_option_values(server->config_options);
server->event_handler = handler;