mirror of
https://github.com/cesanta/mongoose.git
synced 2025-06-12 12:42:50 +08:00
wrapped read_from_cgi() into NO_CGI
This commit is contained in:
parent
47b92e4145
commit
aa59dda966
229
mongoose.c
229
mongoose.c
@ -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]) {
|
|||||||
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) {
|
|||||||
|
|
||||||
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) {
|
|||||||
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,
|
|||||||
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) {
|
|||||||
#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) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
|||||||
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);
|
||||||
|
Loading…
Reference in New Issue
Block a user