Browse code

http: Add a new protocol for opening connections via http proxies

This opens a plain TCP connection through the proxy via the
CONNECT HTTP method. Normally, this is allowed for connections
on port 443, but can in general be used to allow connections
to any port (depending on proxy configuration), and could thus
be used to tunnel any TCP connection via a HTTP proxy.

Signed-off-by: Martin Storsjö <martin@martin.st>

Martin Storsjö authored on 2011/11/10 21:53:16
Showing 3 changed files
... ...
@@ -242,6 +242,7 @@ void av_register_all(void)
242 242
     REGISTER_PROTOCOL (FILE, file);
243 243
     REGISTER_PROTOCOL (GOPHER, gopher);
244 244
     REGISTER_PROTOCOL (HTTP, http);
245
+    REGISTER_PROTOCOL (HTTPPROXY, httpproxy);
245 246
     REGISTER_PROTOCOL (HTTPS, https);
246 247
     REGISTER_PROTOCOL (MMSH, mmsh);
247 248
     REGISTER_PROTOCOL (MMST, mmst);
... ...
@@ -578,3 +578,118 @@ URLProtocol ff_https_protocol = {
578 578
     .priv_data_class     = &https_context_class,
579 579
 };
580 580
 #endif
581
+
582
+#if CONFIG_HTTPPROXY_PROTOCOL
583
+static int http_proxy_close(URLContext *h)
584
+{
585
+    HTTPContext *s = h->priv_data;
586
+    if (s->hd)
587
+        ffurl_close(s->hd);
588
+    return 0;
589
+}
590
+
591
+static int http_proxy_open(URLContext *h, const char *uri, int flags)
592
+{
593
+    HTTPContext *s = h->priv_data;
594
+    char hostname[1024], hoststr[1024];
595
+    char auth[1024], pathbuf[1024], *path;
596
+    char line[1024], lower_url[100];
597
+    int port, ret = 0;
598
+    HTTPAuthType cur_auth_type;
599
+    char *authstr;
600
+
601
+    h->is_streamed = 1;
602
+
603
+    av_url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port,
604
+                 pathbuf, sizeof(pathbuf), uri);
605
+    ff_url_join(hoststr, sizeof(hoststr), NULL, NULL, hostname, port, NULL);
606
+    path = pathbuf;
607
+    if (*path == '/')
608
+        path++;
609
+
610
+    ff_url_join(lower_url, sizeof(lower_url), "tcp", NULL, hostname, port,
611
+                NULL);
612
+redo:
613
+    ret = ffurl_open(&s->hd, lower_url, AVIO_FLAG_READ_WRITE,
614
+                     &h->interrupt_callback, NULL);
615
+    if (ret < 0)
616
+        return ret;
617
+
618
+    authstr = ff_http_auth_create_response(&s->proxy_auth_state, auth,
619
+                                           path, "CONNECT");
620
+    snprintf(s->buffer, sizeof(s->buffer),
621
+             "CONNECT %s HTTP/1.1\r\n"
622
+             "Host: %s\r\n"
623
+             "Connection: close\r\n"
624
+             "%s%s"
625
+             "\r\n",
626
+             path,
627
+             hoststr,
628
+             authstr ? "Proxy-" : "", authstr ? authstr : "");
629
+    av_freep(&authstr);
630
+
631
+    if ((ret = ffurl_write(s->hd, s->buffer, strlen(s->buffer))) < 0)
632
+        goto fail;
633
+
634
+    s->buf_ptr = s->buffer;
635
+    s->buf_end = s->buffer;
636
+    s->line_count = 0;
637
+    s->filesize = -1;
638
+    cur_auth_type = s->proxy_auth_state.auth_type;
639
+
640
+    for (;;) {
641
+        int new_loc;
642
+        // Note: This uses buffering, potentially reading more than the
643
+        // HTTP header. If tunneling a protocol where the server starts
644
+        // the conversation, we might buffer part of that here, too.
645
+        // Reading that requires using the proper ffurl_read() function
646
+        // on this URLContext, not using the fd directly (as the tls
647
+        // protocol does). This shouldn't be an issue for tls though,
648
+        // since the client starts the conversation there, so there
649
+        // is no extra data that we might buffer up here.
650
+        if (http_get_line(s, line, sizeof(line)) < 0) {
651
+            ret = AVERROR(EIO);
652
+            goto fail;
653
+        }
654
+
655
+        av_dlog(h, "header='%s'\n", line);
656
+
657
+        ret = process_line(h, line, s->line_count, &new_loc);
658
+        if (ret < 0)
659
+            goto fail;
660
+        if (ret == 0)
661
+            break;
662
+        s->line_count++;
663
+    }
664
+    if (s->http_code == 407 && cur_auth_type == HTTP_AUTH_NONE &&
665
+        s->proxy_auth_state.auth_type != HTTP_AUTH_NONE) {
666
+        ffurl_close(s->hd);
667
+        s->hd = NULL;
668
+        goto redo;
669
+    }
670
+
671
+    if (s->http_code < 400)
672
+        return 0;
673
+    ret = AVERROR(EIO);
674
+
675
+fail:
676
+    http_proxy_close(h);
677
+    return ret;
678
+}
679
+
680
+static int http_proxy_write(URLContext *h, const uint8_t *buf, int size)
681
+{
682
+    HTTPContext *s = h->priv_data;
683
+    return ffurl_write(s->hd, buf, size);
684
+}
685
+
686
+URLProtocol ff_httpproxy_protocol = {
687
+    .name                = "httpproxy",
688
+    .url_open            = http_proxy_open,
689
+    .url_read            = http_buf_read,
690
+    .url_write           = http_proxy_write,
691
+    .url_close           = http_proxy_close,
692
+    .url_get_file_handle = http_get_file_handle,
693
+    .priv_data_size      = sizeof(HTTPContext),
694
+};
695
+#endif
... ...
@@ -24,7 +24,7 @@
24 24
 #include "libavutil/avutil.h"
25 25
 
26 26
 #define LIBAVFORMAT_VERSION_MAJOR 53
27
-#define LIBAVFORMAT_VERSION_MINOR 14
27
+#define LIBAVFORMAT_VERSION_MINOR 15
28 28
 #define LIBAVFORMAT_VERSION_MICRO  0
29 29
 
30 30
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \