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