2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2014 Hiroyuki Yamamoto
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 # include <winsock2.h>
27 # include <ws2tcpip.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <netinet/ip.h>
37 gint socks4_connect(SockInfo *sock, const gchar *hostname, gushort port);
38 gint socks5_connect(SockInfo *sock, const gchar *hostname, gushort port,
39 const gchar *proxy_name, const gchar *proxy_pass);
41 gint proxy_connect(SockInfo *sock, const gchar *hostname, gushort port,
42 ProxyInfo *proxy_info)
46 g_return_val_if_fail(sock != NULL, -1);
47 g_return_val_if_fail(hostname != NULL, -1);
48 g_return_val_if_fail(proxy_info != NULL, -1);
50 debug_print("proxy_connect: connect to %s:%u via %s:%u\n",
52 proxy_info->proxy_host, proxy_info->proxy_port);
54 if (proxy_info->proxy_type == PROXY_SOCKS5) {
55 ret = socks5_connect(sock, hostname, port,
56 proxy_info->use_proxy_auth ? proxy_info->proxy_name : NULL,
57 proxy_info->use_proxy_auth ? proxy_info->proxy_pass : NULL);
58 /* Scrub the password before returning */
59 if (proxy_info->proxy_pass != NULL) {
60 memset(proxy_info->proxy_pass, 0, strlen(proxy_info->proxy_pass));
61 g_free(proxy_info->proxy_pass);
64 } else if (proxy_info->proxy_type == PROXY_SOCKS4) {
65 return socks4_connect(sock, hostname, port);
67 g_warning("proxy_connect: unknown SOCKS type: %d\n",
68 proxy_info->proxy_type);
74 gint socks4_connect(SockInfo *sock, const gchar *hostname, gushort port)
76 guchar socks_req[1024];
77 struct addrinfo hints, *res, *ai;
78 gboolean got_address = FALSE;
81 g_return_val_if_fail(sock != NULL, -1);
82 g_return_val_if_fail(hostname != NULL, -1);
84 debug_print("socks4_connect: connect to %s:%u\n", hostname, port);
88 *((gushort *)(socks_req + 2)) = htons(port);
91 memset(&hints, 0, sizeof(struct addrinfo));
92 hints.ai_family = AF_INET; /* SOCKS4 only supports IPv4 addresses */
94 s = getaddrinfo(hostname, NULL, &hints, &res);
96 fprintf(stderr, "getaddrinfo for '%s' failed: %s\n",
97 hostname, gai_strerror(s));
101 for (ai = res; ai != NULL; ai = ai->ai_next) {
104 if (ai->ai_family != AF_INET)
107 addr = ((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr;
108 memcpy(socks_req + 4, &addr, 4);
117 g_warning("socks4_connect: could not get valid IPv4 address for '%s'", hostname);
121 debug_print("got a valid IPv4 address, continuing\n");
126 if (sock_write_all(sock, (gchar *)socks_req, 9) != 9) {
127 g_warning("socks4_connect: SOCKS4 initial request write failed");
131 if (sock_read(sock, (gchar *)socks_req, 8) != 8) {
132 g_warning("socks4_connect: SOCKS4 response read failed");
135 if (socks_req[0] != 0) {
136 g_warning("socks4_connect: SOCKS4 response has invalid version");
139 if (socks_req[1] != 90) {
140 g_warning("socks4_connect: SOCKS4 connection to %u.%u.%u.%u:%u failed. (%u)", socks_req[4], socks_req[5], socks_req[6], socks_req[7], ntohs(*(gushort *)(socks_req + 2)), socks_req[1]);
144 /* replace sock->hostname with endpoint */
145 if (sock->hostname != hostname) {
146 g_free(sock->hostname);
147 sock->hostname = g_strdup(hostname);
151 debug_print("socks4_connect: SOCKS4 connection to %s:%u successful.\n", hostname, port);
156 gint socks5_connect(SockInfo *sock, const gchar *hostname, gushort port,
157 const gchar *proxy_name, const gchar *proxy_pass)
159 guchar socks_req[1024];
163 g_return_val_if_fail(sock != NULL, -1);
164 g_return_val_if_fail(hostname != NULL, -1);
166 debug_print("socks5_connect: connect to %s:%u\n", hostname, port);
168 len = strlen(hostname);
170 g_warning("socks5_connect: hostname too long");
175 socks_req[1] = proxy_name ? 2 : 1;
179 if (sock_write_all(sock, (gchar *)socks_req, 2 + socks_req[1]) != 2 + socks_req[1]) {
180 g_warning("socks5_connect: SOCKS5 initial request write failed");
184 if (sock_read(sock, (gchar *)socks_req, 2) != 2) {
185 g_warning("socks5_connect: SOCKS5 response read failed");
188 if (socks_req[0] != 5) {
189 g_warning("socks5_connect: SOCKS5 response has invalid version");
192 if (socks_req[1] == 2) {
194 size_t userlen, passlen;
197 if (proxy_name && proxy_pass) {
198 debug_print("socks5_connect: auth using username '%s'\n", proxy_name);
199 userlen = strlen(proxy_name);
200 passlen = strlen(proxy_pass);
202 userlen = passlen = 0;
205 socks_req[1] = (guchar)userlen;
206 if (proxy_name && userlen > 0)
207 memcpy(socks_req + 2, proxy_name, userlen);
208 socks_req[2 + userlen] = (guchar)passlen;
209 if (proxy_pass && passlen > 0)
210 memcpy(socks_req + 2 + userlen + 1, proxy_pass, passlen);
212 reqlen = 2 + userlen + 1 + passlen;
213 if (sock_write_all(sock, (gchar *)socks_req, reqlen) != reqlen) {
214 memset(socks_req, 0, reqlen);
215 g_warning("socks5_connect: SOCKS5 auth write failed");
218 memset(socks_req, 0, reqlen);
219 if (sock_read(sock, (gchar *)socks_req, 2) != 2) {
220 g_warning("socks5_connect: SOCKS5 auth response read failed");
223 if (socks_req[1] != 0) {
224 g_warning("socks5_connect: SOCKS5 authentication failed: user: %s (%u %u)", proxy_name ? proxy_name : "(none)", socks_req[0], socks_req[1]);
227 } else if (socks_req[1] != 0) {
228 g_warning("socks5_connect: SOCKS5 reply (%u) error", socks_req[1]);
237 socks_req[4] = (guchar)len;
238 memcpy(socks_req + 5, hostname, len);
239 *((gushort *)(socks_req + 5 + len)) = htons(port);
241 if (sock_write_all(sock, (gchar *)socks_req, 5 + len + 2) != 5 + len + 2) {
242 g_warning("socks5_connect: SOCKS5 connect request write failed");
246 if (sock_read(sock, (gchar *)socks_req, 10) != 10) {
247 g_warning("socks5_connect: SOCKS5 connect request response read failed");
250 if (socks_req[0] != 5) {
251 g_warning("socks5_connect: SOCKS5 response has invalid version");
254 if (socks_req[1] != 0) {
255 if (socks_req[3] == 1) { /* IPv4 address */
256 g_warning("socks5_connect: SOCKS5 connection to %u.%u.%u.%u:%u failed. (%u)", socks_req[4], socks_req[5], socks_req[6], socks_req[7], ntohs(*(gushort *)(socks_req + 8)), socks_req[1]);
257 } else if (socks_req[3] == 3) { /* Domain name */
258 gint hnlen = socks_req[4];
259 gchar *hn = malloc(hnlen + 1);
260 hn[hnlen + 1] = '\0';
261 memcpy(hn, &socks_req[5], hnlen);
262 g_warning("socks5_connect: SOCKS5 connection to %s:%u failed. (%u)",
263 hn, ntohs(*(gushort *)(socks_req + 5 + hnlen)), socks_req[1]);
265 } else if (socks_req[3] == 4) { /* IPv6 address */
267 gchar *hn = malloc(hnlen + 1);
268 hn[hnlen + 1] = '\0';
269 memcpy(hn, &socks_req[4], hnlen);
270 g_warning("socks5_connect: SOCKS5 connection to IPv6 %s:%u failed. (%u)",
271 hn, ntohs(*(gushort *)(socks_req + 5 + hnlen)), socks_req[1]);
278 if (socks_req[3] == 3)
279 size = 5 + socks_req[4] + 2;
280 else if (socks_req[3] == 4)
284 if (sock_read(sock, (gchar *)socks_req + 10, size) != size) {
285 g_warning("socks5_connect: SOCKS5 connect request response read failed");
290 /* replace sock->hostname with endpoint */
291 if (sock->hostname != hostname) {
292 g_free(sock->hostname);
293 sock->hostname = g_strdup(hostname);
297 debug_print("socks5_connect: SOCKS5 connection to %s:%u successful.\n", hostname, port);