fuck! don't perform ssl handshake for blocked hosts!
[mediator.git] / src / proxy.c
blob7013a7a45a5c2533928c0cee1be63f6dabfbdd7a
1 #include "hazard.c"
3 //#define DUMP_DATA
5 #define skxabort skclose
8 static const char *get_content_type (const char *fname) {
9 static const struct {
10 const char *ext;
11 const char *type;
12 } types[] = {
13 {.ext=".js", .type="text/javascript"},
14 {.ext=".css", .type="text/css"},
15 {.ext=".html", .type="text/html"},
16 {.ext=".ico", .type="image/png"},
17 {.ext=".png", .type="image/png"},
19 if ((fname = strrchr(fname, '.')) == NULL) return "text/plain";
20 for (size_t f = 0; f < ARRAYLEN(types); ++f) {
21 if (strcasecmp(types[f].ext, fname) == 0) return types[f].type;
23 return "text/plain";
27 static void proxy_thread (void *arg) {
28 coro_info_t *me = (coro_info_t *)arg;
29 xbuf_t *clbuf = &me->bufs[0], *svbuf = &me->bufs[1];
30 sock_t clifd, srvfd;
31 int64_t content_length = -1;
32 char ipstr[INET6_ADDRSTRLEN];
33 http_method mt;
34 char hostname[256], chostname[256], proto[16], portstr[16], *hn;
35 int defport = 0; // default port?
36 int port = 0, cport = 0, allow_iframe = 0, skip_host_blocks = 0;
37 int allow_v4 = 1, allow_v6 = 1;
38 // for getaddrinfo()
39 //struct addrinfo *adrres;
40 int gares;
41 const char *new_ua = NULL;
42 static char pathbuf[4096];
43 const char *rewritten_uri = NULL;
44 /* SSL shit */
45 x509_crt *fake_cert = NULL; // malloc()ed
47 int fuckYoutube = 0;
48 int isRZX = 0;
50 int copy_and_parse_hostname (const void *str, int len) {
51 const uint8_t *hh = (const uint8_t *)str;
52 int p;
53 if (len <= 0) len = 32768;
54 for (p = 0; p < len && hh[p] > 32; ++p) {
55 if (p >= sizeof(hostname)-1) return -1; // too long
56 hostname[p] = tolower(hh[p]);
58 hostname[p] = 0;
60 if (strncmp(hostname, "fuck.", 5) == 0) {
61 skip_host_blocks = 1;
62 memmove(hostname, hostname+5, strlen(hostname+5)+1);
63 } else {
64 skip_host_blocks = 0;
67 //skip_host_blocks = 0;
68 while (buf_remove_header(clbuf->buf, "Host:") > 0) ;
69 strcat(clbuf->buf, "Host: ");
70 strcat(clbuf->buf, hostname);
71 strcat(clbuf->buf, "\r\n");
73 if (hostname[0] == '[') {
74 // IPv6
75 char *pp = strrchr(hostname, ']');
76 if (pp != NULL && pp[1] == ':' && pp[2] >= '0' && pp[2] <= '9') {
77 // port
78 //TODO: error checking!
79 port = atoi((char *)(pp+2));
80 pp[1] = 0;
81 if (port < 1 || port > 65535) return -1;
83 } else {
84 char *pp = strrchr(hostname, ':');
85 if (pp != NULL && pp[1] >= '0' && pp[1] <= '9') {
86 // port
87 //TODO: error checking!
88 port = atoi((char *)(pp+1));
89 pp[0] = 0;
90 if (port < 1 || port > 65535) return -1;
93 if (!hostname[0]) return -1;
94 //if (port < 1) port = 80;
95 return 0;
98 int fix_query (void) {
99 uint8_t *qaddr = (uint8_t *)clbuf->buf;
100 while (*qaddr > 32) ++qaddr;
101 while (*qaddr <= 32 && *qaddr != '\n') ++qaddr;
102 if (*qaddr == '\n') return -1;
103 //och = *eaddr;
104 //*eaddr = 0;
105 //ptlogf(":[%s]\n", (char *)qaddr);
106 if (mt == MT_CONNECT) {
107 // a very special beast
108 if (copy_and_parse_hostname(qaddr, 0) < 0) return -1;
109 strcpy(proto, "https");
110 if (port == 0) port = 443;
111 //if (port == 443) defport = 1;
112 } else if (*qaddr == '/') {
113 // no host in query string, try to find 'Host:' field
114 char *hh = buf_find_header(clbuf->buf, "Host:");
115 if (hh == NULL || *hh == '\r' || *hh == '\n') {
116 if (chostname[0] == 0) return -1; // alas
117 strcpy(hostname, chostname);
118 } else {
119 if (copy_and_parse_hostname(hh, 0) < 0) return -1;
121 strcpy(proto, "http");
122 // no need to fix anything
123 //if (port == 0) port = 80;
124 //if (port == 80) defport = 1;
125 } else {
126 char pp[32];
127 uint8_t *hp, *ee;
129 if (strncasecmp((const char *)qaddr, "http://", 7) == 0) {
130 hp = qaddr+7;
131 port = 80;
132 //defport = 1;
133 strcpy(proto, "http");
134 } else if (strncasecmp((const char *)qaddr, "https://", 8) == 0) {
135 hp = qaddr+8;
136 port = 443;
137 //defport = 1;
138 strcpy(proto, "https");
139 } else {
140 return -1; // unknown protocol
143 ee = (uint8_t *)strchr((char *)hp, '/');
144 if (ee == NULL) return -1; // invalid query (no path), fuck it
145 if (copy_and_parse_hostname(hp, ee-hp) < 0) return -1;
147 if (port) snprintf(pp, sizeof(pp), ":%d", port); else pp[0] = 0;
148 memmove(qaddr, ee, strlen((char *)ee)+1);
150 if (port == 0) { port = 80; /*defport = 1;*/ }
151 snprintf(portstr, sizeof(portstr), "%d", port); // for getaddrinfo()
152 return 0;
155 int read_headers (sock_t *sk) {
156 int hdr_size = 0;
157 // now read HTTP headers
158 clbuf->used = 0;
159 for (;;) {
160 int found, sz, rd;
161 // read data from client or timeout in TIMEOUT_CLIENT msecs
162 if (clbuf->used >= MAX_HDR_SIZE) {
163 ptlogf("WARNING: headers too big in worker thread!\n");
164 return -1;
167 if (!sock_is_ssl(sk)) {
168 // normal reader
169 rd = sock_recv(sk, clbuf->buf+clbuf->used, MAX_HDR_SIZE-clbuf->used, MSG_PEEK);
170 if (rd <= 0) {
171 ptlogf("WARNING: headers recv error in worker thread (connection closed;fd=%d)!\n", sock_fd(sk));
172 return -1;
174 sz = clbuf->used+rd;
175 found = 0;
176 // check if we get complete headers
177 for (hdr_size = 0; hdr_size < sz; ++hdr_size) {
178 if (clbuf->buf[hdr_size] == '\n') {
179 if (hdr_size+1 < sz && clbuf->buf[hdr_size+1] == '\n') {
180 found = 1;
181 hdr_size += 2;
182 break;
184 if (hdr_size+2 < sz && clbuf->buf[hdr_size+1] == '\r' && clbuf->buf[hdr_size+2] == '\n') {
185 found = 1;
186 hdr_size += 3;
187 break;
189 } else if ((unsigned char)(clbuf->buf[hdr_size]) < 32 && clbuf->buf[hdr_size] != '\r' && clbuf->buf[hdr_size] != '\n' && clbuf->buf[hdr_size] != '\t') {
190 ptlogf("WARNING: invalid char in headers in worker thread!\n");
191 return -1;
194 if (!found && rd == 0) {
195 ptlogf("WARNING: connection closed before headers received in worker thread!\n");
196 return -1;
198 if (found) {
199 if (hdr_size <= clbuf->used) {
200 // the thing that should not be
201 ptlogf("FATAL: header reader: the thing that should not be!\n");
202 abort();
204 clbuf->used += recv(sock_fd(sk), clbuf->buf+clbuf->used, hdr_size-clbuf->used, 0); // this will always succeed
205 break;
207 clbuf->used += recv(sock_fd(sk), clbuf->buf+clbuf->used, rd, 0); // this will always succeed
208 } else {
209 // SSL reader
210 rd = sock_recv(sk, clbuf->buf+clbuf->used, 1, MSG_WAITALL);
211 if (rd != 1) {
212 ptlogf("WARNING: SSL header reader failed!\n");
213 return -1;
215 ++clbuf->used;
216 int lpos = clbuf->used-1;
217 if (clbuf->buf[lpos] == '\n') {
218 --lpos; // remove LF
219 if (lpos >= 0 && clbuf->buf[lpos] == '\r') --lpos; // remove CR
220 if (lpos >= 0 && clbuf->buf[lpos] == '\n') {
221 hdr_size = clbuf->used;
222 break;
227 // check headers size
228 if (hdr_size < 10) {
229 ptlogf("WARNING: headers too small in worker thread!\n");
230 return -1;
232 clbuf->buf[hdr_size] = 0;
233 ptlogf("read_headers(%d): %d bytes read\n", sock_fd(sk), hdr_size);
234 // remove last '\r\n'
235 if (clbuf->buf[hdr_size-1] != '\n') {
236 ptlogf("WARNING: invalid headers in worker thread!\n");
237 return -1;
239 if (clbuf->buf[hdr_size-2] == '\r') clbuf->buf[hdr_size-2] = 0; else clbuf->buf[hdr_size-1] = 0;
240 // check content-length
242 char *cl = buf_find_header(clbuf->buf, "Content-Length:");
243 content_length = -1;
244 if (cl != NULL) {
245 if (*cl >= '0' && *cl <= '9') {
246 content_length = 0;
247 while (*cl && (unsigned char)(*cl) > 32) {
248 if (*cl >= '0' && *cl <= '9') {
249 content_length = content_length*10+cl[0]-'0';
250 ++cl;
251 } else {
252 content_length = -1;
253 break;
256 while (*cl && (unsigned char)(*cl) <= 32 && *cl != '\r' && *cl != '\n') ++cl;
257 if (*cl != '\r' && *cl != '\n') content_length = -1;
259 if (content_length < 0) buf_remove_header(clbuf->buf, "Content-Length:");
263 return 0;
266 int do_xbuf_rd (sock_t *sk, xbuf_t *buf, const char *who) {
267 if (buf->used < buf->size) {
268 // try to read in buffer
269 //ptlogf("do_xbuf_rd: %s reading...\n", who);
270 int xr = xbuf_read(sk, buf);
271 //ptlogf("do_xbuf_rd: %s read; xr=%d; used=%d\n", who, xr, buf->used);
272 if (xr <= 0) {
273 ptlogf("tunneling: %s connection closed (2) (0)\n", who);
274 return 0;
276 } else {
277 // do nothing
279 // just check if connection is alive
280 int res = asnet_recv_check(sock_fd(sk));
281 if (res <= 0) {
282 ptlogf("tunneling: %s connection closed (2) (1)\n", who);
283 return 0;
287 return 1;
290 void do_tunnel (void) {
291 ptlogf("established tunnel to %s:%d\n", ipstr, port);
292 clbuf->used = 0;
293 svbuf->used = 0;
294 while (sock_is_alive(&srvfd) && sock_is_alive(&clifd)) {
295 int st_r, cl_r;
297 coro_fd_add(sock_fd(&srvfd), SAR_READ|(clbuf->used > 0 ? SAR_WRITE : 0), TIMEOUT_SERVER);
298 coro_fd_add(sock_fd(&clifd), SAR_READ|(svbuf->used > 0 ? SAR_WRITE : 0), TIMEOUT_SERVER);
299 // wait
300 coro_yield();
301 // something was hit here, cancel all waiters
302 st_r = coro_fd_check(sock_fd(&srvfd));
303 cl_r = coro_fd_check(sock_fd(&clifd));
304 //ptlogf("TUNNELING: st_r=0x%02x; cl_r=0x%02x\n", st_r, cl_r);
305 if ((st_r|cl_r)&SAR_TIMEOUT) {
306 // either client or server times out
307 if (st_r&SAR_TIMEOUT) {
308 ptlogf("tunneling: server connection timeouted (0)\n");
309 sock_abort(&srvfd);
311 if (cl_r&SAR_TIMEOUT) {
312 ptlogf("tunneling: client connection timeouted (0)\n");
313 sock_abort(&clifd);
315 break;
318 if (st_r&SAR_READ) {
319 // can read from server
320 if (do_xbuf_rd(&srvfd, svbuf, "server") == 0) {
321 sock_abort(&srvfd);
325 if (cl_r&SAR_READ) {
326 // can read from client
327 if (do_xbuf_rd(&clifd, clbuf, "client") == 0) {
328 sock_abort(&clifd);
332 if (sock_is_alive(&srvfd) && clbuf->used > 0 && (st_r&SAR_WRITE)) {
333 // can send data to server
334 //ptlogf("tunneling: writing to server (%d)...\n", clbuf->used);
335 int xr = xbuf_write(&srvfd, clbuf);
336 if (xr <= 0) {
337 ptlogf("tunneling: server connection closed (1)\n");
338 sock_abort(&srvfd);
339 break;
343 if (sock_is_alive(&clifd) && svbuf->used > 0 && (cl_r&SAR_WRITE)) {
344 // can send data to client
345 //ptlogf("tunneling: writing to client (%d)...\n", svbuf->used);
346 int xr = xbuf_write(&clifd, svbuf);
347 if (xr <= 0) {
348 ptlogf("tunneling: client connection closed (1)\n");
349 sock_abort(&clifd);
350 break;
354 // now we have one or zero fds opened
355 if (sock_is_alive(&srvfd) && sock_is_alive(&clifd)) {
356 ptlogf("WTF?!!\n");
357 goto tunnel_error;
359 // send client data to server
360 while (sock_is_alive(&srvfd) && clbuf->used > 0) {
361 //ptlogf("tunnel: sending server data; fd=%d; left=%d\n", srvfd, clbuf->used);
362 coro_fd_add(sock_fd(&srvfd), SAR_WRITE, TIMEOUT_SERVER);
363 coro_yield();
364 //ptlogf("tunnel: sending server data; fd=%d; st=0x%02x\n", srvfd, coro_fd_check(srvfd));
365 if (coro_fd_check(sock_fd(&srvfd))&SAR_WRITE) {
366 int xr = xbuf_write(&srvfd, clbuf);
367 if (xr <= 0) {
368 //ptlogf("tunnel: sending server data: error (0)\n");
369 break;
371 } else {
372 //ptlogf("tunnel: sending server data: error (1)\n");
373 break;
376 // send server data to client
377 while (sock_is_alive(&clifd) && svbuf->used > 0) {
378 //ptlogf("tunnel: sending client data; fd=%d; left=%d\n", clifd, svbuf->used);
379 coro_fd_add(sock_fd(&clifd), SAR_WRITE, TIMEOUT_CLIENT);
380 coro_yield();
381 //ptlogf("tunnel: sending client data; fd=%d; st=0x%02x\n", clifd, coro_fd_check(clifd));
382 if (coro_fd_check(sock_fd(&clifd))&SAR_WRITE) {
383 int xr = xbuf_write(&clifd, svbuf);
384 if (xr <= 0) {
385 //ptlogf("tunnel: sending client data: error (0)\n");
386 break;
388 } else {
389 //ptlogf("tunnel: sending client data: error (1)\n");
390 break;
393 tunnel_error:
394 ptlogf("tunnel: server sent %llu bytes, client sent %llu bytes\n", svbuf->total, clbuf->total);
397 //////////////////////////////////////////////////////////////////////////////
398 int build_etag (char *dest, const char *fname) {
399 struct stat st;
400 if (stat(fname, &st) != 0) return -1;
401 if (!S_ISREG(st.st_mode)) return -1;
402 sprintf(dest, "\"e%08x%08x%08x%08x\"", (unsigned int)st.st_dev, (unsigned int)st.st_ino, (unsigned int)st.st_mtime, (unsigned int)st.st_size);
403 return 0;
406 void send_file (const char *fname, const char *ftag) {
407 static char etag[512]; // this is safe, we have only one thread
408 char *filedata = NULL;
409 int fd;
410 off_t filesize;
411 if (build_etag(etag, fname) != 0) {
412 xnotfound:
413 //ptlogf("send_file: '%s' not found!\n", fname);
414 send_full(&clifd, "HTTP/1.0 404 SHIT!\r\nConnection: close\r\n\r\n", -1);
415 goto xquit;
417 if (ftag != NULL && strcmp(ftag, etag) == 0) {
418 // cache hit!
419 ptlogf("CACHE HIT FOR [%s]: %s\n", fname, ftag);
420 send_full(&clifd, "HTTP/1.0 304 ALWAYS\r\nConnection: close\r\n\r\n", -1);
421 goto xquit;
422 } else {
423 ptlogf("CACHE MISS FOR [%s]: %s is not %s\n", fname, (ftag != NULL ? ftag : "\"\""), etag);
425 if ((fd = open(fname, O_RDONLY)) < 0) goto xnotfound;
426 filesize = lseek(fd, 0, SEEK_END);
427 lseek(fd, 0, SEEK_SET);
428 filedata = malloc(filesize+1);
429 if (filesize > 0) read(fd, filedata, filesize); //FIXME: check for errors
430 close(fd);
431 sprintf(clbuf->buf, "HTTP/1.0 200 HERE\r\nConnection: close\r\nETag: %s\r\nContent-Type: %s\r\nContent-Size: %d\r\n\r\n", etag, get_content_type(fname), (int)filesize);
432 if (send_full(&clifd, clbuf->buf, -1) < 0) {
433 goto xquit;
435 if (filesize > 0) send_full(&clifd, filedata, filesize);
436 xquit:
437 if (filedata != NULL) free(filedata);
440 // note that fname can point to clbuf->buf here
441 void receive_file (const char *fname) {
442 if (fname != NULL && fname[0] && fname[strlen(fname)-1] != '/' && content_length >= 0) {
443 static char nbuf[1024];
444 int fd;
445 snprintf(nbuf, sizeof(nbuf), "%s.%d", fname, sock_fd(&clifd));
446 ptlogf("receiving %d bytes to '%s'\n", (int)content_length, nbuf);
447 fd = open(nbuf, O_WRONLY|O_CREAT|O_TRUNC, 0644);
448 if (fd >= 0) {
449 while (content_length > 0) {
450 int toread = (content_length > svbuf->size ? svbuf->size : (int)content_length);
451 int rd = sock_recv(&clifd, svbuf->buf, toread, MSG_WAITALL);
452 if (rd <= 0) break;
453 write(fd, svbuf->buf, rd);
454 content_length -= rd;
456 close(fd);
457 // 'rename' is atomic operation
458 if (rename(nbuf, fname) < 0) {
459 ptlogf("\2CAN'T RENAME '%s' to '%s'!\n", nbuf, fname);
460 unlink(nbuf);
462 send_full(&clifd, "HTTP/1.0 200 OK\r\nConnection: close\r\n\r\n", -1);
463 return;
466 send_full(&clifd, "HTTP/1.0 500 SHIT\r\nConnection: close\r\n\r\n", -1);
469 //////////////////////////////////////////////////////////////////////////////
470 int do_ssl_mitm_headers (void) {
471 int res;
473 fake_cert = malloc(sizeof(*fake_cert));
474 if (fake_cert == NULL) abort(); // the thing that should not be
476 ptlogf("CONNECT, loading root certificate...\n");
477 cert_load_root(fake_cert);
478 if (cert_for_host(hostname, fake_cert, skip_host_blocks) < 0) return -1;
480 if (fake_cert->next == NULL) {
481 ptlogf("SSL: we DON'T have two certificates!\n");
482 return -1;
485 if (send_full(&clifd, "HTTP/1.0 200 OK\r\nConnection: close\r\nProxy-Connection: close\r\n\r\n", -1) != 0) return -1;
487 clifd.ssl = malloc(sizeof(clifd.ssl[0]));
488 if (clifd.ssl == NULL) abort(); // the thing that should not be
489 if ((res = ssl_init(clifd.ssl)) != 0) {
490 ptlogf("FUCKED: ssl_init returned %d\n\n", res);
491 return -1;
494 //ssl_set_dbg(clifd.ssl, ssl_debug, stderr);
495 ssl_set_endpoint(clifd.ssl, SSL_IS_SERVER);
496 ssl_set_authmode(clifd.ssl, SSL_VERIFY_NONE);
497 ssl_set_rng(clifd.ssl, ctr_drbg_random, &ssl_ctr_drbg_srv);
498 ssl_set_bio(clifd.ssl, xssl_net_recv, &clifd, xssl_net_send, &clifd);
499 ssl_set_ca_chain(clifd.ssl, fake_cert, NULL, NULL);
500 ssl_set_own_cert(clifd.ssl, fake_cert->next, &ssl_pkey);
502 srvfd.ssl = malloc(sizeof(srvfd.ssl[0]));
503 if (srvfd.ssl == NULL) abort();
504 if (ssl_init(srvfd.ssl) != 0) return -1;
506 //ssl_set_dbg(srvfd.ssl, ssl_debug, stderr);
507 ssl_set_endpoint(srvfd.ssl, SSL_IS_CLIENT);
508 ssl_set_authmode(srvfd.ssl, SSL_VERIFY_NONE);
509 ssl_set_rng(srvfd.ssl, ctr_drbg_random, &ssl_ctr_drbg_cli);
510 ssl_set_bio(srvfd.ssl, xssl_net_recv, &srvfd, xssl_net_send, &srvfd);
512 sock_handshake(&clifd);
514 ptlogf("SSL: reading headers from client...\n");
515 if (read_headers(&clifd) < 0) return -1;
516 if (opt_dump_client_headers) ptlogf("===\n%s===\n", clbuf->buf);
518 if (strncasecmp(clbuf->buf, "GET ", 4) == 0) mt = MT_GET;
519 else if (strncasecmp(clbuf->buf, "POST ", 5) == 0) mt = MT_POST;
520 else if (strncasecmp(clbuf->buf, "HEAD ", 5) == 0) mt = MT_HEAD;
521 else if (strncasecmp(clbuf->buf, "OPTIONS ", 8) == 0) mt = MT_OPTIONS;
522 else {
523 ptlogf("WARNING: invalid method in headers in worker thread!\n");
524 return -1;
527 cport = port;
528 strcpy(chostname, hostname);
529 if (fix_query() < 0) {
530 ptlogf("WARNING: can't parse client query in headers in worker thread!\n===\n%s===\n", clbuf->buf);
531 return -1;
534 return 0;
537 //////////////////////////////////////////////////////////////////////////////
538 int do_fuck_youtube (void) {
539 fake_cert = malloc(sizeof(*fake_cert));
540 if (fake_cert == NULL) abort(); // the thing that should not be
542 ptlogf("CONNECT, loading root certificate...\n");
543 cert_load_root(fake_cert);
544 if (cert_for_host(hostname, fake_cert, skip_host_blocks) < 0) return -1;
546 if (fake_cert->next == NULL) {
547 ptlogf("SSL: we DON'T have two certificates!\n");
548 return -1;
551 srvfd.ssl = malloc(sizeof(srvfd.ssl[0]));
552 if (srvfd.ssl == NULL) abort();
553 if (ssl_init(srvfd.ssl) != 0) return -1;
555 //ssl_set_dbg(srvfd.ssl, ssl_debug, stderr);
556 ssl_set_endpoint(srvfd.ssl, SSL_IS_CLIENT);
557 ssl_set_authmode(srvfd.ssl, SSL_VERIFY_NONE);
558 ssl_set_rng(srvfd.ssl, ctr_drbg_random, &ssl_ctr_drbg_cli);
559 ssl_set_bio(srvfd.ssl, xssl_net_recv, &srvfd, xssl_net_send, &srvfd);
561 return 0;
564 //////////////////////////////////////////////////////////////////////////////
565 //Content-Type: text/x-dsrc; name="bug.d"; charset=
566 int compareContentType (const char *str, const char *pat) {
567 if (pat == NULL || pat[0] == 0) return 0;
568 if (str == NULL || str[0] == 0) return 0;
569 size_t slen = strlen(str);
570 size_t plen = strlen(pat);
571 if (plen > slen && str[plen] != ';') return 0; // alas
572 return (strncasecmp(str, pat, plen) == 0);
575 //////////////////////////////////////////////////////////////////////////////
576 sock_init_with_fd(&clifd, me->clifd, 1); // client socket
577 sock_init_empty(&srvfd, 0); // server socket
579 ptlogf("*** new connection! (workers=%d; fd=%d)\n", coro_count, sock_fd(&clifd));
580 // disable Nagle
582 int state = 1;
583 setsockopt(sock_fd(&clifd), IPPROTO_TCP, TCP_NODELAY, &state, sizeof(state));
586 chostname[0] = 0;
588 if (read_headers(&clifd) < 0) goto quit;
589 //socket_set_timeouts(sock_fd(&clifd), TIMEOUT_CLIENT);
591 if (opt_dump_client_headers) ptlogf("===\n%s===\n", clbuf->buf);
592 // determine method
593 if (strncasecmp(clbuf->buf, "GET ", 4) == 0) mt = MT_GET;
594 else if (strncasecmp(clbuf->buf, "POST ", 5) == 0) mt = MT_POST;
595 else if (strncasecmp(clbuf->buf, "HEAD ", 5) == 0) mt = MT_HEAD;
596 else if (strncasecmp(clbuf->buf, "OPTIONS ", 8) == 0) mt = MT_OPTIONS;
597 else if (strncasecmp(clbuf->buf, "CONNECT ", 8) == 0) mt = MT_CONNECT;
598 else {
599 ptlogf("WARNING: invalid method in headers in worker thread!\n");
600 goto quit;
603 // check and fix query, add 'Host:' if necessary
604 if (fix_query() < 0) {
605 ptlogf("WARNING: can't parse client query in headers in worker thread!\n===\n%s===\n", clbuf->buf);
606 goto quit;
609 ptlogf("SLEEPING STARTED...");
610 coro_sleep(2500);
611 ptlogf("SLEEPING FINISHED...");
613 // use https for .googlevideo.com
614 if (mt != MT_CONNECT) {
615 const char *p = strstr(hostname, ".googlevideo.com");
616 if (p != NULL && strcmp(p, ".googlevideo.com") == 0) {
617 ptlogf("fucking youtube...");
618 fuckYoutube = 4;
619 while (buf_remove_header(clbuf->buf, "Host:") > 0) ;
620 strcat(clbuf->buf, "Host: ");
621 strcat(clbuf->buf, hostname);
622 strcat(clbuf->buf, ":443\r\n");
626 defport = ((mt == MT_CONNECT && port == 443) || (mt != MT_CONNECT && port == 80));
628 if (mt == MT_CONNECT) {
629 // SSL MITM: read SSL headers
630 if (strncmp(hostname, "fuck.", 5) == 0) {
631 // remove "fuck."
632 skip_host_blocks = 1;
633 memmove(hostname, hostname+5, strlen(hostname+5)+1);
636 if (!skip_host_blocks && is_forbidden_host(mt, hostname, port)) {
637 ptlogf("\x02WARNING: host blocked: [%s]!\n", hostname);
638 send_full(&clifd, "HTTP/1.0 403 SSL-Fucked\r\nContent-Length: 0\r\nConnection: close\r\nProxy-Connection: close\r\n\r\n", -1);
639 goto quit;
641 if (do_ssl_mitm_headers() < 0) goto quit;
642 } else if (fuckYoutube) {
643 if (do_fuck_youtube() < 0) goto quit;
646 if (is_v4_host(mt, hostname, port)) allow_v6 = 0;
647 else if (is_v6_host(mt, hostname, port)) allow_v4 = 0;
650 const repattern_t *ua_rep;
651 char och;
652 char *path_start, *path_end;
653 path_start = clbuf->buf;
654 while ((unsigned char)(*path_start) > 32) ++path_start;
655 while ((unsigned char)(*path_start) <= 32) ++path_start;
656 if (mt != MT_CONNECT) {
657 for (path_end = path_start; (unsigned char)(*path_end) > 32; ++path_end) ;
658 } else {
659 path_end = path_start;
661 och = *path_end;
662 *path_end = 0;
664 if (skip_host_blocks) {
665 // add back "fuck."
666 memmove(hostname+5, hostname, strlen(hostname)+1);
667 strcpy(hostname, "fuck");
668 hostname[4] = '.';
669 //ptlogf("\x01FUCKAGIN URI: [%s://%s:%d%s]!\n", proto, hostname, port, path_start);
671 //ptlogf("\x01FUCKAGIN URI: [%s://%s:%d%s]!\n", proto, hostname, port, path_start);
672 // check for svf/swf and fuck 'em
673 if (!skip_host_blocks) {
674 char *qq = strchr(path_start, '?');
675 if (qq == NULL) qq = path_start+strlen(path_start);
676 char *qh = strchr(path_start, '#');
677 if (qh == NULL) qh = path_start+strlen(path_start);
678 if (qh < qq) qq = qh;
679 char oldch = *qq;
680 *qq = 0;
681 char *ext = strrchr(path_start, '.');
682 if (ext != NULL) {
683 if (strcasecmp(ext, ".svg") == 0 ||
684 strcasecmp(ext, ".swf") == 0
687 ptlogf("\x02WARNING: SVG/SWF blocked: [%s://%s:%d%s]!\n", proto, hostname, port, path_start);
688 goto send_biohazard;
690 isRZX = (strcasecmp(ext, ".rzx") == 0);
692 *qq = oldch;
695 if (!skip_host_blocks && is_blocked_uri(mt, proto, hostname, port, path_start)) {
696 ptlogf("\x02WARNING: URI blocked: [%s://%s:%d%s]!\n", proto, hostname, port, path_start);
697 send_full(&clifd, "HTTP/1.0 403 Fucked\r\nContent-Length: 0\r\nConnection: close\r\nProxy-Connection: close\r\n\r\n", -1);
698 goto quit;
700 // rewrite URI
701 //int wasRewrite = 0;
702 for (int loops = 127; loops > 0; --loops) {
703 int use_loc = 0;
704 int do_again = 0;
705 rewritten_uri = rewrite_list_find(&re_rewrites, proto, hostname, port, path_start, &use_loc, &do_again);
706 if (rewritten_uri != NULL) {
707 cracked_url_t url;
708 //wasRewrite = 1;
709 if (use_loc) {
710 char *ss = malloc(2048);
711 ptlogf("\4REWRITE-LOC: [%s://%s:%d%s] ==> [%s]\n", proto, hostname, port, path_start, rewritten_uri);
712 sprintf(ss, "HTTP/1.0 307 Temporary\r\nLocation: %s\r\n\r\n", rewritten_uri);
713 send_full(&clifd, ss, -1);
714 free(ss);
715 goto quit;
717 ptlogf("\4REWRITE: [%s://%s:%d%s] ==> [%s]\n", proto, hostname, port, path_start, rewritten_uri);
718 if (crack_url(&url, rewritten_uri) == 0) {
719 int olen, nlen;
720 char *np;
721 snprintf(proto, sizeof(proto), "%s", url.scheme);
722 snprintf(hostname, sizeof(hostname), "%s", url.host);
723 port = url.port;
724 /* build new path string */
725 olen = (int)(path_end-path_start);
726 if (url.path == NULL || !url.path[0]) {
727 nlen = 1;
728 np = strdup("/");
729 } else {
730 nlen = strlen(url.path);
731 if (url.query != NULL && url.query[0]) nlen += strlen(url.query);
732 if (url.fragment != NULL && url.fragment[0]) nlen += strlen(url.fragment);
733 np = calloc(1, nlen+1);
734 strcat(np, url.path);
735 if (url.query != NULL && url.query[0]) strcat(np, url.query);
736 if (url.fragment != NULL && url.fragment[0]) strcat(np, url.fragment);
738 cracked_url_clear(&url);
739 /* fix query */
740 *path_end = och; /* restore old char */
741 //ptlogf("\6OLD HEADERS:\n%s", clbuf->buf);
742 //ptlogf("\6PEND:\n%s", path_end);
743 //ptlogf("olen=%d; nlen=%d; np=[%s]", olen, nlen, np);
744 if (olen < nlen) {
745 /* need more space */
746 int sub = nlen-olen;
747 memmove(path_end+sub, path_end, strlen(path_end)+1);
748 path_end += sub;
749 } else if (olen > nlen) {
750 /* too much space */
751 int add = olen-nlen;
752 memmove(path_end-add, path_end, strlen(path_end)+1);
753 path_end -= add;
755 //ptlogf("\5INTERIM HEADERS:\n%s", clbuf->buf);
756 memcpy(path_start, np, strlen(np));
757 *path_end = ' ';
758 //ptlogf("\7INTERIM HEADERS:\n%s", clbuf->buf);
759 free(np);
760 /* fix 'host' field */
761 while (buf_remove_header(clbuf->buf, "Host:") > 0) ;
762 strcat(clbuf->buf, "Host: ");
763 strcat(clbuf->buf, hostname);
764 strcat(clbuf->buf, "\r\n");
765 //ptlogf("\6FIXED HEADERS:\n%s", clbuf->buf);
766 /* trunk headers again */
767 och = *path_end;
768 *path_end = 0;
769 if (do_again) continue;
772 break;
774 // rewrite complete
775 if (/*skip_host_blocks && !wasRewrite &&*/ strncmp(hostname, "fuck.", 5) == 0) {
776 // remove "fuck."
777 skip_host_blocks = 1;
778 memmove(hostname, hostname+5, strlen(hostname+5)+1);
781 if (!skip_host_blocks && is_forbidden_host(mt, hostname, port)) {
782 ptlogf("\x02WARNING: host blocked: [%s]!\n", hostname);
783 send_full(&clifd, "HTTP/1.0 403 Fucked\r\nContent-Length: 0\r\nConnection: close\r\nProxy-Connection: close\r\n\r\n", -1);
784 goto quit;
787 if (mt != MT_CONNECT && !sock_is_ssl(&clifd)) {
788 const char *newhost = is_https_host(mt, hostname, port);
789 if (newhost != NULL) {
790 char *ss = malloc(2048);
791 ptlogf("\x01http-->https: [%s] ==> [%s]\n", hostname, newhost);
792 sprintf(ss, "HTTP/1.0 307 Temporary\r\nLocation: https://%s%s\r\n\r\n", newhost, path_start);
793 //ptlogf("<%s>\n", ss);
794 send_full(&clifd, ss, -1);
795 free(ss);
796 goto quit;
800 if ((allow_iframe = is_iframe_uri(mt, proto, hostname, port, path_start)) != 0) {
801 /*ptlogf("WARNING: FRAMES URI: [%s://%s:%d%s]!\n", proto, hostname, port, path_start);*/
804 if ((ua_rep = relist_find(&re_uareplace, mt, proto, hostname, port, path_start)) != NULL) {
805 //while (buf_remove_header(clbuf->buf, "User-Agent:") > 0) ;
806 //strcat(clbuf->buf, "User-Agent: Opera/9.80\r\n");
807 new_ua = ua_rep->repl;
808 //ptlogf("NEW UA: %s\n", new_ua);
809 } else {
810 new_ua = "Mozilla/5.0 (Linux; en-US; rv:22.0) Gecko/20130405 Firefox/22.0";
813 snprintf(pathbuf, sizeof(pathbuf), "%s", path_start);
814 *path_end = och;
816 // remove cookies if "K8-Kill-Cookies" header is present (for xmlhttprequest)
817 int killCookies = 0;
818 while (buf_remove_header(clbuf->buf, "K8-Kill-Cookies:") > 0) killCookies = 1;
819 if (killCookies) {
820 while (buf_remove_header(clbuf->buf, "Cookie:") > 0) ;
821 while (buf_remove_header(clbuf->buf, "Cookie2:") > 0) ;
824 //ptlogf("host: [%s]; port=%d\n", hostname, port);
825 // remove unneded headers
826 //while (buf_remove_header(clbuf->buf, "Proxy-Connection:") > 0) ;
827 while (buf_remove_header(clbuf->buf, "Connection:") > 0) ;
828 while (buf_remove_header(clbuf->buf, "Keep-Alive:") > 0) ;
829 while (buf_remove_header(clbuf->buf, "Proxy-") > 0) ;
831 while (buf_remove_header(clbuf->buf, "Accept:") > 0) ;
832 while (buf_remove_header(clbuf->buf, "Accept-Language:") > 0) ;
833 while (buf_remove_header(clbuf->buf, "Accept-Encoding:") > 0) ;
835 if (mt == MT_POST && content_length < 0) {
836 ptlogf("WARNING: no 'Content-Length' in POST in worker thread!\n");
837 goto quit;
840 while (buf_remove_header(clbuf->buf, "User-Agent:") > 0) ;
841 if (new_ua != NULL) {
842 strcat(clbuf->buf, "User-Agent: ");
843 strcat(clbuf->buf, new_ua);
844 strcat(clbuf->buf, "\r\n");
847 if (!is_referer_allowed(mt, hostname, port)) {
848 while (buf_remove_header(clbuf->buf, "Referer:") > 0) ;
849 } else {
850 // fix referer: rewrite referer if it points to foreign host
851 int do_remove = 0;
852 char *ref = buf_find_header(clbuf->buf, "Referer:");
853 if (ref != NULL) {
854 char *end = strpbrk(ref, "\r\n");
855 if (end != NULL && end > ref) {
856 static char refstr[4096];
857 memcpy(refstr, ref, end-ref);
858 refstr[end-ref] = 0;
859 ref = strstr(refstr, "://");
860 if (ref != NULL) {
861 ref += 3;
862 end = strchr(ref, '/');
863 if (end == NULL) end = ref+strlen(ref);
864 *end = 0;
865 if (strcasecmp(ref, hostname) != 0) {
866 ptlogf("WARNING: rewriting invalid referer!\n");
867 while (buf_remove_header(clbuf->buf, "Referer:") > 0) ;
868 sprintf(clbuf->buf+strlen(clbuf->buf), "Referer: %s://%s:%d%s\r\n", proto, hostname, port, pathbuf);
869 ptlogf("NEW REFERER: %s://%s:%d%s\r\n", proto, hostname, port, pathbuf);
871 } else {
872 do_remove = 1;
874 } else {
875 do_remove = 1;
877 if (do_remove) while (buf_remove_header(clbuf->buf, "Referer:") > 0) ;
880 if (mt != MT_CONNECT) {
881 strcat(clbuf->buf, "Connection: close\r\n");
882 //strcat(clbuf->buf, "\r\n");
883 //ptlogf("host: %s\nport: %s\n---\n%s---\n", hostname, portstr, clbuf->buf);
885 strcat(clbuf->buf, "Accept: */*\r\n");
886 //strcat(clbuf->buf, "Accept-Encoding: identity\r\n");
887 strcat(clbuf->buf, "Accept-Encoding: deflate\r\n");
888 strcat(clbuf->buf, "\r\n");
890 char *t = strchr(clbuf->buf, '\n'), *p = clbuf->buf;
891 *t = 0;
892 p = clbuf->buf;
893 while (*p && (unsigned char)(*p) > 32) ++p;
894 while (*p && (unsigned char)(*p) <= 32) ++p;
895 ptlogf("\1QUERY: %s %s://%s%s%s%s\n", mt_names[mt], proto, hostname, (defport ? "" : ":"), (defport ? "" : portstr), (mt != MT_CONNECT ? p : ""));
896 *t = '\n';
898 // connect to host
899 if (strcmp(hostname, "mediator") == 0) {
900 // wow, this is our control panel
901 // get path
902 char *path = clbuf->buf, *etag, *e;
903 while (*path && (unsigned char)(*path) > 32) ++path;
904 while (*path && (unsigned char)(*path) <= 32) ++path;
905 for (e = path; *e && *e != '\n' && (unsigned char)(*e) > 32; ++e) ;
906 if (e > path && e[-1] == '\r') --e;
907 *e = 0;
908 // get etag
909 etag = strcasestr(e+1, "\nIf-None-Match:");
910 if (etag != NULL) {
911 etag += 15;
912 while (*etag && *etag != '\r' && *etag != '\n' && (unsigned char)(*etag) <= 32) ++etag;
913 for (e = etag; *e && *e != '\r' && *e != '\n'; ++e) ;
914 *e = 0;
916 #include "mdcc.c"
917 send_full(&clifd, "HTTP/1.0 404 FUCK\r\nContent-Type: text/plain\r\n\r\nWTF?!", -1);
918 goto quit;
921 if (opt_noetag) {
922 while (buf_remove_header(clbuf->buf, "ETag:") > 0) ;
923 //while (buf_remove_header(clbuf->buf, "If-Modified-Since:") > 0) ;
924 //while (buf_remove_header(clbuf->buf, "If-None-Match:") > 0) ;
925 // fuck all conditionals, yeah
926 while (buf_remove_header(clbuf->buf, "If-") > 0) ;
929 if (opt_dump_client_headers) {
930 ptlogf("===\n%s===\n", clbuf->buf);
932 FILE *fo = fopen("/home/ketmar/k8prj/mediator/zx000.hdr", "a");
933 fprintf(fo, "==========\n");
934 fwrite(clbuf->buf, strlen(clbuf->buf), 1, fo);
935 fprintf(fo, "----------\n");
936 fclose(fo);
940 if (chostname[0] == 0) strcpy(chostname, hostname);
941 if (cport == 0) cport = port;
942 snprintf(portstr, sizeof(portstr), "%d", cport);
944 //againyt:
945 if (chostname[0] == '[' && chostname[strlen(chostname)-1] == ']') {
946 //IPv6
947 //memmove(chostname, chostname+1, strlen(chostname)+1);
948 //chostname[strlen(chostname)-1] = 0;
949 hn = chostname+1;
950 hn[strlen(hn)-1] = 0;
951 allow_v6 = 1;
952 } else {
953 hn = chostname;
956 memset(&me->hints, 0, sizeof(me->hints));
957 me->hints.ai_family = AF_UNSPEC;
958 me->hints.ai_socktype = SOCK_STREAM;
959 me->hints.ai_flags = 0|AI_ALL/*|AI_CANONNAME*/;
960 me->hints.ai_protocol = 0;
961 me->hints.ai_canonname = NULL;
962 me->hints.ai_addr = NULL;
963 me->hints.ai_next = NULL;
965 memset(&me->gacb, 0, sizeof(me->gacb));
967 const char *hyaddr = hypehost_find(hn);
968 if (hyaddr != NULL) {
969 ptlogf("Hyperborea host '%s': [%s]\n", hn, hyaddr);
970 me->gacb.ar_name = hyaddr;
971 } else {
972 me->gacb.ar_name = hn;
975 me->gacb.ar_service = (fuckYoutube ? "443" : portstr);
976 me->gacb.ar_request = &me->hints;
978 me->sig.sigev_notify = SIGEV_SIGNAL;
979 me->sig.sigev_signo = SIGRTMIN;
981 //gares = getaddrinfo(chostname, portstr, &hints, &adrres);
982 me->resolving_name = 1;
983 me->gacbarr = &me->gacb;
984 gares = getaddrinfo_a(GAI_NOWAIT, &me->gacbarr, 1, &me->sig);
985 if (gares != 0) {
986 ptlogf("FATAL: getaddrinfo_a: %d\n", /*gai_strerror*/(gares));
987 goto quit;
989 coro_yield();
990 if (hn != chostname) hn[strlen(hn)] = ']'; // it's safe: it was removed earlier
991 if (me->resolving_name < 0) {
992 ptlogf("FATAL: can't resolve %s\n", chostname);
993 goto quit;
995 snprintf(ipstr, sizeof(ipstr), "UNDEFINED");
996 // find address and try to connect
997 for (struct addrinfo *rp = me->gacb.ar_result; rp != NULL; rp = rp->ai_next) {
998 //printf("family: %d (%s)\n", rp->ai_family, (rp->ai_family == AF_INET ? "IPv4" : (rp->ai_family == AF_INET6 ? "IPv6" : "unknown")));
999 if (rp->ai_family == AF_INET && allow_v4) {
1000 struct sockaddr_in *sav4 = (void *)rp->ai_addr;
1001 inet_ntop(rp->ai_addr->sa_family, &sav4->sin_addr, ipstr, sizeof(ipstr));
1002 ptlogf("connecting to IPv4: %s:%d\n", ipstr, cport);
1003 //sav4->sin_port = htons(cport);
1004 } else if (rp->ai_family == AF_INET6 && allow_v6) {
1005 struct sockaddr_in6 *sav6 = (void *)rp->ai_addr;
1006 inet_ntop(rp->ai_addr->sa_family, &sav6->sin6_addr, ipstr+1, sizeof(ipstr)-2);
1007 ipstr[0] = '[';
1008 strcat(ipstr, "]");
1009 ptlogf("connecting to IPv6: %s:%d\n", ipstr, cport);
1010 //sav6->sin6_port = htons(cport);
1011 sav6->sin6_flowinfo = 0; //???
1012 } else {
1013 continue;
1015 srvfd.fd = socket(rp->ai_family, SOCK_STREAM, 0);
1016 if (srvfd.fd < 0) {
1017 ptlogf("FATAL: can't create socket!\n");
1018 freeaddrinfo(me->gacb.ar_result);
1019 send_full(&clifd, "HTTP/1.0 500 Can't create socket!\r\nContent-Length: 0\r\nConnection: close\r\nProxy-Connection: close\r\n\r\n", -1);
1020 goto quit;
1022 socket_set_nonblocking(srvfd.fd);
1023 //socket_set_timeouts(srvfd.fd, TIMEOUT_SERVER);
1024 connect_again:
1025 if (connect(srvfd.fd, rp->ai_addr, rp->ai_addrlen) == 0) {
1026 ptlogf("connected to %s:%d\n", ipstr, cport);
1027 break;
1029 if (errno == EINTR) goto connect_again;
1030 if (errno != EINPROGRESS) {
1031 connection_failed:
1032 freeaddrinfo(me->gacb.ar_result);
1033 ptlogf("connection failed: %s:%d\n", ipstr, cport);
1034 sock_abort(&srvfd);
1035 send_full(&clifd, "HTTP/1.0 404 Can't connect to host!\r\nContent-Length: 0\r\nConnection: close\r\nProxy-Connection: close\r\n\r\n", -1);
1036 goto quit;
1038 // wait for connection to complete
1040 coro_fd_add(sock_fd(&srvfd), SAR_WRITE, TIMEOUT_SERVER);
1041 coro_yield();
1042 if (coro_fd_check(sock_fd(&srvfd)) == SAR_WRITE) {
1043 // check for error
1044 int err = 1;
1045 socklen_t len = sizeof(err);
1046 if (getsockopt(sock_fd(&srvfd), SOL_SOCKET, SO_ERROR, &err, &len) != 0 || err != 0) goto connection_failed;
1047 } else {
1048 goto connection_failed;
1050 ptlogf("connected to %s:%d\n", ipstr, cport);
1051 // disable Nagle
1053 int state = 1;
1054 setsockopt(sock_fd(&srvfd), IPPROTO_TCP, TCP_NODELAY, &state, sizeof(state));
1056 break;
1059 freeaddrinfo(me->gacb.ar_result);
1061 if (!sock_is_alive(&srvfd)) {
1062 // alas
1063 //if (mt == MT_CONNECT) send_full(&clifd, "HTTP/1.0 404 Not Found\r\n\r\n", -1);
1064 send_full(&clifd, "HTTP/1.0 410 Gone\r\nContent-Length: 0\r\nConnection: close\r\nProxy-Connection: close\r\n\r\n", -1);
1065 goto quit;
1068 if (sock_handshake(&srvfd) < 0) {
1069 fuckYoutube = 0;
1070 if (--fuckYoutube < 0) {
1071 send_full(&clifd, "HTTP/1.0 500 SSL fucked\r\nContent-Type: text/plain\r\n\r\nSSL fucked", -1);
1072 goto quit;
1074 // this seems to be unnecessary, so it's disabled
1076 ptlogf("YTB(%d): sleeping 10 seconds...\n", fuckYoutube);
1077 coro_sleep(10000);
1078 ptlogf("YTB(%d): again!\n", fuckYoutube);
1079 sock_close(&srvfd);
1080 if (fake_cert != NULL) {
1081 cert_free(fake_cert);
1082 free(fake_cert);
1083 fake_cert = NULL;
1085 if (do_fuck_youtube() < 0) {
1086 send_full(&clifd, "HTTP/1.0 500 SSL fucked YT\r\nContent-Type: text/plain\r\n\r\nSSL fucked YT", -1);
1087 goto quit;
1089 goto againyt;
1093 if (mt == MT_CONNECT) {
1094 if (send_full(&clifd, "HTTP/1.0 200 OK\r\nConnection: close\r\nProxy-Connection: close\r\n\r\n", -1) == 0) do_tunnel();
1095 goto quit;
1098 //ptlogf("sending server headers (%d)\n", strlen(clbuf->buf));
1099 // send headers to server
1102 FILE *fo = fopen("/home/ketmar/k8prj/mediator/zx000.hdr", "a");
1103 //fprintf(fo, "==========\n");
1104 fwrite(clbuf->buf, strlen(clbuf->buf), 1, fo);
1105 //fprintf(fo, "----------\n");
1106 fclose(fo);
1109 if (send_full(&srvfd, clbuf->buf, -1) < 0) {
1110 ptlogf("WARNING: header sending failed: %s:%d\n", ipstr, port);
1111 goto quit;
1114 // post? transfer data
1115 if (content_length > 0) {
1116 #ifdef DUMP_DATA
1117 int dumpfd = open("zdump_post.log", O_WRONLY|O_CREAT|O_TRUNC, 0644);
1118 write(dumpfd, "--------\n", 9);
1119 #endif
1120 ptlogf("sending POST data to %s:%d\n", ipstr, port);
1121 while (content_length > 0) {
1122 int toread = (content_length > clbuf->size ? clbuf->size : (int)content_length);
1123 int rd = sock_recv(&clifd, clbuf->buf, toread, MSG_WAITALL);
1124 if (rd <= 0) {
1125 #ifdef DUMP_DATA
1126 close(dumpfd);
1127 #endif
1128 goto quit;
1130 #ifdef DUMP_DATA
1131 write(dumpfd, clbuf->buf, rd);
1132 #endif
1133 if (send_full(&srvfd, clbuf->buf, rd) < 0) {
1134 #ifdef DUMP_DATA
1135 close(dumpfd);
1136 #endif
1137 goto quit;
1139 content_length -= rd;
1141 #ifdef DUMP_DATA
1142 close(dumpfd);
1143 #endif
1145 //shutdown(clifd.fd, SHUT_RD);
1146 //shutdown(srvfd.fd, SHUT_WR);
1147 // get and send server headers
1148 ptlogf("reading server headers from %s:%d\n", ipstr, port);
1149 if (read_headers(&srvfd) < 0) goto quit;
1150 if (opt_dump_server_headers) ptlogf("+++\n%s+++\n", clbuf->buf);
1151 // remove SVGs
1152 if (!skip_host_blocks) {
1153 //Content-Type: image/svg+xml
1154 char *ct = buf_find_header(clbuf->buf, "Content-Type:");
1155 if (ct != NULL) {
1156 int doReplace = 0;
1157 char *end = strpbrk(ct, "\r\n");
1158 char oc = *end;
1159 *end = 0;
1161 if (compareContentType(ct, "text/x-dsrc") || compareContentType(ct, "text/x-asm")) {
1162 // this is for dlang bugzilla and opera: i tired of opera trying to open attaches in external app
1163 *end = oc;
1164 while (buf_remove_header(clbuf->buf, "Content-Type:") > 0) ;
1165 while (buf_remove_header(clbuf->buf, "Content-disposition:") > 0) ;
1166 strcat(clbuf->buf, "Content-Type: text/plain\r\n");
1167 } else {
1168 if (strcmp(ct, "image/svg+xml") == 0) doReplace = 1;
1169 else if (strcmp(ct, "application/x-shockwave-flash") == 0) doReplace = 1;
1170 else if (strcmp(ct, "application/shockwave-flash") == 0) doReplace = 1;
1171 if (doReplace) {
1172 // got it, replace with another one
1173 send_biohazard:
1174 ptlogf("\x02WARNING: SVG REPLACEMENT!");
1175 //if (send_full(&clifd, "HTTP/1.0 200 OK\r\nContent-Type: image/png\r\n\r\n", -1) < 0) goto quit;
1176 //send_full(&clifd, hazardPNG, sizeof(hazardPNG));
1177 send_full(&clifd, hazardSVG, -1);
1178 goto quit;
1180 *end = oc;
1184 if (isRZX) {
1185 while (buf_remove_header(clbuf->buf, "Content-Type:") > 0) ;
1186 strcat(clbuf->buf, "Content-Type: application/rzx\r\n");
1188 while (buf_remove_header(clbuf->buf, "Connection:") > 0) ;
1189 while (buf_remove_header(clbuf->buf, "Keep-Alive:") > 0) ;
1190 strcat(clbuf->buf, "Connection: close\r\n");
1191 strcat(clbuf->buf, "Proxy-Connection: close\r\n");
1193 if (!allow_iframe) {
1194 strcat(clbuf->buf, "X-Frame-Options: DENY\r\n");
1195 //ptlogf("\1HOST: [%s]\n", chostname);
1196 } else {
1197 while (buf_remove_header(clbuf->buf, "X-Frame-Options:") > 0) ;
1199 // other shit
1200 while (buf_remove_header(clbuf->buf, "X-Content-Type-Options:") > 0) ;
1201 while (buf_remove_header(clbuf->buf, "X-XSS-Protection:") > 0) ;
1202 while (buf_remove_header(clbuf->buf, "Alternate-Protocol:") > 0) ;
1204 strcat(clbuf->buf, "X-DNSPrefetch-Control: off\r\n");
1205 strcat(clbuf->buf, "Vary: Accept-Encoding\r\n");
1206 strcat(clbuf->buf, "\r\n");
1207 if (fuckYoutube) {
1208 // change Location from https to http (we'll fuck it later)
1209 char *lc = strcasestr(clbuf->buf, "\r\nLocation:");
1210 if (lc != NULL) {
1211 lc += 11; // skip field name
1212 while (*lc && (*lc == '\t' || *lc == ' ')) ++lc; // skip spaces
1213 if (strncmp(lc, "https:", 6) == 0) {
1214 // yes, this is https redirection, remove 's'
1215 lc += 4; // skip 'http'
1216 memmove(lc, lc+1, strlen(lc));
1217 ptlogf("YT: location fixed");
1221 if (opt_dump_server_headers) ptlogf("+++\n%s+++\n", clbuf->buf);
1222 ptlogf("sending server headers from %s:%d\n", ipstr, port);
1223 if (send_full(&clifd, clbuf->buf, -1) < 0) {
1224 ptlogf("WARNING: header sending failed\n");
1225 goto quit;
1227 // now just tunnel
1228 if (content_length >= 0) {
1229 #ifdef DUMP_DATA
1230 int dumpfd = open("zdump_reply.log", O_WRONLY|O_CREAT|O_TRUNC, 0644);
1231 write(dumpfd, "--------\n", 9);
1232 #endif
1233 uint64_t stt, total = 0;
1234 ptlogf("transferring %llu bytes from %s:%d\n", content_length, ipstr, port);
1235 stt = k8clock();
1236 while (content_length > 0) {
1237 int toread = (content_length > clbuf->size ? clbuf->size : (int)content_length);
1238 int rd = sock_recv(&srvfd, clbuf->buf, toread, MSG_WAITALL);
1239 if (rd <= 0) {
1240 ptlogf("WARNING: error %d while reading from %s:%d\n", rd, ipstr, port);
1241 #ifdef DUMP_DATA
1242 close(dumpfd);
1243 #endif
1244 goto quit;
1246 #ifdef DUMP_DATA
1247 write(dumpfd, clbuf->buf, rd);
1248 #endif
1249 if (send_full(&clifd, clbuf->buf, rd) < 0) {
1250 ptlogf("WARNING: error while sending from %s:%d\n", ipstr, port);
1251 #ifdef DUMP_DATA
1252 close(dumpfd);
1253 #endif
1254 goto quit;
1256 content_length -= rd;
1257 total += rd;
1259 stt = k8clock()-stt;
1260 #ifdef DUMP_DATA
1261 close(dumpfd);
1262 #endif
1263 if (stt >= 500) {
1264 ptlogf("transfer complete, average speed: %.2f KB/sec\n", ((double)total/(double)stt)*(1000.0/1024.0));
1266 } else {
1267 do_tunnel();
1269 ptlogf("session with %s:%d complete\n", ipstr, port);
1270 quit:
1271 // negative fds are OK
1272 sock_close(&clifd);
1273 sock_close(&srvfd);
1274 if (fake_cert != NULL) {
1275 cert_free(fake_cert);
1276 free(fake_cert);
1277 fake_cert = NULL;
1279 //ptlogf("*** dead connection! (workers=%d)\n", coro_count-1);
1280 coro_kill(me);