Newer
Older
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
buf[0] += a;
buf[1] += b;
buf[2] += c;
buf[3] += d;
static void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len) {
uint32_t t;
t = ctx->bits[0];
if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
ctx->bits[1]++;
ctx->bits[1] += len >> 29;
if (t) {
unsigned char *p = (unsigned char *) ctx->in + t;
t = 64 - t;
if (len < t) {
memcpy(p, buf, len);
return;
memcpy(p, buf, t);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
buf += t;
len -= t;
while (len >= 64) {
memcpy(ctx->in, buf, 64);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
buf += 64;
len -= 64;
static void MD5Final(unsigned char digest[16], MD5_CTX *ctx) {
unsigned count;
unsigned char *p;
uint32_t *a;
count = (ctx->bits[0] >> 3) & 0x3F;
p = ctx->in + count;
*p++ = 0x80;
count = 64 - 1 - count;
if (count < 8) {
memset(p, 0, count);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
memset(ctx->in, 0, 56);
a = (uint32_t *)ctx->in;
a[14] = ctx->bits[0];
a[15] = ctx->bits[1];
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
byteReverse((unsigned char *) ctx->buf, 4);
memcpy(digest, ctx->buf, 16);
memset((char *) ctx, 0, sizeof(*ctx));
// Stringify binary data. Output buffer must be twice as big as input,
// because each byte takes 2 bytes in string representation
static void bin2str(char *to, const unsigned char *p, size_t len) {
static const char *hex = "0123456789abcdef";
for (; len--; p++) {
*to++ = hex[p[0] >> 4];
*to++ = hex[p[0] & 0x0f];
// Return stringified MD5 hash for list of strings. Buffer must be 33 bytes.
char *mg_md5(char buf[33], ...) {
unsigned char hash[16];
const char *p;
va_list ap;
MD5_CTX ctx;
va_start(ap, buf);
while ((p = va_arg(ap, const char *)) != NULL) {
MD5Update(&ctx, (const unsigned char *) p, (unsigned) strlen(p));
MD5Final(hash, &ctx);
bin2str(buf, hash, sizeof(hash));
return buf;
// Check the user's password, return 1 if OK
static int check_password(const char *method, const char *ha1, const char *uri,
const char *nonce, const char *nc, const char *cnonce,
const char *qop, const char *response) {
char ha2[32 + 1], expected_response[32 + 1];
#if 0
// Check for authentication timeout
if ((unsigned long) time(NULL) - (unsigned long) to64(nonce) > 3600 * 2) {
mg_md5(ha2, method, ":", uri, NULL);
mg_md5(expected_response, ha1, ":", nonce, ":", nc,
":", cnonce, ":", qop, ":", ha2, NULL);
return mg_strcasecmp(response, expected_response) == 0 ? MG_TRUE : MG_FALSE;
// Authorize against the opened passwords file. Return 1 if authorized.
int mg_authorize_digest(struct mg_connection *c, FILE *fp) {
char line[256], f_user[256], ha1[256], f_domain[256], user[100], nonce[100],
uri[MAX_REQUEST_SIZE], cnonce[100], resp[100], qop[100], nc[100];
if (c == NULL || fp == NULL) return 0;
if ((hdr = mg_get_header(c, "Authorization")) == NULL ||
mg_strncasecmp(hdr, "Digest ", 7) != 0) return 0;
if (!mg_parse_header(hdr, "username", user, sizeof(user))) return 0;
if (!mg_parse_header(hdr, "cnonce", cnonce, sizeof(cnonce))) return 0;
if (!mg_parse_header(hdr, "response", resp, sizeof(resp))) return 0;
if (!mg_parse_header(hdr, "uri", uri, sizeof(uri))) return 0;
if (!mg_parse_header(hdr, "qop", qop, sizeof(qop))) return 0;
if (!mg_parse_header(hdr, "nc", nc, sizeof(nc))) return 0;
if (!mg_parse_header(hdr, "nonce", nonce, sizeof(nonce))) return 0;
while (fgets(line, sizeof(line), fp) != NULL) {
if (sscanf(line, "%[^:]:%[^:]:%s", f_user, f_domain, ha1) == 3 &&
!strcmp(user, f_user) &&
// NOTE(lsm): due to a bug in MSIE, we do not compare URIs
!strcmp(conn->server->config_options[AUTH_DOMAIN], f_domain))
return check_password(c->request_method, ha1, uri,
// Return 1 if request is authorised, 0 otherwise.
static int is_authorized(struct connection *conn, const char *path,
int authorized = MG_TRUE;
if ((fp = open_auth_file(conn, path, is_directory)) != NULL) {
authorized = mg_authorize_digest(&conn->mg_conn, fp);
static int is_authorized_for_dav(struct connection *conn) {
const char *auth_file = conn->server->config_options[DAV_AUTH_FILE];
const char *method = conn->mg_conn.request_method;
int authorized = MG_FALSE;
// If dav_auth_file is not set, allow non-authorized PROPFIND
if (method != NULL && !strcmp(method, "PROPFIND") && auth_file == NULL) {
authorized = MG_TRUE;
} else if (auth_file != NULL && (fp = fopen(auth_file, "r")) != NULL) {
authorized = mg_authorize_digest(&conn->mg_conn, fp);
#endif // MONGOOSE_NO_AUTH
static int parse_header(const char *str, size_t str_len, const char *var_name,
int ch = ' ', ch1 = ',', len = 0;
size_t n = strlen(var_name);
const char *p, *end = str + str_len, *s = NULL;
if (buf != NULL && buf_size > 0) buf[0] = '\0';
for (s = str; s != NULL && s + n < end; s++) {
if ((s == str || s[-1] == ch || s[-1] == ch1) && s[n] == '=' &&
while (p < end && p[0] != ch && p[0] != ch1 && len < (int) buf_size) {
if (ch == ch1 && p[0] == '\\' && p[1] == ch) p++;
buf[len++] = *p++;
}
if (len >= (int) buf_size || (ch != ' ' && *p != ch)) {
len = 0;
} else {
if (len > 0 && s[len - 1] == ',') len--;
int mg_parse_header(const char *s, const char *var_name, char *buf,
return parse_header(s, s == NULL ? 0 : strlen(s), var_name, buf, buf_size);
#ifndef MONGOOSE_NO_SSI
static void send_ssi_file(struct mg_connection *, const char *, FILE *, int);
static void send_file_data(struct mg_connection *conn, FILE *fp) {
char buf[IOBUF_SIZE];
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
mg_write(conn, buf, n);
}
}
static void do_ssi_include(struct mg_connection *conn, const char *ssi,
char *tag, int include_level) {
char file_name[IOBUF_SIZE], path[MAX_PATH_SIZE], *p;
char **opts = (MG_CONN_2_CONN(conn))->server->config_options;
FILE *fp;
// sscanf() is safe here, since send_ssi_file() also uses buffer
// of size MG_BUF_LEN to get the tag. So strlen(tag) is always < MG_BUF_LEN.
if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) {
// File name is relative to the webserver root
mg_snprintf(path, sizeof(path), "%s%c%s",
opts[DOCUMENT_ROOT], '/', file_name);
} else if (sscanf(tag, " abspath=\"%[^\"]\"", file_name) == 1) {
// File name is relative to the webserver working directory
// or it is absolute system path
mg_snprintf(path, sizeof(path), "%s", file_name);
} else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1 ||
sscanf(tag, " \"%[^\"]\"", file_name) == 1) {
// File name is relative to the current document
mg_snprintf(path, sizeof(path), "%s", ssi);
if ((p = strrchr(path, '/')) != NULL) {
p[1] = '\0';
}
mg_snprintf(path + strlen(path), sizeof(path) - strlen(path), "%s",
file_name);
} else {
mg_printf(conn, "Bad SSI #include: [%s]", tag);
return;
}
if ((fp = fopen(path, "rb")) == NULL) {
mg_printf(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s",
tag, path, strerror(errno));
} else {
ns_set_close_on_exec(fileno(fp));
if (mg_match_prefix(opts[SSI_PATTERN], strlen(opts[SSI_PATTERN]),
path) > 0) {
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
send_ssi_file(conn, path, fp, include_level + 1);
} else {
send_file_data(conn, fp);
}
fclose(fp);
}
}
#ifndef MONGOOSE_NO_POPEN
static void do_ssi_exec(struct mg_connection *conn, char *tag) {
char cmd[IOBUF_SIZE];
FILE *fp;
if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) {
mg_printf(conn, "Bad SSI #exec: [%s]", tag);
} else if ((fp = popen(cmd, "r")) == NULL) {
mg_printf(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(errno));
} else {
send_file_data(conn, fp);
pclose(fp);
}
}
#endif // !MONGOOSE_NO_POPEN
static void send_ssi_file(struct mg_connection *conn, const char *path,
FILE *fp, int include_level) {
char buf[IOBUF_SIZE];
int ch, offset, len, in_ssi_tag;
if (include_level > 10) {
mg_printf(conn, "SSI #include level is too deep (%s)", path);
return;
}
in_ssi_tag = len = offset = 0;
while ((ch = fgetc(fp)) != EOF) {
if (in_ssi_tag && ch == '>') {
in_ssi_tag = 0;
buf[len++] = (char) ch;
buf[len] = '\0';
assert(len <= (int) sizeof(buf));
if (len < 6 || memcmp(buf, "<!--#", 5) != 0) {
// Not an SSI tag, pass it
(void) mg_write(conn, buf, (size_t) len);
} else {
if (!memcmp(buf + 5, "include", 7)) {
do_ssi_include(conn, path, buf + 12, include_level);
#if !defined(MONGOOSE_NO_POPEN)
} else if (!memcmp(buf + 5, "exec", 4)) {
do_ssi_exec(conn, buf + 9);
#endif // !NO_POPEN
} else {
mg_printf(conn, "%s: unknown SSI " "command: \"%s\"", path, buf);
}
}
len = 0;
} else if (in_ssi_tag) {
if (len == 5 && memcmp(buf, "<!--#", 5) != 0) {
// Not an SSI tag
in_ssi_tag = 0;
} else if (len == (int) sizeof(buf) - 2) {
mg_printf(conn, "%s: SSI tag is too large", path);
len = 0;
}
buf[len++] = ch & 0xff;
} else if (ch == '<') {
in_ssi_tag = 1;
if (len > 0) {
mg_write(conn, buf, (size_t) len);
}
len = 0;
buf[len++] = ch & 0xff;
} else {
buf[len++] = ch & 0xff;
if (len == (int) sizeof(buf)) {
mg_write(conn, buf, (size_t) len);
len = 0;
}
}
}
// Send the rest of buffered data
if (len > 0) {
mg_write(conn, buf, (size_t) len);
}
}
static void handle_ssi_request(struct connection *conn, const char *path) {
FILE *fp;
struct vec mime_vec;
if ((fp = fopen(path, "rb")) == NULL) {
send_http_error(conn, 500, "fopen(%s): %s", path, strerror(errno));
} else {
ns_set_close_on_exec(fileno(fp));
get_mime_type(conn->server, path, &mime_vec);
conn->mg_conn.status_code = 200;
mg_printf(&conn->mg_conn,
"HTTP/1.1 %d OK\r\n"
"Content-Type: %.*s\r\n"
"Connection: close\r\n\r\n",
conn->mg_conn.status_code, (int) mime_vec.len, mime_vec.ptr);
send_ssi_file(&conn->mg_conn, path, fp, 0);
fclose(fp);
close_local_endpoint(conn);
}
}
#endif
static void proxy_request(struct ns_connection *pc, struct mg_connection *c) {
int i, sent_close_header = 0;
ns_printf(pc, "%s %s%s%s HTTP/%s\r\n", c->request_method, c->uri,
c->query_string ? "?" : "",
c->query_string ? c->query_string : "",
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");
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);
}
Sergey Lyubka
committed
#ifdef NS_ENABLE_SSL
int mg_terminate_ssl(struct mg_connection *c, const char *cert) {
static const char ok[] = "HTTP/1.0 200 OK\r\n\r\n";
struct connection *conn = MG_CONN_2_CONN(c);
SSL_CTX *ctx;
DBG(("%p MITM", conn));
if ((ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) return 0;
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.
// TODO(lsm): check for send() failure
send(conn->ns_conn->sock, ok, sizeof(ok) - 1, 0);
//DBG(("%p %lu %d SEND", c, (unsigned long) sizeof(ok) - 1, n));
Sergey Lyubka
committed
conn->ns_conn->send_iobuf.len = 0;
conn->endpoint_type = EP_USER; // To keep-alive in close_local_endpoint()
close_local_endpoint(conn); // Clean up current CONNECT request
if ((conn->ns_conn->ssl = SSL_new(ctx)) != NULL) {
SSL_set_fd(conn->ns_conn->ssl, conn->ns_conn->sock);
}
SSL_CTX_free(ctx);
return 1;
}
#endif
int mg_forward(struct mg_connection *c, const char *addr) {
static const char ok[] = "HTTP/1.1 200 OK\r\n\r\n";
struct connection *conn = MG_CONN_2_CONN(c);
struct ns_connection *pc;
if ((pc = ns_connect(&conn->server->ns_mgr, addr,
mg_ev_handler, conn)) == NULL) {
conn->ns_conn->flags |= NSF_CLOSE_IMMEDIATELY;
return 0;
}
// Interlink two connections
pc->flags |= MG_PROXY_CONN;
conn->endpoint_type = EP_PROXY;
conn->endpoint.nc = pc;
DBG(("%p [%s] [%s] -> %p %p", conn, c->uri, addr, pc, conn->ns_conn->ssl));
if (strcmp(c->request_method, "CONNECT") == 0) {
// For CONNECT request, reply with 200 OK. Tunnel is established.
// TODO(lsm): check for send() failure
(void) send(conn->ns_conn->sock, ok, sizeof(ok) - 1, 0);
} else {
// Strip "http://host:port" part from the URI
if (memcmp(c->uri, "http://", 7) == 0) c->uri += 7;
while (*c->uri != '\0' && *c->uri != '/') c->uri++;
proxy_request(pc, c);
}
return 1;
}
static void proxify_connection(struct connection *conn) {
char proto[10], host[500], cert[500], addr[1000];
Sergey Lyubka
committed
int n = 0;
proto[0] = host[0] = cert[0] = '\0';
if (sscanf(url, "%499[^: ]:%hu%n", host, &port, &n) != 2 &&
sscanf(url, "%9[a-z]://%499[^: ]:%hu%n", proto, host, &port, &n) != 3 &&
sscanf(url, "%9[a-z]://%499[^/ ]%n", proto, host, &n) != 2) {
n = 0;
}
snprintf(addr, sizeof(addr), "%s://%s:%hu",
conn->ns_conn->ssl != NULL ? "ssl" : "tcp", host, port);
if (n <= 0 || !mg_forward(c, addr)) {
conn->ns_conn->flags |= NSF_CLOSE_IMMEDIATELY;
}
}
#ifndef MONGOOSE_NO_FILESYSTEM
void mg_send_file_internal(struct mg_connection *c, const char *file_name,
file_stat_t *st, int exists,
const char *extra_headers) {
struct connection *conn = MG_CONN_2_CONN(c);
const int is_directory = S_ISDIR(st->st_mode);
#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
mg_snprintf(path, sizeof(path), "%s", file_name);
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);
#endif
} else {
send_http_error(conn, 403, NULL);
}
} else if (mg_match_prefix(cgi_pat, strlen(cgi_pat), path) > 0) {
#if !defined(MONGOOSE_NO_CGI)
open_cgi_endpoint(conn, path);
#else
send_http_error(conn, 501, NULL);
#endif // !MONGOOSE_NO_CGI
#ifndef MONGOOSE_NO_SSI
} else if (mg_match_prefix(conn->server->config_options[SSI_PATTERN],
strlen(conn->server->config_options[SSI_PATTERN]),
path) > 0) {
handle_ssi_request(conn, path);
send_http_error(conn, 304, NULL);
} else if ((conn->endpoint.fd = open(path, O_RDONLY | O_BINARY, 0)) != -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, extra_headers);
} else {
send_http_error(conn, 404, NULL);
}
}
void mg_send_file(struct mg_connection *c, const char *file_name,
const char *extra_headers) {
const int exists = stat(file_name, &st) == 0;
mg_send_file_internal(c, file_name, &st, exists, extra_headers);
#endif // !MONGOOSE_NO_FILESYSTEM
static void open_local_endpoint(struct connection *conn, int skip_user) {
#ifndef MONGOOSE_NO_FILESYSTEM
char path[MAX_PATH_SIZE];
file_stat_t st;
int exists = 0;
// 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 > %lu",
(unsigned long) MONGOOSE_POST_SIZE_LIMIT);
if (strcmp(conn->mg_conn.request_method, "CONNECT") == 0 ||
mg_strncasecmp(conn->mg_conn.uri, "http", 4) == 0) {
const char *enp = conn->server->config_options[ENABLE_PROXY];
if (enp == NULL || strcmp(enp, "yes") != 0) {
send_http_error(conn, 405, NULL);
} else {
proxify_connection(conn);
}
if (!strcmp(conn->mg_conn.request_method, "OPTIONS")) {
send_options(conn);
return;
#ifdef MONGOOSE_NO_FILESYSTEM
send_http_error(conn, 404, NULL);
exists = convert_uri_to_file_name(conn, path, sizeof(path), &st);
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);
Alex Skalozub
committed
} else if (must_hide_file(conn, path)) {
send_http_error(conn, 404, NULL);
} 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);
mg_send_file_internal(&conn->mg_conn, path, &st, exists, 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
mg_strncasecmp(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 *) NS_MALLOC(conn->request_len);
if (conn->request == NULL) {
conn->ns_conn->flags |= NSF_CLOSE_IMMEDIATELY;
return;
}
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) {
DBG(("%p parsing -> %d", conn, conn->request_len));
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) {
if (conn->endpoint.nc != NULL) do_proxy(conn);
DBG(("%p %d %lu %d", conn, conn->request_len, (unsigned long)io->len,
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) {
conn->mg_conn.content = io->buf;
conn->mg_conn.content_len = io->len;
n = call_user(conn, MG_RECV);
if (n < 0) {
conn->ns_conn->flags |= NSF_FINISHED_SENDING_DATA;
} else if ((size_t) n <= io->len) {
iobuf_remove(io, n);
}
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_recv = conn->request_len = 0;
NS_FREE(conn->request);
conn->request = NULL;
}
static void process_response(struct connection *conn) {
DBG(("%p %d %lu", conn, conn->request_len, (unsigned long)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 *addr) {
struct ns_connection *nsconn;
struct connection *conn;
nsconn = ns_connect(&server->ns_mgr, addr, mg_ev_handler, NULL);
if ((conn = (struct connection *) NS_CALLOC(1, sizeof(*conn))) == NULL) {
// Interlink two structs
conn->ns_conn = nsconn;
//conn->handler = handler;
conn->mg_conn.server_param = server->ns_mgr.user_data;
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 0",
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);
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;
#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);
NS_FREE(conn->request);
NS_FREE(conn->path_info);
conn->endpoint.nc = NULL;
conn->request = conn->path_info = NULL;
conn->cl = conn->num_bytes_recv = conn->request_len = 0;
conn->ns_conn->flags &= ~(NSF_FINISHED_SENDING_DATA |
NSF_BUFFER_BUT_DONT_SEND | NSF_CLOSE_IMMEDIATELY |
// Do not memset() the whole structure, as some of the fields
// (IP addresses & ports, server_param) must survive. Nullify the rest.
c->request_method = c->uri = c->http_version = c->query_string = NULL;
c->num_headers = c->status_code = c->is_websocket = c->content_len = 0;
c->connection_param = c->callback_param = 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];
// If output buffer is too big, don't send anything. Wait until
// mongoose drains already buffered data to the client.
if (conn->ns_conn->send_iobuf.len > sizeof(buf) * 2) return;
// Do not send anyt
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);
time_t mg_poll_server(struct mg_server *server, int milliseconds) {
return ns_mgr_poll(&server->ns_mgr, milliseconds);
void mg_destroy_server(struct mg_server **server) {
if (server != NULL && *server != NULL) {
ns_mgr_free(&s->ns_mgr);
for (i = 0; i < (int) ARRAY_SIZE(s->config_options); i++) {
NS_FREE(s->config_options[i]); // It is OK to free(NULL)
struct mg_connection *mg_next(struct mg_server *s, struct mg_connection *c) {
Sergey Lyubka
committed
struct ns_connection *nc = ns_next(&s->ns_mgr, c == NULL ? NULL :
MG_CONN_2_CONN(c)->ns_conn);
if (nc != NULL && nc->user_data != NULL) {
return & ((struct connection *) nc->user_data)->mg_conn;
} else {
return NULL;
}
static int get_var(const char *data, size_t data_len, const char *name,