From patchwork Sat Jun 13 15:17:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: ZHAOYI YI X-Patchwork-Id: 20335 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 56EE544A44C for ; Sat, 13 Jun 2020 18:18:36 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 3344F68B5D6; Sat, 13 Jun 2020 18:18:36 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from APC01-PU1-obe.outbound.protection.outlook.com (mail-oln040092254041.outbound.protection.outlook.com [40.92.254.41]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id E0F9268B566 for ; Sat, 13 Jun 2020 18:18:29 +0300 (EEST) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=oPocNCOXVlKFvZA7zo5KRzGSDNcbQu86WWGfjBEwmE4Cy2xKlMCeWTQi+UBSAkHHb3R1sXEwnOGf4BOGuhsRXYBASLJhUZUskgqGgpjHMn2oEHnLayHApsFKNM1FkUGtkPSfQnCne4aHOayKBLdU2fOmeUEwX/Q3B0XA/xZaOcFq5g8WIr2fAcplbA3idpzYoU7Xk5ijQpu7jo7lic6LMaOzPpp6DwfDzFT5bJQWuXmg4W9CYyH8jQTZvdbchUQnThsOFtkH+5Z9wWxLZx5j8HqAO0Z8Md6NJ8sOPMeuflarJW6tCJZI3L+/E3c15Wx8KXOwQhmmyjcpraq4JzBLfA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=D48OKroNa5uWahwHXYtwvmj+Iu2qd0cugeqUFOutQL4=; b=aNG4XXLCe/blAwROkFey2bM7vtB2ZmaUHJe38WpFEqXSSUAdzECSO3VUHYgZk0Cq7UToq91cxC118g3UH2tLllTlB6VVmW04A0AaBXcrE1z+wG5qciPRKLOojPjn8zoME1pYjiEAstabI+bEHMSszJq0KIcZZjKGjmyVqlZgUddP3hyZwqMx8KFf7CEarzwcqztHf04wcKozIqcDAV88ofuW+ewP4Jbg3dphGEnVG5Ea0XgAU1t1Cp3xDPyQN7zrB/yC15CNpJEEjrV1BMMqpDlYx+T4gpIdf3ra1gltMDoTGg1yo3CNvvSjXtcgdnCxxKvQ+YTDXAejRmCE+B3Stw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none Received: from PU1APC01FT022.eop-APC01.prod.protection.outlook.com (2a01:111:e400:7ebe::50) by PU1APC01HT013.eop-APC01.prod.protection.outlook.com (2a01:111:e400:7ebe::65) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3088.18; Sat, 13 Jun 2020 15:18:26 +0000 Received: from HK0PR02MB3601.apcprd02.prod.outlook.com (2a01:111:e400:7ebe::51) by PU1APC01FT022.mail.protection.outlook.com (2a01:111:e400:7ebe::339) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3088.18 via Frontend Transport; Sat, 13 Jun 2020 15:18:25 +0000 X-IncomingTopHeaderMarker: OriginalChecksum:D8087D0CC869FC0E2B963AF0AFF5D4499596FE7C96B589A5B16B898DF9D7E9D6; UpperCasedChecksum:3080B625A3C25DCD63BD4C930FD38834DADD990D8FB1ABC924DE07098F06E723; SizeAsReceived:7335; Count:47 Received: from HK0PR02MB3601.apcprd02.prod.outlook.com ([fe80::29ca:ba4a:c398:d5d7]) by HK0PR02MB3601.apcprd02.prod.outlook.com ([fe80::29ca:ba4a:c398:d5d7%3]) with mapi id 15.20.3088.026; Sat, 13 Jun 2020 15:18:25 +0000 From: levizhao@live.cn To: ffmpeg-devel@ffmpeg.org Date: Sat, 13 Jun 2020 23:17:41 +0800 Message-ID: X-Mailer: git-send-email 2.27.0.windows.1 X-ClientProxiedBy: HK2PR02CA0128.apcprd02.prod.outlook.com (2603:1096:202:16::12) To HK0PR02MB3601.apcprd02.prod.outlook.com (2603:1096:203:95::18) X-Microsoft-Original-Message-ID: <20200613151741.609-1-levizhao@live.cn> MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from localhost.localdomain (61.157.144.181) by HK2PR02CA0128.apcprd02.prod.outlook.com (2603:1096:202:16::12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3088.18 via Frontend Transport; Sat, 13 Jun 2020 15:18:22 +0000 X-Mailer: git-send-email 2.27.0.windows.1 X-Microsoft-Original-Message-ID: <20200613151741.609-1-levizhao@live.cn> X-TMN: [I1ZSeBN9KU1FPyTdkr34p2X63230nugq] X-MS-PublicTrafficType: Email X-IncomingHeaderCount: 47 X-EOPAttributedMessage: 0 X-MS-Office365-Filtering-Correlation-Id: ba4c7b58-2fe8-4691-5b6e-08d80fad0300 X-MS-TrafficTypeDiagnostic: PU1APC01HT013: X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: EgPHH10aeLwD+xu5FV/VmgIymHRjYn0UwhAEAE8vR56iGnLYVHMERFTBs2NUkPbBMvFVmINc6D61pHaqZ5fBoUK13eBRAFt73D4hr9FPUTdiHjDCWrSqc862O179MHjEbr/7qAmmuairMd4A8fwTs3hR+9s5I14u+Gc8KAsYJGV7nVzkmyZMoYpBke/Ms4G19+q+HG0N3KbW04FijKel/Q== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:0; SRV:; IPV:NLI; SFV:NSPM; H:HK0PR02MB3601.apcprd02.prod.outlook.com; PTR:; CAT:NONE; SFTY:; SFS:; DIR:OUT; SFP:1901; X-MS-Exchange-AntiSpam-MessageData: MLkee5IRCk9yP1OZ96NJYB3vXR0J8NqZm3//A+FRgK0c70mzcHJ0OunR/s3rySprXAxE8hssk2wRrfP4zh9Uq8rIB18pMJfzyVpIJ8m4ftm7o/Sip4wJ6svhSgyQkTghBBnbSRJCnD/eyGzvJCykUw== X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: ba4c7b58-2fe8-4691-5b6e-08d80fad0300 X-MS-Exchange-CrossTenant-OriginalArrivalTime: 13 Jun 2020 15:18:25.3563 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-FromEntityHeader: Internet X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: PU1APC01HT013 Subject: [FFmpeg-devel] [PATCH 2/2] add socks5 support for tcp clients X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: zhaoyi Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" From: zhaoyi --- 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,