diff options
Diffstat (limited to 'fluent-bit/lib/monkey/plugins/cgi/event.c')
-rw-r--r-- | fluent-bit/lib/monkey/plugins/cgi/event.c | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/fluent-bit/lib/monkey/plugins/cgi/event.c b/fluent-bit/lib/monkey/plugins/cgi/event.c new file mode 100644 index 00000000..b8d39526 --- /dev/null +++ b/fluent-bit/lib/monkey/plugins/cgi/event.c @@ -0,0 +1,170 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* Monkey HTTP Server + * ================== + * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io> + * Copyright (C) 2012-2013, Lauri Kasanen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cgi.h" + +/* + * The reason for this function is that some CGI apps + * + * use LFLF and some use CRLFCRLF. + * + * If that app then sends content that has the other break + * in the beginning, monkey can accidentally send part of the + * content as headers. + */ + +static char *getearliestbreak(const char buf[], const unsigned bufsize, + unsigned char * const advance) + { + char * const crend = memmem(buf, bufsize, MK_IOV_CRLFCRLF, + sizeof(MK_IOV_CRLFCRLF) - 1); + char * const lfend = memmem(buf, bufsize, MK_IOV_LFLF, + sizeof(MK_IOV_LFLF) - 1); + + if (!crend && !lfend) + return NULL; + + /* If only one found, return that one */ + if (!crend) { + *advance = 2; + return lfend; + } + if (!lfend) + return crend; + + /* Both found, return the earlier one - the latter one is part of content */ + if (lfend < crend) { + *advance = 2; + return lfend; + } + return crend; +} + +int process_cgi_data(struct cgi_request *r) +{ + int ret; + int len; + int status; + char *buf = r->in_buf; + char *outptr = r->in_buf; + char *end; + char *endl; + unsigned char advance; + + mk_api->socket_cork_flag(r->cs->socket, TCP_CORK_OFF); + if (!r->status_done && r->in_len >= 8) { + if (memcmp(buf, "Status: ", 8) == 0) { + status = atoi(buf + 8); + mk_api->header_set_http_status(r->sr, status); + endl = memchr(buf + 8, '\n', r->in_len - 8); + if (!endl) { + return MK_PLUGIN_RET_EVENT_OWNED; + } + else { + endl++; + outptr = endl; + r->in_len -= endl - buf; + } + } + else if (memcmp(buf, "HTTP", 4) == 0) { + status = atoi(buf + 9); + mk_api->header_set_http_status(r->sr, status); + + endl = memchr(buf + 8, '\n', r->in_len - 8); + if (!endl) { + return MK_PLUGIN_RET_EVENT_OWNED; + } + else { + endl++; + outptr = endl; + r->in_len -= endl - buf; + } + } + mk_api->header_prepare(r->plugin, r->cs, r->sr); + r->status_done = 1; + } + + if (!r->all_headers_done) { + advance = 4; + + /* Write the rest of the headers without chunking */ + end = getearliestbreak(outptr, r->in_len, &advance); + if (!end) { + /* Let's return until we have the headers break */ + return MK_PLUGIN_RET_EVENT_OWNED; + } + end += advance; + len = end - outptr; + channel_write(r, outptr, len); + outptr += len; + r->in_len -= len; + + r->all_headers_done = 1; + if (r->in_len == 0) { + return MK_PLUGIN_RET_EVENT_OWNED; + } + } + + if (r->chunked) { + char tmp[16]; + len = snprintf(tmp, 16, "%x\r\n", r->in_len); + ret = channel_write(r, tmp, len); + if (ret < 0) + return MK_PLUGIN_RET_EVENT_CLOSE; + } + + ret = channel_write(r, outptr, r->in_len); + if (ret < 0) { + return MK_PLUGIN_RET_EVENT_CLOSE; + } + + r->in_len = 0; + if (r->chunked) { + channel_write(r, MK_CRLF, 2); + } + return MK_PLUGIN_RET_EVENT_OWNED; +} + +int cb_cgi_read(void *data) +{ + int n; + struct cgi_request *r = data; + + if (r->active == MK_FALSE) { + return -1; + } + + if ((BUFLEN - r->in_len) < 1) { + PLUGIN_TRACE("CLOSE BY SIZE"); + cgi_finish(r); + return -1; + } + + n = read(r->fd, r->in_buf + r->in_len, BUFLEN - r->in_len); + PLUGIN_TRACE("FD=%i CGI READ=%d", r->fd, n); + if (n <= 0) { + /* It most of cases this means the child process finished */ + cgi_finish(r); + return MK_PLUGIN_RET_EVENT_CLOSE; + } + r->in_len += n; + process_cgi_data(r); + return 0; +} |