summaryrefslogtreecommitdiff
path: root/libsylph/socks.c
diff options
context:
space:
mode:
authorSimeon Simeonov2018-02-26 11:23:00 +0100
committerSimeon Simeonov2018-02-26 11:23:00 +0100
commit0b3cbf57875fd692e4ba0b336fefa4bee1ed00dc (patch)
treeaf916a30553c78ce9d4f2d8658656a175c5b6921 /libsylph/socks.c
Initial commit for sylpheed 3.7.0
Diffstat (limited to 'libsylph/socks.c')
-rw-r--r--libsylph/socks.c267
1 files changed, 267 insertions, 0 deletions
diff --git a/libsylph/socks.c b/libsylph/socks.c
new file mode 100644
index 0000000..b725ba7
--- /dev/null
+++ b/libsylph/socks.c
@@ -0,0 +1,267 @@
1/*
2 * LibSylph -- E-Mail client library
3 * Copyright (C) 1999-2014 Hiroyuki Yamamoto
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library 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 GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifdef HAVE_CONFIG_H
21# include "config.h"
22#endif
23
24#include <glib.h>
25
26#ifdef G_OS_WIN32
27# include <winsock2.h>
28# include <ws2tcpip.h>
29#endif
30
31#include "socket.h"
32#include "socks.h"
33#include "utils.h"
34
35
36SocksInfo *socks_info_new(SocksType type, const gchar *proxy_host,
37 gushort proxy_port, const gchar *proxy_name,
38 const gchar *proxy_pass)
39{
40 SocksInfo *socks_info;
41
42 socks_info = g_new0(SocksInfo, 1);
43 socks_info->type = type;
44 socks_info->proxy_host = g_strdup(proxy_host);
45 socks_info->proxy_port = proxy_port;
46 socks_info->proxy_name = g_strdup(proxy_name);
47 socks_info->proxy_pass = g_strdup(proxy_pass);
48
49 return socks_info;
50}
51
52void socks_info_free(SocksInfo *socks_info)
53{
54 if (!socks_info)
55 return;
56 g_free(socks_info->proxy_host);
57 g_free(socks_info->proxy_name);
58 g_free(socks_info->proxy_pass);
59 g_free(socks_info);
60}
61
62gint socks_connect(SockInfo *sock, const gchar *hostname, gushort port,
63 SocksInfo *socks_info)
64{
65 g_return_val_if_fail(sock != NULL, -1);
66 g_return_val_if_fail(hostname != NULL, -1);
67 g_return_val_if_fail(socks_info != NULL, -1);
68
69 debug_print("socks_connect: connect to %s:%u via %s:%u\n",
70 hostname, port,
71 socks_info->proxy_host, socks_info->proxy_port);
72
73 if (socks_info->type == SOCKS_SOCKS5)
74 return socks5_connect(sock, hostname, port,
75 socks_info->proxy_name,
76 socks_info->proxy_pass);
77 else if (socks_info->type == SOCKS_SOCKS4)
78 return socks4_connect(sock, hostname, port);
79 else
80 g_warning("socks_connect: unknown SOCKS type: %d\n",
81 socks_info->type);
82
83 return -1;
84}
85
86gint socks4_connect(SockInfo *sock, const gchar *hostname, gushort port)
87{
88 guchar socks_req[1024];
89 struct hostent *hp;
90
91 g_return_val_if_fail(sock != NULL, -1);
92 g_return_val_if_fail(hostname != NULL, -1);
93
94 debug_print("socks4_connect: connect to %s:%u\n", hostname, port);
95
96 socks_req[0] = 4;
97 socks_req[1] = 1;
98 *((gushort *)(socks_req + 2)) = htons(port);
99
100 /* lookup */
101 if ((hp = my_gethostbyname(hostname)) == NULL) {
102 g_warning("socks4_connect: cannot lookup host: %s", hostname);
103 return -1;
104 }
105 if (hp->h_length != 4) {
106 g_warning("socks4_connect: invalid address length for host: %s", hostname);
107 return -1;
108 }
109 memcpy(socks_req + 4, (guchar *)hp->h_addr, 4);
110
111 /* userid (empty) */
112 socks_req[8] = 0;
113
114 if (sock_write_all(sock, (gchar *)socks_req, 9) != 9) {
115 g_warning("socks4_connect: SOCKS4 initial request write failed");
116 return -1;
117 }
118
119 if (sock_read(sock, (gchar *)socks_req, 8) != 8) {
120 g_warning("socks4_connect: SOCKS4 response read failed");
121 return -1;
122 }
123 if (socks_req[0] != 0) {
124 g_warning("socks4_connect: SOCKS4 response has invalid version");
125 return -1;
126 }
127 if (socks_req[1] != 90) {
128 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]);
129 return -1;
130 }
131
132 /* replace sock->hostname with endpoint */
133 if (sock->hostname != hostname) {
134 g_free(sock->hostname);
135 sock->hostname = g_strdup(hostname);
136 sock->port = port;
137 }
138
139 debug_print("socks4_connect: SOCKS4 connection to %s:%u successful.\n", hostname, port);
140
141 return 0;
142}
143
144gint socks5_connect(SockInfo *sock, const gchar *hostname, gushort port,
145 const gchar *proxy_name, const gchar *proxy_pass)
146{
147 guchar socks_req[1024];
148 size_t len;
149 size_t size;
150
151 g_return_val_if_fail(sock != NULL, -1);
152 g_return_val_if_fail(hostname != NULL, -1);
153
154 debug_print("socks5_connect: connect to %s:%u\n", hostname, port);
155
156 len = strlen(hostname);
157 if (len > 255) {
158 g_warning("socks5_connect: hostname too long");
159 return -1;
160 }
161
162 socks_req[0] = 5;
163 socks_req[1] = proxy_name ? 2 : 1;
164 socks_req[2] = 0;
165 socks_req[3] = 2;
166
167 if (sock_write_all(sock, (gchar *)socks_req, 2 + socks_req[1]) != 2 + socks_req[1]) {
168 g_warning("socks5_connect: SOCKS5 initial request write failed");
169 return -1;
170 }
171
172 if (sock_read(sock, (gchar *)socks_req, 2) != 2) {
173 g_warning("socks5_connect: SOCKS5 response read failed");
174 return -1;
175 }
176 if (socks_req[0] != 5) {
177 g_warning("socks5_connect: SOCKS5 response has invalid version");
178 return -1;
179 }
180 if (socks_req[1] == 2) {
181 /* auth */
182 size_t userlen, passlen;
183 gint reqlen;
184
185 if (proxy_name && proxy_pass) {
186 userlen = strlen(proxy_name);
187 passlen = strlen(proxy_pass);
188 } else
189 userlen = passlen = 0;
190
191 socks_req[0] = 1;
192 socks_req[1] = (guchar)userlen;
193 if (proxy_name && userlen > 0)
194 memcpy(socks_req + 2, proxy_name, userlen);
195 socks_req[2 + userlen] = (guchar)passlen;
196 if (proxy_pass && passlen > 0)
197 memcpy(socks_req + 2 + userlen + 1, proxy_pass, passlen);
198
199 reqlen = 2 + userlen + 1 + passlen;
200 if (sock_write_all(sock, (gchar *)socks_req, reqlen) != reqlen) {
201 g_warning("socks5_connect: SOCKS5 auth write failed");
202 return -1;
203 }
204 if (sock_read(sock, (gchar *)socks_req, 2) != 2) {
205 g_warning("socks5_connect: SOCKS5 auth response read failed");
206 return -1;
207 }
208 if (socks_req[1] != 0) {
209 g_warning("socks5_connect: SOCKS5 authentication failed: user: %s (%u %u)", proxy_name ? proxy_name : "(none)", socks_req[0], socks_req[1]);
210 return -1;
211 }
212 } else if (socks_req[1] != 0) {
213 g_warning("socks5_connect: SOCKS5 reply (%u) error", socks_req[1]);
214 return -1;
215 }
216
217 socks_req[0] = 5;
218 socks_req[1] = 1;
219 socks_req[2] = 0;
220
221 socks_req[3] = 3;
222 socks_req[4] = (guchar)len;
223 memcpy(socks_req + 5, hostname, len);
224 *((gushort *)(socks_req + 5 + len)) = htons(port);
225
226 if (sock_write_all(sock, (gchar *)socks_req, 5 + len + 2) != 5 + len + 2) {
227 g_warning("socks5_connect: SOCKS5 connect request write failed");
228 return -1;
229 }
230
231 if (sock_read(sock, (gchar *)socks_req, 10) != 10) {
232 g_warning("socks5_connect: SOCKS5 connect request response read failed");
233 return -1;
234 }
235 if (socks_req[0] != 5) {
236 g_warning("socks5_connect: SOCKS5 response has invalid version");
237 return -1;
238 }
239 if (socks_req[1] != 0) {
240 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]);
241 return -1;
242 }
243
244 size = 10;
245 if (socks_req[3] == 3)
246 size = 5 + socks_req[4] + 2;
247 else if (socks_req[3] == 4)
248 size = 4 + 16 + 2;
249 if (size > 10) {
250 size -= 10;
251 if (sock_read(sock, (gchar *)socks_req + 10, size) != size) {
252 g_warning("socks5_connect: SOCKS5 connect request response read failed");
253 return -1;
254 }
255 }
256
257 /* replace sock->hostname with endpoint */
258 if (sock->hostname != hostname) {
259 g_free(sock->hostname);
260 sock->hostname = g_strdup(hostname);
261 sock->port = port;
262 }
263
264 debug_print("socks5_connect: SOCKS5 connection to %s:%u successful.\n", hostname, port);
265
266 return 0;
267}