[FFmpeg-devel] [PATCH 2/2] add socks5 support for tcp clients

levizhao at live.cn levizhao at live.cn
Sat Jun 13 18:17:41 EEST 2020


From: zhaoyi <levizhao at live.cn>

---
 libavformat/tcp.c | 204 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 204 insertions(+)

diff --git a/libavformat/tcp.c b/libavformat/tcp.c
index 2198e0f00e..ca6ba2867e 100644
--- a/libavformat/tcp.c
+++ b/libavformat/tcp.c
@@ -45,6 +45,7 @@ typedef struct TCPContext {
 #if !HAVE_WINSOCK2_H
     int tcp_mss;
 #endif /* !HAVE_WINSOCK2_H */
+    char *socks_proxy;
 } TCPContext;
 
 #define OFFSET(x) offsetof(TCPContext, x)
@@ -52,6 +53,7 @@ typedef struct TCPContext {
 #define E AV_OPT_FLAG_ENCODING_PARAM
 static const AVOption options[] = {
     { "listen",          "Listen for incoming connections",  OFFSET(listen),         AV_OPT_TYPE_INT, { .i64 = 0 },     0,       2,       .flags = D|E },
+    { "socks_proxy",     "set socks proxy for connection", OFFSET(socks_proxy),   AV_OPT_TYPE_STRING, { .str = NULL }, 0,     0,       .flags = D },
     { "timeout",     "set timeout (in microseconds) of socket I/O operations", OFFSET(rw_timeout),     AV_OPT_TYPE_INT, { .i64 = -1 },         -1, INT_MAX, .flags = D|E },
     { "listen_timeout",  "Connection awaiting timeout (in milliseconds)",      OFFSET(listen_timeout), AV_OPT_TYPE_INT, { .i64 = -1 },         -1, INT_MAX, .flags = D|E },
     { "send_buffer_size", "Socket send buffer size (in bytes)",                OFFSET(send_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 },         -1, INT_MAX, .flags = D|E },
@@ -213,6 +215,207 @@ static int tcp_open(URLContext *h, const char *uri, int flags)
     return ret;
 }
 
+/* use options for socks5 proxy from input */
+static int tcp_open2(URLContext *h, const char *uri, int flags, AVDictionary **options) {
+    struct addrinfo hints = { 0 }, *ai, *cur_ai;
+    int port, fd = -1;
+    TCPContext *s = h->priv_data;
+    const char *p;
+    char buf[256];
+    int ret;
+    char hostname[1024],proto[1024],path[1024];
+    char portstr[10];
+    /* current just processing the socks5 non authentication */
+    const char *proxy_path;
+    char hostname_proxy[1024] = { 0 },portstr_proxy[10] = { 0 },proto_proxy[1024] = { 0 },path_proxy[1024] = { 0 };
+    int use_proxy = 0;
+    proxy_path = getenv("socks_proxy");
+    use_proxy = proxy_path && av_strstart(proxy_path, "socks5://", NULL);
+    if(use_proxy) {
+        av_url_split(proto_proxy, sizeof(proto_proxy), NULL, 0, hostname_proxy, sizeof(hostname_proxy),
+            &port, path_proxy, sizeof(path_proxy), proxy_path);
+        port = (port > 0 && port < 65536) ? port : 1080;
+        snprintf(portstr_proxy, sizeof(portstr_proxy), "%d", port);
+    }
+
+    s->open_timeout = 5000000;
+    av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
+        &port, path, sizeof(path), uri);
+    if (strcmp(proto, "tcp"))
+        return AVERROR(EINVAL);
+    if (port <= 0 || port >= 65536) {
+        av_log(h, AV_LOG_ERROR, "Port missing in uri\n");
+        return AVERROR(EINVAL);
+    }
+    p = strchr(uri, '?');
+    if (p) {
+        if (av_find_info_tag(buf, sizeof(buf), "listen", p)) {
+            char *endptr = NULL;
+            s->listen = strtol(buf, &endptr, 10);
+            /* assume if no digits were found it is a request to enable it */
+            if (buf == endptr)
+                s->listen = 1;
+        }
+        if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) {
+            s->rw_timeout = strtol(buf, NULL, 10);
+        }
+        if (av_find_info_tag(buf, sizeof(buf), "listen_timeout", p)) {
+            s->listen_timeout = strtol(buf, NULL, 10);
+        }
+    }
+    if (s->rw_timeout >= 0) {
+        s->open_timeout =
+        h->rw_timeout   = s->rw_timeout;
+    }
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = SOCK_STREAM;
+    snprintf(portstr, sizeof(portstr), "%d", port);
+    if (s->listen)
+        hints.ai_flags |= AI_PASSIVE;
+    if (!hostname[0])
+        ret = getaddrinfo(NULL, portstr, &hints, &ai);
+    else if (use_proxy)
+        ret = getaddrinfo(hostname_proxy, portstr_proxy, &hints, &ai); 
+    else
+        ret = getaddrinfo(hostname, portstr, &hints, &ai);
+    if (ret) {
+        av_log(h, AV_LOG_ERROR,
+               "Failed to resolve hostname %s: %s\n",
+               hostname, gai_strerror(ret));
+        return AVERROR(EIO);
+    }
+
+    cur_ai = ai;
+
+#if HAVE_STRUCT_SOCKADDR_IN6
+    // workaround for IOS9 getaddrinfo in IPv6 only network use hardcode IPv4 address can not resolve port number.
+    if (cur_ai->ai_family == AF_INET6){
+        struct sockaddr_in6 * sockaddr_v6 = (struct sockaddr_in6 *)cur_ai->ai_addr;
+        if (!sockaddr_v6->sin6_port){
+            sockaddr_v6->sin6_port = htons(port);
+        }
+    }
+#endif
+
+    if (s->listen > 0) {
+        while (cur_ai && fd < 0) {
+            fd = ff_socket(cur_ai->ai_family,
+                           cur_ai->ai_socktype,
+                           cur_ai->ai_protocol);
+            if (fd < 0) {
+                ret = ff_neterrno();
+                cur_ai = cur_ai->ai_next;
+            }
+        }
+        if (fd < 0)
+            goto fail1;
+        customize_fd(s, fd);
+    }
+
+    if (s->listen == 2) {
+        // multi-client
+        if ((ret = ff_listen(fd, cur_ai->ai_addr, cur_ai->ai_addrlen)) < 0)
+            goto fail1;
+    } else if (s->listen == 1) {
+        // single client
+        if ((ret = ff_listen_bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
+                                  s->listen_timeout, h)) < 0)
+            goto fail1;
+        // Socket descriptor already closed here. Safe to overwrite to client one.
+        fd = ret;
+    } else {
+        ret = ff_connect_parallel(ai, s->open_timeout / 1000, 3, h, &fd, customize_fd, s);
+        if (ret < 0)
+            goto fail1;
+    }
+
+    h->is_streamed = 1;
+    s->fd = fd;
+
+
+    if(use_proxy) {
+        // make socks5 proxy request
+        in_addr_t addr;
+        int next_pos;
+        unsigned char req[1024];
+        req[0] = 5;
+        req[1] = 1;
+        req[2] = 0;
+        
+        // send handshake
+        if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
+            ret = ff_network_wait_fd_timeout(s->fd, 1, h->rw_timeout, &h->interrupt_callback);
+            if (ret)
+                goto fail1;
+        }
+        ret = send(s->fd, req, 3, MSG_NOSIGNAL);
+        if (ret != 3)
+            goto fail1;
+
+        // receive handshake response
+        if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
+            ret = ff_network_wait_fd_timeout(s->fd, 0, h->rw_timeout, &h->interrupt_callback);
+            if (ret)
+                goto fail1;
+        }
+        ret = recv(s->fd, req, 2, 0);
+        if (ret != 2) {
+            ret = AVERROR_INVALIDDATA;
+            goto fail1;
+        }
+
+        // send connect request
+        req[0] = 5;
+        req[1] = 1;
+        req[2] = 0;
+        addr = inet_addr(hostname);
+        req[3] = INADDR_NONE == addr ? 3 : 1;
+        next_pos = 0;
+        if(req[3] == 3) {
+            req[4] = strlen(hostname);
+            memcpy(req + 5, hostname, strlen(hostname));
+            next_pos = 5 + strlen(hostname);
+        } else {
+            memcpy(req + 4, (unsigned char*)&addr, sizeof(addr));
+            next_pos = 4 + sizeof(addr);
+        }
+        *(unsigned short *)(req + next_pos) = htons(port);
+        // req[next_pos] = (htons(port) & 0x00FF);
+        // req[next_pos + 1] = (htons(port) >> 8);
+
+        if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
+            ret = ff_network_wait_fd_timeout(s->fd, 1, h->rw_timeout, &h->interrupt_callback);
+            if (ret)
+                goto fail1;
+        }
+        ret = send(s->fd, req, next_pos + 2, MSG_NOSIGNAL);
+        if (ret != next_pos + 2)
+            goto fail1;
+
+        // recv connect response
+        if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
+            ret = ff_network_wait_fd_timeout(s->fd, 0, h->rw_timeout, &h->interrupt_callback);
+            if (ret)
+                goto fail1;
+        }
+        ret = recv(s->fd, req, 10, 0);
+        if (ret != 10) {
+            av_log(s, AV_LOG_ERROR, "socks5 connect failed bytes %d\n", ret);
+            ret = AVERROR_INVALIDDATA;
+            goto fail1;
+        }
+    }
+
+    freeaddrinfo(ai);
+    return 0;
+
+ fail1:
+    if (fd >= 0)
+        closesocket(fd);
+    freeaddrinfo(ai);
+    return ret;
+}
+
 static int tcp_accept(URLContext *s, URLContext **c)
 {
     TCPContext *sc = s->priv_data;
@@ -313,6 +516,7 @@ static int tcp_get_window_size(URLContext *h)
 const URLProtocol ff_tcp_protocol = {
     .name                = "tcp",
     .url_open            = tcp_open,
+    .url_open2            = tcp_open2,
     .url_accept          = tcp_accept,
     .url_read            = tcp_read,
     .url_write           = tcp_write,
-- 
2.27.0.windows.1



More information about the ffmpeg-devel mailing list