diff --git a/ChangeLog b/ChangeLog index 56d2b1a..78d2294 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +Version 1.94b: +-------------- + + - Proxy support! Currently only works for HTTP, put behind #ifdef + PROXY_SUPPORT. + + - Change to prefix() and change_prefix() macros to limit the risk of bugs. + Version 1.93b: -------------- diff --git a/Makefile b/Makefile index 138f6dc..0544162 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ # PROGNAME = skipfish -VERSION = 1.93b +VERSION = 1.94b OBJFILES = http_client.c database.c crawler.c analysis.c report.c INCFILES = alloc-inl.h string-inl.h debug.h types.h http_client.h \ diff --git a/README b/README index 57abb62..f2de531 100644 --- a/README +++ b/README @@ -475,24 +475,24 @@ Below is a list of features currently missing in skipfish. If you wish to improve the tool by contributing code in one of these areas, please let me know: - * Buffer overflow checks: after careful consideration, I suspect there is - no reliable way to test for buffer overflows remotely. Much like the actual + * Buffer overflow checks: after careful consideration, I suspect there is + no reliable way to test for buffer overflows remotely. Much like the actual fault condition we are looking for, proper buffer size checks may also result in uncaught exceptions, 500 messages, etc. I would love to be proved wrong, though. - * Fully-fledged JavaScript XSS detection: several rudimentary checks are - present in the code, but there is no proper script engine to evaluate - expressions and DOM access built in. + * Fully-fledged JavaScript XSS detection: several rudimentary checks are + present in the code, but there is no proper script engine to evaluate + expressions and DOM access built in. - * Variable length encoding character consumption / injection bugs: these + * Variable length encoding character consumption / injection bugs: these problems seem to be largely addressed on browser level at this point, so - they were much lower priority at the time of this writing. + they were much lower priority at the time of this writing. - * Security checks and link extraction for third-party, plugin-based + * Security checks and link extraction for third-party, plugin-based content (Flash, Java, PDF, etc). - * Password brute-force and numerical filename brute-force probes. + * Password brute-force and numerical filename brute-force probes. * Search engine integration (vhosts, starting paths). @@ -500,11 +500,11 @@ know: * NTLM and digest authentication. - * More specific PHP tests (eval injection, RFI). + * More specific PHP tests (eval injection, RFI). - * Proxy support: somewhat incompatible with performance control features - currently employed by skipfish; but in the long run, should be provided - as a last-resort option. + * Proxy support: an experimental HTTP proxy support is available through + a #define directive in config.h. Adding support for HTTPS proxying is + more complicated, and still in the works. * Scan resume option. diff --git a/analysis.c b/analysis.c index 2b14c8a..882205f 100644 --- a/analysis.c +++ b/analysis.c @@ -80,7 +80,7 @@ void pivot_header_checks(struct http_request* req, for (i=0;ihdr.c;i++) { if (res->hdr.t[i] != PARAM_HEADER || - strncasecmp((char*)res->hdr.n[i], "X-", 2)) continue; + case_prefix(res->hdr.n[i], "X-")) continue; if (!RPAR(req)->res) par_hdr = NULL; else par_hdr = GET_HDR(res->hdr.n[i], &RPAR(req)->res->hdr); @@ -96,7 +96,7 @@ void pivot_header_checks(struct http_request* req, for (i=0;ires->hdr.c;i++) { if (RPAR(req)->res->hdr.t[i] != PARAM_HEADER || - strncasecmp((char*)RPAR(req)->res->hdr.n[i], "X-", 2)) continue; + case_prefix(RPAR(req)->res->hdr.n[i], "X-")) continue; cur_hdr = GET_HDR(RPAR(req)->res->hdr.n[i], &res->hdr); @@ -122,10 +122,10 @@ static void test_add_link(u8* str, struct http_request* ref, /* Don't add injected links. */ - if (!strncasecmp((char*)str, "skipfish:", 9) || - !strncasecmp((char*)str, "//skipfish.invalid/", 19) || + if (!case_prefix(str, "skipfish:") || + !case_prefix(str, "//skipfish.invalid/") || inl_strcasestr(str, (u8*) "/" BOGUS_FILE) || - !strncasecmp((char*)str, "http://skipfish.invalid/", 24)) return; + !case_prefix(str, "http://skipfish.invalid/")) return; /* Don't add links that look like they came from JS code with fragmented HTML snippets, etc. */ @@ -136,7 +136,7 @@ static void test_add_link(u8* str, struct http_request* ref, if ((str[0] == '\'' || str[0] == '"') && (str[1] == '+' || str[1] == ' ')) return; - if (!strncasecmp((char*)str, "mailto:", 7)) { + if (!case_prefix(str, "mailto:")) { if (log_ext_urls) { u8* qmark = (u8*)strchr((char*)str, '?'); @@ -262,19 +262,19 @@ static u8* html_decode_param(u8* url, u8 also_js) { if (url[i] == '&') { - if (!strncasecmp((char*)url + i + 1, "amp;", 4)) { + if (!case_prefix(url + i + 1, "amp;")) { ret[pos++] = '&'; i += 4; continue; - } else if (!strncasecmp((char*)url + i + 1, "quot;", 5)) { + } else if (!case_prefix(url + i + 1, "quot;")) { ret[pos++] = '\''; i += 5; continue; - } else if (!strncasecmp((char*)url + i + 1, "lt;", 3)) { + } else if (!case_prefix(url + i + 1, "lt;")) { ret[pos++] = '<'; i += 3; continue; - } else if (!strncasecmp((char*)url + i + 1, "gt;", 3)) { + } else if (!case_prefix(url + i + 1, "gt;")) { ret[pos++] = '>'; i += 3; continue; @@ -398,8 +398,7 @@ static u8 maybe_xsrf(u8* token) { /* Unix time is not a valid token. */ - if (!strncasecmp((char*)token, (char*)tm_prefix, strlen((char*)tm_prefix))) - return 0; + if (!case_prefix(token, tm_prefix)) return 0; tmp = token; while (*tmp && (isdigit(*tmp) || strchr("abcdef", tolower(*tmp)))) { @@ -464,7 +463,7 @@ static void collect_form_data(struct http_request* req, cur_str++; *tag_end = 0; - if (!strncasecmp((char*)cur_str, "/form", 5)) { + if (!case_prefix(cur_str, "/form")) { *tag_end = '>'; goto final_checks; } @@ -909,8 +908,7 @@ next_tag: We do not make assumptins about syntax such as /foo/, though, as it could very well be a regex in a JS block. */ - if (!strncmp((char*)clean_url, "./", 2) || !strncmp((char*)clean_url, - "../", 3)) { + if (!prefix(clean_url, "./") || !prefix(clean_url, "../")) { add_link: test_add_link(clean_url, base ? base : req, res, 0, 0); goto url_done; @@ -921,7 +919,7 @@ add_link: while (clean_url[lead] && (isalnum(clean_url[lead]))) lead++; - if (lead && !strncmp((char*)clean_url + lead, "://", 3) && + if (lead && !prefix(clean_url + lead, "://") && clean_url[lead + 3]) goto add_link; /* URL CHECK 3: If the result ends with ., @@ -1047,7 +1045,7 @@ static u8 is_css(struct http_response* res) { /* Skip HTML, CSS comments. */ - if (!strncmp((char*)text, ""); if (next) { tmp = next + 3; @@ -1644,7 +1642,7 @@ void content_checks(struct http_request* req, struct http_response* res) { strcasecmp((char*)tag_name, "input")) || !strcasecmp((char*)param_name, "codebase")) && clean_val) { - if (!strncasecmp((char*)clean_val, "skipfish://", 11)) + if (!case_prefix(clean_val, "skipfish:")) problem(PROB_URL_XSS, req, res, tag_name, req->pivot, 0); /* A bit hairy, but in essence, links to attacker-supplied @@ -1652,8 +1650,8 @@ void content_checks(struct http_request* req, struct http_response* res) { are sorta noteworthy, depending on context; and A links are usually of little relevance. */ - if (!strncasecmp((char*)clean_val, "http://skipfish.invalid/", 24) || - !strncasecmp((char*)clean_val, "//skipfish.invalid/", 19)) { + if (!case_prefix(clean_val, "http://skipfish.invalid/") || + !case_prefix(clean_val, "//skipfish.invalid/")) { if (!strcasecmp((char*)tag_name, "script") || !strcasecmp((char*)tag_name, "link")) @@ -1679,14 +1677,14 @@ void content_checks(struct http_request* req, struct http_response* res) { url += 4; if (*url == '\'' || *url == '"') { url++; semi_safe = 1; } - if (!strncasecmp((char*)url, "http://skipfish.invalid/", 24) || - !strncasecmp((char*)url, "//skipfish.invalid/", 19)) + if (!case_prefix(url, "http://skipfish.invalid/") || + !case_prefix(url, "//skipfish.invalid/")) problem(PROB_URL_REDIR, req, res, (u8*)"injected URL in META refresh", req->pivot, 0); /* Unescaped semicolon in Refresh headers is unsafe with MSIE6. */ - if (!strncasecmp((char*)url, "skipfish://", 11) || + if (!case_prefix(url, "skipfish:") || (!semi_safe && strchr((char*)url, ';'))) problem(PROB_URL_XSS, req, res, (u8*)"injected URL in META refresh", req->pivot, 0); @@ -1705,7 +1703,7 @@ void content_checks(struct http_request* req, struct http_response* res) { /* CHECK 3.3: JavaScript on*=, CSS style= parameters. */ - if ((!strncasecmp((char*)param_name, "on", 2) || + if ((!case_prefix(param_name, "on") || !strcasecmp((char*)param_name, "style")) && clean_val) check_js_xss(req, res, clean_val); @@ -1941,19 +1939,19 @@ static void detect_mime(struct http_request* req, struct http_response* res) { } - if (!strncmp((char*)sniffbuf, "%!PS", 4)) { + if (!prefix(sniffbuf, "%!PS")) { res->sniff_mime_id = MIME_ASC_POSTSCRIPT; return; } - if (!strncmp((char*)sniffbuf, "{\\rtf", 5)) { + if (!prefix(sniffbuf, "{\\rtf")) { res->sniff_mime_id = MIME_ASC_RTF; return; } /* Adobe PDF (may be mostly ASCII in some cases). */ - if (!strncmp((char*)sniffbuf, "%PDF", 4)) { + if (!prefix(sniffbuf, "%PDF")) { res->sniff_mime_id = MIME_EXT_PDF; return; } @@ -2058,29 +2056,29 @@ static void detect_mime(struct http_request* req, struct http_response* res) { return; } - if (!strncmp((char*)sniffbuf, "GIF8", 4)) { + if (!prefix(sniffbuf, "GIF8")) { res->sniff_mime_id = MIME_IMG_GIF; return; } - if (sniffbuf[0] == 0x89 && !strncmp((char*)sniffbuf + 1, "PNG", 3)) { + if (sniffbuf[0] == 0x89 && !prefix(sniffbuf + 1, "PNG")) { res->sniff_mime_id = MIME_IMG_PNG; return; } - if (!strncmp((char*)sniffbuf, "BM", 2)) { + if (!prefix(sniffbuf, "BM")) { res->sniff_mime_id = MIME_IMG_BMP; return; } - if (!strncmp((char*)sniffbuf, "II", 2) && sniffbuf[2] == 42 /* dec */) { + if (!prefix(sniffbuf, "II") && sniffbuf[2] == 42 /* dec */) { res->sniff_mime_id = MIME_IMG_TIFF; return; } /* Next: RIFF containers (AVI, ANI, WAV). */ - if (!strncmp((char*)sniffbuf, "RIFF", 4)) { + if (!prefix(sniffbuf, "RIFF")) { if (sniffbuf[8] == 'A') { if (sniffbuf[9] == 'C') @@ -2123,27 +2121,27 @@ static void detect_mime(struct http_request* req, struct http_response* res) { return; } - if (!strncasecmp((char*)sniffbuf, "OggS", 4)) { + if (!prefix(sniffbuf, "OggS")) { res->sniff_mime_id = MIME_AV_OGG; return; } - if (sniffbuf[0] == 0x28 && !strncasecmp((char*)sniffbuf + 1, "RMF", 3)) { + if (sniffbuf[0] == 0x28 && !prefix(sniffbuf + 1, "RMF")) { res->sniff_mime_id = MIME_AV_RA; return; } - if (sniffbuf[0] == 0x2E && !strncasecmp((char*)sniffbuf + 1, "RMF", 3)) { + if (sniffbuf[0] == 0x2E && !prefix(sniffbuf + 1, "RMF")) { res->sniff_mime_id = MIME_AV_RV; return; } - if (!strncmp((char*)sniffbuf + 4, "free", 4) || - !strncmp((char*)sniffbuf + 4, "mdat", 4) || - !strncmp((char*)sniffbuf + 4, "wide", 4) || - !strncmp((char*)sniffbuf + 4, "pnot", 4) || - !strncmp((char*)sniffbuf + 4, "skip", 4) || - !strncmp((char*)sniffbuf + 4, "moov", 4)) { + if (!prefix(sniffbuf + 4, "free") || + !prefix(sniffbuf + 4, "mdat") || + !prefix(sniffbuf + 4, "wide") || + !prefix(sniffbuf + 4, "pnot") || + !prefix(sniffbuf + 4, "skip") || + !prefix(sniffbuf + 4, "moov")) { /* Oookay, that was weird... */ res->sniff_mime_id = MIME_AV_QT; return; @@ -2151,20 +2149,20 @@ static void detect_mime(struct http_request* req, struct http_response* res) { /* Flash and FLV. */ - if (!strncmp((char*)sniffbuf, "FLV", 3)) { + if (!prefix(sniffbuf, "FLV")) { res->sniff_mime_id = MIME_AV_FLV; return; } - if (!strncmp((char*)sniffbuf, "FCWS", 4) || - !strncmp((char*)sniffbuf, "CWS", 3)) { + if (!prefix(sniffbuf, "FCWS") || + !prefix(sniffbuf, "CWS")) { res->sniff_mime_id = MIME_EXT_FLASH; return; } /* Adobe PDF. */ - if (!strncmp((char*)sniffbuf, "%PDF", 4)) { + if (!prefix(sniffbuf, "%PDF")) { res->sniff_mime_id = MIME_EXT_PDF; return; } @@ -2172,7 +2170,7 @@ static void detect_mime(struct http_request* req, struct http_response* res) { /* JAR versus ZIP. A bit tricky, because, well, they are both just ZIP archives. */ - if (!strncmp((char*)sniffbuf, "PK", 2) && + if (!prefix(sniffbuf, "PK") && sniffbuf[2] < 6 && sniffbuf[3] < 7) { if (inl_memmem(res->payload, res->pay_len, "META-INF/", 9)) @@ -2429,7 +2427,7 @@ static void check_for_stuff(struct http_request* req, /* This should also cover most cases of Perl, Python, etc. */ - if (!strncmp((char*)sniffbuf, "#!/", 3)) { + if (!prefix(sniffbuf, "#!/")) { problem(PROB_FILE_POI, req, res, (u8*)"shell script", req->pivot, 0); return; } diff --git a/config.h b/config.h index 5bbf921..32f3aa5 100644 --- a/config.h +++ b/config.h @@ -27,6 +27,12 @@ #define SHOW_SPLASH 1 /* Annoy user with a splash screen */ +/* Define this to enable experimental HTTP proxy support, through the -J + option in the command line. This mode will not work as expected for + HTTPS requests at this point. */ + +// #define PROXY_SUPPORT 1 + /* Default paths to runtime files: */ #define ASSETS_DIR "assets" diff --git a/crawler.c b/crawler.c index ca41a92..9ca8888 100644 --- a/crawler.c +++ b/crawler.c @@ -980,12 +980,12 @@ static u8 inject_check5_callback(struct http_request* req, if (val) { - if (!strncasecmp((char*)val, "http://skipfish.invalid/", 24) || - !strncasecmp((char*)val, "//skipfish.invalid/", 19)) + if (!case_prefix(val, "http://skipfish.invalid/") || + !case_prefix(val, "//skipfish.invalid/")) problem(PROB_URL_REDIR, req, res, (u8*)"injected URL in 'Location' header", req->pivot, 0); - if (!strncasecmp((char*)val, "skipfish://", 11)) + if (!case_prefix(val, "skipfish:")) problem(PROB_URL_XSS, req, res, (u8*)"injected URL in 'Location' header", req->pivot, 0); @@ -998,14 +998,14 @@ static u8 inject_check5_callback(struct http_request* req, if (*val == '\'' || *val == '"') { val++; semi_safe++; } - if (!strncasecmp((char*)val, "http://skipfish.invalid/", 24) || - !strncasecmp((char*)val, "//skipfish.invalid/", 19)) + if (!case_prefix(val, "http://skipfish.invalid/") || + !case_prefix(val, "//skipfish.invalid/")) problem(PROB_URL_REDIR, req, res, (u8*)"injected URL in 'Refresh' header", req->pivot, 0); /* Unescaped semicolon in Refresh headers is unsafe with MSIE6. */ - if (!strncasecmp((char*)val, "skipfish://", 11) || + if (!case_prefix(val, "skipfish:") || (!semi_safe && strchr((char*)val, ';'))) problem(PROB_URL_XSS, req, res, (u8*)"injected URL in 'Refresh' header", req->pivot, 0); @@ -2821,7 +2821,7 @@ static u8 dir_dict_callback(struct http_request* req, /* Do not add 403 responses to .ht* requests - workaround for Apache filtering to keep reports clean. */ - if (lp && !strncmp((char*)lp,".ht",3)) + if (lp && !prefix(lp, ".ht")) i = ~req->pivot->r404_cnt; /* If not 404, do response, and does not look like diff --git a/http_client.c b/http_client.c index 891ff2b..fcebb4e 100644 --- a/http_client.c +++ b/http_client.c @@ -86,6 +86,12 @@ u64 bytes_sent, u8 *auth_user, *auth_pass; +#ifdef PROXY_SUPPORT +u8* use_proxy; +u32 use_proxy_addr; +u16 use_proxy_port; +#endif /* PROXY_SUPPORT */ + u8 ignore_cookies; /* Internal globals for queue management: */ @@ -187,10 +193,10 @@ u8 parse_url(u8* url, struct http_request* req, struct http_request* ref) { if (maybe_proto && url[maybe_proto] == ':') { - if (!strncasecmp((char*)url, "http:", 5)) { + if (!case_prefix(url, "http:")) { req->proto = PROTO_HTTP; cur += 5; - } else if (!strncasecmp((char*)url, "https:", 6)) { + } else if (!case_prefix(url, "https:")) { req->proto = PROTO_HTTPS; cur += 6; } else return 1; @@ -509,12 +515,12 @@ void tokenize_path(u8* str, struct http_request* req, u8 add_slash) { probes. This is to avoid recursion if it actually worked in some way. */ - if (!strncmp((char*)cur, "/\\.\\", 4) && (cur[4] == '/' || !cur[4])) { + if (!prefix(cur, "/\\.\\") && (cur[4] == '/' || !cur[4])) { cur += 4; continue; } - if (!strncasecmp((char*)cur, "/%5c.%5c", 8) && + if (!case_prefix(cur, "/%5c.%5c") && (cur[8] == '/' || !cur[8])) { cur += 8; continue; @@ -758,6 +764,39 @@ u32 maybe_lookup_host(u8* name) { u32 ret_addr = 0; struct in_addr in; +#ifdef PROXY_SUPPORT + + /* If configured to use proxy, look up proxy IP once; and return that + address for all host names. */ + + if (use_proxy) { + + if (!use_proxy_addr) { + + /* Don't bother resolving raw IP addresses, naturally. */ + + if (inet_aton((char*)use_proxy, &in)) + return (use_proxy_addr = (u32)in.s_addr); + + h = gethostbyname((char*)use_proxy); + + /* If lookup fails with a transient error, be nice - try again. */ + + if (!h && h_errno == TRY_AGAIN) h = gethostbyname((char*)name); + + if (!h || !(use_proxy_addr = *(u32*)h->h_addr_list[0])) + FATAL("Unable to resolve proxy host name '%s'.", use_proxy); + + } + + return use_proxy_addr; + + } + + /* If no proxy... */ + +#endif /* PROXY_SUPPORT */ + /* Don't bother resolving raw IP addresses, naturally. */ if (inet_aton((char*)name, &in)) @@ -848,6 +887,25 @@ u8* build_request_data(struct http_request* req) { if (req->method) ASD(req->method); else ASD((u8*)"GET"); ASD(" "); + +#ifdef PROXY_SUPPORT + + /* For non-CONNECT proxy requests, insert http://host[:port] too. */ + + if (use_proxy && req->proto == PROTO_HTTP) { + ASD("http://"); + ASD(req->host); + + if (req->port != 80) { + char port[7]; + sprintf((char*)port, ":%u", req->port); + ASD(port); + } + + } + +#endif /* PROXY_SUPPORT */ + ASD(path); ASD(" HTTP/1.1\r\n"); ck_free(path); @@ -927,7 +985,6 @@ u8* build_request_data(struct http_request* req) { } - /* Request a limited range up front to minimize unwanted traffic. Note that some Oracle servers apparently fail on certain ranged requests, so allowing -H override seems like a good idea. */ @@ -1224,7 +1281,7 @@ u8 parse_response(struct http_request* req, struct http_response* res, return 1; } - if (strncmp((char*)cur_line, "HTTP/1.", 7)) { + if (prefix(cur_line, "HTTP/1.")) { ck_free(cur_line); return 2; } @@ -1248,7 +1305,7 @@ u8 parse_response(struct http_request* req, struct http_response* res, if (!cur_line[0]) break; - if (!strncasecmp((char*)cur_line, "Content-Length:", 15)) { + if (!case_prefix(cur_line, "Content-Length:")) { /* The value in Content-Length header would be useful for seeing if we have all the requested data already. Reject invalid values to avoid @@ -1261,7 +1318,7 @@ u8 parse_response(struct http_request* req, struct http_response* res, } } else pay_len = -1; - } else if (!strncasecmp((char*)cur_line, "Transfer-Encoding:", 18)) { + } else if (!case_prefix(cur_line, "Transfer-Encoding:")) { /* Transfer-Encoding: chunked must be accounted for to properly determine if we received all the data when Content-Length not found. */ @@ -1271,7 +1328,7 @@ u8 parse_response(struct http_request* req, struct http_response* res, while (isspace(*x)) x++; if (!strcasecmp((char*)x, "chunked")) chunked = 1; - } else if (!strncasecmp((char*)cur_line, "Content-Encoding:", 17)) { + } else if (!case_prefix(cur_line, "Content-Encoding:")) { /* Content-Encoding is good to know, too. */ @@ -1282,7 +1339,7 @@ u8 parse_response(struct http_request* req, struct http_response* res, if (!strcasecmp((char*)x, "deflate") || !strcasecmp((char*)x, "gzip")) compressed = 1; - } else if (!strncasecmp((char*)cur_line, "Connection:", 11)) { + } else if (!case_prefix(cur_line, "Connection:")) { u8* x = cur_line + 11; @@ -1887,7 +1944,12 @@ connect_error: } sin.sin_family = PF_INET; + +#ifdef PROXY_SUPPORT + sin.sin_port = htons(use_proxy ? use_proxy_port : c->port); +#else sin.sin_port = htons(c->port); +#endif /* ^PROXY_SUPPORT */ memcpy(&sin.sin_addr, &q->req->addr, 4); @@ -2053,7 +2115,6 @@ SSL_read_more: c->SSL_rd_w_wr = 0; - read_res = SSL_read(c->srv_ssl, c->read_buf + c->read_len, READ_CHUNK); diff --git a/http_client.h b/http_client.h index a2a3a54..9e0ceca 100644 --- a/http_client.h +++ b/http_client.h @@ -405,6 +405,12 @@ extern u8 auth_type; extern u8 *auth_user, *auth_pass; +#ifdef PROXY_SUPPORT +extern u8* use_proxy; +extern u32 use_proxy_addr; +extern u16 use_proxy_port; +#endif /* PROXY_SUPPORT */ + /* Global HTTP cookies, extra headers: */ extern struct param_array global_http_par; diff --git a/skipfish.c b/skipfish.c index fcd52cc..496bd74 100644 --- a/skipfish.c +++ b/skipfish.c @@ -77,6 +77,9 @@ static void usage(char* argv0) { " -C name=val - append a custom cookie to all requests\n" " -H name=val - append a custom HTTP header to all requests\n" " -b (i|f|p) - use headers consistent with MSIE / Firefox / iPhone\n" +#ifdef PROXY_SUPPORT + " -J proxy - use a specified HTTP proxy server\n" +#endif /* PROXY_SUPPORT */ " -N - do not accept any new cookies\n\n" "Crawl scope options:\n\n" @@ -234,7 +237,8 @@ static void read_urls(u8* fn) { int main(int argc, char** argv) { s32 opt; u32 loop_cnt = 0, purge_age = 0, seed; - u8 dont_save_words = 0, show_once = 0, be_quiet = 0, display_mode = 0; + u8 dont_save_words = 0, show_once = 0, be_quiet = 0, display_mode = 0, + has_fake = 0; u8 *wordlist = (u8*)DEF_WORDLIST, *output_dir = NULL; struct termios term; @@ -254,7 +258,7 @@ int main(int argc, char** argv) { SAY("skipfish version " VERSION " by \n"); while ((opt = getopt(argc, argv, - "+A:F:C:H:b:Nd:c:x:r:p:I:X:D:POYQMZUEK:W:LVT:G:R:B:q:g:m:f:t:w:i:s:o:hue")) > 0) + "+A:F:C:H:b:Nd:c:x:r:p:I:X:D:POYQMZUEK:W:LVT:J:G:R:B:q:g:m:f:t:w:i:s:o:hue")) > 0) switch (opt) { @@ -268,6 +272,18 @@ int main(int argc, char** argv) { break; } +#ifdef PROXY_SUPPORT + case 'J': { + u8* x = (u8*)strchr(optarg, ':'); + if (!x) FATAL("Proxy data must be in 'host:port' form."); + *(x++) = 0; + use_proxy = (u8*)optarg; + use_proxy_port = atoi((char*)x); + if (!use_proxy_port) FATAL("Incorrect proxy port number."); + break; + } +#endif /* PROXY_SUPPORT */ + case 'F': { u8* x = (u8*)strchr(optarg, '='); u32 fake_addr; @@ -277,6 +293,7 @@ int main(int argc, char** argv) { if (fake_addr == (u32)-1) FATAL("Could not parse IP address '%s'.", x + 1); fake_host((u8*)optarg, fake_addr); + has_fake = 1; break; } @@ -487,6 +504,11 @@ int main(int argc, char** argv) { } +#ifdef PROXY_SUPPORT + if (has_fake && use_proxy) + FATAL("-F and -J should not be used together."); +#endif /* PROXY_SUPPORT */ + if (access(ASSETS_DIR "/index.html", R_OK)) PFATAL("Unable to access '%s/index.html' - wrong directory?", ASSETS_DIR); diff --git a/string-inl.h b/string-inl.h index 578664f..935f62a 100644 --- a/string-inl.h +++ b/string-inl.h @@ -51,6 +51,15 @@ #include "types.h" +/* Macros for easy string prefix matching */ + +#define prefix(_long, _short) \ + strncmp((char*)(_long), (char*)(_short), strlen((char*)(_short))) + +#define case_prefix(_long, _short) \ + strncasecmp((char*)(_long), (char*)(_short), strlen((char*)(_short))) + + /* Modified NetBSD strcasestr() implementation (rolling strncasecmp). */ static inline u8* inl_strcasestr(const u8* haystack, const u8* needle) {