Mercurial > pidgin
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 } |