http.c
字号:
/* ratproxy - HTTP request handling -------------------------------- The following routines take care of HTTP request handling, parsing, and error reporting. Note that this code is one-shot, process is terminated when request handling is done - and as such, we rely on the OS to do garbage collection. Author: Michal Zalewski <lcamtuf@google.com> Copyright 2007, 2008 by Google Inc. All Rights Reserved. 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. */#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/socket.h>#include <netinet/in.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <string.h>#include <sys/wait.h>#include <ctype.h>#include <netdb.h>#include <openssl/md5.h>#include <time.h>#include "config.h"#include "types.h"#include "debug.h"#include "nlist.h"#include "http.h"#include "ssl.h"#include "string-inl.h"extern _u8* use_proxy; /* Runtime setting exports from ratproxy. */extern _u32 proxy_port;extern _u8 use_len;static _u8 srv_buf[MAXLINE], /* libc IO buffers */ cli_buf[MAXLINE];/* Read a single line of HTTP headers, strip whitespaces */static _u8* grab_line(FILE* where) { static _u8 inbuf[MAXLINE]; _u32 l; if (!fgets(inbuf,MAXLINE,where)) return 0; l = strlen(inbuf); /* Excessive line length is bad, let's bail out. */ if (l == MAXLINE-1) return 0; while (l && isspace(inbuf[l-1])) inbuf[--l] = 0; return inbuf;}/* Return a generic HTTP error message, end current process. Note that this function should not handle user-controlled data. */static void http_error(FILE* client, _u8* message,_u8 sink) { if (client) { _u8* l; if (sink) while ((l=grab_line(client)) && l[0]); fprintf(client, "HTTP/1.0 500 %s\n" "Content-type: text/html\n\n" "<font face=\"Bitstream Vera Sans Mono,Andale Mono,Lucida Console\">\n" "The proxy is unable to process your request.\n" "<h1><font color=red><b>%s.</b></font></h1>\n", message, message); fflush(client); fclose(client); } debug("[!] WARNING: %s.\n", message); exit(0);}static _u8* BASE16 = "0123456789ABCDEF";/* Decode URL-encoded parameter string */void parse_urlencoded(struct naive_list_p* p, _u8* string) { _u8 val_now = 0; _u8 name[MAXLINE+1], val[MAXLINE+1]; _u32 nlen = 0, vlen = 0; name[0] = 0; val[0] = 0; do { _u8 dec = 0; switch (*string) { case '+': dec = ' '; break; case '=': val_now = 1; break; case '%': { _u8 *a, *b; /* Parse %nn code, if valid; default to '?nn' if not, replace with ? if \0. */ if (!string[1] || !string[2] || !(a=strchr(BASE16,toupper(string[1]))) || !(b=strchr(BASE16,toupper(string[2])))) { dec = '?'; break; } dec = (a-BASE16) * 16 + (b-BASE16); string += 2; if (!dec) dec = '?'; break; } case '&': case 0: /* Handle parameter terminator; note that we also iterate over \0 because of loop condition placement. */ if (nlen) { name[nlen] = 0; val[vlen] = 0; DYN_ADDP(*p,name,val,""); } val_now = 0; nlen = 0; vlen = 0; break; default: if (!(dec=*string)) dec = '?'; } /* Append decoded char, if any, to field name or value as needed. */ if (dec) { if (!val_now) { if (nlen < MAXLINE) name[nlen++] = dec; } else { if (vlen < MAXLINE) val[vlen++] = dec; } } } while (*(string++)); }/* Read a line of multipart data from a linear buffer, advance buffer pointer. */static _u8* get_multipart_line(_u8** buf) { static _u8* retbuf; _u8* x; _u32 cnt; if (retbuf) free(retbuf); /* We assume \r\n formatting here, which is RFC-mandated and implemtned by well-behaved browsers. */ x = strchr(*buf,'\r'); if (!x || x[1] != '\n') { _u32 l = strlen(*buf); retbuf = malloc(l + 1); if (!retbuf) fatal("out of memory"); strcpy(retbuf,*buf); *buf += l; return retbuf; } cnt = x - *buf; retbuf = malloc(cnt + 1); if (!retbuf) fatal("out of memory"); memcpy(retbuf,*buf,cnt); retbuf[cnt] = 0; *buf += cnt + 2; return retbuf;}/* Collect multipart data from a reasonably well-behaved browser. This routine makes multiple assumptions that might be not true for maliciously formatted data, but we do not strive to serve such requests well. */void parse_multipart(struct naive_list_p* p, _u8* string, _u32 slen) { _u8* field, *fname; _u8* endptr = string + slen; do { _u8 *l, *end, *c; field = 0; fname = 0; /* Skip boundary */ l = get_multipart_line(&string); if (l[0] != '-' || l[1] != '-') return; /* Sink headers, but grab field name if any */ while ((l = get_multipart_line(&string)) && l[0]) { if (!strncasecmp(l,"Content-Disposition:",20)) { /* Grab field name. */ _u8* f = rp_strcasestr(l,"; name=\""); if (!f) continue; f += 7; c = strchr(++f,'"'); if (!c) continue; *c = 0; field = strdup(f); if (!field) fatal("out of memory"); /* Grab file name, if any. */ f = rp_strcasestr(c + 1,"; filename=\""); if (!f) continue; f += 11; c = strchr(++f,'"'); if (!c) continue; *c = 0; fname = strdup(f); if (!fname) fatal("out of memory"); } } end = rp_memmem(string,endptr - string, "\r\n--", 4); if (!end) return; if (field) DYN_ADDP_RAWMEM(*p,field,string,end-string,fname ? fname : (_u8*)""); string = end + 2; } while (1);}#define BASE64 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+/_-"/* Looks for what could pass for a reasonably robust session token or XSRF protection. */_u8 contains_token(_u8* name, _u8* value) { _u32 run16 = 0, run64 = 0, run64_true = 0, run64_num = 0, run64_up = 0; _u8* st = 0; static _u32 tmin,tmax; _u32 fno = 0; if (!tmin) { tmin = time(0); tmax = tmin + (60 * 60 * 24 * 30); /* One month forward */ tmin -= (60 * 60 * 24 * 365 * 5); /* Five years back */ } /* Known bad field names - return 0. */ fno = 0; while (no_xsrf_fields[fno]) { if (no_xsrf_fields[fno][0] == '=') { if (!strcasecmp(name,no_xsrf_fields[fno] + 1)) return 0; } else { if (rp_strcasestr(name,no_xsrf_fields[fno])) return 0; } fno++; } /* Known safe field names - return 1. */ fno = 0; while (xsrf_fields[fno]) { if (xsrf_fields[fno][0] == '=') { if (!strcasecmp(name,xsrf_fields[fno] + 1)) return 1; } else { if (rp_strcasestr(name,xsrf_fields[fno])) return 1; } fno++; } /* URLs are not anti-XSRF tokens, no matter how random they look. */ if (!strncmp(value,"http",4)) return 0; /* Iterate over value data, compute base16 / base64 runs, collect basic character disttributin data, rule out patterns such as unix time, and make the call. */ do { if (*value && strchr(BASE16,toupper(*value))) { run16++; } else { if (run16 >= XSRF_B16_MIN && run16 <= XSRF_B16_MAX) { _u8 tmp[5]; _u32 val; strncpy(tmp,st,4); tmp[4] = 0; val = atoi(tmp); if ((val < tmin / 1000000 || val > tmax / 1000000) && (st[0] != st[1] || st[0] != st[2])) return 1; } run16 = 0; } if (*value && strchr(BASE64,toupper(*value))) { if (!isalpha(*value)) run64_num++; if (isupper(*value)) run64_up++; if (!run16) run64_true = 1; if (!run64) st = value; run64++; } else { if (run64 >= XSRF_B64_MIN && run64 <= XSRF_B64_MAX && ((run64_num >= XSRF_B64_NUM && run64_up >= XSRF_B64_UP) || (run64_num >= XSRF_B64_NUM2)) && run64_true) if (st[0] != st[1] || st[0] != st[2]) return 1; run64 = 0; run64_num = 0; run64_true = 0; st = 0; } } while (*(value++)); return 0;}/* Try to parse cookie header values. */static void parse_cookies(_u8* str, struct naive_list2* c) { _u8 name[128], val[128]; /* Iterate over cookies. We ignore cookies over 128 bytes for name / value, and "special" values such as expiration date, version, etc. */ while (str) { while (isspace(*str)) str++; if (sscanf(str,"%127[^;=]=%127[^;]",name,val) == 2) { if (strcasecmp(name,"expires") && strcasecmp(name,"comment") && strcasecmp(name,"version") && strcasecmp(name,"max-age") && strcasecmp(name,"path") && strcasecmp(name,"domain") && name[0] != '$') DYN_ADD2(*c,name,val); } str = strchr(str + 1 ,';'); if (str) str++; }}/* Process the entire HTTP request, parse fields, and extract some preliminary signals. */struct http_request* collect_request(FILE* client,_u8* ssl_host, _u32 ssl_port) { struct http_request* ret; _u8 *line, *x; _u32 i; /* Begin carefully - on CONNECT requests, we do not want to read more than absolutely necessary. As soon as non-CONNECT is confirmed, we switch to proper buffering. */ setvbuf(client, cli_buf, _IONBF, 0); ret = calloc(1, sizeof(struct http_request)); if (!ret) fatal("out of memory"); line = grab_line(client); if (!line || !line[0]) exit(0); x = strchr(line,' '); if (!x || x == line) http_error(client, "URL address missing or malformed request",1); *(x++) = 0; ret->method = strdup(line); if (!ret->method) fatal("out of memory"); if (strcmp(line,"CONNECT")) { /* Ok, safe to handle HTTP at full speed now. */ setvbuf(client, cli_buf, _IOFBF, sizeof(cli_buf)); if (!ssl_host) { /* Unless coming from within CONNECT, we want a properly specified protocol and so forth. */ if (x[0] == '/') http_error(client, "Direct HTTP requests not allowed",1); if (strncmp(x,"http://",7)) http_error(client, "Unsupported protocol",1); x += 7; } } else { /* We do not want CONNECT requests within CONNECT requests, really. */ if (ssl_host) http_error(client,"Evil CONNECT nesting",1); ret->is_connect = 1; } ret->host = x; x = strchr(ret->host,' '); if (!x) http_error(client,"Missing HTTP protocol version",1); if (strcmp(x," HTTP/1.0") && strcmp(x," HTTP/1.1")) http_error(client,"unsupported HTTP protocol version",1); /* Trim HTTP/1.x part now, we do not need it */ *x = 0; if (!ret->is_connect) { ret->path = strchr(ret->host,'/'); if (!ret->path) http_error(client,"Incomplete request URL",1); *(ret->path++) = 0; } /* Try to find port, if any */ x = strchr(ret->host,':'); if (x) { ret->port = atoi(x+1); if (!ret->port || ret->port > 65535) http_error(client,"Illegal port specification",1); if (ret->port < 1024 && ret->port != 80 && ret->port != 443) http_error(client,"Access to this port denied",1); *x = 0; } else { if (ret->is_connect) ret->port = 443; else ret->port = 80; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -