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