Fix error creation and warning
[claws.git] / src / common / proxy.c
blob6eb92ce981d043646271e5023deb2428e7407dcd
1 /*
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/>.
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
23 #include <glib.h>
25 #ifdef G_OS_WIN32
26 # include <winsock2.h>
27 # include <ws2tcpip.h>
28 # include <stdint.h>
29 #else
30 # include <sys/socket.h>
31 # include <netinet/in.h>
32 # include <netinet/ip.h>
33 #endif
35 #include "proxy.h"
36 #include "socket.h"
37 #include "utils.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)
46 gint ret;
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",
53 hostname, port,
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;
66 return ret;
67 } else if (proxy_info->proxy_type == PROXY_SOCKS4) {
68 return socks4_connect(sock, hostname, port);
69 } else {
70 g_warning("proxy_connect: unknown SOCKS type: %d",
71 proxy_info->proxy_type);
74 return -1;
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;
82 int s;
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);
89 socks_req[0] = 4;
90 socks_req[1] = 1;
91 *((gushort *)(socks_req + 2)) = htons(port);
93 /* lookup */
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);
98 if (s != 0) {
99 fprintf(stderr, "getaddrinfo for '%s' failed: %s\n",
100 hostname, gai_strerror(s));
101 return -1;
104 for (ai = res; ai != NULL; ai = ai->ai_next) {
105 uint32_t addr;
107 if (ai->ai_family != AF_INET)
108 continue;
110 addr = ((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr;
111 memcpy(socks_req + 4, &addr, 4);
112 got_address = TRUE;
113 break;
116 if (res != NULL)
117 freeaddrinfo(res);
119 if (!got_address) {
120 g_warning("socks4_connect: could not get valid IPv4 address for '%s'", hostname);
121 return -1;
124 debug_print("got a valid IPv4 address, continuing\n");
126 /* userid (empty) */
127 socks_req[8] = 0;
129 if (sock_write_all(sock, (gchar *)socks_req, 9) != 9) {
130 g_warning("socks4_connect: SOCKS4 initial request write failed");
131 return -1;
134 if (sock_read(sock, (gchar *)socks_req, 8) != 8) {
135 g_warning("socks4_connect: SOCKS4 response read failed");
136 return -1;
138 if (socks_req[0] != 0) {
139 g_warning("socks4_connect: SOCKS4 response has invalid version");
140 return -1;
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]);
144 return -1;
147 /* replace sock->hostname with endpoint */
148 if (sock->hostname != hostname) {
149 g_free(sock->hostname);
150 sock->hostname = g_strdup(hostname);
151 sock->port = port;
154 debug_print("socks4_connect: SOCKS4 connection to %s:%u successful.\n", hostname, port);
156 return 0;
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];
163 size_t len;
164 size_t size;
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);
172 if (len > 255) {
173 g_warning("socks5_connect: hostname too long");
174 return -1;
177 socks_req[0] = 5;
178 socks_req[1] = proxy_name ? 2 : 1;
179 socks_req[2] = 0;
180 socks_req[3] = 2;
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");
184 return -1;
187 if (sock_read(sock, (gchar *)socks_req, 2) != 2) {
188 g_warning("socks5_connect: SOCKS5 response read failed");
189 return -1;
191 if (socks_req[0] != 5) {
192 g_warning("socks5_connect: SOCKS5 response has invalid version");
193 return -1;
195 if (socks_req[1] == 2) {
196 /* auth */
197 size_t userlen, passlen;
198 gint reqlen;
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);
204 } else
205 userlen = passlen = 0;
207 socks_req[0] = 1;
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");
219 return -1;
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");
224 return -1;
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]);
228 return -1;
230 } else if (socks_req[1] != 0) {
231 g_warning("socks5_connect: SOCKS5 reply (%u) error", socks_req[1]);
232 return -1;
235 socks_req[0] = 5;
236 socks_req[1] = 1;
237 socks_req[2] = 0;
239 socks_req[3] = 3;
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");
246 return -1;
249 if (sock_read(sock, (gchar *)socks_req, 10) != 10) {
250 g_warning("socks5_connect: SOCKS5 connect request response read failed");
251 return -1;
253 if (socks_req[0] != 5) {
254 g_warning("socks5_connect: SOCKS5 response has invalid version");
255 return -1;
257 if (socks_req[1] != 0) {
258 g_warning("socks5_connect: SOCKS5 connection to %s:%u failed. (%u)",
259 hostname, port, socks_req[1]);
260 return -1;
263 size = 10;
264 if (socks_req[3] == 3)
265 size = 5 + socks_req[4] + 2;
266 else if (socks_req[3] == 4)
267 size = 4 + 16 + 2;
268 if (size > 10) {
269 size -= 10;
270 if (sock_read(sock, (gchar *)socks_req + 10, size) != size) {
271 g_warning("socks5_connect: SOCKS5 connect request response read failed");
272 return -1;
276 /* replace sock->hostname with endpoint */
277 if (sock->hostname != hostname) {
278 g_free(sock->hostname);
279 sock->hostname = g_strdup(hostname);
280 sock->port = port;
283 debug_print("socks5_connect: SOCKS5 connection to %s:%u successful.\n", hostname, port);
285 return 0;