libpurple/protocols/qq/udp_proxy_s5.c

branch
release-2.4.3
changeset 23195
1b9563a4ab49
parent 23189
679a03272eb6
parent 23193
384ba35bfa8f
child 23196
ee37c234be19
equal deleted inserted replaced
23189:679a03272eb6 23195:1b9563a4ab49
1 /**
2 * @file udp_proxy_s5.c
3 *
4 * purple
5 *
6 * Purple is the legal property of its developers, whose names are too numerous
7 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * source distribution.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
23 */
24
25 #include "debug.h"
26
27 #include "udp_proxy_s5.h"
28
29 static void _qq_s5_canread_again(gpointer data, gint source, PurpleInputCondition cond)
30 {
31 unsigned char buf[512];
32 struct PHB *phb = data;
33 struct sockaddr_in sin;
34 int len, error;
35 socklen_t errlen;
36 int flags;
37
38 purple_input_remove(phb->inpa);
39 purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Able to read again.\n");
40
41 len = read(source, buf, 10);
42 if (len < 10) {
43 purple_debug(PURPLE_DEBUG_WARNING, "socks5 proxy", "or not...\n");
44 close(source);
45
46 if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
47
48 phb->func(phb->data, source, NULL);
49 }
50
51 g_free(phb->host);
52 g_free(phb);
53 return;
54 }
55 if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
56 if ((buf[0] == 0x05) && (buf[1] < 0x09))
57 purple_debug(PURPLE_DEBUG_ERROR, "socks5 proxy", "socks5 error: %x\n", buf[1]);
58 else
59 purple_debug(PURPLE_DEBUG_ERROR, "socks5 proxy", "Bad data.\n");
60 close(source);
61
62 if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
63
64 phb->func(phb->data, -1, _("Unable to connect"));
65 }
66
67 g_free(phb->host);
68 g_free(phb);
69 return;
70 }
71
72 sin.sin_family = AF_INET;
73 memcpy(&sin.sin_addr.s_addr, buf + 4, 4);
74 memcpy(&sin.sin_port, buf + 8, 2);
75
76 if (connect(phb->udpsock, (struct sockaddr *) &sin, sizeof(struct sockaddr_in)) < 0) {
77 purple_debug(PURPLE_DEBUG_INFO, "s5_canread_again", "connect failed: %s\n", g_strerror(errno));
78 close(phb->udpsock);
79 close(source);
80 g_free(phb->host);
81 g_free(phb);
82 return;
83 }
84
85 error = ETIMEDOUT;
86 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connect didn't block\n");
87 errlen = sizeof(error);
88 if (getsockopt(phb->udpsock, SOL_SOCKET, SO_ERROR, &error, &errlen) < 0) {
89 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "getsockopt failed.\n");
90 close(phb->udpsock);
91 return;
92 }
93 flags = fcntl(phb->udpsock, F_GETFL);
94 fcntl(phb->udpsock, F_SETFL, flags & ~O_NONBLOCK);
95
96 if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
97 phb->func(phb->data, phb->udpsock, NULL);
98 }
99
100 g_free(phb->host);
101 g_free(phb);
102 }
103
104 static void _qq_s5_sendconnect(gpointer data, gint source)
105 {
106 unsigned char buf[512];
107 struct PHB *phb = data;
108 struct sockaddr_in sin, ctlsin;
109 int port;
110 socklen_t ctllen;
111 int flags;
112
113 purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "remote host is %s:%d\n", phb->host, phb->port);
114
115 buf[0] = 0x05;
116 buf[1] = 0x03; /* udp relay */
117 buf[2] = 0x00; /* reserved */
118 buf[3] = 0x01; /* address type -- ipv4 */
119 memset(buf + 4, 0, 0x04);
120
121 ctllen = sizeof(ctlsin);
122 if (getsockname(source, (struct sockaddr *) &ctlsin, &ctllen) < 0) {
123 purple_debug(PURPLE_DEBUG_INFO, "QQ", "getsockname: %s\n", g_strerror(errno));
124 close(source);
125 g_free(phb->host);
126 g_free(phb);
127 return;
128 }
129
130 phb->udpsock = socket(PF_INET, SOCK_DGRAM, 0);
131 purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "UDP socket=%d\n", phb->udpsock);
132 if (phb->udpsock < 0) {
133 close(source);
134 g_free(phb->host);
135 g_free(phb);
136 return;
137 }
138
139 flags = fcntl(phb->udpsock, F_GETFL);
140 fcntl(phb->udpsock, F_SETFL, flags | O_NONBLOCK);
141
142 port = g_ntohs(ctlsin.sin_port) + 1;
143 while (1) {
144 inet_aton("0.0.0.0", &(sin.sin_addr));
145 sin.sin_family = AF_INET;
146 sin.sin_port = g_htons(port);
147 if (bind(phb->udpsock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
148 port++;
149 if (port > 65500) {
150 close(source);
151 g_free(phb->host);
152 g_free(phb);
153 return;
154 }
155 } else
156 break;
157 }
158
159 memset(buf + 4, 0, 0x04);
160 memcpy(buf + 8, &(sin.sin_port), 0x02);
161
162 if (write(source, buf, 10) < 10) {
163 close(source);
164 purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "packet too small\n");
165
166 if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
167 phb->func(phb->data, -1, _("Unable to connect"));
168 }
169
170 g_free(phb->host);
171 g_free(phb);
172 return;
173 }
174
175 phb->inpa = purple_input_add(source, PURPLE_INPUT_READ, _qq_s5_canread_again, phb);
176 }
177
178 static void _qq_s5_readauth(gpointer data, gint source, PurpleInputCondition cond)
179 {
180 unsigned char buf[512];
181 struct PHB *phb = data;
182
183 purple_input_remove(phb->inpa);
184 purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Got auth response.\n");
185
186 if (read(source, buf, 2) < 2) {
187 close(source);
188
189 if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
190
191 phb->func(phb->data, -1, _("Unable to connect"));
192 }
193
194 g_free(phb->host);
195 g_free(phb);
196 return;
197 }
198
199 if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
200 close(source);
201
202 if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
203
204 phb->func(phb->data, -1, _("Unable to connect"));
205 }
206
207 g_free(phb->host);
208 g_free(phb);
209 return;
210 }
211
212 _qq_s5_sendconnect(phb, source);
213 }
214
215 static void _qq_s5_canread(gpointer data, gint source, PurpleInputCondition cond)
216 {
217 unsigned char buf[512];
218 struct PHB *phb;
219 int ret;
220
221 phb = data;
222
223 purple_input_remove(phb->inpa);
224 purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Able to read.\n");
225
226 ret = read(source, buf, 2);
227 if (ret < 2) {
228 purple_debug(PURPLE_DEBUG_INFO, "s5_canread", "packet smaller than 2 octet\n");
229 close(source);
230
231 if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
232
233 phb->func(phb->data, -1, _("Unable to connect"));
234 }
235
236 g_free(phb->host);
237 g_free(phb);
238 return;
239 }
240
241 if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
242 purple_debug(PURPLE_DEBUG_INFO, "s5_canread", "unsupport\n");
243 close(source);
244
245 if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
246
247 phb->func(phb->data, -1, _("Unable to connect"));
248 }
249
250 g_free(phb->host);
251 g_free(phb);
252 return;
253 }
254
255 if (buf[1] == 0x02) {
256 unsigned int i, j;
257
258 i = strlen(purple_proxy_info_get_username(phb->gpi));
259 j = strlen(purple_proxy_info_get_password(phb->gpi));
260
261 buf[0] = 0x01; /* version 1 */
262 buf[1] = i;
263 memcpy(buf + 2, purple_proxy_info_get_username(phb->gpi), i);
264 buf[2 + i] = j;
265 memcpy(buf + 2 + i + 1, purple_proxy_info_get_password(phb->gpi), j);
266
267 if (write(source, buf, 3 + i + j) < 3 + i + j) {
268 close(source);
269
270 if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
271
272 phb->func(phb->data, -1, _("Unable to connect"));
273 }
274
275 g_free(phb->host);
276 g_free(phb);
277 return;
278 }
279
280 phb->inpa = purple_input_add(source, PURPLE_INPUT_READ, _qq_s5_readauth, phb);
281 } else {
282 purple_debug(PURPLE_DEBUG_INFO, "s5_canread", "calling s5_sendconnect\n");
283 _qq_s5_sendconnect(phb, source);
284 }
285 }
286
287 static void _qq_s5_canwrite(gpointer data, gint source, PurpleInputCondition cond)
288 {
289 unsigned char buf[512];
290 int i;
291 struct PHB *phb = data;
292 socklen_t len;
293 int error = ETIMEDOUT;
294 int flags;
295
296 purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Connected.\n");
297
298 if (phb->inpa > 0)
299 purple_input_remove(phb->inpa);
300
301 len = sizeof(error);
302 if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
303 purple_debug(PURPLE_DEBUG_INFO, "getsockopt", "%s\n", g_strerror(errno));
304 close(source);
305 if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
306
307 phb->func(phb->data, -1, _("Unable to connect"));
308 }
309
310 g_free(phb->host);
311 g_free(phb);
312 return;
313 }
314 flags = fcntl(source, F_GETFL);
315 fcntl(source, F_SETFL, flags & ~O_NONBLOCK);
316
317 i = 0;
318 buf[0] = 0x05; /* SOCKS version 5 */
319
320 if (purple_proxy_info_get_username(phb->gpi) != NULL) {
321 buf[1] = 0x02; /* two methods */
322 buf[2] = 0x00; /* no authentication */
323 buf[3] = 0x02; /* username/password authentication */
324 i = 4;
325 } else {
326 buf[1] = 0x01;
327 buf[2] = 0x00;
328 i = 3;
329 }
330
331 if (write(source, buf, i) < i) {
332 purple_debug(PURPLE_DEBUG_INFO, "write", "%s\n", g_strerror(errno));
333 purple_debug(PURPLE_DEBUG_ERROR, "socks5 proxy", "Unable to write\n");
334 close(source);
335
336 if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
337
338 phb->func(phb->data, -1, _("Unable to connect"));
339 }
340
341 g_free(phb->host);
342 g_free(phb);
343 return;
344 }
345
346 phb->inpa = purple_input_add(source, PURPLE_INPUT_READ, _qq_s5_canread, phb);
347 }
348
349 gint qq_proxy_socks5(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen)
350 {
351 gint fd;
352 int flags;
353
354 purple_debug(PURPLE_DEBUG_INFO, "QQ",
355 "Connecting to %s:%d via %s:%d using SOCKS5\n",
356 phb->host, phb->port, purple_proxy_info_get_host(phb->gpi), purple_proxy_info_get_port(phb->gpi));
357
358 if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0)
359 return -1;
360
361 purple_debug(PURPLE_DEBUG_INFO, "QQ", "proxy_sock5 return fd=%d\n", fd);
362
363 flags = fcntl(fd, F_GETFL);
364 fcntl(fd, F_SETFL, flags | O_NONBLOCK);
365 if (connect(fd, addr, addrlen) < 0) {
366 if ((errno == EINPROGRESS) || (errno == EINTR)) {
367 purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Connect in asynchronous mode.\n");
368 phb->inpa = purple_input_add(fd, PURPLE_INPUT_WRITE, _qq_s5_canwrite, phb);
369 } else {
370 close(fd);
371 return -1;
372 }
373 } else {
374 purple_debug(PURPLE_DEBUG_MISC, "QQ", "Connect in blocking mode.\n");
375 flags = fcntl(fd, F_GETFL);
376 fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
377 _qq_s5_canwrite(phb, fd, PURPLE_INPUT_WRITE);
378 }
379
380 return fd;
381 }

mercurial