Commit aa59dda9 authored by Sergey Lyubka's avatar Sergey Lyubka

wrapped read_from_cgi() into NO_CGI

parent 47b92e41
...@@ -516,6 +516,63 @@ static int mg_snprintf(char *buf, size_t buflen, const char *fmt, ...) { ...@@ -516,6 +516,63 @@ static int mg_snprintf(char *buf, size_t buflen, const char *fmt, ...) {
return n; return n;
} }
// Check whether full request is buffered. Return:
// -1 if request is malformed
// 0 if request is not yet fully buffered
// >0 actual request length, including last \r\n\r\n
static int get_request_len(const char *s, int buf_len) {
const unsigned char *buf = (unsigned char *) s;
int i;
for (i = 0; i < buf_len; i++) {
// Control characters are not allowed but >=128 are.
// Abort scan as soon as one malformed character is found.
if (!isprint(buf[i]) && buf[i] != '\r' && buf[i] != '\n' && buf[i] < 128) {
return -1;
} else if (buf[i] == '\n' && i + 1 < buf_len && buf[i + 1] == '\n') {
return i + 2;
} else if (buf[i] == '\n' && i + 2 < buf_len && buf[i + 1] == '\r' &&
buf[i + 2] == '\n') {
return i + 3;
}
}
return 0;
}
// Skip the characters until one of the delimiters characters found.
// 0-terminate resulting word. Skip the rest of the delimiters if any.
// Advance pointer to buffer to the next word. Return found 0-terminated word.
static char *skip(char **buf, const char *delimiters) {
char *p, *begin_word, *end_word, *end_delimiters;
begin_word = *buf;
end_word = begin_word + strcspn(begin_word, delimiters);
end_delimiters = end_word + strspn(end_word, delimiters);
for (p = end_word; p < end_delimiters; p++) {
*p = '\0';
}
*buf = end_delimiters;
return begin_word;
}
// Parse HTTP headers from the given buffer, advance buffer to the point
// where parsing stopped.
static void parse_http_headers(char **buf, struct mg_connection *ri) {
size_t i;
for (i = 0; i < ARRAY_SIZE(ri->http_headers); i++) {
ri->http_headers[i].name = skip(buf, ": ");
ri->http_headers[i].value = skip(buf, "\r\n");
if (ri->http_headers[i].name[0] == '\0')
break;
ri->num_headers = i + 1;
}
}
static const char *status_code_to_str(int status_code) { static const char *status_code_to_str(int status_code) {
switch (status_code) { switch (status_code) {
case 200: return "OK"; case 200: return "OK";
...@@ -677,6 +734,10 @@ static int mg_socketpair(sock_t sp[2]) { ...@@ -677,6 +734,10 @@ static int mg_socketpair(sock_t sp[2]) {
return ret; return ret;
} }
static int is_error(int n) {
return n == 0 || (n < 0 && errno != EINTR && errno != EAGAIN);
}
#ifndef NO_CGI #ifndef NO_CGI
#ifdef _WIN32 #ifdef _WIN32
static pid_t start_process(char *interp, const char *cmd, const char *env, static pid_t start_process(char *interp, const char *cmd, const char *env,
...@@ -941,6 +1002,58 @@ static void open_cgi_endpoint(struct connection *conn, const char *prog) { ...@@ -941,6 +1002,58 @@ static void open_cgi_endpoint(struct connection *conn, const char *prog) {
closesocket(fds[1]); closesocket(fds[1]);
} }
static void read_from_cgi(struct connection *conn) {
char buf[IOBUF_SIZE];
int len, n = recv(conn->endpoint.cgi_sock, buf, sizeof(buf), 0);
DBG(("%p %d", conn, n));
if (is_error(n)) {
close_local_endpoint(conn);
} else if (n > 0) {
if (conn->num_bytes_sent == 0 && conn->remote_iobuf.len == 0) {
// Parse CGI headers, and modify the reply line if needed
if ((len = get_request_len(buf, n)) > 0) {
const char *status = NULL;
char *s = buf, buf2[sizeof(buf)];
struct mg_connection c;
int i, k;
memset(&c, 0, sizeof(c));
buf[len - 1] = '\0';
parse_http_headers(&s, &c);
if (mg_get_header(&c, "Location") != NULL) {
status = "302 Moved";
} else if ((status = (char *) mg_get_header(&c, "Status")) == NULL) {
status = "200 OK";
}
k = mg_snprintf(buf2, sizeof(buf2), "HTTP/1.1 %s\r\n", status);
spool(&conn->remote_iobuf, buf2, k);
for (i = 0; i < c.num_headers; i++) {
k = mg_snprintf(buf2, sizeof(buf2), "%s: %s\r\n",
c.http_headers[i].name, c.http_headers[i].value);
spool(&conn->remote_iobuf, buf2, k);
}
spool(&conn->remote_iobuf, "\r\n", 2);
memmove(buf, buf + len, n - len);
n -= len;
} else {
static const char ok_200[] = "HTTP/1.1 200 OK\r\n";
spool(&conn->remote_iobuf, ok_200, sizeof(ok_200) - 1);
}
}
spool(&conn->remote_iobuf, buf, n);
}
}
static void forward_post_data(struct connection *conn) {
struct iobuf *io = &conn->local_iobuf;
int n = send(conn->endpoint.cgi_sock, io->buf, io->len, 0);
if (n > 0) {
memmove(io->buf, io->buf + n, io->len - n);
io->len -= n;
}
}
#endif // !NO_CGI #endif // !NO_CGI
// 'sa' must be an initialized address to bind to // 'sa' must be an initialized address to bind to
...@@ -1079,50 +1192,6 @@ static void close_conn(struct connection *conn) { ...@@ -1079,50 +1192,6 @@ static void close_conn(struct connection *conn) {
free(conn); free(conn);
} }
// Check whether full request is buffered. Return:
// -1 if request is malformed
// 0 if request is not yet fully buffered
// >0 actual request length, including last \r\n\r\n
static int get_request_len(const char *s, int buf_len) {
const unsigned char *buf = (unsigned char *) s;
int i;
for (i = 0; i < buf_len; i++) {
// Control characters are not allowed but >=128 are.
// Abort scan as soon as one malformed character is found.
if (!isprint(buf[i]) && buf[i] != '\r' && buf[i] != '\n' && buf[i] < 128) {
return -1;
} else if (buf[i] == '\n' && i + 1 < buf_len && buf[i + 1] == '\n') {
return i + 2;
} else if (buf[i] == '\n' && i + 2 < buf_len && buf[i + 1] == '\r' &&
buf[i + 2] == '\n') {
return i + 3;
}
}
return 0;
}
// Skip the characters until one of the delimiters characters found.
// 0-terminate resulting word. Skip the rest of the delimiters if any.
// Advance pointer to buffer to the next word. Return found 0-terminated word.
static char *skip(char **buf, const char *delimiters) {
char *p, *begin_word, *end_word, *end_delimiters;
begin_word = *buf;
end_word = begin_word + strcspn(begin_word, delimiters);
end_delimiters = end_word + strspn(end_word, delimiters);
for (p = end_word; p < end_delimiters; p++) {
*p = '\0';
}
*buf = end_delimiters;
return begin_word;
}
// Protect against directory disclosure attack by removing '..', // Protect against directory disclosure attack by removing '..',
// excessive '/' and '\' characters // excessive '/' and '\' characters
static void remove_double_dots_and_double_slashes(char *s) { static void remove_double_dots_and_double_slashes(char *s) {
...@@ -1167,20 +1236,6 @@ int mg_url_decode(const char *src, int src_len, char *dst, ...@@ -1167,20 +1236,6 @@ int mg_url_decode(const char *src, int src_len, char *dst,
return i >= src_len ? j : -1; return i >= src_len ? j : -1;
} }
// Parse HTTP headers from the given buffer, advance buffer to the point
// where parsing stopped.
static void parse_http_headers(char **buf, struct mg_connection *ri) {
size_t i;
for (i = 0; i < ARRAY_SIZE(ri->http_headers); i++) {
ri->http_headers[i].name = skip(buf, ": ");
ri->http_headers[i].value = skip(buf, "\r\n");
if (ri->http_headers[i].name[0] == '\0')
break;
ri->num_headers = i + 1;
}
}
static int is_valid_http_method(const char *method) { static int is_valid_http_method(const char *method) {
return !strcmp(method, "GET") || !strcmp(method, "POST") || return !strcmp(method, "GET") || !strcmp(method, "POST") ||
!strcmp(method, "HEAD") || !strcmp(method, "CONNECT") || !strcmp(method, "HEAD") || !strcmp(method, "CONNECT") ||
...@@ -1688,10 +1743,6 @@ static void ping_idle_websocket_connection(struct connection *conn, time_t t) { ...@@ -1688,10 +1743,6 @@ static void ping_idle_websocket_connection(struct connection *conn, time_t t) {
#define ping_idle_websocket_connection(conn, t) #define ping_idle_websocket_connection(conn, t)
#endif // !NO_WEBSOCKET #endif // !NO_WEBSOCKET
static int is_error(int n) {
return n == 0 || (n < 0 && errno != EINTR && errno != EAGAIN);
}
static void write_to_client(struct connection *conn) { static void write_to_client(struct connection *conn) {
struct iobuf *io = &conn->remote_iobuf; struct iobuf *io = &conn->remote_iobuf;
int n = conn->ssl == NULL ? send(conn->client_sock, io->buf, io->len, 0) : int n = conn->ssl == NULL ? send(conn->client_sock, io->buf, io->len, 0) :
...@@ -3183,15 +3234,6 @@ static void send_continue_if_expected(struct connection *conn) { ...@@ -3183,15 +3234,6 @@ static void send_continue_if_expected(struct connection *conn) {
} }
} }
static void forward_post_data(struct connection *conn) {
struct iobuf *io = &conn->local_iobuf;
int n = send(conn->endpoint.cgi_sock, io->buf, io->len, 0);
if (n > 0) {
memmove(io->buf, io->buf + n, io->len - n);
io->len -= n;
}
}
static int is_valid_uri(const char *uri) { static int is_valid_uri(const char *uri) {
// Conform to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2 // Conform to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
// URI can be an asterisk (*) or should start with slash. // URI can be an asterisk (*) or should start with slash.
...@@ -3275,49 +3317,6 @@ static void read_from_client(struct connection *conn) { ...@@ -3275,49 +3317,6 @@ static void read_from_client(struct connection *conn) {
} }
} }
static void read_from_cgi(struct connection *conn) {
char buf[IOBUF_SIZE];
int len, n = recv(conn->endpoint.cgi_sock, buf, sizeof(buf), 0);
DBG(("%p %d", conn, n));
if (is_error(n)) {
close_local_endpoint(conn);
} else if (n > 0) {
if (conn->num_bytes_sent == 0 && conn->remote_iobuf.len == 0) {
// Parse CGI headers, and modify the reply line if needed
if ((len = get_request_len(buf, n)) > 0) {
const char *status = NULL;
char *s = buf, buf2[sizeof(buf)];
struct mg_connection c;
int i, k;
memset(&c, 0, sizeof(c));
buf[len - 1] = '\0';
parse_http_headers(&s, &c);
if (mg_get_header(&c, "Location") != NULL) {
status = "302 Moved";
} else if ((status = (char *) mg_get_header(&c, "Status")) == NULL) {
status = "200 OK";
}
k = mg_snprintf(buf2, sizeof(buf2), "HTTP/1.1 %s\r\n", status);
spool(&conn->remote_iobuf, buf2, k);
for (i = 0; i < c.num_headers; i++) {
k = mg_snprintf(buf2, sizeof(buf2), "%s: %s\r\n",
c.http_headers[i].name, c.http_headers[i].value);
spool(&conn->remote_iobuf, buf2, k);
}
spool(&conn->remote_iobuf, "\r\n", 2);
memmove(buf, buf + len, n - len);
n -= len;
} else {
static const char ok_200[] = "HTTP/1.1 200 OK\r\n";
spool(&conn->remote_iobuf, ok_200, sizeof(ok_200) - 1);
}
}
spool(&conn->remote_iobuf, buf, n);
}
}
#ifndef NO_LOGGING #ifndef NO_LOGGING
static void log_header(const struct mg_connection *conn, const char *header, static void log_header(const struct mg_connection *conn, const char *header,
FILE *fp) { FILE *fp) {
...@@ -3475,10 +3474,12 @@ unsigned int mg_poll_server(struct mg_server *server, int milliseconds) { ...@@ -3475,10 +3474,12 @@ unsigned int mg_poll_server(struct mg_server *server, int milliseconds) {
conn->last_activity_time = current_time; conn->last_activity_time = current_time;
read_from_client(conn); read_from_client(conn);
} }
#ifndef NO_CGI
if (conn->endpoint_type == EP_CGI && if (conn->endpoint_type == EP_CGI &&
FD_ISSET(conn->endpoint.cgi_sock, &read_set)) { FD_ISSET(conn->endpoint.cgi_sock, &read_set)) {
read_from_cgi(conn); read_from_cgi(conn);
} }
#endif
if (FD_ISSET(conn->client_sock, &write_set)) { if (FD_ISSET(conn->client_sock, &write_set)) {
conn->last_activity_time = current_time; conn->last_activity_time = current_time;
write_to_client(conn); write_to_client(conn);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment