2016-03-19 15:14:35 +00:00
|
|
|
#include "first.h"
|
|
|
|
|
2009-10-11 14:31:42 +00:00
|
|
|
#include "server.h"
|
|
|
|
#include "stat_cache.h"
|
|
|
|
#include "keyvalue.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "connections.h"
|
|
|
|
#include "joblist.h"
|
2016-04-22 03:57:14 +00:00
|
|
|
#include "response.h"
|
2009-10-11 14:31:42 +00:00
|
|
|
#include "http_chunk.h"
|
|
|
|
|
|
|
|
#include "plugin.h"
|
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
#include <sys/types.h>
|
2016-02-10 19:33:57 +00:00
|
|
|
#include "sys-mmap.h"
|
2009-10-11 14:31:42 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
#ifdef __WIN32
|
2009-10-11 14:31:42 +00:00
|
|
|
# include <winsock2.h>
|
2005-02-20 14:27:00 +00:00
|
|
|
#else
|
2009-10-11 14:31:42 +00:00
|
|
|
# include <sys/socket.h>
|
|
|
|
# include <sys/wait.h>
|
|
|
|
# include <netinet/in.h>
|
|
|
|
# include <arpa/inet.h>
|
2005-02-20 14:27:00 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <fdevent.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
2005-12-20 12:47:39 +00:00
|
|
|
#include <fcntl.h>
|
2005-02-20 14:27:00 +00:00
|
|
|
|
2016-12-23 05:31:23 +00:00
|
|
|
static int pipe_cloexec(int pipefd[2]) {
|
|
|
|
#ifdef HAVE_PIPE2
|
|
|
|
if (0 == pipe2(pipefd, O_CLOEXEC)) return 0;
|
|
|
|
#endif
|
|
|
|
return 0 == pipe(pipefd)
|
|
|
|
#ifdef FD_CLOEXEC
|
|
|
|
&& 0 == fcntl(pipefd[0], F_SETFD, FD_CLOEXEC)
|
|
|
|
&& 0 == fcntl(pipefd[1], F_SETFD, FD_CLOEXEC)
|
|
|
|
#endif
|
|
|
|
? 0
|
|
|
|
: -1;
|
|
|
|
}
|
2013-12-24 04:28:44 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
enum {EOL_UNSET, EOL_N, EOL_RN};
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
char **ptr;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
size_t size;
|
|
|
|
size_t used;
|
|
|
|
} char_array;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
pid_t *ptr;
|
|
|
|
size_t used;
|
|
|
|
size_t size;
|
|
|
|
} buffer_pid_t;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
array *cgi;
|
2009-07-04 20:23:00 +00:00
|
|
|
unsigned short execute_x_only;
|
2017-02-26 22:49:47 +00:00
|
|
|
unsigned short local_redir;
|
2016-04-22 03:57:14 +00:00
|
|
|
unsigned short xsendfile_allow;
|
|
|
|
array *xsendfile_docroot;
|
2005-02-20 14:27:00 +00:00
|
|
|
} plugin_config;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
PLUGIN_DATA;
|
|
|
|
buffer_pid_t cgi_pid;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
buffer *parse_response;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
plugin_config **config_storage;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
|
|
|
plugin_config conf;
|
2005-02-20 14:27:00 +00:00
|
|
|
} plugin_data;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
pid_t pid;
|
|
|
|
int fd;
|
2016-04-12 04:21:03 +00:00
|
|
|
int fdtocgi;
|
2005-02-20 14:27:00 +00:00
|
|
|
int fde_ndx; /* index into the fd-event buffer */
|
2016-04-12 04:21:03 +00:00
|
|
|
int fde_ndx_tocgi; /* index into the fd-event buffer */
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
connection *remote_conn; /* dumb pointer */
|
|
|
|
plugin_data *plugin_data; /* dumb pointer */
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
buffer *response;
|
|
|
|
buffer *response_header;
|
2016-12-16 16:06:29 +00:00
|
|
|
buffer *cgi_handler; /* dumb pointer */
|
2016-11-28 17:39:37 +00:00
|
|
|
plugin_config conf;
|
2005-02-20 14:27:00 +00:00
|
|
|
} handler_ctx;
|
|
|
|
|
2012-08-31 14:11:41 +00:00
|
|
|
static handler_ctx * cgi_handler_ctx_init(void) {
|
2005-02-20 14:27:00 +00:00
|
|
|
handler_ctx *hctx = calloc(1, sizeof(*hctx));
|
|
|
|
|
2014-02-16 13:08:20 +00:00
|
|
|
force_assert(hctx);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
hctx->response = buffer_init();
|
|
|
|
hctx->response_header = buffer_init();
|
2016-04-07 00:46:43 +00:00
|
|
|
hctx->fd = -1;
|
2016-04-12 04:21:03 +00:00
|
|
|
hctx->fdtocgi = -1;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return hctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cgi_handler_ctx_free(handler_ctx *hctx) {
|
|
|
|
buffer_free(hctx->response);
|
|
|
|
buffer_free(hctx->response_header);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
free(hctx);
|
|
|
|
}
|
|
|
|
|
2016-07-14 18:29:02 +00:00
|
|
|
enum {FDEVENT_HANDLED_UNSET, FDEVENT_HANDLED_FINISHED, FDEVENT_HANDLED_NOT_FINISHED, FDEVENT_HANDLED_COMEBACK, FDEVENT_HANDLED_ERROR};
|
2005-02-20 14:27:00 +00:00
|
|
|
|
|
|
|
INIT_FUNC(mod_cgi_init) {
|
|
|
|
plugin_data *p;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
p = calloc(1, sizeof(*p));
|
|
|
|
|
2014-02-16 13:08:20 +00:00
|
|
|
force_assert(p);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
p->parse_response = buffer_init();
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FREE_FUNC(mod_cgi_free) {
|
|
|
|
plugin_data *p = p_d;
|
|
|
|
buffer_pid_t *r = &(p->cgi_pid);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
UNUSED(srv);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (p->config_storage) {
|
|
|
|
size_t i;
|
|
|
|
for (i = 0; i < srv->config_context->used; i++) {
|
|
|
|
plugin_config *s = p->config_storage[i];
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2015-05-14 09:38:33 +00:00
|
|
|
if (NULL == s) continue;
|
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
array_free(s->cgi);
|
2016-04-22 03:57:14 +00:00
|
|
|
array_free(s->xsendfile_docroot);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
free(s);
|
|
|
|
}
|
|
|
|
free(p->config_storage);
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
|
|
|
|
if (r->ptr) free(r->ptr);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
buffer_free(p->parse_response);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
free(p);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return HANDLER_GO_ON;
|
|
|
|
}
|
|
|
|
|
|
|
|
SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) {
|
|
|
|
plugin_data *p = p_d;
|
|
|
|
size_t i = 0;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
|
|
|
config_values_t cv[] = {
|
2005-02-20 14:27:00 +00:00
|
|
|
{ "cgi.assign", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
|
2009-07-04 20:23:00 +00:00
|
|
|
{ "cgi.execute-x-only", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
|
2016-05-07 05:18:34 +00:00
|
|
|
{ "cgi.x-sendfile", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
|
|
|
|
{ "cgi.x-sendfile-docroot", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
|
2017-02-26 22:49:47 +00:00
|
|
|
{ "cgi.local-redir", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 4 */
|
2005-02-20 14:27:00 +00:00
|
|
|
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET}
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!p) return HANDLER_ERROR;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2013-11-13 11:43:26 +00:00
|
|
|
p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
|
2015-10-16 19:44:06 +00:00
|
|
|
force_assert(p->config_storage);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
for (i = 0; i < srv->config_context->used; i++) {
|
2015-11-07 12:51:11 +00:00
|
|
|
data_config const* config = (data_config const*)srv->config_context->data[i];
|
2005-02-20 14:27:00 +00:00
|
|
|
plugin_config *s;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-08-16 13:07:46 +00:00
|
|
|
s = calloc(1, sizeof(plugin_config));
|
2014-02-16 13:08:20 +00:00
|
|
|
force_assert(s);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
s->cgi = array_init();
|
2009-07-04 20:23:00 +00:00
|
|
|
s->execute_x_only = 0;
|
2017-02-26 22:49:47 +00:00
|
|
|
s->local_redir = 0;
|
2016-04-22 03:57:14 +00:00
|
|
|
s->xsendfile_allow= 0;
|
|
|
|
s->xsendfile_docroot = array_init();
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
cv[0].destination = s->cgi;
|
2009-07-04 20:23:00 +00:00
|
|
|
cv[1].destination = &(s->execute_x_only);
|
2016-04-22 03:57:14 +00:00
|
|
|
cv[2].destination = &(s->xsendfile_allow);
|
|
|
|
cv[3].destination = s->xsendfile_docroot;
|
2017-02-26 22:49:47 +00:00
|
|
|
cv[4].destination = &(s->local_redir);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
p->config_storage[i] = s;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2015-11-07 12:51:11 +00:00
|
|
|
if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
|
2005-02-20 14:27:00 +00:00
|
|
|
return HANDLER_ERROR;
|
|
|
|
}
|
2016-04-22 03:57:14 +00:00
|
|
|
|
2017-03-05 20:39:45 +00:00
|
|
|
if (!array_is_kvstring(s->cgi)) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s",
|
|
|
|
"unexpected value for cgi.assign; expected list of \"ext\" => \"exepath\"");
|
|
|
|
return HANDLER_ERROR;
|
|
|
|
}
|
|
|
|
|
2016-04-22 03:57:14 +00:00
|
|
|
if (s->xsendfile_docroot->used) {
|
|
|
|
size_t j;
|
|
|
|
for (j = 0; j < s->xsendfile_docroot->used; ++j) {
|
|
|
|
data_string *ds = (data_string *)s->xsendfile_docroot->data[j];
|
|
|
|
if (ds->type != TYPE_STRING) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s",
|
|
|
|
"unexpected type for key cgi.x-sendfile-docroot; expected: cgi.x-sendfile-docroot = ( \"/allowed/path\", ... )");
|
|
|
|
return HANDLER_ERROR;
|
|
|
|
}
|
|
|
|
if (ds->value->ptr[0] != '/') {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "SBs",
|
|
|
|
"cgi.x-sendfile-docroot paths must begin with '/'; invalid: \"", ds->value, "\"");
|
|
|
|
return HANDLER_ERROR;
|
|
|
|
}
|
|
|
|
buffer_path_simplify(ds->value, ds->value);
|
|
|
|
buffer_append_slash(ds->value);
|
|
|
|
}
|
|
|
|
}
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return HANDLER_GO_ON;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int cgi_pid_add(server *srv, plugin_data *p, pid_t pid) {
|
|
|
|
int m = -1;
|
|
|
|
size_t i;
|
|
|
|
buffer_pid_t *r = &(p->cgi_pid);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
UNUSED(srv);
|
|
|
|
|
|
|
|
for (i = 0; i < r->used; i++) {
|
|
|
|
if (r->ptr[i] > m) m = r->ptr[i];
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (r->size == 0) {
|
|
|
|
r->size = 16;
|
|
|
|
r->ptr = malloc(sizeof(*r->ptr) * r->size);
|
2015-10-16 19:44:06 +00:00
|
|
|
force_assert(r->ptr);
|
2005-02-20 14:27:00 +00:00
|
|
|
} else if (r->used == r->size) {
|
|
|
|
r->size += 16;
|
|
|
|
r->ptr = realloc(r->ptr, sizeof(*r->ptr) * r->size);
|
2015-10-16 19:44:06 +00:00
|
|
|
force_assert(r->ptr);
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
r->ptr[r->used++] = pid;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cgi_pid_del(server *srv, plugin_data *p, pid_t pid) {
|
|
|
|
size_t i;
|
|
|
|
buffer_pid_t *r = &(p->cgi_pid);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
UNUSED(srv);
|
|
|
|
|
|
|
|
for (i = 0; i < r->used; i++) {
|
|
|
|
if (r->ptr[i] == pid) break;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (i != r->used) {
|
|
|
|
/* found */
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (i != r->used - 1) {
|
|
|
|
r->ptr[i] = r->ptr[r->used - 1];
|
|
|
|
}
|
|
|
|
r->used--;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-08-17 21:04:20 +00:00
|
|
|
static int cgi_response_parse(server *srv, connection *con, plugin_data *p, buffer *in) {
|
2005-02-20 14:27:00 +00:00
|
|
|
char *ns;
|
|
|
|
const char *s;
|
|
|
|
int line = 0;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
UNUSED(srv);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2015-02-08 12:37:10 +00:00
|
|
|
buffer_copy_buffer(p->parse_response, in);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
|
|
|
for (s = p->parse_response->ptr;
|
2007-08-17 21:04:20 +00:00
|
|
|
NULL != (ns = strchr(s, '\n'));
|
|
|
|
s = ns + 1, line++) {
|
2005-02-20 14:27:00 +00:00
|
|
|
const char *key, *value;
|
|
|
|
int key_len;
|
|
|
|
data_string *ds;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2007-08-17 21:04:20 +00:00
|
|
|
/* strip the \n */
|
2005-02-20 14:27:00 +00:00
|
|
|
ns[0] = '\0';
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2007-08-17 21:04:20 +00:00
|
|
|
if (ns > s && ns[-1] == '\r') ns[-1] = '\0';
|
|
|
|
|
2006-10-04 13:26:23 +00:00
|
|
|
if (line == 0 &&
|
2005-02-20 14:27:00 +00:00
|
|
|
0 == strncmp(s, "HTTP/1.", 7)) {
|
|
|
|
/* non-parsed header ... we parse them anyway */
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if ((s[7] == '1' ||
|
|
|
|
s[7] == '0') &&
|
|
|
|
s[8] == ' ') {
|
|
|
|
int status;
|
|
|
|
/* after the space should be a status code for us */
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
status = strtol(s+9, NULL, 10);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2007-08-18 11:35:27 +00:00
|
|
|
if (status >= 100 &&
|
|
|
|
status < 1000) {
|
2005-02-20 14:27:00 +00:00
|
|
|
/* we expected 3 digits and didn't got them */
|
|
|
|
con->parsed_response |= HTTP_STATUS;
|
|
|
|
con->http_status = status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2007-08-17 21:04:20 +00:00
|
|
|
/* parse the headers */
|
2005-02-20 14:27:00 +00:00
|
|
|
key = s;
|
|
|
|
if (NULL == (value = strchr(s, ':'))) {
|
|
|
|
/* we expect: "<key>: <value>\r\n" */
|
|
|
|
continue;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
key_len = value - key;
|
|
|
|
value += 1;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* skip LWS */
|
|
|
|
while (*value == ' ' || *value == '\t') value++;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
|
|
|
|
ds = data_response_init();
|
|
|
|
}
|
|
|
|
buffer_copy_string_len(ds->key, key, key_len);
|
|
|
|
buffer_copy_string(ds->value, value);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
array_insert_unique(con->response.headers, (data_unset *)ds);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
switch(key_len) {
|
|
|
|
case 4:
|
|
|
|
if (0 == strncasecmp(key, "Date", key_len)) {
|
|
|
|
con->parsed_response |= HTTP_DATE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
if (0 == strncasecmp(key, "Status", key_len)) {
|
2016-03-26 10:58:49 +00:00
|
|
|
int status = strtol(value, NULL, 10);
|
|
|
|
if (status >= 100 && status < 1000) {
|
|
|
|
con->http_status = status;
|
|
|
|
con->parsed_response |= HTTP_STATUS;
|
|
|
|
} else {
|
|
|
|
con->http_status = 502;
|
|
|
|
}
|
2017-01-25 16:26:10 +00:00
|
|
|
/* do not send Status to client */
|
|
|
|
buffer_reset(ds->value);
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
if (0 == strncasecmp(key, "Location", key_len)) {
|
|
|
|
con->parsed_response |= HTTP_LOCATION;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
if (0 == strncasecmp(key, "Connection", key_len)) {
|
|
|
|
con->response.keep_alive = (0 == strcasecmp(value, "Keep-Alive")) ? 1 : 0;
|
|
|
|
con->parsed_response |= HTTP_CONNECTION;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 14:
|
|
|
|
if (0 == strncasecmp(key, "Content-Length", key_len)) {
|
2016-03-26 10:58:49 +00:00
|
|
|
con->response.content_length = strtoul(value, NULL, 10);
|
2005-02-20 14:27:00 +00:00
|
|
|
con->parsed_response |= HTTP_CONTENT_LENGTH;
|
|
|
|
}
|
|
|
|
break;
|
2017-02-05 01:33:45 +00:00
|
|
|
case 17:
|
|
|
|
if (0 == strncasecmp(key, "Transfer-Encoding", key_len)) {
|
|
|
|
con->parsed_response |= HTTP_TRANSFER_ENCODING;
|
|
|
|
}
|
|
|
|
break;
|
2005-02-20 14:27:00 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* CGI/1.1 rev 03 - 7.2.1.2 */
|
|
|
|
if ((con->parsed_response & HTTP_LOCATION) &&
|
|
|
|
!(con->parsed_response & HTTP_STATUS)) {
|
|
|
|
con->http_status = 302;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int cgi_demux_response(server *srv, handler_ctx *hctx) {
|
|
|
|
plugin_data *p = hctx->plugin_data;
|
|
|
|
connection *con = hctx->remote_conn;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
while(1) {
|
|
|
|
int n;
|
2011-03-13 17:44:39 +00:00
|
|
|
int toread;
|
|
|
|
|
|
|
|
#if defined(__WIN32)
|
2015-02-08 19:10:39 +00:00
|
|
|
buffer_string_prepare_copy(hctx->response, 4 * 1024);
|
2011-03-13 17:44:39 +00:00
|
|
|
#else
|
2017-01-13 20:00:32 +00:00
|
|
|
if (ioctl(hctx->fd, FIONREAD, &toread) || toread <= 4*1024) {
|
2015-02-08 19:10:39 +00:00
|
|
|
buffer_string_prepare_copy(hctx->response, 4 * 1024);
|
2011-03-13 17:44:39 +00:00
|
|
|
} else {
|
|
|
|
if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT;
|
2015-02-08 19:10:39 +00:00
|
|
|
buffer_string_prepare_copy(hctx->response, toread);
|
2011-03-13 17:44:39 +00:00
|
|
|
}
|
|
|
|
#endif
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (-1 == (n = read(hctx->fd, hctx->response->ptr, hctx->response->size - 1))) {
|
|
|
|
if (errno == EAGAIN || errno == EINTR) {
|
|
|
|
/* would block, wait for signal */
|
|
|
|
return FDEVENT_HANDLED_NOT_FINISHED;
|
|
|
|
}
|
|
|
|
/* error */
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sdd", strerror(errno), con->fd, hctx->fd);
|
|
|
|
return FDEVENT_HANDLED_ERROR;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (n == 0) {
|
|
|
|
/* read finished */
|
|
|
|
return FDEVENT_HANDLED_FINISHED;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2015-02-08 19:10:44 +00:00
|
|
|
buffer_commit(hctx->response, n);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* split header from body */
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (con->file_started == 0) {
|
2007-08-17 21:04:20 +00:00
|
|
|
int is_header = 0;
|
|
|
|
int is_header_end = 0;
|
|
|
|
size_t last_eol = 0;
|
2015-02-08 19:10:44 +00:00
|
|
|
size_t i, header_len;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
buffer_append_string_buffer(hctx->response_header, hctx->response);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2007-08-17 21:04:20 +00:00
|
|
|
/**
|
|
|
|
* we have to handle a few cases:
|
|
|
|
*
|
|
|
|
* nph:
|
|
|
|
*
|
|
|
|
* HTTP/1.0 200 Ok\n
|
|
|
|
* Header: Value\n
|
|
|
|
* \n
|
|
|
|
*
|
|
|
|
* CGI:
|
|
|
|
* Header: Value\n
|
|
|
|
* Status: 200\n
|
|
|
|
* \n
|
|
|
|
*
|
|
|
|
* and different mixes of \n and \r\n combinations
|
|
|
|
*
|
|
|
|
* Some users also forget about CGI and just send a response and hope
|
|
|
|
* we handle it. No headers, no header-content seperator
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* nph (non-parsed headers) */
|
2007-08-17 21:04:20 +00:00
|
|
|
if (0 == strncmp(hctx->response_header->ptr, "HTTP/1.", 7)) is_header = 1;
|
2015-02-08 19:10:44 +00:00
|
|
|
|
|
|
|
header_len = buffer_string_length(hctx->response_header);
|
|
|
|
for (i = 0; !is_header_end && i < header_len; i++) {
|
2007-08-17 21:04:20 +00:00
|
|
|
char c = hctx->response_header->ptr[i];
|
|
|
|
|
|
|
|
switch (c) {
|
|
|
|
case ':':
|
|
|
|
/* we found a colon
|
|
|
|
*
|
|
|
|
* looks like we have a normal header
|
|
|
|
*/
|
|
|
|
is_header = 1;
|
|
|
|
break;
|
|
|
|
case '\n':
|
|
|
|
/* EOL */
|
|
|
|
if (is_header == 0) {
|
|
|
|
/* we got a EOL but we don't seem to got a HTTP header */
|
|
|
|
|
|
|
|
is_header_end = 1;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
break;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2007-08-17 21:04:20 +00:00
|
|
|
/**
|
|
|
|
* check if we saw a \n(\r)?\n sequence
|
|
|
|
*/
|
|
|
|
if (last_eol > 0 &&
|
|
|
|
((i - last_eol == 1) ||
|
|
|
|
(i - last_eol == 2 && hctx->response_header->ptr[i - 1] == '\r'))) {
|
|
|
|
is_header_end = 1;
|
2005-02-20 14:27:00 +00:00
|
|
|
break;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2007-08-17 21:04:20 +00:00
|
|
|
last_eol = i;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2007-08-17 21:04:20 +00:00
|
|
|
break;
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2007-08-17 21:04:20 +00:00
|
|
|
if (is_header_end) {
|
|
|
|
if (!is_header) {
|
2005-02-20 14:27:00 +00:00
|
|
|
/* no header, but a body */
|
2016-05-25 20:45:09 +00:00
|
|
|
if (0 != http_chunk_append_buffer(srv, con, hctx->response_header)) {
|
|
|
|
return FDEVENT_HANDLED_ERROR;
|
|
|
|
}
|
2017-02-22 16:58:21 +00:00
|
|
|
if (0 == con->http_status) con->http_status = 200; /* OK */
|
2005-02-20 14:27:00 +00:00
|
|
|
} else {
|
2007-08-17 21:04:20 +00:00
|
|
|
const char *bstart;
|
|
|
|
size_t blen;
|
2015-02-08 19:10:44 +00:00
|
|
|
|
|
|
|
/* the body starts after the EOL */
|
|
|
|
bstart = hctx->response_header->ptr + i;
|
|
|
|
blen = header_len - i;
|
|
|
|
|
2007-08-17 21:04:20 +00:00
|
|
|
/**
|
|
|
|
* i still points to the char after the terminating EOL EOL
|
|
|
|
*
|
|
|
|
* put it on the last \n again
|
|
|
|
*/
|
|
|
|
i--;
|
2015-02-08 19:10:44 +00:00
|
|
|
|
2007-08-17 21:04:20 +00:00
|
|
|
/* string the last \r?\n */
|
|
|
|
if (i > 0 && (hctx->response_header->ptr[i - 1] == '\r')) {
|
|
|
|
i--;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2015-02-08 19:10:44 +00:00
|
|
|
buffer_string_set_length(hctx->response_header, i);
|
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* parse the response header */
|
2007-08-17 21:04:20 +00:00
|
|
|
cgi_response_parse(srv, con, p, hctx->response_header);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2017-01-25 16:22:39 +00:00
|
|
|
/* [RFC3875] 6.2.2 Local Redirect Response
|
|
|
|
*
|
|
|
|
* The CGI script can return a URI path and query-string
|
|
|
|
* ('local-pathquery') for a local resource in a Location header field.
|
|
|
|
* This indicates to the server that it should reprocess the request
|
|
|
|
* using the path specified.
|
|
|
|
*
|
|
|
|
* local-redir-response = local-Location NL
|
|
|
|
*
|
|
|
|
* The script MUST NOT return any other header fields or a message-body,
|
|
|
|
* and the server MUST generate the response that it would have produced
|
|
|
|
* in response to a request containing the URL
|
|
|
|
*
|
|
|
|
* scheme "://" server-name ":" server-port local-pathquery
|
|
|
|
*
|
|
|
|
* (Might not have begun to receive body yet, but do skip local-redir
|
|
|
|
* if we already have started receiving a response body (blen > 0))
|
|
|
|
* (Also, while not required by the RFC, do not send local-redir back
|
|
|
|
* to same URL, since CGI should have handled it internally if it
|
|
|
|
* really wanted to do that internally)
|
|
|
|
*/
|
2017-02-26 22:49:47 +00:00
|
|
|
if (hctx->conf.local_redir && con->http_status >= 300 && con->http_status < 400) {
|
2016-07-14 18:29:02 +00:00
|
|
|
/*(con->parsed_response & HTTP_LOCATION)*/
|
[mod_cgi] skip local-redir handling if to self (fixes #2779, #2108)
Loosen local redirect handling in mod_cgi to skip handling as local
redirect if the Location matches con->uri.path, since if the request
is intended to redirect back to the same CGI using the same request
method, path info, and query string, the CGI would logically just
return the final intended response. Loosening this handling avoids a
problem with applications (potentially) accessible through multiple
gateways, where the application is not aware of this specific handling
of Location in the Common Gateway Interface (CGI/1.1), the application
sends abs-path in the Location response header instead of absoluteURI,
and the application expects the client to receive this Location response
header instead of the server to process as a CGI local redirect.
One example of such an application is LuCI,
which sends Set-Cookie with Location: /abs-path
https://github.com/openwrt/luci
(Note that this loose check for matching con->uri.path is not perfect
and might not match if the CGI returned a path with a different case
and the server is on a case-insensitive filesystem, or if the path
returned by the CGI is rewritten elsewhere to a different con->uri.path
before getting to mod_cgi.)
RFC3875 CGI 1.1 specification section 6.2.2 Local Redirect Response
http://www.ietf.org/rfc/rfc3875
x-ref:
"CGI local-redir handling conflicts with LuCI redirect w/ Set-Cookie"
https://redmine.lighttpd.net/issues/2779
"CGI local redirect not implemented correctly"
https://redmine.lighttpd.net/issues/2108
2017-01-06 23:03:02 +00:00
|
|
|
size_t ulen = buffer_string_length(con->uri.path);
|
2016-07-14 18:29:02 +00:00
|
|
|
data_string *ds;
|
|
|
|
if (NULL != (ds = (data_string *) array_get_element(con->response.headers, "Location"))
|
[mod_cgi] skip local-redir handling if to self (fixes #2779, #2108)
Loosen local redirect handling in mod_cgi to skip handling as local
redirect if the Location matches con->uri.path, since if the request
is intended to redirect back to the same CGI using the same request
method, path info, and query string, the CGI would logically just
return the final intended response. Loosening this handling avoids a
problem with applications (potentially) accessible through multiple
gateways, where the application is not aware of this specific handling
of Location in the Common Gateway Interface (CGI/1.1), the application
sends abs-path in the Location response header instead of absoluteURI,
and the application expects the client to receive this Location response
header instead of the server to process as a CGI local redirect.
One example of such an application is LuCI,
which sends Set-Cookie with Location: /abs-path
https://github.com/openwrt/luci
(Note that this loose check for matching con->uri.path is not perfect
and might not match if the CGI returned a path with a different case
and the server is on a case-insensitive filesystem, or if the path
returned by the CGI is rewritten elsewhere to a different con->uri.path
before getting to mod_cgi.)
RFC3875 CGI 1.1 specification section 6.2.2 Local Redirect Response
http://www.ietf.org/rfc/rfc3875
x-ref:
"CGI local-redir handling conflicts with LuCI redirect w/ Set-Cookie"
https://redmine.lighttpd.net/issues/2779
"CGI local redirect not implemented correctly"
https://redmine.lighttpd.net/issues/2108
2017-01-06 23:03:02 +00:00
|
|
|
&& ds->value->ptr[0] == '/'
|
|
|
|
&& (0 != strncmp(ds->value->ptr, con->uri.path->ptr, ulen)
|
|
|
|
|| (ds->value->ptr[ulen] != '\0' && ds->value->ptr[ulen] != '/' && ds->value->ptr[ulen] != '?'))
|
2017-01-25 16:22:39 +00:00
|
|
|
&& 0 == blen
|
|
|
|
&& !(con->parsed_response & HTTP_STATUS) /* no "Status" or NPH response line */
|
|
|
|
&& 1 == con->response.headers->used) {
|
2016-07-14 18:29:02 +00:00
|
|
|
if (++con->loops_per_request > 5) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sb", "too many internal loops while processing request:", con->request.orig_uri);
|
|
|
|
con->http_status = 500; /* Internal Server Error */
|
|
|
|
con->mode = DIRECT;
|
|
|
|
return FDEVENT_HANDLED_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer_copy_buffer(con->request.uri, ds->value);
|
|
|
|
|
|
|
|
if (con->request.content_length) {
|
2016-12-16 16:06:29 +00:00
|
|
|
if (con->request.content_length != con->request_content_queue->bytes_in) {
|
2016-07-14 18:29:02 +00:00
|
|
|
con->keep_alive = 0;
|
|
|
|
}
|
|
|
|
con->request.content_length = 0;
|
|
|
|
chunkqueue_reset(con->request_content_queue);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (con->http_status != 307 && con->http_status != 308) {
|
|
|
|
/* Note: request body (if any) sent to initial dynamic handler
|
|
|
|
* and is not available to the internal redirect */
|
|
|
|
con->request.http_method = HTTP_METHOD_GET;
|
|
|
|
}
|
|
|
|
|
|
|
|
connection_response_reset(srv, con); /*(includes con->http_status = 0)*/
|
2017-02-20 19:47:13 +00:00
|
|
|
plugins_call_connection_reset(srv, con);
|
2016-07-14 18:29:02 +00:00
|
|
|
return FDEVENT_HANDLED_COMEBACK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-28 17:39:37 +00:00
|
|
|
if (hctx->conf.xsendfile_allow) {
|
2016-04-22 03:57:14 +00:00
|
|
|
data_string *ds;
|
|
|
|
if (NULL != (ds = (data_string *) array_get_element(con->response.headers, "X-Sendfile"))) {
|
2016-11-28 17:39:37 +00:00
|
|
|
http_response_xsendfile(srv, con, ds->value, hctx->conf.xsendfile_docroot);
|
2016-04-22 03:57:14 +00:00
|
|
|
return FDEVENT_HANDLED_FINISHED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-08-17 21:04:20 +00:00
|
|
|
if (blen > 0) {
|
2016-05-25 20:45:09 +00:00
|
|
|
if (0 != http_chunk_append_mem(srv, con, bstart, blen)) {
|
|
|
|
return FDEVENT_HANDLED_ERROR;
|
|
|
|
}
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
con->file_started = 1;
|
[core] option to stream response body to client (fixes #949, #760, #1283, #1387)
Set server.stream-response-body = 1 or server.stream-response-body = 2
to have lighttpd stream response body to client as it arrives from the
backend (CGI, FastCGI, SCGI, proxy).
default: buffer entire response body before sending response to client.
(This preserves existing behavior for now, but may in the future be
changed to stream response to client, which is the behavior more
commonly expected.)
x-ref:
"fastcgi, cgi, flush, php5 problem."
https://redmine.lighttpd.net/issues/949
"Random crashing on FreeBSD 6.1"
https://redmine.lighttpd.net/issues/760
"Memory usage increases when proxy+ssl+large file"
https://redmine.lighttpd.net/issues/1283
"lighttpd+fastcgi memory problem"
https://redmine.lighttpd.net/issues/1387
2016-06-11 15:04:01 +00:00
|
|
|
} else {
|
|
|
|
/*(reuse MAX_HTTP_REQUEST_HEADER as max size for response headers from backends)*/
|
|
|
|
if (header_len > MAX_HTTP_REQUEST_HEADER) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sb", "response headers too large for", con->uri.path);
|
|
|
|
con->http_status = 502; /* Bad Gateway */
|
|
|
|
con->mode = DIRECT;
|
|
|
|
return FDEVENT_HANDLED_FINISHED;
|
|
|
|
}
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
|
|
|
} else {
|
2016-05-25 20:45:09 +00:00
|
|
|
if (0 != http_chunk_append_buffer(srv, con, hctx->response)) {
|
|
|
|
return FDEVENT_HANDLED_ERROR;
|
|
|
|
}
|
[core] option to stream response body to client (fixes #949, #760, #1283, #1387)
Set server.stream-response-body = 1 or server.stream-response-body = 2
to have lighttpd stream response body to client as it arrives from the
backend (CGI, FastCGI, SCGI, proxy).
default: buffer entire response body before sending response to client.
(This preserves existing behavior for now, but may in the future be
changed to stream response to client, which is the behavior more
commonly expected.)
x-ref:
"fastcgi, cgi, flush, php5 problem."
https://redmine.lighttpd.net/issues/949
"Random crashing on FreeBSD 6.1"
https://redmine.lighttpd.net/issues/760
"Memory usage increases when proxy+ssl+large file"
https://redmine.lighttpd.net/issues/1283
"lighttpd+fastcgi memory problem"
https://redmine.lighttpd.net/issues/1387
2016-06-11 15:04:01 +00:00
|
|
|
if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)
|
|
|
|
&& chunkqueue_length(con->write_queue) > 65536 - 4096) {
|
|
|
|
if (!con->is_writable) {
|
|
|
|
/*(defer removal of FDEVENT_IN interest since
|
|
|
|
* connection_state_machine() might be able to send data
|
|
|
|
* immediately, unless !con->is_writable, where
|
|
|
|
* connection_state_machine() might not loop back to call
|
|
|
|
* mod_cgi_handle_subrequest())*/
|
|
|
|
fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
|
|
|
#if 0
|
2005-02-20 14:27:00 +00:00
|
|
|
log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), b->ptr);
|
|
|
|
#endif
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return FDEVENT_HANDLED_NOT_FINISHED;
|
|
|
|
}
|
|
|
|
|
2016-04-12 04:21:03 +00:00
|
|
|
static void cgi_connection_close_fdtocgi(server *srv, handler_ctx *hctx) {
|
|
|
|
/*(closes only hctx->fdtocgi)*/
|
|
|
|
fdevent_event_del(srv->ev, &(hctx->fde_ndx_tocgi), hctx->fdtocgi);
|
|
|
|
fdevent_unregister(srv->ev, hctx->fdtocgi);
|
2016-08-24 19:30:11 +00:00
|
|
|
fdevent_sched_close(srv->ev, hctx->fdtocgi, 0);
|
2016-04-12 04:21:03 +00:00
|
|
|
hctx->fdtocgi = -1;
|
|
|
|
}
|
|
|
|
|
2016-04-07 00:46:43 +00:00
|
|
|
static void cgi_connection_close(server *srv, handler_ctx *hctx) {
|
2005-02-20 14:27:00 +00:00
|
|
|
int status;
|
|
|
|
pid_t pid;
|
2016-03-04 18:54:26 +00:00
|
|
|
plugin_data *p = hctx->plugin_data;
|
|
|
|
connection *con = hctx->remote_conn;
|
2005-02-20 14:27:00 +00:00
|
|
|
|
|
|
|
#ifndef __WIN32
|
2006-10-04 13:26:23 +00:00
|
|