/***************************************************************************** * Open LiteSpeed is an open source HTTP server. * * Copyright (C) 2013 - 2015 LiteSpeed Technologies, Inc. * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see http://www.gnu.org/licenses/. * *****************************************************************************/ #include "../include/ls.h" #include #include #include #include /** * Define the module name, MUST BE the same as .so file name; * i.e., if MNAME is testmodule, then the .so must be testmodule.so. * * How to test this sample * 1, build it, put the file reqinfhandler.so in $LSWS_HOME/modules * 2, in web admin console configuration, enable this module * 3, visit http(s)://.../reqinfo(/echo|/md5|/upload) with/without request body */ #define MNAME reqinfhandler lsi_module_t MNAME; static char testuri[] = "/reqinfo"; #define CONTENT_HEAD "Server request varibles\r\n" #define CONTENT_FORMAT "%s%s\r\n" #define CONTENT_TAIL "\r\n" typedef struct mydata_s { int type; FILE *fp; MD5_CTX ctx; int req_body_len; // length in "Content-Length" int rcvd_req_body_len; // recved int rcvd_done; // finished or due to connection error int resp_done; } mydata_t; // The following matches the index, DO NOT change the order const char *req_array[] = { "REMOTE_ADDR", "REMOTE_PORT", "REMOTE_HOST", "REMOTE_USER", "REMOTE_IDENT", "REQUEST_METHOD", "QUERY_STRING", "AUTH_TYPE", "PATH_INFO", "SCRIPT_FILENAME", "REQUEST_FILENAME", "REQUEST_URI", "DOCUMENT_ROOT", "SERVER_ADMIN", "SERVER_NAME", "SERVER_ADDR", "SERVER_PORT", "SERVER_PROTOCOL", "SERVER_SOFTWARE", "API_VERSION", "THE_REQUEST", "IS_SUBREQ", "TIME", "TIME_YEAR", "TIME_MON", "TIME_DAY", "TIME_HOUR", "TIME_MIN", "TIME_SEC", "TIME_WDAY", "SCRIPT_NAME", "CURRENT_URI", "REQUEST_BASENAME", "SCRIPT_UID", "SCRIPT_GID", "SCRIPT_USERNAME", "SCRIPT_GROUPNAME", "SCRIPT_MODE", "SCRIPT_BASENAME", "SCRIPT_URI", "ORG_REQ_URI", "HTTPS", "SSL_VERSION", "SSL_SESSION_ID", "SSL_CIPHER", "SSL_CIPHER_USEKEYSIZE", "SSL_CIPHER_ALGKEYSIZE", "SSL_CLIENT_CERT", "GEOIP_ADDR", "PATH_TRANSLATED", }; // The following is used by name value, you can change the order const char *env_array[] = { "PATH", "REMOTE_ADDR", "REMOTE_PORT", "REMOTE_HOST", "REMOTE_USER", "REMOTE_IDENT", "REQUEST_METHOD", "QUERY_STRING", "AUTH_TYPE", "PATH_INFO", "SCRIPT_FILENAME", "REQUEST_FILENAME", "REQUEST_URI", "DOCUMENT_ROOT", "SERVER_ADMIN", "SERVER_NAME", "SERVER_ADDR", "SERVER_PORT", "SERVER_PROTOCOL", "SERVER_SOFTWARE", "API_VERSION", "THE_REQUEST", "IS_SUBREQ", "TIME", "TIME_YEAR", "TIME_MON", "TIME_DAY", "TIME_HOUR", "TIME_MIN", "TIME_SEC", "TIME_WDAY", "SCRIPT_NAME", "CURRENT_URI", "REQUEST_BASENAME", "SCRIPT_UID", "SCRIPT_GID", "SCRIPT_USERNAME", "SCRIPT_GROUPNAME", "SCRIPT_MODE", "SCRIPT_BASENAME", "SCRIPT_URI", "ORG_REQ_URI", "HTTPS", "SSL_VERSION", "SSL_SESSION_ID", "SSL_CIPHER", "SSL_CIPHER_USEKEYSIZE", "SSL_CIPHER_ALGKEYSIZE", "SSL_CLIENT_CERT", "GEOIP_ADDR", "PATH_TRANSLATED", }; const char *reqhdr_array[] = { "Accept", "Accept-Charset", "Accept-Encoding", "Accept-Language", "Accept-Datetime", "Authorization", "Cache-Control", "Connection", "Content-Type", "Content-Length", "Content-MD5", "Cookie", "Date", "Expect", "From", "Host", "Pragma", "Proxy-Authorization" "If-Match", "If-Modified-Since", "If-None-Match", "If-Range", "If-Unmodified-Since", "Max-Forwards", "Origin", "Referer", "User-Agent", "Range", "Upgrade", "Via", "X-Forwarded-For", }; static int free_mydata(void *data) { mydata_t *mydata = (mydata_t *)data; if (mydata != NULL) { // free (mydata); Do not need to free with session pool, // will be freed at the end of the session. g_api->log(NULL, LSI_LOG_DEBUG, "#### reqinfomodule free_mydata\n"); } return 0; } static int initdata(lsi_param_t *param) { mydata_t *mydata = (mydata_t *)g_api->get_module_data(param->session, &MNAME, LSI_DATA_HTTP); ls_xpool_t *pPool = g_api->get_session_pool(param->session); mydata = (mydata_t *)ls_xpool_alloc(pPool, sizeof(mydata_t)); memset(mydata, 0, sizeof(mydata_t)); g_api->log(NULL, LSI_LOG_DEBUG, "#### reqinfomodule initdata\n"); g_api->set_module_data(param->session, &MNAME, LSI_DATA_HTTP, (void *)mydata); return 0; } // 0:no body or no deal, 1,echo, 2: md5, 3, save to file static int get_reqbody_dealertype(const lsi_session_t *session) { char path[512]; int n; if (g_api->get_req_content_length(session) > 0) { n = g_api->get_req_var_by_id(session, LSI_VAR_PATH_INFO, path, 512); if (n >= 5 && strncasecmp(path, "/echo", 5) == 0) return 1; else if (n >= 4 && strncasecmp(path, "/md5", 4) == 0) return 2; else if (n >= 7 && strncasecmp(path, "/upload", 7) == 0) return 3; } // All other cases, no deal return 0; } static inline void append(const lsi_session_t *session, const char *s, int n) { if (n == 0) n = strlen(s); g_api->append_resp_body(session, (char *)s, n); } static int on_read(const lsi_session_t *session) { unsigned char md5[16]; char buf[8192]; int ret, i; int readbytes = 0, written = 0; mydata_t *mydata = (mydata_t *)g_api->get_module_data(session, &MNAME, LSI_DATA_HTTP); if (mydata == NULL || mydata->type == 0) { g_api->end_resp(session); return 0; } while ((ret = g_api->read_req_body(session, buf, sizeof(buf))) > 0) { mydata->rcvd_req_body_len += ret; readbytes += ret; if (mydata->type == 1) { append(session, buf, ret); written += ret; } else if (mydata->type == 2) MD5_Update(&mydata->ctx, buf, ret); else fwrite(buf, 1, ret, mydata->fp); if (mydata->rcvd_req_body_len >= mydata->req_body_len) { mydata->rcvd_done = 1; break; } } if (ret == 0) mydata->rcvd_done = 1; if (mydata->rcvd_done == 1) { if (mydata->type == 2) { MD5_Final(md5, &mydata->ctx); for (i = 0; i < 16; ++i) sprintf(buf + i * 2, "%02X", md5[i]); append(session, "MD5 is
", 10); append(session, buf, 32); written += 42; } else if (mydata->type == 3) { if (mydata->fp != NULL) { fclose(mydata->fp); mydata->fp = NULL; append(session, "File uploaded.
", 18); written += 18; } } mydata->resp_done = 1; } if (written > 0) g_api->flush(session); g_api->set_handler_write_state(session, 1); //g_api->end_resp(session); return readbytes; } static int begin_process(const lsi_session_t *session) { #define VALMAXSIZE 4096 #define LINEMAXSIZE (VALMAXSIZE + 50) char val[VALMAXSIZE], line[LINEMAXSIZE] = {0}; int n; int i; const char *p; char *buf; mydata_t *mydata = (mydata_t *)g_api->get_module_data(session, &MNAME, LSI_DATA_HTTP); ls_xpool_t *pPool = g_api->get_session_pool(session); //Create response body append(session, CONTENT_HEAD, 0); //Original request header n = g_api->get_req_raw_headers_length(session); buf = (char *)ls_xpool_alloc(pPool, n + 1); memset(buf, 0, n + 1); n = g_api->get_req_raw_headers(session, buf, n + 1); append(session, "Original request\r\n", 0); ls_xpool_free(pPool, buf); append(session, "\r\n
\r\n", 0);
    append(session, buf, n);
    append(session, "\r\n

Request headers
\r\n", 0); for (i = 0; i < sizeof(reqhdr_array) / sizeof(char *); ++i) { p = g_api->get_req_header(session, reqhdr_array[i], strlen(reqhdr_array[i]), &n); if ((p != NULL) && p[0] != 0 && n > 0) { memcpy(val, p, n); val[n] = '\0'; n = snprintf(line, LINEMAXSIZE - 1, CONTENT_FORMAT, reqhdr_array[i], val); append(session, line, n); } } append(session, "\r\n

Server req env
\r\n", 0); //Server req env for (i = LSI_VAR_REMOTE_ADDR; i < LSI_VAR_COUNT; ++i) { n = g_api->get_req_var_by_id(session, i, val, VALMAXSIZE); if (n > 0) { val[n] = '\0'; n = snprintf(line, LINEMAXSIZE - 1, CONTENT_FORMAT, req_array[i], val); append(session, line, n); } } append(session, "\r\n

env varibles
\r\n", 0); for (i = 0; i < sizeof(env_array) / sizeof(char *); ++i) { //env varibles n = g_api->get_req_env(session, env_array[i], strlen(env_array[i]), val, VALMAXSIZE); if (n > 0) { val[n] = '\0'; n = snprintf(line, LINEMAXSIZE - 1, CONTENT_FORMAT, env_array[i], val); append(session, line, n); } } p = g_api->get_req_cookies(session, &n); if ((p != NULL) && p[0] != 0 && n > 0) { append(session, "\r\n

Request cookies
\r\n", 0); append(session, "", 0); } n = g_api->get_req_cookie_count(session); if (n > 0) { //try get a certen cookie p = g_api->get_cookie_value(session, "LSWSWEBUI", 9, &n); if ((p != NULL) && n > 0) { append(session, "", 0); } } append(session, "
Cookie", 0); append(session, p, n); append(session, "
cookie_LSWSWEBUI", 0); append(session, p, n); append(session, "
", 0); n = get_reqbody_dealertype(session); mydata->req_body_len = g_api->get_req_content_length(session); mydata->rcvd_req_body_len = 0; mydata->type = n; sprintf(line, "Will deal with the req body.Type = %d, req body lenghth = %d
", n, mydata->req_body_len); append(session, line, 0); if (mydata->type == 0) { append(session, CONTENT_TAIL, 0); mydata->rcvd_done = 1; mydata->resp_done = 1; } g_api->set_status_code(session, 200); if (mydata->type == 3) // Save to file mydata->fp = fopen("/tmp/uploadfile", "wb"); else if (mydata->type == 2) // Md5 MD5_Init(&mydata->ctx); g_api->flush(session); // if ( mydata->type != 0) on_read(session); //g_api->end_resp(session); return 0; } static int on_write(const lsi_session_t *session) { mydata_t *mydata = (mydata_t *)g_api->get_module_data(session, &MNAME, LSI_DATA_HTTP); return (mydata == NULL || mydata->type == 0 || mydata->resp_done == 1) ? LSI_RSP_DONE : LSI_RSP_MORE; } static int rcvd_req_header_cbf(lsi_param_t *param) { const char *uri; int len; uri = g_api->get_req_uri(param->session, &len); if ((uri != NULL) && (len >= sizeof(testuri) - 1) && (strncasecmp(uri, testuri, sizeof(testuri) - 1) == 0)) g_api->register_req_handler(param->session, &MNAME, sizeof(testuri) - 1); return LSI_OK; } static int init_module(lsi_module_t *module) { g_api->init_module_data(module, free_mydata, LSI_DATA_HTTP); return 0; } static int clean_up(const lsi_session_t *session) { g_api->free_module_data(session, &MNAME, LSI_DATA_HTTP, free_mydata); return 0; } static lsi_serverhook_t server_hooks[] = { { LSI_HKPT_HTTP_BEGIN, initdata, LSI_HOOK_NORMAL, LSI_FLAG_ENABLED }, //{ LSI_HKPT_HTTP_END, resetdata, LSI_HOOK_NORMAL, LSI_FLAG_ENABLED }, { LSI_HKPT_RCVD_REQ_HEADER, rcvd_req_header_cbf, LSI_HOOK_NORMAL, LSI_FLAG_ENABLED }, LSI_HOOK_END //Must put this at the end position }; static lsi_reqhdlr_t myhandler = { begin_process, on_read, on_write, clean_up }; lsi_module_t MNAME = { LSI_MODULE_SIGNATURE, init_module, &myhandler, NULL, "", server_hooks };