http.c
字号:
if (r->port != 443) sprintf(port_spec,":%u",r->port); } if (use_proxy && !r->from_ssl) fprintf(server, "%s http://%s:%u/%s%s%s HTTP/1.0\r\n" "Connection: close\r\n" "Host: %s%s\r\n" "Accept-Encoding: identity\r\n" "X-Ratproxy-Loop: 1\r\n" "Content-Length: %u\r\n", r->method, r->host, r->port, r->path, r->query ? "?" : "", r->query ? r->query : (_u8*)"", r->host, port_spec, r->payload_len); else fprintf(server, "%s /%s%s%s HTTP/1.0\r\n" "Connection: close\r\n" "Host: %s%s\r\n" "Accept-Encoding: identity\r\n" "X-Ratproxy-Loop: 1\r\n" "Content-Length: %u\r\n", r->method, r->path, r->query ? "?" : "", r->query ? r->query : (_u8*)"", r->host, port_spec, r->payload_len); if (!strip_state) for (i=0;i<r->h.c;i++) { /* There are several types of headers we'd rather skip and override elsewhere. */#ifdef FORCE_NOCACHE if (!strncasecmp(r->h.v1[i],"If-",3)) continue;#endif /* FORCE_NOCACHE */ if (!strcasecmp(r->h.v1[i],"Host")) continue; if (!strcasecmp(r->h.v1[i],"Range")) continue; if (!strcasecmp(r->h.v1[i],"Connection")) continue; if (!strncasecmp(r->h.v1[i],"Proxy-",6)) continue; if (!strcasecmp(r->h.v1[i],"Accept-Encoding")) continue; if (!strcasecmp(r->h.v1[i],"Content-Length")) continue; /* Override multipart boundary on requests after rewriting. */ if (!strcasecmp(r->h.v1[i],"Content-Type") && r->use_boundary) { fprintf(server,"Content-Type: multipart/form-data; boundary=%s\r\n",r->use_boundary); continue; } fprintf(server,"%s: %s\r\n",r->h.v1[i],r->h.v2[i]); } fprintf(server,"\r\n"); if (r->payload_len) fwrite(r->payload,r->payload_len,1,server); fflush(server); /* Ok, sending complete. */ /* Process the response... */ ret = calloc(1,sizeof(struct http_response)); if (!ret) fatal("out of memory"); ret->ext = r->ext; line = grab_line(server); if (!line || !line[0]) http_error(client,"Malformed HTTP response",0); x = strchr(line,' '); if (!x || x == line) http_error(client,"HTTP response code missing",0); *(x++) = 0; ret->code = atoi(x); if (ret->code < 100 || ret->code > 999) http_error(client,"Invalid HTTP response code",0); while (1) { line = grab_line(server); if (!line) http_error(client,"Premature end of server headers",0); if (!line[0]) break; x = strchr(line,':'); if (!x) http_error(client,"Invalid response header",0); *x = 0; while (isspace(*(++x))); for (i=0;i<ret->h.c;i++) if (!strcasecmp(line,ret->h.v1[i]) && strcmp(x,ret->h.v2[i]) && strncasecmp(line,"Set-Cookie",10) && strncasecmp(line,"X-Cache",7) && strncasecmp(line,"Server",7)) ret->has_multiple = 1; /* Again, some headers need to be analyzed in more detail or skipped. */ /* Caching headers checks... */ if (!strcasecmp(line,"Expires")) { exp_value = strdup(x); if (!exp_value) fatal("out of memory"); } if (!strcasecmp(line,"Date")) { dat_value = strdup(x); if (!dat_value) fatal("out of memory"); } /* Both "no-store" and "max-age=0" are generally discouraged, but in practice, should be sufficient, so let's be polite. */ /* TODO: These checks should be probably more robust to detect typos such as missing whitespaces. */ if (!strcasecmp(line,"Cache-Control")) { if (strstr(x,"no-cache") || strstr(x,"private") || strstr(x,"max-age=0") || strstr(x,"no-store")) ret->cc11intent = INTENT_PRIV; else ret->cc11intent = INTENT_PUB; } if (!strcasecmp(line,"Pragma")) { if (strstr(x,"no-cache")) ret->pr10intent = INTENT_PRIV; else ret->pr10intent = INTENT_PUB; } if (!strcasecmp(line,"Connection")) continue; if (!strcasecmp(line,"Content-Range")) continue; if (!strcasecmp(line,"Content-Type")) { _u8 *copy = strdup(x), *y; if (!copy) fatal("out of memory"); if ((y = strrchr(copy,';'))) { *(y++) = 0; while (isspace(*y)) y++; if (!strncasecmp(y,"charset=",8)) { y += 8; if (*y == '"' && y[strlen(y)-1] == '"') { y[strlen(y)-1]=0; y++; } ret->charset = y; } } ret->mime_type = copy; } if (!strcasecmp(line,"Content-Disposition")) { _u8* y; ret->is_attach = (strncasecmp(x,"attachment;",11) == 0) || (strcasecmp(x,"attachment") == 0); /* If filename is specified, try to grab it (it supersedes any URL-derived ones). */ y=strrchr(x,'.'); if (y && y[1] && y[1] != '"') { ret->ext = strdup(y + 1); if (!ret->ext) fatal("out of memory"); y = strchr(y + 1,'"'); if (y) *y=0; } } if (!strcasecmp(line,"Location")) { ret->location = strdup(x); if (!ret->location) fatal("out of memory"); } if (!strcasecmp(line,"Set-Cookie")) parse_cookies(x,&ret->cookies); if (!strcasecmp(line,"Content-Length")) { decl_clen = atoi(x); if (decl_clen < 0) http_error(client,"Bogus content length returned by server.",0); continue; } DYN_ADD2(ret->h,line,x); } /* Some final "Expires" parsing for caching headers checks... */ if (exp_value) { _u8* year = 0, *z = strchr(exp_value,','); ret->ex10intent = INTENT_PUB; /* Try to extract the year, at least roughly... */ if (!isalnum(exp_value[0])) { /* "Expires: -1" is a nasty trick, but it works. */ ret->ex10intent = INTENT_PRIV; } else if (dat_value && (!strcmp(exp_value,dat_value) || !comp_dates(exp_value,dat_value))) { /* Date == Expires is an alternative and valid method. */ ret->ex10intent = INTENT_PRIV; } else { if (z && z == exp_value + 3 && strlen(exp_value) > 11) { /* Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 */ year = exp_value + 11; if (*year == ' ') year++; } else if (z) { /* Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 */ year = strchr(z,'-'); if (year) year = strchr(year + 1,'-'); if (year) year++; } else if (strlen(x) > 19) { /* Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format */ year = exp_value + 19; if (*year == ' ') year++; } if (year) { _u32 yval = atoi(year); if (yval < 1000) { yval += 1900; /* 94 -> 1994, 104 -> 2004 */ if (yval < 1970) yval += 100; /* 03 -> 2003, 93 -> 1993 */ } if (yval < 2008) ret->ex10intent = INTENT_PRIV; } } } /* Headers read. Grab the actual payload, regardless of content length (but note a discrepancy, if present). */ while (1) { _u8 buf[1024]; _s32 i; if ((i = fread(buf,1,1024,server)) <= 0) break; ret->payload = realloc(ret->payload, ret->payload_len + i + 1); if (!ret->payload) fatal("out of memory"); memcpy(ret->payload + ret->payload_len, buf, i); ret->payload_len += i; if (ret->payload_len > MAXPAYLOAD) http_error(client,"Response size limit exceeded",0); } if (ret->payload_len) ret->payload[ret->payload_len] = 0; /* Let payload_len < decl_clen slip through - transmission errors happen. */ if (decl_clen >= 0 && ret->payload_len > decl_clen) ret->has_badclen = 1; fflush(server); fclose(server); return ret;}/* Just send data back to client. */void send_response(FILE* client, struct http_response* r) { _u32 i; setvbuf(client, cli_buf, _IOFBF, sizeof(cli_buf)); fprintf(client, "HTTP/1.0 %u Proxied response\r\n" "Connection: close\r\n"#ifdef FORCE_NOCACHE "Pragma: no-cache\r\n" "Expires: Fri, 01 Jan 1990 00:00:00 GMT\r\n" "Cache-Control: no-cache, must-revalidate\r\n"#endif /* FORCE_NOCACHE */ "Content-Length: %u\r\n", r->code, r->payload_len); for (i=0;i<r->h.c;i++) {#ifdef FORCE_NOCACHE if (!strcasecmp(r->h1[i],"Expires")) continue; if (!strcasecmp(r->h1[i],"Last-Modified")) continue; if (!strcasecmp(r->h1[i],"Cache-Control")) continue; if (!strcasecmp(r->h1[i],"Pragma")) continue;#endif /* FORCE_NOCACHE */ fprintf(client,"%s: %s\r\n",r->h.v1[i],r->h.v2[i]); } fprintf(client,"\r\n"); if (r->payload_len) fwrite(r->payload,r->payload_len,1,client); fflush(client); fclose(client);}/* Calculate a checksum for response payload */void checksum_response(struct http_response* r) { MD5_CTX ctx; _u8 res[16]; if (use_len) { r->cksum = r->payload_len; return; } if (!r->payload_len) return; MD5_Init(&ctx); MD5_Update(&ctx, r->payload, r->payload_len); MD5_Final((char*)res, &ctx); r->cksum = *(_u64*)res;}/* Attempt charset sniffing inside the payload; currently, supports HTML http-equiv only; kinda fuzzy, but should be good enough. *//* TODO: Make this a bit more robust; reversed http-equiv / content order is not detected, for example. */void detect_charset(struct http_response* r) { _u8 sniffed[33]; _u32 i, max; _u8 got_equiv = 0; if (r->payload_len > CHARSNIFF) max = CHARSNIFF; else max = r->payload_len; for (i=0;i<max;i++) { if (r->payload[i] < 0x20 && !isspace(r->payload[i])) break; if (!strncasecmp(r->payload+i,"http-equiv",10)) got_equiv = 1; if (r->payload[i] == '>') got_equiv = 0; if (got_equiv && !strncasecmp(r->payload+i,"charset=",8)) { _u32 p = 0; _u8* cp = r->payload + i + 8; while (p < 32 && (isalnum(*cp) || *cp == '-' || *cp == '_')) sniffed[p++] = *(cp++); sniffed[p] = 0; break; } } if (i != max) { if (r->charset && strcasecmp(sniffed,r->charset)) r->has_multiple = 1; r->charset = strdup(sniffed); if (!r->charset) fatal("out of memory"); } if (!r->charset) return; i = 0; while (valid_charsets[i]) { if (!strcasecmp(r->charset,valid_charsets[i])) return; i++; } /* But note that utf8, iso_8859_2, etc, are not recognized and lead to XSS... */ r->bad_cset = 1; if (!r->charset[0]) r->charset = 0;}#define TOHEX(c) ("0123456789abcdef"[c])/* Sanitize output; make sure it's easily reversible, too. */_u8* S(_u8* string, _u8 nl) { _u8* ret = malloc(MAXTOKEN + 10 /* �...\0 */), *wp = ret; if (!ret) fatal("out of memory"); while (*string) { switch (tolower(*string)) { /* Well, we kind-of want to maintain readaibility of text output, so let's pay the price and let '&' through. */ case '&': /* Quote literally */ case 'a' ... 'z': case '0' ... '9': case ' ': case '+': case '!': case '@': case '#': case '$': case '%': case '^': case '*': case '(': case ')': case '-': case '_': case '=': case '{': case '[': case '}': case ']': case ':': case ';': case ',': case '.': case '?': case '/': case '~': case '`': case '\\': *(wp++) = *string; break; /* These can be harmful or confusing, so replace with HTML entities */ case '"': case '\'': case '<': case '>': case '|': case 127 ... 255:entitify: *(wp++) = '&'; *(wp++) = '#'; *(wp++) = 'x'; *(wp++) = TOHEX(*string / 16); *(wp++) = TOHEX(*string % 16); *(wp++) = ';'; break; /* Replace with shorthand codes */ case '\r': if (nl) { *(wp++) = *string; } else { *(wp++) = '\\'; *(wp++) = 'r'; } break; case '\n': if (nl) { *(wp++) = *string; } else { *(wp++) = '\\'; *(wp++) = 'n'; } break; case '\t': if (nl) { *(wp++) = *string; } else { *(wp++) = '\\'; *(wp++) = 't'; } break; /* Replace with hex tokens */ default: if (nl) goto entitify; *(wp++) = '\\'; *(wp++) = 'x'; *(wp++) = TOHEX(*string / 16); *(wp++) = TOHEX(*string % 16); } if (wp - ret >= MAXTOKEN) { *(wp++) = '.'; *(wp++) = '.'; *(wp++) = '.'; break; } string++; } *(wp++) = 0; return ret;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -