comparison libgaim/protocols/qq/udp_proxy_s5.c @ 14192:60b1bc8dbf37

[gaim-migrate @ 16863] Renamed 'core' to 'libgaim' committer: Tailor Script <tailor@pidgin.im>
author Evan Schoenberg <evan.s@dreskin.net>
date Sat, 19 Aug 2006 01:50:10 +0000
parents
children 902c3aa4950a
comparison
equal deleted inserted replaced
14191:009db0b357b5 14192:60b1bc8dbf37
1 /**
2 * The QQ2003C protocol plugin
3 *
4 * for gaim
5 *
6 * Copyright (C) 2004 Puzzlebird
7 * Henry Ou <henry@linux.net>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #include "debug.h"
25
26 #include "udp_proxy_s5.h"
27
28 extern gint /* defined in qq_proxy.c */
29 _qq_fill_host(struct sockaddr_in *addr, const gchar * host, guint16 port);
30
31 static void _qq_s5_canread_again(gpointer data, gint source, GaimInputCondition cond)
32 {
33 unsigned char buf[512];
34 struct PHB *phb = data;
35 struct sockaddr_in sin;
36 int len, error;
37
38 gaim_input_remove(phb->inpa);
39 gaim_debug(GAIM_DEBUG_INFO, "socks5 proxy", "Able to read again.\n");
40
41 len = read(source, buf, 10);
42 if (len < 10) {
43 gaim_debug(GAIM_DEBUG_WARNING, "socks5 proxy", "or not...\n");
44 close(source);
45
46 if (phb->account == NULL || gaim_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 gaim_debug(GAIM_DEBUG_ERROR, "socks5 proxy", "socks5 error: %x\n", buf[1]);
58 else
59 gaim_debug(GAIM_DEBUG_ERROR, "socks5 proxy", "Bad data.\n");
60 close(source);
61
62 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
63
64 phb->func(phb->data, -1, NULL);
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 gaim_debug(GAIM_DEBUG_INFO, "s5_canread_again", "connect failed: %s\n", 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 gaim_debug(GAIM_DEBUG_INFO, "QQ", "Connect didn't block\n");
87 len = sizeof(error);
88 if (getsockopt(phb->udpsock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
89 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "getsockopt failed.\n");
90 close(phb->udpsock);
91 return;
92 }
93 fcntl(phb->udpsock, F_SETFL, 0);
94
95 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
96 phb->func(phb->data, phb->udpsock, NULL);
97 }
98
99 g_free(phb->host);
100 g_free(phb);
101 }
102
103 static void _qq_s5_sendconnect(gpointer data, gint source)
104 {
105 unsigned char buf[512];
106 struct PHB *phb = data;
107 struct sockaddr_in sin, ctlsin;
108 int port, 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 = ntohs(ctlsin.sin_port) + 1;
139 while (1) {
140 _qq_fill_host(&sin, "0.0.0.0", port);
141 if (bind(phb->udpsock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
142 port++;
143 if (port > 65500) {
144 close(source);
145 g_free(phb->host);
146 g_free(phb);
147 return;
148 }
149 } else
150 break;
151 }
152
153 memset(buf + 4, 0, 0x04);
154 memcpy(buf + 8, &(sin.sin_port), 0x02);
155
156 if (write(source, buf, 10) < 10) {
157 close(source);
158 gaim_debug(GAIM_DEBUG_INFO, "s5_sendconnect", "packet too small\n");
159
160 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
161 phb->func(phb->data, -1, NULL);
162 }
163
164 g_free(phb->host);
165 g_free(phb);
166 return;
167 }
168
169 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, _qq_s5_canread_again, phb);
170 }
171
172 static void _qq_s5_readauth(gpointer data, gint source, GaimInputCondition cond)
173 {
174 unsigned char buf[512];
175 struct PHB *phb = data;
176
177 gaim_input_remove(phb->inpa);
178 gaim_debug(GAIM_DEBUG_INFO, "socks5 proxy", "Got auth response.\n");
179
180 if (read(source, buf, 2) < 2) {
181 close(source);
182
183 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
184
185 phb->func(phb->data, -1, NULL);
186 }
187
188 g_free(phb->host);
189 g_free(phb);
190 return;
191 }
192
193 if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
194 close(source);
195
196 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
197
198 phb->func(phb->data, -1, NULL);
199 }
200
201 g_free(phb->host);
202 g_free(phb);
203 return;
204 }
205
206 _qq_s5_sendconnect(phb, source);
207 }
208
209 static void _qq_s5_canread(gpointer data, gint source, GaimInputCondition cond)
210 {
211 unsigned char buf[512];
212 struct PHB *phb;
213 int ret;
214
215 phb = data;
216
217 gaim_input_remove(phb->inpa);
218 gaim_debug(GAIM_DEBUG_INFO, "socks5 proxy", "Able to read.\n");
219
220 ret = read(source, buf, 2);
221 if (ret < 2) {
222 gaim_debug(GAIM_DEBUG_INFO, "s5_canread", "packet smaller than 2 octet\n");
223 close(source);
224
225 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
226
227 phb->func(phb->data, source, NULL);
228 }
229
230 g_free(phb->host);
231 g_free(phb);
232 return;
233 }
234
235 if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
236 gaim_debug(GAIM_DEBUG_INFO, "s5_canread", "unsupport\n");
237 close(source);
238
239 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
240
241 phb->func(phb->data, -1, NULL);
242 }
243
244 g_free(phb->host);
245 g_free(phb);
246 return;
247 }
248
249 if (buf[1] == 0x02) {
250 unsigned int i, j;
251
252 i = strlen(gaim_proxy_info_get_username(phb->gpi));
253 j = strlen(gaim_proxy_info_get_password(phb->gpi));
254
255 buf[0] = 0x01; /* version 1 */
256 buf[1] = i;
257 memcpy(buf + 2, gaim_proxy_info_get_username(phb->gpi), i);
258 buf[2 + i] = j;
259 memcpy(buf + 2 + i + 1, gaim_proxy_info_get_password(phb->gpi), j);
260
261 if (write(source, buf, 3 + i + j) < 3 + i + j) {
262 close(source);
263
264 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
265
266 phb->func(phb->data, -1, NULL);
267 }
268
269 g_free(phb->host);
270 g_free(phb);
271 return;
272 }
273
274 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, _qq_s5_readauth, phb);
275 } else {
276 gaim_debug(GAIM_DEBUG_INFO, "s5_canread", "calling s5_sendconnect\n");
277 _qq_s5_sendconnect(phb, source);
278 }
279 }
280
281 static void _qq_s5_canwrite(gpointer data, gint source, GaimInputCondition cond)
282 {
283 unsigned char buf[512];
284 int i;
285 struct PHB *phb = data;
286 unsigned int len;
287 int error = ETIMEDOUT;
288
289 gaim_debug(GAIM_DEBUG_INFO, "socks5 proxy", "Connected.\n");
290
291 if (phb->inpa > 0)
292 gaim_input_remove(phb->inpa);
293
294 len = sizeof(error);
295 if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
296 gaim_debug(GAIM_DEBUG_INFO, "getsockopt", "%s\n", strerror(errno));
297 close(source);
298 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
299
300 phb->func(phb->data, -1, NULL);
301 }
302
303 g_free(phb->host);
304 g_free(phb);
305 return;
306 }
307 fcntl(source, F_SETFL, 0);
308
309 i = 0;
310 buf[0] = 0x05; /* SOCKS version 5 */
311
312 if (gaim_proxy_info_get_username(phb->gpi) != NULL) {
313 buf[1] = 0x02; /* two methods */
314 buf[2] = 0x00; /* no authentication */
315 buf[3] = 0x02; /* username/password authentication */
316 i = 4;
317 } else {
318 buf[1] = 0x01;
319 buf[2] = 0x00;
320 i = 3;
321 }
322
323 if (write(source, buf, i) < i) {
324 gaim_debug(GAIM_DEBUG_INFO, "write", "%s\n", strerror(errno));
325 gaim_debug(GAIM_DEBUG_ERROR, "socks5 proxy", "Unable to write\n");
326 close(source);
327
328 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
329
330 phb->func(phb->data, -1, NULL);
331 }
332
333 g_free(phb->host);
334 g_free(phb);
335 return;
336 }
337
338 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, _qq_s5_canread, phb);
339 }
340
341 gint qq_proxy_socks5(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen)
342 {
343 gint fd;
344 gaim_debug(GAIM_DEBUG_INFO, "QQ",
345 "Connecting to %s:%d via %s:%d using SOCKS5\n",
346 phb->host, phb->port, gaim_proxy_info_get_host(phb->gpi), gaim_proxy_info_get_port(phb->gpi));
347
348 if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0)
349 return -1;
350
351 gaim_debug(GAIM_DEBUG_INFO, "QQ", "proxy_sock5 return fd=%d\n", fd);
352
353 fcntl(fd, F_SETFL, O_NONBLOCK);
354 if (connect(fd, addr, addrlen) < 0) {
355 if ((errno == EINPROGRESS) || (errno == EINTR)) {
356 gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Connect in asynchronous mode.\n");
357 phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, _qq_s5_canwrite, phb);
358 } else {
359 close(fd);
360 return -1;
361 }
362 } else {
363 gaim_debug(GAIM_DEBUG_MISC, "QQ", "Connect in blocking mode.\n");
364 fcntl(fd, F_SETFL, 0);
365 _qq_s5_canwrite(phb, fd, GAIM_INPUT_WRITE);
366 }
367
368 return fd;
369 }