diff options
author | Jason Fesler <jfesler@free.gigo.com> | 2014-04-02 19:48:27 -0700 |
---|---|---|
committer | Jason Fesler <jfesler@free.gigo.com> | 2014-04-02 19:48:27 -0700 |
commit | cb465c6f511d63b5445a640735295de967126e2c (patch) | |
tree | 4b2e5639c92e9e659bc234b07eefcaae031b501c /mod_ip.c | |
parent | e8e6401f8c7bb6ab64dbbcfc10618e792fcbd85b (diff) | |
download | mod_ip-cb465c6f511d63b5445a640735295de967126e2c.tar.gz mod_ip-cb465c6f511d63b5445a640735295de967126e2c.zip |
Imported without history from https://falling-sky.googlecode.com/svn/trunk/mod_ip revision 15111.0
Diffstat (limited to 'mod_ip.c')
-rw-r--r-- | mod_ip.c | 645 |
1 files changed, 645 insertions, 0 deletions
diff --git a/mod_ip.c b/mod_ip.c new file mode 100644 index 0000000..f1ed7a3 --- /dev/null +++ b/mod_ip.c @@ -0,0 +1,645 @@ +/* + Copyright 2013 Jason Fesler + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + + +/* This module reports the IP the user came from, as a JSONP response. GET requests required; CGI style arguments are permitted. + * callback=[name] to call a function name of your choosing size=[number] to bad the http data portion of the response. + * (Does not attempt to offset header response) If the callback is "?", it will output to the screen (calling it "callback"). Any + * other, and a JSON mime type is used instead. */ + + +/* NOTICE + * + * Portions of this module are inspired by: "The Apache Modules Book: Application Development with Apache" by Nick Kew; published by + * Prentice Hall, January 27 2007. + * + */ + +/* Changes: + * + * 2013-04-29 - jfesler@gigo.com + * Add headers to output (content type, expires, cache control) + * Stop depending on mod_headers (and user config) + * + * 2013-04-28 - jfesler@gigo.com + * Add built in definitions for 6to4 and Teredo + * Seperate the function that adds new prefixes, from the config parser/hook + * Made ./configure friendly + * + * 2013-04-14 - jfesler@gigo.com + * - ASN queries also populate a string with the entire list of ASNs + * + * 2013-03-23 - jfesler@gigo.com + * - ASN queries always compiled in. Triggers on asn=1 in request arguments. + * - ALWAYS_PADDING now full time. + * - Conditional compiling directives removed. Troublesome across platforms. + * + * 2013-01-25 - eric@vyncke.org + * - conditional compile for ASN related DNS queries + * - also report asn_name + * + * 2012-12-26 - eric@vyncke.org + * - also return the ASN number (to be used to detect tunnels by the client) code from mysasn.code.google.com + * + * 2011-02-07 - ondrej.sury@nic.cz + * - use apache (per request) memory pools and not static buffers + * - drop my_strtok (now thread safe code) + * - optimize parse_form_from_string + * - rewrite output function + * - limit maximum padding size to 1600 (see the #define) + * - always add padding="" if compiled with -DALWAYS_PADDING + * - limit the maximum length of callback function name to 64 (#define) + * - some more micro optimizations to make code less nested, etc. + * - limit callback name characters (security) + * + * 2011-02-20 - jfesler@gigo.com + * - new parameter: testip= (40 chars or less; alphanum) for testing purposes + * - prefixes are now parsed on startup; and do cidr notation. + * - http "Via" lines are now reported. + * + * 2011-02-27 - jfesler@gigo.com - use config file instead. mod_ip_prefix 2002::/16 "6to4" + * - Also, disabled proxy reporting; just returns empty string or 'yes' now. Suspect in core dumps. + * + * 2011-04-19 - ondrej@sury.org - Generate size buffer on-the-fly, not in the buffer + * + * 2011-05-02 - ondrej@sury.org - Move MAX_PADDING_SIZE to config file (mod_ip_max_padding) + * + */ + + + +#include <ctype.h> +#include <httpd.h> +#include <http_protocol.h> +#include <http_config.h> +#include <http_log.h> +#include <apr_hash.h> +#include <apr_strings.h> +#include <apr_network_io.h> + +/* Those unfortunately look like they claim the autoconf variables, so let's undefine them */ + +#undef PACKAGE_VERSION +#undef PACKAGE_URL +#undef PACKAGE_STRING +#undef PACKAGE_BUGREPORT +#undef PACKAGE_NAME +#undef PACKAGE_TARNAME + +/* We need some libraries besides what Apache provides. */ + +#include "config.h" + + + +#define MAX_PADDING_SIZE 64*1024*1024 +#define MAX_CALLBACK_SIZE 64 +#define MAX_TESTIP_SIZE 40 +#define MAX_PREFIXES 500 + +#define PADDING_BUFFER_SIZE 4096 +#define PADDING_DEFAULT_SIZE 1600 + +const char *padding_buffer = "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gQWxpcXVhbSBzb2RhbGVzIHVsdHJpY2llcyBlZ2VzdGFzLiBOdWxsYW0gYWNjdW1zYW4gdGluY2lkdW50IG1hc3NhLCBpZCB2ZXN0aWJ1bHVtIHB1cnVzIHNvZGFsZXMgY29uZ3VlLiBJbiBpZCBpYWN1bGlzIGF1Z3VlLiBEb25lYyBhdCBpcHN1bSBtYXNzYSwgcXVpcyBmcmluZ2lsbGEgYW50ZS4gTnVsbGFtIHJ1dHJ1bSwgZHVpIGZldWdpYXQgY29uZGltZW50dW0gbGFvcmVldCwgdXJuYSBhcmN1IHBvcnRhIGVzdCwgZXQgY29uZ3VlIHNhcGllbiBsZWN0dXMgcXVpcyBtYXVyaXMuIENyYXMgbmVjIHNlbSB2ZWxpdCwgdm9sdXRwYXQgbWF0dGlzIG1hc3NhLiBDcmFzIGZldWdpYXQgZHVpIHNjZWxlcmlzcXVlIGFudGUgYXVjdG9yIGF0IGZyaW5naWxsYSBzYXBpZW4gaGVuZHJlcml0LiBTZWQgaGVuZHJlcml0IG5pc2wgdml0YWUgbmlzaSBjb21tb2RvIGlkIGZhY2lsaXNpcyB1cm5hIGltcGVyZGlldC4gSW50ZWdlciBlc3QgZmVsaXMsIGxhY2luaWEgaWQgYWRpcGlzY2luZyBub24sIGVsZWlmZW5kIGlkIG5lcXVlLiBNYXVyaXMgZ3JhdmlkYSB2ZWxpdCBxdWlzIHNhcGllbiBzb2RhbGVzIGluIGlhY3VsaXMgZWxpdCBwbGFjZXJhdC4gUGhhc2VsbHVzIGhlbmRyZXJpdCBlcm9zIHJpc3VzLCBxdWlzIHVsdHJpY2llcyBsYWN1cy4gUXVpc3F1ZSBmYWNpbGlzaXMgYWxpcXVhbSBhdWd1ZSB2aXRhZSB2b2x1dHBhdC4gTmFtIHNpdCBhbWV0IGxvcmVtIGluIGxhY3VzIHBoYXJldHJhIHRlbXBvciBzZWQgZWdldCBudWxsYS4gTnVuYyBsb2JvcnRpcyB0aW5jaWR1bnQgbmlzbCBldSBldWlzbW9kLiBJbiBoYWMgaGFiaXRhc3NlIHBsYXRlYSBkaWN0dW1zdC4KCk51bGxhbSBuZWMgZW5pbSBhYyBwdXJ1cyBvcm5hcmUgc2NlbGVyaXNxdWUgZWdldCBzaXQgYW1ldCBzZW0uIERvbmVjIG1vbGxpcyBqdXN0byB2ZWwgbnVuYyBkaWN0dW0gY3Vyc3VzLiBDdXJhYml0dXIgdml2ZXJyYSBwb3N1ZXJlIGNvbnZhbGxpcy4gVmVzdGlidWx1bSB2dWxwdXRhdGUgc2FnaXR0aXMgc3VzY2lwaXQuIFF1aXNxdWUgdmVsIG51bmMgYSBzZW0gdm9sdXRwYXQgcG9ydGEuIEludGVnZXIgaW4gbWkgZWdldCBhbnRlIGx1Y3R1cyBzY2VsZXJpc3F1ZSBpZCBpbiBsZW8uIE1vcmJpIGNvbnNlcXVhdCBkaWduaXNzaW0gdmVoaWN1bGEuIFF1aXNxdWUgbWF0dGlzIGVsZW1lbnR1bSBkaWFtIHZpdGFlIGxvYm9ydGlzLiBOdWxsYSBldSBmYXVjaWJ1cyBvZGlvLiBNb3JiaSB2ZW5lbmF0aXMgc29kYWxlcyBlbmltLCBzaXQgYW1ldCBzdXNjaXBpdCBvcmNpIGFsaXF1ZXQgdmVsLiBJbnRlZ2VyIGVsZW1lbnR1bSBkaWFtIGV0IG51bGxhIGxhY2luaWEgc3VzY2lwaXQuIERvbmVjIGxlY3R1cyBhdWd1ZSwgbG9ib3J0aXMgYWxpcXVldCBldWlzbW9kIGV0LCBlZ2VzdGFzIGluIG5lcXVlLiBWZXN0aWJ1bHVtIGV1IGxvcmVtIHRlbGx1cy4gRXRpYW0gZGlnbmlzc2ltIHBvc3VlcmUgbGFjdXMgcXVpcyB1bHRyaWNlcy4KClV0IGVsZWlmZW5kIGNvbnNlY3RldHVyIHR1cnBpcyBzaXQgYW1ldCB2dWxwdXRhdGUuIFF1aXNxdWUgZXUgbWFzc2EgYXQgYW50ZSBhdWN0b3IgYmxhbmRpdCB2aXRhZSBxdWlzIGxlby4gTW9yYmkgYSByaXN1cyB2aXRhZSBzZW0gbW9sZXN0aWUgcnV0cnVtIG5vbiBuZWMgbGlndWxhLiBQZWxsZW50ZXNxdWUgZXQgbGFvcmVldCBuZXF1ZS4gU2VkIGJsYW5kaXQgY29uc2VxdWF0IHJob25jdXMuIE51bGxhIGZhY2lsaXNpLiBNYXVyaXMgZWdldCBsZWN0dXMgdml0YWUgc2FwaWVuIHNlbXBlciB0cmlzdGlxdWUuIE51bmMgYXVjdG9yIGVsaXQgYXQgc2FwaWVuIHRpbmNpZHVudCBxdWlzIGZlcm1lbnR1bSBsaWd1bGEgcGxhY2VyYXQuIEZ1c2NlIGJpYmVuZHVtIGxvcmVtIGlkIG1pIGxhY2luaWEgaWQgY3Vyc3VzIG9kaW8gZGljdHVtLiBWaXZhbXVzIGVsaXQgb3JjaSwgcHVsdmluYXIgdmVsIGFjY3Vtc2FuIGV0LCBhbGlxdWFtIGF0IGFyY3UuIEN1cmFiaXR1ciBmYWNpbGlzaXMgZWdlc3RhcyBkaWFtLCBub24gc29sbGljaXR1ZGluIHZlbGl0IGFsaXF1YW0gdml0YWUuIERvbmVjIGxhY2luaWEsIG5pc2wgYSBpbnRlcmR1bSBlbGVpZmVuZCwgdmVsaXQgbmliaCBoZW5kcmVyaXQgaXBzdW0sIGFjIGFjY3Vtc2FuIGF1Z3VlIGVyYXQgdmVsIHNhcGllbi4gTWF1cmlzIHNlZCBuaXNpIHF1aXMgYW50ZSBwcmV0aXVtIGRpY3R1bS4gSW50ZWdlciBldCBsZWN0dXMgYXQgbGVvIGludGVyZHVtIHNvZGFsZXMgZWdldCBub24gdmVsaXQuCgpVdCBpZCBzYXBpZW4gYXQgbG9yZW0gZmV1Z2lhdCB1bHRyaWNpZXMgZGljdHVtIGFkaXBpc2Npbmcgb2Rpby4gVmVzdGlidWx1bSBsYWNpbmlhIGVyb3MgcXVpcyBuaXNpIG1vbGxpcyB2ZWhpY3VsYS4gVmVzdGlidWx1bSBhbnRlIGlwc3VtIHByaW1pcyBpbiBmYXVjaWJ1cyBvcmNpIGx1Y3R1cyBldCB1bHRyaWNlcyBwb3N1ZXJlIGN1YmlsaWEgQ3VyYWU7IEZ1c2NlIGNvbnNlcXVhdCwgZXN0IG5lYyB2dWxwdXRhdGUgaW50ZXJkdW0sIGVzdCBuaWJoIHBvcnRhIHNlbSwgdXQgcGhhcmV0cmEgYW50ZSBsb3JlbSBldCBsaWJlcm8uIFF1aXNxdWUgdmVzdGlidWx1bSBtYXVyaXMgbm9uIGZlbGlzIGVsZWlmZW5kIHBlbGxlbnRlc3F1ZS4gRnVzY2UgdHJpc3RpcXVlIGp1c3RvIHNpdCBhbWV0IGxlY3R1cyBpbXBlcmRpZXQgbG9ib3J0aXMuIFZlc3RpYnVsdW0gbGVjdHVzIHF1YW0sIGdyYXZpZGEgc2VkIGZlcm1lbnR1bSBuZWMsIGNvbnNlcXVhdCBlZ2V0IHF1YW0uIEN1bSBzb2NpaXMgbmF0b3F1ZSBwZW5hdGlidXMgZXQgbWFnbmlzIGRpcyBwYXJ0dXJpZW50IG1vbnRlcywgbmFzY2V0dXIgcmlkaWN1bHVzIG11cy4gTnVsbGEgZmFjaWxpc2kuIEluIGNvbnNlcXVhdCBibGFuZGl0IHRlbGx1cyB1dCBzb2xsZXQgbmV0dXMgZXQgbWFsZXN1YWRhIGZhbWVzIGFjIHR1cnBpcyBlZ2VzdGFzLgoKRHVpcyBiaWJlbmR1bSBpbXBlcmRpZXQgbGliZXJvIHZlbCBwb3J0YS4gUXVpc3F1ZSBub24gbmVxdWUgc2VkIHF1YW0gdWxsYW1jb3JwZXIgZnJpbmdpbGxhLiBQcm9pbiBmZWxpcyBtYXVyaXMsIGFjY3Vt"; + +#define DELIM "&;" + +#define ASNLIST_LENGTH 64 + +#if AP_SERVER_MAJORVERSION_NUMBER > 2 || \ + (AP_SERVER_MAJORVERSION_NUMBER == 2 && AP_SERVER_MINORVERSION_NUMBER >= 4) +#define CLIENT_IP(conn) ((conn)->client_ip) +#else +#define CLIENT_IP(conn) ((conn)->remote_ip) +#endif + +typedef struct mod_ip_request_t +{ + apr_int64_t size; + apr_byte_t getasn; + char *callback; + char *testip; +} mod_ip_request_t; + + +typedef struct mod_ip_prefixes_t +{ + apr_ipsubnet_t *ip; + char *provider; + void *next; +} mod_ip_prefixes_t; + +mod_ip_prefixes_t *mod_ip_prefix_head = NULL; + + + +typedef struct +{ + apr_ipsubnet_t *cidr; + char *info; +} cidrinfo_type; + +typedef struct +{ + apr_array_header_t *cidrinfo; + apr_int64_t max_padding; +} mod_ip_svr_cfg; + + +static void *mod_ip_create_svr_conf (apr_pool_t * pool, char *x); +static void *mod_ip_merge_svr_conf (apr_pool_t * pool, void *BASE, void *ADD); +static void mod_ip_hooks (apr_pool_t * pool); +static const char *mod_ip_config_subnet (cmd_parms * cmd, void *CFG, const char *arg1, const char *arg2); +static const char *mod_ip_max_padding(cmd_parms * cmd, void *CFG, const char *arg); +module AP_MODULE_DECLARE_DATA mod_ip_module; + + +/* Parse form data from a string. The input string is NOT preserved. */ +static mod_ip_request_t * +parse_form_from_string (request_rec * r, char *args, mod_ip_request_t * formdata) +{ + char *pair; + char *eq; + char *last = NULL; + mod_ip_svr_cfg *cfg = ap_get_module_config (r->server->module_config, &mod_ip_module); + + /* Sanity check */ + if (formdata == NULL) { + return NULL; + } + /* Defaults */ + formdata->size = 0; + formdata->callback = NULL; + formdata->testip = NULL; + + /* No arguments? No parsing! */ + if (args == NULL) { + return formdata; + } + /* Split the input on '&' */ + for (pair = apr_strtok (args, DELIM, &last); pair != NULL; pair = apr_strtok (NULL, DELIM, &last)) { + + /* Unescape '+' to ' ' */ + for (eq = pair; *eq; ++eq) { + if (*eq == '+') { + *eq = ' '; + } + } + + if (strncmp (pair, "size=", 5) == 0) { + formdata->size = apr_atoi64 (pair + 5); + if (formdata->size > cfg->max_padding) { + ap_log_rerror (APLOG_MARK, APLOG_DEBUG | APLOG_ERR, 0, + r, "[mod_ip.c] size: `%lld' >= max_padding_size `%d'", (long long) formdata->size, + MAX_PADDING_SIZE); + return NULL; + } + } else if (strncmp (pair, "asn=", 4) == 0) { + formdata->getasn = (apr_atoi64 (pair + 4) > 0); + } else if (strncmp (pair, "callback=", 9) == 0) { + if (strlen (pair + 9) > MAX_CALLBACK_SIZE) { + /* Max function name length */ + ap_log_rerror (APLOG_MARK, APLOG_DEBUG | APLOG_ERR, 0, + r, + "[mod_ip.c] function name length: `%s' (`%zu') >= max_callback_size `%d'", + pair + 9, strlen (pair + 9), MAX_CALLBACK_SIZE); + return NULL; + } + ap_unescape_url (pair + 9); + ap_log_rerror (APLOG_MARK, APLOG_DEBUG | APLOG_DEBUG, 0, r, "[mod_ip.c] checking function name: `%s'", + pair + 9); + if (strcmp (pair + 9, "?") != 0) { /* If callback=? then use plain_text */ + for (eq = pair + 9; *eq; ++eq) { + if (!isalnum (*eq) && (*eq != '_')) { + /* Callback function name violation */ + ap_log_rerror (APLOG_MARK, APLOG_DEBUG | APLOG_ERR, 0, + r, "[mod_ip.c] function name: `%s' violates naming convention: `%c'", + pair + 9, *eq); + } + } + formdata->callback = apr_pstrdup (r->pool, (const char *) pair + 9); + } + } else if (strncmp (pair, "testip=", 7) == 0) { + if (strlen (pair + 7) > MAX_TESTIP_SIZE) { + ap_log_rerror (APLOG_MARK, APLOG_DEBUG | APLOG_ERR, 0, + r, + "[mod_ip.c] function name length: `%s' (`%zu') >= max_callback_size `%d'", + pair + 9, strlen (pair + 9), MAX_CALLBACK_SIZE); + return NULL; + + } + for (eq = pair + 7; *eq; ++eq) { + if (!isalnum (*eq) && (*eq != ':') && (*eq != '.')) { + /* Callback function name violation */ + ap_log_rerror (APLOG_MARK, APLOG_DEBUG | APLOG_ERR, 0, + r, "[mod_ip.c] testip value: `%s' violates naming convention: `%c'", + pair + 7, *eq); + return NULL; + } + } + formdata->testip = apr_pstrdup (r->pool, (const char *) pair + 7); + } + } + + return formdata; +} + +static mod_ip_request_t * +parse_form_from_GET (request_rec * r, mod_ip_request_t * formdata) +{ + return parse_form_from_string (r, r->args, formdata); +} + + + +static char * +escape_string(request_rec * r, char *str) +{ + char *newstr = NULL; + char *source = NULL; + char *dest = NULL; + char *maxdest = NULL; + int l; + + if (str == NULL) { + return ""; + } + /* We might potentially escape EVERY character. Worst case. */ + l = strlen(str); + newstr = apr_palloc(r->pool, l * 2 + 2); + + source = str; + dest = newstr; + maxdest = newstr + l * 2; + + while ((*source) && (dest < maxdest)) { + switch (*source) { + case '"': + *(dest++) = '\\'; + *(dest++) = '\"'; + source++; + break; + case '\r': + *(dest++) = '\\'; + *(dest++) = 'r'; + source++; + break; + case '\n': + *(dest++) = '\\'; + *(dest++) = 'n'; + source++; + break; + case '\b': + *(dest++) = '\\'; + *(dest++) = 'b'; + source++; + break; + default: + *(dest++) = *(source++); + } + } + *dest = '\0'; + return newstr; +} + +char * +mod_ip_find_info (request_rec * r, char *myip) +{ + int i = 0; + apr_array_header_t *arr; + mod_ip_svr_cfg *cfg = ap_get_module_config (r->server->module_config, &mod_ip_module); + apr_sockaddr_t *sockaddr = NULL; + apr_status_t rv; + cidrinfo_type *cidrinfo = NULL; + apr_ipsubnet_t *cidr; + char *info; + + + if ((cfg) && (cfg->cidrinfo) && ((rv = apr_sockaddr_info_get (&sockaddr, myip, AF_INET6, 0, 0, r->pool)) == APR_SUCCESS)) { + arr = cfg->cidrinfo; /* apr_array_header_t */ +/* cidrinfo = (cidrinfo_type**) arr->elts;*/ + for (i = 0; i < arr->nelts; i++) { + + + cidrinfo = &((cidrinfo_type*) arr->elts)[i]; + cidr = cidrinfo->cidr; + info = cidrinfo->info; + + if (apr_ipsubnet_test (cidr, sockaddr)) { + return info; + } + } + } + + return ""; +} + +void +gen_output (request_rec * r, struct mod_ip_request_t *formdata) +{ + size_t output_len; + char *padding = ",\"padding\":\""; + char *p = NULL; + int added = 0; + char *myip = CLIENT_IP(r->connection); + char *mytype = "ipv4"; + char *mysubtype = ""; + char *VIA = ""; + char *asnlist = NULL; + + + + /* Identify X-Forwarded-For; include in results after stripping characters. */ + VIA = escape_string (r, (char *) apr_table_get (r->headers_in, "Via")); + + if (!myip) + myip = "0.0.0.0 undefined"; + if (formdata->testip) + myip = formdata->testip; + + /* If Teredo or 6to4, don't do ASN lookups. The data will always show *some* ISP, but it isn't ours.*/ + if ((strncmp(myip,"2001:0:",7)==NULL) || (strncmp(myip,"2002:",5)==NULL)) { + formdata->getasn=0; + } + + + if (strchr (myip, ':')) { + mytype = "ipv6"; + mysubtype = mod_ip_find_info(r,myip); + } + /* Start generating a reply */ + + p = apr_psprintf (r->pool, + "%s({\"ip\":\"%s\",\"type\":\"%s\",\"subtype\":\"%s\",\"via\":\"%s\"", + formdata->callback ? formdata->callback : "callback", myip, mytype, mysubtype, VIA); + ap_rputs(p, r); + output_len = strlen (p) + 4; + + int my_asn ; + char * asn_name ; + + if (formdata->getasn) { + my_asn = GetASN(r, myip, &asnlist); + if (my_asn > 0) { + p = apr_psprintf (r->pool, ",\"asn\":\"%ld\"", my_asn); + ap_rputs(p, r); + output_len += strlen (p) ; + asn_name = apr_palloc(r->pool, 64) ; + if ((asn_name != NULL) && (GetASNName(my_asn, asn_name, 64) >= 0)) { + p = apr_psprintf (r->pool, ",\"asn_name\":\"%s\"", asn_name); + ap_rputs(p, r); + output_len += strlen (p) ; + } + } + if ((asnlist) && (asnlist[0])) { + p = apr_psprintf(r->pool, ",\"asnlist\":\"%s\"", asnlist); + ap_rputs(p, r); + output_len += strlen (p) ; + } + } + + ap_log_rerror (APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, + r, "[mod_ip.c] size: `%lld', output_len: `%zd'", (long long) formdata->size, output_len); + + if (formdata->size > output_len) { + added = formdata->size - output_len - strlen (padding); + if (added < 0) { + added = 0; + } + } else { + added = 0; + } + + ap_log_rerror (APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, "[mod_ip.c] added: `%d', padding: `%s'", added, padding); + + if (padding) { + output_len += strlen (padding) + added; + } + + if (padding) { + ap_rputs(padding, r); + while (added > 0) { + ap_rwrite(padding_buffer, (added > PADDING_BUFFER_SIZE)?PADDING_BUFFER_SIZE:added, r); + added -= PADDING_BUFFER_SIZE; + } + ap_rputc('\"', r); + } + ap_rputs("})\n", r); +} + + +static int +mod_ip_handler (request_rec * r) +{ + mod_ip_request_t *formdata; + + if ((r->handler == NULL) || (strcmp (r->handler, "mod_ip") != 0)) { + return DECLINED; + } + + /* Set the headers */ + ap_set_content_type(r,"text/javascript;charset=UTF-8"); + apr_table_set(r->headers_out,"Cache-Control","no-cache"); + apr_table_set(r->headers_out,"Pragma","no-cache"); + apr_table_set(r->headers_out,"Expires","Thu, 01 Jan 1971 00:00:00 GMT"); + apr_table_set(r->headers_out,"X-Mod-Ip",PACKAGE_VERSION); + + + /* pcalloc sets memory to '\0' */ + formdata = (mod_ip_request_t *) apr_pcalloc (r->pool, sizeof (mod_ip_request_t)); + + /* GET requests: parse data. Else, abort. */ + if (r->method_number != M_GET) { + return HTTP_METHOD_NOT_ALLOWED; + } + formdata = parse_form_from_GET (r, formdata); + if (formdata == NULL) { + ap_rputs ("<p>Error reading form data!</p>", r); + /* Bad user. No cookie. */ + return HTTP_BAD_REQUEST; + } + /* Display data. */ + if (formdata->callback == NULL) { + /* interactive user called (no callback specified) */ + ap_set_content_type (r, "text/plain;charset=UTF-8"); + } else { + /* jsonp called */ + ap_set_content_type (r, "application/javascript;charset=UTF-8"); + } + + /* Report to the user their IP */ + /* ap_rputs (gen_output (r, formdata), r); */ + gen_output(r, formdata); + + /* ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Survived line %u", __LINE__); */ + + return OK; +} + + + +/* + * ################################################################ + * # Apache config file parsing # + * ################################################################ + */ + + +static const command_rec mod_ip_cmds[] = { + AP_INIT_TAKE2 ("mod_ip_prefix", mod_ip_config_subnet, NULL, OR_ALL, + "Define a known subnet"), + AP_INIT_TAKE1 ("mod_ip_max_padding", mod_ip_max_padding, NULL, OR_ALL, + "Define maximum padding size"), + {NULL} +}; + +module AP_MODULE_DECLARE_DATA mod_ip_module = { + STANDARD20_MODULE_STUFF, + NULL, /* Per-directory configuration handler */ + NULL, /* Merge handler for per-directory configurations */ + mod_ip_create_svr_conf, /* Per-server configuration handler */ + mod_ip_merge_svr_conf, /* Merge handler for per-server configurations */ + mod_ip_cmds, /* Any directives we may have for httpd */ + mod_ip_hooks /* Our hook registering function */ +}; + + + + + +static const char * +mod_ip_config_subnet_parsed (apr_pool_t * pool, void *CFG, const char *arg1, const char *arg2) +{ +// asm("int3"); /* DEBUGGER */ + + char *where = NULL; + char *bits = NULL; + apr_status_t rv; + mod_ip_svr_cfg *cfg = CFG; + + if (!cfg) { + return "Failed to get config pointer"; + } + + + cidrinfo_type *newelt = apr_array_push (cfg->cidrinfo); + newelt->info = apr_pstrdup (pool, arg2); + + + where = apr_pstrdup (pool, arg1); + if ((bits = ap_strchr (where, '/')) != NULL) { + *bits++ = '\0'; + } + rv = apr_ipsubnet_create (&newelt->cidr, where, bits, pool); + if (rv != APR_SUCCESS) { + return ("mod_ip_prefix: Bad prefix"); + } + if (!newelt->cidr) { + return "mod_ip_prefix: Bad prefix"; + } + + + return (NULL); +}; + + +static const char * +mod_ip_config_subnet (cmd_parms * cmd, void *CFG, const char *arg1, const char *arg2) +{ + /* See Section 9.4.1 Configuration functions */ +// asm("int3"); /* DEBUGGER */ + + apr_pool_t *pool = cmd->pool; + + server_rec *s = cmd->server; + mod_ip_svr_cfg *cfg = ap_get_module_config (s->module_config, &mod_ip_module); + + if (!cfg) { + return "Failed to get config pointer"; + } + + return mod_ip_config_subnet_parsed(pool,cfg,arg1,arg2); +}; + + +void * +mod_ip_create_svr_conf (apr_pool_t * pool, char *x) +{ + mod_ip_svr_cfg *svr = apr_pcalloc (pool, sizeof (mod_ip_svr_cfg)); + svr->cidrinfo = apr_array_make (pool, MAX_PREFIXES, sizeof (cidrinfo_type)); + svr->max_padding = PADDING_DEFAULT_SIZE; + + mod_ip_config_subnet_parsed(pool,svr,"2001:0::/32","Teredo"); + mod_ip_config_subnet_parsed(pool,svr,"2002::/16","6to4"); + return svr; +} + + + + +static const char * +mod_ip_max_padding(cmd_parms * cmd, void *CFG, const char *arg) +{ + apr_int64_t padding; + server_rec *s = cmd->server; + mod_ip_svr_cfg *cfg = ap_get_module_config (s->module_config, &mod_ip_module); + char buf[120]; + + padding = apr_atoi64(arg); + + if (padding < 1600) + return apr_pstrcat(cmd->temp_pool, "mod_ip_max_padding \"", arg, + "\" must be a greater than 1600", + NULL); + + if (padding > MAX_PADDING_SIZE) + return apr_pstrcat(cmd->temp_pool, "mod_ip_max_padding \"", arg, + "\" too large (>", apr_ltoa(cmd->temp_pool, MAX_PADDING_SIZE), ")", + NULL); + + if (padding < 0) + return apr_pstrcat(cmd->temp_pool, "mod_ip_max_padding \"", arg, + "\" must be a non-negative integer", + NULL); + + cfg->max_padding = padding; + + return (NULL); +} + +static void * +mod_ip_merge_svr_conf (apr_pool_t * pool, void *BASE, void *ADD) +{ + mod_ip_svr_cfg *base = (mod_ip_svr_cfg *) BASE; + mod_ip_svr_cfg *add = (mod_ip_svr_cfg *) ADD; + mod_ip_svr_cfg *conf = apr_palloc (pool, sizeof (mod_ip_svr_cfg)); + /* With an APR data type we can delegate all the real work to APR */ + conf->cidrinfo = apr_array_append (pool, base->cidrinfo, add->cidrinfo); + conf->max_padding = (base->max_padding > add->max_padding)?base->max_padding:add->max_padding; + return conf; +}; + + + + + +/* Hook our handler into Apache at startup */ +static void +mod_ip_hooks (apr_pool_t * pool) +{ + ap_hook_handler (mod_ip_handler, NULL, NULL, APR_HOOK_MIDDLE); +} |