comparison libpurple/protocols/qq/udp_proxy_s5.c @ 15373:5fe8042783c1

Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author Sean Egan <seanegan@gmail.com>
date Sat, 20 Jan 2007 02:32:10 +0000
parents
children 32c366eeeb99
comparison
equal deleted inserted replaced
15372:f79e0f4df793 15373:5fe8042783c1
1 /**
2 * @file udp_proxy_s5.c
3 *
4 * gaim
5 *
6 * Gaim 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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, GaimInputCondition 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
37 gaim_input_remove(phb->inpa);
38 gaim_debug(GAIM_DEBUG_INFO, "socks5 proxy", "Able to read again.\n");
39
40 len = read(source, buf, 10);
41 if (len < 10) {
42 gaim_debug(GAIM_DEBUG_WARNING, "socks5 proxy", "or not...\n");
43 close(source);
44
45 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
46
47 phb->func(phb->data, source, NULL);
48 }
49
50 g_free(phb->host);
51 g_free(phb);
52 return;
53 }
54 if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
55 if ((buf[0] == 0x05) && (buf[1] < 0x09))
56 gaim_debug(GAIM_DEBUG_ERROR, "socks5 proxy", "socks5 error: %x\n", buf[1]);
57 else
58 gaim_debug(GAIM_DEBUG_ERROR, "socks5 proxy", "Bad data.\n");
59 close(source);
60
61 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
62
63 phb->func(phb->data, -1, _("Unable to connect"));
64 }
65
66 g_free(phb->host);
67 g_free(phb);
68 return;
69 }
70
71 sin.sin_family = AF_INET;
72 memcpy(&sin.sin_addr.s_addr, buf + 4, 4);
73 memcpy(&sin.sin_port, buf + 8, 2);
74
75 if (connect(phb->udpsock, (struct sockaddr *) &sin, sizeof(struct sockaddr_in)) < 0) {
76 gaim_debug(GAIM_DEBUG_INFO, "s5_canread_again", "connect failed: %s\n", strerror(errno));
77 close(phb->udpsock);
78 close(source);
79 g_free(phb->host);
80 g_free(phb);
81 return;
82 }
83
84 error = ETIMEDOUT;
85 gaim_debug(GAIM_DEBUG_INFO, "QQ", "Connect didn't block\n");
86 errlen = sizeof(error);
87 if (getsockopt(phb->udpsock, SOL_SOCKET, SO_ERROR, &error, &errlen) < 0) {
88 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "getsockopt failed.\n");
89 close(phb->udpsock);
90 return;
91 }
92 fcntl(phb->udpsock, F_SETFL, 0);
93
94 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
95 phb->func(phb->data, phb->udpsock, NULL);
96 }
97
98 g_free(phb->host);
99 g_free(phb);
100 }
101
102 static void _qq_s5_sendconnect(gpointer data, gint source)
103 {
104 unsigned char buf[512];
105 struct PHB *phb = data;
106 struct sockaddr_in sin, ctlsin;
107 int port;
108 socklen_t ctllen;
109
110 gaim_debug(GAIM_DEBUG_INFO, "s5_sendconnect", "remote host is %s:%d\n", phb->host, phb->port);
111
112 buf[0] = 0x05;
113 buf[1] = 0x03; /* udp relay */
114 buf[2] = 0x00; /* reserved */
115 buf[3] = 0x01; /* address type -- ipv4 */
116 memset(buf + 4, 0, 0x04);
117
118 ctllen = sizeof(ctlsin);
119 if (getsockname(source, (struct sockaddr *) &ctlsin, &ctllen) < 0) {
120 gaim_debug(GAIM_DEBUG_INFO, "QQ", "getsockname: %s\n", strerror(errno));
121 close(source);
122 g_free(phb->host);
123 g_free(phb);
124 return;
125 }
126
127 phb->udpsock = socket(PF_INET, SOCK_DGRAM, 0);
128 gaim_debug(GAIM_DEBUG_INFO, "s5_sendconnect", "UDP socket=%d\n", phb->udpsock);
129 if (phb->udpsock < 0) {
130 close(source);
131 g_free(phb->host);
132 g_free(phb);
133 return;
134 }
135
136 fcntl(phb->udpsock, F_SETFL, O_NONBLOCK);
137
138 port = g_ntohs(ctlsin.sin_port) + 1;
139 while (1) {
140 inet_aton("0.0.0.0", &(sin.sin_addr));
141 sin.sin_family = AF_INET;
142 sin.sin_port = g_htons(port);
143 if (bind(phb->udpsock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
144 port++;
145 if (port > 65500) {
146 close(source);
147 g_free(phb->host);
148 g_free(phb);
149 return;
150 }
151 } else
152 break;
153 }
154
155 memset(buf + 4, 0, 0x04);
156 memcpy(buf + 8, &(sin.sin_port), 0x02);
157
158 if (write(source, buf, 10) < 10) {
159 close(source);
160 gaim_debug(GAIM_DEBUG_INFO, "s5_sendconnect", "packet too small\n");
161
162 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
163 phb->func(phb->data, -1, _("Unable to connect"));
164 }
165
166 g_free(phb->host);
167 g_free(phb);
168 return;
169 }
170
171 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, _qq_s5_canread_again, phb);
172 }
173
174 static void _qq_s5_readauth(gpointer data, gint source, GaimInputCondition cond)
175 {
176 unsigned char buf[512];
177 struct PHB *phb = data;
178
179 gaim_input_remove(phb->inpa);
180 gaim_debug(GAIM_DEBUG_INFO, "socks5 proxy", "Got auth response.\n");
181
182 if (read(source, buf, 2) < 2) {
183 close(source);
184
185 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
186
187 phb->func(phb->data, -1, _("Unable to connect"));
188 }
189
190 g_free(phb->host);
191 g_free(phb);
192 return;
193 }
194
195 if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
196 close(source);
197
198 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
199
200 phb->func(phb->data, -1, _("Unable to connect"));
201 }
202
203 g_free(phb->host);
204 g_free(phb);
205 return;
206 }
207
208 _qq_s5_sendconnect(phb, source);
209 }
210
211 static void _qq_s5_canread(gpointer data, gint source, GaimInputCondition cond)
212 {
213 unsigned char buf[512];
214 struct PHB *phb;
215 int ret;
216
217 phb = data;
218
219 gaim_input_remove(phb->inpa);
220 gaim_debug(GAIM_DEBUG_INFO, "socks5 proxy", "Able to read.\n");
221
222 ret = read(source, buf, 2);
223 if (ret < 2) {
224 gaim_debug(GAIM_DEBUG_INFO, "s5_canread", "packet smaller than 2 octet\n");
225 close(source);
226
227 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
228
229 phb->func(phb->data, -1, _("Unable to connect"));
230 }
231
232 g_free(phb->host);
233 g_free(phb);
234 return;
235 }
236
237 if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
238 gaim_debug(GAIM_DEBUG_INFO, "s5_canread", "unsupport\n");
239 close(source);
240
241 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
242
243 phb->func(phb->data, -1, _("Unable to connect"));
244 }
245
246 g_free(phb->host);
247 g_free(phb);
248 return;
249 }
250
251 if (buf[1] == 0x02) {
252 unsigned int i, j;
253
254 i = strlen(gaim_proxy_info_get_username(phb->gpi));
255 j = strlen(gaim_proxy_info_get_password(phb->gpi));
256
257 buf[0] = 0x01; /* version 1 */
258 buf[1] = i;
259 memcpy(buf + 2, gaim_proxy_info_get_username(phb->gpi), i);
260 buf[2 + i] = j;
261 memcpy(buf + 2 + i + 1, gaim_proxy_info_get_password(phb->gpi), j);
262
263 if (write(source, buf, 3 + i + j) < 3 + i + j) {
264 close(source);
265
266 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
267
268 phb->func(phb->data, -1, _("Unable to connect"));
269 }
270
271 g_free(phb->host);
272 g_free(phb);
273 return;
274 }
275
276 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, _qq_s5_readauth, phb);
277 } else {
278 gaim_debug(GAIM_DEBUG_INFO, "s5_canread", "calling s5_sendconnect\n");
279 _qq_s5_sendconnect(phb, source);
280 }
281 }
282
283 static void _qq_s5_canwrite(gpointer data, gint source, GaimInputCondition cond)
284 {
285 unsigned char buf[512];
286 int i;
287 struct PHB *phb = data;
288 unsigned int len;
289 int error = ETIMEDOUT;
290
291 gaim_debug(GAIM_DEBUG_INFO, "socks5 proxy", "Connected.\n");
292
293 if (phb->inpa > 0)
294 gaim_input_remove(phb->inpa);
295
296 len = sizeof(error);
297 if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
298 gaim_debug(GAIM_DEBUG_INFO, "getsockopt", "%s\n", strerror(errno));
299 close(source);
300 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
301
302 phb->func(phb->data, -1, _("Unable to connect"));
303 }
304
305 g_free(phb->host);
306 g_free(phb);
307 return;
308 }
309 fcntl(source, F_SETFL, 0);
310
311 i = 0;
312 buf[0] = 0x05; /* SOCKS version 5 */
313
314 if (gaim_proxy_info_get_username(phb->gpi) != NULL) {
315 buf[1] = 0x02; /* two methods */
316 buf[2] = 0x00; /* no authentication */
317 buf[3] = 0x02; /* username/password authentication */
318 i = 4;
319 } else {
320 buf[1] = 0x01;
321 buf[2] = 0x00;
322 i = 3;
323 }
324
325 if (write(source, buf, i) < i) {
326 gaim_debug(GAIM_DEBUG_INFO, "write", "%s\n", strerror(errno));
327 gaim_debug(GAIM_DEBUG_ERROR, "socks5 proxy", "Unable to write\n");
328 close(source);
329
330 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
331
332 phb->func(phb->data, -1, _("Unable to connect"));
333 }
334
335 g_free(phb->host);
336 g_free(phb);
337 return;
338 }
339
340 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, _qq_s5_canread, phb);
341 }
342
343 gint qq_proxy_socks5(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen)
344 {
345 gint fd;
346 gaim_debug(GAIM_DEBUG_INFO, "QQ",
347 "Connecting to %s:%d via %s:%d using SOCKS5\n",
348 phb->host, phb->port, gaim_proxy_info_get_host(phb->gpi), gaim_proxy_info_get_port(phb->gpi));
349
350 if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0)
351 return -1;
352
353 gaim_debug(GAIM_DEBUG_INFO, "QQ", "proxy_sock5 return fd=%d\n", fd);
354
355 fcntl(fd, F_SETFL, O_NONBLOCK);
356 if (connect(fd, addr, addrlen) < 0) {
357 if ((errno == EINPROGRESS) || (errno == EINTR)) {
358 gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Connect in asynchronous mode.\n");
359 phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, _qq_s5_canwrite, phb);
360 } else {
361 close(fd);
362 return -1;
363 }
364 } else {
365 gaim_debug(GAIM_DEBUG_MISC, "QQ", "Connect in blocking mode.\n");
366 fcntl(fd, F_SETFL, 0);
367 _qq_s5_canwrite(phb, fd, GAIM_INPUT_WRITE);
368 }
369
370 return fd;
371 }