2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 2018 the Claws Mail team
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>
39 gint
socks4_connect(SockInfo
*sock
, const gchar
*hostname
, gushort port
);
40 gint
socks5_connect(SockInfo
*sock
, const gchar
*hostname
, gushort port
,
41 const gchar
*proxy_name
, const gchar
*proxy_pass
);
43 gint
proxy_connect(SockInfo
*sock
, const gchar
*hostname
, gushort port
,
44 ProxyInfo
*proxy_info
)
48 g_return_val_if_fail(sock
!= NULL
, -1);
49 g_return_val_if_fail(hostname
!= NULL
, -1);
50 g_return_val_if_fail(proxy_info
!= NULL
, -1);
52 debug_print("proxy_connect: connect to %s:%u via %s:%u\n",
54 proxy_info
->proxy_host
, proxy_info
->proxy_port
);
56 if (proxy_info
->proxy_type
== PROXY_SOCKS5
) {
57 ret
= socks5_connect(sock
, hostname
, port
,
58 proxy_info
->use_proxy_auth
? proxy_info
->proxy_name
: NULL
,
59 proxy_info
->use_proxy_auth
? proxy_info
->proxy_pass
: NULL
);
60 /* Scrub the password before returning */
61 if (proxy_info
->proxy_pass
!= NULL
) {
62 memset(proxy_info
->proxy_pass
, 0, strlen(proxy_info
->proxy_pass
));
63 g_free(proxy_info
->proxy_pass
);
64 proxy_info
->proxy_pass
= NULL
;
67 } else if (proxy_info
->proxy_type
== PROXY_SOCKS4
) {
68 return socks4_connect(sock
, hostname
, port
);
70 g_warning("proxy_connect: unknown SOCKS type: %d",
71 proxy_info
->proxy_type
);
77 gint
socks4_connect(SockInfo
*sock
, const gchar
*hostname
, gushort port
)
79 guchar socks_req
[1024];
80 struct addrinfo hints
, *res
, *ai
;
81 gboolean got_address
= FALSE
;
84 g_return_val_if_fail(sock
!= NULL
, -1);
85 g_return_val_if_fail(hostname
!= NULL
, -1);
87 debug_print("socks4_connect: connect to %s:%u\n", hostname
, port
);
91 *((gushort
*)(socks_req
+ 2)) = htons(port
);
94 memset(&hints
, 0, sizeof(struct addrinfo
));
95 hints
.ai_family
= AF_INET
; /* SOCKS4 only supports IPv4 addresses */
97 s
= getaddrinfo(hostname
, NULL
, &hints
, &res
);
99 fprintf(stderr
, "getaddrinfo for '%s' failed: %s\n",
100 hostname
, gai_strerror(s
));
104 for (ai
= res
; ai
!= NULL
; ai
= ai
->ai_next
) {
107 if (ai
->ai_family
!= AF_INET
)
110 addr
= ((struct sockaddr_in
*)ai
->ai_addr
)->sin_addr
.s_addr
;
111 memcpy(socks_req
+ 4, &addr
, 4);
120 g_warning("socks4_connect: could not get valid IPv4 address for '%s'", hostname
);
124 debug_print("got a valid IPv4 address, continuing\n");
129 if (sock_write_all(sock
, (gchar
*)socks_req
, 9) != 9) {
130 g_warning("socks4_connect: SOCKS4 initial request write failed");
134 if (sock_read(sock
, (gchar
*)socks_req
, 8) != 8) {
135 g_warning("socks4_connect: SOCKS4 response read failed");
138 if (socks_req
[0] != 0) {
139 g_warning("socks4_connect: SOCKS4 response has invalid version");
142 if (socks_req
[1] != 90) {
143 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]);
147 /* replace sock->hostname with endpoint */
148 if (sock
->hostname
!= hostname
) {
149 g_free(sock
->hostname
);
150 sock
->hostname
= g_strdup(hostname
);
154 debug_print("socks4_connect: SOCKS4 connection to %s:%u successful.\n", hostname
, port
);
159 gint
socks5_connect(SockInfo
*sock
, const gchar
*hostname
, gushort port
,
160 const gchar
*proxy_name
, const gchar
*proxy_pass
)
162 guchar socks_req
[1024];
166 g_return_val_if_fail(sock
!= NULL
, -1);
167 g_return_val_if_fail(hostname
!= NULL
, -1);
169 debug_print("socks5_connect: connect to %s:%u\n", hostname
, port
);
171 len
= strlen(hostname
);
173 g_warning("socks5_connect: hostname too long");
178 socks_req
[1] = proxy_name
? 2 : 1;
182 if (sock_write_all(sock
, (gchar
*)socks_req
, 2 + socks_req
[1]) != 2 + socks_req
[1]) {
183 g_warning("socks5_connect: SOCKS5 initial request write failed");
187 if (sock_read(sock
, (gchar
*)socks_req
, 2) != 2) {
188 g_warning("socks5_connect: SOCKS5 response read failed");
191 if (socks_req
[0] != 5) {
192 g_warning("socks5_connect: SOCKS5 response has invalid version");
195 if (socks_req
[1] == 2) {
197 size_t userlen
, passlen
;
200 if (proxy_name
&& proxy_pass
) {
201 debug_print("socks5_connect: auth using username '%s'\n", proxy_name
);
202 userlen
= strlen(proxy_name
);
203 passlen
= strlen(proxy_pass
);
205 userlen
= passlen
= 0;
208 socks_req
[1] = (guchar
)userlen
;
209 if (proxy_name
&& userlen
> 0)
210 memcpy(socks_req
+ 2, proxy_name
, userlen
);
211 socks_req
[2 + userlen
] = (guchar
)passlen
;
212 if (proxy_pass
&& passlen
> 0)
213 memcpy(socks_req
+ 2 + userlen
+ 1, proxy_pass
, passlen
);
215 reqlen
= 2 + userlen
+ 1 + passlen
;
216 if (sock_write_all(sock
, (gchar
*)socks_req
, reqlen
) != reqlen
) {
217 memset(socks_req
, 0, reqlen
);
218 g_warning("socks5_connect: SOCKS5 auth write failed");
221 memset(socks_req
, 0, reqlen
);
222 if (sock_read(sock
, (gchar
*)socks_req
, 2) != 2) {
223 g_warning("socks5_connect: SOCKS5 auth response read failed");
226 if (socks_req
[1] != 0) {
227 g_warning("socks5_connect: SOCKS5 authentication failed: user: %s (%u %u)", proxy_name
? proxy_name
: "(none)", socks_req
[0], socks_req
[1]);
230 } else if (socks_req
[1] != 0) {
231 g_warning("socks5_connect: SOCKS5 reply (%u) error", socks_req
[1]);
240 socks_req
[4] = (guchar
)len
;
241 memcpy(socks_req
+ 5, hostname
, len
);
242 *((gushort
*)(socks_req
+ 5 + len
)) = htons(port
);
244 if (sock_write_all(sock
, (gchar
*)socks_req
, 5 + len
+ 2) != 5 + len
+ 2) {
245 g_warning("socks5_connect: SOCKS5 connect request write failed");
249 if (sock_read(sock
, (gchar
*)socks_req
, 10) != 10) {
250 g_warning("socks5_connect: SOCKS5 connect request response read failed");
253 if (socks_req
[0] != 5) {
254 g_warning("socks5_connect: SOCKS5 response has invalid version");
257 if (socks_req
[1] != 0) {
258 g_warning("socks5_connect: SOCKS5 connection to %s:%u failed. (%u)",
259 hostname
, port
, socks_req
[1]);
264 if (socks_req
[3] == 3)
265 size
= 5 + socks_req
[4] + 2;
266 else if (socks_req
[3] == 4)
270 if (sock_read(sock
, (gchar
*)socks_req
+ 10, size
) != size
) {
271 g_warning("socks5_connect: SOCKS5 connect request response read failed");
276 /* replace sock->hostname with endpoint */
277 if (sock
->hostname
!= hostname
) {
278 g_free(sock
->hostname
);
279 sock
->hostname
= g_strdup(hostname
);
283 debug_print("socks5_connect: SOCKS5 connection to %s:%u successful.\n", hostname
, port
);