diff --git a/src/Cedar/Cedar.h b/src/Cedar/Cedar.h index 408bc44e..b5ad275e 100644 --- a/src/Cedar/Cedar.h +++ b/src/Cedar/Cedar.h @@ -116,8 +116,6 @@ #define MAX_ACCOUNT_NAME_LEN 255 // Maximum account name length #define MAX_USERNAME_LEN 255 // User name maximum length #define MAX_PASSWORD_LEN 255 // Password name maximum length -#define MAX_PROXY_USERNAME_LEN 255 // Proxy user name maximum length -#define MAX_PROXY_PASSWORD_LEN 255 // Proxy Password maximum length #define MAX_SERVER_STR_LEN 255 // Maximum length of server string #define MAX_CLIENT_STR_LEN 255 // Maximum length of client string #define MAX_HUBNAME_LEN 255 // Maximum length of HUB name @@ -190,7 +188,6 @@ #define TIMEOUT_MAX (60 * 1000) // Maximum timeout in seconds #define TIMEOUT_DEFAULT (30 * 1000) // Default number of seconds to timeout #define CONNECTING_TIMEOUT (15 * 1000) // Timeout in seconds of being connected -#define CONNECTING_TIMEOUT_PROXY (4 * 1000) // Timeout in seconds of being connected (Proxy) #define CONNECTING_POOLING_SPAN (3 * 1000) // Polling interval of connected #define MIN_RETRY_INTERVAL (5 * 1000) // Minimum retry interval #define MAX_RETRY_INTERVAL (300 * 1000) // Maximum retry interval diff --git a/src/Cedar/Connection.h b/src/Cedar/Connection.h index de6a50fd..a134b1ed 100644 --- a/src/Cedar/Connection.h +++ b/src/Cedar/Connection.h @@ -64,8 +64,8 @@ struct CLIENT_OPTION UINT ProxyType; // Type of proxy char ProxyName[MAX_HOST_NAME_LEN + 1]; // Proxy server name UINT ProxyPort; // Port number of the proxy server - char ProxyUsername[MAX_PROXY_USERNAME_LEN + 1]; // Maximum user name length - char ProxyPassword[MAX_PROXY_PASSWORD_LEN + 1]; // Maximum password length + char ProxyUsername[PROXY_MAX_USERNAME_LEN + 1]; // Maximum user name length + char ProxyPassword[PROXY_MAX_PASSWORD_LEN + 1]; // Maximum password length char CustomHttpHeader[HTTP_CUSTOM_HEADER_MAX_SIZE + 1]; // Custom HTTP proxy header UINT NumRetry; // Automatic retries UINT RetryInterval; // Retry interval diff --git a/src/Cedar/Protocol.c b/src/Cedar/Protocol.c index 3bdecd91..b1498441 100644 --- a/src/Cedar/Protocol.c +++ b/src/Cedar/Protocol.c @@ -5996,104 +5996,89 @@ SOCK *ClientConnectToServer(CONNECTION *c) // Return a socket by connecting to the server SOCK *ClientConnectGetSocket(CONNECTION *c, bool additional_connect) { - SOCK *s = NULL; - CLIENT_OPTION *o; - WPC_CONNECT w; - wchar_t tmp[MAX_SIZE]; - SESSION *sess; volatile bool *cancel_flag = NULL; - void *hWnd; - UINT nat_t_err = 0; - bool is_additional_rudp_session = false; - UCHAR uc = 0; - IP ret_ip; + char hostname[MAX_HOST_NAME_LEN]; + bool save_resolved_ip = false; + CLIENT_OPTION *o; + SESSION *sess; + SOCK *sock = NULL; + IP resolved_ip; // Validate arguments - if (c == NULL) + if (c == NULL || c->Session == NULL || c->Session->ClientOption == NULL) { return NULL; } - Zero(&ret_ip, sizeof(IP)); - Zero(&w, sizeof(w)); - + cancel_flag = &c->Halt; sess = c->Session; - - if (sess != NULL) - { - cancel_flag = &sess->CancelConnect; - is_additional_rudp_session = sess->IsRUDPSession; - } - - hWnd = c->hWndForUI; - o = c->Session->ClientOption; - if (additional_connect) - { - if (sess != NULL) - { - Copy(&ret_ip, &sess->ServerIP_CacheForNextConnect, sizeof(IP)); - } - } + Zero(&resolved_ip, sizeof(resolved_ip)); - if (c->RestoreServerNameAndPort && additional_connect) + if (additional_connect == false && c->RestoreServerNameAndPort) { - // Restore to the original server name and port number + // Update server name and port number. + // At the time of writing this comment RestoreServerNameAndPort is never true. c->RestoreServerNameAndPort = false; if (StrCmpi(c->ServerName, o->Hostname) != 0) { StrCpy(c->ServerName, sizeof(c->ServerName), o->Hostname); - Zero(&ret_ip, sizeof(IP)); } c->ServerPort = o->Port; } - StrCpy(w.HostName, sizeof(w.HostName), c->ServerName); - w.Port = c->ServerPort; - StrCpy(w.ProxyHostName, sizeof(w.ProxyHostName), o->ProxyName); - w.ProxyPort = o->ProxyPort; - StrCpy(w.ProxyUsername, sizeof(w.ProxyUsername), o->ProxyUsername); - StrCpy(w.ProxyPassword, sizeof(w.ProxyPassword), o->ProxyPassword); - StrCpy(w.CustomHttpHeader, sizeof(w.CustomHttpHeader), o->CustomHttpHeader); - - switch (o->ProxyType) + if (IsZeroIP(&sess->ServerIP_CacheForNextConnect) == false) { - case PROXY_DIRECT: // TCP/IP - UniFormat(tmp, sizeof(tmp), _UU("STATUS_4"), w.HostName); + IPToStr(hostname, sizeof(hostname), &sess->ServerIP_CacheForNextConnect); + Debug("ClientConnectGetSocket(): Using cached IP address %s\n", hostname); + } + else + { + IP tmp; + + StrCpy(hostname, sizeof(hostname), o->ProxyType == PROXY_DIRECT ? c->ServerName : o->ProxyName); + + if (StrToIP(&tmp, hostname) == false) + { + // The hostname is not an IP address + save_resolved_ip = true; + } + } + + if (o->ProxyType == PROXY_DIRECT) + { + UINT nat_t_err = 0; + wchar_t tmp[MAX_SIZE]; + UniFormat(tmp, sizeof(tmp), _UU("STATUS_4"), hostname); PrintStatus(sess, tmp); - // Production job if (o->PortUDP == 0) { - { - // If additional_connect == false, enable trying to NAT-T connection - // If additional_connect == true, follow the IsRUDPSession setting in this session - s = TcpIpConnectEx(w.HostName, w.Port, - (bool *)cancel_flag, hWnd, &nat_t_err, (additional_connect ? (!is_additional_rudp_session) : false), - true, &ret_ip); - } + // If additional_connect == false, enable trying to NAT-T connection + // If additional_connect == true, follow the IsRUDPSession setting in this session + sock = TcpIpConnectEx(hostname, c->ServerPort, + (bool *)cancel_flag, c->hWndForUI, &nat_t_err, (additional_connect ? (!sess->IsRUDPSession) : false), + true, &resolved_ip); } else { // Mode to connect with R-UDP directly without using NAT-T server when using UDP IP ip; - - Zero(&ip, sizeof(ip)); - - StrToIP(&ip, o->Hostname); - - - s = NewRUDPClientDirect(VPN_RUDP_SVC_NAME, &ip, o->PortUDP, &nat_t_err, - TIMEOUT_TCP_PORT_CHECK, (bool *)cancel_flag, NULL, NULL, 0, false); - - if (s != NULL) + if (StrToIP(&ip, hostname)) { - StrCpy(s->UnderlayProtocol, sizeof(s->UnderlayProtocol), SOCK_UNDERLAY_NAT_T); + sock = NewRUDPClientDirect(VPN_RUDP_SVC_NAME, &ip, o->PortUDP, &nat_t_err, + TIMEOUT_TCP_PORT_CHECK, (bool *)cancel_flag, NULL, NULL, 0, false); + + if (sock != NULL) + { + StrCpy(sock->UnderlayProtocol, sizeof(sock->UnderlayProtocol), SOCK_UNDERLAY_NAT_T); + } } } - if (s == NULL) + + if (sock == NULL) { // Connection failure if (nat_t_err != RUDP_ERROR_NAT_T_TWO_OR_MORE) @@ -6104,822 +6089,108 @@ SOCK *ClientConnectGetSocket(CONNECTION *c, bool additional_connect) { c->Err = ERR_NAT_T_TWO_OR_MORE; } + return NULL; } - break; - - case PROXY_HTTP: // HTTP Proxy - UniFormat(tmp, sizeof(tmp), _UU("STATUS_2"), w.HostName, w.ProxyHostName); - PrintStatus(sess, tmp); - - // Proxy connection - s = ProxyConnectEx3(c, &w, additional_connect, (bool *)cancel_flag, hWnd, 0); - if (s == NULL) - { - // Connection failure - return NULL; - } - break; - - case PROXY_SOCKS: // SOCKS4 Proxy - UniFormat(tmp, sizeof(tmp), _UU("STATUS_2"), w.HostName, w.ProxyHostName); - PrintStatus(sess, tmp); - - // SOCKS4 connection - s = SocksConnectEx2(c, w.ProxyHostName, w.ProxyPort, - w.HostName, w.Port, w.ProxyUsername, additional_connect, (bool *)cancel_flag, - hWnd, 0, &ret_ip); - if (s == NULL) - { - // Connection failure - return NULL; - } - break; - - case PROXY_SOCKS5: // SOCKS5 Proxy - UniFormat(tmp, sizeof(tmp), _UU("STATUS_2"), w.HostName, w.ProxyHostName); - PrintStatus(sess, tmp); - - // SOCKS5 connection - s = Socks5Connect(c, &w, additional_connect, (bool *)cancel_flag, hWnd, 0, &ret_ip); - if (s == NULL) - { - // Connection failure - return NULL; - } - break; - } - - if (s == NULL) - { - // Connection failure - c->Err = ERR_CONNECT_FAILED; } else - { - // Success to connect - // Keep a note of the IP address - if (additional_connect == false || IsZeroIP(&s->RemoteIP)) - { - char *hostname = o->ProxyType == PROXY_DIRECT ? w.HostName : w.ProxyHostName; - if (((s->IsRUDPSocket || s->IPv6) && IsZeroIP(&s->RemoteIP) == false && o->ProxyType == PROXY_DIRECT) || GetIP(&c->Session->ServerIP, hostname) == false) - { - Copy(&c->Session->ServerIP, &s->RemoteIP, sizeof(IP)); - } - } - - if (IsZeroIP(&ret_ip) == false) - { - if (c->Session != NULL) - { - if (additional_connect == false) - { - Copy(&c->Session->ServerIP_CacheForNextConnect, &ret_ip, sizeof(IP)); - - Debug("Saved ServerIP_CacheForNextConnect: %s = %r\n", c->ServerName, &ret_ip); - } - } - } - } - - return s; -} - -// Connect via SOCKS4 -SOCK *SocksConnectEx2(CONNECTION *c, char *proxy_host_name, UINT proxy_port, - char *server_host_name, UINT server_port, - char *username, bool additional_connect, - bool *cancel_flag, void *hWnd, UINT timeout, IP *ret_ip) -{ - SOCK *s = NULL; - IP ip; - // Validate arguments - if (c == NULL || proxy_host_name == NULL || proxy_port == 0 || server_host_name == NULL - || server_port == 0) - { - if (c != NULL) - { - c->Err = ERR_PROXY_CONNECT_FAILED; - } - return NULL; - } - - // Get the IP address of the destination server - if (GetIP(&ip, server_host_name) == false) - { - // Failure - c->Err = ERR_CONNECT_FAILED; - return NULL; - } - - if (c->Halt) - { - // Stop - c->Err = ERR_USER_CANCEL; - return NULL; - } - - // Connection - s = TcpConnectEx3(proxy_host_name, proxy_port, timeout, cancel_flag, hWnd, true, NULL, false, ret_ip); - if (s == NULL) - { - // Failure - c->Err = ERR_PROXY_CONNECT_FAILED; - return NULL; - } - - // Timeout setting - SetTimeout(s, MIN(CONNECTING_TIMEOUT_PROXY, (timeout == 0 ? INFINITE : timeout))); - - if (additional_connect == false) - { - c->FirstSock = s; - } - - // Request packet transmission - if (SocksSendRequestPacket(c, s, server_port, &ip, username) == false) - { - // Failure - if (additional_connect == false) - { - c->FirstSock = NULL; - } - Disconnect(s); - ReleaseSock(s); - return NULL; - } - - // Receive a response packet - if (SocksRecvResponsePacket(c, s) == false) - { - // Failure - if (additional_connect == false) - { - c->FirstSock = NULL; - } - Disconnect(s); - ReleaseSock(s); - return NULL; - } - - SetTimeout(s, INFINITE); - - return s; -} - -// Receive a SOCKS4 response packet -bool SocksRecvResponsePacket(CONNECTION *c, SOCK *s) -{ - BUF *b; - UCHAR tmp[8]; - UCHAR vn, cd; - // Validate arguments - if (c == NULL || s == NULL) - { - return false; - } - - if (RecvAll(s, tmp, sizeof(tmp), false) == false) - { - c->Err = ERR_DISCONNECTED; - return false; - } - - b = NewBuf(); - WriteBuf(b, tmp, sizeof(tmp)); - SeekBuf(b, 0, 0); - - ReadBuf(b, &vn, 1); - ReadBuf(b, &cd, 1); - - FreeBuf(b); - - if (vn != 0) - { - c->Err = ERR_PROXY_ERROR; - return false; - } - - switch (cd) - { - case 90: - // Success - return true; - - case 93: - // Authentication failure - c->Err = ERR_PROXY_AUTH_FAILED; - return false; - - default: - // Connection to the server failure - c->Err = ERR_CONNECT_FAILED; - return false; - } -} - -// Send a SOCKS4 request packet -bool SocksSendRequestPacket(CONNECTION *c, SOCK *s, UINT dest_port, IP *dest_ip, char *userid) -{ - BUF *b; - UCHAR vn, cd; - USHORT port; - UINT ip; - bool ret; - // Validate arguments - if (s == NULL || dest_port == 0 || dest_ip == NULL || c == NULL) - { - return false; - } - if (userid == NULL) - { - userid = ""; - } - - b = NewBuf(); - vn = 4; - cd = 1; - WriteBuf(b, &vn, 1); - WriteBuf(b, &cd, 1); - port = Endian16((USHORT)dest_port); - ip = IPToUINT(dest_ip); - WriteBuf(b, &port, 2); - WriteBuf(b, &ip, 4); - WriteBuf(b, userid, StrLen(userid) + 1); - - ret = SendAll(s, b->Buf, b->Size, false); - if (ret == false) - { - c->Err = ERR_DISCONNECTED; - } - - FreeBuf(b); - - return ret; -} - -// Connect via SOCKS5 (RFC1928) -SOCK *Socks5Connect(CONNECTION *c, WPC_CONNECT *w, bool additional_connect, bool *cancel_flag, void *hWnd, UINT timeout, IP *ret_ip) -{ - UCHAR tmp, recv_buf[2], *recv_buf_final; - USHORT port; - bool ret; - SOCK *s; - BUF *b; - IP ip; - // Validate arguments - if (c == NULL || w == NULL || w->Port == 0 || w->ProxyPort == 0 || IsEmptyStr(w->HostName) || IsEmptyStr(w->ProxyHostName)) - { - if (c != NULL) - { - c->Err = ERR_PROXY_CONNECT_FAILED; - } - return NULL; - } - - if (c->Halt) - { - // Stop - c->Err = ERR_USER_CANCEL; - return NULL; - } - - // Open TCP connection to the proxy server - s = TcpConnectEx3(w->ProxyHostName, w->ProxyPort, timeout, cancel_flag, hWnd, true, NULL, false, ret_ip); - if (s == NULL) - { - // Failure - c->Err = ERR_PROXY_CONNECT_FAILED; - return NULL; - } - - // Set the timeout setting - SetTimeout(s, MIN(CONNECTING_TIMEOUT_PROXY, (timeout == 0 ? INFINITE : timeout))); - - if (additional_connect == false) - { - c->FirstSock = s; - } - - // +----+----------+----------+ - // |VER | NMETHODS | METHODS | - // +----+----------+----------+ - // | 1 | 1 | 1 to 255 | - // +----+----------+----------+ - // - // X'00' NO AUTHENTICATION REQUIRED - // X'01' GSSAPI - // X'02' USERNAME/PASSWORD - // X'03' to X'7F' IANA ASSIGNED - // X'80' to X'FE' RESERVED FOR PRIVATE METHODS - // X'FF' NO ACCEPTABLE METHOD - - b = NewBuf(); - tmp = 5; - WriteBuf(b, &tmp, sizeof(tmp)); // SOCKS version - tmp = 2; - WriteBuf(b, &tmp, sizeof(tmp)); // Number of supported methods - tmp = 0; - WriteBuf(b, &tmp, sizeof(tmp)); // No authentication - tmp = 2; - WriteBuf(b, &tmp, sizeof(tmp)); // Username/password - - ret = SendAll(s, b->Buf, b->Size, false); - FreeBuf(b); - - if (ret == false) - { - Debug("Socks5Connect(): [Phase 1] Failed to send initial data to the server.\n"); - c->Err = ERR_DISCONNECTED; - goto failure; - } - - // +----+--------+ - // |VER | METHOD | - // +----+--------+ - // | 1 | 1 | - // +----+--------+ - - if (RecvAll(s, recv_buf, sizeof(recv_buf), false) == false) - { - Debug("Socks5Connect(): [Phase 1] Failed to receive initial data response from the server.\n"); - c->Err = ERR_DISCONNECTED; - goto failure; - } - - if (recv_buf[0] != 5) - { - Debug("Socks5Connect(): [Phase 1] Unmatching version: %u.\n", recv_buf[0]); - c->Err = ERR_PROXY_ERROR; - goto failure; - } - - // Username/password authentication (RFC1929) - if (recv_buf[1] == 2) - { - // +----+------+----------+------+----------+ - // |VER | ULEN | UNAME | PLEN | PASSWD | - // +----+------+----------+------+----------+ - // | 1 | 1 | 1 to 255 | 1 | 1 to 255 | - // +----+------+----------+------+----------+ - - b = NewBuf(); - tmp = 1; - WriteBuf(b, &tmp, sizeof(tmp)); // Authentication protocol version - tmp = StrLen(w->ProxyUsername); - WriteBuf(b, &tmp, sizeof(tmp)); // Username length - WriteBuf(b, w->ProxyUsername, tmp); // Username - tmp = StrLen(w->ProxyPassword); - WriteBuf(b, &tmp, sizeof(tmp)); // Password length - WriteBuf(b, w->ProxyPassword, tmp); // Password - - ret = SendAll(s, b->Buf, b->Size, false); - FreeBuf(b); - - if (ret == false) - { - Debug("Socks5Connect(): [Phase 1] Failed to send authentication data to the server.\n"); - c->Err = ERR_DISCONNECTED; - goto failure; - } - - // +----+--------+ - // |VER | STATUS | - // +----+--------+ - // | 1 | 1 | - // +----+--------+ - - if (RecvAll(s, recv_buf, sizeof(recv_buf), false) == false) - { - Debug("Socks5Connect(): [Phase 1] Failed to receive authentication data response from the server.\n"); - c->Err = ERR_DISCONNECTED; - goto failure; - } - - if (recv_buf[1] != 0) - { - Debug("Socks5Connect(): [Phase 1] Authentication failure error code sent by the server: %u.\n", recv_buf[1]); - c->Err = ERR_PROXY_AUTH_FAILED; - goto failure; - } - } - - // +----+-----+-------+------+----------+----------+ - // |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | - // +----+-----+-------+------+----------+----------+ - // | 1 | 1 | X'00' | 1 | Variable | 2 | - // +----+-----+-------+------+----------+----------+ - // - // VER protocol version: X'05' - // CMD - // CONNECT X'01' - // BIND X'02' - // UDP ASSOCIATE X'03' - // RSV RESERVED - // ATYP address type of following address - // IP V4 address X'01' - // DOMAINNAME X'03' - // IP V6 address X'04' - // DST.ADDR desired destination address - // DST.PORT desired destination port in network octet order - - // Prepare data to send - b = NewBuf(); - tmp = 5; - WriteBuf(b, &tmp, sizeof(tmp)); // SOCKS version - tmp = 1; - WriteBuf(b, &tmp, sizeof(tmp)); // Command - tmp = 0; - WriteBuf(b, &tmp, sizeof(tmp)); // Reserved byte - - // Convert the hostname to an IP structure (if it's an IP address) - StrToIP(&ip, w->HostName); - - // If the IP structure doesn't contain an IP address, it means that the string is an hostname - if (IsZeroIp(&ip)) - { - UCHAR dest_length = StrLen(w->HostName); - tmp = 3; - WriteBuf(b, &tmp, sizeof(tmp)); // Destination type (hostname) - WriteBuf(b, &dest_length, sizeof(dest_length)); // Destination hostname length - WriteBuf(b, w->HostName, dest_length); // Destination hostname - } - else - { - if (IsIP6(&ip)) - { - tmp = 4; - WriteBuf(b, &tmp, sizeof(tmp)); // Destination type (IPv6) - WriteBuf(b, ip.ipv6_addr, sizeof(ip.ipv6_addr)); // Destination IPv6 address - } - else - { - tmp = 1; - WriteBuf(b, &tmp, sizeof(tmp)); // Destination type (IPv4) - WriteBuf(b, ip.addr, sizeof(ip.addr)); // Destination IPv4 address - } - } - - // Convert the port in network octet order - port = Endian16((USHORT)w->Port); - WriteBuf(b, &port, sizeof(port)); // Destination port - - // Send data - ret = SendAll(s, b->Buf, b->Size, false); - FreeBuf(b); - - if (ret == false) - { - Debug("Socks5Connect(): [Phase 2] Failed to send data to the server.\n"); - c->Err = ERR_DISCONNECTED; - goto failure; - } - - // +----+-----+-------+------+----------+----------+ - // |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | - // +----+-----+-------+------+----------+----------+ - // | 1 | 1 | X’00’ | 1 | Variable | 2 | - // +----+-----+-------+------+----------+----------+ - // - // VER protocol version: X’05’ - // REP Reply field: - // X’00’ succeeded - // X’01’ general SOCKS server failure - // X’02’ connection not allowed by ruleset - // X’03’ Network unreachable - // X’04’ Host unreachable - // X’05’ Connection refused - // X’06’ TTL expired - // X’07’ Command not supported - // X’08’ Address type not supported - // X’09’ to X’FF’ unassigned - - // The packet sent by the server should always have the same size as the one we sent to it. - // However, there are some implementations which send fixed values (aside from the first 2 bytes). - // In order to support such implementations, we read the first 4 bytes in order to know the address type before trying to read the rest of the packet. - recv_buf_final = Malloc(4); - - if (RecvAll(s, recv_buf_final, 4, false) == false) - { - Free(recv_buf_final); - Debug("Socks5Connect(): [Phase 2] Failed to receive response from the server.\n"); - c->Err = ERR_DISCONNECTED; - goto failure; - } - - // We only need the first two bytes (version and response code), but we have to read the entire packet from the socket - recv_buf[0] = recv_buf_final[0]; - recv_buf[1] = recv_buf_final[1]; - - // We receive the rest of the packet by knowing the size according to the address type - switch (recv_buf_final[3]) - { - case 1: - // IPv4 - recv_buf_final = ReAlloc(recv_buf_final, 6); // 4 bytes (IPv4) + 2 bytes (port) - ret = RecvAll(s, recv_buf_final, 6, false); - break; - case 4: - // IPv6 - recv_buf_final = ReAlloc(recv_buf_final, 18); // 4 bytes (IPv4) + 2 bytes (port) - ret = RecvAll(s, recv_buf_final, 18, false); - break; - case 3: - // Hostname - ret = RecvAll(s, &tmp, 1, false); - if (ret == true) - { - recv_buf_final = ReAlloc(recv_buf_final, tmp + 2); // Hostname length + 2 bytes (port) - ret = RecvAll(s, recv_buf_final, tmp + 2, false); - } - } - - Free(recv_buf_final); - - if (ret == false) - { - Debug("Socks5Connect(): [Phase 2] Malformed response received from the server.\n"); - c->Err = ERR_DISCONNECTED; - goto failure; - } - - if (recv_buf[0] != 5) - { - Debug("Socks5Connect(): [Phase 2] Unmatching version: %u.\n", recv_buf_final[0]); - c->Err = ERR_PROXY_ERROR; - goto failure; - } - - if (recv_buf[1] == 0) - { - // Success - SetTimeout(s, INFINITE); - return s; - } - else - { - Debug("Socks5Connect(): [Phase 2] Connection failed with error: %u\n", recv_buf[1]); - c->Err = ERR_PROXY_ERROR; - } - -failure: - if (additional_connect == false) - { - c->FirstSock = NULL; - } - - Disconnect(s); - ReleaseSock(s); - return NULL; -} - -// Connect through a proxy -SOCK *ProxyConnectEx2(CONNECTION *c, char *proxy_host_name, UINT proxy_port, - char *server_host_name, UINT server_port, - char *username, char *password, bool additional_connect, - bool *cancel_flag, void *hWnd, UINT timeout) -{ - WPC_CONNECT wpc_connect; - Zero(&wpc_connect, sizeof(wpc_connect)); - - StrCpy(wpc_connect.ProxyHostName, sizeof(wpc_connect.ProxyHostName), proxy_host_name); - wpc_connect.ProxyPort = proxy_port; - StrCpy(wpc_connect.HostName, sizeof(wpc_connect.HostName), server_host_name); - wpc_connect.Port = server_port; - StrCpy(wpc_connect.ProxyUsername, sizeof(wpc_connect.ProxyUsername), username); - StrCpy(wpc_connect.ProxyPassword, sizeof(wpc_connect.ProxyPassword), password); - - return ProxyConnectEx3(c, &wpc_connect, additional_connect, cancel_flag, hWnd, timeout); -} -SOCK *ProxyConnectEx3(CONNECTION *c, WPC_CONNECT *wpc_connect, - bool additional_connect, bool *cancel_flag, void *hWnd, - UINT timeout) -{ - SOCK *s = NULL; - bool use_auth = false; - char tmp[MAX_SIZE]; - char auth_tmp_str[MAX_SIZE], auth_b64_str[MAX_SIZE * 2]; - char basic_str[MAX_SIZE * 2]; - UINT http_error_code; - HTTP_HEADER *h; - char server_host_name_tmp[256]; - UINT i, len; - // Validate arguments - if (c == NULL || IsEmptyStr(wpc_connect->ProxyHostName) || wpc_connect->ProxyPort == 0 || IsEmptyStr(wpc_connect->HostName) || wpc_connect->Port == 0) - { - if (c != NULL) - { - c->Err = ERR_PROXY_CONNECT_FAILED; - } - return NULL; - } - - if ((IsEmptyStr(wpc_connect->ProxyUsername) || IsEmptyStr(wpc_connect->ProxyPassword)) == false) - { - use_auth = true; - } - - if (c->Halt) - { - // Stop - c->Err = ERR_USER_CANCEL; - return NULL; - } - - Zero(server_host_name_tmp, sizeof(server_host_name_tmp)); - StrCpy(server_host_name_tmp, sizeof(server_host_name_tmp), wpc_connect->HostName); - - len = StrLen(server_host_name_tmp); - - for (i = 0;i < len;i++) - { - if (server_host_name_tmp[i] == '/') - { - server_host_name_tmp[i] = 0; - } - } - - // Connection - s = TcpConnectEx3(wpc_connect->ProxyHostName, wpc_connect->ProxyPort, timeout, cancel_flag, hWnd, true, NULL, false, NULL); - if (s == NULL) - { - // Failure - c->Err = ERR_PROXY_CONNECT_FAILED; - return NULL; - } - - // Timeout setting - SetTimeout(s, MIN(CONNECTING_TIMEOUT_PROXY, (timeout == 0 ? INFINITE : timeout))); - - if (additional_connect == false) - { - c->FirstSock = s; - } - - // HTTP header generation - if (IsStrIPv6Address(server_host_name_tmp)) - { - IP ip; - char iptmp[MAX_PATH]; - - StrToIP(&ip, server_host_name_tmp); - IPToStr(iptmp, sizeof(iptmp), &ip); - - Format(tmp, sizeof(tmp), "[%s]:%u", iptmp, wpc_connect->Port); - } - else - { - Format(tmp, sizeof(tmp), "%s:%u", server_host_name_tmp, wpc_connect->Port); - } - - h = NewHttpHeader("CONNECT", tmp, "HTTP/1.0"); - - if (IsEmptyStr(wpc_connect->CustomHttpHeader) == false) - { - TOKEN_LIST *tokens = ParseToken(wpc_connect->CustomHttpHeader, "\r\n"); - if (tokens != NULL) - { - for (i = 0; i < tokens->NumTokens; i++) - { - AddHttpValueStr(h, tokens->Token[i]); - } - - FreeToken(tokens); - } - } - - if (GetHttpValue(h, "User-Agent") == NULL) - { - AddHttpValue(h, NewHttpValue("User-Agent", (c->Cedar == NULL ? DEFAULT_USER_AGENT : c->Cedar->HttpUserAgent))); - } - - if (GetHttpValue(h, "Host") == NULL) - { - AddHttpValue(h, NewHttpValue("Host", server_host_name_tmp)); - } - - if (GetHttpValue(h, "Content-Length") == NULL) - { - AddHttpValue(h, NewHttpValue("Content-Length", "0")); - } - - if (GetHttpValue(h, "Proxy-Connection") == NULL) - { - AddHttpValue(h, NewHttpValue("Proxy-Connection", "Keep-Alive")); - } - - if (GetHttpValue(h, "Pragma") == NULL) - { - AddHttpValue(h, NewHttpValue("Pragma", "no-cache")); - } - - if (use_auth && GetHttpValue(h, "Proxy-Authorization") == NULL) { wchar_t tmp[MAX_SIZE]; - UniFormat(tmp, sizeof(tmp), _UU("STATUS_3"), server_host_name_tmp); - // Generate the authentication string - Format(auth_tmp_str, sizeof(auth_tmp_str), "%s:%s", - wpc_connect->ProxyUsername, wpc_connect->ProxyPassword); + PROXY_PARAM_OUT out; + PROXY_PARAM_IN in; + UINT ret; - // Base64 encode - Zero(auth_b64_str, sizeof(auth_b64_str)); - Encode64(auth_b64_str, auth_tmp_str); - Format(basic_str, sizeof(basic_str), "Basic %s", auth_b64_str); + Zero(&in, sizeof(in)); - AddHttpValue(h, NewHttpValue("Proxy-Authorization", basic_str)); - } + in.Timeout = 0; - // Transmission - if (SendHttpHeader(s, h) == false) - { - // Failure - if (additional_connect == false) + StrCpy(in.TargetHostname, sizeof(in.TargetHostname), c->ServerName); + in.TargetPort = c->ServerPort; + + StrCpy(in.Hostname, sizeof(in.Hostname), IsEmptyStr(hostname) ? o->ProxyName : hostname); + in.Port = o->ProxyPort; + + StrCpy(in.Username, sizeof(in.Username), o->ProxyUsername); + StrCpy(in.Password, sizeof(in.Password), o->ProxyPassword); + + StrCpy(in.HttpCustomHeader, sizeof(in.HttpCustomHeader), o->CustomHttpHeader); + StrCpy(in.HttpUserAgent, sizeof(in.HttpUserAgent), c->Cedar->HttpUserAgent); + +#ifdef OS_WIN32 + in.Hwnd = c->hWndForUI; +#endif + + UniFormat(tmp, sizeof(tmp), _UU("STATUS_2"), in.TargetHostname, in.Hostname); + PrintStatus(sess, tmp); + + switch (o->ProxyType) { - c->FirstSock = NULL; - } - FreeHttpHeader(h); - Disconnect(s); - ReleaseSock(s); - c->Err = ERR_PROXY_ERROR; - return NULL; - } - - FreeHttpHeader(h); - - if (c->Halt) - { - // Stop - if (additional_connect == false) - { - c->FirstSock = NULL; - } - Disconnect(s); - ReleaseSock(s); - c->Err = ERR_USER_CANCEL; - return NULL; - } - - // Receive the results - h = RecvHttpHeader(s); - if (h == NULL) - { - // Failure - if (additional_connect == false) - { - c->FirstSock = NULL; - } - FreeHttpHeader(h); - Disconnect(s); - ReleaseSock(s); - c->Err = ERR_PROXY_ERROR; - return NULL; - } - - http_error_code = 0; - if (StrLen(h->Method) == 8) - { - if (Cmp(h->Method, "HTTP/1.", 7) == 0) - { - http_error_code = ToInt(h->Target); - } - } - FreeHttpHeader(h); - - // Check the code - switch (http_error_code) - { - case 401: - case 403: - case 407: - // Authentication failure - if (additional_connect == false) - { - c->FirstSock = NULL; - } - Disconnect(s); - ReleaseSock(s); - c->Err = ERR_PROXY_AUTH_FAILED; - return NULL; - - default: - if ((http_error_code / 100) == 2) - { - // Success - SetTimeout(s, INFINITE); - return s; - } - else - { - // Receive an unknown result - if (additional_connect == false) - { - c->FirstSock = NULL; - } - Disconnect(s); - ReleaseSock(s); - c->Err = ERR_PROXY_ERROR; + case PROXY_HTTP: + ret = ProxyHttpConnect(&out, &in, cancel_flag); + break; + case PROXY_SOCKS: + ret = ProxySocks4Connect(&out, &in, cancel_flag); + break; + case PROXY_SOCKS5: + ret = ProxySocks5Connect(&out, &in, cancel_flag); + break; + default: + c->Err = ERR_INTERNAL_ERROR; + Debug("ClientConnectGetSocket(): Unknown proxy type: %u!\n", o->ProxyType); return NULL; } + + c->Err = ProxyCodeToCedar(ret); + + if (c->Err != ERR_NO_ERROR) + { + Debug("ClientConnectGetSocket(): Connection via proxy server failed with error %u\n", ret); + return NULL; + } + + sock = out.Sock; + + CopyIP(&resolved_ip, &out.ResolvedIp); + } + + if (additional_connect == false || IsZeroIP(&sock->RemoteIP)) + { + if (((sock->IsRUDPSocket || sock->IPv6) && IsZeroIP(&sock->RemoteIP) == false && o->ProxyType == PROXY_DIRECT) || GetIP(&c->Session->ServerIP, hostname) == false) + { + Copy(&c->Session->ServerIP, &sock->RemoteIP, sizeof(c->Session->ServerIP)); + } + } + + if (save_resolved_ip && IsZeroIP(&resolved_ip) == false) + { + Copy(&c->Session->ServerIP_CacheForNextConnect, &resolved_ip, sizeof(c->Session->ServerIP_CacheForNextConnect)); + Debug("ClientConnectGetSocket(): Saved %s IP address %r for future connections.\n", hostname, &resolved_ip); + } + + return sock; +} + +UINT ProxyCodeToCedar(UINT code) +{ + switch (code) + { + case PROXY_ERROR_SUCCESS: + return ERR_NO_ERROR; + case PROXY_ERROR_GENERIC: + case PROXY_ERROR_VERSION: + return ERR_PROXY_ERROR; + case PROXY_ERROR_CANCELED: + return ERR_USER_CANCEL; + case PROXY_ERROR_CONNECTION: + return ERR_PROXY_CONNECT_FAILED; + case PROXY_ERROR_TARGET: + return ERR_CONNECT_FAILED; + case PROXY_ERROR_DISCONNECTED: + return ERR_DISCONNECTED; + case PROXY_ERROR_AUTHENTICATION: + return ERR_PROXY_AUTH_FAILED; + default: + return ERR_INTERNAL_ERROR; } } diff --git a/src/Cedar/Protocol.h b/src/Cedar/Protocol.h index fabc0887..260a6b4e 100644 --- a/src/Cedar/Protocol.h +++ b/src/Cedar/Protocol.h @@ -120,11 +120,11 @@ bool ClientUploadAuth(CONNECTION *c); SOCK *ClientConnectGetSocket(CONNECTION *c, bool additional_connect); SOCK *TcpConnectEx3(char *hostname, UINT port, UINT timeout, bool *cancel_flag, void *hWnd, bool no_nat_t, UINT *nat_t_error_code, bool try_start_ssl, IP *ret_ip); +UINT ProxyCodeToCedar(UINT code); + void InitProtocol(); void FreeProtocol(); - - POLICY *PackGetPolicy(PACK *p); void PackAddPolicy(PACK *p, POLICY *y); PACK *PackWelcome(SESSION *s); @@ -151,20 +151,6 @@ SOCK *ClientAdditionalConnectToServer(CONNECTION *c); bool ClientUploadAuth2(CONNECTION *c, SOCK *s); bool GetSessionKeyFromPack(PACK *p, UCHAR *session_key, UINT *session_key_32); -SOCK *ProxyConnectEx2(CONNECTION *c, char *proxy_host_name, UINT proxy_port, - char *server_host_name, UINT server_port, - char *username, char *password, bool additional_connect, - bool *cancel_flag, void *hWnd, UINT timeout); -SOCK *ProxyConnectEx3(CONNECTION *c, WPC_CONNECT *wpc_connect, - bool additional_connect, bool *cancel_flag, void *hWnd, - UINT timeout); -SOCK *SocksConnectEx2(CONNECTION *c, char *proxy_host_name, UINT proxy_port, - char *server_host_name, UINT server_port, - char *username, bool additional_connect, - bool *cancel_flag, void *hWnd, UINT timeout, IP *ret_ip); -bool SocksSendRequestPacket(CONNECTION *c, SOCK *s, UINT dest_port, IP *dest_ip, char *userid); -bool SocksRecvResponsePacket(CONNECTION *c, SOCK *s); -SOCK *Socks5Connect(CONNECTION *c, WPC_CONNECT *w, bool additional_connect, bool *cancel_flag, void *hWnd, UINT timeout, IP *ret_ip); void CreateNodeInfo(NODE_INFO *info, CONNECTION *c); UINT SecureSign(SECURE_SIGN *sign, UINT device_id, char *pin); void ClientUploadNoop(CONNECTION *c); diff --git a/src/Cedar/Wpc.c b/src/Cedar/Wpc.c index 22faaf5e..7b608562 100644 --- a/src/Cedar/Wpc.c +++ b/src/Cedar/Wpc.c @@ -500,64 +500,73 @@ SOCK *WpcSockConnect(WPC_CONNECT *param, UINT *error_code, UINT timeout) } SOCK *WpcSockConnectEx(WPC_CONNECT *param, UINT *error_code, UINT timeout, bool *cancel) { - CONNECTION c; SOCK *sock; - UINT err = ERR_NO_ERROR; + UINT ret; // Validate arguments if (param == NULL) { return NULL; } - Zero(&c, sizeof(c)); - - sock = NULL; - err = ERR_INTERNAL_ERROR; - - switch (param->ProxyType) + if (error_code == NULL) + { + error_code = &ret; + } + + if (param->ProxyType == PROXY_DIRECT) { - case PROXY_DIRECT: sock = TcpConnectEx3(param->HostName, param->Port, timeout, cancel, NULL, true, NULL, false, NULL); - if (sock == NULL) - { - err = ERR_CONNECT_FAILED; - } - break; - - case PROXY_HTTP: - sock = ProxyConnectEx3(&c, param, false, cancel, NULL, timeout); - if (sock == NULL) - { - err = c.Err; - } - break; - - case PROXY_SOCKS: - // SOCKS4 connection - sock = SocksConnectEx2(&c, param->ProxyHostName, param->ProxyPort, - param->HostName, param->Port, - param->ProxyUsername, false, cancel, NULL, timeout, NULL); - if (sock == NULL) - { - err = c.Err; - } - break; - - case PROXY_SOCKS5: - // SOCKS5 connection - sock = Socks5Connect(&c, param, false, cancel, NULL, timeout, NULL); - if (sock == NULL) - { - err = c.Err; - } + *error_code = (sock != NULL ? ERR_NO_ERROR : ERR_CONNECT_FAILED); + return sock; } - - if (error_code != NULL) + else { - *error_code = err; - } + PROXY_PARAM_OUT out; + PROXY_PARAM_IN in; + UINT ret; - return sock; + Zero(&in, sizeof(in)); + + in.Timeout = timeout; + + StrCpy(in.TargetHostname, sizeof(in.TargetHostname), param->HostName); + in.TargetPort = param->Port; + + StrCpy(in.Hostname, sizeof(in.Hostname), param->ProxyHostName); + in.Port = param->ProxyPort; + + StrCpy(in.Username, sizeof(in.Username), param->ProxyUsername); + StrCpy(in.Password, sizeof(in.Password), param->ProxyPassword); + + StrCpy(in.HttpCustomHeader, sizeof(in.HttpCustomHeader), param->CustomHttpHeader); + + switch (param->ProxyType) + { + case PROXY_HTTP: + ret = ProxyHttpConnect(&out, &in, cancel); + break; + case PROXY_SOCKS: + ret = ProxySocks4Connect(&out, &in, cancel); + break; + case PROXY_SOCKS5: + ret = ProxySocks5Connect(&out, &in, cancel); + break; + default: + *error_code = ERR_INTERNAL_ERROR; + Debug("WpcSockConnect(): Unknown proxy type: %u!\n", param->ProxyType); + return NULL; + } + + *error_code = ProxyCodeToCedar(ret); + + if (*error_code != ERR_NO_ERROR) + { + Debug("ClientConnectGetSocket(): Connection via proxy server failed with error %u\n", ret); + return NULL; + } + + return out.Sock; + } } SOCK *WpcSockConnect2(char *hostname, UINT port, INTERNET_SETTING *t, UINT *error_code, UINT timeout) { diff --git a/src/Mayaqua/HTTP.h b/src/Mayaqua/HTTP.h index 84814a8d..8d45cf22 100644 --- a/src/Mayaqua/HTTP.h +++ b/src/Mayaqua/HTTP.h @@ -16,15 +16,17 @@ #define HTTP_SAITAMA "/saitama.jpg" #define HTTP_PICTURES "/picture" // Maximum size of the custom HTTP header -#define HTTP_CUSTOM_HEADER_MAX_SIZE 1024 +#define HTTP_CUSTOM_HEADER_MAX_SIZE 1024 // Maximum size of a single line in the HTTP header -#define HTTP_HEADER_LINE_MAX_SIZE 4096 +#define HTTP_HEADER_LINE_MAX_SIZE 4096 // Maximum number of lines in the HTTP header -#define HTTP_HEADER_MAX_LINES 128 +#define HTTP_HEADER_MAX_LINES 128 +// Maximum size of the user agent string +#define HTTP_HEADER_USER_AGENT_MAX_SIZE 512 // Maximum size of the random number to be included in the PACK -#define HTTP_PACK_RAND_SIZE_MAX 1000 +#define HTTP_PACK_RAND_SIZE_MAX 1000 // Maximum PACK size in the HTTP -#define HTTP_PACK_MAX_SIZE 65536 +#define HTTP_PACK_MAX_SIZE 65536 // HTTP value struct HTTP_VALUE diff --git a/src/Mayaqua/MayaType.h b/src/Mayaqua/MayaType.h index 8f4cdece..b2ea200c 100644 --- a/src/Mayaqua/MayaType.h +++ b/src/Mayaqua/MayaType.h @@ -510,5 +510,8 @@ typedef struct IKE_HEADER IKE_HEADER; // HTTP.h typedef struct HTTP_MIME_TYPE HTTP_MIME_TYPE; -#endif // MAYATYPE_H +// Proxy.h +typedef struct PROXY_PARAM_IN PROXY_PARAM_IN; +typedef struct PROXY_PARAM_OUT PROXY_PARAM_OUT; +#endif // MAYATYPE_H diff --git a/src/Mayaqua/Mayaqua.h b/src/Mayaqua/Mayaqua.h index 2d88536f..236cf2d3 100644 --- a/src/Mayaqua/Mayaqua.h +++ b/src/Mayaqua/Mayaqua.h @@ -277,6 +277,9 @@ int iconv_close (iconv_t __cd); // HTTP #include +// Proxy +#include + // 64 bit real-time clock #include diff --git a/src/Mayaqua/Mayaqua.vcproj b/src/Mayaqua/Mayaqua.vcproj index efd2aa72..d3eda17b 100644 --- a/src/Mayaqua/Mayaqua.vcproj +++ b/src/Mayaqua/Mayaqua.vcproj @@ -366,6 +366,10 @@ RelativePath=".\Pack.c" > + + @@ -464,6 +468,10 @@ RelativePath=".\Pack.h" > + + diff --git a/src/Mayaqua/Proxy.c b/src/Mayaqua/Proxy.c new file mode 100644 index 00000000..9a5c4670 --- /dev/null +++ b/src/Mayaqua/Proxy.c @@ -0,0 +1,618 @@ +#include + +#include + +SOCK *Internal_ProxyTcpConnect(PROXY_PARAM_IN *param, volatile bool *cancel_flag, IP *resolved_ip) +{ +#ifdef OS_WIN32 + if (param->Hwnd != NULL) + { + return WinConnectEx3((HWND)param->Hwnd, param->Hostname, param->Port, param->Timeout, 0, NULL, NULL, NULL, NULL, false); + } +#endif + + return ConnectEx4(param->Hostname, param->Port, param->Timeout, (bool *)cancel_flag, NULL, NULL, false, true, resolved_ip); +} + +// Connect to an HTTP proxy +UINT ProxyHttpConnect(PROXY_PARAM_OUT *out, PROXY_PARAM_IN *in, volatile bool *cancel_flag) +{ + bool dummy_cancel_flag = false, use_auth = false; + char target_hostname[MAX_HOST_NAME_LEN + 1]; + char target_hostname_port[MAX_SIZE]; + HTTP_HEADER *h; + UINT i, ret; + SOCK *s; + // Validate arguments + if (out == NULL || in == NULL || in->Port == 0 || in->TargetPort == 0 || IsEmptyStr(in->Hostname) || IsEmptyStr(in->TargetHostname)) + { + return PROXY_ERROR_PARAMETER; + } + + if (cancel_flag == NULL) + { + cancel_flag = &dummy_cancel_flag; + } + else if (*cancel_flag) + { + return PROXY_ERROR_CANCELED; + } + + Zero(out, sizeof(PROXY_PARAM_OUT)); + + // Open TCP connection to the proxy server + s = Internal_ProxyTcpConnect(in, cancel_flag, &out->ResolvedIp); + if (s == NULL) + { + return PROXY_ERROR_CONNECTION; + } + + SetTimeout(s, MIN(PROXY_CONNECTION_TIMEOUT, (in->Timeout == 0 ? INFINITE : in->Timeout))); + + if ((IsEmptyStr(in->Username) || IsEmptyStr(in->Password)) == false) + { + use_auth = true; + } + + Zero(target_hostname, sizeof(target_hostname)); + StrCpy(target_hostname, sizeof(target_hostname), in->TargetHostname); + + for (i = 0; i < StrLen(target_hostname); ++i) + { + if (target_hostname[i] == '/') + { + target_hostname[i] = 0; + } + } + + // Generate HTTP header + if (IsStrIPv6Address(target_hostname)) + { + IP ip; + char iptmp[MAX_PATH]; + + StrToIP(&ip, target_hostname); + IPToStr(iptmp, sizeof(iptmp), &ip); + + Format(target_hostname_port, sizeof(target_hostname_port), "[%s]:%hu", iptmp, in->TargetPort); + } + else + { + Format(target_hostname_port, sizeof(target_hostname_port), "%s:%hu", target_hostname, in->TargetPort); + } + + h = NewHttpHeader("CONNECT", target_hostname_port, "HTTP/1.0"); + + if (IsEmptyStr(in->HttpCustomHeader) == false) + { + TOKEN_LIST *tokens = ParseToken(in->HttpCustomHeader, "\r\n"); + if (tokens != NULL) + { + for (i = 0; i < tokens->NumTokens; i++) + { + AddHttpValueStr(h, tokens->Token[i]); + } + + FreeToken(tokens); + } + } + + if (GetHttpValue(h, "User-Agent") == NULL) + { + AddHttpValue(h, NewHttpValue("User-Agent", IsEmptyStr(in->HttpUserAgent) ? DEFAULT_USER_AGENT : in->HttpUserAgent)); + } + + if (GetHttpValue(h, "Host") == NULL) + { + AddHttpValue(h, NewHttpValue("Host", target_hostname)); + } + + if (GetHttpValue(h, "Content-Length") == NULL) + { + AddHttpValue(h, NewHttpValue("Content-Length", "0")); + } + + if (GetHttpValue(h, "Proxy-Connection") == NULL) + { + AddHttpValue(h, NewHttpValue("Proxy-Connection", "Keep-Alive")); + } + + if (GetHttpValue(h, "Pragma") == NULL) + { + AddHttpValue(h, NewHttpValue("Pragma", "no-cache")); + } + + if (use_auth && GetHttpValue(h, "Proxy-Authorization") == NULL) + { + char auth_str[MAX_SIZE * 2], auth_b64_str[MAX_SIZE * 2]; + + // Generate the authentication string + Format(auth_str, sizeof(auth_str), "%s:%s", in->Username, in->Password); + + // Base64 encode + Zero(auth_b64_str, sizeof(auth_b64_str)); + Encode64(auth_b64_str, auth_str); + + // Generate final string + Format(auth_str, sizeof(auth_str), "Basic %s", auth_b64_str); + + AddHttpValue(h, NewHttpValue("Proxy-Authorization", auth_str)); + } + + // Transmission + ret = SendHttpHeader(s, h); + + FreeHttpHeader(h); + + if (ret == false) + { + ret = PROXY_ERROR_DISCONNECTED; + goto FAILURE; + } + + if (*cancel_flag) + { + ret = PROXY_ERROR_CANCELED; + goto FAILURE; + } + + // Receive the results + h = RecvHttpHeader(s); + if (h == NULL) + { + FreeHttpHeader(h); + ret = PROXY_ERROR_GENERIC; + goto FAILURE; + } + + ret = 0; + if (StrLen(h->Method) == 8) + { + if (Cmp(h->Method, "HTTP/1.", 7) == 0) + { + ret = ToInt(h->Target); + } + } + FreeHttpHeader(h); + + // Check the code + switch (ret) + { + case 401: + case 403: + case 407: + // Authentication failure + ret = PROXY_ERROR_AUTHENTICATION; + goto FAILURE; + + default: + if ((ret / 100) == 2) + { + // Success + SetTimeout(s, INFINITE); + out->Sock = s; + return PROXY_ERROR_SUCCESS; + } + else + { + // Unknown result + ret = PROXY_ERROR_GENERIC; + } + } + +FAILURE: + Disconnect(s); + ReleaseSock(s); + return ret; +} + +// Connect to a SOCKS5 proxy (RFC1928, RFC1929 defines username/password authentication) +UINT ProxySocks5Connect(PROXY_PARAM_OUT *out, PROXY_PARAM_IN *in, volatile bool *cancel_flag) +{ + bool dummy_cancel_flag = false; + UCHAR tmp, recv_buf[2], *recv_buf_final; + USHORT target_port; + IP target_ip; + UINT ret; + SOCK *s; + BUF *b; + // Validate arguments + if (out == NULL || in == NULL || in->Port == 0 || in->TargetPort == 0 || IsEmptyStr(in->Hostname) || IsEmptyStr(in->TargetHostname)) + { + return PROXY_ERROR_PARAMETER; + } + + if (cancel_flag == NULL) + { + cancel_flag = &dummy_cancel_flag; + } + else if (*cancel_flag) + { + return PROXY_ERROR_CANCELED; + } + + Zero(out, sizeof(PROXY_PARAM_OUT)); + + // Open TCP connection to the proxy server + s = Internal_ProxyTcpConnect(in, cancel_flag, &out->ResolvedIp); + if (s == NULL) + { + return PROXY_ERROR_CONNECTION; + } + + SetTimeout(s, MIN(PROXY_CONNECTION_TIMEOUT, (in->Timeout == 0 ? INFINITE : in->Timeout))); + + // +----+----------+----------+ + // |VER | NMETHODS | METHODS | + // +----+----------+----------+ + // | 1 | 1 | 1 to 255 | + // +----+----------+----------+ + // + // X'00' NO AUTHENTICATION REQUIRED + // X'01' GSSAPI + // X'02' USERNAME/PASSWORD + // X'03' to X'7F' IANA ASSIGNED + // X'80' to X'FE' RESERVED FOR PRIVATE METHODS + // X'FF' NO ACCEPTABLE METHOD + + b = NewBuf(); + tmp = 5; + WriteBuf(b, &tmp, sizeof(tmp)); // SOCKS version + tmp = 2; + WriteBuf(b, &tmp, sizeof(tmp)); // Number of supported methods + tmp = 0; + WriteBuf(b, &tmp, sizeof(tmp)); // No authentication + tmp = 2; + WriteBuf(b, &tmp, sizeof(tmp)); // Username/password + + ret = SendAll(s, b->Buf, b->Size, false); + + if (ret == false) + { + FreeBuf(b); + Debug("ProxySocks5Connect(): [Phase 1] Failed to send initial data to the server.\n"); + ret = PROXY_ERROR_DISCONNECTED; + goto FAILURE; + } + + // +----+--------+ + // |VER | METHOD | + // +----+--------+ + // | 1 | 1 | + // +----+--------+ + + if (RecvAll(s, recv_buf, sizeof(recv_buf), false) == false) + { + FreeBuf(b); + Debug("ProxySocks5Connect(): [Phase 1] Failed to receive initial data response from the server.\n"); + ret = PROXY_ERROR_DISCONNECTED; + goto FAILURE; + } + + if (recv_buf[0] != 5) + { + FreeBuf(b); + Debug("ProxySocks5Connect(): [Phase 1] Unmatching version: %u.\n", recv_buf[0]); + ret = PROXY_ERROR_VERSION; + goto FAILURE; + } + + ClearBuf(b); + + // Username/password authentication (RFC1929) + if (recv_buf[1] == 2) + { + // +----+------+----------+------+----------+ + // |VER | ULEN | UNAME | PLEN | PASSWD | + // +----+------+----------+------+----------+ + // | 1 | 1 | 1 to 255 | 1 | 1 to 255 | + // +----+------+----------+------+----------+ + + tmp = 1; + WriteBuf(b, &tmp, sizeof(tmp)); // Authentication protocol version + tmp = StrLen(in->Username); + WriteBuf(b, &tmp, sizeof(tmp)); // Username length + WriteBuf(b, in->Username, tmp); // Username + tmp = StrLen(in->Password); + WriteBuf(b, &tmp, sizeof(tmp)); // Password length + WriteBuf(b, in->Password, tmp); // Password + + ret = SendAll(s, b->Buf, b->Size, false); + + ClearBuf(b); + + if (ret == false) + { + Debug("ProxySocks5Connect(): [Phase 1] Failed to send authentication data to the server.\n"); + ret = PROXY_ERROR_DISCONNECTED; + goto FAILURE; + } + + // +----+--------+ + // |VER | STATUS | + // +----+--------+ + // | 1 | 1 | + // +----+--------+ + + if (RecvAll(s, recv_buf, sizeof(recv_buf), false) == false) + { + Debug("ProxySocks5Connect(): [Phase 1] Failed to receive authentication data response from the server.\n"); + ret = PROXY_ERROR_DISCONNECTED; + goto FAILURE; + } + + if (recv_buf[1] != 0) + { + Debug("ProxySocks5Connect(): [Phase 1] Authentication failure, error code sent by the server: %u.\n", recv_buf[1]); + ret = PROXY_ERROR_AUTHENTICATION; + goto FAILURE; + } + } + + // +----+-----+-------+------+----------+----------+ + // |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | + // +----+-----+-------+------+----------+----------+ + // | 1 | 1 | X'00' | 1 | Variable | 2 | + // +----+-----+-------+------+----------+----------+ + // + // VER protocol version: X'05' + // CMD + // CONNECT X'01' + // BIND X'02' + // UDP ASSOCIATE X'03' + // RSV RESERVED + // ATYP address type of following address + // IP V4 address X'01' + // DOMAINNAME X'03' + // IP V6 address X'04' + // DST.ADDR desired destination address + // DST.PORT desired destination port in network octet order + + // Prepare data to send + tmp = 5; + WriteBuf(b, &tmp, sizeof(tmp)); // SOCKS version + tmp = 1; + WriteBuf(b, &tmp, sizeof(tmp)); // Command + tmp = 0; + WriteBuf(b, &tmp, sizeof(tmp)); // Reserved byte + + // Convert the hostname to an IP structure (if it's an IP address) + StrToIP(&target_ip, in->TargetHostname); + + // If the IP structure doesn't contain an IP address, the string should be an hostname + if (IsZeroIp(&target_ip)) + { + UCHAR dest_length = StrLen(in->TargetHostname); + tmp = 3; + WriteBuf(b, &tmp, sizeof(tmp)); // Destination type (hostname) + WriteBuf(b, &dest_length, sizeof(dest_length)); // Destination hostname length + WriteBuf(b, in->TargetHostname, dest_length); // Destination hostname + } + else + { + if (IsIP6(&target_ip)) + { + tmp = 4; + WriteBuf(b, &tmp, sizeof(tmp)); // Destination type (IPv6) + WriteBuf(b, target_ip.ipv6_addr, sizeof(target_ip.ipv6_addr)); // Destination IPv6 address + } + else + { + tmp = 1; + WriteBuf(b, &tmp, sizeof(tmp)); // Destination type (IPv4) + WriteBuf(b, target_ip.addr, sizeof(target_ip.addr)); // Destination IPv4 address + } + } + + // Convert the port in network octet order + target_port = Endian16(in->TargetPort); + WriteBuf(b, &target_port, sizeof(target_port)); // Destination port + + // Send data + ret = SendAll(s, b->Buf, b->Size, false); + FreeBuf(b); + + if (ret == false) + { + Debug("ProxySocks5Connect(): [Phase 2] Failed to send data to the server.\n"); + ret = PROXY_ERROR_DISCONNECTED; + goto FAILURE; + } + + // +----+-----+-------+------+----------+----------+ + // |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | + // +----+-----+-------+------+----------+----------+ + // | 1 | 1 | X’00’ | 1 | Variable | 2 | + // +----+-----+-------+------+----------+----------+ + // + // VER protocol version: X’05’ + // REP Reply field: + // X’00’ succeeded + // X’01’ general SOCKS server failure + // X’02’ connection not allowed by ruleset + // X’03’ Network unreachable + // X’04’ Host unreachable + // X’05’ Connection refused + // X’06’ TTL expired + // X’07’ Command not supported + // X’08’ Address type not supported + // X’09’ to X’FF’ unassigned + + // The packet sent by the server should always have the same size as the one we sent to it. + // However, there are some implementations which send fixed values (aside from the first 2 bytes). + // In order to support such implementations, we read the first 4 bytes in order to know the address type before trying to read the rest of the packet. + recv_buf_final = Malloc(4); + + if (RecvAll(s, recv_buf_final, 4, false) == false) + { + Free(recv_buf_final); + Debug("ProxySocks5Connect(): [Phase 2] Failed to receive response from the server.\n"); + ret = PROXY_ERROR_DISCONNECTED; + goto FAILURE; + } + + // We only need the first two bytes (version and response code), but we have to read the entire packet from the socket + recv_buf[0] = recv_buf_final[0]; + recv_buf[1] = recv_buf_final[1]; + + // We receive the rest of the packet by knowing the size according to the address type + switch (recv_buf_final[3]) + { + case 1: + // IPv4 + recv_buf_final = ReAlloc(recv_buf_final, 6); // 4 bytes (IPv4) + 2 bytes (port) + ret = RecvAll(s, recv_buf_final, 6, false); + break; + case 4: + // IPv6 + recv_buf_final = ReAlloc(recv_buf_final, 18); // 16 bytes (IPv6) + 2 bytes (port) + ret = RecvAll(s, recv_buf_final, 18, false); + break; + case 3: + // Hostname + ret = RecvAll(s, &tmp, 1, false); + if (ret == true) + { + recv_buf_final = ReAlloc(recv_buf_final, tmp + 2); // Hostname length + 2 bytes (port) + ret = RecvAll(s, recv_buf_final, tmp + 2, false); + } + } + + Free(recv_buf_final); + + if (ret == false) + { + Debug("ProxySocks5Connect(): [Phase 2] Malformed response received from the server.\n"); + ret = PROXY_ERROR_DISCONNECTED; + goto FAILURE; + } + + if (recv_buf[0] != 5) + { + Debug("ProxySocks5Connect(): [Phase 2] Unmatching version: %u.\n", recv_buf_final[0]); + ret = PROXY_ERROR_VERSION; + goto FAILURE; + } + + switch (recv_buf[1]) + { + case 0: + // Success + SetTimeout(s, INFINITE); + out->Sock = s; + return PROXY_ERROR_SUCCESS; + case 3: + case 4: + case 5: + Debug("ProxySocks5Connect(): [Phase 2] Connection to target failed with error: %u\n", recv_buf[1]); + ret = PROXY_ERROR_TARGET; + goto FAILURE; + default: + Debug("ProxySocks5Connect(): [Phase 2] Connection failed with error: %u\n", recv_buf[1]); + ret = PROXY_ERROR_GENERIC; + goto FAILURE; + } + +FAILURE: + Disconnect(s); + ReleaseSock(s); + return ret; +} + +// Connect to a SOCKS4 proxy +UINT ProxySocks4Connect(PROXY_PARAM_OUT *out, PROXY_PARAM_IN *in, volatile bool *cancel_flag) +{ + bool dummy_cancel_flag = false; + UCHAR tmp, recv_buf[8]; + USHORT target_port; + IP target_ip; + UINT ret; + SOCK *s; + BUF *b; + // Validate arguments + if (out == NULL || in == NULL || in->Port == 0 || in->TargetPort == 0 || IsEmptyStr(in->Hostname) || IsEmptyStr(in->TargetHostname)) + { + return PROXY_ERROR_PARAMETER; + } + + if (cancel_flag == NULL) + { + cancel_flag = &dummy_cancel_flag; + } + else if (*cancel_flag) + { + return PROXY_ERROR_CANCELED; + } + + Zero(out, sizeof(PROXY_PARAM_OUT)); + + // Get the IP address of the destination server + if (GetIP(&target_ip, in->TargetHostname) == false) + { + return PROXY_ERROR_CONNECTION; + } + + // Open TCP connection to the proxy server + s = Internal_ProxyTcpConnect(in, cancel_flag, &out->ResolvedIp); + if (s == NULL) + { + return PROXY_ERROR_CONNECTION; + } + + SetTimeout(s, MIN(PROXY_CONNECTION_TIMEOUT, (in->Timeout == 0 ? INFINITE : in->Timeout))); + + // Send request packet + b = NewBuf(); + tmp = 4; + WriteBuf(b, &tmp, sizeof(tmp)); + tmp = 1; + WriteBuf(b, &tmp, sizeof(tmp)); + target_port = Endian16(in->TargetPort); + WriteBuf(b, &target_port, sizeof(target_port)); + WriteBuf(b, target_ip.addr, sizeof(target_ip.addr)); + WriteBuf(b, in->Username, StrLen(in->Username) + 1); + + ret = SendAll(s, b->Buf, b->Size, false); + + FreeBuf(b); + + if (ret == false) + { + ret = PROXY_ERROR_DISCONNECTED; + goto FAILURE; + } + + // Receive response packet + if (RecvAll(s, recv_buf, sizeof(recv_buf), false) == false) + { + ret = PROXY_ERROR_DISCONNECTED; + goto FAILURE; + } + + if (recv_buf[0] != 0) + { + ret = PROXY_ERROR_GENERIC; + goto FAILURE; + } + + switch (recv_buf[1]) + { + case 90: + // Success + SetTimeout(s, INFINITE); + out->Sock = s; + return PROXY_ERROR_SUCCESS; + case 93: + // Authentication failure + ret = PROXY_ERROR_AUTHENTICATION; + goto FAILURE; + default: + // Failed to connect to the target server + ret = PROXY_ERROR_TARGET; + } + +FAILURE: + Disconnect(s); + ReleaseSock(s); + return ret; +} diff --git a/src/Mayaqua/Proxy.h b/src/Mayaqua/Proxy.h new file mode 100644 index 00000000..27b67c36 --- /dev/null +++ b/src/Mayaqua/Proxy.h @@ -0,0 +1,45 @@ +#ifndef PROXY_H +#define PROXY_H + +#define PROXY_CONNECTION_TIMEOUT (4 * 1000) + +#define PROXY_MAX_USERNAME_LEN 255 +#define PROXY_MAX_PASSWORD_LEN 255 + +#define PROXY_ERROR_SUCCESS 0 +#define PROXY_ERROR_GENERIC 1 +#define PROXY_ERROR_PARAMETER 2 +#define PROXY_ERROR_CANCELED 3 +#define PROXY_ERROR_CONNECTION 4 +#define PROXY_ERROR_DISCONNECTED 5 +#define PROXY_ERROR_VERSION 6 +#define PROXY_ERROR_AUTHENTICATION 7 +#define PROXY_ERROR_TARGET 8 + +struct PROXY_PARAM_IN +{ + char Hostname[MAX_HOST_NAME_LEN + 1]; + USHORT Port; + char TargetHostname[MAX_HOST_NAME_LEN + 1]; + USHORT TargetPort; + char Username[PROXY_MAX_USERNAME_LEN + 1]; + char Password[PROXY_MAX_PASSWORD_LEN + 1]; + UINT Timeout; + char HttpCustomHeader[HTTP_CUSTOM_HEADER_MAX_SIZE + 1]; + char HttpUserAgent[HTTP_HEADER_USER_AGENT_MAX_SIZE + 1]; +#ifdef OS_WIN32 + void *Hwnd; +#endif +}; + +struct PROXY_PARAM_OUT +{ + SOCK *Sock; + IP ResolvedIp; +}; + +UINT ProxyHttpConnect(PROXY_PARAM_OUT *out, PROXY_PARAM_IN *in, volatile bool *cancel_flag); +UINT ProxySocks5Connect(PROXY_PARAM_OUT *out, PROXY_PARAM_IN *in, volatile bool *cancel_flag); +UINT ProxySocks4Connect(PROXY_PARAM_OUT *out, PROXY_PARAM_IN *in, volatile bool *cancel_flag); + +#endif