5 #define skxabort skclose
8 static const char *get_content_type (const char *fname
) {
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
;
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];
31 int64_t content_length
= -1;
32 char ipstr
[INET6_ADDRSTRLEN
];
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;
39 //struct addrinfo *adrres;
41 const char *new_ua
= NULL
;
42 static char pathbuf
[4096];
43 const char *rewritten_uri
= NULL
;
45 x509_crt
*fake_cert
= NULL
; // malloc()ed
50 int copy_and_parse_hostname (const void *str
, int len
) {
51 const uint8_t *hh
= (const uint8_t *)str
;
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
]);
60 if (strncmp(hostname
, "fuck.", 5) == 0) {
62 memmove(hostname
, hostname
+5, strlen(hostname
+5)+1);
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] == '[') {
75 char *pp
= strrchr(hostname
, ']');
76 if (pp
!= NULL
&& pp
[1] == ':' && pp
[2] >= '0' && pp
[2] <= '9') {
78 //TODO: error checking!
79 port
= atoi((char *)(pp
+2));
81 if (port
< 1 || port
> 65535) return -1;
84 char *pp
= strrchr(hostname
, ':');
85 if (pp
!= NULL
&& pp
[1] >= '0' && pp
[1] <= '9') {
87 //TODO: error checking!
88 port
= atoi((char *)(pp
+1));
90 if (port
< 1 || port
> 65535) return -1;
93 if (!hostname
[0]) return -1;
94 //if (port < 1) port = 80;
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;
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
);
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;
129 if (strncasecmp((const char *)qaddr
, "http://", 7) == 0) {
133 strcpy(proto
, "http");
134 } else if (strncasecmp((const char *)qaddr
, "https://", 8) == 0) {
138 strcpy(proto
, "https");
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()
155 int read_headers (sock_t
*sk
) {
157 // now read HTTP headers
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");
167 if (!sock_is_ssl(sk
)) {
169 rd
= sock_recv(sk
, clbuf
->buf
+clbuf
->used
, MAX_HDR_SIZE
-clbuf
->used
, MSG_PEEK
);
171 ptlogf("WARNING: headers recv error in worker thread (connection closed;fd=%d)!\n", sock_fd(sk
));
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') {
184 if (hdr_size
+2 < sz
&& clbuf
->buf
[hdr_size
+1] == '\r' && clbuf
->buf
[hdr_size
+2] == '\n') {
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");
194 if (!found
&& rd
== 0) {
195 ptlogf("WARNING: connection closed before headers received in worker thread!\n");
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");
204 clbuf
->used
+= recv(sock_fd(sk
), clbuf
->buf
+clbuf
->used
, hdr_size
-clbuf
->used
, 0); // this will always succeed
207 clbuf
->used
+= recv(sock_fd(sk
), clbuf
->buf
+clbuf
->used
, rd
, 0); // this will always succeed
210 rd
= sock_recv(sk
, clbuf
->buf
+clbuf
->used
, 1, MSG_WAITALL
);
212 ptlogf("WARNING: SSL header reader failed!\n");
216 int lpos
= clbuf
->used
-1;
217 if (clbuf
->buf
[lpos
] == '\n') {
219 if (lpos
>= 0 && clbuf
->buf
[lpos
] == '\r') --lpos
; // remove CR
220 if (lpos
>= 0 && clbuf
->buf
[lpos
] == '\n') {
221 hdr_size
= clbuf
->used
;
227 // check headers size
229 ptlogf("WARNING: headers too small in worker thread!\n");
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");
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:");
245 if (*cl
>= '0' && *cl
<= '9') {
247 while (*cl
&& (unsigned char)(*cl
) > 32) {
248 if (*cl
>= '0' && *cl
<= '9') {
249 content_length
= content_length
*10+cl
[0]-'0';
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:");
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);
273 ptlogf("tunneling: %s connection closed (2) (0)\n", who
);
279 // just check if connection is alive
280 int res = asnet_recv_check(sock_fd(sk));
282 ptlogf("tunneling: %s connection closed (2) (1)\n", who);
290 void do_tunnel (void) {
291 ptlogf("established tunnel to %s:%d\n", ipstr
, port
);
294 while (sock_is_alive(&srvfd
) && sock_is_alive(&clifd
)) {
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
);
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");
311 if (cl_r
&SAR_TIMEOUT
) {
312 ptlogf("tunneling: client connection timeouted (0)\n");
319 // can read from server
320 if (do_xbuf_rd(&srvfd
, svbuf
, "server") == 0) {
326 // can read from client
327 if (do_xbuf_rd(&clifd
, clbuf
, "client") == 0) {
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
);
337 ptlogf("tunneling: server connection closed (1)\n");
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
);
348 ptlogf("tunneling: client connection closed (1)\n");
354 // now we have one or zero fds opened
355 if (sock_is_alive(&srvfd
) && sock_is_alive(&clifd
)) {
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
);
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
);
368 //ptlogf("tunnel: sending server data: error (0)\n");
372 //ptlogf("tunnel: sending server data: error (1)\n");
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
);
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
);
385 //ptlogf("tunnel: sending client data: error (0)\n");
389 //ptlogf("tunnel: sending client data: error (1)\n");
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
) {
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
);
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
;
411 if (build_etag(etag
, fname
) != 0) {
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);
417 if (ftag
!= NULL
&& strcmp(ftag
, etag
) == 0) {
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);
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
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) {
435 if (filesize
> 0) send_full(&clifd
, filedata
, filesize
);
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];
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);
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
);
453 write(fd
, svbuf
->buf
, rd
);
454 content_length
-= rd
;
457 // 'rename' is atomic operation
458 if (rename(nbuf
, fname
) < 0) {
459 ptlogf("\2CAN'T RENAME '%s' to '%s'!\n", nbuf
, fname
);
462 send_full(&clifd
, "HTTP/1.0 200 OK\r\nConnection: close\r\n\r\n", -1);
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) {
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");
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
);
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
;
523 ptlogf("WARNING: invalid method in headers in worker thread!\n");
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
);
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");
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
);
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
));
583 setsockopt(sock_fd(&clifd
), IPPROTO_TCP
, TCP_NODELAY
, &state
, sizeof(state
));
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
);
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
;
599 ptlogf("WARNING: invalid method in headers in worker thread!\n");
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
);
609 ptlogf("SLEEPING STARTED...");
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...");
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) {
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);
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
;
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
) ;
659 path_end
= path_start
;
664 if (skip_host_blocks
) {
666 memmove(hostname
+5, hostname
, strlen(hostname
)+1);
667 strcpy(hostname
, "fuck");
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
;
681 char *ext
= strrchr(path_start
, '.');
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
);
690 isRZX
= (strcasecmp(ext
, ".rzx") == 0);
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);
701 //int wasRewrite = 0;
702 for (int loops
= 127; loops
> 0; --loops
) {
705 rewritten_uri
= rewrite_list_find(&re_rewrites
, proto
, hostname
, port
, path_start
, &use_loc
, &do_again
);
706 if (rewritten_uri
!= NULL
) {
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);
717 ptlogf("\4REWRITE: [%s://%s:%d%s] ==> [%s]\n", proto
, hostname
, port
, path_start
, rewritten_uri
);
718 if (crack_url(&url
, rewritten_uri
) == 0) {
721 snprintf(proto
, sizeof(proto
), "%s", url
.scheme
);
722 snprintf(hostname
, sizeof(hostname
), "%s", url
.host
);
724 /* build new path string */
725 olen
= (int)(path_end
-path_start
);
726 if (url
.path
== NULL
|| !url
.path
[0]) {
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
);
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);
745 /* need more space */
747 memmove(path_end
+sub
, path_end
, strlen(path_end
)+1);
749 } else if (olen
> nlen
) {
752 memmove(path_end
-add
, path_end
, strlen(path_end
)+1);
755 //ptlogf("\5INTERIM HEADERS:\n%s", clbuf->buf);
756 memcpy(path_start
, np
, strlen(np
));
758 //ptlogf("\7INTERIM HEADERS:\n%s", clbuf->buf);
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 */
769 if (do_again
) continue;
775 if (/*skip_host_blocks && !wasRewrite &&*/ strncmp(hostname
, "fuck.", 5) == 0) {
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);
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);
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);
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
);
816 // remove cookies if "K8-Kill-Cookies" header is present (for xmlhttprequest)
818 while (buf_remove_header(clbuf
->buf
, "K8-Kill-Cookies:") > 0) killCookies
= 1;
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");
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) ;
850 // fix referer: rewrite referer if it points to foreign host
852 char *ref
= buf_find_header(clbuf
->buf
, "Referer:");
854 char *end
= strpbrk(ref
, "\r\n");
855 if (end
!= NULL
&& end
> ref
) {
856 static char refstr
[4096];
857 memcpy(refstr
, ref
, end
-ref
);
859 ref
= strstr(refstr
, "://");
862 end
= strchr(ref
, '/');
863 if (end
== NULL
) end
= ref
+strlen(ref
);
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
);
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
;
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
: ""));
899 if (strcmp(hostname
, "mediator") == 0) {
900 // wow, this is our control panel
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
;
909 etag
= strcasestr(e
+1, "\nIf-None-Match:");
912 while (*etag
&& *etag
!= '\r' && *etag
!= '\n' && (unsigned char)(*etag
) <= 32) ++etag
;
913 for (e
= etag
; *e
&& *e
!= '\r' && *e
!= '\n'; ++e
) ;
917 send_full(&clifd
, "HTTP/1.0 404 FUCK\r\nContent-Type: text/plain\r\n\r\nWTF?!", -1);
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");
940 if (chostname
[0] == 0) strcpy(chostname
, hostname
);
941 if (cport
== 0) cport
= port
;
942 snprintf(portstr
, sizeof(portstr
), "%d", cport
);
945 if (chostname
[0] == '[' && chostname
[strlen(chostname
)-1] == ']') {
947 //memmove(chostname, chostname+1, strlen(chostname)+1);
948 //chostname[strlen(chostname)-1] = 0;
950 hn
[strlen(hn
)-1] = 0;
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
;
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
);
986 ptlogf("FATAL: getaddrinfo_a: %d\n", /*gai_strerror*/(gares
));
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
);
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);
1009 ptlogf("connecting to IPv6: %s:%d\n", ipstr
, cport
);
1010 //sav6->sin6_port = htons(cport);
1011 sav6
->sin6_flowinfo
= 0; //???
1015 srvfd
.fd
= socket(rp
->ai_family
, SOCK_STREAM
, 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);
1022 socket_set_nonblocking(srvfd
.fd
);
1023 //socket_set_timeouts(srvfd.fd, TIMEOUT_SERVER);
1025 if (connect(srvfd
.fd
, rp
->ai_addr
, rp
->ai_addrlen
) == 0) {
1026 ptlogf("connected to %s:%d\n", ipstr
, cport
);
1029 if (errno
== EINTR
) goto connect_again
;
1030 if (errno
!= EINPROGRESS
) {
1032 freeaddrinfo(me
->gacb
.ar_result
);
1033 ptlogf("connection failed: %s:%d\n", ipstr
, cport
);
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);
1038 // wait for connection to complete
1040 coro_fd_add(sock_fd(&srvfd
), SAR_WRITE
, TIMEOUT_SERVER
);
1042 if (coro_fd_check(sock_fd(&srvfd
)) == SAR_WRITE
) {
1045 socklen_t len
= sizeof(err
);
1046 if (getsockopt(sock_fd(&srvfd
), SOL_SOCKET
, SO_ERROR
, &err
, &len
) != 0 || err
!= 0) goto connection_failed
;
1048 goto connection_failed
;
1050 ptlogf("connected to %s:%d\n", ipstr
, cport
);
1054 setsockopt(sock_fd(&srvfd
), IPPROTO_TCP
, TCP_NODELAY
, &state
, sizeof(state
));
1059 freeaddrinfo(me
->gacb
.ar_result
);
1061 if (!sock_is_alive(&srvfd
)) {
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);
1068 if (sock_handshake(&srvfd
) < 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);
1074 // this seems to be unnecessary, so it's disabled
1076 ptlogf("YTB(%d): sleeping 10 seconds...\n", fuckYoutube);
1078 ptlogf("YTB(%d): again!\n", fuckYoutube);
1080 if (fake_cert != NULL) {
1081 cert_free(fake_cert);
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);
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();
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");
1109 if (send_full(&srvfd
, clbuf
->buf
, -1) < 0) {
1110 ptlogf("WARNING: header sending failed: %s:%d\n", ipstr
, port
);
1114 // post? transfer data
1115 if (content_length
> 0) {
1117 int dumpfd
= open("zdump_post.log", O_WRONLY
|O_CREAT
|O_TRUNC
, 0644);
1118 write(dumpfd
, "--------\n", 9);
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
);
1131 write(dumpfd
, clbuf
->buf
, rd
);
1133 if (send_full(&srvfd
, clbuf
->buf
, rd
) < 0) {
1139 content_length
-= rd
;
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
);
1152 if (!skip_host_blocks
) {
1153 //Content-Type: image/svg+xml
1154 char *ct
= buf_find_header(clbuf
->buf
, "Content-Type:");
1157 char *end
= strpbrk(ct
, "\r\n");
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
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");
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;
1172 // got it, replace with another one
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);
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);
1197 while (buf_remove_header(clbuf
->buf
, "X-Frame-Options:") > 0) ;
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");
1208 // change Location from https to http (we'll fuck it later)
1209 char *lc
= strcasestr(clbuf
->buf
, "\r\nLocation:");
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");
1228 if (content_length
>= 0) {
1230 int dumpfd
= open("zdump_reply.log", O_WRONLY
|O_CREAT
|O_TRUNC
, 0644);
1231 write(dumpfd
, "--------\n", 9);
1233 uint64_t stt
, total
= 0;
1234 ptlogf("transferring %llu bytes from %s:%d\n", content_length
, ipstr
, port
);
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
);
1240 ptlogf("WARNING: error %d while reading from %s:%d\n", rd
, ipstr
, port
);
1247 write(dumpfd
, clbuf
->buf
, rd
);
1249 if (send_full(&clifd
, clbuf
->buf
, rd
) < 0) {
1250 ptlogf("WARNING: error while sending from %s:%d\n", ipstr
, port
);
1256 content_length
-= rd
;
1259 stt
= k8clock()-stt
;
1264 ptlogf("transfer complete, average speed: %.2f KB/sec\n", ((double)total
/(double)stt
)*(1000.0/1024.0));
1269 ptlogf("session with %s:%d complete\n", ipstr
, port
);
1271 // negative fds are OK
1274 if (fake_cert
!= NULL
) {
1275 cert_free(fake_cert
);
1279 //ptlogf("*** dead connection! (workers=%d)\n", coro_count-1);