diff --git a/.drone.yml b/.drone.yml deleted file mode 100644 index db9a25b..0000000 --- a/.drone.yml +++ /dev/null @@ -1,28 +0,0 @@ ---- -kind: pipeline -type: exec -name: default -platform: - os: linux - arch: amd64 -clone: - disable: true -steps: -- name: clone - commands: - - pwd - - git clone ssh://git@iohub.dev/lxsang/ant-http.git - - cd ./ant-http && git checkout master -- name: build - commands: - - cd ./ant-http - - libtoolize - - aclocal - - autoconf - - automake --add-missing - - ./configure --prefix=/usr - - make - - DESTDIR=/opt/cloud/artifacts make install -trigger: - branch: - - master diff --git a/.gitignore b/.gitignore index 139e1cd..e2d16fe 100644 --- a/.gitignore +++ b/.gitignore @@ -70,4 +70,5 @@ config.sub configure aclocal.m4 ltmain.sh -Makefile.in \ No newline at end of file +Makefile.in +configure~ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 68dfb64..0000000 --- a/.travis.yml +++ /dev/null @@ -1,11 +0,0 @@ -language: c -before_install: - - sudo apt-get -qq update - - sudo apt-get install libssl-dev libsqlite3-dev autotools-dev autoconf libtool libtool-bin -script: - - libtoolize - - aclocal - - autoconf - - automake --add-missing - - ./configure - - make diff --git a/Makefile.am b/Makefile.am index 288ddec..d97a0d5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -34,20 +34,19 @@ pkginclude_HEADERS = lib/ini.h \ lib/list.h \ lib/bst.h \ lib/scheduler.h \ - lib/plugin.h + lib/plugin.h EXTRA_DIST = plugin_manager.h http_server.h README.md LICENSE antd-config.ini ant-d antd.service -# check for db -if DB -libantd_la_SOURCES += lib/dbhelper.c -pkginclude_HEADERS += lib/dbhelper.h -endif # bin bin_PROGRAMS = antd # lib source files -antd_SOURCES = plugin_manager.c http_server.c httpd.c +antd_SOURCES = plugin_manager.c \ + server.c \ + config.c \ + decode.c \ + httpd.c antd_LDADD = libantd.la diff --git a/config.c b/config.c new file mode 100644 index 0000000..b9e8099 --- /dev/null +++ b/config.c @@ -0,0 +1,399 @@ +#include +#include +#include +#include "lib/ini.h" +#include "lib/utils.h" +#include "config.h" +#include "plugin_manager.h" + +#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 + +extern config_t g_server_config; + +// define all basic mime here +static mime_t _mimes[] = { + {"image/bmp", "bmp"}, + {"image/jpeg", "jpg,jpeg"}, + {"text/css", "css"}, + {"text/markdown", "md"}, + {"text/csv", "csv"}, + {"application/pdf", "pdf"}, + {"image/gif", "gif"}, + {"text/html", "html"}, + {"application/json", "json"}, + {"application/javascript", "js"}, + {"image/png", "png"}, + {"text/plain", "txt"}, + {"application/xhtml+xml", "xhtml"}, + {"application/xml", "xml"}, + {"image/svg+xml", "svg"}, + {NULL, NULL}}; + +static void init_plugins() +{ + chain_t it, it2; + dictionary_t config; + const char *value; + for_each_assoc(it, g_server_config.plugins) + { + config = (dictionary_t)it->value; + if (config) + { + for_each_assoc(it2, config) + { + LOG("Plugin %s: [%s] -> [%s]", it->key, it2->key, (char *)it2->value); + if (strncmp(it2->key, "file_type", 9) == 0 && it2->value) + { + char *file_type = strdup((char *)it2->value); + char *token; + char *stringp = file_type; + while ((token = strsep(&stringp, ","))) + { + trim(token, ' '); + if (strlen(token) > 0) + { + dput(g_server_config.handlers, token, strdup((char *)it->key)); + LOG("Plugin %s: support %s file", it->key, token); + } + } + free(file_type); + } + } + value = (char *)dvalue(config, "autoload"); + if (value && (strncmp(value, "1", 1) == 0 || strncmp(value, "true", 3) == 0)) + { + // load the plugin + LOG("Plugin %s: autoloading...", it->key); + plugin_load(it->key, config); + } + } + } +} + +static int config_handler(void *conf, const char *section, const char *name, + const char *value) +{ + config_t *pconfig = (config_t *)conf; + regmatch_t regex_matches[2]; + char buf[255]; + char *tmp; + struct stat st; + // trim(section, ' '); + // trim(value,' '); + // trim(name,' '); + // char * ppath = NULL; + if (MATCH("SERVER", "plugins")) + { + if (stat(value, &st) == -1) + mkdirp(value, 0755); + tmp = realpath(value, NULL); + if (!tmp) + { + ERROR("Unable to query real path for %s: %s", value, strerror(errno)); + } + else + { + if (pconfig->plugins_dir) + free(pconfig->plugins_dir); + pconfig->plugins_dir = tmp; + LOG("Plugin root is %s", pconfig->plugins_dir); + } + } + else if (MATCH("SERVER", "plugins_ext")) + { + if (pconfig->plugins_ext) + free(pconfig->plugins_ext); + pconfig->plugins_ext = strdup(value); + } + else if (MATCH("SERVER", "database")) + { + if (stat(value, &st) == -1) + mkdirp(value, 0700); + tmp = realpath(value, NULL); + if (!tmp) + { + ERROR("Unable to query real path for %s: %s", value, strerror(errno)); + } + else + { + if (pconfig->db_path) + free(pconfig->db_path); + pconfig->db_path = tmp; + LOG("Database root is %s", pconfig->db_path); + } + } + else if (MATCH("SERVER", "tmpdir")) + { + if (stat(value, &st) == -1) + { + mkdirp(value, 0755); + } + else + { + removeAll(value, 0); + } + tmp = realpath(value, NULL); + if (!tmp) + { + ERROR("Unable to query real path for %s: %s", value, strerror(errno)); + } + else + { + if (pconfig->tmpdir) + free(pconfig->tmpdir); + pconfig->tmpdir = tmp; + LOG("TMP root is %s", pconfig->tmpdir); + } + } + else if (MATCH("SERVER", "statistic_fifo")) + { + if (pconfig->stat_fifo_path) + free(pconfig->stat_fifo_path); + pconfig->stat_fifo_path = strdup(value); + } + else if (MATCH("SERVER", "max_upload_size")) + { + pconfig->max_upload_size = atoi(value); + } + else if (MATCH("SERVER", "maxcon")) + { + pconfig->maxcon = atoi(value); + } + else if (MATCH("SERVER", "backlog")) + { + pconfig->backlog = atoi(value); + } + else if (MATCH("SERVER", "workers")) + { + pconfig->n_workers = atoi(value); + } + else if (MATCH("SERVER", "debug_enable")) + { + (void)setenv("ANTD_DEBUG", value, 1); + pconfig->debug_enable = atoi(value); + } + else if (MATCH("SERVER", "scheduler_timeout")) + { + pconfig->scheduler_timeout = atoi(value); + } +#ifdef USE_ZLIB + else if (MATCH("SERVER", "gzip_enable")) + { + pconfig->gzip_enable = atoi(value); + } + else if (MATCH("SERVER", "gzip_types")) + { + pconfig->gzip_types = split(value, ","); + } +#endif +#ifdef USE_OPENSSL + else if (MATCH("SERVER", "ssl.cert")) + { + if (pconfig->sslcert) + free(pconfig->sslcert); + pconfig->sslcert = strdup(value); + } + else if (MATCH("SERVER", "ssl.key")) + { + if (pconfig->sslkey) + free(pconfig->sslkey); + pconfig->sslkey = strdup(value); + } + else if (MATCH("SERVER", "ssl.cipher")) + { + if (pconfig->ssl_cipher) + free(pconfig->ssl_cipher); + pconfig->ssl_cipher = strdup(value); + } +#endif + else if (strcmp(section, "MIMES") == 0) + { + dput(pconfig->mimes, name, strdup(value)); + } + else if (regex_match("PORT:\\s*([0-9]+)", section, 2, regex_matches)) + { + memset(buf, '\0', sizeof(buf)); + memcpy(buf, section + regex_matches[1].rm_so, regex_matches[1].rm_eo - regex_matches[1].rm_so); + port_config_t *p = dvalue(pconfig->ports, buf); + if (!p) + { + p = (port_config_t *)malloc(sizeof(port_config_t)); + p->htdocs = NULL; + p->plugins = NULL; + p->sock = -1; + p->type = ANTD_PROTO_ALL; + p->rules = dict_n(1); + dput(pconfig->ports, buf, p); + p->port = atoi(buf); + } + if (strcmp(name, "htdocs") == 0) + { + if (stat(value, &st) == -1) + { + mkdirp(value, 0755); + } + p->htdocs = realpath(value, NULL); + if (!p->htdocs) + { + ERROR("Unable to query real path for %s: %s", value, strerror(errno)); + p->htdocs = strdup(value); + } + else + { + LOG("Server root is %s", p->htdocs); + } + } + else if (strcmp(name, "plugins") == 0) + { + p->plugins = strdup(value); + } + else if (strcmp(name, "ssl.enable") == 0) + { + p->usessl = atoi(value); + if (p->usessl) + pconfig->enable_ssl = 1; + } + else if (strcmp(name, "protocol") == 0) + { + if (strcmp(value, "ipv4") == 0) + { + p->type = ANTD_PROTO_IP_4; + } + else if (strcmp(value, "ipv6") == 0) + { + p->type = ANTD_PROTO_IP_6; + } + else + { + ERROR("Unknown IP protocol setting %s. Enable both.", value); + } + } + else + { + // other thing should be rules + dput(p->rules, name, strdup(value)); + } + } + // plugin configuration + else if (regex_match("PLUGIN:\\s*(.*)", section, 2, regex_matches)) + { + memset(buf, '\0', sizeof(buf)); + memcpy(buf, section + regex_matches[1].rm_so, regex_matches[1].rm_eo - regex_matches[1].rm_so); + dictionary_t p = dvalue(pconfig->plugins, buf); + if (!p) + { + p = dict(); + dput(pconfig->plugins, buf, p); + } + dput(p, name, strdup(value)); + } + else + { + return 0; /* unknown section/name, error */ + } + return 1; +} + +void load_config(const char *file) +{ + g_server_config.ports = dict(); + g_server_config.plugins = dict(); + g_server_config.plugins_dir = strdup("plugins/"); + g_server_config.plugins_ext = strdup(".so"); + g_server_config.db_path = strdup("databases/"); + // g_server_config.htdocs = "htdocs/"; + g_server_config.tmpdir = strdup("/tmp/"); + g_server_config.stat_fifo_path = strdup(""); + g_server_config.n_workers = 4; + g_server_config.backlog = 1000; + g_server_config.handlers = dict(); + g_server_config.maxcon = 100; + g_server_config.max_upload_size = 10000000; // 10Mb + g_server_config.connection = 0; + g_server_config.mimes = dict(); + g_server_config.enable_ssl = 0; + g_server_config.sslcert = strdup("cert.pem"); + g_server_config.sslkey = strdup("key.pem"); + g_server_config.ssl_cipher = NULL; + g_server_config.gzip_enable = 0; + g_server_config.gzip_types = NULL; + g_server_config.debug_enable = 0; + g_server_config.scheduler_timeout = 30; // 30 s + // put it default mimes + for (int i = 0; _mimes[i].type != NULL; i++) + { + dput(g_server_config.mimes, _mimes[i].type, strdup(_mimes[i].ext)); + } + if (ini_parse(file, config_handler, &g_server_config) < 0) + { + ERROR("Can't load '%s'. Used defaut configuration", file); + } + else + { + LOG("Using configuration : %s", file); +#ifdef USE_OPENSSL + LOG("SSL enable %d", g_server_config.enable_ssl); + LOG("SSL cert %s", g_server_config.sslcert); + LOG("SSL key %s", g_server_config.sslkey); + /*if(!g_server_config.ssl_cipher) + LOG("SSL Cipher suite: %s", "HIGH"); + else + LOG("SSL Cipher suite: %s", g_server_config.ssl_cipher);*/ +#endif + } + LOG("%d mimes entries found", g_server_config.mimes->size); + // Init plugins if necessary + init_plugins(); +} + +void destroy_config() +{ + chain_t it; + freedict(g_server_config.handlers); + if (g_server_config.plugins_dir) + free(g_server_config.plugins_dir); + if (g_server_config.plugins_ext) + free(g_server_config.plugins_ext); + if (g_server_config.db_path) + free(g_server_config.db_path); + if (g_server_config.tmpdir) + free(g_server_config.tmpdir); + if (g_server_config.ssl_cipher) + free(g_server_config.ssl_cipher); + if (g_server_config.gzip_types) + list_free(&g_server_config.gzip_types); + if (g_server_config.mimes) + freedict(g_server_config.mimes); + if (g_server_config.stat_fifo_path) + free(g_server_config.stat_fifo_path); + if (g_server_config.plugins) + { + for_each_assoc(it, g_server_config.plugins) + { + freedict((dictionary_t)it->value); + } + freedict(g_server_config.plugins); + } + if (g_server_config.ports) + { + port_config_t *cnf; + for_each_assoc(it, g_server_config.ports) + { + cnf = (port_config_t *)it->value; + if (cnf != NULL) + { + if (cnf->htdocs != NULL) + free(cnf->htdocs); + if (cnf->plugins) + free(cnf->plugins); + if (cnf->sock > 0) + { + close(cnf->sock); + } + freedict(cnf->rules); + } + } + freedict(g_server_config.ports); + } + LOG("Unclosed connection: %d", g_server_config.connection); +} \ No newline at end of file diff --git a/config.h b/config.h new file mode 100644 index 0000000..61df315 --- /dev/null +++ b/config.h @@ -0,0 +1,52 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#include "lib/handle.h" + +#ifndef CONFIG_FILE +#define CONFIG_FILE "antd-config.ini" +#endif + +typedef struct +{ + unsigned int port; + int usessl; + char *htdocs; + char* plugins; + int sock; + antd_proto_t type; + dictionary_t rules; +} port_config_t; + +typedef struct +{ + //int port; + char *plugins_dir; + char *plugins_ext; + char *db_path; + //char* htdocs; + char *tmpdir; + char *stat_fifo_path; + dictionary_t handlers; + int backlog; + int maxcon; + int connection; + int n_workers; + int scheduler_timeout; + int max_upload_size; + // ssl + int enable_ssl; + char *sslcert; + char *sslkey; + char *ssl_cipher; + int gzip_enable; + int debug_enable; + list_t gzip_types; + dictionary_t mimes; + dictionary_t ports; + dictionary_t plugins; + // #endif +} config_t; +void load_config(const char *file); +void destroy_config(); +#endif \ No newline at end of file diff --git a/configure.ac b/configure.ac index 4425697..b6adcc0 100644 --- a/configure.ac +++ b/configure.ac @@ -10,17 +10,6 @@ AC_PROG_CC # libtool for linking AC_PROG_LIBTOOL # check if sqlite3 header exists -use_db=no -AC_CHECK_HEADER([sqlite3.h],[ - AC_DEFINE([USE_DB], [1],[Use sqlite3]) - use_db=yes - # check if the library exists -],[]) -AC_CHECK_LIB([sqlite3],[sqlite3_open],[],[ - if test "$use_db" = "yes"; then - AC_MSG_ERROR([Unable to find sqlite3 shared library]) - fi -]) use_ssl=no # check if libssl header exists @@ -101,7 +90,6 @@ esac # build_windows=yes # ;; # Pass the conditionals to automake -AM_CONDITIONAL([DB], [test "$use_db" = "yes"]) AM_CONDITIONAL([LINUX], [test "$build_linux" = "yes"]) AM_CONDITIONAL([WINDOWS], [test "$build_windows" = "yes"]) AM_CONDITIONAL([OSX], [test "$build_mac" = "yes"]) diff --git a/decode.c b/decode.c new file mode 100644 index 0000000..085ff10 --- /dev/null +++ b/decode.c @@ -0,0 +1,777 @@ +#ifdef USE_OPENSSL +#include +#else +#include "lib/sha1.h" +#endif +#include +#include +#include +#include +#include + +#include "decode.h" +#include "lib/handle.h" +#include "lib/utils.h" +#include "lib/scheduler.h" +#include "server.h" +#include "lib/base64.h" +#include "config.h" + +#define WS_MAGIC_STRING "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" +#define HEADER_MAX_SIZE 8192 + +extern config_t g_server_config; + +static int rule_check(const char *k, const char *v, const char *host, const char *_url, const char *_query, char *buf) +{ + // first perfom rule check on host, if not success, perform on url + regmatch_t key_matches[10]; + regmatch_t val_matches[2]; + char *query = strdup(_query); + char *url = strdup(_url); + int ret; + char *target; + char *tmp, rep[10]; + int idx = 0; + memset(rep, 0, 10); + LOG("Verify %s=%s on %s or %s", k, v, url, host); + // 1 group + if (!host || !(ret = regex_match(k, host, 10, key_matches))) + { + target = url; + ret = regex_match(k, url, 10, key_matches); + } + else + target = (char *)host; + + if (!ret) + { + free(url); + free(query); + return 0; + } + LOG("Match found on %s", target); + tmp = (char *)v; + char *search = "<([a-zA-Z0-9]+)>"; + while ((ret = regex_match(search, tmp, 2, val_matches))) + { + memcpy(buf + idx, tmp, val_matches[1].rm_so - 1); + idx += val_matches[1].rm_so - 1; + memcpy(rep, tmp + val_matches[1].rm_so, val_matches[1].rm_eo - val_matches[1].rm_so); + if (strcasecmp(rep, "url") == 0) + { + memcpy(buf + idx, url, strlen(url)); + idx += strlen(url); + } + else if (strcasecmp(rep, "query") == 0) + { + memcpy(buf + idx, query, strlen(query)); + idx += strlen(query); + } + else if (match_int(rep)) + { + int i = atoi(rep); + memcpy(buf + idx, target + key_matches[i].rm_so, key_matches[i].rm_eo - key_matches[i].rm_so); + idx += key_matches[i].rm_eo - key_matches[i].rm_so; + } + else if (strcasecmp(rep, "break") == 0) + { + // ignore it + LOG("Found break command, will break after this rule"); + } + else + { // just keep it + memcpy(buf + idx, tmp + val_matches[1].rm_so - 1, val_matches[1].rm_eo + 2 - val_matches[1].rm_so); + idx += val_matches[1].rm_eo + 2 - val_matches[1].rm_so; + } + tmp += val_matches[1].rm_eo + 1; + // break; + } + // now modify the match 2 group + if (idx > 0) + { + if (tmp) + { + // copy the remainning of tmp + memcpy(buf + idx, tmp, strlen(tmp)); + idx += strlen(tmp); + } + buf[idx] = '\0'; + } + free(url); + free(query); + LOG("New URI is %s", buf); + return 1; +} + +static char *apply_rules(dictionary_t rules, const char *host, char *url) +{ + // rule check + char *query_string = url; + while ((*query_string != '?') && (*query_string != '\0')) + query_string++; + if (*query_string == '?') + { + *query_string = '\0'; + query_string++; + } + // char* oldurl = strdup(url); + chain_t it; + char *k; + char *v; + int should_break = 0; + for_each_assoc(it, rules) + { + k = it->key; + if (it->value) + { + v = (char *)it->value; + // 1 group + if (regex_match("$", v, 0, NULL)) + { + should_break = 1; + } + if (rule_check(k, v, host, url, query_string, url)) + { + query_string = url; + + while ((*query_string != '?') && (*query_string != '\0')) + query_string++; + if (*query_string == '?') + { + *query_string = '\0'; + query_string++; + } + if (should_break) + { + i = rules->cap; + LOG("Break rule check as matched found at %s -> %s", k, v); + break; + } + } + } + } + + return strdup(query_string); +} + +static void ws_confirm_request(void *client, const char *key) +{ + char buf[256]; + char rkey[128]; + char sha_d[20]; + char base64[64]; + strncpy(rkey, key, sizeof(rkey) - 1); + int n = (int)sizeof(rkey) - (int)strlen(key); + if (n < 0) + n = 0; + strncat(rkey, WS_MAGIC_STRING, n); +#ifdef USE_OPENSSL + SHA_CTX context; +#else + SHA1_CTX context; +#endif + + SHA1_Init(&context); + SHA1_Update(&context, rkey, strlen(rkey)); + SHA1_Final((uint8_t *)sha_d, &context); + Base64encode(base64, sha_d, 20); + // send accept to client + sprintf(buf, "HTTP/1.1 101 Switching Protocols\r\n"); + antd_send(client, buf, strlen(buf)); + sprintf(buf, "Upgrade: websocket\r\n"); + antd_send(client, buf, strlen(buf)); + sprintf(buf, "Connection: Upgrade\r\n"); + antd_send(client, buf, strlen(buf)); + sprintf(buf, "Sec-WebSocket-Accept: %s\r\n", base64); + antd_send(client, buf, strlen(buf)); + sprintf(buf, "\r\n"); + antd_send(client, buf, strlen(buf)); + + LOG("%s", "Websocket is now enabled for plugin"); +} + +static void *decode_request(void *data) +{ + antd_request_t *rq = (antd_request_t *)data; + dictionary_t headers = dvalue(rq->request, "REQUEST_HEADER"); + int ws = 0; + char *ws_key = NULL; + char *method = NULL; + char *tmp; + antd_task_t *task = NULL; + ws_key = (char *)dvalue(headers, "Sec-WebSocket-Key"); + tmp = (char *)dvalue(headers, "Upgrade"); + if (tmp && strcasecmp(tmp, "websocket") == 0) + ws = 1; + method = (char *)dvalue(rq->request, "METHOD"); + task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io); + // antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE); + if (EQU(method, "GET")) + { + // if(ctype) free(ctype); + if (ws && ws_key != NULL) + { + ws_confirm_request(rq->client, ws_key); + // insert wsocket flag to request + // plugin should handle this ugraded connection + // not the server + dput(rq->request, "__web_socket__", strdup("1")); + } + // resolve task + task->handle = resolve_request; + return task; + } + else if (EQU(method, "HEAD") || EQU(method, "OPTIONS") || EQU(method, "DELETE")) + { + task->handle = resolve_request; + return task; + } + else if (EQU(method, "POST") || EQU(method, "PUT") || EQU(method, "PATCH")) + { + task->handle = resolve_request; + return task; + } + else + { + antd_error(rq->client, 501, "Request Method Not Implemented"); + return task; + } +} + +/** + * Check if the current request is e reverse proxy + * return a proxy task if this is the case + */ +static void *check_proxy(antd_request_t *rq, const char *path, const char *query) +{ + char *pattern = "^(https?)://([^:]+):([0-9]+)(.*)$"; + antd_task_t *task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io); + char buff[256]; + regmatch_t matches[5]; + int ret, size; + ret = regex_match(pattern, path, 5, matches); + + if (!ret) + { + return NULL; + } + + if (matches[1].rm_eo - matches[1].rm_so == 5) + { + // https is not supported for now + // TODO add https support + antd_error(rq->client, 503, "Service Unavailable"); + return task; + } + // http proxy request + size = matches[2].rm_eo - matches[2].rm_so < (int)sizeof(buff) ? matches[2].rm_eo - matches[2].rm_so : (int)sizeof(buff); + (void)memcpy(buff, path + matches[2].rm_so, size); + buff[size] = '\0'; + dput(rq->request, "PROXY_HOST", strdup(buff)); + + size = matches[3].rm_eo - matches[3].rm_so < (int)sizeof(buff) ? matches[3].rm_eo - matches[3].rm_so : (int)sizeof(buff); + (void)memcpy(buff, path + matches[3].rm_so, size); + buff[size] = '\0'; + dput(rq->request, "PROXY_PORT", strdup(buff)); + + dput(rq->request, "PROXY_PATH", strdup(path + matches[4].rm_so)); + dput(rq->request, "PROXY_QUERY", strdup(query)); + + task->handle = proxify; + antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_READABLE | TASK_EVT_ON_WRITABLE); + return task; +} + +/** + * Decode the cookie header to a dictionary + * @param client The client socket + * @return The Dictionary socket or NULL + */ +static void decode_cookie(const char *line, dictionary_t dic) +{ + char *token, *token1; + char *cpstr = strdup(line); + char *orgcpy = cpstr; + trim(cpstr, ' '); + trim(cpstr, '\n'); + trim(cpstr, '\r'); + + while ((token = strsep(&cpstr, ";"))) + { + trim(token, ' '); + token1 = strsep(&token, "="); + if (token1 && token && strlen(token) > 0) + { + dput(dic, token1, strdup(token)); + } + } + free(orgcpy); +} + +/** + * Decode a query string (GET request or POST URL encoded) to + * a dictionary of key-value + * @param query : the query string + * @return a dictionary of key-value + */ +static void decode_url_request(const char *query, dictionary_t dic) +{ + if (query == NULL) + return; + // str_copy = ; + char *token; + if (strlen(query) == 0) + return; + char *str_copy = strdup(query); + char *org_copy = str_copy; + // dictionary dic = dict(); + while ((token = strsep(&str_copy, "&"))) + { + char *key; + char *val = NULL; + if (strlen(token) > 0) + { + key = strsep(&token, "="); + if (key && strlen(key) > 0) + { + val = strsep(&token, "="); + if (!val) + val = ""; + dput(dic, key, url_decode(val)); + } + } + } + free(org_copy); + // return dic; +} + +/** + * Decode the HTTP request header + */ + +void *decode_request_header(void *data) +{ + antd_request_t *rq = (antd_request_t *)data; + rq->client->state = ANTD_CLIENT_HEADER_DECODE; + dictionary_t cookie = NULL; + char *line; + char *token; + char *query = NULL; + char *host = NULL; + char buf[2 * BUFFLEN]; + int header_size = 0; + int ret; + char *url = (char *)dvalue(rq->request, "REQUEST_QUERY"); + dictionary_t xheader = dvalue(rq->request, "REQUEST_HEADER"); + dictionary_t request = dvalue(rq->request, "REQUEST_DATA"); + char *port_s = (char *)dvalue(rq->request, "SERVER_PORT"); + port_config_t *pcnf = (port_config_t *)dvalue(g_server_config.ports, port_s); + antd_task_t *task; + // first real all header + // this for check if web socket is enabled + + while (((ret = read_buf(rq->client, buf, sizeof(buf))) > 0) && strcmp("\r\n", buf)) + { + header_size += ret; + line = buf; + trim(line, '\n'); + trim(line, '\r'); + token = strsep(&line, ":"); + trim(token, ' '); + trim(line, ' '); + if (token && line && strlen(line) > 0) + { + verify_header(token); + dput(xheader, token, strdup(line)); + } + if (token != NULL && strcasecmp(token, "Cookie") == 0) + { + if (!cookie) + { + cookie = dict(); + } + decode_cookie(line, cookie); + } + else if (token != NULL && strcasecmp(token, "Host") == 0) + { + host = strdup(line); + } + if (header_size > HEADER_MAX_SIZE) + { + antd_error(rq->client, 413, "Payload Too Large"); + ERROR("Header size too large (%d): %d vs %d", rq->client->sock, header_size, HEADER_MAX_SIZE); + task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io); + return task; + } + } + if (ret == 0) + { + // antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE); + task = antd_create_task(decode_request_header, (void *)rq, NULL, rq->client->last_io); + antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_READABLE); + return task; + } + // check for content length size + line = (char *)dvalue(xheader, "Content-Length"); + if (line) + { + int clen = atoi(line); + if (clen > g_server_config.max_upload_size) + { + antd_error(rq->client, 413, "Request body data is too large"); + // dirty fix, wait for message to be sent + // 100 ms sleep + usleep(100000); + task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io); + return task; + } + } + +#ifdef USE_ZLIB + // check for gzip + line = (char *)dvalue(xheader, "Accept-Encoding"); + if (line) + { + if (regex_match("gzip", line, 0, NULL)) + { + rq->client->z_level = ANTD_CGZ; + } + else if (regex_match("deflate", line, 0, NULL)) + { + rq->client->z_level = ANTD_CDEFL; + } + else + { + rq->client->z_level = ANTD_CNONE; + } + } + else + { + rq->client->z_level = ANTD_CNONE; + } +#endif + // if(line) free(line); + memset(buf, 0, sizeof(buf)); + strncat(buf, url, sizeof(buf) - 1); + LOG("Original query (%d): %s", rq->client->sock, url); + query = apply_rules(pcnf->rules, host, buf); + LOG("Processed query: %s", query); + if (cookie) + dput(rq->request, "COOKIE", cookie); + if (host) + free(host); + + // check if this is a reverse proxy ? + task = check_proxy(rq, buf, query); + if (task) + { + if (query) + free(query); + return task; + } + LOG("REQUEST_URI:%s", buf); + dput(rq->request, "REQUEST_URI", url_decode(buf)); + if (query) + { + decode_url_request(query, request); + dput(rq->request, "REQUEST_QUERY", query); + //if(url) + // free(url); + } + // header ok, now checkmethod + task = antd_create_task(decode_request, (void *)rq, NULL, rq->client->last_io); + antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE); // + return task; +} + + + + + /** + * Decode post query string to string + */ +static char *post_data_decode(void *client, int len) +{ + char *query = (char *)malloc((len + 1) * sizeof(char)); + char *ptr = query; + int readlen = len > BUFFLEN ? BUFFLEN : len; + int read = 0, stat = 1; + while (readlen > 0 && stat >= 0) + { + stat = antd_recv_upto(client, ptr + read, readlen); + if (stat > 0) + { + read += stat; + readlen = (len - read) > BUFFLEN ? BUFFLEN : (len - read); + } + if (stat == 0) + { + if (difftime(time(NULL), ((antd_client_t *)client)->last_io) > MAX_IO_WAIT_TIME) + { + stat = -1; + } + else + { + usleep(POLL_EVENT_TO * 1000); + } + } + } + + if (read > 0) + query[read] = '\0'; + else + { + free(query); + query = NULL; + } + return query; +} + +static void *decode_multi_part_request_data(void *data) +{ + // loop through each part separated by the boundary + char *line; + char *part_name = NULL; + char *part_file = NULL; + char *file_path; + char buf[BUFFLEN]; + char *field; + int len; + // dictionary dic = NULL; + int fd = -1; + char *token, *keytoken, *valtoken; + antd_request_t *rq = (antd_request_t *)data; + antd_task_t *task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io); + antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE); + char *boundary = (char *)dvalue(rq->request, "MULTI_PART_BOUNDARY"); + dictionary_t dic = (dictionary_t)dvalue(rq->request, "REQUEST_DATA"); + // search for content disposition: + while (((len = read_buf(rq->client, buf, sizeof(buf))) > 0) && !strstr(buf, "Content-Disposition:")) + ; + ; + if (len <= 0 || !strstr(buf, "Content-Disposition:")) + { + return task; + } + char *boundend = __s("%s--", boundary); + line = buf; + // extract parameters from header + while ((token = strsep(&line, ";"))) + { + keytoken = strsep(&token, "="); + if (keytoken && strlen(keytoken) > 0) + { + trim(keytoken, ' '); + valtoken = strsep(&token, "="); + if (valtoken) + { + trim(valtoken, ' '); + trim(valtoken, '\n'); + trim(valtoken, '\r'); + trim(valtoken, '\"'); + if (strcmp(keytoken, "name") == 0) + { + part_name = strdup(valtoken); + } + else if (strcmp(keytoken, "filename") == 0) + { + part_file = strdup(valtoken); + } + } + } + } + line = NULL; + // get the binary data + LOG("Part file: %s part name: %s", part_file, part_name); + if (part_name != NULL) + { + // go to the beginning of data bock + while ((len = read_buf(rq->client, buf, sizeof(buf))) > 0 && strncmp(buf, "\r\n", 2) != 0) + ; + ; + + if (part_file == NULL) + { + /** + * WARNING: + * This allow only 1024 bytes of data (max), + * out of this range, the data is cut out. + * Need an efficient way to handle this + */ + len = read_buf(rq->client, buf, sizeof(buf)); + if (len > 0) + { + line = buf; + trim(line, '\n'); + trim(line, '\r'); + trim(line, ' '); + dput(dic, part_name, strdup(line)); + } + // find the next boundary + while ((len = read_buf(rq->client, buf, sizeof(buf))) > 0 && !strstr(buf, boundary)) + { + line = buf; + } + } + else + { + file_path = __s("%s/%s.%u", g_server_config.tmpdir, part_file, (unsigned)time(NULL)); + fd = open(file_path, O_WRONLY | O_CREAT, 0600); + if (fd > 0) + { + int totalsize = 0, len = 0; + // read until the next boundary + // TODO: this is not efficient for big file + // need a solution + while ((len = read_buf(rq->client, buf, sizeof(buf))) > 0 && !strstr(buf, boundary)) + { + len = guard_write(fd, buf, len); + totalsize += len; + } + + // remove \r\n at the end + lseek(fd, 0, SEEK_SET); + // fseek(fp,-2, SEEK_CUR); + totalsize -= 2; + int stat = ftruncate(fd, totalsize); + LOG("Write %d bytes to %s", totalsize, file_path); + UNUSED(stat); + close(fd); + line = buf; + + field = __s("%s.file", part_name); + dput(dic, field, strdup(part_file)); + free(field); + field = __s("%s.tmp", part_name); + dput(dic, field, strdup(file_path)); + free(field); + field = __s("%s.size", part_name); + dput(dic, field, __s("%d", totalsize)); + free(field); + field = __s("%s.ext", part_name); + dput(dic, field, ext(part_file)); + free(field); + } + else + { + ERROR("Cannot write file to :%s", file_path); + } + free(file_path); + free(part_file); + } + free(part_name); + } + /** + * The upload procedure may take time, the task access time should be updated + * after the procedure finish + */ + task->access_time = rq->client->last_io; + // check if end of request + if (line && strstr(line, boundend)) + { + // LOG("End request %s", boundend); + free(boundend); + // antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE); + return task; + } + free(boundend); + if (line && strstr(line, boundary)) + { + // continue upload + // antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_READABLE); + task->handle = decode_multi_part_request_data; + return task; + } + // antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE); + return task; +} +/** + * Decode the multi-part form data from the POST request + * If it is a file upload, copy the file to tmp dir + */ +static void *decode_multi_part_request(void *data, const char *ctype) +{ + char *boundary; + char line[BUFFLEN]; + char *str_copy = (char *)ctype; + int len; + antd_request_t *rq = (antd_request_t *)data; + antd_task_t *task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io); + // antd_task_bind_event(task, rq->client->sock, 0, ); + antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE); + // dictionary dic = NULL; + boundary = strsep(&str_copy, "="); // discard first part + boundary = str_copy; + if (boundary && strlen(boundary) > 0) + { + // dic = dict(); + trim(boundary, ' '); + dput(rq->request, "MULTI_PART_BOUNDARY", strdup(boundary)); + // find first boundary + while (((len = read_buf(rq->client, line, sizeof(line))) > 0) && !strstr(line, boundary)) + ; + if (len > 0) + { + task->handle = decode_multi_part_request_data; + } + } + return task; +} + + +void *decode_post_request(void *data) +{ + antd_request_t *rq = (antd_request_t *)data; + rq->client->state = ANTD_CLIENT_RQ_DATA_DECODE; + dictionary_t request = dvalue(rq->request, "REQUEST_DATA"); + dictionary_t headers = dvalue(rq->request, "REQUEST_HEADER"); + char *ctype = NULL; + int clen = -1; + char *tmp; + antd_task_t *task = NULL; + ctype = (char *)dvalue(headers, "Content-Type"); + tmp = (char *)dvalue(headers, "Content-Length"); + if (tmp) + clen = atoi(tmp); + char *method = (char *)dvalue(rq->request, "METHOD"); + task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io); + // antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE); + if (!method || (!EQU(method, "POST") && !EQU(method, "PUT") && !EQU(method, "PATCH"))) + return task; + if (ctype == NULL || clen == -1) + { + antd_error(rq->client, 400, "Bad Request, missing content description"); + return task; + } + // decide what to do with the data + if (strstr(ctype, FORM_URL_ENCODE)) + { + char *pquery = post_data_decode(rq->client, clen); + if (pquery) + { + decode_url_request(pquery, request); + free(pquery); + } + else if (clen > 0) + { + // WARN: this may not work on ssl socket + // antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_READABLE | TASK_EVT_ON_WRITABLE); + // task->handle = decode_post_request; + antd_error(rq->client, 400, "Bad Request, missing content data"); + return task; + } + } + else if (strstr(ctype, FORM_MULTI_PART)) + { + free(task); + return decode_multi_part_request(rq, ctype); + } + else + { + /*let plugin hande this data as we dont known how to deal with it*/ + dput(request, "HAS_RAW_BODY", strdup("true")); + } + antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE); + return task; +} \ No newline at end of file diff --git a/decode.h b/decode.h new file mode 100644 index 0000000..9e6be57 --- /dev/null +++ b/decode.h @@ -0,0 +1,7 @@ +#ifndef DECODE_H +#define DECODE_H + +void *decode_request_header(void *data); +void *decode_post_request(void *data); + +#endif \ No newline at end of file diff --git a/http_server.c b/http_server.c deleted file mode 100644 index 361371a..0000000 --- a/http_server.c +++ /dev/null @@ -1,1827 +0,0 @@ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef USE_OPENSSL -#include -#include -#include -#else -#include "lib/sha1.h" -#endif - -#include "http_server.h" -#include "lib/handle.h" -#include "plugin_manager.h" -#include "lib/scheduler.h" -#include "lib/utils.h" -#include "lib/ini.h" -#include "lib/base64.h" - -#define HEADER_MAX_SIZE 8192 - -typedef union -{ - struct sockaddr_in6 addr6; - struct sockaddr_in addr4; -} antd_server_sockaddr_t; - - -// define all basic mime here -static mime_t _mimes[] = { - {"image/bmp", "bmp"}, - {"image/jpeg", "jpg,jpeg"}, - {"text/css", "css"}, - {"text/markdown", "md"}, - {"text/csv", "csv"}, - {"application/pdf", "pdf"}, - {"image/gif", "gif"}, - {"text/html", "html"}, - {"application/json", "json"}, - {"application/javascript", "js"}, - {"image/png", "png"}, - {"text/plain", "txt"}, - {"application/xhtml+xml", "xhtml"}, - {"application/xml", "xml"}, - {"image/svg+xml", "svg"}, - {NULL, NULL}}; - -static pthread_mutex_t server_mux = PTHREAD_MUTEX_INITIALIZER; -config_t server_config; - -config_t *config() -{ - return &server_config; -} -void destroy_config() -{ - chain_t it; - freedict(server_config.handlers); - if (server_config.plugins_dir) - free(server_config.plugins_dir); - if (server_config.plugins_ext) - free(server_config.plugins_ext); - if (server_config.db_path) - free(server_config.db_path); - if (server_config.tmpdir) - free(server_config.tmpdir); - if (server_config.ssl_cipher) - free(server_config.ssl_cipher); - if (server_config.gzip_types) - list_free(&server_config.gzip_types); - if (server_config.mimes) - freedict(server_config.mimes); - if (server_config.stat_fifo_path) - free(server_config.stat_fifo_path); - if (server_config.plugins) - { - for_each_assoc(it, server_config.plugins) - { - freedict((dictionary_t)it->value); - } - freedict(server_config.plugins); - } - if (server_config.ports) - { - port_config_t *cnf; - for_each_assoc(it, server_config.ports) - { - cnf = (port_config_t *)it->value; - if (cnf != NULL) - { - if (cnf->htdocs != NULL) - free(cnf->htdocs); - if (cnf->plugins) - free(cnf->plugins); - if (cnf->sock > 0) - { - close(cnf->sock); - } - freedict(cnf->rules); - } - } - freedict(server_config.ports); - } - LOG("Unclosed connection: %d", server_config.connection); -} - -static int config_handler(void *conf, const char *section, const char *name, - const char *value) -{ - config_t *pconfig = (config_t *)conf; - regmatch_t regex_matches[2]; - char buf[255]; - char *tmp; - struct stat st; - // trim(section, ' '); - // trim(value,' '); - // trim(name,' '); - // char * ppath = NULL; - if (MATCH("SERVER", "plugins")) - { - if (stat(value, &st) == -1) - mkdirp(value, 0755); - tmp = realpath(value, NULL); - if (!tmp) - { - ERROR("Unable to query real path for %s: %s", value, strerror(errno)); - } - else - { - if (pconfig->plugins_dir) - free(pconfig->plugins_dir); - pconfig->plugins_dir = tmp; - LOG("Plugin root is %s", pconfig->plugins_dir); - } - } - else if (MATCH("SERVER", "plugins_ext")) - { - if (pconfig->plugins_ext) - free(pconfig->plugins_ext); - pconfig->plugins_ext = strdup(value); - } - else if (MATCH("SERVER", "database")) - { - if (stat(value, &st) == -1) - mkdirp(value, 0700); - tmp = realpath(value, NULL); - if (!tmp) - { - ERROR("Unable to query real path for %s: %s", value, strerror(errno)); - } - else - { - if (pconfig->db_path) - free(pconfig->db_path); - pconfig->db_path = tmp; - LOG("Database root is %s", pconfig->db_path); - } - } - else if (MATCH("SERVER", "tmpdir")) - { - if (stat(value, &st) == -1) - { - mkdirp(value, 0755); - } - else - { - removeAll(value, 0); - } - tmp = realpath(value, NULL); - if (!tmp) - { - ERROR("Unable to query real path for %s: %s", value, strerror(errno)); - } - else - { - if (pconfig->tmpdir) - free(pconfig->tmpdir); - pconfig->tmpdir = tmp; - LOG("TMP root is %s", pconfig->tmpdir); - } - } - else if (MATCH("SERVER", "statistic_fifo")) - { - if (pconfig->stat_fifo_path) - free(pconfig->stat_fifo_path); - pconfig->stat_fifo_path = strdup(value); - } - else if (MATCH("SERVER", "max_upload_size")) - { - pconfig->max_upload_size = atoi(value); - } - else if (MATCH("SERVER", "maxcon")) - { - pconfig->maxcon = atoi(value); - } - else if (MATCH("SERVER", "backlog")) - { - pconfig->backlog = atoi(value); - } - else if (MATCH("SERVER", "workers")) - { - pconfig->n_workers = atoi(value); - } - else if (MATCH("SERVER", "debug_enable")) - { - (void)setenv("ANTD_DEBUG", value, 1); - pconfig->debug_enable = atoi(value); - } - else if (MATCH("SERVER", "scheduler_timeout")) - { - pconfig->scheduler_timeout = atoi(value); - } -#ifdef USE_ZLIB - else if (MATCH("SERVER", "gzip_enable")) - { - pconfig->gzip_enable = atoi(value); - } - else if (MATCH("SERVER", "gzip_types")) - { - pconfig->gzip_types = split(value, ","); - } -#endif -#ifdef USE_OPENSSL - else if (MATCH("SERVER", "ssl.cert")) - { - if (pconfig->sslcert) - free(pconfig->sslcert); - pconfig->sslcert = strdup(value); - } - else if (MATCH("SERVER", "ssl.key")) - { - if (pconfig->sslkey) - free(pconfig->sslkey); - pconfig->sslkey = strdup(value); - } - else if (MATCH("SERVER", "ssl.cipher")) - { - if (pconfig->ssl_cipher) - free(pconfig->ssl_cipher); - pconfig->ssl_cipher = strdup(value); - } -#endif - else if (strcmp(section, "MIMES") == 0) - { - dput(pconfig->mimes, name, strdup(value)); - } - else if (regex_match("PORT:\\s*([0-9]+)", section, 2, regex_matches)) - { - memset(buf, '\0', sizeof(buf)); - memcpy(buf, section + regex_matches[1].rm_so, regex_matches[1].rm_eo - regex_matches[1].rm_so); - port_config_t *p = dvalue(pconfig->ports, buf); - if (!p) - { - p = (port_config_t *)malloc(sizeof(port_config_t)); - p->htdocs = NULL; - p->plugins = NULL; - p->sock = -1; - p->type = ANTD_PROTO_ALL; - p->rules = dict_n(1); - dput(pconfig->ports, buf, p); - p->port = atoi(buf); - } - if (strcmp(name, "htdocs") == 0) - { - if (stat(value, &st) == -1) - { - mkdirp(value, 0755); - } - p->htdocs = realpath(value, NULL); - if (!p->htdocs) - { - ERROR("Unable to query real path for %s: %s", value, strerror(errno)); - p->htdocs = strdup(value); - } - else - { - LOG("Server root is %s", p->htdocs); - } - } - else if (strcmp(name, "plugins") == 0) - { - p->plugins = strdup(value); - } - else if (strcmp(name, "ssl.enable") == 0) - { - p->usessl = atoi(value); - if (p->usessl) - pconfig->enable_ssl = 1; - } - else if(strcmp(name, "protocol") == 0) - { - if(strcmp(value, "ipv4") == 0) - { - p->type = ANTD_PROTO_IP_4; - } - else if(strcmp(value, "ipv6") == 0) - { - p->type = ANTD_PROTO_IP_6; - } - else - { - ERROR("Unknown IP protocol setting %s. Enable both.", value); - } - } - else - { - // other thing should be rules - dput(p->rules, name, strdup(value)); - } - } - // plugin configuration - else if (regex_match("PLUGIN:\\s*(.*)", section, 2, regex_matches)) - { - memset(buf, '\0', sizeof(buf)); - memcpy(buf, section + regex_matches[1].rm_so, regex_matches[1].rm_eo - regex_matches[1].rm_so); - dictionary_t p = dvalue(pconfig->plugins, buf); - if (!p) - { - p = dict(); - dput(pconfig->plugins, buf, p); - } - dput(p, name, strdup(value)); - } - else - { - return 0; /* unknown section/name, error */ - } - return 1; -} - -static void init_plugins() -{ - chain_t it, it2; - dictionary_t config; - const char *value; - for_each_assoc(it, server_config.plugins) - { - config = (dictionary_t)it->value; - if (config) - { - for_each_assoc(it2, config) - { - LOG("Plugin %s: [%s] -> [%s]", it->key, it2->key, (char *)it2->value); - if (strncmp(it2->key, "file_type", 9) == 0 && it2->value) - { - char *file_type = strdup((char *)it2->value); - char *token; - char *stringp = file_type; - while ((token = strsep(&stringp, ","))) - { - trim(token, ' '); - if (strlen(token) > 0) - { - dput(server_config.handlers, token, strdup((char *)it->key)); - LOG("Plugin %s: support %s file", it->key, token); - } - } - free(file_type); - } - } - value = (char *)dvalue(config, "autoload"); - if (value && (strncmp(value, "1", 1) == 0 || strncmp(value, "true", 3) == 0)) - { - // load the plugin - LOG("Plugin %s: autoloading...", it->key); - plugin_load(it->key, config); - } - } - } -} - -void load_config(const char *file) -{ - server_config.ports = dict(); - server_config.plugins = dict(); - server_config.plugins_dir = strdup("plugins/"); - server_config.plugins_ext = strdup(".so"); - server_config.db_path = strdup("databases/"); - // server_config.htdocs = "htdocs/"; - server_config.tmpdir = strdup("/tmp/"); - server_config.stat_fifo_path = strdup(""); - server_config.n_workers = 4; - server_config.backlog = 1000; - server_config.handlers = dict(); - server_config.maxcon = 100; - server_config.max_upload_size = 10000000; // 10Mb - server_config.connection = 0; - server_config.mimes = dict(); - server_config.enable_ssl = 0; - server_config.sslcert = strdup("cert.pem"); - server_config.sslkey = strdup("key.pem"); - server_config.ssl_cipher = NULL; - server_config.gzip_enable = 0; - server_config.gzip_types = NULL; - server_config.debug_enable = 0; - server_config.scheduler_timeout = 30; // 30 s - // put it default mimes - for (int i = 0; _mimes[i].type != NULL; i++) - { - dput(server_config.mimes, _mimes[i].type, strdup(_mimes[i].ext)); - } - if (ini_parse(file, config_handler, &server_config) < 0) - { - ERROR("Can't load '%s'. Used defaut configuration", file); - } - else - { - LOG("Using configuration : %s", file); -#ifdef USE_OPENSSL - LOG("SSL enable %d", server_config.enable_ssl); - LOG("SSL cert %s", server_config.sslcert); - LOG("SSL key %s", server_config.sslkey); - /*if(!server_config.ssl_cipher) - LOG("SSL Cipher suite: %s", "HIGH"); - else - LOG("SSL Cipher suite: %s", server_config.ssl_cipher);*/ -#endif - } - LOG("%d mimes entries found", server_config.mimes->size); - // Init plugins if necessary - init_plugins(); -} - -void *accept_request(void *data) -{ - char buf[BUFFLEN]; - char *token = NULL; - char *line = NULL; - antd_task_t *task; - antd_request_t *rq = (antd_request_t *)data; - - task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io); - antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE); - // first verify if the socket is ready - antd_client_t *client = (antd_client_t *)rq->client; - - struct pollfd pfd[1]; - - pfd[0].fd = client->sock; - pfd[0].events = POLLIN | POLLOUT; - - int sel = poll(pfd, 1, POLL_EVENT_TO); - if (sel == -1) - { - antd_error(rq->client, 400, "Bad request"); - return task; - } - if (pfd[0].revents & POLLERR || pfd[0].revents & POLLHUP) - { - antd_error(rq->client, 400, "Bad request"); - return task; - } - if (sel == 0 || (!(pfd[0].revents & POLLIN) && !(pfd[0].revents & POLLOUT))) - { - task->handle = accept_request; - return task; - } - // perform the ssl handshake if enabled -#ifdef USE_OPENSSL - int ret = -1, stat; - if (client->ssl && client->state == ANTD_CLIENT_ACCEPT) - { - // LOG("Atttempt %d\n", client->attempt); - if (SSL_accept((SSL *)client->ssl) == -1) - { - stat = SSL_get_error((SSL *)client->ssl, ret); - switch (stat) - { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - case SSL_ERROR_NONE: - task->handle = accept_request; - return task; - default: - ERROR("Error performing SSL handshake %d %d %s", stat, ret, ERR_error_string(ERR_get_error(), NULL)); - antd_error(rq->client, 400, "Invalid SSL request"); - // server_config.connection++; - ERR_print_errors_fp(stderr); - return task; - } - } - client->state = ANTD_CLIENT_HANDSHAKE; - task->handle = accept_request; - // LOG("Handshake finish for %d\n", client->sock); - return task; - } - else - { - if (!((pfd[0].revents & POLLIN))) - { - task->handle = accept_request; - return task; - } - } -#endif - // LOG("Ready for reading %d\n", client->sock); - // server_config.connection++; - client->state = ANTD_CLIENT_PROTO_CHECK; - read_buf(rq->client, buf, sizeof(buf)); - line = buf; - LOG("Request (%d): %s", rq->client->sock, line); - // get the method string - token = strsep(&line, " "); - if (!line) - { - // LOG("No method found"); - antd_error(rq->client, 405, "No method found"); - return task; - } - trim(token, ' '); - trim(line, ' '); - dput(rq->request, "METHOD", strdup(token)); - // get the request - token = strsep(&line, " "); - if (!line) - { - // LOG("No request found"); - antd_error(rq->client, 400, "Bad request"); - return task; - } - trim(token, ' '); - trim(line, ' '); - trim(line, '\n'); - trim(line, '\r'); - dput(rq->request, "PROTOCOL", strdup(line)); - dput(rq->request, "REQUEST_QUERY", strdup(token)); - line = token; - token = strsep(&line, "?"); - dput(rq->request, "REQUEST_PATH", url_decode(token)); - // decode request - // now return the task - task->handle = decode_request_header; - return task; -} - -void *resolve_request(void *data) -{ - struct stat st; - char path[2 * BUFFLEN]; - antd_request_t *rq = (antd_request_t *)data; - antd_task_t *task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io); - char *url = (char *)dvalue(rq->request, "REQUEST_URI"); - char *newurl = NULL; - char *rqp = NULL; - char *oldrqp = NULL; - rq->client->state = ANTD_CLIENT_RESOLVE_REQUEST; - char * root = (char *)dvalue(rq->request, "SERVER_WWW_ROOT"); - snprintf(path, sizeof(path), "%s/%s", root, url); - LOG("URL is : %s", url); - LOG("Resource Path is : %s", path); - // if (path[strlen(path) - 1] == '/') - // strcat(path, "index.html"); - if (stat(path, &st) == -1) - { - free(task); - rqp = strdup(url); - oldrqp = rqp; - trim(rqp, '/'); - newurl = strsep(&rqp, "/"); - if (!rqp) - rqp = strdup("/"); - else - rqp = strdup(rqp); - dput(rq->request, "REQUEST_URI", rqp); - dput(rq->request, "RESOURCE_PATH", __s("%s/%s", root,rqp)); - LOG("Execute plugin %s", newurl); - task = execute_plugin(rq, newurl); - free(oldrqp); - return task; - } - else - { - if (S_ISDIR(st.st_mode)) - { - strcat(path, "/index.html"); - if (stat(path, &st) == -1) - { - chain_t it; - newurl = NULL; - for_each_assoc(it, server_config.handlers) - { - memset(path, 0, sizeof(path)); - snprintf(path, sizeof(path), "%s/%s/index.%s", root, url, it->key); - if (stat(path, &st) == 0) - { - i = server_config.handlers->cap; - newurl = path; - break; - } - } - if (!newurl) - { - antd_error(rq->client, 404, "Resource Not Found"); - return task; - } - } - } - dput(rq->request, "RESOURCE_PATH", strdup(path)); - // check if the mime is supported - // if the mime is not supported - // find an handler plugin to process it - // if the plugin is not found, forbidden access to the file should be sent - char *mime_type = mime(path); - dput(rq->request, "RESOURCE_MIME", strdup(mime_type)); - if (strcmp(mime_type, "application/octet-stream") == 0) - { - char *ex = ext(path); - char *h = NULL; - if (ex) - { - h = dvalue(server_config.handlers, ex); - free(ex); - } - if (h) - { - // sprintf(path,"/%s%s",h,url); - // LOG("WARNING::::Access octetstream via handle %s", h); - // if(execute_plugin(client,buf,method,rq) < 0) - // cannot_execute(client); - free(task); - return execute_plugin(rq, h); - } - else - antd_error(rq->client, 403, "Access forbidden"); - } - else - { - // discard all request data - dictionary_t headers = (dictionary_t)dvalue(rq->request, "REQUEST_HEADER"); - if (headers) - { - char *sclen = (char *)dvalue(headers, "Content-Length"); - unsigned clen = 0; - unsigned read = 0; - int count; - if (sclen) - { - clen = atoi(sclen); - while (read < clen) - { - count = antd_recv(rq->client, path, sizeof(path) < clen ? sizeof(path) : clen); - if (count <= 0) - break; - read += count; - } - } - } - antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE); - task->handle = serve_file; - } - return task; - } -} - -void *finish_request(void *data) -{ - if (!data) - return NULL; - destroy_request(data); - server_config.connection--; - LOG("Remaining connection %d", server_config.connection); - return NULL; -} - -int rule_check(const char *k, const char *v, const char *host, const char *_url, const char *_query, char *buf) -{ - // first perfom rule check on host, if not success, perform on url - regmatch_t key_matches[10]; - regmatch_t val_matches[2]; - char *query = strdup(_query); - char *url = strdup(_url); - int ret; - char *target; - char *tmp, rep[10]; - int idx = 0; - memset(rep, 0, 10); - LOG("Verify %s=%s on %s or %s", k, v, url, host); - // 1 group - if (!host || !(ret = regex_match(k, host, 10, key_matches))) - { - target = url; - ret = regex_match(k, url, 10, key_matches); - } - else - target = (char *)host; - - if (!ret) - { - free(url); - free(query); - return 0; - } - LOG("Match found on %s", target); - tmp = (char *)v; - char *search = "<([a-zA-Z0-9]+)>"; - while ((ret = regex_match(search, tmp, 2, val_matches))) - { - memcpy(buf + idx, tmp, val_matches[1].rm_so - 1); - idx += val_matches[1].rm_so - 1; - memcpy(rep, tmp + val_matches[1].rm_so, val_matches[1].rm_eo - val_matches[1].rm_so); - if (strcasecmp(rep, "url") == 0) - { - memcpy(buf + idx, url, strlen(url)); - idx += strlen(url); - } - else if (strcasecmp(rep, "query") == 0) - { - memcpy(buf + idx, query, strlen(query)); - idx += strlen(query); - } - else if (match_int(rep)) - { - int i = atoi(rep); - memcpy(buf + idx, target + key_matches[i].rm_so, key_matches[i].rm_eo - key_matches[i].rm_so); - idx += key_matches[i].rm_eo - key_matches[i].rm_so; - } - else if (strcasecmp(rep, "break") == 0) - { - // ignore it - LOG("Found break command, will break after this rule"); - } - else - { // just keep it - memcpy(buf + idx, tmp + val_matches[1].rm_so - 1, val_matches[1].rm_eo + 2 - val_matches[1].rm_so); - idx += val_matches[1].rm_eo + 2 - val_matches[1].rm_so; - } - tmp += val_matches[1].rm_eo + 1; - // break; - } - // now modify the match 2 group - if (idx > 0) - { - if (tmp) - { - // copy the remainning of tmp - memcpy(buf + idx, tmp, strlen(tmp)); - idx += strlen(tmp); - } - buf[idx] = '\0'; - } - free(url); - free(query); - LOG("New URI is %s", buf); - return 1; -} - -void *serve_file(void *data) -{ - antd_request_t *rq = (antd_request_t *)data; - antd_task_t *task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io); - char *path = (char *)dvalue(rq->request, "RESOURCE_PATH"); - char *mime_type = (char *)dvalue(rq->request, "RESOURCE_MIME"); - rq->client->state = ANTD_CLIENT_SERVE_FILE; - struct stat st; - int s = stat(path, &st); - - if (s == -1) - { - antd_error(rq->client, 404, "File not found"); - } - else - { - // check if it is modified - dictionary_t header = (dictionary_t)dvalue(rq->request, "REQUEST_HEADER"); - char *last_modif_since = (char *)dvalue(header, "If-Modified-Since"); - time_t t = st.st_ctime; - struct tm tm; - if (last_modif_since) - { - strptime(last_modif_since, "%a, %d %b %Y %H:%M:%S GMT", &tm); - t = timegm(&tm); - // t = mktime(localtime(&t)); - } - - if (last_modif_since && st.st_ctime == t) - { - // return the not changed - antd_error(rq->client, 304, ""); - } - else - { - int size = (int)st.st_size; - char ibuf[64]; - snprintf(ibuf, sizeof(ibuf), "%d", size); - antd_response_header_t rhd; - rhd.cookie = NULL; - rhd.status = 200; - rhd.header = dict(); - dput(rhd.header, "Content-Type", strdup(mime_type)); -#ifdef USE_ZLIB - if (!compressable(mime_type) || rq->client->z_level == ANTD_CNONE) -#endif - dput(rhd.header, "Content-Length", strdup(ibuf)); - gmtime_r(&st.st_ctime, &tm); - strftime(ibuf, 64, "%a, %d %b %Y %H:%M:%S GMT", &tm); - dput(rhd.header, "Last-Modified", strdup(ibuf)); - dput(rhd.header, "Cache-Control", strdup("no-cache")); - antd_send_header(rq->client, &rhd); - - __f(rq->client, path); - } - } - - return task; -} - -int startup(unsigned *port, int ipv6) -{ - int httpd = 0; - antd_server_sockaddr_t name; - memset(&name, 0, sizeof(name)); - struct sockaddr *ptr; - // TODO: allow to set listen address - if(ipv6) - { - name.addr6.sin6_port = htons(*port); - name.addr6.sin6_family = AF_INET6; - name.addr6.sin6_addr = in6addr_any; - httpd = socket(AF_INET6, SOCK_STREAM, 0); - ptr = (struct sockaddr *) & name.addr6; - } - else - { - name.addr4.sin_port = htons(*port); - name.addr4.sin_family = AF_INET; - name.addr4.sin_addr.s_addr = htonl(INADDR_ANY); - httpd = socket(AF_INET, SOCK_STREAM, 0); - ptr = (struct sockaddr *) & name.addr4; - } - - if (httpd == -1) - { - ERROR("Port %d - socket: %s", *port, strerror(errno)); - return -1; - } - - if (setsockopt(httpd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) == -1) - { - ERROR("Unable to set reuse address on port %d - setsockopt: %s", *port, strerror(errno)); - } - - if (bind(httpd, ptr, sizeof(name)) < 0) - { - ERROR("Port %d -bind: %s", *port, strerror(errno)); - return -1; - } - if (*port == 0) /* if dynamically allocating a port */ - { - socklen_t namelen = sizeof(name); - if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1) - { - ERROR("Port %d - getsockname: %s", *port, strerror(errno)); - return -1; - } - if(ipv6) - { - *port = ntohs(name.addr6.sin6_port); - } - else - { - *port = ntohs(name.addr4.sin_port); - } - } - - if (listen(httpd, server_config.backlog) < 0) - { - ERROR("Port %d - listen: %s", *port, strerror(errno)); - return -1; - } - LOG("%s Listen on port %d", ipv6?"IPv6":"IPv4", *port ); - return (httpd); -} - -char *apply_rules(dictionary_t rules, const char *host, char *url) -{ - // rule check - char *query_string = url; - while ((*query_string != '?') && (*query_string != '\0')) - query_string++; - if (*query_string == '?') - { - *query_string = '\0'; - query_string++; - } - // char* oldurl = strdup(url); - chain_t it; - char *k; - char *v; - int should_break = 0; - for_each_assoc(it, rules) - { - k = it->key; - if (it->value) - { - v = (char *)it->value; - // 1 group - if (regex_match("$", v, 0, NULL)) - { - should_break = 1; - } - if (rule_check(k, v, host, url, query_string, url)) - { - query_string = url; - - while ((*query_string != '?') && (*query_string != '\0')) - query_string++; - if (*query_string == '?') - { - *query_string = '\0'; - query_string++; - } - if (should_break) - { - i = rules->cap; - LOG("Break rule check as matched found at %s -> %s", k, v); - break; - } - } - } - } - - return strdup(query_string); -} - -static void *proxy_monitor(void *data) -{ - antd_request_t *rq = (antd_request_t *)data; - rq->client->state = ANTD_CLIENT_PROXY_MONITOR; - antd_client_t *proxy = (antd_client_t *)dvalue(rq->request, "PROXY_HANDLE"); - antd_task_t *task = antd_create_task(NULL, data, NULL, rq->client->last_io); - int pret, ret, sz1 = 0, sz2 = 0; - char *buf = NULL; - buf = (char *)malloc(BUFFLEN); - struct pollfd pfd[1]; - memset(pfd, 0, sizeof(pfd)); - pfd[0].fd = proxy->sock; - pfd[0].events = POLLIN; - ret = 1; - - do - { - sz1 = antd_recv_upto(rq->client, buf, BUFFLEN); - - if ((sz1 < 0) || (sz1 > 0 && antd_send(proxy, buf, sz1) != sz1)) - { - ret = 0; - break; - } - pret = poll(pfd, 1, 0); - if (pret < 0) - { - (void)close(proxy->sock); - return task; - } - sz2 = 0; - if (pret > 0 && (pfd[0].revents & POLLIN)) - { - sz2 = antd_recv_upto(proxy, buf, BUFFLEN); - if (sz2 <= 0 || (sz2 > 0 && antd_send(rq->client, buf, sz2) != sz2)) - { - ret = 0; - break; - } - } - if ((pret > 0) && (pfd[0].revents & POLLERR || - pfd[0].revents & POLLRDHUP || - pfd[0].revents & POLLHUP || - pfd[0].revents & POLLNVAL)) - { - ret = 0; - break; - } - } while (sz1 > 0 || sz2 > 0); - free(buf); - - if (ret == 0) - { - (void)close(proxy->sock); - return task; - } - - if (pfd[0].revents & POLLIN) - { - antd_task_bind_event(task, proxy->sock, 0, TASK_EVT_ON_READABLE); - } - else - { - antd_task_bind_event(task, proxy->sock, 50u, TASK_EVT_ON_TIMEOUT); - } - task->handle = proxy_monitor; - task->access_time = rq->client->last_io; - - antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_READABLE); - return task; -} - -static void *proxify(void *data) -{ - int sock_fd, size, ret; - char *str = NULL; - chain_t it; - antd_request_t *rq = (antd_request_t *)data; - antd_client_t *proxy = NULL; - rq->client->state = ANTD_CLIENT_RESOLVE_REQUEST; - char *host = dvalue(rq->request, "PROXY_HOST"); - int port = atoi(dvalue(rq->request, "PROXY_PORT")); - char *path = dvalue(rq->request, "PROXY_PATH"); - char *query = dvalue(rq->request, "PROXY_QUERY"); - char *ptr, *ip; - dictionary_t xheader = dvalue(rq->request, "REQUEST_HEADER"); - antd_task_t *task = antd_create_task(NULL, data, NULL, rq->client->last_io); - if (!xheader) - { - antd_error(rq->client, 400, "Badd Request"); - return task; - } - pthread_mutex_lock(&server_mux); - ip = NULL; - // ip_from_host is not threadsafe, need to lock it - ptr = ip_from_hostname(host); - if (ptr) - { - ip = strdup(ptr); - } - pthread_mutex_unlock(&server_mux); - - if (!ip) - { - antd_error(rq->client, 502, "Badd address"); - return task; - } - - sock_fd = request_socket(ip, port); - free(ip); - if (sock_fd == -1) - { - antd_error(rq->client, 503, "Service Unavailable"); - return task; - } - set_nonblock(sock_fd); - /*struct timeval timeout; - timeout.tv_sec = 2; - timeout.tv_usec = 0; //POLL_EVENT_TO*1000; - if (setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) < 0) - { - ERROR("setsockopt failed:%s", strerror(errno)); - antd_error(rq->client, 500, "Internal proxy error"); - (void)close(sock_fd); - return task; - } - - if (setsockopt(sock_fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) < 0) - { - ERROR("setsockopt failed:%s", strerror(errno)); - antd_error(rq->client, 500, "Internal proxy error"); - (void)close(sock_fd); - return task; - }*/ - - proxy = (antd_client_t *)malloc(sizeof(antd_client_t)); - proxy->sock = sock_fd; - proxy->ssl = NULL; - proxy->zstream = NULL; - proxy->z_level = ANTD_CNONE; - time(&proxy->last_io); - - // store content length here - dput(rq->request, "PROXY_HANDLE", proxy); - - str = __s("%s %s?%s HTTP/1.1\r\n", (char *)dvalue(rq->request, "METHOD"), path, query); - size = strlen(str); - ret = antd_send(proxy, str, size); - free(str); - if (ret != size) - { - antd_error(rq->client, 500, ""); - (void)close(sock_fd); - return task; - } - for_each_assoc(it, xheader) - { - str = __s("%s: %s\r\n", it->key, (char *)it->value); - size = strlen(str); - ret = antd_send(proxy, str, size); - free(str); - if (ret != size) - { - antd_error(rq->client, 500, ""); - (void)close(sock_fd); - return task; - } - } - (void)antd_send(proxy, "\r\n", 2); - // now monitor the proxy - task->handle = proxy_monitor; - task->access_time = rq->client->last_io; - // register event - antd_task_bind_event(task, proxy->sock, 0, TASK_EVT_ON_READABLE | TASK_EVT_ON_WRITABLE); - return task; -} -/** - * Check if the current request is e reverse proxy - * return a proxy task if this is the case - */ -static void *check_proxy(antd_request_t *rq, const char *path, const char *query) -{ - char *pattern = "^(https?)://([^:]+):([0-9]+)(.*)$"; - antd_task_t *task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io); - char buff[256]; - regmatch_t matches[5]; - int ret, size; - ret = regex_match(pattern, path, 5, matches); - - if (!ret) - { - return NULL; - } - - if (matches[1].rm_eo - matches[1].rm_so == 5) - { - // https is not supported for now - // TODO add https support - antd_error(rq->client, 503, "Service Unavailable"); - return task; - } - // http proxy request - size = matches[2].rm_eo - matches[2].rm_so < (int)sizeof(buff) ? matches[2].rm_eo - matches[2].rm_so : (int)sizeof(buff); - (void)memcpy(buff, path + matches[2].rm_so, size); - buff[size] = '\0'; - dput(rq->request, "PROXY_HOST", strdup(buff)); - - size = matches[3].rm_eo - matches[3].rm_so < (int)sizeof(buff) ? matches[3].rm_eo - matches[3].rm_so : (int)sizeof(buff); - (void)memcpy(buff, path + matches[3].rm_so, size); - buff[size] = '\0'; - dput(rq->request, "PROXY_PORT", strdup(buff)); - - dput(rq->request, "PROXY_PATH", strdup(path + matches[4].rm_so)); - dput(rq->request, "PROXY_QUERY", strdup(query)); - - task->handle = proxify; - antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_READABLE | TASK_EVT_ON_WRITABLE); - return task; -} -/** - * Decode the HTTP request header - */ - -void *decode_request_header(void *data) -{ - antd_request_t *rq = (antd_request_t *)data; - rq->client->state = ANTD_CLIENT_HEADER_DECODE; - dictionary_t cookie = NULL; - char *line; - char *token; - char *query = NULL; - char *host = NULL; - char buf[2 * BUFFLEN]; - int header_size = 0; - int ret; - char *url = (char *)dvalue(rq->request, "REQUEST_QUERY"); - dictionary_t xheader = dvalue(rq->request, "REQUEST_HEADER"); - dictionary_t request = dvalue(rq->request, "REQUEST_DATA"); - char *port_s = (char *)dvalue(rq->request, "SERVER_PORT"); - port_config_t *pcnf = (port_config_t *)dvalue(server_config.ports, port_s); - antd_task_t *task; - // first real all header - // this for check if web socket is enabled - - while (((ret = read_buf(rq->client, buf, sizeof(buf))) > 0) && strcmp("\r\n", buf)) - { - header_size += ret; - line = buf; - trim(line, '\n'); - trim(line, '\r'); - token = strsep(&line, ":"); - trim(token, ' '); - trim(line, ' '); - if (token && line && strlen(line) > 0) - { - verify_header(token); - dput(xheader, token, strdup(line)); - } - if (token != NULL && strcasecmp(token, "Cookie") == 0) - { - if (!cookie) - { - cookie = dict(); - } - decode_cookie(line, cookie); - } - else if (token != NULL && strcasecmp(token, "Host") == 0) - { - host = strdup(line); - } - if (header_size > HEADER_MAX_SIZE) - { - antd_error(rq->client, 413, "Payload Too Large"); - ERROR("Header size too large (%d): %d vs %d", rq->client->sock, header_size, HEADER_MAX_SIZE); - task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io); - return task; - } - } - if (ret == 0) - { - // antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE); - task = antd_create_task(decode_request_header, (void *)rq, NULL, rq->client->last_io); - antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_READABLE); - return task; - } - // check for content length size - line = (char *)dvalue(xheader, "Content-Length"); - if (line) - { - int clen = atoi(line); - if (clen > server_config.max_upload_size) - { - antd_error(rq->client, 413, "Request body data is too large"); - // dirty fix, wait for message to be sent - // 100 ms sleep - usleep(100000); - task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io); - return task; - } - } - -#ifdef USE_ZLIB - // check for gzip - line = (char *)dvalue(xheader, "Accept-Encoding"); - if (line) - { - if (regex_match("gzip", line, 0, NULL)) - { - rq->client->z_level = ANTD_CGZ; - } - else if (regex_match("deflate", line, 0, NULL)) - { - rq->client->z_level = ANTD_CDEFL; - } - else - { - rq->client->z_level = ANTD_CNONE; - } - } - else - { - rq->client->z_level = ANTD_CNONE; - } -#endif - // if(line) free(line); - memset(buf, 0, sizeof(buf)); - strncat(buf, url, sizeof(buf) - 1); - LOG("Original query (%d): %s", rq->client->sock, url); - query = apply_rules(pcnf->rules, host, buf); - LOG("Processed query: %s", query); - if (cookie) - dput(rq->request, "COOKIE", cookie); - if (host) - free(host); - - // check if this is a reverse proxy ? - task = check_proxy(rq, buf, query); - if (task) - { - if (query) - free(query); - return task; - } - LOG("REQUEST_URI:%s", buf); - dput(rq->request, "REQUEST_URI", url_decode(buf)); - if (query) - { - decode_url_request(query, request); - dput(rq->request, "REQUEST_QUERY", query); - //if(url) - // free(url); - } - // header ok, now checkmethod - task = antd_create_task(decode_request, (void *)rq, NULL, rq->client->last_io); - antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE); // - return task; -} - -void *decode_request(void *data) -{ - antd_request_t *rq = (antd_request_t *)data; - dictionary_t headers = dvalue(rq->request, "REQUEST_HEADER"); - int ws = 0; - char *ws_key = NULL; - char *method = NULL; - char *tmp; - antd_task_t *task = NULL; - ws_key = (char *)dvalue(headers, "Sec-WebSocket-Key"); - tmp = (char *)dvalue(headers, "Upgrade"); - if (tmp && strcasecmp(tmp, "websocket") == 0) - ws = 1; - method = (char *)dvalue(rq->request, "METHOD"); - task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io); - // antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE); - if (EQU(method, "GET")) - { - // if(ctype) free(ctype); - if (ws && ws_key != NULL) - { - ws_confirm_request(rq->client, ws_key); - // insert wsocket flag to request - // plugin should handle this ugraded connection - // not the server - dput(rq->request, "__web_socket__", strdup("1")); - } - // resolve task - task->handle = resolve_request; - return task; - } - else if (EQU(method, "HEAD") || EQU(method, "OPTIONS") || EQU(method, "DELETE")) - { - task->handle = resolve_request; - return task; - } - else if (EQU(method, "POST") || EQU(method, "PUT") || EQU(method, "PATCH")) - { - task->handle = resolve_request; - return task; - } - else - { - antd_error(rq->client, 501, "Request Method Not Implemented"); - return task; - } -} - -void *decode_post_request(void *data) -{ - antd_request_t *rq = (antd_request_t *)data; - rq->client->state = ANTD_CLIENT_RQ_DATA_DECODE; - dictionary_t request = dvalue(rq->request, "REQUEST_DATA"); - dictionary_t headers = dvalue(rq->request, "REQUEST_HEADER"); - char *ctype = NULL; - int clen = -1; - char *tmp; - antd_task_t *task = NULL; - ctype = (char *)dvalue(headers, "Content-Type"); - tmp = (char *)dvalue(headers, "Content-Length"); - if (tmp) - clen = atoi(tmp); - char *method = (char *)dvalue(rq->request, "METHOD"); - task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io); - // antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE); - if (!method || (!EQU(method, "POST") && !EQU(method, "PUT") && !EQU(method, "PATCH"))) - return task; - if (ctype == NULL || clen == -1) - { - antd_error(rq->client, 400, "Bad Request, missing content description"); - return task; - } - // decide what to do with the data - if (strstr(ctype, FORM_URL_ENCODE)) - { - char *pquery = post_data_decode(rq->client, clen); - if (pquery) - { - decode_url_request(pquery, request); - free(pquery); - } - else if (clen > 0) - { - // WARN: this may not work on ssl socket - // antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_READABLE | TASK_EVT_ON_WRITABLE); - // task->handle = decode_post_request; - antd_error(rq->client, 400, "Bad Request, missing content data"); - return task; - } - } - else if (strstr(ctype, FORM_MULTI_PART)) - { - free(task); - return decode_multi_part_request(rq, ctype); - } - else - { - /*let plugin hande this data as we dont known how to deal with it*/ - dput(request, "HAS_RAW_BODY", strdup("true")); - } - antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE); - return task; -} - -/** - * Send header to the client to confirm - * that the websocket is accepted by - * our server - */ -void ws_confirm_request(void *client, const char *key) -{ - char buf[256]; - char rkey[128]; - char sha_d[20]; - char base64[64]; - strncpy(rkey, key, sizeof(rkey) - 1); - int n = (int)sizeof(rkey) - (int)strlen(key); - if (n < 0) - n = 0; - strncat(rkey, WS_MAGIC_STRING, n); -#ifdef USE_OPENSSL - SHA_CTX context; -#else - SHA1_CTX context; -#endif - - SHA1_Init(&context); - SHA1_Update(&context, rkey, strlen(rkey)); - SHA1_Final((uint8_t *)sha_d, &context); - Base64encode(base64, sha_d, 20); - // send accept to client - sprintf(buf, "HTTP/1.1 101 Switching Protocols\r\n"); - antd_send(client, buf, strlen(buf)); - sprintf(buf, "Upgrade: websocket\r\n"); - antd_send(client, buf, strlen(buf)); - sprintf(buf, "Connection: Upgrade\r\n"); - antd_send(client, buf, strlen(buf)); - sprintf(buf, "Sec-WebSocket-Accept: %s\r\n", base64); - antd_send(client, buf, strlen(buf)); - sprintf(buf, "\r\n"); - antd_send(client, buf, strlen(buf)); - - LOG("%s", "Websocket is now enabled for plugin"); -} -/** - * Decode the cookie header to a dictionary - * @param client The client socket - * @return The Dictionary socket or NULL - */ -void decode_cookie(const char *line, dictionary_t dic) -{ - char *token, *token1; - char *cpstr = strdup(line); - char *orgcpy = cpstr; - trim(cpstr, ' '); - trim(cpstr, '\n'); - trim(cpstr, '\r'); - - while ((token = strsep(&cpstr, ";"))) - { - trim(token, ' '); - token1 = strsep(&token, "="); - if (token1 && token && strlen(token) > 0) - { - dput(dic, token1, strdup(token)); - } - } - free(orgcpy); -} -/** - * Decode the multi-part form data from the POST request - * If it is a file upload, copy the file to tmp dir - */ -void *decode_multi_part_request(void *data, const char *ctype) -{ - char *boundary; - char line[BUFFLEN]; - char *str_copy = (char *)ctype; - int len; - antd_request_t *rq = (antd_request_t *)data; - antd_task_t *task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io); - // antd_task_bind_event(task, rq->client->sock, 0, ); - antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE); - // dictionary dic = NULL; - boundary = strsep(&str_copy, "="); // discard first part - boundary = str_copy; - if (boundary && strlen(boundary) > 0) - { - // dic = dict(); - trim(boundary, ' '); - dput(rq->request, "MULTI_PART_BOUNDARY", strdup(boundary)); - // find first boundary - while (((len = read_buf(rq->client, line, sizeof(line))) > 0) && !strstr(line, boundary)) - ; - if (len > 0) - { - task->handle = decode_multi_part_request_data; - } - } - return task; -} -void *decode_multi_part_request_data(void *data) -{ - // loop through each part separated by the boundary - char *line; - char *part_name = NULL; - char *part_file = NULL; - char *file_path; - char buf[BUFFLEN]; - char *field; - int len; - // dictionary dic = NULL; - int fd = -1; - char *token, *keytoken, *valtoken; - antd_request_t *rq = (antd_request_t *)data; - antd_task_t *task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io); - antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE); - char *boundary = (char *)dvalue(rq->request, "MULTI_PART_BOUNDARY"); - dictionary_t dic = (dictionary_t)dvalue(rq->request, "REQUEST_DATA"); - // search for content disposition: - while (((len = read_buf(rq->client, buf, sizeof(buf))) > 0) && !strstr(buf, "Content-Disposition:")) - ; - ; - if (len <= 0 || !strstr(buf, "Content-Disposition:")) - { - return task; - } - char *boundend = __s("%s--", boundary); - line = buf; - // extract parameters from header - while ((token = strsep(&line, ";"))) - { - keytoken = strsep(&token, "="); - if (keytoken && strlen(keytoken) > 0) - { - trim(keytoken, ' '); - valtoken = strsep(&token, "="); - if (valtoken) - { - trim(valtoken, ' '); - trim(valtoken, '\n'); - trim(valtoken, '\r'); - trim(valtoken, '\"'); - if (strcmp(keytoken, "name") == 0) - { - part_name = strdup(valtoken); - } - else if (strcmp(keytoken, "filename") == 0) - { - part_file = strdup(valtoken); - } - } - } - } - line = NULL; - // get the binary data - LOG("Part file: %s part name: %s", part_file, part_name); - if (part_name != NULL) - { - // go to the beginning of data bock - while ((len = read_buf(rq->client, buf, sizeof(buf))) > 0 && strncmp(buf, "\r\n", 2) != 0) - ; - ; - - if (part_file == NULL) - { - /** - * WARNING: - * This allow only 1024 bytes of data (max), - * out of this range, the data is cut out. - * Need an efficient way to handle this - */ - len = read_buf(rq->client, buf, sizeof(buf)); - if (len > 0) - { - line = buf; - trim(line, '\n'); - trim(line, '\r'); - trim(line, ' '); - dput(dic, part_name, strdup(line)); - } - // find the next boundary - while ((len = read_buf(rq->client, buf, sizeof(buf))) > 0 && !strstr(buf, boundary)) - { - line = buf; - } - } - else - { - file_path = __s("%s/%s.%u", server_config.tmpdir, part_file, (unsigned)time(NULL)); - fd = open(file_path, O_WRONLY | O_CREAT, 0600); - if (fd > 0) - { - int totalsize = 0, len = 0; - // read until the next boundary - // TODO: this is not efficient for big file - // need a solution - while ((len = read_buf(rq->client, buf, sizeof(buf))) > 0 && !strstr(buf, boundary)) - { - len = guard_write(fd, buf, len); - totalsize += len; - } - - // remove \r\n at the end - lseek(fd, 0, SEEK_SET); - // fseek(fp,-2, SEEK_CUR); - totalsize -= 2; - int stat = ftruncate(fd, totalsize); - LOG("Write %d bytes to %s", totalsize, file_path); - UNUSED(stat); - close(fd); - line = buf; - - field = __s("%s.file", part_name); - dput(dic, field, strdup(part_file)); - free(field); - field = __s("%s.tmp", part_name); - dput(dic, field, strdup(file_path)); - free(field); - field = __s("%s.size", part_name); - dput(dic, field, __s("%d", totalsize)); - free(field); - field = __s("%s.ext", part_name); - dput(dic, field, ext(part_file)); - free(field); - } - else - { - ERROR("Cannot write file to :%s", file_path); - } - free(file_path); - free(part_file); - } - free(part_name); - } - /** - * The upload procedure may take time, the task access time should be updated - * after the procedure finish - */ - task->access_time = rq->client->last_io; - // check if end of request - if (line && strstr(line, boundend)) - { - // LOG("End request %s", boundend); - free(boundend); - // antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE); - return task; - } - free(boundend); - if (line && strstr(line, boundary)) - { - // continue upload - // antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_READABLE); - task->handle = decode_multi_part_request_data; - return task; - } - // antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE); - return task; -} -/** - * Decode a query string (GET request or POST URL encoded) to - * a dictionary of key-value - * @param query : the query string - * @return a dictionary of key-value - */ -void decode_url_request(const char *query, dictionary_t dic) -{ - if (query == NULL) - return; - // str_copy = ; - char *token; - if (strlen(query) == 0) - return; - char *str_copy = strdup(query); - char *org_copy = str_copy; - // dictionary dic = dict(); - while ((token = strsep(&str_copy, "&"))) - { - char *key; - char *val = NULL; - if (strlen(token) > 0) - { - key = strsep(&token, "="); - if (key && strlen(key) > 0) - { - val = strsep(&token, "="); - if (!val) - val = ""; - dput(dic, key, url_decode(val)); - } - } - } - free(org_copy); - // return dic; -} -/** - * Decode post query string to string - */ -char *post_data_decode(void *client, int len) -{ - char *query = (char *)malloc((len + 1) * sizeof(char)); - char *ptr = query; - int readlen = len > BUFFLEN ? BUFFLEN : len; - int read = 0, stat = 1; - while (readlen > 0 && stat >= 0) - { - stat = antd_recv_upto(client, ptr + read, readlen); - if (stat > 0) - { - read += stat; - readlen = (len - read) > BUFFLEN ? BUFFLEN : (len - read); - } - if (stat == 0) - { - if (difftime(time(NULL), ((antd_client_t *)client)->last_io) > MAX_IO_WAIT_TIME) - { - stat = -1; - } - else - { - usleep(POLL_EVENT_TO * 1000); - } - } - } - - if (read > 0) - query[read] = '\0'; - else - { - free(query); - query = NULL; - } - return query; -} - -/** - * Execute a plugin based on the http requeset - * First decode the http request header to find the correct plugin - * and the correct function on the plugin - * Second, decode all parameters necessary of the request and pass it - * to the callback function. - * Execute the callback function if sucess - * @param client soket client - * @param path request path - * @param method request method - * @param query_string GET query string - * @return -1 if failure - * 1 if sucess - */ -void *execute_plugin(void *data, const char *pname) -{ - void *(*fn)(void *); - plugin_header_t *(*metafn)(); - plugin_header_t *meta = NULL; - struct plugin_entry *plugin; - char *error; - char pattern[256]; - - antd_request_t *rq = (antd_request_t *)data; - antd_task_t *task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io); - antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE); - - snprintf(pattern, sizeof(pattern), "\\b%s\\b", pname); - char *port_s = (char *)dvalue(rq->request, "SERVER_PORT"); - port_config_t *pcnf = (port_config_t *)dvalue(server_config.ports, port_s); - - // check if plugin is enabled on this port - if (!pcnf->plugins || !regex_match(pattern, pcnf->plugins, 0, NULL)) - { - LOG("No plugin matched in [%s] using pattern [%s]", pcnf->plugins, pattern); - antd_error(rq->client, 403, "Access forbidden"); - return task; - } - - // LOG("Plugin name '%s'", pname); - rq->client->state = ANTD_CLIENT_PLUGIN_EXEC; - // load the plugin - pthread_mutex_lock(&server_mux); - plugin = plugin_load((char *)pname, dvalue(server_config.plugins, pname)); - pthread_mutex_unlock(&server_mux); - if (plugin == NULL) - { - antd_error(rq->client, 503, "Requested service not found"); - return task; - } - // check if the plugin want rawbody or decoded body - metafn = (plugin_header_t * (*)()) dlsym(plugin->handle, "meta"); - if ((error = dlerror()) == NULL) - { - meta = metafn(); - } - // load the function - fn = (void *(*)(void *))dlsym(plugin->handle, PLUGIN_HANDLER); - if ((error = dlerror()) != NULL) - { - ERROR("Problem when finding %s method from %s : %s", PLUGIN_HANDLER, pname, error); - antd_error(rq->client, 503, "Requested service not found"); - return task; - } - // check if we need the raw data or not - if (meta && meta->raw_body == 1) - { - task->handle = fn; - } - else - { - free(task); - task = antd_create_task(decode_post_request, (void *)rq, fn, rq->client->last_io); - } - return task; -} - -dictionary_t mimes_list() -{ - return server_config.mimes; -} - -#ifdef USE_ZLIB -int compressable(char *ctype) -{ - if (!server_config.gzip_enable || server_config.gzip_types == NULL) - return 0; - item_t it; - list_for_each(it, server_config.gzip_types) - { - if (it->type == LIST_TYPE_POINTER && it->value.ptr && regex_match((const char *)it->value.ptr, ctype, 0, NULL)) - { - return 1; - } - } - return 0; -} -#endif diff --git a/http_server.h b/http_server.h deleted file mode 100644 index 948031c..0000000 --- a/http_server.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef HTTP_SERVER -#define HTTP_SERVER - -#include -#include -#include "lib/handle.h" - -#define PLUGIN_HANDLER "handle" -#define WS_MAGIC_STRING "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" -#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 -#ifndef CONFIG_FILE -#define CONFIG_FILE "antd-config.ini" -#endif - -config_t *config(); -void destroy_config(); -void load_config(const char *file); -void *accept_request(void *); -void *finish_request(void *); -void cat(void *, FILE *); -void cannot_execute(void *); -//int get_line(int, char *, int); -void *serve_file(void *); -int startup(unsigned *, int); -int rule_check(const char *, const char *, const char *, const char *, const char *, char *); -void ws_confirm_request(void *, const char *); -char *post_url_decode(void *client, int len); -void decode_url_request(const char *query, dictionary_t); -void *decode_request_header(void *data); -void *decode_request(void *data); -void *decode_post_request(void *data); -void *resolve_request(void *data); -void *decode_multi_part_request(void *, const char *); -void *decode_multi_part_request_data(void *data); -void decode_cookie(const char *, dictionary_t d); -char *post_data_decode(void *, int); -void set_nonblock(int); -void *execute_plugin(void *data, const char *path); - -#endif \ No newline at end of file diff --git a/httpd.c b/httpd.c index ec5f34f..3764dcb 100644 --- a/httpd.c +++ b/httpd.c @@ -9,18 +9,21 @@ #include #include #include +#include #include #include -#include "http_server.h" -#include "lib/ini.h" +#include "server.h" #include "lib/scheduler.h" -#include "plugin_manager.h" #include "lib/utils.h" +#include "config.h" +#include "plugin_manager.h" #define SEND_STAT(fd, buff, ret, ...) \ snprintf(buff, BUFFLEN, ##__VA_ARGS__); \ ret = write(fd, buff, strlen(buff)); +extern config_t g_server_config; + static antd_scheduler_t *scheduler; #ifdef USE_OPENSSL @@ -97,8 +100,7 @@ static void configure_context(SSL_CTX *ctx) SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_SSLv2 | SSL_OP_NO_TICKET); SSL_CTX_set_session_id_context(ctx, (void *)&ssl_session_ctx_id, sizeof(ssl_session_ctx_id)); // set the cipher suit - config_t *cnf = config(); - const char *suit = cnf->ssl_cipher ? cnf->ssl_cipher : CIPHER_SUIT; + const char *suit = g_server_config.ssl_cipher ? g_server_config.ssl_cipher : CIPHER_SUIT; LOG("Cirpher suit used: %s", suit); if (SSL_CTX_set_cipher_list(ctx, suit) != 1) { @@ -109,16 +111,16 @@ static void configure_context(SSL_CTX *ctx) /* Set the key and cert */ /* use the full chain bundle of certificate */ // if (SSL_CTX_use_certificate_file(ctx, server_config->sslcert, SSL_FILETYPE_PEM) <= 0) { - if (SSL_CTX_use_certificate_chain_file(ctx, cnf->sslcert) <= 0) + if (SSL_CTX_use_certificate_chain_file(ctx, g_server_config.sslcert) <= 0) { - ERROR("Fail to read SSL certificate chain file: %s", cnf->sslcert); + ERROR("Fail to read SSL certificate chain file: %s", g_server_config.sslcert); ERR_print_errors_fp(stderr); exit(EXIT_FAILURE); } - if (SSL_CTX_use_PrivateKey_file(ctx, cnf->sslkey, SSL_FILETYPE_PEM) <= 0) + if (SSL_CTX_use_PrivateKey_file(ctx, g_server_config.sslkey, SSL_FILETYPE_PEM) <= 0) { - ERROR("Fail to read SSL private file: %s", cnf->sslkey); + ERROR("Fail to read SSL private file: %s", g_server_config.sslkey); ERR_print_errors_fp(stderr); exit(EXIT_FAILURE); } @@ -169,10 +171,9 @@ static void antd_monitor(port_config_t *pcnf, int sock) { antd_task_t *task = NULL; int client_sock = -1; - struct sockaddr_in client_name; + antd_sockaddr_t client_name; socklen_t client_name_len = sizeof(client_name); - char *client_ip = NULL; - config_t *conf = config(); + char client_ip[INET6_ADDRSTRLEN] = {0}; if (sock > 0) { client_sock = accept(sock, (struct sockaddr *)&client_name, &client_name_len); @@ -194,9 +195,16 @@ static void antd_monitor(port_config_t *pcnf, int sock) /* get the remote IP */ - if (client_name.sin_family == AF_INET) + if ( pcnf->type == ANTD_PROTO_IP_4 && client_name.addr4.sin_family == AF_INET) + { + inet_ntop(AF_INET,&client_name.addr4,client_ip,INET6_ADDRSTRLEN); + } + else if(client_name.addr6.sin6_family == AF_INET6) + { + inet_ntop(AF_INET6,&client_name.addr6,client_ip,INET6_ADDRSTRLEN); + } + if(client_ip[0] != '\0') { - client_ip = inet_ntoa(client_name.sin_addr); LOG("Connect to client IP: %s on port:%d (%d)", client_ip, pcnf->port, client_sock); // ip address dput(request->request, "REMOTE_ADDR", (void *)strdup(client_ip)); @@ -236,7 +244,7 @@ static void antd_monitor(port_config_t *pcnf, int sock) } #endif antd_scheduler_lock(scheduler); - conf->connection++; + g_server_config.connection++; antd_scheduler_unlock(scheduler); // create callback for the server task = antd_create_task(accept_request, (void *)request, finish_request, client->last_io); @@ -307,8 +315,7 @@ void antd_scheduler_destroy_data(void *data) int antd_scheduler_validate_data(antd_task_t *task) { - config_t *conf = config(); - return !(difftime(time(NULL), task->access_time) > conf->scheduler_timeout); + return !(difftime(time(NULL), task->access_time) > g_server_config.scheduler_timeout); } int antd_task_data_id(void *data) @@ -344,10 +351,8 @@ int main(int argc, char *argv[]) signal(SIGPIPE, SIG_IGN); signal(SIGABRT, SIG_IGN); signal(SIGINT, stop_serve); - - config_t *conf = config(); // start syslog - if (conf->debug_enable == 1) + if (g_server_config.debug_enable == 1) { setlogmask(LOG_UPTO(LOG_NOTICE)); } @@ -358,7 +363,7 @@ int main(int argc, char *argv[]) openlog(SERVER_NAME, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_DAEMON); #ifdef USE_OPENSSL - if (conf->enable_ssl == 1) + if (g_server_config.enable_ssl == 1) { init_openssl(); ctx = create_context(); @@ -369,7 +374,7 @@ int main(int argc, char *argv[]) #endif // enable scheduler // default to 4 workers - scheduler = antd_scheduler_init(conf->n_workers, conf->stat_fifo_path); + scheduler = antd_scheduler_init(g_server_config.n_workers, g_server_config.stat_fifo_path); if (scheduler == NULL) { ERROR("Unable to initialise scheduler. Exit"); @@ -377,18 +382,18 @@ int main(int argc, char *argv[]) exit(1); } FD_ZERO(&master_set); - for_each_assoc(it, conf->ports) + for_each_assoc(it, g_server_config.ports) { pcnf = (port_config_t *)it->value; if (pcnf) { if(pcnf->type == ANTD_PROTO_IP_4) { - pcnf->sock = startup(&pcnf->port,0); + pcnf->sock = antd_listen(&pcnf->port,0,g_server_config.backlog); } else { - pcnf->sock = startup(&pcnf->port,1); + pcnf->sock = antd_listen(&pcnf->port,1,g_server_config.backlog); } if (pcnf->sock > 0) { @@ -424,9 +429,9 @@ int main(int argc, char *argv[]) while (antd_scheduler_ok(scheduler)) { - if (conf->connection > conf->maxcon) + if (g_server_config.connection > g_server_config.maxcon) { - // ERROR("Reach max connection %d", conf->connection); + // ERROR("Reach max connection %d", g_server_config.connection); timeout.tv_sec = 0; timeout.tv_usec = 10000; // 5 ms select(0, NULL, NULL, NULL, &timeout); @@ -445,7 +450,7 @@ int main(int argc, char *argv[]) { continue; } - for_each_assoc(it, conf->ports) + for_each_assoc(it, g_server_config.ports) { pcnf = (port_config_t *)it->value; if (pcnf && pcnf->sock > 0 && FD_ISSET(pcnf->sock, &working_set)) diff --git a/lib/dbhelper.c b/lib/dbhelper.c deleted file mode 100644 index 0a235df..0000000 --- a/lib/dbhelper.c +++ /dev/null @@ -1,264 +0,0 @@ -#include -#include - -#include "dbhelper.h" -#include "utils.h" - -sqlite3 * database(const char* file) -{ - sqlite3* db; - int rc = sqlite3_open(file,&db); - if (rc != SQLITE_OK) { - ERROR( "Cannot open database: %s %s",file, sqlite3_errmsg(db)); - sqlite3_close(db); - return NULL; - } - return db; -} -void dbclose(sqlite3* db) -{ - sqlite3_close(db); -} -int dbquery(sqlite3* db,const char* sql, int (*call_back)()) -{ - char *err_msg = 0; - sqlite3_mutex_enter(sqlite3_db_mutex(db)); - int rc = sqlite3_exec(db,sql,call_back,0,&err_msg); - sqlite3_mutex_leave(sqlite3_db_mutex(db)); - if(rc != SQLITE_OK) - { - ERROR("Cannot query : '%s' [%s]", sql,err_msg); - sqlite3_free(err_msg); - return 0; - } - return 1; -} -dbfield __field() -{ - dbfield ret = malloc(sizeof *ret); - ret->name = NULL; - ret->value = NULL; - ret->next = NULL; - return ret; -} -dbrecord __record() -{ - dbrecord ret = malloc(sizeof *ret); - ret->fields = NULL; - ret->idx = 0; - ret->next = NULL; - return ret; -} - -char* __name_list(const dbfield f) -{ - char* sql = f->name; - for(dbfield p=f->next;p!=NULL;p=p->next) - { - sql = __s("%s,%s",sql,p->name); - } - return sql; -} -char* __value_list(const dbfield f) -{ - char* sql = __s("'%s'",f->value); - for(dbfield p=f->next;p!=NULL;p=p->next) - { - sql = __s("%s,'%s'",sql,p->value); - } - return sql; -} -char* __name_value_list(const dbfield f) -{ - char* sql = __s("%s='%s'", f->name,f->value); - for(dbfield p=f->next;p!=NULL;p=p->next) - { - sql = __s("%s,%s='%s'",sql, p->name,f->value); - } - return sql; -} - -void add_record(dbrecord* r,dbfield f) -{ - dbrecord new_r = malloc(sizeof *new_r); - new_r->fields = f; - new_r->idx = 1; - new_r->next = NULL; - if((*r)->idx == 0) - { - freerecord(&(*r)); - *r = new_r; - } - else - { - dbrecord* temp; - for(temp = r;(*temp)->next!=NULL;temp=&((*temp)->next)) - { - (*temp)->idx++; - } - (*temp)->next = new_r; - } - //new_r->next = *r; - //*r = new_r; -} - -void add_field(dbfield* field,const char* name, const char* value) -{ - dbfield new_field = malloc(sizeof *new_field); - new_field->name = strdup(name); - new_field->value = strdup(value); - new_field->next = *field; - *field = new_field; -} -char* value_of(const dbfield f,const char* key) -{ - for(dbfield t = f; t != NULL; t=t->next) - if(strcmp(t->name,key)==0) - return t->value; - return NULL; -} -int dbinsert(sqlite3* db,const char* table, const dbfield fields) -{ - char* sqlprefix = "INSERT INTO %s (%s) VALUES (%s)"; - char* name_list = __name_list(fields); - char* value_list = __value_list(fields); - char* sql = __s(sqlprefix,table,name_list,value_list); - int ret = dbquery(db,sql,NULL); - free(name_list); - free(value_list); - free(sql); - - if(ret == 0) - return -1; - return sqlite3_last_insert_rowid(db); -} -dbrecord dball(sqlite3* db,const char* table) -{ - return dbselect(db,table,"1=%s","1"); -} -dbrecord dbselect(sqlite3* db, const char* table, const char* fname,const char* fstring,...) -{ - char* sql; - char* prefix = "SELECT %s FROM %s WHERE %s"; - char* cond; - va_list arguments; - int dlen; - sqlite3_stmt *statement; - dbrecord records = __record(); - - va_start( arguments, fstring); - dlen = vsnprintf(0,0,fstring,arguments) + 1; - va_end(arguments); - cond = (char*) malloc(dlen*sizeof(char)); - va_start(arguments, fstring); - vsnprintf(cond, dlen, fstring, arguments); - va_end(arguments); - sql = __s(prefix, fname,table,cond); - - if(sqlite3_prepare_v2(db, sql, -1, &statement, 0) == SQLITE_OK) - { - int cols = sqlite3_column_count(statement); - int result = 0; - while((result = sqlite3_step(statement)) == SQLITE_ROW) - { - dbfield fields = __field(); - for(int col = 0; col < cols; col++) - { - const char *value = (const char*)sqlite3_column_text(statement, col); - const char *name = sqlite3_column_name(statement, col); - add_field(&fields,name,(value!=0)?value:""); - } - add_record(&records,fields); - } - sqlite3_finalize(statement); - } - else - { - LOG("Can not select:%s [%s]\n",sql,sqlite3_errmsg(db)); - } - - free(cond); - free(sql); - return records; -} -int hastable(sqlite3* db,const char* table) -{ - //char * prefix = __s("type='table' and name='%s'",table); - dbrecord rc = dbselect(db,"sqlite_master","*","type='table' and name='%s'", table); - //free(prefix); - if(!rc) return 0; - if(!rc->fields) - { - freerecord(&rc); - return 0; - } - freerecord(&rc); - return 1; -} -int dbupdate(sqlite3* db,const char* table,const dbfield field,const char* fstring,...) -{ - char* sql; - char* prefix = "UPDATE %s SET %s WHERE (%s)"; - char* cond; - char* list; - va_list arguments; - int dlen; - va_start( arguments, fstring); - dlen = vsnprintf(0,0,fstring,arguments) + 1; - va_end(arguments); - cond = (char*) malloc(dlen*sizeof(char)); - va_start(arguments, fstring); - vsnprintf(cond, dlen, fstring, arguments); - va_end(arguments); - - list = __name_value_list(field); - sql = __s(prefix,table,list,cond); - int ret = dbquery(db,sql,NULL); - free(cond); - free(list); - free(sql); - return ret; -} - -int dbdelete(sqlite3* db,const char* table,const char* fstring,...) -{ - char* sql; - char* prefix = "DELETE FROM %s WHERE (%s)"; - char* cond; - va_list arguments; - int dlen; - va_start( arguments, fstring); - dlen = vsnprintf(0,0,fstring,arguments) + 1; - va_end(arguments); - cond = (char*) malloc(dlen*sizeof(char)); - va_start(arguments, fstring); - vsnprintf(cond, dlen, fstring, arguments); - va_end(arguments); - sql = __s(prefix,table,cond); - int ret = dbquery(db,sql,NULL); - free(cond); - free(sql); - return ret; -} - -void freerecord(dbrecord * r) -{ - dbrecord curr; - while ((curr = (*r)) != NULL) { - (*r) = (*r)->next; - if(curr->fields) - freefield(&(curr->fields)); - free (curr); - } -} -void freefield(dbfield *f) -{ - dbfield curr; - while( (curr = (*f)) != NULL ) - { - (*f) = (*f)->next; - if(curr->name) free(curr->name); - if(curr->value) free(curr->value); - free(curr); - } -} \ No newline at end of file diff --git a/lib/dbhelper.h b/lib/dbhelper.h deleted file mode 100644 index dedc9a6..0000000 --- a/lib/dbhelper.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef DB_HELPER -#define DB_HELPER -#include - -sqlite3 * database(const char*); -typedef struct _dbfield -{ - char* name; - char* value; - struct _dbfield* next; -} *dbfield ; - -typedef struct _dbrecord -{ - dbfield fields; - int idx; - struct _dbrecord* next; -} * dbrecord; - -int dbquery(sqlite3*,const char*, int (*)()); -int dbinsert(sqlite3*,const char*,const dbfield); -int hastable(sqlite3*,const char*); -int dbupdate(sqlite3*,const char*,const dbfield,const char*,...); -dbrecord dbselect(sqlite3*, const char*, const char*,const char*,...); -dbrecord dball(sqlite3*,const char*); -int dbdelete(sqlite3*,const char*,const char*,...); -dbfield __field(); -dbrecord __record(); -char* __name_list(const dbfield); -char* __value_list(const dbfield); -char* __name_value_list(const dbfield); -char* value_of(const dbfield,const char*); -void add_field(dbfield*,const char*, const char*); -void add_record(dbrecord*,dbfield); -void dbclose(sqlite3*); -void freerecord(dbrecord *); -void freefield(dbfield *); -#endif \ No newline at end of file diff --git a/lib/handle.h b/lib/handle.h index c06af1f..beabfc3 100644 --- a/lib/handle.h +++ b/lib/handle.h @@ -40,25 +40,13 @@ typedef enum ANTD_CNONE } antd_compress_t; + typedef enum { ANTD_PROTO_IP_4, ANTD_PROTO_IP_6, ANTD_PROTO_ALL } antd_proto_t; -//extern config_t server_config; - -typedef struct -{ - unsigned int port; - int usessl; - char *htdocs; - char* plugins; - int sock; - antd_proto_t type; - dictionary_t rules; -} port_config_t; - typedef struct { int sock; @@ -85,36 +73,6 @@ typedef struct } antd_response_header_t; -typedef struct -{ - //int port; - char *plugins_dir; - char *plugins_ext; - char *db_path; - //char* htdocs; - char *tmpdir; - char *stat_fifo_path; - dictionary_t handlers; - int backlog; - int maxcon; - int connection; - int n_workers; - int scheduler_timeout; - int max_upload_size; - // ssl - int enable_ssl; - char *sslcert; - char *sslkey; - char *ssl_cipher; - int gzip_enable; - int debug_enable; - list_t gzip_types; - dictionary_t mimes; - dictionary_t ports; - dictionary_t plugins; - // #endif -} config_t; - typedef struct { char name[MAX_PATH_LEN]; @@ -124,10 +82,9 @@ typedef struct dictionary_t config; int raw_body; int status; + void *instance_data; } plugin_header_t; - - void set_nonblock(int socket); //void set_block(int socket); diff --git a/lib/plugin.h b/lib/plugin.h index 1eb0519..0d63ab9 100644 --- a/lib/plugin.h +++ b/lib/plugin.h @@ -6,19 +6,8 @@ #include "utils.h" #include "handle.h" -#include "dbhelper.h" -//typedef void(*call)(); -#ifdef USE_DB -typedef sqlite3* sqldb; -#endif - - -#ifdef USE_DB -sqldb getdb(); -sqldb __getdb(char *name); -#endif char* config_dir(); /*Default function for plugin*/ @@ -49,24 +38,6 @@ void use_raw_body() { __plugin__.raw_body = 1; } -#ifdef USE_DB -sqldb __getdb(char *name) -{ - int plen = strlen(name)+strlen(__plugin__.dbpath)+4; - char* path = (char*) malloc(plen*sizeof(char)); - strcpy(path,__plugin__.dbpath); - strcat(path,name); - strcat(path,".db"); - //LOG("database: %s\n", path); - sqldb ret = (sqldb)database(path); - free(path); - return ret; -} -sqldb getdb() -{ - return __getdb(__plugin__.name); -} -#endif plugin_header_t* meta() { diff --git a/lib/scheduler.c b/lib/scheduler.c index 28853ec..7a14eae 100644 --- a/lib/scheduler.c +++ b/lib/scheduler.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include "scheduler.h" #include "utils.h" #include "bst.h" diff --git a/lib/sha1.c b/lib/sha1.c index 5271d84..a6a5e22 100644 --- a/lib/sha1.c +++ b/lib/sha1.c @@ -77,6 +77,8 @@ A million repetitions of "a" */ /* #define SHA1HANDSOFF */ +#include +#include #include "sha1.h" void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]); diff --git a/lib/sha1.h b/lib/sha1.h index 88550ed..3361ea3 100644 --- a/lib/sha1.h +++ b/lib/sha1.h @@ -3,8 +3,6 @@ #ifndef __SHA1_H #define __SHA1_H -#include -#include #include diff --git a/lib/utils.c b/lib/utils.c index 278d634..08d6176 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -43,64 +43,64 @@ THE SOFTWARE. #include "sha1.h" #endif - #include "dictionary.h" // #include - /** * Trim a string by a character on both ends * @param str The target string * @param delim the delim */ -void trim(char* str, const char delim) +void trim(char *str, const char delim) { - if(!str || strlen(str) == 0) return; - char * p = str; + if (!str || strlen(str) == 0) + return; + char *p = str; int l = strlen(p); - while(l > 0 && p[l - 1] == delim) + while (l > 0 && p[l - 1] == delim) p[--l] = 0; - while(* p && (* p) == delim ) ++p, --l; + while (*p && (*p) == delim) + ++p, --l; memmove(str, p, l + 1); } -void removeAll(const char* path,int mode) +void removeAll(const char *path, int mode) { - DIR *d; - struct dirent *dir; - char* file; - struct stat st; - if( stat(path, &st) == 0) - { - if(S_ISDIR(st.st_mode)) - { - d = opendir(path); - if(d) - { - while ((dir = readdir(d)) != NULL) - { - if(strcmp(dir->d_name,".") == 0 || strcmp(dir->d_name,"..")==0) continue; - file = __s("%s/%s",path,dir->d_name); + DIR *d; + struct dirent *dir; + char *file; + struct stat st; + if (stat(path, &st) == 0) + { + if (S_ISDIR(st.st_mode)) + { + d = opendir(path); + if (d) + { + while ((dir = readdir(d)) != NULL) + { + if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) + continue; + file = __s("%s/%s", path, dir->d_name); - removeAll(file,1); - free(file); - } - closedir(d); - } - } - if(mode) - remove(path); - } - + removeAll(file, 1); + free(file); + } + closedir(d); + } + } + if (mode) + remove(path); + } } // WARNING: // TODO: remove it, this function is not thread-safe -void timestr(time_t time, char* buf,int len,char* format, int gmt) +void timestr(time_t time, char *buf, int len, char *format, int gmt) { - struct tm t; - if(gmt) + struct tm t; + if (gmt) { gmtime_r(&time, &t); } @@ -108,52 +108,55 @@ void timestr(time_t time, char* buf,int len,char* format, int gmt) { localtime_r(&time, &t); } - strftime(buf, len, format, &t); + strftime(buf, len, format, &t); } -void server_time(char* buf, int len) +void server_time(char *buf, int len) { - timestr(time(NULL), buf, len ,"%a, %d %b %Y %H:%M:%S", 0); + timestr(time(NULL), buf, len, "%a, %d %b %Y %H:%M:%S", 0); } /** * Get extension of a file name * @param file The file name * @return the extension */ -char* ext(const char* file) +char *ext(const char *file) { - char* token,*ltoken = ""; - if(file == NULL) return NULL; - char* str_cpy = strdup(file); - char* str_org = str_cpy; - if(!strstr(str_cpy,".")) + char *token, *ltoken = ""; + if (file == NULL) + return NULL; + char *str_cpy = strdup(file); + char *str_org = str_cpy; + if (!strstr(str_cpy, ".")) { free(str_org); return NULL; - } - if(*file == '.') - trim(str_cpy,'.'); + } + if (*file == '.') + trim(str_cpy, '.'); - while((token = strsep(&str_cpy,".")) && strlen(token)>0) {ltoken = token;} - char* ext = strdup(ltoken); + while ((token = strsep(&str_cpy, ".")) && strlen(token) > 0) + { + ltoken = token; + } + char *ext = strdup(ltoken); free(str_org); - return ext; - + return ext; } /*get mime file info from extension*/ -mime_t mime_from_ext(const char* ex) +mime_t mime_from_ext(const char *ex) { dictionary_t mime_list = mimes_list(); - mime_t ret = (mime_t){"application/octet-stream",NULL}; - if(!mime_list) + mime_t ret = (mime_t){"application/octet-stream", NULL}; + if (!mime_list) return ret; chain_t it; - char * pattern = __s("(^\\s*%s\\s*,)|(\\s*,\\s*%s\\s*,\\s*)|(^\\s*%s\\s*$)|(,\\s*%s\\s*$)", ex, ex, ex, ex); - if(pattern) + char *pattern = __s("(^\\s*%s\\s*,)|(\\s*,\\s*%s\\s*,\\s*)|(^\\s*%s\\s*$)|(,\\s*%s\\s*$)", ex, ex, ex, ex); + if (pattern) { - for_each_assoc(it,mime_list) + for_each_assoc(it, mime_list) { - - if(regex_match(pattern,it->value,0, NULL)) + + if (regex_match(pattern, it->value, 0, NULL)) { ret.type = it->key; ret.ext = it->value; @@ -166,18 +169,17 @@ mime_t mime_from_ext(const char* ex) return ret; } -void verify_header(char* k) +void verify_header(char *k) { k[0] = toupper(k[0]); int len = strlen(k); for (int i = 0; i < len; i++) { - if(k[i] == '-' && i < len-1) + if (k[i] == '-' && i < len - 1) { - k[i+1] = toupper(k[i+1]); + k[i + 1] = toupper(k[i + 1]); } } - } dictionary_t mimes_list() @@ -185,19 +187,19 @@ dictionary_t mimes_list() return NULL; } /*get mime file info from type*/ -mime_t mime_from_type(const char* type) +mime_t mime_from_type(const char *type) { dictionary_t mime_list = mimes_list(); - mime_t ret = (mime_t){NULL,NULL}; - if(!mime_list) + mime_t ret = (mime_t){NULL, NULL}; + if (!mime_list) return ret; chain_t it = dlookup(mime_list, type); - if(it) + if (it) { ret.type = it->key; ret.ext = it->value; } - return ret; + return ret; } /** * Get correct HTTP mime type of a file @@ -206,157 +208,176 @@ mime_t mime_from_type(const char* type) * @param file File name * @return The HTTP Mime Type */ -char* mime(const char* file) +char *mime(const char *file) { - char * ex = ext(file); - if(!ex) return "application/octet-stream"; - mime_t m = mime_from_ext(ex); - if(ex) + char *ex = ext(file); + if (!ex) + return "application/octet-stream"; + mime_t m = mime_from_ext(ex); + if (ex) { free(ex); } - return (char*)m.type; + return (char *)m.type; } -int match_int(const char* search) +int match_int(const char *search) { - return regex_match("^[-+]?[0-9]+$",search,0, NULL); + return regex_match("^[-+]?[0-9]+$", search, 0, NULL); } -int match_float(const char* search) +int match_float(const char *search) { - return regex_match("^[+-]?[0-9]*\\.[0-9]+$",search,0,NULL); + return regex_match("^[+-]?[0-9]*\\.[0-9]+$", search, 0, NULL); } /* -regmatch_t matches[MAX_MATCHES]; -if (regexec(&exp, sz, MAX_MATCHES, matches, 0) == 0) { - memcpy(buff, sz + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so); - printf("group1: %s\n", buff); +regmatch_t matches[MAX_MATCHES]; +if (regexec(&exp, sz, MAX_MATCHES, matches, 0) == 0) { + memcpy(buff, sz + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so); + printf("group1: %s\n", buff); } */ -int regex_match(const char* expr,const char* search, int msize, regmatch_t* matches) +int regex_match(const char *expr, const char *search, int msize, regmatch_t *matches) { - regex_t regex; + regex_t regex; int reti; char msgbuf[100]; int ret; - /* Compile regular expression */ + /* Compile regular expression */ reti = regcomp(®ex, expr, REG_ICASE | REG_EXTENDED); - if( reti ){ - //ERROR("Could not compile regex: %s",expr); + if (reti) + { + // ERROR("Could not compile regex: %s",expr); regerror(reti, ®ex, msgbuf, sizeof(msgbuf)); ERROR("Regex match failed: %s", msgbuf); - //return 0; + // return 0; } - /* Execute regular expression */ + /* Execute regular expression */ reti = regexec(®ex, search, msize, matches, 0); - if( !reti ){ - //LOG("Match"); - ret = 1; + if (!reti) + { + // LOG("Match"); + ret = 1; } - else if( reti == REG_NOMATCH ){ - //LOG("No match"); - ret = 0; + else if (reti == REG_NOMATCH) + { + // LOG("No match"); + ret = 0; } - else{ + else + { regerror(reti, ®ex, msgbuf, sizeof(msgbuf)); - //ERROR("Regex match failed: %s\n", msgbuf); + // ERROR("Regex match failed: %s\n", msgbuf); ret = 0; } - - regfree(®ex); - return ret; + regfree(®ex); + return ret; } -char *url_decode(const char *str) { - if(!str) +char *url_decode(const char *str) +{ + if (!str) { return NULL; } - char *pstr = (char*)str, *buf = malloc(strlen(str) + 1), *pbuf = buf; + char *pstr = (char *)str, *buf = malloc(strlen(str) + 1), *pbuf = buf; - while (*pstr) { - if (*pstr == '%') { - if (pstr[1] && pstr[2]) { - *pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]); - pstr += 2; - } - } else if (*pstr == '+') { - *pbuf++ = ' '; - } else { - *pbuf++ = *pstr; - } - pstr++; - } - *pbuf = '\0'; + while (*pstr) + { + if (*pstr == '%') + { + if (pstr[1] && pstr[2]) + { + *pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]); + pstr += 2; + } + } + else if (*pstr == '+') + { + *pbuf++ = ' '; + } + else + { + *pbuf++ = *pstr; + } + pstr++; + } + *pbuf = '\0'; - return buf; + return buf; } -char *url_encode(const char *str) { - char *pstr = (char*)str, *buf = malloc(strlen(str) * 3 + 1), *pbuf = buf; - while (*pstr) { - if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~') - *pbuf++ = *pstr; - else if (*pstr == ' ') - *pbuf++ = '+'; - else - *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15); - pstr++; - } - *pbuf = '\0'; - return buf; -} - -char from_hex(char ch) { - return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10; -} - -char to_hex(char code) { - static char hex[] = "0123456789abcdef"; - return hex[code & 15]; -} -unsigned hash(const char* key, int hash_size) +char *url_encode(const char *str) { - unsigned hashval = simple_hash(key); - return hashval % hash_size; + char *pstr = (char *)str, *buf = malloc(strlen(str) * 3 + 1), *pbuf = buf; + while (*pstr) + { + if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~') + *pbuf++ = *pstr; + else if (*pstr == ' ') + *pbuf++ = '+'; + else + *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15); + pstr++; + } + *pbuf = '\0'; + return buf; } -unsigned simple_hash(const char* key) + +char from_hex(char ch) { - unsigned hashval; + return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10; +} + +char to_hex(char code) +{ + static char hex[] = "0123456789abcdef"; + return hex[code & 15]; +} +unsigned hash(const char *key, int hash_size) +{ + unsigned hashval = simple_hash(key); + return hashval % hash_size; +} +unsigned simple_hash(const char *key) +{ + unsigned hashval; for (hashval = 0; *key != '\0'; key++) - hashval = *key + 31 * hashval; - return hashval; + hashval = *key + 31 * hashval; + return hashval; } -int _exist(const char* f) +int _exist(const char *f) { - struct stat st; - return !(stat(f, &st) == -1); + struct stat st; + return !(stat(f, &st) == -1); } -int is_file(const char* f) +int is_file(const char *f) { - int st = is_dir(f); - if(st == -1) return -1; - else return st==0; + int st = is_dir(f); + if (st == -1) + return -1; + else + return st == 0; } -int is_dir(const char* f) +int is_dir(const char *f) { - struct stat st; - if(stat(f, &st) == -1) - return -1; // unknow - else if(st.st_mode & S_IFDIR) - return 1; - else - return 0; + struct stat st; + if (stat(f, &st) == -1) + return -1; // unknow + else if (st.st_mode & S_IFDIR) + return 1; + else + return 0; } // These vars will contain the hash - -void md5(uint8_t *initial_msg, size_t initial_len, char* buff) { - uint32_t h0, h1, h2, h3; - char tmp[80]; + +void md5(uint8_t *initial_msg, size_t initial_len, char *buff) +{ + uint32_t h0, h1, h2, h3; + char tmp[80]; uint8_t *msg = NULL; uint32_t r[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, - 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21}; // Use binary integer part of the sines of integers (in radians) as constants// Initialize variables: @@ -377,104 +398,111 @@ void md5(uint8_t *initial_msg, size_t initial_len, char* buff) { 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391}; - + h0 = 0x67452301; h1 = 0xefcdab89; h2 = 0x98badcfe; h3 = 0x10325476; - + // Pre-processing: adding a single 1 bit - //append "1" bit to message + // append "1" bit to message /* Notice: the input bytes are considered as bits strings, where the first bit is the most significant bit of the byte.[37] */ - + // Pre-processing: padding with zeros - //append "0" bit until message length in bit ≡ 448 (mod 512) - //append length mod (2 pow 64) to message - + // append "0" bit until message length in bit ≡ 448 (mod 512) + // append length mod (2 pow 64) to message + int new_len; - for(new_len = initial_len*8 + 1; new_len%512!=448; new_len++); + for (new_len = initial_len * 8 + 1; new_len % 512 != 448; new_len++) + ; new_len /= 8; - - msg = calloc(new_len + 64, 1); // also appends "0" bits + + msg = calloc(new_len + 64, 1); // also appends "0" bits // (we alloc also 64 extra bytes...) memcpy(msg, initial_msg, initial_len); msg[initial_len] = 128; // write the "1" bit - - uint32_t bits_len = 8*initial_len; // note, we append the len - memcpy(msg + new_len, &bits_len, 4); // in bits at the end of the buffer - + + uint32_t bits_len = 8 * initial_len; // note, we append the len + memcpy(msg + new_len, &bits_len, 4); // in bits at the end of the buffer + // Process the message in successive 512-bit chunks: - //for each 512-bit chunk of message: + // for each 512-bit chunk of message: int offset; - for(offset=0; offset BUFFLEN) + if (strlen(path) > BUFFLEN) { ERROR("mkdirp: Path is too long %s", path); return -1; @@ -516,16 +544,17 @@ int mkdirp(const char* path, mode_t mode) char *p = NULL; size_t len; int stat; - snprintf(tmp, sizeof(tmp),"%s",path); + snprintf(tmp, sizeof(tmp), "%s", path); len = strlen(tmp); - if(tmp[len - 1] == '/') - tmp[len - 1] = 0; - for(p = tmp + 1; *p; p++) + if (tmp[len - 1] == '/') + tmp[len - 1] = 0; + for (p = tmp + 1; *p; p++) { - if(*p == '/') { + if (*p == '/') + { *p = 0; stat = mkdir(tmp, mode); - if(stat == -1 && errno != EEXIST) + if (stat == -1 && errno != EEXIST) return stat; *p = '/'; } @@ -533,22 +562,21 @@ int mkdirp(const char* path, mode_t mode) return mkdir(path, mode); } - -int guard_read(int fd, void* buffer, size_t size) +int guard_read(int fd, void *buffer, size_t size) { int n = 0; int read_len; int st; - while(n != (int)size) + while (n != (int)size) { read_len = (int)size - n; - st = read(fd,buffer + n,read_len); - if(st == -1) + st = read(fd, buffer + n, read_len); + if (st == -1) { - ERROR( "Unable to read from #%d: %s", fd, strerror(errno)); + ERROR("Unable to read from #%d: %s", fd, strerror(errno)); return -1; } - if(st == 0) + if (st == 0) { ERROR("Endpoint %d is closed", fd); return -1; @@ -558,21 +586,21 @@ int guard_read(int fd, void* buffer, size_t size) return n; } -int guard_write(int fd, void* buffer, size_t size) +int guard_write(int fd, void *buffer, size_t size) { int n = 0; int write_len; int st; - while(n != (int)size) + while (n != (int)size) { write_len = (int)size - n; - st = write(fd,buffer + n,write_len); - if(st == -1) + st = write(fd, buffer + n, write_len); + if (st == -1) { ERROR("Unable to write to #%d: %s", fd, strerror(errno)); return -1; } - if(st == 0) + if (st == 0) { ERROR("Endpoint %d is closed", fd); return -1; @@ -585,63 +613,130 @@ int guard_write(int fd, void* buffer, size_t size) /* send a request */ -int request_socket(const char *ip, int port) +int antd_request_socket(const char *ip, int port) { - int sockfd; - struct sockaddr_in dest; - if(!ip) + int sockfd; + struct sockaddr_in dest; + if (!ip) { ERROR("Invalid IP address"); return -1; } - if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) - { - ERROR("Socket: %s", strerror(errno)); - return -1; - } - /*struct linger lingerStruct; + if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + { + ERROR("Socket: %s", strerror(errno)); + return -1; + } + /*struct linger lingerStruct; lingerStruct.l_onoff = 0; // turn lingering off for sockets setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &lingerStruct, sizeof(lingerStruct));*/ - bzero(&dest, sizeof(dest)); - dest.sin_family = AF_INET; - dest.sin_port = htons(port); - if (inet_aton(ip, &dest.sin_addr) == 0) - { - perror(ip); - close(sockfd); - return -1; - } - if (connect(sockfd, (struct sockaddr *)&dest, sizeof(dest)) != 0) - { - close(sockfd); - ERROR("Connect:%s", strerror(errno)); - return -1; - } - return sockfd; + bzero(&dest, sizeof(dest)); + dest.sin_family = AF_INET; + dest.sin_port = htons(port); + if (inet_aton(ip, &dest.sin_addr) == 0) + { + ERROR("[%s] inet_aton: %s", ip, strerror(errno)); + close(sockfd); + return -1; + } + if (connect(sockfd, (struct sockaddr *)&dest, sizeof(dest)) != 0) + { + close(sockfd); + ERROR("Connect:%s", strerror(errno)); + return -1; + } + return sockfd; } -char* ip_from_hostname(const char *hostname) +char *ip_from_hostname(const char *hostname) { - struct hostent *he; - struct in_addr **addr_list; - int i; - if(!hostname) + struct hostent *he; + struct in_addr **addr_list; + int i; + if (!hostname) { return NULL; } - if ((he = gethostbyname(hostname)) == NULL) - { - // get the host info - ERROR("gethostbyname:%s", strerror(errno)); - return NULL; - } - addr_list = (struct in_addr **)he->h_addr_list; + if ((he = gethostbyname(hostname)) == NULL) + { + // get the host info + ERROR("gethostbyname:%s", strerror(errno)); + return NULL; + } + addr_list = (struct in_addr **)he->h_addr_list; - for (i = 0; addr_list[i] != NULL; i++) - { - //Return the first one; - return inet_ntoa(*addr_list[i]); - } - return NULL; -} \ No newline at end of file + for (i = 0; addr_list[i] != NULL; i++) + { + // Return the first one; + return inet_ntoa(*addr_list[i]); + } + return NULL; +} + +int antd_listen(unsigned *port, int ipv6, int backlog) +{ + int httpd = 0; + antd_sockaddr_t name; + memset(&name, 0, sizeof(name)); + struct sockaddr *ptr; + // TODO: allow to set listen address + if (ipv6) + { + name.addr6.sin6_port = htons(*port); + name.addr6.sin6_family = AF_INET6; + name.addr6.sin6_addr = in6addr_any; + httpd = socket(AF_INET6, SOCK_STREAM, 0); + ptr = (struct sockaddr *)&name.addr6; + } + else + { + name.addr4.sin_port = htons(*port); + name.addr4.sin_family = AF_INET; + name.addr4.sin_addr.s_addr = htonl(INADDR_ANY); + httpd = socket(AF_INET, SOCK_STREAM, 0); + ptr = (struct sockaddr *)&name.addr4; + } + + if (httpd == -1) + { + ERROR("Port %d - socket: %s", *port, strerror(errno)); + return -1; + } + + if (setsockopt(httpd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) == -1) + { + ERROR("Unable to set reuse address on port %d - setsockopt: %s", *port, strerror(errno)); + } + + if (bind(httpd, ptr, sizeof(name)) < 0) + { + ERROR("Port %d -bind: %s", *port, strerror(errno)); + return -1; + } + if (*port == 0) /* if dynamically allocating a port */ + { + socklen_t namelen = sizeof(name); + if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1) + { + ERROR("Port %d - getsockname: %s", *port, strerror(errno)); + return -1; + } + if (ipv6) + { + *port = ntohs(name.addr6.sin6_port); + } + else + { + *port = ntohs(name.addr4.sin_port); + } + } + + if (listen(httpd, backlog) < 0) + { + ERROR("Port %d - listen: %s", *port, strerror(errno)); + return -1; + } + LOG("%s Listen on port %d", ipv6 ? "IPv6" : "IPv4", *port); + return (httpd); +} diff --git a/lib/utils.h b/lib/utils.h index 9411863..3c36a8a 100644 --- a/lib/utils.h +++ b/lib/utils.h @@ -30,6 +30,7 @@ THE SOFTWARE. #include #include #include +#include #include "dictionary.h" @@ -58,6 +59,12 @@ typedef struct{ const char* ext; } mime_t; +typedef union +{ + struct sockaddr_in6 addr6; + struct sockaddr_in addr4; +} antd_sockaddr_t; + dictionary_t __attribute__((weak)) mimes_list(); char* __s(const char*,...); void trim(char*,const char); @@ -87,6 +94,8 @@ void digest_to_hex(const uint8_t *, char *); void verify_header(char* k); int guard_read(int fd, void* buffer, size_t size); int guard_write(int fd, void* buffer, size_t size); -int request_socket(const char *ip, int port); char* ip_from_hostname(const char *hostname); + +int antd_request_socket(const char *ip, int port); +int antd_listen(unsigned *port, int ipv6, int backlog); #endif diff --git a/lib/ws.c b/lib/ws.c index 2338628..f6ebe83 100644 --- a/lib/ws.c +++ b/lib/ws.c @@ -15,6 +15,13 @@ #include "handle.h" #include "ws.h" +#define CONN_TIME_OUT_S 3 +#define BITV(v, i) ((v & (1 << i)) >> i) +#define MAX_BUFF 1024 +#define PREFERRED_WS_CIPHERS "HIGH:!aNULL:!kRSA:!SRP:!PSK:!CAMELLIA:!RC4:!MD5:!DSS" +#define CLIENT_RQ "GET /%s HTTP/1.1\r\nHost: %s\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\n\r\n" +#define SERVER_WS_KEY "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=" + static void ws_gen_mask_key(ws_msg_header_t *header) { int r = rand(); @@ -24,10 +31,10 @@ static void ws_gen_mask_key(ws_msg_header_t *header) header->mask_key[3] = r & 0xFF; } /** -* Read a frame header -* based on this header, we'll decide -* the appropriate handle for frame data -*/ + * Read a frame header + * based on this header, we'll decide + * the appropriate handle for frame data + */ ws_msg_header_t *ws_read_header(void *client) { @@ -41,8 +48,8 @@ ws_msg_header_t *ws_read_header(void *client) if (BITV(byte, 6) || BITV(byte, 5) || BITV(byte, 4)) goto fail; // all RSV bit must be 0 - //printf("FIN: %d, RSV1: %d, RSV2: %d, RSV3:%d, opcode:%d\n", BITV(byte,7), BITV(byte,6), BITV(byte,5), BITV(byte,4),(byte & 0x0F) ); - // find and opcode + // printf("FIN: %d, RSV1: %d, RSV2: %d, RSV3:%d, opcode:%d\n", BITV(byte,7), BITV(byte,6), BITV(byte,5), BITV(byte,4),(byte & 0x0F) ); + // find and opcode header->fin = BITV(byte, 7); header->opcode = (byte & 0x0F); @@ -50,8 +57,8 @@ ws_msg_header_t *ws_read_header(void *client) if (antd_recv(client, &byte, sizeof(byte)) <= 0) goto fail; - //printf("MASK: %d paylen:%d\n", BITV(byte,7), (byte & 0x7F)); - // check mask bit, should be 1 + // printf("MASK: %d paylen:%d\n", BITV(byte,7), (byte & 0x7F)); + // check mask bit, should be 1 header->mask = BITV(byte, 7); /*if(!BITV(byte,7)) { @@ -73,28 +80,28 @@ ws_msg_header_t *ws_read_header(void *client) } else { - //read only last 4 byte + // read only last 4 byte if (antd_recv(client, bytes, 8 * sizeof(uint8_t)) <= 0) goto fail; header->plen = (bytes[4] << 24) + (bytes[5] << 16) + (bytes[6] << 8) + bytes[7]; } - //printf("len: %d\n", header->plen); - // last step is to get the maskey + // printf("len: %d\n", header->plen); + // last step is to get the maskey if (header->mask) if (antd_recv(client, header->mask_key, 4 * sizeof(uint8_t)) <= 0) goto fail; - //printf("key 0: %d key 1: %d key2:%d, key3: %d\n",header->mask_key[0],header->mask_key[1],header->mask_key[2], header->mask_key[3] ); + // printf("key 0: %d key 1: %d key2:%d, key3: %d\n",header->mask_key[0],header->mask_key[1],header->mask_key[2], header->mask_key[3] ); // check wheather it is a ping or a close message // process it and return NULL - //otherwise return the header - //return the header + // otherwise return the header + // return the header switch (header->opcode) { case WS_CLOSE: // client requests to close the connection // send back a close message UNUSED(ws_send_close(client, 1000, header->mask ? 0 : 1)); - //goto fail; + // goto fail; break; case WS_PING: // client send a ping @@ -112,9 +119,9 @@ fail: return NULL; } /** -* Read data from client -* and unmask data using the key -*/ + * Read data from client + * and unmask data using the key + */ int ws_read_data(void *client, ws_msg_header_t *header, int len, uint8_t *data) { // if len == -1 ==> read all remaining data to 'data'; @@ -137,14 +144,14 @@ int _send_header(void *client, ws_msg_header_t header) uint8_t bytes[8]; for (int i = 0; i < 8; i++) bytes[i] = 0; - //first byte |FIN|000|opcode| + // first byte |FIN|000|opcode| byte = (header.fin << 7) + header.opcode; - //printf("BYTE: %d\n", byte); + // printf("BYTE: %d\n", byte); if (antd_send(client, &byte, 1) != 1) return -1; // second byte, payload length // mask may be 0 or 1 - //if(header.mask == 1) + // if(header.mask == 1) // printf("Data is masked\n"); if (header.plen <= 125) { @@ -183,8 +190,8 @@ int _send_header(void *client, ws_msg_header_t header) return 0; } /** -* Send a frame to client -*/ + * Send a frame to client + */ int ws_send_frame(void *client, uint8_t *data, ws_msg_header_t header) { uint8_t *masked; @@ -212,8 +219,8 @@ int ws_send_frame(void *client, uint8_t *data, ws_msg_header_t header) return 0; } /** -* send a text data frame to client -*/ + * send a text data frame to client + */ int ws_send_text(void *client, const char *data, int mask) { ws_msg_header_t header; @@ -222,13 +229,13 @@ int ws_send_text(void *client, const char *data, int mask) header.mask = mask; header.plen = strlen(data); //_send_header(client,header); - //send(client, data, header.plen,0); + // send(client, data, header.plen,0); return ws_send_frame(client, (uint8_t *)data, header); } /** -* send a single binary data fram to client -* not tested yet, but should work -*/ + * send a single binary data fram to client + * not tested yet, but should work + */ int ws_send_binary(void *client, uint8_t *data, int l, int mask) { ws_msg_header_t header; @@ -238,11 +245,11 @@ int ws_send_binary(void *client, uint8_t *data, int l, int mask) header.mask = mask; return ws_send_frame(client, data, header); //_send_header(client,header); - //send(client, data, header.plen,0); + // send(client, data, header.plen,0); } /* -* send a file as binary data -*/ + * send a file as binary data + */ int ws_send_file(void *client, const char *file, int mask) { uint8_t buff[1024]; @@ -257,7 +264,7 @@ int ws_send_file(void *client, const char *file, int mask) size_t size; int first_frame = 1; int ret = 0; - //ws_send_frame(client,buff,header); + // ws_send_frame(client,buff,header); header.mask = mask; while (!feof(ptr)) { @@ -275,7 +282,7 @@ int ws_send_file(void *client, const char *file, int mask) else header.opcode = 0; header.plen = size; - //printf("FIN: %d OC:%d\n", header.fin, header.opcode); + // printf("FIN: %d OC:%d\n", header.fin, header.opcode); ret += ws_send_frame(client, buff, header); } fclose(ptr); @@ -286,9 +293,9 @@ int ws_send_file(void *client, const char *file, int mask) return 0; } /** -* Not tested yet -* but should work -*/ + * Not tested yet + * but should work + */ int ws_pong(void *client, ws_msg_header_t *oheader, int mask) { ws_msg_header_t pheader; @@ -310,7 +317,7 @@ int ws_pong(void *client, ws_msg_header_t *oheader, int mask) ret = ws_send_frame(client, data, pheader); free(data); //_send_header(client, pheader); - //send(client, data, len, 0); + // send(client, data, len, 0); return ret; } int ws_ping(void *client, const char *echo, int mask) @@ -323,11 +330,11 @@ int ws_ping(void *client, const char *echo, int mask) return ws_send_frame(client, (uint8_t *)echo, pheader); } /* -* Not tested yet, but should work -*/ + * Not tested yet, but should work + */ int ws_send_close(void *client, unsigned int status, int mask) { - //printf("CLOSED\n"); + // printf("CLOSED\n"); ws_msg_header_t header; header.fin = 1; header.opcode = WS_CLOSE; @@ -345,7 +352,7 @@ int ws_send_close(void *client, unsigned int status, int mask) }*/ return ws_send_frame(client, bytes, header); //_send_header(client, header); - //send(client,bytes,2,0); + // send(client,bytes,2,0); } void ws_client_close(ws_client_t *wsclient) @@ -368,13 +375,13 @@ void ws_client_close(ws_client_t *wsclient) #endif } -//this is for the client side, not use for now -int ws_client_connect(ws_client_t *wsclient, port_config_t pcnf) +// this is for the client side, not use for now +int ws_client_connect(ws_client_t *wsclient, ws_port_config_t pcnf) { - char* ip = ip_from_hostname(wsclient->host); + char *ip = ip_from_hostname(wsclient->host); if (ip == NULL) return -1; - int sock = request_socket(ip, pcnf.port); + int sock = antd_request_socket(ip, pcnf.port); if (sock <= 0) { ERROR("Cannot request socket"); @@ -383,13 +390,13 @@ int ws_client_connect(ws_client_t *wsclient, port_config_t pcnf) // time out setting struct timeval timeout; timeout.tv_sec = CONN_TIME_OUT_S; - timeout.tv_usec = 0; //3 s - if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) < 0) + timeout.tv_usec = 0; // 3 s + if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) < 0) ERROR("setsockopt failed:%s", strerror(errno)); if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) < 0) ERROR("setsockopt failed:%s", strerror(errno)); - + // will be free wsclient->antdsock->sock = sock; wsclient->antdsock->z_status = 0; @@ -427,7 +434,7 @@ int ws_client_connect(ws_client_t *wsclient, port_config_t pcnf) SSL_CTX_set_options(wsclient->ssl_ctx, SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_SSLv2 | SSL_OP_NO_TICKET); // set the cipher suit const char *suit = wsclient->ciphersuit ? wsclient->ciphersuit : PREFERRED_WS_CIPHERS; - //const char* suit = "AES128-SHA"; + // const char* suit = "AES128-SHA"; if (SSL_CTX_set_cipher_list(wsclient->ssl_ctx, suit) != 1) { ssl_err = ERR_get_error(); @@ -514,7 +521,7 @@ int ws_open_handshake(ws_client_t *client) char buf[MAX_BUFF]; // now send ws request handshake sprintf(buf, CLIENT_RQ, client->resource, client->host); - //printf("Send %s\n", buf); + // printf("Send %s\n", buf); int size = antd_send(client->antdsock, buf, strlen(buf)); if (size != (int)strlen(buf)) { @@ -538,7 +545,7 @@ int ws_open_handshake(ws_client_t *client) trim(token, '\r'); if (strcasecmp(token, SERVER_WS_KEY) == 0) { - //LOG("Handshake sucessfull\n"); + // LOG("Handshake sucessfull\n"); done = 1; } else @@ -547,30 +554,10 @@ int ws_open_handshake(ws_client_t *client) return -1; } } - //if(line) free(line); + // if(line) free(line); size = read_buf(client->antdsock, buf, MAX_BUFF); } if (done) return 0; return -1; } -char *get_ip_address() -{ - struct ifaddrs *addrs; - getifaddrs(&addrs); - struct ifaddrs *tmp = addrs; - char *ip; - while (tmp) - { - if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET) - { - struct sockaddr_in *pAddr = (struct sockaddr_in *)tmp->ifa_addr; - ip = inet_ntoa(pAddr->sin_addr); - if (strcmp(ip, "127.0.0.1") != 0) - return ip; - } - tmp = tmp->ifa_next; - } - freeifaddrs(addrs); - return "127.0.0.1"; -} \ No newline at end of file diff --git a/lib/ws.h b/lib/ws.h index 7fff10f..b2870d8 100644 --- a/lib/ws.h +++ b/lib/ws.h @@ -3,10 +3,6 @@ #include -#include "handle.h" - -#define CONN_TIME_OUT_S 3 -#define BITV(v, i) ((v & (1 << i)) >> i) #define WS_TEXT 0x1 #define WS_BIN 0x2 #define WS_CLOSE 0x8 @@ -16,11 +12,14 @@ #define ws_b(c, d, z) (ws_send_binary(c, d, z, 0)) #define ws_f(c, f) (ws_send_file(c, f, 0)) #define ws_close(c, r) (ws_send_close(c, r, 0)) -#define MAX_BUFF 1024 -#define PREFERRED_WS_CIPHERS "HIGH:!aNULL:!kRSA:!SRP:!PSK:!CAMELLIA:!RC4:!MD5:!DSS" -#define CLIENT_RQ "GET /%s HTTP/1.1\r\nHost: %s\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\n\r\n" -#define SERVER_WS_KEY "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=" +typedef struct +{ + unsigned int port; + int usessl; + int sock; + antd_proto_t type; +} ws_port_config_t; typedef struct { @@ -57,12 +56,10 @@ int ws_send_file(void *client, const char *file, int mask); int ws_send_binary(void *client, uint8_t *data, int l, int mask); int ws_read_data(void *, ws_msg_header_t *, int, uint8_t *); -//int ws_open_hand_shake(const char* host, int port, const char* resource); -char *get_ip_address(); // client void ws_client_close(ws_client_t *wsclient); -int ws_client_connect(ws_client_t *wsclient, port_config_t pcnf); +int ws_client_connect(ws_client_t *wsclient, ws_port_config_t pcnf); int ws_open_handshake(ws_client_t *client); #endif \ No newline at end of file diff --git a/plugin_manager.c b/plugin_manager.c index cc1ddbf..9b762e9 100644 --- a/plugin_manager.c +++ b/plugin_manager.c @@ -4,10 +4,14 @@ #include #include #include +#include #include "plugin_manager.h" #include "lib/utils.h" #include "lib/handle.h" -#include "http_server.h" +#include "config.h" + +extern config_t g_server_config; + static void unload_plugin_by_name(const char *); static void *plugin_from_file(char *name, char *path, dictionary_t conf); @@ -25,7 +29,7 @@ struct plugin_entry *plugin_lookup(char *s) { struct plugin_entry *np; for (np = plugin_table[hash(s, HASHSIZE)]; np != NULL; np = np->next) - if (strcmp(s, np->pname) == 0) + if (strcmp(s, np->name) == 0) return np; /* found */ return NULL; /* not found */ } @@ -45,7 +49,6 @@ struct plugin_entry *plugin_load(char *name, dictionary_t pconf) unsigned hashval; plugin_header_t *(*metafn)(); plugin_header_t *meta = NULL; - config_t *sconf = config(); int fromfd, tofd; char *error; struct stat st; @@ -65,11 +68,11 @@ struct plugin_entry *plugin_load(char *name, dictionary_t pconf) return NULL; } - (void)snprintf(path, sizeof(path), "%s/%s%s", sconf->plugins_dir, name, sconf->plugins_ext); + (void)snprintf(path, sizeof(path), "%s/%s%s", g_server_config.plugins_dir, name, g_server_config.plugins_ext); if (pname && strcmp(name, pname) != 0) { // copy plugin file to tmpdir - (void)snprintf(path, sizeof(path), "%s/%s%s", sconf->plugins_dir, pname, sconf->plugins_ext); + (void)snprintf(path, sizeof(path), "%s/%s%s", g_server_config.plugins_dir, pname, g_server_config.plugins_ext); LOG("Original plugin file: %s", path); if ((fromfd = open(path, O_RDONLY)) < 0) { @@ -82,7 +85,7 @@ struct plugin_entry *plugin_load(char *name, dictionary_t pconf) ERROR("Unable to get file stat %s: %s", path, strerror(errno)); return NULL; } - (void)snprintf(path, sizeof(path), "%s/%s%s", sconf->tmpdir, name, sconf->plugins_ext); + (void)snprintf(path, sizeof(path), "%s/%s%s", g_server_config.tmpdir, name, g_server_config.plugins_ext); LOG("TMP plugin file: %s", path); if ((tofd = open(path, O_WRONLY | O_CREAT, 0600)) < 0) { @@ -100,16 +103,17 @@ struct plugin_entry *plugin_load(char *name, dictionary_t pconf) is_tmp = 1; } - np->pname = strdup(name); + np->name = strdup(name); np->handle = plugin_from_file(name, path, pconf); if (is_tmp) { + //TODO change this (void)remove(path); } if (np->handle == NULL) { - if (np->pname) - free(np->pname); + if (np->name) + free(np->name); if (np) free(np); return NULL; @@ -152,7 +156,6 @@ static void *plugin_from_file(char *name, char *path, dictionary_t conf) { void *lib_handle; char *error; - config_t *cnf = config(); void (*fn)(plugin_header_t *, dictionary_t); lib_handle = dlopen(path, RTLD_LAZY | RTLD_LOCAL /*| RTLD_NODELETE*/); if (!lib_handle) @@ -160,7 +163,6 @@ static void *plugin_from_file(char *name, char *path, dictionary_t conf) ERROR("Cannot load plugin '%s' : '%s'", name, dlerror()); return NULL; } - // set database path fn = (void (*)(plugin_header_t *, dictionary_t))dlsym(lib_handle, "__init_plugin__"); if ((error = dlerror()) != NULL) ERROR("Problem when finding plugin init function for %s : %s", name, error); @@ -168,9 +170,9 @@ static void *plugin_from_file(char *name, char *path, dictionary_t conf) { plugin_header_t header; strncpy(header.name, name, MAX_PATH_LEN - 1); - strncpy(header.dbpath, cnf->db_path, MAX_PATH_LEN - 1); - strncpy(header.tmpdir, cnf->tmpdir, MAX_PATH_LEN - 1); - strncpy(header.pdir, cnf->plugins_dir, MAX_PATH_LEN - 1); + strncpy(header.dbpath, g_server_config.db_path, MAX_PATH_LEN - 1); + strncpy(header.tmpdir, g_server_config.tmpdir, MAX_PATH_LEN - 1); + strncpy(header.pdir, g_server_config.plugins_dir, MAX_PATH_LEN - 1); header.config = conf; header.raw_body = 0; header.status = ANTD_PLUGIN_INIT; @@ -188,17 +190,17 @@ void unload_plugin(struct plugin_entry *np) fn = (void (*)())dlsym(np->handle, "__release__"); if ((error = dlerror()) != NULL) { - ERROR("Cant not release plugin %s : %s", np->pname, error); + ERROR("Cant not release plugin %s : %s", np->name, error); } if (fn) { (*fn)(); } dlclose(np->handle); - LOG("Unloaded %s", np->pname); + LOG("Unloaded %s", np->name); // free((void *) np->handle); - if (np->pname) - free((void *)np->pname); + if (np->name) + free((void *)np->name); } /* Unload a plugin by its name @@ -208,7 +210,7 @@ void unload_plugin_by_name(const char *name) struct plugin_entry *np; int hasval = hash(name, HASHSIZE); np = plugin_table[hasval]; - if (strcmp(np->pname, name) == 0) + if (strcmp(np->name, name) == 0) { unload_plugin(np); plugin_table[hasval] = np->next; @@ -217,7 +219,7 @@ void unload_plugin_by_name(const char *name) { for (np = plugin_table[hasval]; np != NULL; np = np->next) { - if (np->next != NULL && strcmp(name, np->next->pname) == 0) + if (np->next != NULL && strcmp(name, np->next->name) == 0) { break; } diff --git a/plugin_manager.h b/plugin_manager.h index 72447c1..d3384b7 100644 --- a/plugin_manager.h +++ b/plugin_manager.h @@ -3,14 +3,17 @@ #include "lib/dictionary.h" +#define PLUGIN_HANDLER "handle" + struct plugin_entry { struct plugin_entry *next; - char *pname; + char *name; void *handle; + dictionary_t instances; }; -/* lookup: look for s in hashtab */ +/* lookup: look for s in hashtable */ struct plugin_entry *plugin_lookup(char *s); -/* install: put (name, defn) in hashtab */ +/* install: put (name, defn) in hashtable */ struct plugin_entry *plugin_load(char *name, dictionary_t config); void unload_all_plugin(); void unload_plugin(struct plugin_entry*); diff --git a/server.c b/server.c new file mode 100644 index 0000000..33c0be1 --- /dev/null +++ b/server.c @@ -0,0 +1,618 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +//#include +//#include + +#ifdef USE_OPENSSL +#include +#include +#endif + +#include "server.h" +#include "lib/handle.h" +#include "plugin_manager.h" +#include "lib/scheduler.h" +#include "lib/utils.h" +#include "decode.h" +#include "config.h" + + +static pthread_mutex_t server_mux = PTHREAD_MUTEX_INITIALIZER; +config_t g_server_config; + +void *execute_plugin(void *data, const char *pname); +void *serve_file(void *data); + +void *accept_request(void *data) +{ + char buf[BUFFLEN]; + char *token = NULL; + char *line = NULL; + antd_task_t *task; + antd_request_t *rq = (antd_request_t *)data; + + task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io); + antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE); + // first verify if the socket is ready + antd_client_t *client = (antd_client_t *)rq->client; + + struct pollfd pfd[1]; + + pfd[0].fd = client->sock; + pfd[0].events = POLLIN | POLLOUT; + + int sel = poll(pfd, 1, POLL_EVENT_TO); + if (sel == -1) + { + antd_error(rq->client, 400, "Bad request"); + return task; + } + if (pfd[0].revents & POLLERR || pfd[0].revents & POLLHUP) + { + antd_error(rq->client, 400, "Bad request"); + return task; + } + if (sel == 0 || (!(pfd[0].revents & POLLIN) && !(pfd[0].revents & POLLOUT))) + { + task->handle = accept_request; + return task; + } + // perform the ssl handshake if enabled +#ifdef USE_OPENSSL + int ret = -1, stat; + if (client->ssl && client->state == ANTD_CLIENT_ACCEPT) + { + // LOG("Atttempt %d\n", client->attempt); + if (SSL_accept((SSL *)client->ssl) == -1) + { + stat = SSL_get_error((SSL *)client->ssl, ret); + switch (stat) + { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_NONE: + task->handle = accept_request; + return task; + default: + ERROR("Error performing SSL handshake %d %d %s", stat, ret, ERR_error_string(ERR_get_error(), NULL)); + antd_error(rq->client, 400, "Invalid SSL request"); + // server_config.connection++; + ERR_print_errors_fp(stderr); + return task; + } + } + client->state = ANTD_CLIENT_HANDSHAKE; + task->handle = accept_request; + // LOG("Handshake finish for %d\n", client->sock); + return task; + } + else + { + if (!((pfd[0].revents & POLLIN))) + { + task->handle = accept_request; + return task; + } + } +#endif + // LOG("Ready for reading %d\n", client->sock); + // server_config.connection++; + client->state = ANTD_CLIENT_PROTO_CHECK; + read_buf(rq->client, buf, sizeof(buf)); + line = buf; + LOG("Request (%d): %s", rq->client->sock, line); + // get the method string + token = strsep(&line, " "); + if (!line) + { + // LOG("No method found"); + antd_error(rq->client, 405, "No method found"); + return task; + } + trim(token, ' '); + trim(line, ' '); + dput(rq->request, "METHOD", strdup(token)); + // get the request + token = strsep(&line, " "); + if (!line) + { + // LOG("No request found"); + antd_error(rq->client, 400, "Bad request"); + return task; + } + trim(token, ' '); + trim(line, ' '); + trim(line, '\n'); + trim(line, '\r'); + dput(rq->request, "PROTOCOL", strdup(line)); + dput(rq->request, "REQUEST_QUERY", strdup(token)); + line = token; + token = strsep(&line, "?"); + dput(rq->request, "REQUEST_PATH", url_decode(token)); + // decode request + // now return the task + task->handle = decode_request_header; + return task; +} + +void *resolve_request(void *data) +{ + struct stat st; + char path[2 * BUFFLEN]; + antd_request_t *rq = (antd_request_t *)data; + antd_task_t *task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io); + char *url = (char *)dvalue(rq->request, "REQUEST_URI"); + char *newurl = NULL; + char *rqp = NULL; + char *oldrqp = NULL; + rq->client->state = ANTD_CLIENT_RESOLVE_REQUEST; + char * root = (char *)dvalue(rq->request, "SERVER_WWW_ROOT"); + snprintf(path, sizeof(path), "%s/%s", root, url); + LOG("URL is : %s", url); + LOG("Resource Path is : %s", path); + // if (path[strlen(path) - 1] == '/') + // strcat(path, "index.html"); + if (stat(path, &st) == -1) + { + free(task); + rqp = strdup(url); + oldrqp = rqp; + trim(rqp, '/'); + newurl = strsep(&rqp, "/"); + if (!rqp) + rqp = strdup("/"); + else + rqp = strdup(rqp); + dput(rq->request, "REQUEST_URI", rqp); + dput(rq->request, "RESOURCE_PATH", __s("%s/%s", root,rqp)); + LOG("Execute plugin %s", newurl); + task = execute_plugin(rq, newurl); + free(oldrqp); + return task; + } + else + { + if (S_ISDIR(st.st_mode)) + { + strcat(path, "/index.html"); + if (stat(path, &st) == -1) + { + chain_t it; + newurl = NULL; + for_each_assoc(it, g_server_config.handlers) + { + memset(path, 0, sizeof(path)); + snprintf(path, sizeof(path), "%s/%s/index.%s", root, url, it->key); + if (stat(path, &st) == 0) + { + i = g_server_config.handlers->cap; + newurl = path; + break; + } + } + if (!newurl) + { + antd_error(rq->client, 404, "Resource Not Found"); + return task; + } + } + } + dput(rq->request, "RESOURCE_PATH", strdup(path)); + // check if the mime is supported + // if the mime is not supported + // find an handler plugin to process it + // if the plugin is not found, forbidden access to the file should be sent + char *mime_type = mime(path); + dput(rq->request, "RESOURCE_MIME", strdup(mime_type)); + if (strcmp(mime_type, "application/octet-stream") == 0) + { + char *ex = ext(path); + char *h = NULL; + if (ex) + { + h = dvalue(g_server_config.handlers, ex); + free(ex); + } + if (h) + { + // sprintf(path,"/%s%s",h,url); + // LOG("WARNING::::Access octetstream via handle %s", h); + // if(execute_plugin(client,buf,method,rq) < 0) + // cannot_execute(client); + free(task); + return execute_plugin(rq, h); + } + else + antd_error(rq->client, 403, "Access forbidden"); + } + else + { + // discard all request data + dictionary_t headers = (dictionary_t)dvalue(rq->request, "REQUEST_HEADER"); + if (headers) + { + char *sclen = (char *)dvalue(headers, "Content-Length"); + unsigned clen = 0; + unsigned read = 0; + int count; + if (sclen) + { + clen = atoi(sclen); + while (read < clen) + { + count = antd_recv(rq->client, path, sizeof(path) < clen ? sizeof(path) : clen); + if (count <= 0) + break; + read += count; + } + } + } + antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE); + task->handle = serve_file; + } + return task; + } +} + +void *finish_request(void *data) +{ + if (!data) + return NULL; + destroy_request(data); + g_server_config.connection--; + LOG("Remaining connection %d", g_server_config.connection); + return NULL; +} + + + +void *serve_file(void *data) +{ + antd_request_t *rq = (antd_request_t *)data; + antd_task_t *task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io); + char *path = (char *)dvalue(rq->request, "RESOURCE_PATH"); + char *mime_type = (char *)dvalue(rq->request, "RESOURCE_MIME"); + rq->client->state = ANTD_CLIENT_SERVE_FILE; + struct stat st; + int s = stat(path, &st); + + if (s == -1) + { + antd_error(rq->client, 404, "File not found"); + } + else + { + // check if it is modified + dictionary_t header = (dictionary_t)dvalue(rq->request, "REQUEST_HEADER"); + char *last_modif_since = (char *)dvalue(header, "If-Modified-Since"); + time_t t = st.st_ctime; + struct tm tm; + if (last_modif_since) + { + strptime(last_modif_since, "%a, %d %b %Y %H:%M:%S GMT", &tm); + t = timegm(&tm); + // t = mktime(localtime(&t)); + } + + if (last_modif_since && st.st_ctime == t) + { + // return the not changed + antd_error(rq->client, 304, ""); + } + else + { + int size = (int)st.st_size; + char ibuf[64]; + snprintf(ibuf, sizeof(ibuf), "%d", size); + antd_response_header_t rhd; + rhd.cookie = NULL; + rhd.status = 200; + rhd.header = dict(); + dput(rhd.header, "Content-Type", strdup(mime_type)); +#ifdef USE_ZLIB + if (!compressable(mime_type) || rq->client->z_level == ANTD_CNONE) +#endif + dput(rhd.header, "Content-Length", strdup(ibuf)); + gmtime_r(&st.st_ctime, &tm); + strftime(ibuf, 64, "%a, %d %b %Y %H:%M:%S GMT", &tm); + dput(rhd.header, "Last-Modified", strdup(ibuf)); + dput(rhd.header, "Cache-Control", strdup("no-cache")); + antd_send_header(rq->client, &rhd); + + __f(rq->client, path); + } + } + + return task; +} + +static void *proxy_monitor(void *data) +{ + antd_request_t *rq = (antd_request_t *)data; + rq->client->state = ANTD_CLIENT_PROXY_MONITOR; + antd_client_t *proxy = (antd_client_t *)dvalue(rq->request, "PROXY_HANDLE"); + antd_task_t *task = antd_create_task(NULL, data, NULL, rq->client->last_io); + int pret, ret, sz1 = 0, sz2 = 0; + char *buf = NULL; + buf = (char *)malloc(BUFFLEN); + struct pollfd pfd[1]; + memset(pfd, 0, sizeof(pfd)); + pfd[0].fd = proxy->sock; + pfd[0].events = POLLIN; + ret = 1; + + do + { + sz1 = antd_recv_upto(rq->client, buf, BUFFLEN); + + if ((sz1 < 0) || (sz1 > 0 && antd_send(proxy, buf, sz1) != sz1)) + { + ret = 0; + break; + } + pret = poll(pfd, 1, 0); + if (pret < 0) + { + (void)close(proxy->sock); + return task; + } + sz2 = 0; + if (pret > 0 && (pfd[0].revents & POLLIN)) + { + sz2 = antd_recv_upto(proxy, buf, BUFFLEN); + if (sz2 <= 0 || (sz2 > 0 && antd_send(rq->client, buf, sz2) != sz2)) + { + ret = 0; + break; + } + } + if ((pret > 0) && (pfd[0].revents & POLLERR || + pfd[0].revents & POLLRDHUP || + pfd[0].revents & POLLHUP || + pfd[0].revents & POLLNVAL)) + { + ret = 0; + break; + } + } while (sz1 > 0 || sz2 > 0); + free(buf); + + if (ret == 0) + { + (void)close(proxy->sock); + return task; + } + + if (pfd[0].revents & POLLIN) + { + antd_task_bind_event(task, proxy->sock, 0, TASK_EVT_ON_READABLE); + } + else + { + antd_task_bind_event(task, proxy->sock, 50u, TASK_EVT_ON_TIMEOUT); + } + task->handle = proxy_monitor; + task->access_time = rq->client->last_io; + + antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_READABLE); + return task; +} + +void *proxify(void *data) +{ + int sock_fd, size, ret; + char *str = NULL; + chain_t it; + antd_request_t *rq = (antd_request_t *)data; + antd_client_t *proxy = NULL; + rq->client->state = ANTD_CLIENT_RESOLVE_REQUEST; + char *host = dvalue(rq->request, "PROXY_HOST"); + int port = atoi(dvalue(rq->request, "PROXY_PORT")); + char *path = dvalue(rq->request, "PROXY_PATH"); + char *query = dvalue(rq->request, "PROXY_QUERY"); + char *ptr, *ip; + dictionary_t xheader = dvalue(rq->request, "REQUEST_HEADER"); + antd_task_t *task = antd_create_task(NULL, data, NULL, rq->client->last_io); + if (!xheader) + { + antd_error(rq->client, 400, "Badd Request"); + return task; + } + pthread_mutex_lock(&server_mux); + ip = NULL; + // ip_from_host is not threadsafe, need to lock it + ptr = ip_from_hostname(host); + if (ptr) + { + ip = strdup(ptr); + } + pthread_mutex_unlock(&server_mux); + + if (!ip) + { + antd_error(rq->client, 502, "Badd address"); + return task; + } + // TODO support ipv6 + sock_fd = antd_request_socket(ip, port); + free(ip); + if (sock_fd == -1) + { + antd_error(rq->client, 503, "Service Unavailable"); + return task; + } + set_nonblock(sock_fd); + /*struct timeval timeout; + timeout.tv_sec = 2; + timeout.tv_usec = 0; //POLL_EVENT_TO*1000; + if (setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) < 0) + { + ERROR("setsockopt failed:%s", strerror(errno)); + antd_error(rq->client, 500, "Internal proxy error"); + (void)close(sock_fd); + return task; + } + + if (setsockopt(sock_fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) < 0) + { + ERROR("setsockopt failed:%s", strerror(errno)); + antd_error(rq->client, 500, "Internal proxy error"); + (void)close(sock_fd); + return task; + }*/ + + proxy = (antd_client_t *)malloc(sizeof(antd_client_t)); + proxy->sock = sock_fd; + proxy->ssl = NULL; + proxy->zstream = NULL; + proxy->z_level = ANTD_CNONE; + time(&proxy->last_io); + + // store content length here + dput(rq->request, "PROXY_HANDLE", proxy); + + str = __s("%s %s?%s HTTP/1.1\r\n", (char *)dvalue(rq->request, "METHOD"), path, query); + size = strlen(str); + ret = antd_send(proxy, str, size); + free(str); + if (ret != size) + { + antd_error(rq->client, 500, ""); + (void)close(sock_fd); + return task; + } + for_each_assoc(it, xheader) + { + str = __s("%s: %s\r\n", it->key, (char *)it->value); + size = strlen(str); + ret = antd_send(proxy, str, size); + free(str); + if (ret != size) + { + antd_error(rq->client, 500, ""); + (void)close(sock_fd); + return task; + } + } + (void)antd_send(proxy, "\r\n", 2); + // now monitor the proxy + task->handle = proxy_monitor; + task->access_time = rq->client->last_io; + // register event + antd_task_bind_event(task, proxy->sock, 0, TASK_EVT_ON_READABLE | TASK_EVT_ON_WRITABLE); + return task; +} + + + + + + + +/** + * Execute a plugin based on the http requeset + * First decode the http request header to find the correct plugin + * and the correct function on the plugin + * Second, decode all parameters necessary of the request and pass it + * to the callback function. + * Execute the callback function if sucess + * @param client soket client + * @param path request path + * @param method request method + * @param query_string GET query string + * @return -1 if failure + * 1 if sucess + */ +void *execute_plugin(void *data, const char *pname) +{ + void *(*fn)(void *); + plugin_header_t *(*metafn)(); + plugin_header_t *meta = NULL; + struct plugin_entry *plugin; + char *error; + char pattern[256]; + + antd_request_t *rq = (antd_request_t *)data; + antd_task_t *task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io); + antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE); + + snprintf(pattern, sizeof(pattern), "\\b%s\\b", pname); + char *port_s = (char *)dvalue(rq->request, "SERVER_PORT"); + port_config_t *pcnf = (port_config_t *)dvalue(g_server_config.ports, port_s); + + // check if plugin is enabled on this port + if (!pcnf->plugins || !regex_match(pattern, pcnf->plugins, 0, NULL)) + { + LOG("No plugin matched in [%s] using pattern [%s]", pcnf->plugins, pattern); + antd_error(rq->client, 403, "Access forbidden"); + return task; + } + + // LOG("Plugin name '%s'", pname); + rq->client->state = ANTD_CLIENT_PLUGIN_EXEC; + // load the plugin + pthread_mutex_lock(&server_mux); + plugin = plugin_load((char *)pname, dvalue(g_server_config.plugins, pname)); + pthread_mutex_unlock(&server_mux); + if (plugin == NULL) + { + antd_error(rq->client, 503, "Requested service not found"); + return task; + } + // check if the plugin want rawbody or decoded body + metafn = (plugin_header_t * (*)()) dlsym(plugin->handle, "meta"); + if ((error = dlerror()) == NULL) + { + meta = metafn(); + } + // load the function + fn = (void *(*)(void *))dlsym(plugin->handle, PLUGIN_HANDLER); + if ((error = dlerror()) != NULL) + { + ERROR("Problem when finding %s method from %s : %s", PLUGIN_HANDLER, pname, error); + antd_error(rq->client, 503, "Requested service not found"); + return task; + } + // check if we need the raw data or not + if (meta && meta->raw_body == 1) + { + task->handle = fn; + } + else + { + free(task); + task = antd_create_task(decode_post_request, (void *)rq, fn, rq->client->last_io); + } + return task; +} + +dictionary_t mimes_list() +{ + return g_server_config.mimes; +} + +#ifdef USE_ZLIB +int compressable(char *ctype) +{ + if (!g_server_config.gzip_enable || g_server_config.gzip_types == NULL) + return 0; + item_t it; + list_for_each(it, g_server_config.gzip_types) + { + if (it->type == LIST_TYPE_POINTER && it->value.ptr && regex_match((const char *)it->value.ptr, ctype, 0, NULL)) + { + return 1; + } + } + return 0; +} +#endif diff --git a/server.h b/server.h new file mode 100644 index 0000000..90bf66a --- /dev/null +++ b/server.h @@ -0,0 +1,8 @@ +#ifndef HTTP_SERVER +#define HTTP_SERVER + +void *accept_request(void *); +void *proxify(void *data); +void *resolve_request(void *data); +void *finish_request(void *data); +#endif \ No newline at end of file