[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