From 93fb9118d7e9e91ab5dd9e26af0a8775534b1051 Mon Sep 17 00:00:00 2001 From: Jelle Vergeer Date: Sat, 15 Aug 2020 12:42:34 +0200 Subject: [PATCH 1/2] Completed proxy support --- StreamDivert/InboundDivertProxy.h | 9 - StreamDivert/InboundTCPDivertProxy.cpp | 51 +- StreamDivert/InboundTCPDivertProxy.h | 1 - StreamDivert/SocksProxyServer.cpp | 662 ++++++++++++++++++++++ StreamDivert/SocksProxyServer.h | 108 ++++ StreamDivert/StreamDivert.cpp | 14 +- StreamDivert/StreamDivert.vcxproj | 4 + StreamDivert/StreamDivert.vcxproj.filters | 12 + StreamDivert/cfg.txt | 2 + StreamDivert/config.cpp | 10 + StreamDivert/config.h | 7 + StreamDivert/socks.cpp | 2 + StreamDivert/socks.h | 0 StreamDivert/sockutils.cpp | 116 ++++ StreamDivert/sockutils.h | 24 + 15 files changed, 962 insertions(+), 60 deletions(-) create mode 100644 StreamDivert/SocksProxyServer.cpp create mode 100644 StreamDivert/SocksProxyServer.h create mode 100644 StreamDivert/socks.cpp create mode 100644 StreamDivert/socks.h create mode 100644 StreamDivert/sockutils.cpp create mode 100644 StreamDivert/sockutils.h diff --git a/StreamDivert/InboundDivertProxy.h b/StreamDivert/InboundDivertProxy.h index 8cc9140..7241e24 100644 --- a/StreamDivert/InboundDivertProxy.h +++ b/StreamDivert/InboundDivertProxy.h @@ -10,12 +10,3 @@ struct ProxyConnectionWorkerData sockaddr_in6 clientAddr; }; -struct ProxyTunnelWorkerData -{ - SOCKET sockA; - IpAddr sockAAddr; - UINT16 sockAPort; - SOCKET sockB; - IpAddr sockBAddr; - UINT16 sockBPort; -}; diff --git a/StreamDivert/InboundTCPDivertProxy.cpp b/StreamDivert/InboundTCPDivertProxy.cpp index d9f20af..3d80a75 100644 --- a/StreamDivert/InboundTCPDivertProxy.cpp +++ b/StreamDivert/InboundTCPDivertProxy.cpp @@ -3,6 +3,7 @@ #include "utils.h" #include "windivert.h" #include +#include "sockutils.h" InboundTCPDivertProxy::InboundTCPDivertProxy(const UINT16 localPort, const std::vector& proxyRecords) @@ -229,8 +230,8 @@ void InboundTCPDivertProxy::ProxyConnectionWorker(ProxyConnectionWorkerData* pro tunnelDataB->sockB = clientSock; tunnelDataB->sockBAddr = clientSockIp; tunnelDataB->sockBPort = clientSrcPort; - std::thread tunnelThread(&InboundTCPDivertProxy::ProxyTunnelWorker, this, tunnelDataA); - this->ProxyTunnelWorker(tunnelDataB); + std::thread tunnelThread(&ProxyTunnelWorker, tunnelDataA, this->selfDescStr); + ProxyTunnelWorker(tunnelDataB, this->selfDescStr); tunnelThread.join(); } @@ -244,52 +245,6 @@ void InboundTCPDivertProxy::ProxyConnectionWorker(ProxyConnectionWorkerData* pro return; } -void InboundTCPDivertProxy::ProxyTunnelWorker(ProxyTunnelWorkerData* proxyTunnelWorkerData) -{ - SOCKET sockA = proxyTunnelWorkerData->sockA; - std::string sockAAddrStr = proxyTunnelWorkerData->sockAAddr.to_string(); - UINT16 sockAPort = proxyTunnelWorkerData->sockAPort; - SOCKET sockB = proxyTunnelWorkerData->sockB; - std::string sockBAddrStr = proxyTunnelWorkerData->sockBAddr.to_string(); - UINT16 sockBPort = proxyTunnelWorkerData->sockBPort; - delete proxyTunnelWorkerData; - char buf[8192]; - int recvLen; - std::string selfDesc = this->getStringDesc(); - while (true) - { - recvLen = recv(sockA, buf, sizeof(buf), 0); - if (recvLen == SOCKET_ERROR) - { - warning("%s: failed to recv from socket A(%s:%hu): %d", selfDesc.c_str(), sockAAddrStr.c_str(), sockAPort, WSAGetLastError()); - goto failure; - } - if (recvLen == 0) - { - shutdown(sockA, SD_RECEIVE); - shutdown(sockB, SD_SEND); - goto end; //return - } - - for (int i = 0; i < recvLen; ) - { - int sendLen = send(sockB, buf + i, recvLen - i, 0); - if (sendLen == SOCKET_ERROR) - { - warning("%s: failed to send to socket B(%s:%hu): %d", selfDesc.c_str(), sockBAddrStr.c_str(), sockBPort, WSAGetLastError()); - goto failure; //return - } - i += sendLen; - } - } - -failure: - shutdown(sockA, SD_BOTH); - shutdown(sockB, SD_BOTH); -end: - info("%s: ProxyTunnelWorker(%s:%hu -> %s:%hu) exiting", selfDesc.c_str(), sockAAddrStr.c_str(), sockAPort, sockBAddrStr.c_str(), sockBPort); -} - std::string InboundTCPDivertProxy::generateDivertFilterString() { std::string result = "tcp"; diff --git a/StreamDivert/InboundTCPDivertProxy.h b/StreamDivert/InboundTCPDivertProxy.h index 6958a9f..0fd35c4 100644 --- a/StreamDivert/InboundTCPDivertProxy.h +++ b/StreamDivert/InboundTCPDivertProxy.h @@ -27,7 +27,6 @@ class InboundTCPDivertProxy : public BaseProxy void ProcessUDPPacket(unsigned char * packet, UINT & packet_len, PWINDIVERT_ADDRESS addr, PWINDIVERT_IPHDR ip_hdr, PWINDIVERT_IPV6HDR ip6_hdr, PWINDIVERT_UDPHDR udp_header, IpAddr & srcAddr, IpAddr & dstAddr); void ProxyWorker(); void ProxyConnectionWorker(ProxyConnectionWorkerData* proxyConnectionWorkerData); - void ProxyTunnelWorker(ProxyTunnelWorkerData* proxyTunnelWorkerData); std::string generateDivertFilterString(); bool findProxyRecordBySrcAddr(IpAddr& srcIp, InboundRelayEntry& proxyRecord); public: diff --git a/StreamDivert/SocksProxyServer.cpp b/StreamDivert/SocksProxyServer.cpp new file mode 100644 index 0000000..fc9d918 --- /dev/null +++ b/StreamDivert/SocksProxyServer.cpp @@ -0,0 +1,662 @@ +#include "stdafx.h" +#include "SocksProxyServer.h" +#include "utils.h" +#include +#include +#include "ipaddr.h" +#include "sockutils.h" + + +SocksProxyServer::SocksProxyServer(int port, bool enableSocks4, bool enableSocks5) +{ + this->port = port; + this->serverSock = INVALID_SOCKET; + this->selfDescStr = this->getSelfDescription(); + this->running = false; + this->socks5AuthType = Socks5AuthMethods::NOAUTH; + this->username = ""; + this->password = ""; + this->enableSocks4 = enableSocks4; + this->enableSocks5 = enableSocks5; +} + +SocksProxyServer::SocksProxyServer(int port) + : SocksProxyServer(port, true, true) +{ +} + +SocksProxyServer::~SocksProxyServer() +{ +} + +void SocksProxyServer::SetAuthType(Socks5AuthMethods method) +{ + this->socks5AuthType = method; +} + +void SocksProxyServer::SetAuthUsername(std::string& username) +{ + this->username = username; +} + +void SocksProxyServer::SetAuthPassword(std::string& password) +{ + this->password = password; +} + + +std::string SocksProxyServer::getSelfDescription() +{ + return "SocksProxyServer(" + std::to_string(this->port) + ")"; +} + +void SocksProxyServer::ProxyServerWorker() +{ + while (true) + { + struct sockaddr_in6 clientSockAddr; + int size = sizeof(clientSockAddr); + SOCKET incommingSock = accept(this->serverSock, (SOCKADDR*)&clientSockAddr, &size); + if (incommingSock == INVALID_SOCKET) + { + std::lock_guard lock(this->resourceLock); + if (this->running == false) + { + goto cleanup; + } + warning("%s: failed to accept socket (%d)", this->selfDescStr.c_str(), WSAGetLastError()); + continue; + } + IpAddr clientSockIp = IpAddr(clientSockAddr.sin6_addr); + std::string srcAddr = clientSockIp.to_string(); + info("%s: Incoming connection from %s:%hu", this->selfDescStr.c_str(), srcAddr.c_str(), ntohs(clientSockAddr.sin6_port)); + + int one = 1; + setsockopt(incommingSock, IPPROTO_TCP, TCP_NODELAY, (const char*)&one, sizeof(one)); + + SocksServerConnectionData* proxyConnectionWorkerData = new SocksServerConnectionData(); + proxyConnectionWorkerData->clientSocket = incommingSock; + proxyConnectionWorkerData->clientAddr = clientSockAddr; + std::thread proxyConnectionThread(&SocksProxyServer::ProxyConnectionWorker, this, proxyConnectionWorkerData); + proxyConnectionThread.detach(); + } +cleanup: + if (this->serverSock != NULL) + { + closesocket(this->serverSock); + this->serverSock = NULL; + } + info("%s: ProxyServerWorker exiting", this->selfDescStr.c_str()); +} + +SOCKET SocksProxyServer::ProcessSocks4Connection(SOCKET sock) +{ + SOCKET proxySock = INVALID_SOCKET; + char cmd; + int received = 0; + received = recvall(sock, &cmd, 1); + if (cmd == CMD_CONNECT) + { + unsigned short int port; + in_addr ipv4AddrStore; + IpAddr ipv4Addr; + char userid[1024]; + char domain[1024]; + int useridLen, domainLen; + useridLen = sizeof(userid); + domainLen = sizeof(domain); + if (!recvallb(sock, (char*)&port, sizeof(port))) + { + return false; + } + port = ntohs(port); + if (!recvallb(sock, (char*)&ipv4AddrStore.S_un.S_addr, sizeof(ipv4AddrStore.S_un.S_addr))) + { + return false; + } + if (!recvstr(sock, &userid[0], &useridLen)) + { + return false; + } + + if (this->socks4aIsInvalidIpv4(ipv4AddrStore.S_un.S_addr)) + { + if (!recvstr(sock, &domain[0], &domainLen)) + { + return false; + } + proxySock = SocksProxyServer::socksConnect(std::string(&domain[0]), port); + } + else + { + ipv4Addr = IpAddr(ipv4AddrStore); + proxySock = SocksProxyServer::socksConnect(ipv4Addr, port); + } + if (proxySock != INVALID_SOCKET) + { + this->socks4aSendClientResponse(sock, Socks4aClientResponse::RequestGranted); + } + else + { + this->socks4aSendClientResponse(sock, Socks4aClientResponse::RequestRejectedOrFailed); + } + } + else + { + error("Unsupported socks4 cmd: %hhi", cmd); + } + return proxySock; +} + +SOCKET SocksProxyServer::ProcessSocks5Connection(SOCKET sock) +{ + SOCKET proxySock = INVALID_SOCKET; + char methods; + char buffer[4]; + IpAddr ipAddr; + std::string domain; + unsigned short int port; + Socks5AddressType addrType; + + if (!recvallb(sock, &methods, 1)) + { + goto failure; + } + if (!this->socks5Auth(sock, methods)) + { + goto failure; + } + + /* + +----+-----+-------+------+----------+----------+ + |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | + +----+-----+-------+------+----------+----------+ + | 1 | 1 | X'00' | 1 | Variable | 2 | + +----+-----+-------+------+----------+----------+ + */ + + if (!recvallb(sock, &buffer[0], sizeof(buffer))) + { + goto failure; + } + if (buffer[0] != SocksVersion::Socks5) + { + goto failure; + } + if (buffer[1] != CMD_CONNECT) //Others not supported + { + goto failure; + } + + addrType = (Socks5AddressType)buffer[3]; + if (addrType == Socks5AddressType::AddrTypeIPv4) + { + in_addr addr; + if (!recvallb(sock, (char*)&addr.S_un.S_addr, sizeof(addr))) + { + goto failure; + } + if (!recvallb(sock, (char*)&port, sizeof(port))) + { + goto failure; + } + port = ntohs(port); + ipAddr = IpAddr(addr); + proxySock = this->socksConnect(ipAddr, port); + } + else if (addrType == Socks5AddressType::AddrTypeIPv6) + { + in6_addr addr; + if (!recvallb(sock, (char*)&addr.u.Byte, sizeof(addr))) + { + goto failure; + } + if (!recvallb(sock, (char*)&port, sizeof(port))) + { + goto failure; + } + port = ntohs(port); + ipAddr = IpAddr(addr); + proxySock = this->socksConnect(ipAddr, port); + } + else if (addrType == Socks5AddressType::AddrTypeDomainName) + { + unsigned char domainLen; + char domainBuf[1024]; + if (!recvallb(sock, (char*)&domainLen, sizeof(domainLen))) + { + goto failure; + } + if (!recvallb(sock, (char*)&domainBuf[0], domainLen)) + { + goto failure; + } + if (!recvallb(sock, (char*)&port, sizeof(port))) + { + goto failure; + } + port = ntohs(port); + domainBuf[domainLen] = 0; + domain = std::string(domainBuf); + proxySock = this->socksConnect(domain, port); + } + + if (proxySock != INVALID_SOCKET) + { + if (this->socks5SendClientResponse(sock, Socks5ClientResponse::succeeded, addrType, &ipAddr, &domain, port)) + { + return proxySock; + } + } + +failure: + closesocket(proxySock); + return INVALID_SOCKET; +} + +bool SocksProxyServer::socks5Auth(SOCKET sock, int methods) +{ + bool supported = false; + for (int i = 0; i < methods; i++) { + char type; + recvallb(sock, (char*)&type, 1); + if (type == this->socks5AuthType) { + supported = true; + } + } + if (!supported) { + this->socks5SendAuthNotSupported(sock); + return false; + } + switch (this->socks5AuthType) { + case Socks5AuthMethods::NOAUTH: + this->socks5SendNoAth(sock); + return true; + break; + case Socks5AuthMethods::USERPASS: + return this->socks5UserPassAuthentication(sock); + break; + } + return false; +} + +void SocksProxyServer::socks5SendAuthNotSupported(SOCKET sock) +{ + char answer[2] = { (char)SocksVersion::Socks5 , Socks5AuthMethods::NOMETHOD }; + sendallb(sock, answer, sizeof(answer)); +} + +void SocksProxyServer::socks5SendNoAth(SOCKET sock) +{ + char answer[2] = { (char)SocksVersion::Socks5, Socks5AuthMethods::NOAUTH }; + sendallb(sock, answer, sizeof(answer)); +} + +bool SocksProxyServer::socks5UserPassAuthentication(SOCKET sock) +{ + char answer[2] = { (char)SocksVersion::Socks5, Socks5AuthMethods::USERPASS }; + if (!sendallb(sock, answer, sizeof(answer))) { + return false; + } + + /* + +----+------+----------+------+----------+ + |VER | ULEN | UNAME | PLEN | PASSWD | + +----+------+----------+------+----------+ + | 1 | 1 | 1 to 255 | 1 | 1 to 255 | + +----+------+----------+------+----------+ + */ + char ver; + if (!recvallb(sock, &ver, sizeof(ver))) + { + return false; + } + if (ver != AUTH_VERSION) + { + return false; + } + std::string username; + std::string password; + if (!this->socks5GetUserPassStr(sock, username)) + { + return false; + } + if (!this->socks5GetUserPassStr(sock, password)) + { + return false; + } + if (username == this->username && password == this->password) + { + char authokResp[2] = { AUTH_VERSION, Socks5UserPassAuth::AuthOk }; + return sendallb(sock, authokResp, sizeof(authokResp)); + } + char authFailResp[2] = { AUTH_VERSION, Socks5UserPassAuth::AuthFail }; + sendallb(sock, authFailResp, sizeof(authFailResp)); + return false; +} + +bool SocksProxyServer::socks5GetUserPassStr(SOCKET sock, std::string& value) +{ + unsigned char size; + char buf[256] = { 0 }; + if (!recvallb(sock, (char*)&size, sizeof(size))) + { + return false; + } + if (!recvallb(sock, &buf[0], size)) + { + return false; + } + value = std::string(buf); + return true; +} + +bool SocksProxyServer::socks5SendClientResponse(SOCKET sock, Socks5ClientResponse reply, Socks5AddressType addrType, IpAddr* ipAddr, std::string* domain, unsigned short int port) +{ + char response[4] = { (char)SocksVersion::Socks5, reply, 0, (char)addrType }; + if (!sendallb(sock, &response[0], sizeof(response))) + { + return false; + } + if (addrType == Socks5AddressType::AddrTypeIPv4 ) + { + in_addr addr = ipAddr->get_ipv4_addr(); + if (!sendallb(sock, (char*)&addr, sizeof(addr))) + { + return false; + } + } + else if (addrType == Socks5AddressType::AddrTypeIPv6) + { + in6_addr addr = ipAddr->get_addr(); + if (!sendallb(sock, (char*)&addr, sizeof(addr))) + { + return false; + } + } + else if (addrType == Socks5AddressType::AddrTypeDomainName) + { + unsigned char len = domain->length(); + if (!sendallb(sock, (char*)&len, sizeof(len))) + { + return false; + } + if (!sendallb(sock, domain->c_str(), len)) + { + return false; + } + } + + if (!sendallb(sock, (char*)&port, sizeof(port))) + { + return false; + } + return true; +} + +bool SocksProxyServer::socks4aIsInvalidIpv4(int ip) +{ + char* rawIp = (char*)&ip; + return (rawIp[0] == 0 && rawIp[1] == 0 && rawIp[2] == 0 && rawIp[3] != 0); +} + +bool SocksProxyServer::socks4aSendClientResponse(SOCKET sock, Socks4aClientResponse status) +{ + /* + +----+----+----+----+----+----+----+----+ + | VN | CD | DSTPORT | DSTIP | + +----+----+----+----+----+----+----+----+ + # of bytes: 1 1 2 4 + + VN is the version of the reply code and should be 0. CD is the result + code with one of the following values: + + 90: request granted + 91: request rejected or failed + 92: request rejected becasue SOCKS server cannot connect to + identd on the client + 93: request rejected because the client program and identd + report different user-ids + */ + char resp[8] = { 0x00, (char)status, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + return sendallb(sock, resp, sizeof(resp)); +} + +SOCKET SocksProxyServer::socksConnect(IpAddr& ip, int port) +{ + int off = 0; + SOCKET sock; + struct sockaddr_in6 destAddr; + ZeroMemory(&destAddr, sizeof(destAddr)); + destAddr.sin6_family = AF_INET6; + destAddr.sin6_addr = ip.get_addr(); + destAddr.sin6_port = htons(port); + + info("%s: Setting up client connection to %s:%d", selfDescStr.c_str(), ip.to_string().c_str(), port); + sock = socket(AF_INET6, SOCK_STREAM, 0); + if (sock == INVALID_SOCKET) + { + error("%s: failed to create socket (%d)", selfDescStr.c_str(), WSAGetLastError()); + goto failure; + } + if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&off, sizeof(int)) == SOCKET_ERROR) + { + error("%s: failed to set connect socket dual-stack (%d)", selfDescStr.c_str(), GetLastError()); + goto failure; + } + if (connect(sock, (struct sockaddr*)&destAddr, sizeof(destAddr)) == SOCKET_ERROR) + { + error("%s: failed to connect socket (%d)", selfDescStr.c_str(), WSAGetLastError()); + } + return sock; + +failure: + closesocket(sock); + return INVALID_SOCKET; +} + +SOCKET SocksProxyServer::socksConnect(std::string domain, int port) +{ + SOCKET sock; + char portStr[6]; + struct addrinfo* res = NULL; + IpAddr ipAddr; + + info("%s: Setting up client connection to %s:%d", selfDescStr.c_str(), domain.c_str(), port); + snprintf(portStr, sizeof(portStr), "%d", port); + int ret = getaddrinfo((char*)domain.c_str(), portStr, NULL, &res); + if (ret == EAI_NODATA) + { + return INVALID_SOCKET; + } + else if (ret == 0) + { + struct addrinfo* r; + for (r = res; r != NULL; r = r->ai_next) + { + if (r->ai_family == AF_INET) + { + ipAddr = IpAddr(((sockaddr_in*)r->ai_addr)->sin_addr); + sock = this->socksConnect(ipAddr, port); + } + else if (r->ai_family == AF_INET6) + { + ipAddr = IpAddr(((sockaddr_in6*)r->ai_addr)->sin6_addr); + sock = this->socksConnect(ipAddr, port); + } + + if (sock != INVALID_SOCKET) + { + break; + } + } + } + + if (res != NULL) + { + freeaddrinfo(res); + res = NULL; + } + return sock; +} + +void SocksProxyServer::ProxyConnectionWorker(SocksServerConnectionData* data) +{ + SOCKET sock = data->clientSocket; + sockaddr_in6 clientAddr = data->clientAddr; + delete data; + data = NULL; + SOCKET proxySock = INVALID_SOCKET; + + SocksVersion version = this->recvSocksVersion(sock); + info("SOCKS version: %d", version); + switch (version) + { + case SocksVersion::Socks4: + { + if (this->enableSocks4) + { + proxySock = this->ProcessSocks4Connection(sock); + } + else + { + warning("Received unsupported SOCKS connection"); + } + } + break; + case SocksVersion::Socks5: + { + if (this->enableSocks5) + { + proxySock = this->ProcessSocks5Connection(sock); + } + else + { + warning("Received unsupported SOCKS connection"); + } + } + break; + } + if (proxySock != INVALID_SOCKET) + { + sockaddr_in6 proxySockAddr; + int proxySockAddrLen = sizeof(proxySockAddr); + if (getsockname(proxySock, (struct sockaddr*)&proxySockAddr, &proxySockAddrLen) == -1) + { + error("%s: failed to get bind socket port (%d)", this->selfDescStr.c_str(), WSAGetLastError()); + goto failure; + } + + + ProxyTunnelWorkerData* tunnelDataA = new ProxyTunnelWorkerData(); + ProxyTunnelWorkerData* tunnelDataB = new ProxyTunnelWorkerData(); + tunnelDataA->sockA = sock; + tunnelDataA->sockAAddr = IpAddr(clientAddr.sin6_addr); + tunnelDataA->sockAPort = ntohs(clientAddr.sin6_port); + tunnelDataA->sockB = proxySock; + tunnelDataA->sockBAddr = IpAddr(proxySockAddr.sin6_addr); + tunnelDataA->sockBPort = ntohs(proxySockAddr.sin6_port); + + tunnelDataB->sockA = proxySock; + tunnelDataB->sockAAddr = tunnelDataA->sockBAddr; + tunnelDataB->sockAPort = tunnelDataA->sockBPort; + tunnelDataB->sockB = sock; + tunnelDataB->sockBAddr = tunnelDataA->sockAAddr; + tunnelDataB->sockBPort = tunnelDataA->sockAPort; + std::thread tunnelThread(&ProxyTunnelWorker, tunnelDataA, this->selfDescStr); + ProxyTunnelWorker(tunnelDataB, this->selfDescStr); + tunnelThread.join(); + } + +failure: + closesocket(sock); + closesocket(proxySock); +} + +SocksVersion SocksProxyServer::recvSocksVersion(SOCKET sock) +{ + char buf; + int bufLen = sizeof(buf); + recvall(sock, (char*)&buf, bufLen); + return (SocksVersion)buf; +} + +bool SocksProxyServer::Start() +{ + int on = 1; + int off = 0; + WSADATA wsa_data; + WORD wsa_version = MAKEWORD(2, 2); + struct sockaddr_in6 addr; + info("%s: Start", this->selfDescStr.c_str()); + + if (WSAStartup(wsa_version, &wsa_data) != 0) + { + error("%s: failed to start WSA (%d)", this->selfDescStr.c_str(), GetLastError()); + goto failure; + } + this->serverSock = socket(AF_INET6, SOCK_STREAM, 0); + if (this->serverSock == INVALID_SOCKET) + { + error("%s: failed to create socket (%d)", this->selfDescStr.c_str(), WSAGetLastError()); + goto failure; + } + if (WSAStartup(wsa_version, &wsa_data) != 0) + { + error("%s: failed to start WSA (%d)", this->selfDescStr.c_str(), GetLastError()); + goto failure; + } + if (setsockopt(this->serverSock, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(int)) == SOCKET_ERROR) + { + error("%s: failed to re-use address (%d)", this->selfDescStr.c_str(), GetLastError()); + goto failure; + } + if (setsockopt(this->serverSock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&off, sizeof(int)) == SOCKET_ERROR) + { + error("%s: failed to set socket dual-stack (%d)", this->selfDescStr.c_str(), GetLastError()); + goto failure; + } + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_port = htons(this->port); + addr.sin6_addr = in6addr_any; + + if (::bind(this->serverSock, (SOCKADDR*)&addr, sizeof(addr)) == SOCKET_ERROR) + { + error("%s: failed to bind socket (%d)", this->selfDescStr.c_str(), WSAGetLastError()); + goto failure; + } + + if (this->port == 0) + { + struct sockaddr_in6 bind_addr; + int bind_addr_len = sizeof(bind_addr); + if (getsockname(this->serverSock, (struct sockaddr*)&bind_addr, &bind_addr_len) == -1) + { + error("%s: failed to get bind socket port (%d)", this->selfDescStr.c_str(), WSAGetLastError()); + } + this->port = ntohs(bind_addr.sin6_port); + } + + if (listen(this->serverSock, 25) == SOCKET_ERROR) + { + error("%s: failed to listen socket (%d)", this->selfDescStr.c_str(), WSAGetLastError()); + goto failure; + } + + this->selfDescStr = this->getSelfDescription(); + this->serverThread = std::thread(&SocksProxyServer::ProxyServerWorker, this); + this->running = true; + info("%s: Start completed", this->selfDescStr.c_str()); + return true; + +failure: + error("%s: Start failed", this->selfDescStr.c_str()); + this->Stop(); + return false; +} + +bool SocksProxyServer::Stop() +{ + return false; +} diff --git a/StreamDivert/SocksProxyServer.h b/StreamDivert/SocksProxyServer.h new file mode 100644 index 0000000..a12377a --- /dev/null +++ b/StreamDivert/SocksProxyServer.h @@ -0,0 +1,108 @@ +#pragma once +#include +#include +#include +#include "ipaddr.h" + +struct SocksServerConnectionData +{ + SOCKET clientSocket; + sockaddr_in6 clientAddr; +}; + +enum SocksVersion +{ + SocksVerNone = 0x0, + Socks4 = 0x04, + Socks5 = 0x05, +}; + + + +enum Socks4aClientResponse : char +{ + RequestGranted = 90, + RequestRejectedOrFailed = 91, + RequestRejectedIdentd = 92, + RequestRejectedUserid = 93 +}; + +enum Socks5ClientResponse : char +{ + succeeded = 0, + GeneralServerFailure = 1, + ConnectionNotAllowed = 2, + NetworkUnreachable = 3, + HostUnreachable = 4, + ConnectionRefused = 5, + TTLExpired = 6, + CommandNotSupported = 7, + AddressTypeNotSupported = 8 +}; + +enum Socks5AuthMethods { + NOAUTH = 0x00, + USERPASS = 0x02, + NOMETHOD = 0xff +}; + +enum Socks5UserPassAuth +{ + AuthOk = 0, + AuthFail = 0xff +}; + +enum Socks5AddressType +{ + AddrTypeNone = 0, + AddrTypeIPv4 = 1, + AddrTypeDomainName = 3, + AddrTypeIPv6 = 4 +}; + +#define CMD_CONNECT 0x1 +#define AUTH_VERSION 0x1 + +class SocksProxyServer +{ +protected: + int port; + SOCKET serverSock; + std::thread serverThread; + std::string selfDescStr; + std::recursive_mutex resourceLock; + bool running; + Socks5AuthMethods socks5AuthType; + std::string username; + std::string password; + bool enableSocks4; + bool enableSocks5; + + std::string getSelfDescription(); + void ProxyServerWorker(); + void ProxyConnectionWorker(SocksServerConnectionData* data); + SocksVersion recvSocksVersion(SOCKET sock); + SOCKET ProcessSocks4Connection(SOCKET sock); + bool socks4aIsInvalidIpv4(int ip); + bool socks4aSendClientResponse(SOCKET sock, Socks4aClientResponse response); + SOCKET ProcessSocks5Connection(SOCKET sock); + bool socks5Auth(SOCKET sock, int methods); + void socks5SendAuthNotSupported(SOCKET sock); + void socks5SendNoAth(SOCKET sock); + bool socks5UserPassAuthentication(SOCKET sock); + bool socks5GetUserPassStr(SOCKET sock, std::string& user); + bool socks5SendClientResponse(SOCKET sock, Socks5ClientResponse reply, Socks5AddressType addrType, IpAddr* ipAddr, std::string* domain, unsigned short int port); + SOCKET socksConnect(IpAddr& ip, int port); + SOCKET socksConnect(std::string domain, int port); +public: + SocksProxyServer(int port, bool enableSocks4, bool enableSocks5); + SocksProxyServer(int port); + + ~SocksProxyServer(); + void SetAuthType(Socks5AuthMethods method); + void SetAuthUsername(std::string& username); + void SetAuthPassword(std::string& password); + bool Start(); + bool Stop(); +}; + diff --git a/StreamDivert/StreamDivert.cpp b/StreamDivert/StreamDivert.cpp index b5991dc..f9b3562 100644 --- a/StreamDivert/StreamDivert.cpp +++ b/StreamDivert/StreamDivert.cpp @@ -19,7 +19,7 @@ #include "utils.h" #include "config.h" #include "WindowsFirewall.h" - +#include "SocksProxyServer.h" // Global Variables: HINSTANCE hInst; // current instance @@ -56,6 +56,16 @@ int __cdecl main(int argc, char **argv) error("Failed to initialize FW object"); } } + + SocksProxyServer server(1337, true, true); + server.SetAuthUsername(std::string("jelle")); + server.SetAuthPassword(std::string("test")); + server.SetAuthType(Socks5AuthMethods::USERPASS); + server.Start(); + //Wait indefinitely + std::promise p; + p.get_future().wait(); + info("Parsing config file..."); RelayConfig cfg = LoadConfig(cfgPath); @@ -103,7 +113,7 @@ int __cdecl main(int argc, char **argv) proxies.push_back(outboundProxy); //Wait indefinitely - std::promise p; + //std::promise p; p.get_future().wait(); } diff --git a/StreamDivert/StreamDivert.vcxproj b/StreamDivert/StreamDivert.vcxproj index 0f61221..daa6564 100644 --- a/StreamDivert/StreamDivert.vcxproj +++ b/StreamDivert/StreamDivert.vcxproj @@ -188,6 +188,8 @@ xcopy "$(SolutionDir)\WinDivert\$(PlatformTargetAsMSBuildArchitecture)\WinDivert + + @@ -203,6 +205,8 @@ xcopy "$(SolutionDir)\WinDivert\$(PlatformTargetAsMSBuildArchitecture)\WinDivert + + Create Create diff --git a/StreamDivert/StreamDivert.vcxproj.filters b/StreamDivert/StreamDivert.vcxproj.filters index 7a41680..184ba00 100644 --- a/StreamDivert/StreamDivert.vcxproj.filters +++ b/StreamDivert/StreamDivert.vcxproj.filters @@ -66,6 +66,12 @@ Header Files + + Header Files + + + Header Files + @@ -101,6 +107,12 @@ Source Files + + Source Files + + + Source Files + diff --git a/StreamDivert/cfg.txt b/StreamDivert/cfg.txt index 19fe4b0..c239933 100644 --- a/StreamDivert/cfg.txt +++ b/StreamDivert/cfg.txt @@ -2,6 +2,8 @@ //tcp < 445 0.0.0.0 -> fe80::20c:29ff:fe6f:88ff 445 tcp < 445 fe80::f477:846a:775d:d37 -> fe80::20c:29ff:fe6f:88ff 445 //tcp < 445 fe80::f477:846a:775d:d37 -> 10.0.1.49 445 +tcp < 445 0.0.0.0 -> socks5 + udp < 8080 10.0.1.50 -> 10.0.1.49 8081 udp < 8080 0.0.0.0 -> 10.0.1.49 8080 diff --git a/StreamDivert/config.cpp b/StreamDivert/config.cpp index c49ec99..fb40be1 100644 --- a/StreamDivert/config.cpp +++ b/StreamDivert/config.cpp @@ -24,6 +24,7 @@ RelayConfig LoadConfig(std::string path) if (sscanf_s(line.c_str(), "%[a-z] < %hu %s -> %s %hu", &proto[0], _countof(proto), &localPort, &srcAddr[0], _countof(srcAddr), &forwardAddr[0], _countof(forwardAddr), &forwardPort) == 5) { InboundRelayEntry entry; + entry.type = InboundRelayEntryType::Divert; entry.protocol = std::string(proto); entry.localPort = localPort; entry.srcAddr = IpAddr(srcAddr); @@ -31,6 +32,15 @@ RelayConfig LoadConfig(std::string path) entry.forwardPort = forwardPort; result.inboundRelayEntries.push_back(entry); } + else if (sscanf_s(line.c_str(), "%[a-z] < %hu %s -> socks", &proto[0], _countof(proto), &localPort, &srcAddr[0], _countof(srcAddr)) == 3) + { + InboundRelayEntry entry; + entry.type = InboundRelayEntryType::Socks; + entry.protocol = std::string(proto); + entry.localPort = localPort; + entry.srcAddr = IpAddr(srcAddr); + result.inboundRelayEntries.push_back(entry); + } else if(sscanf_s(line.c_str(), "%[a-z] > %s %hu -> %s %hu", &proto[0], _countof(proto), &dstAddr[0], _countof(dstAddr), &dstPort, &forwardAddr[0], _countof(forwardAddr), &forwardPort) == 5) { OutboundRelayEntry entry; diff --git a/StreamDivert/config.h b/StreamDivert/config.h index 8cc616e..33a05d9 100644 --- a/StreamDivert/config.h +++ b/StreamDivert/config.h @@ -4,9 +4,16 @@ #include #include +enum InboundRelayEntryType +{ + None, + Divert, + Socks +}; struct InboundRelayEntry { + InboundRelayEntryType type; std::string protocol; UINT16 localPort; IpAddr srcAddr; diff --git a/StreamDivert/socks.cpp b/StreamDivert/socks.cpp new file mode 100644 index 0000000..e4629f0 --- /dev/null +++ b/StreamDivert/socks.cpp @@ -0,0 +1,2 @@ +#include "stdafx.h" +#include "socks.h" diff --git a/StreamDivert/socks.h b/StreamDivert/socks.h new file mode 100644 index 0000000..e69de29 diff --git a/StreamDivert/sockutils.cpp b/StreamDivert/sockutils.cpp new file mode 100644 index 0000000..fd2981a --- /dev/null +++ b/StreamDivert/sockutils.cpp @@ -0,0 +1,116 @@ +#include "stdafx.h" +#include "sockutils.h" +#include +#include "utils.h" + + +int recvall(SOCKET sock, char* buffer, int len) +{ + char* dataPtr = buffer; + int totalRead = 0; + while (totalRead < len) + { + int read = recv(sock, dataPtr, len - totalRead, 0); + if(read == 0 || read == SOCKET_ERROR) + { + break; + } + totalRead += read; + dataPtr += read; + } + return totalRead; +} + +bool recvallb(SOCKET sock, char* buffer, int len) +{ + return recvall(sock, buffer, len) == len; +} + +int sendall(SOCKET sock, const char* buffer, int len) +{ + const char* dataPtr = buffer; + int totalSent = 0; + while (totalSent < len) + { + int sent = send(sock, dataPtr, len - totalSent, 0); + if (sent == SOCKET_ERROR) + { + break; + } + totalSent += sent; + dataPtr += sent; + } + return totalSent; +} + +bool sendallb(SOCKET sock, const char* buffer, int len) +{ + return sendall(sock, buffer, len) == len; +} + +bool recvstr(SOCKET sock, char* buf, int* len) +{ + int totalRead = 0; + int read = 0; + while (totalRead < *len) + { + read = recv(sock, buf + totalRead, 1, 0); + if (read == 0 || read == SOCKET_ERROR) + { + return false; + } + if (*(buf + totalRead) == 0) + { + return true; + } + } + return false; +} + +void ProxyTunnelWorker(ProxyTunnelWorkerData* proxyTunnelWorkerData, std::string& logDesc) +{ + SOCKET sockA = proxyTunnelWorkerData->sockA; + std::string sockAAddrStr = proxyTunnelWorkerData->sockAAddr.to_string(); + UINT16 sockAPort = proxyTunnelWorkerData->sockAPort; + SOCKET sockB = proxyTunnelWorkerData->sockB; + std::string sockBAddrStr = proxyTunnelWorkerData->sockBAddr.to_string(); + UINT16 sockBPort = proxyTunnelWorkerData->sockBPort; + delete proxyTunnelWorkerData; + char buf[8192]; + int recvLen; + + info("Tunneling %s:%d -> %s:%d", sockAAddrStr.c_str(), sockAPort, sockBAddrStr.c_str(), sockBPort); + + while (true) + { + recvLen = recv(sockA, buf, sizeof(buf), 0); + if (recvLen == SOCKET_ERROR) + { + warning("%s: failed to recv from socket A(%s:%hu): %d", logDesc.c_str(), sockAAddrStr.c_str(), sockAPort, WSAGetLastError()); + goto failure; + } + if (recvLen == 0) + { + shutdown(sockA, SD_RECEIVE); + shutdown(sockB, SD_SEND); + goto end; //return + } + + for (int i = 0; i < recvLen; ) + { + int sendLen = send(sockB, buf + i, recvLen - i, 0); + if (sendLen == SOCKET_ERROR) + { + warning("%s: failed to send to socket B(%s:%hu): %d", logDesc.c_str(), sockBAddrStr.c_str(), sockBPort, WSAGetLastError()); + goto failure; //return + } + i += sendLen; + } + } + +failure: + shutdown(sockA, SD_BOTH); + shutdown(sockB, SD_BOTH); +end: + info("%s: ProxyTunnelWorker(%s:%hu -> %s:%hu) exiting", logDesc.c_str(), sockAAddrStr.c_str(), sockAPort, sockBAddrStr.c_str(), sockBPort); +} \ No newline at end of file diff --git a/StreamDivert/sockutils.h b/StreamDivert/sockutils.h new file mode 100644 index 0000000..c5658cd --- /dev/null +++ b/StreamDivert/sockutils.h @@ -0,0 +1,24 @@ +#pragma once +#include "ipaddr.h" + + +int recvall(SOCKET sock, char* buffer, int len); +bool recvallb(SOCKET sock, char* buffer, int len); + +int sendall(SOCKET sock, const char* buffer, int len); +bool sendallb(SOCKET sock, const char* buffer, int len); + +bool recvstr(SOCKET sock, char* buf, int* len); + + +struct ProxyTunnelWorkerData +{ + SOCKET sockA; + IpAddr sockAAddr; + UINT16 sockAPort; + SOCKET sockB; + IpAddr sockBAddr; + UINT16 sockBPort; +}; + +void ProxyTunnelWorker(ProxyTunnelWorkerData* proxyTunnelWorkerData, std::string& logDesc); \ No newline at end of file From b38503f4f84b89026b3ced3dc0bc0953ecc37e16 Mon Sep 17 00:00:00 2001 From: Jelle Vergeer Date: Sat, 15 Aug 2020 16:08:01 +0200 Subject: [PATCH 2/2] Completed SOCKS support --- StreamDivert/InboundTCPDivertProxy.cpp | 57 ++++++++++++++++++++------ StreamDivert/InboundTCPDivertProxy.h | 3 ++ StreamDivert/SocksProxyServer.cpp | 5 +++ StreamDivert/SocksProxyServer.h | 1 + StreamDivert/StreamDivert.cpp | 12 +----- StreamDivert/cfg.txt | 2 +- 6 files changed, 56 insertions(+), 24 deletions(-) diff --git a/StreamDivert/InboundTCPDivertProxy.cpp b/StreamDivert/InboundTCPDivertProxy.cpp index 3d80a75..f33b7cb 100644 --- a/StreamDivert/InboundTCPDivertProxy.cpp +++ b/StreamDivert/InboundTCPDivertProxy.cpp @@ -7,12 +7,14 @@ InboundTCPDivertProxy::InboundTCPDivertProxy(const UINT16 localPort, const std::vector& proxyRecords) + : socksServer(0) { this->localPort = localPort; this->localProxyPort = 0; this->proxyRecords = proxyRecords; this->proxySock = NULL; this->selfDescStr = this->getStringDesc(); + this->containsSocksRecords = false; } InboundTCPDivertProxy::~InboundTCPDivertProxy() @@ -77,7 +79,17 @@ bool InboundTCPDivertProxy::Start() { error("%s: failed to listen socket (%d)", this->selfDescStr.c_str(), WSAGetLastError()); goto failure; - } + } + + for each (auto record in this->proxyRecords) + { + if (record.type == InboundRelayEntryType::Socks) + { + this->socksServer.Start(); + containsSocksRecords = true; + break; + } + } BaseProxy::Start(); }//lock scope @@ -116,23 +128,38 @@ void InboundTCPDivertProxy::ProcessTCPPacket(unsigned char* packet, UINT& packet tcp_hdr->DstPort == htons(this->localPort)) { std::string dstAddrStr = dstAddr.to_string(); - info("%s: Modify packet dst -> %s:%hu", this->selfDescStr.c_str(), dstAddrStr.c_str(), this->localProxyPort); - tcp_hdr->DstPort = htons(this->localProxyPort); - break; + if (record->type == InboundRelayEntryType::Divert) + { + info("%s: Modify packet dst -> %s:%hu", this->selfDescStr.c_str(), dstAddrStr.c_str(), this->localProxyPort); + tcp_hdr->DstPort = htons(this->localProxyPort); + break; + } + else if (record->type == InboundRelayEntryType::Socks) + { + int socksPort = this->socksServer.GetPort(); + info("%s: Modify packet dst -> %s:%hu", this->selfDescStr.c_str(), dstAddrStr.c_str(), socksPort); + tcp_hdr->DstPort = htons(socksPort); + break; + } } - } + } } else { for (auto record = this->proxyRecords.begin(); record != this->proxyRecords.end(); ++record) { - if ((dstAddr == record->srcAddr || record->srcAddr == anyIpAddr) && - tcp_hdr->SrcPort == htons(this->localProxyPort)) + if ((dstAddr == record->srcAddr || record->srcAddr == anyIpAddr)) { - std::string srcAddrStr = srcAddr.to_string(); - info("%s: Modify packet src -> %s:%hu", this->selfDescStr.c_str(), srcAddrStr.c_str(), this->localPort); - tcp_hdr->SrcPort = htons(this->localPort); - break; + if ( + (record->type == InboundRelayEntryType::Divert && tcp_hdr->SrcPort == htons(this->localProxyPort) ) || + (record->type == InboundRelayEntryType::Socks && tcp_hdr->SrcPort == htons(this->socksServer.GetPort())) + ) + { + std::string srcAddrStr = srcAddr.to_string(); + info("%s: Modify packet src -> %s:%hu", this->selfDescStr.c_str(), srcAddrStr.c_str(), this->localPort); + tcp_hdr->SrcPort = htons(this->localPort); + break; + } } } } @@ -252,6 +279,12 @@ std::string InboundTCPDivertProxy::generateDivertFilterString() std::string proxyFilterStr = "(tcp.SrcPort == " + std::to_string(this->localProxyPort) + ")"; orExpressions.push_back(proxyFilterStr); + if (this->containsSocksRecords) + { + proxyFilterStr = "(tcp.SrcPort == " + std::to_string(this->socksServer.GetPort()) + ")"; + orExpressions.push_back(proxyFilterStr); + } + //check for wildcard address bool containsWildcard = false; for (auto record = this->proxyRecords.begin(); record != this->proxyRecords.end(); ++record) @@ -262,7 +295,7 @@ std::string InboundTCPDivertProxy::generateDivertFilterString() orExpressions.push_back(recordFilterStr); containsWildcard = true; break; - } + } } if (!containsWildcard) diff --git a/StreamDivert/InboundTCPDivertProxy.h b/StreamDivert/InboundTCPDivertProxy.h index 0fd35c4..6eda28d 100644 --- a/StreamDivert/InboundTCPDivertProxy.h +++ b/StreamDivert/InboundTCPDivertProxy.h @@ -9,6 +9,7 @@ #include "config.h" #include"ipaddr.h" #include "InboundDivertProxy.h" +#include "SocksProxyServer.h" class InboundTCPDivertProxy : public BaseProxy @@ -20,6 +21,8 @@ class InboundTCPDivertProxy : public BaseProxy UINT16 localPort; UINT16 localProxyPort; std::vector proxyRecords; + SocksProxyServer socksServer; + bool containsSocksRecords; std::string getStringDesc(); void ProcessTCPPacket(unsigned char* packet, UINT& packet_len, PWINDIVERT_ADDRESS addr, PWINDIVERT_IPHDR ip_hdr, PWINDIVERT_IPV6HDR ip6_hdr, PWINDIVERT_TCPHDR tcp_hdr, IpAddr& srcAddr, IpAddr& dstAddr); diff --git a/StreamDivert/SocksProxyServer.cpp b/StreamDivert/SocksProxyServer.cpp index fc9d918..9a00117 100644 --- a/StreamDivert/SocksProxyServer.cpp +++ b/StreamDivert/SocksProxyServer.cpp @@ -44,6 +44,11 @@ void SocksProxyServer::SetAuthPassword(std::string& password) this->password = password; } +int SocksProxyServer::GetPort() +{ + return this->port; +} + std::string SocksProxyServer::getSelfDescription() { diff --git a/StreamDivert/SocksProxyServer.h b/StreamDivert/SocksProxyServer.h index a12377a..6771d7e 100644 --- a/StreamDivert/SocksProxyServer.h +++ b/StreamDivert/SocksProxyServer.h @@ -102,6 +102,7 @@ class SocksProxyServer void SetAuthType(Socks5AuthMethods method); void SetAuthUsername(std::string& username); void SetAuthPassword(std::string& password); + int GetPort(); bool Start(); bool Stop(); }; diff --git a/StreamDivert/StreamDivert.cpp b/StreamDivert/StreamDivert.cpp index f9b3562..f102f2a 100644 --- a/StreamDivert/StreamDivert.cpp +++ b/StreamDivert/StreamDivert.cpp @@ -56,16 +56,6 @@ int __cdecl main(int argc, char **argv) error("Failed to initialize FW object"); } } - - SocksProxyServer server(1337, true, true); - server.SetAuthUsername(std::string("jelle")); - server.SetAuthPassword(std::string("test")); - server.SetAuthType(Socks5AuthMethods::USERPASS); - server.Start(); - //Wait indefinitely - std::promise p; - p.get_future().wait(); - info("Parsing config file..."); RelayConfig cfg = LoadConfig(cfgPath); @@ -113,7 +103,7 @@ int __cdecl main(int argc, char **argv) proxies.push_back(outboundProxy); //Wait indefinitely - //std::promise p; + std::promise p; p.get_future().wait(); } diff --git a/StreamDivert/cfg.txt b/StreamDivert/cfg.txt index c239933..6f7523d 100644 --- a/StreamDivert/cfg.txt +++ b/StreamDivert/cfg.txt @@ -2,7 +2,7 @@ //tcp < 445 0.0.0.0 -> fe80::20c:29ff:fe6f:88ff 445 tcp < 445 fe80::f477:846a:775d:d37 -> fe80::20c:29ff:fe6f:88ff 445 //tcp < 445 fe80::f477:846a:775d:d37 -> 10.0.1.49 445 -tcp < 445 0.0.0.0 -> socks5 +tcp < 445 0.0.0.0 -> socks udp < 8080 10.0.1.50 -> 10.0.1.49 8081