diff options
author | Daniel Baumann <daniel@debian.org> | 2024-11-21 15:51:37 +0100 |
---|---|---|
committer | Daniel Baumann <daniel@debian.org> | 2024-11-21 15:51:37 +0100 |
commit | ebb64aabedd789b5affbf30f03e43fcf3a0561f4 (patch) | |
tree | ec4dd9937434be85039f900efcc48c75c182d81d /src/server.cpp | |
parent | Initial commit. (diff) | |
download | packetq-upstream.tar.xz packetq-upstream.zip |
Adding upstream version 1.7.3+dfsg.upstream/1.7.3+dfsgupstream
Signed-off-by: Daniel Baumann <daniel@debian.org>
Diffstat (limited to '')
-rw-r--r-- | src/server.cpp | 1183 |
1 files changed, 1183 insertions, 0 deletions
diff --git a/src/server.cpp b/src/server.cpp new file mode 100644 index 0000000..96e14d3 --- /dev/null +++ b/src/server.cpp @@ -0,0 +1,1183 @@ +/* + * Copyright (c) 2017-2024 OARC, Inc. + * Copyright (c) 2011-2017, IIS - The Internet Foundation in Sweden + * All rights reserved. + * + * This file is part of PacketQ. + * + * PacketQ is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PacketQ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with PacketQ. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "packetq.h" +#include "pcap.h" +#include "reader.h" + +#include <arpa/inet.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <list> +#include <map> +#include <netdb.h> +#include <netinet/in.h> +#include <poll.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <string> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> + +#define MAXHOSTNAME 256 + +namespace packetq { +namespace httpd { + class Socket; + class Server; + + static char redirect[] = "HTTP/1.1 307 Temporary Redirect\r\n" + "Location: /\r\n" + "Connection: close\r\n" + "Content-Type: text/html\r\n" + "\r\n<html><head><title>moved</title></head><body><h1>moved</h1>this page has moved to /</body></html>"; + + static char header[] = "HTTP/1.1 200 OK\r\n" + "Server: PacketQ builtin\r\n" + "Connection: close\r\n" + "Access-Control-Allow-Origin: *\r\n" + "Content-Type: %s\r\n" + "\r\n"; + + static Server* g_server = 0; + + class SocketPool { + public: + SocketPool() + { + m_free = 0; + for (int i = 0; i < FD_SETSIZE; i++) { + m_sockets[i] = 0; + } + m_socket_count = 0; + } + int add(Socket* s) + { + if (m_free < FD_SETSIZE) + m_sockets[m_free] = s; + else + return -1; + int idx = m_free; + while (m_free < FD_SETSIZE && m_sockets[m_free]) + m_free++; + m_socket_count++; + return idx; + } + void remove(int s) + { + m_sockets[s] = 0; + if (s < m_free) + m_free = s; + m_socket_count--; + } + + int get_sockets() { return m_socket_count; } + void select(); + + fd_set m_readset; + fd_set m_writeset; + int m_free; + int m_socket_count; + Socket* m_sockets[FD_SETSIZE]; + }; + + SocketPool g_pool; + + class Stream { + public: + class Buffer { + private: + Buffer& operator=(const Buffer& other); + Buffer const& operator=(Buffer&& other); + + public: + Buffer(const unsigned char* buf, int len) + { + m_buf = new unsigned char[len]; + memcpy(m_buf, buf, len); + m_len = len; + m_pos = 0; + } + Buffer(Buffer&& other) noexcept + { + m_buf = other.m_buf; + m_len = other.m_len; + m_pos = other.m_pos; + other.m_buf = 0; + other.m_len = 0; + other.m_pos = 0; + } + ~Buffer() + { + m_len = 0; + delete[] m_buf; + } + unsigned char* m_buf; + int m_len; + int m_pos; + }; + Stream() + { + m_len = 0; + } + void push_front(unsigned char* data, int len) + { + m_stream.push_front(Buffer(data, len)); + m_len += len; + } + void write(unsigned char* data, int len) + { + m_stream.push_back(Buffer(data, len)); + m_len += len; + } + int read(unsigned char* data, int maxlen) + { + int p = 0; + while (p < maxlen && m_len > 0) { + Buffer& buf = m_stream.front(); + int l = maxlen - p; + if (l > buf.m_len - buf.m_pos) + l = buf.m_len - buf.m_pos; + for (int i = 0; i < l; i++) { + data[p++] = buf.m_buf[buf.m_pos++]; + } + m_len -= l; + if (l == 0) { + m_stream.pop_front(); + } + } + return p; + } + int len() + { + return m_len; + } + int get() + { + unsigned char c = '@'; + int r = read(&c, 1); + if (r == 0) + return -1; + return c; + } + + private: + int m_len; + std::list<Stream::Buffer> m_stream; + }; + + class Socket { + private: + Socket& operator=(const Socket& other); + Socket(Socket&& other) noexcept; + Socket const& operator=(Socket&& other); + + public: + Socket(int s, bool serv) + : m_want_write(false) + { + int flags; + + // Add non-blocking flag + if ((flags = fcntl(s, F_GETFL)) == -1) { + syslog(LOG_ERR | LOG_USER, "fcntl(%d, F_GETFL) failed: %d\n", s, errno); + exit(-1); + } + if (fcntl(s, F_SETFL, flags | O_NONBLOCK)) { + syslog(LOG_ERR | LOG_USER, "fcntl(%d, F_SETFL, 0x%x) failed: %d\n", s, errno, flags | O_NONBLOCK); + exit(-1); + } + + m_bytes_written = 0; + m_bytes_read = 0; + m_serv = serv; + m_socket = s; + m_sidx = g_pool.add(this); + m_close_when_empty = false; + m_file = 0; + } + virtual ~Socket() + { + g_pool.remove(m_sidx); + close(m_socket); + } + void process(bool read) + { + // m_serv means this is a listening socket + if (m_serv) + return; + if (!read) { + on_write(); + unsigned char ptr[4096]; + int len; + + while ((len = m_write.read(ptr, sizeof(ptr)))) { + int res = write(m_socket, ptr, len); +#if EAGAIN != EWOULDBLOCK + if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { +#else + if (res == -1 && (errno == EAGAIN)) { +#endif + m_write.push_front(ptr, len); + set_want_write(); + return; + } + if (res < 0) { + delete this; + return; + } + m_bytes_written += res; + if (res < len) { + m_write.push_front(&ptr[res], len - res); + set_want_write(); + return; + } + } + if (m_close_when_empty) { + shutdown(m_socket, SHUT_WR); + // fflush(m_socket); + delete this; + } + } + if (read) { + int avail = read_sock(); + + if (avail) + on_read(); + } + } + virtual void file_write() + { + } + virtual void file_read() + { + } + void set_delete() + { + m_close_when_empty = true; + m_want_write = false; + } + virtual void on_write() + { + } + virtual void on_read() + { + } + int read_sock() + { + unsigned char buf[2000]; + int len = 0; + if ((len = recv(m_socket, buf, sizeof(buf) - 1, 0)) > 0) { + m_bytes_read += len; + buf[len] = 0; + m_read.write(buf, len); + } + if (len < 0) { + delete this; + return 0; + } + return len; + } + bool failed() + { + return m_sidx == -1; + } + bool has_data() + { + return m_want_write || m_write.len() > 0; + } + void set_want_write(bool set = true) + { + m_want_write = set; + } + + int m_bytes_written; + int m_bytes_read; + int m_socket; + int m_file; + int m_sidx; + bool m_serv; + bool m_want_write; + bool m_close_when_empty; + char m_buffer[4096]; + + Stream m_read, m_write; + }; + + void SocketPool::select() + { + FD_ZERO(&m_readset); + FD_ZERO(&m_writeset); + int max = 0; + for (int i = 0; i < FD_SETSIZE; i++) { + if (m_sockets[i]) { + if (max < m_sockets[i]->m_socket) + max = m_sockets[i]->m_socket; + FD_SET(m_sockets[i]->m_socket, &m_readset); + if (m_sockets[i]->has_data()) + FD_SET(m_sockets[i]->m_socket, &m_writeset); + if (max < m_sockets[i]->m_file) + max = m_sockets[i]->m_file; + FD_SET(m_sockets[i]->m_file, &m_readset); + } + } + timeval timeout; + timeout.tv_sec = 30; + timeout.tv_usec = 0; + + int sel = ::select(max + 1, &m_readset, &m_writeset, 0, &timeout); + if (sel < 0) { + syslog(LOG_ERR | LOG_USER, "sel -1 errno = %d %s max:%d", errno, strerror(errno), max); + exit(-1); + } + if (sel) { + for (int i = 0; i < FD_SETSIZE; i++) { + if (m_sockets[i] && m_sockets[i]->m_file) { + if (FD_ISSET(m_sockets[i]->m_file, &m_readset)) { + m_sockets[i]->file_read(); + } + if (m_sockets[i] && FD_ISSET(m_sockets[i]->m_file, &m_writeset)) { + m_sockets[i]->file_write(); + } + } + if (m_sockets[i] && FD_ISSET(m_sockets[i]->m_socket, &m_readset)) { + m_sockets[i]->process(true); + } + if (m_sockets[i] && FD_ISSET(m_sockets[i]->m_socket, &m_writeset)) { + m_sockets[i]->process(false); + } + } + } + } + + class Server { + public: + Socket m_socket; + + Server(int port, const std::string& pcaproot, const std::string& webroot) + : m_socket(establish(port), true) + , m_pcaproot(pcaproot) + , m_webroot(webroot) + { + if (m_socket.m_socket < 0) { + if (m_socket.m_socket == EADDRINUSE) + syslog(LOG_ERR | LOG_USER, "Fail EADDRINUSE (%d)\n", m_socket.m_socket); + else + syslog(LOG_ERR | LOG_USER, "Fail %d port:%d\n", m_socket.m_socket, port); + exit(-1); + } + } + + ~Server() + { + } + + int get_connection() + { + int s = m_socket.m_socket; + int t; /* socket of connection */ + if ((t = accept(s, NULL, NULL)) < 0) /* accept connection if there is one */ + return (-1); + + return (t); + } + + int establish(unsigned short portnum) + { + int s, res; + sockaddr_in sa; + memset(&sa, 0, sizeof(struct sockaddr_in)); + + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = htonl(INADDR_ANY); + + sa.sin_port = htons(portnum); + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) + return (-2); + int on = 1; + + res = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + if ((res = bind(s, (const sockaddr*)&sa, sizeof(struct sockaddr_in))) < 0) { + close(s); + return (res); /* bind address to socket */ + } + + // Add non-blocking flag + int flags; + if ((flags = fcntl(s, F_GETFL)) == -1) { + syslog(LOG_ERR | LOG_USER, "fcntl(%d, F_GETFL) failed: %d\n", s, errno); + exit(-1); + } + if (fcntl(s, F_SETFL, flags | O_NONBLOCK)) { + syslog(LOG_ERR | LOG_USER, "fcntl(%d, F_SETFL, 0x%x) failed: %d\n", s, errno, flags | O_NONBLOCK); + exit(-1); + } + + listen(s, 511); /* max # of queued connects */ + return (s); + } + + std::string m_pcaproot; + std::string m_webroot; + }; + + class Url { + public: + Url(const char* url) + : m_full(url) + { + int i = 0; + for (; i < m_full.length();) { + char c = m_full.c_str()[i]; + if (c == '?') { + i++; + break; + } + m_path += c; + i++; + } + m_path = decode(m_path); + if (m_path == "") + m_path = "/"; + decode_params(m_full.substr(i).c_str()); + } + void decode_params(const char* params) + { + if (!params) + return; + std::string str = params; + for (int i = 0; i < str.length();) { + std::string param = "", value = ""; + for (; i < str.length();) { + char c = str.c_str()[i]; + if (c == '=' || c == '&') { + i++; + break; + } + param += c; + i++; + } + for (; i < str.length();) { + char c = str.c_str()[i]; + if (c == '&') { + i++; + break; + } + value += c; + i++; + } + add_param(decode(param), decode(value)); + } + } + const char* get_param(const char* param) + { + auto it = m_params.find(std::string(param)); + if (it == m_params.end()) + return 0; + return it->second.c_str(); + } + void add_param(std::string key, std::string val) + { + auto it = m_params.find(key); + if (it == m_params.end()) { + m_params[key] = val; + m_counts[key] = 1; + return; + } + char cnt[100]; + int n = m_counts[key]; + m_counts[key] = n + 1; + + snprintf(cnt, sizeof(cnt) - 1, "%d", n); + cnt[99] = 0; + std::string keyn = key; + keyn += cnt; + m_params[keyn] = val; + } + + std::string decode(std::string str) + { + std::string dst; + int percent_state = 0; + int code = 0; + for (int i = 0; i < str.length(); i++) { + char c = str.c_str()[i]; + if (percent_state) { + int n = 0; + if (c >= '0' && c <= '9') + n = c - '0'; + if (c >= 'a' && c <= 'f') + n = c - 'a' + 10; + if (c >= 'A' && c <= 'F') + n = c - 'A' + 10; + code = (code << 4) | n; + percent_state--; + if (!percent_state) { + dst += char(code); + code = 0; + } + } else { + if (c == '%') { + percent_state = 2; + } else + dst += c; + } + } + return dst; + } + + std::string get_full() + { + return m_full; + } + std::string get_path() + { + return m_path; + } + + std::string m_full; + std::string m_path; + std::map<std::string, std::string> m_params; + std::map<std::string, int> m_counts; + }; + + class Page { + public: + Page(const char* url, const char* body) + : m_url(url) + { + m_url.decode_params(body); + } + + void process() + { + + if (m_url.get_path().compare("/query") == 0) { + if (!m_url.get_param("file")) { + printf(header, "text/plain"); + printf("no file selected\n"); + return; + } + + printf(header, "text/plain"); + if (m_url.get_param("sql")) + query(m_url.get_param("sql")); + else + printf("no query defined \n"); + } else if (m_url.get_path().substr(0, 8).compare("/resolve") == 0) { + resolve(); + } else if (m_url.get_path().substr(0, 5).compare("/list") == 0) { + serve_dir(); + } else { + serve_static(); + } + + delete g_app; + } + static std::string join_path(const std::string& a, const std::string& b) + { + if (b.find("..") != std::string::npos) + return a; + if (a.length() == 0) + return b; + if (b.length() == 0) + return a; + if (a[a.length() - 1] != '/' && b[0] != '/') + return a + std::string("/") + b; + return a + b; + } + const char* get_mimetype(const std::string& file) + { + int p = file.find_last_of('.'); + if (p == std::string::npos || p + 1 >= file.length()) + return 0; + std::string suff = file.substr(p + 1); + if (suff.compare("js") == 0) + return "application/x-javascript"; + if (suff.compare("jpg") == 0) + return "image/jpeg"; + if (suff.compare("html") == 0) + return "text/html"; + if (suff.compare("htm") == 0) + return "text/html"; + if (suff.compare("txt") == 0) + return "text/plain"; + if (suff.compare("png") == 0) + return "image/png"; + if (suff.compare("gif") == 0) + return "image/gif"; + if (suff.compare("ico") == 0) + return "image/x-icon"; + if (suff.compare("json") == 0) + return "application/json"; + if (suff.compare("css") == 0) + return "text/css"; + + return 0; + } + bool serve_file(const std::string& file) + { + const char* mimetype = get_mimetype(file); + + if (mimetype) { + FILE* fp = fopen(file.c_str(), "rb"); + if (fp) { + printf(header, mimetype); + char buffer[8192]; + int len; + while ((len = fread(buffer, 1, 200, fp)) > 0) { + fwrite(buffer, 1, len, stdout); + } + fclose(fp); + return true; + } + } + return false; + } + void serve_static() + { + if (g_server->m_webroot == "") { + printf(header, "text/html"); + printf("<h2>This server is not configured to serve static pages</h2>"); + printf("Start using the -w option to set a html directory"); + return; + } + std::string file = join_path(g_server->m_webroot, m_url.get_path()); + + if (serve_file(file)) + return; + if (serve_file(join_path(file, "index.html"))) + return; + + if (m_url.get_path().compare("/") != 0) { + printf("%s", redirect); + return; + } + + printf(header, "text/html"); + printf("<h2>It works !</h2><br>\n"); + printf("%s", "<a href=\"/query?file=sample.pcap&sql=select%20qr,qname,protocol%20from%20dns%20limit%2018;\">Test query</a><br/>\n"); + printf("%s", "<a href=\"/list\">list available files</a><br/>\n"); + } + + void resolve() + { + const char* ip = m_url.get_param("ip"); + const char* name = m_url.get_param("name"); + + if (ip) { + + printf(header, "application/json"); + + printf("["); + + struct addrinfo* result; + struct addrinfo* res; + int error; + + error = getaddrinfo(ip, NULL, NULL, &result); + if (error == 0) { + for (res = result; res != NULL; res = res->ai_next) { + char hostname[NI_MAXHOST] = ""; + + error = getnameinfo(res->ai_addr, res->ai_addrlen, hostname, NI_MAXHOST, NULL, 0, 0); + if (error != 0) { + continue; + } + if (*hostname != '\0') { + printf("\"%s\"", hostname); + break; + } + } + freeaddrinfo(result); + } + printf("]\n"); + } else if (name) { + char tmp[100]; + printf(header, "application/json"); + + printf("["); + + struct addrinfo* result; + struct addrinfo* res; + int error; + + error = getaddrinfo(name, NULL, NULL, &result); + char empty[] = "", line[] = ",\n"; + char* sep = empty; + if (error == 0) { + for (res = result; res != NULL; res = res->ai_next) { + void* ptr = &((struct sockaddr_in*)res->ai_addr)->sin_addr; + if (res->ai_family == AF_INET6) + ptr = &((struct sockaddr_in6*)res->ai_addr)->sin6_addr; + tmp[0] = 0; + inet_ntop(res->ai_family, ptr, tmp, sizeof(tmp)); + printf("%s\"%s\"", sep, tmp); + sep = line; + } + freeaddrinfo(result); + } + printf("]\n"); + } else + printf("[]\n"); + } + + void serve_dir() + { + if (g_server->m_pcaproot == "") { + printf(header, "text/html"); + printf("<h2>This server is not configured to list pcapfiles</h2>"); + printf("Start using the -r option to set a pcap directory"); + return; + } + std::string directory = join_path(g_server->m_pcaproot, m_url.get_path().substr(5)); + + DIR* dir = opendir(directory.c_str()); + if (!dir) { + printf("%s", redirect); + return; + } + + printf(header, "application/json"); + + printf("[\n"); + struct dirent* d; + struct stat statbuf; + + char comma = ' '; + + while ((d = readdir(dir)) != 0) { + std::string subject = join_path(directory, d->d_name); + int fd = open(subject.c_str(), O_RDONLY); + + if (fd < 0) + continue; + if (fstat(fd, &statbuf) == -1) { + close(fd); + continue; + } + if (S_ISDIR(statbuf.st_mode)) { + if ((strcmp(d->d_name, ".") != 0) && (strcmp(d->d_name, "..") != 0)) { + printf(" %c{\n \"data\" : \"%s\",\n \"attr\" : { \"id\": \"%s\" },\n \"children\" : [], \"state\" : \"closed\" }\n", + comma, d->d_name, join_path(m_url.get_path(), d->d_name).substr(5).c_str()); + comma = ','; + } + } else { + bool found = false; + FILE* fp = fdopen(fd, "rb"); + if (fp) { + Pcap_file pfile(fp); + if (pfile.get_header()) { + unsigned char* data = 0; + int s = 0, us, len; + data = pfile.get_packet(len, s, us); + if (data) { + printf(" %c{\n \"data\" : \"%s\",\n \"attr\" : { \"id\" : \"%s\", \"size\": %d, \"time\": %d,\"type\": \"pcap\" },\n \"children\" : [] }\n", + comma, d->d_name, join_path(m_url.get_path(), d->d_name).substr(5).c_str(), int(statbuf.st_size), s); + comma = ','; + found = true; + } + } + fclose(fp); + } + if (!found) { + std::string str = subject; + transform(str.begin(), str.end(), str.begin(), tolower); + if (str.rfind(".json") == str.length() - 5) { + printf(" %c{\n \"data\" : \"%s\",\n \"attr\" : { \"id\" : \"%s\", \"size\": %d, \"type\": \"json\" },\n \"children\" : [] }\n", + comma, d->d_name, join_path(m_url.get_path(), d->d_name).substr(5).c_str(), int(statbuf.st_size)); + comma = ','; + } + } + } + close(fd); + } + + printf("]\n"); + + closedir(dir); + } + + void query(const char* sql) + { + Query query("result", sql); + + query.parse(); + + std::vector<std::string> in_files; + + int i = 0; + while (true) { + char param[50] = "file"; + + std::string par = "file"; + if (i > 0) { + snprintf(param, sizeof(param) - 1, "file%d", i); + param[49] = 0; + } + i++; + const char* f = m_url.get_param(param); + if (!f) + break; + std::string file = join_path(g_server->m_pcaproot, f); + in_files.push_back(file); + } + + Reader reader(in_files, g_app->get_limit()); + + query.execute(reader); + query.m_result->json(false); + } + Url m_url; + }; + + class Http_socket : public Socket { + private: + Http_socket& operator=(const Http_socket& other); + Http_socket(Http_socket&& other) noexcept; + Http_socket const& operator=(Http_socket&& other); + + public: + enum State { + get_post, + header, + body, + error, + wait_child, + done + }; + Http_socket(int socket) + : Socket(socket, false) + , m_http_version(0) + , m_emptyline(0) + { + m_state = get_post; + m_nextc = -1; + m_cr = false; + m_line = ""; + m_url = ""; + m_child_fd = 0; + m_child_pid = 0; + m_child_read = 0; + m_body_cnt = -1; + m_content_len = 0; + } + ~Http_socket() + { + if (m_child_pid) { + kill(m_child_pid, SIGHUP); + int status; + waitpid(m_child_pid, &status, 0); + } + if (m_child_fd) + close(m_child_fd); + m_file = 0; + } + inline void print(const char* fmt, ...) + { + char string[4096]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(string, sizeof(string), fmt, ap); + va_end(ap); + + m_write.write((unsigned char*)string, strlen(string)); + } + + int peek() + { + if (m_nextc >= 0) + return m_nextc; + m_nextc = m_read.get(); + return m_nextc; + } + int getc() + { + int c = peek(); + m_nextc = -1; + return c; + } + + void on_read() + { + while (true) { + int c = peek(); + if (c == -1) + return; + if (m_body_cnt >= 0) { + c = getc(); + if (!(m_body_cnt == 0 && c == 10)) { + m_line += char(c); + m_content_len--; + } + m_body_cnt++; + if (m_content_len == 0) + parseline(); + continue; + } + c = getc(); + if (c != 13 && c != 10) { + m_line += char(c); + m_cr = false; + } else { + bool cr = m_cr; + m_cr = false; + if (c == 10 && cr) { + continue; + } + if (c == 13) + m_cr = true; + parseline(); + } + } + } + virtual void file_read() + { + set_want_write(); + } + virtual void file_write() + { + } + void on_write() + { + set_want_write(false); + if (m_state == wait_child) { + unsigned char buffer[4096]; + int status; + bool done = true; + if (0 == waitpid(m_child_pid, &status, WNOHANG)) { + done = false; + } + if (m_child_fd) { + // Add non-blocking flag + int flags; + if ((flags = fcntl(m_child_fd, F_GETFL)) == -1) { + syslog(LOG_ERR | LOG_USER, "fcntl(%d, F_GETFL) failed: %d\n", m_child_fd, errno); + exit(-1); + } + if (fcntl(m_child_fd, F_SETFL, flags | O_NONBLOCK)) { + syslog(LOG_ERR | LOG_USER, "fcntl(%d, F_SETFL, 0x%x) failed: %d\n", m_child_fd, errno, flags | O_NONBLOCK); + exit(-1); + } + + size_t res; + pollfd pfd; + pfd.fd = m_child_fd; + pfd.events = POLLIN; + pfd.revents = 0; + if (1 == poll(&pfd, 1, 0) && (pfd.revents & POLLIN) != 0) { + if ((res = read(m_child_fd, buffer, (int)sizeof(buffer))) > 0) { + done = false; + m_child_read += res; + m_write.write(buffer, res); + } + } + } + if (done) { + m_child_pid = 0; + if (m_child_fd) { + close(m_child_fd); + m_child_fd = 0; + m_file = 0; + } + set_delete(); + } + } + } + void parseline() + { + switch (m_state) { + case (get_post): { + m_state = error; + syslog(LOG_INFO | LOG_USER, "%s\n", m_line.c_str()); + int p = 0; + if (m_line.find("GET ") != -1) { + if ((p = m_line.find(" HTTP/1.1")) != -1) { + m_http_version = 1; + } else if ((p = m_line.find(" HTTP/1.0")) != -1) { + m_http_version = 0; + } else { + return; + } + m_url = m_line.substr(4, p - 4); + m_state = header; + } else if (m_line.find("POST ") != -1) { + if ((p = m_line.find(" HTTP/1.1")) != -1) { + m_http_version = 1; + } else if ((p = m_line.find(" HTTP/1.0")) != -1) { + m_http_version = 0; + } else { + return; + } + m_url = m_line.substr(5, p - 5); + m_state = header; + } + } break; + case (header): + if (m_line.length() == 0) { + m_body_cnt = 0; + m_state = body; + } else { + int colon = m_line.find(": "); + std::string key = m_line.substr(0, colon); + std::string val = m_line.substr(colon + 2); + if (key == "Content-Length") { + if (val.length() > 0) + m_content_len = atoi(val.c_str()); + } + } + break; + case (body): + m_body = m_line; + header_done(); + break; + default: + printf("error line: %s !\n", m_line.c_str()); + break; + } + m_line = ""; + } + void header_done() + { + fflush(stdout); // required before fork or any unflushed output will go to the client + int fd[2]; + if (pipe(fd) < 0) + return; + + // Add non-blocking flag + int flags; + if ((flags = fcntl(fd[0], F_GETFL)) == -1) { + syslog(LOG_ERR | LOG_USER, "fcntl(%d, F_GETFL) failed: %d\n", fd[0], errno); + exit(-1); + } + if (fcntl(fd[0], F_SETFL, flags | O_NONBLOCK)) { + syslog(LOG_ERR | LOG_USER, "fcntl(%d, F_SETFL, 0x%x) failed: %d\n", fd[0], errno, flags | O_NONBLOCK); + exit(-1); + } + + m_child_pid = fork(); + if (m_child_pid < 0) { + print("Internal error"); + set_delete(); + return; + } + if (m_child_pid == 0) { + ////////// child code ///////// + dup2(fd[1], fileno(stdout)); + dup2(fd[1], fileno(stderr)); + close(fd[1]); + + Page page(m_url.c_str(), m_body.c_str()); + page.process(); + fflush(stdout); + exit(0); + ///////////// child exit() /////////////// + } else { + close(fd[1]); + m_child_fd = fd[0]; + m_file = m_child_fd; + m_state = wait_child; + } + set_want_write(); + } + State m_state; + bool m_cr; + int m_body_cnt; + int m_content_len; + int m_nextc; + int m_child_pid; + int m_child_fd; + + int m_child_read; + + int m_http_version; // 0 = HTTP/1.0 1 = HTTP/1.1 + + int m_emptyline; + std::string m_line; + std::string m_url; + std::string m_body; + }; + +} // namespace httpd + +using namespace httpd; + +void start_server(int port, bool fork_me, const std::string& pcaproot, const std::string& webroot, int max_conn) +{ + pid_t pid, sid; + bool fg = !fork_me; + + printf("listening on port %d\n", port); + + if (!fg) { + pid = fork(); + + if (pid < 0) { + exit(EXIT_FAILURE); + } else if (pid > 0) { + exit(EXIT_SUCCESS); + } + + sid = setsid(); + + if (sid < 0) { + exit(EXIT_FAILURE); + } + } + openlog("packetq", LOG_PID, LOG_USER); + + httpd::Server server(port, pcaproot, webroot); + g_server = &server; + + while (true) { + httpd::g_pool.select(); + int cnt = g_pool.get_sockets(); + if (cnt < max_conn) { + int c = server.get_connection(); + if (c > -1) { + Http_socket* s = new (std::nothrow) Http_socket(c); + if (s && s->failed()) { + syslog(LOG_ERR | LOG_USER, "failed to create socket"); + delete s; + } + } + } + usleep(1000); + } + // loop will never break + // g_server = 0; + // syslog(LOG_INFO | LOG_USER, "exiting"); + // exit(EXIT_SUCCESS); +} + +} // namespace packetq |