From: zhaoyi <leviz...@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 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".