Mercurial > pidgin.yaz
annotate libgaim/proxy.c @ 14980:c157efddc62a
[gaim-migrate @ 17758]
Don't lose the account proxy settings just because they are hidden and not in use.
Also, make the MSN HTTP method use the same proxy setup code as the core - this should improve env. var proxy handling.
committer: Tailor Script <tailor@pidgin.im>
author | Daniel Atallah <daniel.atallah@gmail.com> |
---|---|
date | Thu, 16 Nov 2006 04:58:40 +0000 |
parents | 721465a37d4e |
children |
rev | line source |
---|---|
14192 | 1 /** |
2 * @file proxy.c Proxy API | |
3 * @ingroup core | |
4 * | |
5 * gaim | |
6 * | |
7 * Gaim is the legal property of its developers, whose names are too numerous | |
8 * to list here. Please refer to the COPYRIGHT file distributed with this | |
9 * source distribution. | |
10 * | |
11 * This program is free software; you can redistribute it and/or modify | |
12 * it under the terms of the GNU General Public License as published by | |
13 * the Free Software Foundation; either version 2 of the License, or | |
14 * (at your option) any later version. | |
15 * | |
16 * This program is distributed in the hope that it will be useful, | |
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 * GNU General Public License for more details. | |
20 * | |
21 * You should have received a copy of the GNU General Public License | |
22 * along with this program; if not, write to the Free Software | |
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
24 * | |
25 */ | |
26 | |
27 /* this is a little piece of code to handle proxy connection */ | |
28 /* it is intended to : 1st handle http proxy, using the CONNECT command | |
29 , 2nd provide an easy way to add socks support | |
30 , 3rd draw women to it like flies to honey */ | |
31 | |
32 #include "internal.h" | |
33 #include "cipher.h" | |
34 #include "debug.h" | |
35 #include "dnsquery.h" | |
36 #include "notify.h" | |
37 #include "ntlm.h" | |
38 #include "prefs.h" | |
39 #include "proxy.h" | |
40 #include "util.h" | |
41 | |
14262 | 42 struct _GaimProxyConnectData { |
14837 | 43 void *handle; |
14192 | 44 GaimProxyConnectFunction connect_cb; |
45 gpointer data; | |
14451 | 46 gchar *host; |
14192 | 47 int port; |
48 int fd; | |
49 guint inpa; | |
50 GaimProxyInfo *gpi; | |
51 GaimDnsQueryData *query_data; | |
52 | |
53 /** | |
54 * This contains alternating length/char* values. The char* | |
55 * values need to be freed when removed from the linked list. | |
56 */ | |
57 GSList *hosts; | |
58 | |
59 /* | |
60 * All of the following variables are used when establishing a | |
61 * connection through a proxy. | |
62 */ | |
63 guchar *write_buffer; | |
64 gsize write_buf_len; | |
65 gsize written_len; | |
66 GaimInputFunction read_cb; | |
67 guchar *read_buffer; | |
68 gsize read_buf_len; | |
69 gsize read_len; | |
70 }; | |
71 | |
72 static const char *socks5errors[] = { | |
73 "succeeded\n", | |
74 "general SOCKS server failure\n", | |
75 "connection not allowed by ruleset\n", | |
76 "Network unreachable\n", | |
77 "Host unreachable\n", | |
78 "Connection refused\n", | |
79 "TTL expired\n", | |
80 "Command not supported\n", | |
81 "Address type not supported\n" | |
82 }; | |
83 | |
84 static GaimProxyInfo *global_proxy_info = NULL; | |
85 | |
14837 | 86 static GSList *handles = NULL; |
14192 | 87 |
14262 | 88 static void try_connect(GaimProxyConnectData *connect_data); |
14192 | 89 |
90 /************************************************************************** | |
91 * Proxy structure API | |
92 **************************************************************************/ | |
93 GaimProxyInfo * | |
94 gaim_proxy_info_new(void) | |
95 { | |
96 return g_new0(GaimProxyInfo, 1); | |
97 } | |
98 | |
99 void | |
100 gaim_proxy_info_destroy(GaimProxyInfo *info) | |
101 { | |
102 g_return_if_fail(info != NULL); | |
103 | |
104 g_free(info->host); | |
105 g_free(info->username); | |
106 g_free(info->password); | |
107 | |
108 g_free(info); | |
109 } | |
110 | |
111 void | |
112 gaim_proxy_info_set_type(GaimProxyInfo *info, GaimProxyType type) | |
113 { | |
114 g_return_if_fail(info != NULL); | |
115 | |
116 info->type = type; | |
117 } | |
118 | |
119 void | |
120 gaim_proxy_info_set_host(GaimProxyInfo *info, const char *host) | |
121 { | |
122 g_return_if_fail(info != NULL); | |
123 | |
124 g_free(info->host); | |
125 info->host = g_strdup(host); | |
126 } | |
127 | |
128 void | |
129 gaim_proxy_info_set_port(GaimProxyInfo *info, int port) | |
130 { | |
131 g_return_if_fail(info != NULL); | |
132 | |
133 info->port = port; | |
134 } | |
135 | |
136 void | |
137 gaim_proxy_info_set_username(GaimProxyInfo *info, const char *username) | |
138 { | |
139 g_return_if_fail(info != NULL); | |
140 | |
141 g_free(info->username); | |
142 info->username = g_strdup(username); | |
143 } | |
144 | |
145 void | |
146 gaim_proxy_info_set_password(GaimProxyInfo *info, const char *password) | |
147 { | |
148 g_return_if_fail(info != NULL); | |
149 | |
150 g_free(info->password); | |
151 info->password = g_strdup(password); | |
152 } | |
153 | |
154 GaimProxyType | |
155 gaim_proxy_info_get_type(const GaimProxyInfo *info) | |
156 { | |
157 g_return_val_if_fail(info != NULL, GAIM_PROXY_NONE); | |
158 | |
159 return info->type; | |
160 } | |
161 | |
162 const char * | |
163 gaim_proxy_info_get_host(const GaimProxyInfo *info) | |
164 { | |
165 g_return_val_if_fail(info != NULL, NULL); | |
166 | |
167 return info->host; | |
168 } | |
169 | |
170 int | |
171 gaim_proxy_info_get_port(const GaimProxyInfo *info) | |
172 { | |
173 g_return_val_if_fail(info != NULL, 0); | |
174 | |
175 return info->port; | |
176 } | |
177 | |
178 const char * | |
179 gaim_proxy_info_get_username(const GaimProxyInfo *info) | |
180 { | |
181 g_return_val_if_fail(info != NULL, NULL); | |
182 | |
183 return info->username; | |
184 } | |
185 | |
186 const char * | |
187 gaim_proxy_info_get_password(const GaimProxyInfo *info) | |
188 { | |
189 g_return_val_if_fail(info != NULL, NULL); | |
190 | |
191 return info->password; | |
192 } | |
193 | |
194 /************************************************************************** | |
195 * Global Proxy API | |
196 **************************************************************************/ | |
197 GaimProxyInfo * | |
198 gaim_global_proxy_get_info(void) | |
199 { | |
200 return global_proxy_info; | |
201 } | |
202 | |
203 static GaimProxyInfo * | |
204 gaim_gnome_proxy_get_info(void) | |
205 { | |
206 static GaimProxyInfo info = {0, NULL, 0, NULL, NULL}; | |
207 gchar *path; | |
208 if ((path = g_find_program_in_path("gconftool-2"))) { | |
209 gchar *tmp; | |
210 | |
211 g_free(path); | |
212 | |
213 /* See whether to use a proxy. */ | |
214 if (!g_spawn_command_line_sync("gconftool-2 -g /system/http_proxy/use_http_proxy", &tmp, | |
215 NULL, NULL, NULL)) | |
216 return gaim_global_proxy_get_info(); | |
217 if (!strcmp(tmp, "false\n")) { | |
218 info.type = GAIM_PROXY_NONE; | |
219 g_free(tmp); | |
220 return &info; | |
221 } else if (strcmp(tmp, "true\n")) { | |
222 g_free(tmp); | |
223 return gaim_global_proxy_get_info(); | |
224 } | |
225 | |
226 g_free(tmp); | |
227 info.type = GAIM_PROXY_HTTP; | |
228 | |
229 /* Free the old fields */ | |
230 if (info.host) { | |
231 g_free(info.host); | |
232 info.host = NULL; | |
233 } | |
234 if (info.username) { | |
235 g_free(info.username); | |
236 info.username = NULL; | |
237 } | |
238 if (info.password) { | |
239 g_free(info.password); | |
240 info.password = NULL; | |
241 } | |
242 | |
243 /* Get the new ones */ | |
244 if (!g_spawn_command_line_sync("gconftool-2 -g /system/http_proxy/host", &info.host, | |
245 NULL, NULL, NULL)) | |
246 return gaim_global_proxy_get_info(); | |
247 g_strchomp(info.host); | |
248 | |
249 if (!g_spawn_command_line_sync("gconftool-2 -g /system/http_proxy/authentication_user", &info.username, | |
250 NULL, NULL, NULL)) | |
251 return gaim_global_proxy_get_info(); | |
252 g_strchomp(info.username); | |
253 | |
254 if (!g_spawn_command_line_sync("gconftool-2 -g /system/http_proxy/authentication_password", &info.password, | |
255 NULL, NULL, NULL)) | |
256 return gaim_global_proxy_get_info(); | |
257 g_strchomp(info.password); | |
258 | |
259 if (!g_spawn_command_line_sync("gconftool-2 -g /system/http_proxy/port", &tmp, | |
260 NULL, NULL, NULL)) | |
261 return gaim_global_proxy_get_info(); | |
262 info.port = atoi(tmp); | |
263 g_free(tmp); | |
264 | |
265 return &info; | |
266 } | |
267 return gaim_global_proxy_get_info(); | |
268 } | |
269 /************************************************************************** | |
270 * Proxy API | |
271 **************************************************************************/ | |
272 | |
14451 | 273 /** |
274 * Whoever calls this needs to have called | |
275 * gaim_proxy_connect_data_disconnect() beforehand. | |
14192 | 276 */ |
277 static void | |
14262 | 278 gaim_proxy_connect_data_destroy(GaimProxyConnectData *connect_data) |
14192 | 279 { |
14837 | 280 handles = g_slist_remove(handles, connect_data); |
14192 | 281 |
14262 | 282 if (connect_data->query_data != NULL) |
283 gaim_dnsquery_destroy(connect_data->query_data); | |
14238 | 284 |
14262 | 285 while (connect_data->hosts != NULL) |
14192 | 286 { |
287 /* Discard the length... */ | |
14262 | 288 connect_data->hosts = g_slist_remove(connect_data->hosts, connect_data->hosts->data); |
14192 | 289 /* Free the address... */ |
14262 | 290 g_free(connect_data->hosts->data); |
291 connect_data->hosts = g_slist_remove(connect_data->hosts, connect_data->hosts->data); | |
14192 | 292 } |
293 | |
14262 | 294 g_free(connect_data->host); |
295 g_free(connect_data); | |
14192 | 296 } |
297 | |
14451 | 298 /** |
299 * Free all information dealing with a connection attempt and | |
300 * reset the connect_data to prepare for it to try to connect | |
301 * to another IP address. | |
302 * | |
303 * If an error message is passed in, then we know the connection | |
304 * attempt failed. If the connection attempt failed and | |
305 * connect_data->hosts is not empty then we try the next IP address. | |
306 * If the connection attempt failed and we have no more hosts | |
307 * try try then we call the callback with the given error message, | |
308 * then destroy the connect_data. | |
309 * | |
310 * @param error_message An error message explaining why the connection | |
311 * failed. This will be passed to the callback function | |
312 * specified in the call to gaim_proxy_connect(). If the | |
313 * connection was successful then pass in null. | |
314 */ | |
315 static void | |
316 gaim_proxy_connect_data_disconnect(GaimProxyConnectData *connect_data, const gchar *error_message) | |
317 { | |
318 if (connect_data->inpa > 0) | |
319 { | |
320 gaim_input_remove(connect_data->inpa); | |
321 connect_data->inpa = 0; | |
322 } | |
323 | |
324 if (connect_data->fd >= 0) | |
325 { | |
326 close(connect_data->fd); | |
327 connect_data->fd = -1; | |
328 } | |
329 | |
330 g_free(connect_data->write_buffer); | |
331 connect_data->write_buffer = NULL; | |
332 | |
333 g_free(connect_data->read_buffer); | |
334 connect_data->read_buffer = NULL; | |
335 | |
336 if (error_message != NULL) | |
337 { | |
338 gaim_debug_info("proxy", "Connection attempt failed: %s\n", | |
339 error_message); | |
340 if (connect_data->hosts != NULL) | |
341 try_connect(connect_data); | |
342 else | |
343 { | |
344 /* Everything failed! Tell the originator of the request. */ | |
345 connect_data->connect_cb(connect_data->data, -1, error_message); | |
346 gaim_proxy_connect_data_destroy(connect_data); | |
347 } | |
348 } | |
349 } | |
350 | |
351 /** | |
352 * This calls gaim_proxy_connect_data_disconnect(), but it lets you | |
353 * specify the error_message using a printf()-like syntax. | |
354 */ | |
355 static void | |
356 gaim_proxy_connect_data_disconnect_formatted(GaimProxyConnectData *connect_data, const char *format, ...) | |
357 { | |
358 va_list args; | |
359 gchar *tmp; | |
360 | |
361 va_start(args, format); | |
362 tmp = g_strdup_vprintf(format, args); | |
363 va_end(args); | |
364 | |
365 gaim_proxy_connect_data_disconnect(connect_data, tmp); | |
366 g_free(tmp); | |
367 } | |
368 | |
14192 | 369 static void |
14262 | 370 gaim_proxy_connect_data_connected(GaimProxyConnectData *connect_data) |
14192 | 371 { |
14262 | 372 connect_data->connect_cb(connect_data->data, connect_data->fd, NULL); |
14192 | 373 |
374 /* | |
375 * We've passed the file descriptor to the protocol, so it's no longer | |
376 * our responsibility, and we should be careful not to free it when | |
14262 | 377 * we destroy the connect_data. |
14192 | 378 */ |
14262 | 379 connect_data->fd = -1; |
14192 | 380 |
14451 | 381 gaim_proxy_connect_data_disconnect(connect_data, NULL); |
14262 | 382 gaim_proxy_connect_data_destroy(connect_data); |
14192 | 383 } |
384 | |
385 static void | |
14451 | 386 socket_ready_cb(gpointer data, gint source, GaimInputCondition cond) |
14192 | 387 { |
14262 | 388 GaimProxyConnectData *connect_data = data; |
14192 | 389 socklen_t len; |
14451 | 390 int error = 0; |
391 int ret; | |
14192 | 392 |
393 gaim_debug_info("proxy", "Connected.\n"); | |
394 | |
395 /* | |
396 * getsockopt after a non-blocking connect returns -1 if something is | |
397 * really messed up (bad descriptor, usually). Otherwise, it returns 0 and | |
398 * error holds what connect would have returned if it blocked until now. | |
399 * Thus, error == 0 is success, error == EINPROGRESS means "try again", | |
400 * and anything else is a real error. | |
401 * | |
402 * (error == EINPROGRESS can happen after a select because the kernel can | |
403 * be overly optimistic sometimes. select is just a hint that you might be | |
404 * able to do something.) | |
405 */ | |
14451 | 406 len = sizeof(error); |
14262 | 407 ret = getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len); |
14451 | 408 |
14192 | 409 if (ret == 0 && error == EINPROGRESS) |
14451 | 410 /* No worries - we'll be called again later */ |
411 /* TODO: Does this ever happen? */ | |
412 return; | |
413 | |
414 if (ret != 0 || error != 0) { | |
415 if (ret != 0) | |
14192 | 416 error = errno; |
14451 | 417 gaim_proxy_connect_data_disconnect(connect_data, strerror(error)); |
14192 | 418 return; |
419 } | |
420 | |
14262 | 421 gaim_proxy_connect_data_connected(connect_data); |
14192 | 422 } |
423 | |
424 static gboolean | |
425 clean_connect(gpointer data) | |
426 { | |
14451 | 427 gaim_proxy_connect_data_connected(data); |
14192 | 428 |
429 return FALSE; | |
430 } | |
431 | |
14451 | 432 static void |
14262 | 433 proxy_connect_none(GaimProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen) |
14192 | 434 { |
435 gaim_debug_info("proxy", "Connecting to %s:%d with no proxy\n", | |
14262 | 436 connect_data->host, connect_data->port); |
14192 | 437 |
14262 | 438 connect_data->fd = socket(addr->sa_family, SOCK_STREAM, 0); |
439 if (connect_data->fd < 0) | |
14192 | 440 { |
14451 | 441 gaim_proxy_connect_data_disconnect_formatted(connect_data, |
442 _("Unable to create socket:\n%s"), strerror(errno)); | |
443 return; | |
14192 | 444 } |
14451 | 445 |
14262 | 446 fcntl(connect_data->fd, F_SETFL, O_NONBLOCK); |
14192 | 447 #ifndef _WIN32 |
14262 | 448 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC); |
14192 | 449 #endif |
450 | |
14451 | 451 if (connect(connect_data->fd, addr, addrlen) != 0) |
14192 | 452 { |
14451 | 453 if ((errno == EINPROGRESS) || (errno == EINTR)) |
454 { | |
14192 | 455 gaim_debug_info("proxy", "Connection in progress\n"); |
14451 | 456 connect_data->inpa = gaim_input_add(connect_data->fd, |
457 GAIM_INPUT_WRITE, socket_ready_cb, connect_data); | |
14192 | 458 } |
14451 | 459 else |
460 { | |
461 gaim_proxy_connect_data_disconnect(connect_data, strerror(errno)); | |
14192 | 462 } |
463 } | |
464 else | |
465 { | |
466 /* | |
467 * The connection happened IMMEDIATELY... strange, but whatever. | |
468 */ | |
469 socklen_t len; | |
470 int error = ETIMEDOUT; | |
14451 | 471 int ret; |
472 | |
14192 | 473 gaim_debug_info("proxy", "Connected immediately.\n"); |
14451 | 474 |
14192 | 475 len = sizeof(error); |
14451 | 476 ret = getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len); |
477 if ((ret != 0) || (error != 0)) | |
14192 | 478 { |
14451 | 479 if (ret != 0) |
480 error = errno; | |
481 gaim_proxy_connect_data_disconnect(connect_data, strerror(error)); | |
482 return; | |
14192 | 483 } |
484 | |
485 /* | |
486 * We want to call the "connected" callback eventually, but we | |
487 * don't want to call it before we return, just in case. | |
488 */ | |
14262 | 489 gaim_timeout_add(10, clean_connect, connect_data); |
14192 | 490 } |
491 } | |
492 | |
14451 | 493 /** |
494 * This is a utility function used by the HTTP, SOCKS4 and SOCKS5 | |
495 * connect functions. It writes data from a buffer to a socket. | |
496 * When all the data is written it sets up a watcher to read a | |
497 * response and call a specified function. | |
498 */ | |
14192 | 499 static void |
500 proxy_do_write(gpointer data, gint source, GaimInputCondition cond) | |
501 { | |
14451 | 502 GaimProxyConnectData *connect_data; |
503 const guchar *request; | |
504 gsize request_len; | |
505 int ret; | |
14192 | 506 |
14451 | 507 connect_data = data; |
508 request = connect_data->write_buffer + connect_data->written_len; | |
509 request_len = connect_data->write_buf_len - connect_data->written_len; | |
14192 | 510 |
14451 | 511 ret = write(connect_data->fd, request, request_len); |
512 if (ret <= 0) | |
513 { | |
514 if (errno == EAGAIN) | |
515 /* No worries */ | |
516 return; | |
517 | |
518 /* Error! */ | |
519 gaim_proxy_connect_data_disconnect(connect_data, strerror(errno)); | |
14192 | 520 return; |
14451 | 521 } |
522 if (ret < request_len) { | |
14262 | 523 connect_data->written_len += ret; |
14192 | 524 return; |
525 } | |
526 | |
14451 | 527 /* We're done writing data! Wait for a response. */ |
14262 | 528 g_free(connect_data->write_buffer); |
529 connect_data->write_buffer = NULL; | |
14451 | 530 gaim_input_remove(connect_data->inpa); |
531 connect_data->inpa = gaim_input_add(connect_data->fd, | |
532 GAIM_INPUT_READ, connect_data->read_cb, connect_data); | |
14192 | 533 } |
534 | |
535 #define HTTP_GOODSTRING "HTTP/1.0 200" | |
536 #define HTTP_GOODSTRING2 "HTTP/1.1 200" | |
537 | |
14451 | 538 /** |
539 * We're using an HTTP proxy for a non-port 80 tunnel. Read the | |
540 * response to the CONNECT request. | |
541 */ | |
14192 | 542 static void |
543 http_canread(gpointer data, gint source, GaimInputCondition cond) | |
544 { | |
545 int len, headers_len, status = 0; | |
546 gboolean error; | |
14262 | 547 GaimProxyConnectData *connect_data = data; |
14192 | 548 guchar *p; |
549 gsize max_read; | |
550 | |
14451 | 551 if (connect_data->read_buffer == NULL) |
552 { | |
14262 | 553 connect_data->read_buf_len = 8192; |
554 connect_data->read_buffer = g_malloc(connect_data->read_buf_len); | |
555 connect_data->read_len = 0; | |
14192 | 556 } |
557 | |
14262 | 558 p = connect_data->read_buffer + connect_data->read_len; |
559 max_read = connect_data->read_buf_len - connect_data->read_len - 1; | |
14192 | 560 |
14262 | 561 len = read(connect_data->fd, p, max_read); |
14451 | 562 |
563 if (len == 0) | |
564 { | |
565 gaim_proxy_connect_data_disconnect(connect_data, | |
566 _("Server closed the connection.")); | |
14192 | 567 return; |
14451 | 568 } |
569 | |
570 if (len < 0) | |
571 { | |
572 if (errno == EAGAIN) | |
573 /* No worries */ | |
574 return; | |
575 | |
576 /* Error! */ | |
577 gaim_proxy_connect_data_disconnect_formatted(connect_data, | |
578 _("Lost connection with server:\n%s"), strerror(errno)); | |
14192 | 579 return; |
580 } | |
14451 | 581 |
582 connect_data->read_len += len; | |
14192 | 583 p[len] = '\0'; |
584 | |
14451 | 585 p = (guchar *)g_strstr_len((const gchar *)connect_data->read_buffer, |
586 connect_data->read_len, "\r\n\r\n"); | |
587 if (p != NULL) { | |
14192 | 588 *p = '\0'; |
14262 | 589 headers_len = (p - connect_data->read_buffer) + 4; |
14192 | 590 } else if(len == max_read) |
591 headers_len = len; | |
592 else | |
593 return; | |
594 | |
14262 | 595 error = strncmp((const char *)connect_data->read_buffer, "HTTP/", 5) != 0; |
14192 | 596 if (!error) |
597 { | |
598 int major; | |
14262 | 599 p = connect_data->read_buffer + 5; |
14192 | 600 major = strtol((const char *)p, (char **)&p, 10); |
601 error = (major == 0) || (*p != '.'); | |
602 if(!error) { | |
603 int minor; | |
604 p++; | |
605 minor = strtol((const char *)p, (char **)&p, 10); | |
606 error = (*p != ' '); | |
607 if(!error) { | |
608 p++; | |
609 status = strtol((const char *)p, (char **)&p, 10); | |
610 error = (*p != ' '); | |
611 } | |
612 } | |
613 } | |
614 | |
615 /* Read the contents */ | |
14262 | 616 p = (guchar *)g_strrstr((const gchar *)connect_data->read_buffer, "Content-Length: "); |
14192 | 617 if (p != NULL) |
618 { | |
619 gchar *tmp; | |
620 int len = 0; | |
621 char tmpc; | |
622 p += strlen("Content-Length: "); | |
623 tmp = strchr((const char *)p, '\r'); | |
624 if(tmp) | |
625 *tmp = '\0'; | |
626 len = atoi((const char *)p); | |
627 if(tmp) | |
628 *tmp = '\r'; | |
629 | |
630 /* Compensate for what has already been read */ | |
14262 | 631 len -= connect_data->read_len - headers_len; |
14192 | 632 /* I'm assuming that we're doing this to prevent the server from |
633 complaining / breaking since we don't read the whole page */ | |
14451 | 634 while (len--) { |
14192 | 635 /* TODO: deal with EAGAIN (and other errors) better */ |
14262 | 636 if (read(connect_data->fd, &tmpc, 1) < 0 && errno != EAGAIN) |
14192 | 637 break; |
638 } | |
639 } | |
640 | |
641 if (error) | |
642 { | |
14451 | 643 gaim_proxy_connect_data_disconnect_formatted(connect_data, |
14356 | 644 _("Unable to parse response from HTTP proxy: %s\n"), |
14262 | 645 connect_data->read_buffer); |
14192 | 646 return; |
647 } | |
648 else if (status != 200) | |
649 { | |
650 gaim_debug_error("proxy", | |
651 "Proxy server replied with:\n%s\n", | |
14262 | 652 connect_data->read_buffer); |
14192 | 653 |
14451 | 654 if (status == 407 /* Proxy Auth */) |
655 { | |
14192 | 656 gchar *ntlm; |
14451 | 657 ntlm = g_strrstr((const gchar *)connect_data->read_buffer, |
658 "Proxy-Authenticate: NTLM "); | |
659 if (ntlm != NULL) | |
660 { | |
661 /* Check for Type-2 */ | |
14192 | 662 gchar *tmp = ntlm; |
663 guint8 *nonce; | |
14262 | 664 gchar *domain = (gchar*)gaim_proxy_info_get_username(connect_data->gpi); |
14192 | 665 gchar *username; |
666 gchar *request; | |
667 gchar *response; | |
668 username = strchr(domain, '\\'); | |
669 if (username == NULL) | |
670 { | |
14451 | 671 gaim_proxy_connect_data_disconnect_formatted(connect_data, |
14356 | 672 _("HTTP proxy connection error %d"), status); |
14192 | 673 return; |
674 } | |
675 *username = '\0'; | |
676 username++; | |
677 ntlm += strlen("Proxy-Authenticate: NTLM "); | |
678 while(*tmp != '\r' && *tmp != '\0') tmp++; | |
679 *tmp = '\0'; | |
680 nonce = gaim_ntlm_parse_type2(ntlm, NULL); | |
681 response = gaim_ntlm_gen_type3(username, | |
14262 | 682 (gchar*) gaim_proxy_info_get_password(connect_data->gpi), |
683 (gchar*) gaim_proxy_info_get_host(connect_data->gpi), | |
14192 | 684 domain, nonce, NULL); |
685 username--; | |
686 *username = '\\'; | |
687 request = g_strdup_printf( | |
688 "CONNECT %s:%d HTTP/1.1\r\n" | |
689 "Host: %s:%d\r\n" | |
690 "Proxy-Authorization: NTLM %s\r\n" | |
691 "Proxy-Connection: Keep-Alive\r\n\r\n", | |
14262 | 692 connect_data->host, connect_data->port, connect_data->host, |
693 connect_data->port, response); | |
14192 | 694 g_free(response); |
695 | |
14262 | 696 g_free(connect_data->read_buffer); |
697 connect_data->read_buffer = NULL; | |
14192 | 698 |
14262 | 699 connect_data->write_buffer = (guchar *)request; |
700 connect_data->write_buf_len = strlen(request); | |
701 connect_data->written_len = 0; | |
702 connect_data->read_cb = http_canread; | |
14192 | 703 |
14451 | 704 gaim_input_remove(connect_data->inpa); |
14262 | 705 connect_data->inpa = gaim_input_add(connect_data->fd, |
706 GAIM_INPUT_WRITE, proxy_do_write, connect_data); | |
707 proxy_do_write(connect_data, connect_data->fd, cond); | |
14192 | 708 return; |
14262 | 709 } else if((ntlm = g_strrstr((const char *)connect_data->read_buffer, "Proxy-Authenticate: NTLM"))) { /* Empty message */ |
14192 | 710 gchar request[2048]; |
14262 | 711 gchar *domain = (gchar*) gaim_proxy_info_get_username(connect_data->gpi); |
14192 | 712 gchar *username; |
713 int request_len; | |
714 username = strchr(domain, '\\'); | |
715 if (username == NULL) | |
716 { | |
14451 | 717 gaim_proxy_connect_data_disconnect_formatted(connect_data, |
14356 | 718 _("HTTP proxy connection error %d"), status); |
14192 | 719 return; |
720 } | |
721 *username = '\0'; | |
722 | |
723 request_len = g_snprintf(request, sizeof(request), | |
724 "CONNECT %s:%d HTTP/1.1\r\n" | |
725 "Host: %s:%d\r\n", | |
14262 | 726 connect_data->host, connect_data->port, |
727 connect_data->host, connect_data->port); | |
14192 | 728 |
729 g_return_if_fail(request_len < sizeof(request)); | |
730 request_len += g_snprintf(request + request_len, | |
731 sizeof(request) - request_len, | |
732 "Proxy-Authorization: NTLM %s\r\n" | |
733 "Proxy-Connection: Keep-Alive\r\n\r\n", | |
734 gaim_ntlm_gen_type1( | |
14262 | 735 (gchar*) gaim_proxy_info_get_host(connect_data->gpi), |
14192 | 736 domain)); |
737 *username = '\\'; | |
738 | |
14262 | 739 gaim_input_remove(connect_data->inpa); |
740 g_free(connect_data->read_buffer); | |
741 connect_data->read_buffer = NULL; | |
14192 | 742 |
14262 | 743 connect_data->write_buffer = g_memdup(request, request_len); |
744 connect_data->write_buf_len = request_len; | |
745 connect_data->written_len = 0; | |
14192 | 746 |
14262 | 747 connect_data->read_cb = http_canread; |
14192 | 748 |
14262 | 749 connect_data->inpa = gaim_input_add(connect_data->fd, |
750 GAIM_INPUT_WRITE, proxy_do_write, connect_data); | |
14192 | 751 |
14262 | 752 proxy_do_write(connect_data, connect_data->fd, cond); |
14192 | 753 return; |
754 } else { | |
14451 | 755 gaim_proxy_connect_data_disconnect_formatted(connect_data, |
14356 | 756 _("HTTP proxy connection error %d"), status); |
14192 | 757 return; |
758 } | |
759 } | |
14451 | 760 if (status == 403) |
761 { | |
762 /* Forbidden */ | |
763 gaim_proxy_connect_data_disconnect_formatted(connect_data, | |
764 _("Access denied: HTTP proxy server forbids port %d tunneling."), | |
14356 | 765 connect_data->port); |
14192 | 766 } else { |
14451 | 767 gaim_proxy_connect_data_disconnect_formatted(connect_data, |
14356 | 768 _("HTTP proxy connection error %d"), status); |
14192 | 769 } |
770 } else { | |
14262 | 771 gaim_input_remove(connect_data->inpa); |
772 connect_data->inpa = 0; | |
773 g_free(connect_data->read_buffer); | |
774 connect_data->read_buffer = NULL; | |
14192 | 775 gaim_debug_info("proxy", "HTTP proxy connection established\n"); |
14262 | 776 gaim_proxy_connect_data_connected(connect_data); |
14192 | 777 return; |
778 } | |
779 } | |
780 | |
781 static void | |
782 http_canwrite(gpointer data, gint source, GaimInputCondition cond) | |
783 { | |
14451 | 784 GString *request; |
785 GaimProxyConnectData *connect_data; | |
14192 | 786 socklen_t len; |
787 int error = ETIMEDOUT; | |
14451 | 788 int ret; |
14192 | 789 |
14451 | 790 gaim_debug_info("proxy", "Connected.\n"); |
791 | |
792 connect_data = data; | |
14192 | 793 |
14262 | 794 if (connect_data->inpa > 0) |
14192 | 795 { |
14262 | 796 gaim_input_remove(connect_data->inpa); |
797 connect_data->inpa = 0; | |
14192 | 798 } |
799 | |
800 len = sizeof(error); | |
14451 | 801 ret = getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len); |
802 if ((ret != 0) || (error != 0)) | |
803 { | |
804 if (ret != 0) | |
805 error = errno; | |
806 gaim_proxy_connect_data_disconnect(connect_data, strerror(error)); | |
14192 | 807 return; |
808 } | |
809 | |
14451 | 810 gaim_debug_info("proxy", "Using CONNECT tunneling for %s:%d\n", |
14262 | 811 connect_data->host, connect_data->port); |
14192 | 812 |
14451 | 813 request = g_string_sized_new(4096); |
814 g_string_append_printf(request, | |
815 "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n", | |
816 connect_data->host, connect_data->port, | |
817 connect_data->host, connect_data->port); | |
818 | |
819 if (gaim_proxy_info_get_username(connect_data->gpi) != NULL) | |
820 { | |
14192 | 821 char *t1, *t2; |
14451 | 822 |
14192 | 823 t1 = g_strdup_printf("%s:%s", |
14262 | 824 gaim_proxy_info_get_username(connect_data->gpi), |
825 gaim_proxy_info_get_password(connect_data->gpi) ? | |
826 gaim_proxy_info_get_password(connect_data->gpi) : ""); | |
14192 | 827 t2 = gaim_base64_encode((const guchar *)t1, strlen(t1)); |
828 g_free(t1); | |
829 | |
14451 | 830 g_string_append_printf(request, |
14192 | 831 "Proxy-Authorization: Basic %s\r\n" |
832 "Proxy-Authorization: NTLM %s\r\n" | |
14451 | 833 "Proxy-Connection: Keep-Alive\r\n", |
834 t2, gaim_ntlm_gen_type1( | |
835 gaim_proxy_info_get_host(connect_data->gpi), "")); | |
14192 | 836 g_free(t2); |
837 } | |
838 | |
14451 | 839 g_string_append(request, "\r\n"); |
840 | |
841 connect_data->write_buf_len = request->len; | |
842 connect_data->write_buffer = (guchar *)g_string_free(request, FALSE); | |
14262 | 843 connect_data->written_len = 0; |
844 connect_data->read_cb = http_canread; | |
14192 | 845 |
14451 | 846 connect_data->inpa = gaim_input_add(connect_data->fd, |
847 GAIM_INPUT_WRITE, proxy_do_write, connect_data); | |
14262 | 848 proxy_do_write(connect_data, connect_data->fd, cond); |
14192 | 849 } |
850 | |
14451 | 851 static void |
14262 | 852 proxy_connect_http(GaimProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen) |
14192 | 853 { |
14451 | 854 gaim_debug_info("proxy", |
14192 | 855 "Connecting to %s:%d via %s:%d using HTTP\n", |
14451 | 856 connect_data->host, connect_data->port, |
14262 | 857 (gaim_proxy_info_get_host(connect_data->gpi) ? gaim_proxy_info_get_host(connect_data->gpi) : "(null)"), |
858 gaim_proxy_info_get_port(connect_data->gpi)); | |
14192 | 859 |
14262 | 860 connect_data->fd = socket(addr->sa_family, SOCK_STREAM, 0); |
861 if (connect_data->fd < 0) | |
14451 | 862 { |
863 gaim_proxy_connect_data_disconnect_formatted(connect_data, | |
864 _("Unable to create socket:\n%s"), strerror(errno)); | |
865 return; | |
866 } | |
14192 | 867 |
14262 | 868 fcntl(connect_data->fd, F_SETFL, O_NONBLOCK); |
14192 | 869 #ifndef _WIN32 |
14262 | 870 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC); |
14192 | 871 #endif |
872 | |
14262 | 873 if (connect(connect_data->fd, addr, addrlen) != 0) |
14192 | 874 { |
875 if ((errno == EINPROGRESS) || (errno == EINTR)) { | |
14451 | 876 gaim_debug_info("proxy", "Connection in progress\n"); |
14192 | 877 |
14451 | 878 if (connect_data->port != 80) |
879 { | |
14192 | 880 /* we need to do CONNECT first */ |
14451 | 881 connect_data->inpa = gaim_input_add(connect_data->fd, |
882 GAIM_INPUT_WRITE, http_canwrite, connect_data); | |
883 } | |
884 else | |
885 { | |
886 /* | |
14474 | 887 * If we're trying to connect to something running on |
888 * port 80 then we assume the traffic using this | |
889 * connection is going to be HTTP traffic. If it's | |
890 * not then this will fail (uglily). But it's good | |
891 * to avoid using the CONNECT method because it's | |
892 * not always allowed. | |
14451 | 893 */ |
14192 | 894 gaim_debug_info("proxy", "HTTP proxy connection established\n"); |
14262 | 895 gaim_proxy_connect_data_connected(connect_data); |
14192 | 896 } |
14451 | 897 } |
898 else | |
899 { | |
900 gaim_proxy_connect_data_disconnect(connect_data, strerror(errno)); | |
14192 | 901 } |
902 } | |
14451 | 903 else |
904 { | |
905 gaim_debug_info("proxy", "Connected immediately.\n"); | |
14192 | 906 |
14262 | 907 http_canwrite(connect_data, connect_data->fd, GAIM_INPUT_WRITE); |
14192 | 908 } |
909 } | |
910 | |
911 static void | |
912 s4_canread(gpointer data, gint source, GaimInputCondition cond) | |
913 { | |
14262 | 914 GaimProxyConnectData *connect_data = data; |
14192 | 915 guchar *buf; |
916 int len, max_read; | |
917 | |
918 /* This is really not going to block under normal circumstances, but to | |
919 * be correct, we deal with the unlikely scenario */ | |
920 | |
14262 | 921 if (connect_data->read_buffer == NULL) { |
922 connect_data->read_buf_len = 12; | |
923 connect_data->read_buffer = g_malloc(connect_data->read_buf_len); | |
924 connect_data->read_len = 0; | |
14192 | 925 } |
926 | |
14262 | 927 buf = connect_data->read_buffer + connect_data->read_len; |
928 max_read = connect_data->read_buf_len - connect_data->read_len; | |
14192 | 929 |
14262 | 930 len = read(connect_data->fd, buf, max_read); |
14192 | 931 |
14262 | 932 if ((len < 0 && errno == EAGAIN) || (len > 0 && len + connect_data->read_len < 4)) |
14192 | 933 return; |
14262 | 934 else if (len + connect_data->read_len >= 4) { |
935 if (connect_data->read_buffer[1] == 90) { | |
936 gaim_proxy_connect_data_connected(connect_data); | |
14192 | 937 return; |
938 } | |
939 } | |
940 | |
14451 | 941 gaim_proxy_connect_data_disconnect(connect_data, strerror(errno)); |
14192 | 942 } |
943 | |
944 static void | |
945 s4_canwrite(gpointer data, gint source, GaimInputCondition cond) | |
946 { | |
947 unsigned char packet[9]; | |
948 struct hostent *hp; | |
14262 | 949 GaimProxyConnectData *connect_data = data; |
14192 | 950 socklen_t len; |
951 int error = ETIMEDOUT; | |
14451 | 952 int ret; |
14192 | 953 |
954 gaim_debug_info("socks4 proxy", "Connected.\n"); | |
955 | |
14262 | 956 if (connect_data->inpa > 0) |
14192 | 957 { |
14262 | 958 gaim_input_remove(connect_data->inpa); |
959 connect_data->inpa = 0; | |
14192 | 960 } |
961 | |
962 len = sizeof(error); | |
14451 | 963 ret = getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len); |
964 if ((ret != 0) || (error != 0)) | |
965 { | |
966 if (ret != 0) | |
967 error = errno; | |
968 gaim_proxy_connect_data_disconnect(connect_data, strerror(error)); | |
14192 | 969 return; |
970 } | |
971 | |
972 /* | |
973 * The socks4 spec doesn't include support for doing host name | |
974 * lookups by the proxy. Some socks4 servers do this via | |
975 * extensions to the protocol. Since we don't know if a | |
976 * server supports this, it would need to be implemented | |
977 * with an option, or some detection mechanism - in the | |
978 * meantime, stick with plain old SOCKS4. | |
979 */ | |
14451 | 980 /* TODO: Use gaim_dnsquery_a() */ |
14262 | 981 hp = gethostbyname(connect_data->host); |
14192 | 982 if (hp == NULL) { |
14451 | 983 gaim_proxy_connect_data_disconnect_formatted(connect_data, |
984 _("Error resolving %s"), connect_data->host); | |
14192 | 985 return; |
986 } | |
987 | |
988 packet[0] = 4; | |
989 packet[1] = 1; | |
14262 | 990 packet[2] = connect_data->port >> 8; |
991 packet[3] = connect_data->port & 0xff; | |
14192 | 992 packet[4] = (unsigned char)(hp->h_addr_list[0])[0]; |
993 packet[5] = (unsigned char)(hp->h_addr_list[0])[1]; | |
994 packet[6] = (unsigned char)(hp->h_addr_list[0])[2]; | |
995 packet[7] = (unsigned char)(hp->h_addr_list[0])[3]; | |
996 packet[8] = 0; | |
997 | |
14262 | 998 connect_data->write_buffer = g_memdup(packet, sizeof(packet)); |
999 connect_data->write_buf_len = sizeof(packet); | |
1000 connect_data->written_len = 0; | |
1001 connect_data->read_cb = s4_canread; | |
14192 | 1002 |
14262 | 1003 connect_data->inpa = gaim_input_add(connect_data->fd, GAIM_INPUT_WRITE, proxy_do_write, connect_data); |
14192 | 1004 |
14262 | 1005 proxy_do_write(connect_data, connect_data->fd, cond); |
14192 | 1006 } |
1007 | |
14451 | 1008 static void |
14262 | 1009 proxy_connect_socks4(GaimProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen) |
14192 | 1010 { |
14451 | 1011 gaim_debug_info("proxy", |
14192 | 1012 "Connecting to %s:%d via %s:%d using SOCKS4\n", |
14262 | 1013 connect_data->host, connect_data->port, |
1014 gaim_proxy_info_get_host(connect_data->gpi), | |
1015 gaim_proxy_info_get_port(connect_data->gpi)); | |
14192 | 1016 |
14262 | 1017 connect_data->fd = socket(addr->sa_family, SOCK_STREAM, 0); |
1018 if (connect_data->fd < 0) | |
14451 | 1019 { |
1020 gaim_proxy_connect_data_disconnect_formatted(connect_data, | |
1021 _("Unable to create socket:\n%s"), strerror(errno)); | |
1022 return; | |
1023 } | |
14192 | 1024 |
14262 | 1025 fcntl(connect_data->fd, F_SETFL, O_NONBLOCK); |
14192 | 1026 #ifndef _WIN32 |
14262 | 1027 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC); |
14192 | 1028 #endif |
1029 | |
14262 | 1030 if (connect(connect_data->fd, addr, addrlen) != 0) |
14192 | 1031 { |
14451 | 1032 if ((errno == EINPROGRESS) || (errno == EINTR)) |
1033 { | |
1034 gaim_debug_info("proxy", "Connection in progress.\n"); | |
1035 connect_data->inpa = gaim_input_add(connect_data->fd, | |
1036 GAIM_INPUT_WRITE, s4_canwrite, connect_data); | |
14192 | 1037 } |
14451 | 1038 else |
14192 | 1039 { |
14451 | 1040 gaim_proxy_connect_data_disconnect(connect_data, strerror(errno)); |
14192 | 1041 } |
14451 | 1042 } |
1043 else | |
1044 { | |
1045 gaim_debug_info("proxy", "Connected immediately.\n"); | |
14192 | 1046 |
14262 | 1047 s4_canwrite(connect_data, connect_data->fd, GAIM_INPUT_WRITE); |
14192 | 1048 } |
1049 } | |
1050 | |
1051 static void | |
1052 s5_canread_again(gpointer data, gint source, GaimInputCondition cond) | |
1053 { | |
1054 guchar *dest, *buf; | |
14262 | 1055 GaimProxyConnectData *connect_data = data; |
14192 | 1056 int len; |
1057 | |
14262 | 1058 if (connect_data->read_buffer == NULL) { |
1059 connect_data->read_buf_len = 512; | |
1060 connect_data->read_buffer = g_malloc(connect_data->read_buf_len); | |
1061 connect_data->read_len = 0; | |
14192 | 1062 } |
1063 | |
14262 | 1064 dest = connect_data->read_buffer + connect_data->read_len; |
1065 buf = connect_data->read_buffer; | |
14192 | 1066 |
1067 gaim_debug_info("socks5 proxy", "Able to read again.\n"); | |
1068 | |
14262 | 1069 len = read(connect_data->fd, dest, (connect_data->read_buf_len - connect_data->read_len)); |
14451 | 1070 |
1071 if (len == 0) | |
1072 { | |
1073 gaim_proxy_connect_data_disconnect(connect_data, | |
1074 _("Server closed the connection.")); | |
14192 | 1075 return; |
1076 } | |
14451 | 1077 |
1078 if (len < 0) | |
1079 { | |
1080 if (errno == EAGAIN) | |
1081 /* No worries */ | |
1082 return; | |
1083 | |
1084 /* Error! */ | |
1085 gaim_proxy_connect_data_disconnect_formatted(connect_data, | |
1086 _("Lost connection with server:\n%s"), strerror(errno)); | |
1087 return; | |
1088 } | |
1089 | |
14262 | 1090 connect_data->read_len += len; |
14192 | 1091 |
14262 | 1092 if(connect_data->read_len < 4) |
14192 | 1093 return; |
1094 | |
1095 if ((buf[0] != 0x05) || (buf[1] != 0x00)) { | |
14451 | 1096 if ((buf[0] == 0x05) && (buf[1] < 0x09)) { |
14192 | 1097 gaim_debug_error("socks5 proxy", socks5errors[buf[1]]); |
14451 | 1098 gaim_proxy_connect_data_disconnect(connect_data, |
1099 socks5errors[buf[1]]); | |
1100 } else { | |
14192 | 1101 gaim_debug_error("socks5 proxy", "Bad data.\n"); |
14451 | 1102 gaim_proxy_connect_data_disconnect(connect_data, |
1103 _("Received invalid data on connection with server.")); | |
1104 } | |
14192 | 1105 return; |
1106 } | |
1107 | |
1108 /* Skip past BND.ADDR */ | |
1109 switch(buf[3]) { | |
1110 case 0x01: /* the address is a version-4 IP address, with a length of 4 octets */ | |
14262 | 1111 if(connect_data->read_len < 4 + 4) |
14192 | 1112 return; |
1113 buf += 4 + 4; | |
1114 break; | |
1115 case 0x03: /* the address field contains a fully-qualified domain name. The first | |
1116 octet of the address field contains the number of octets of name that | |
1117 follow, there is no terminating NUL octet. */ | |
14262 | 1118 if(connect_data->read_len < 4 + 1) |
14192 | 1119 return; |
1120 buf += 4 + 1; | |
14262 | 1121 if(connect_data->read_len < 4 + 1 + buf[0]) |
14192 | 1122 return; |
1123 buf += buf[0]; | |
1124 break; | |
1125 case 0x04: /* the address is a version-6 IP address, with a length of 16 octets */ | |
14262 | 1126 if(connect_data->read_len < 4 + 16) |
14192 | 1127 return; |
1128 buf += 4 + 16; | |
1129 break; | |
1130 } | |
1131 | |
14262 | 1132 if(connect_data->read_len < (buf - connect_data->read_buffer) + 2) |
14192 | 1133 return; |
1134 | |
1135 /* Skip past BND.PORT */ | |
1136 buf += 2; | |
1137 | |
14262 | 1138 gaim_proxy_connect_data_connected(connect_data); |
14192 | 1139 } |
1140 | |
1141 static void | |
1142 s5_sendconnect(gpointer data, int source) | |
1143 { | |
14262 | 1144 GaimProxyConnectData *connect_data = data; |
1145 int hlen = strlen(connect_data->host); | |
1146 connect_data->write_buf_len = 5 + hlen + 2; | |
1147 connect_data->write_buffer = g_malloc(connect_data->write_buf_len); | |
1148 connect_data->written_len = 0; | |
14192 | 1149 |
14262 | 1150 connect_data->write_buffer[0] = 0x05; |
1151 connect_data->write_buffer[1] = 0x01; /* CONNECT */ | |
1152 connect_data->write_buffer[2] = 0x00; /* reserved */ | |
1153 connect_data->write_buffer[3] = 0x03; /* address type -- host name */ | |
1154 connect_data->write_buffer[4] = hlen; | |
1155 memcpy(connect_data->write_buffer + 5, connect_data->host, hlen); | |
1156 connect_data->write_buffer[5 + hlen] = connect_data->port >> 8; | |
1157 connect_data->write_buffer[5 + hlen + 1] = connect_data->port & 0xff; | |
14192 | 1158 |
14262 | 1159 connect_data->read_cb = s5_canread_again; |
14192 | 1160 |
14262 | 1161 connect_data->inpa = gaim_input_add(connect_data->fd, GAIM_INPUT_WRITE, proxy_do_write, connect_data); |
1162 proxy_do_write(connect_data, connect_data->fd, GAIM_INPUT_WRITE); | |
14192 | 1163 } |
1164 | |
1165 static void | |
1166 s5_readauth(gpointer data, gint source, GaimInputCondition cond) | |
1167 { | |
14262 | 1168 GaimProxyConnectData *connect_data = data; |
14192 | 1169 int len; |
1170 | |
14262 | 1171 if (connect_data->read_buffer == NULL) { |
1172 connect_data->read_buf_len = 2; | |
1173 connect_data->read_buffer = g_malloc(connect_data->read_buf_len); | |
1174 connect_data->read_len = 0; | |
14192 | 1175 } |
1176 | |
1177 gaim_debug_info("socks5 proxy", "Got auth response.\n"); | |
1178 | |
14262 | 1179 len = read(connect_data->fd, connect_data->read_buffer + connect_data->read_len, |
1180 connect_data->read_buf_len - connect_data->read_len); | |
14451 | 1181 |
1182 if (len == 0) | |
1183 { | |
1184 gaim_proxy_connect_data_disconnect(connect_data, | |
1185 _("Server closed the connection.")); | |
14192 | 1186 return; |
1187 } | |
14451 | 1188 |
1189 if (len < 0) | |
1190 { | |
1191 if (errno == EAGAIN) | |
1192 /* No worries */ | |
1193 return; | |
1194 | |
1195 /* Error! */ | |
1196 gaim_proxy_connect_data_disconnect_formatted(connect_data, | |
1197 _("Lost connection with server:\n%s"), strerror(errno)); | |
1198 return; | |
1199 } | |
1200 | |
14262 | 1201 connect_data->read_len += len; |
1202 if (connect_data->read_len < 2) | |
14192 | 1203 return; |
1204 | |
14262 | 1205 gaim_input_remove(connect_data->inpa); |
1206 connect_data->inpa = 0; | |
14192 | 1207 |
14262 | 1208 if ((connect_data->read_buffer[0] != 0x01) || (connect_data->read_buffer[1] != 0x00)) { |
14451 | 1209 gaim_proxy_connect_data_disconnect(connect_data, |
1210 _("Received invalid data on connection with server.")); | |
14192 | 1211 return; |
1212 } | |
1213 | |
14262 | 1214 g_free(connect_data->read_buffer); |
1215 connect_data->read_buffer = NULL; | |
14192 | 1216 |
14262 | 1217 s5_sendconnect(connect_data, connect_data->fd); |
14192 | 1218 } |
1219 | |
1220 static void | |
1221 hmacmd5_chap(const unsigned char * challenge, int challen, const char * passwd, unsigned char * response) | |
1222 { | |
1223 GaimCipher *cipher; | |
1224 GaimCipherContext *ctx; | |
1225 int i; | |
1226 unsigned char Kxoripad[65]; | |
1227 unsigned char Kxoropad[65]; | |
1228 int pwlen; | |
1229 | |
1230 cipher = gaim_ciphers_find_cipher("md5"); | |
1231 ctx = gaim_cipher_context_new(cipher, NULL); | |
1232 | |
1233 memset(Kxoripad,0,sizeof(Kxoripad)); | |
1234 memset(Kxoropad,0,sizeof(Kxoropad)); | |
1235 | |
1236 pwlen=strlen(passwd); | |
1237 if (pwlen>64) { | |
1238 gaim_cipher_context_append(ctx, (const guchar *)passwd, strlen(passwd)); | |
1239 gaim_cipher_context_digest(ctx, sizeof(Kxoripad), Kxoripad, NULL); | |
1240 pwlen=16; | |
1241 } else { | |
1242 memcpy(Kxoripad, passwd, pwlen); | |
1243 } | |
1244 memcpy(Kxoropad,Kxoripad,pwlen); | |
1245 | |
1246 for (i=0;i<64;i++) { | |
1247 Kxoripad[i]^=0x36; | |
1248 Kxoropad[i]^=0x5c; | |
1249 } | |
1250 | |
1251 gaim_cipher_context_reset(ctx, NULL); | |
1252 gaim_cipher_context_append(ctx, Kxoripad, 64); | |
1253 gaim_cipher_context_append(ctx, challenge, challen); | |
1254 gaim_cipher_context_digest(ctx, sizeof(Kxoripad), Kxoripad, NULL); | |
1255 | |
1256 gaim_cipher_context_reset(ctx, NULL); | |
1257 gaim_cipher_context_append(ctx, Kxoropad, 64); | |
1258 gaim_cipher_context_append(ctx, Kxoripad, 16); | |
1259 gaim_cipher_context_digest(ctx, 16, response, NULL); | |
1260 | |
1261 gaim_cipher_context_destroy(ctx); | |
1262 } | |
1263 | |
1264 static void | |
1265 s5_readchap(gpointer data, gint source, GaimInputCondition cond) | |
1266 { | |
1267 guchar *cmdbuf, *buf; | |
14262 | 1268 GaimProxyConnectData *connect_data = data; |
14192 | 1269 int len, navas, currentav; |
1270 | |
1271 gaim_debug(GAIM_DEBUG_INFO, "socks5 proxy", "Got CHAP response.\n"); | |
1272 | |
14262 | 1273 if (connect_data->read_buffer == NULL) { |
1274 connect_data->read_buf_len = 20; | |
1275 connect_data->read_buffer = g_malloc(connect_data->read_buf_len); | |
1276 connect_data->read_len = 0; | |
14192 | 1277 } |
1278 | |
14262 | 1279 len = read(connect_data->fd, connect_data->read_buffer + connect_data->read_len, |
1280 connect_data->read_buf_len - connect_data->read_len); | |
14192 | 1281 |
14451 | 1282 if (len == 0) |
1283 { | |
1284 gaim_proxy_connect_data_disconnect(connect_data, | |
1285 _("Server closed the connection.")); | |
14192 | 1286 return; |
1287 } | |
14451 | 1288 |
1289 if (len < 0) | |
1290 { | |
1291 if (errno == EAGAIN) | |
1292 /* No worries */ | |
1293 return; | |
1294 | |
1295 /* Error! */ | |
1296 gaim_proxy_connect_data_disconnect_formatted(connect_data, | |
1297 _("Lost connection with server:\n%s"), strerror(errno)); | |
1298 return; | |
1299 } | |
1300 | |
14262 | 1301 connect_data->read_len += len; |
1302 if (connect_data->read_len < 2) | |
14192 | 1303 return; |
1304 | |
14262 | 1305 cmdbuf = connect_data->read_buffer; |
14192 | 1306 |
1307 if (*cmdbuf != 0x01) { | |
14451 | 1308 gaim_proxy_connect_data_disconnect(connect_data, |
1309 _("Received invalid data on connection with server.")); | |
14192 | 1310 return; |
1311 } | |
1312 cmdbuf++; | |
1313 | |
1314 navas = *cmdbuf; | |
1315 cmdbuf++; | |
1316 | |
1317 for (currentav = 0; currentav < navas; currentav++) { | |
14262 | 1318 if (connect_data->read_len - (cmdbuf - connect_data->read_buffer) < 2) |
14192 | 1319 return; |
14262 | 1320 if (connect_data->read_len - (cmdbuf - connect_data->read_buffer) < cmdbuf[1]) |
14192 | 1321 return; |
1322 buf = cmdbuf + 2; | |
1323 switch (cmdbuf[0]) { | |
1324 case 0x00: | |
1325 /* Did auth work? */ | |
1326 if (buf[0] == 0x00) { | |
14262 | 1327 gaim_input_remove(connect_data->inpa); |
1328 connect_data->inpa = 0; | |
1329 g_free(connect_data->read_buffer); | |
1330 connect_data->read_buffer = NULL; | |
14192 | 1331 /* Success */ |
14262 | 1332 s5_sendconnect(connect_data, connect_data->fd); |
14192 | 1333 return; |
1334 } else { | |
1335 /* Failure */ | |
1336 gaim_debug_warning("proxy", | |
1337 "socks5 CHAP authentication " | |
1338 "failed. Disconnecting..."); | |
14451 | 1339 gaim_proxy_connect_data_disconnect(connect_data, |
1340 _("Authentication failed")); | |
14192 | 1341 return; |
1342 } | |
1343 break; | |
1344 case 0x03: | |
1345 /* Server wants our credentials */ | |
1346 | |
14262 | 1347 connect_data->write_buf_len = 16 + 4; |
1348 connect_data->write_buffer = g_malloc(connect_data->write_buf_len); | |
1349 connect_data->written_len = 0; | |
14192 | 1350 |
1351 hmacmd5_chap(buf, cmdbuf[1], | |
14262 | 1352 gaim_proxy_info_get_password(connect_data->gpi), |
1353 connect_data->write_buffer + 4); | |
1354 connect_data->write_buffer[0] = 0x01; | |
1355 connect_data->write_buffer[1] = 0x01; | |
1356 connect_data->write_buffer[2] = 0x04; | |
1357 connect_data->write_buffer[3] = 0x10; | |
14192 | 1358 |
14262 | 1359 gaim_input_remove(connect_data->inpa); |
1360 g_free(connect_data->read_buffer); | |
1361 connect_data->read_buffer = NULL; | |
14192 | 1362 |
14262 | 1363 connect_data->read_cb = s5_readchap; |
14192 | 1364 |
14262 | 1365 connect_data->inpa = gaim_input_add(connect_data->fd, |
1366 GAIM_INPUT_WRITE, proxy_do_write, connect_data); | |
14192 | 1367 |
14262 | 1368 proxy_do_write(connect_data, connect_data->fd, GAIM_INPUT_WRITE); |
14192 | 1369 break; |
1370 case 0x11: | |
1371 /* Server wants to select an algorithm */ | |
1372 if (buf[0] != 0x85) { | |
1373 /* Only currently support HMAC-MD5 */ | |
1374 gaim_debug_warning("proxy", | |
1375 "Server tried to select an " | |
1376 "algorithm that we did not advertise " | |
1377 "as supporting. This is a violation " | |
1378 "of the socks5 CHAP specification. " | |
1379 "Disconnecting..."); | |
14451 | 1380 gaim_proxy_connect_data_disconnect(connect_data, |
1381 _("Received invalid data on connection with server.")); | |
14192 | 1382 return; |
1383 } | |
1384 break; | |
1385 } | |
1386 cmdbuf = buf + cmdbuf[1]; | |
1387 } | |
1388 /* Fell through. We ran out of CHAP events to process, but haven't | |
1389 * succeeded or failed authentication - there may be more to come. | |
1390 * If this is the case, come straight back here. */ | |
1391 } | |
1392 | |
1393 static void | |
1394 s5_canread(gpointer data, gint source, GaimInputCondition cond) | |
1395 { | |
14262 | 1396 GaimProxyConnectData *connect_data = data; |
14192 | 1397 int len; |
1398 | |
14262 | 1399 if (connect_data->read_buffer == NULL) { |
1400 connect_data->read_buf_len = 2; | |
1401 connect_data->read_buffer = g_malloc(connect_data->read_buf_len); | |
1402 connect_data->read_len = 0; | |
14192 | 1403 } |
1404 | |
1405 gaim_debug_info("socks5 proxy", "Able to read.\n"); | |
1406 | |
14262 | 1407 len = read(connect_data->fd, connect_data->read_buffer + connect_data->read_len, |
1408 connect_data->read_buf_len - connect_data->read_len); | |
14451 | 1409 |
1410 if (len == 0) | |
1411 { | |
1412 gaim_proxy_connect_data_disconnect(connect_data, | |
1413 _("Server closed the connection.")); | |
14192 | 1414 return; |
1415 } | |
14451 | 1416 |
1417 if (len < 0) | |
1418 { | |
1419 if (errno == EAGAIN) | |
1420 /* No worries */ | |
1421 return; | |
1422 | |
1423 /* Error! */ | |
1424 gaim_proxy_connect_data_disconnect_formatted(connect_data, | |
1425 _("Lost connection with server:\n%s"), strerror(errno)); | |
1426 return; | |
1427 } | |
1428 | |
14262 | 1429 connect_data->read_len += len; |
1430 if (connect_data->read_len < 2) | |
14192 | 1431 return; |
1432 | |
14262 | 1433 gaim_input_remove(connect_data->inpa); |
1434 connect_data->inpa = 0; | |
14192 | 1435 |
14262 | 1436 if ((connect_data->read_buffer[0] != 0x05) || (connect_data->read_buffer[1] == 0xff)) { |
14451 | 1437 gaim_proxy_connect_data_disconnect(connect_data, |
1438 _("Received invalid data on connection with server.")); | |
14192 | 1439 return; |
1440 } | |
1441 | |
14262 | 1442 if (connect_data->read_buffer[1] == 0x02) { |
14192 | 1443 gsize i, j; |
1444 const char *u, *p; | |
1445 | |
14262 | 1446 u = gaim_proxy_info_get_username(connect_data->gpi); |
1447 p = gaim_proxy_info_get_password(connect_data->gpi); | |
14192 | 1448 |
1449 i = (u == NULL) ? 0 : strlen(u); | |
1450 j = (p == NULL) ? 0 : strlen(p); | |
1451 | |
14262 | 1452 connect_data->write_buf_len = 1 + 1 + i + 1 + j; |
1453 connect_data->write_buffer = g_malloc(connect_data->write_buf_len); | |
1454 connect_data->written_len = 0; | |
14192 | 1455 |
14262 | 1456 connect_data->write_buffer[0] = 0x01; /* version 1 */ |
1457 connect_data->write_buffer[1] = i; | |
14192 | 1458 if (u != NULL) |
14262 | 1459 memcpy(connect_data->write_buffer + 2, u, i); |
1460 connect_data->write_buffer[2 + i] = j; | |
14192 | 1461 if (p != NULL) |
14262 | 1462 memcpy(connect_data->write_buffer + 2 + i + 1, p, j); |
14192 | 1463 |
14262 | 1464 g_free(connect_data->read_buffer); |
1465 connect_data->read_buffer = NULL; | |
14192 | 1466 |
14262 | 1467 connect_data->read_cb = s5_readauth; |
14192 | 1468 |
14262 | 1469 connect_data->inpa = gaim_input_add(connect_data->fd, GAIM_INPUT_WRITE, |
1470 proxy_do_write, connect_data); | |
14192 | 1471 |
14262 | 1472 proxy_do_write(connect_data, connect_data->fd, GAIM_INPUT_WRITE); |
14192 | 1473 |
1474 return; | |
14262 | 1475 } else if (connect_data->read_buffer[1] == 0x03) { |
14192 | 1476 gsize userlen; |
14262 | 1477 userlen = strlen(gaim_proxy_info_get_username(connect_data->gpi)); |
14192 | 1478 |
14262 | 1479 connect_data->write_buf_len = 7 + userlen; |
1480 connect_data->write_buffer = g_malloc(connect_data->write_buf_len); | |
1481 connect_data->written_len = 0; | |
14192 | 1482 |
14262 | 1483 connect_data->write_buffer[0] = 0x01; |
1484 connect_data->write_buffer[1] = 0x02; | |
1485 connect_data->write_buffer[2] = 0x11; | |
1486 connect_data->write_buffer[3] = 0x01; | |
1487 connect_data->write_buffer[4] = 0x85; | |
1488 connect_data->write_buffer[5] = 0x02; | |
1489 connect_data->write_buffer[6] = userlen; | |
1490 memcpy(connect_data->write_buffer + 7, | |
1491 gaim_proxy_info_get_username(connect_data->gpi), userlen); | |
14192 | 1492 |
14262 | 1493 g_free(connect_data->read_buffer); |
1494 connect_data->read_buffer = NULL; | |
14192 | 1495 |
14262 | 1496 connect_data->read_cb = s5_readchap; |
14192 | 1497 |
14262 | 1498 connect_data->inpa = gaim_input_add(connect_data->fd, GAIM_INPUT_WRITE, |
1499 proxy_do_write, connect_data); | |
14192 | 1500 |
14262 | 1501 proxy_do_write(connect_data, connect_data->fd, GAIM_INPUT_WRITE); |
14192 | 1502 |
1503 return; | |
1504 } else { | |
14262 | 1505 g_free(connect_data->read_buffer); |
1506 connect_data->read_buffer = NULL; | |
14192 | 1507 |
14262 | 1508 s5_sendconnect(connect_data, connect_data->fd); |
14192 | 1509 } |
1510 } | |
1511 | |
1512 static void | |
1513 s5_canwrite(gpointer data, gint source, GaimInputCondition cond) | |
1514 { | |
1515 unsigned char buf[5]; | |
1516 int i; | |
14262 | 1517 GaimProxyConnectData *connect_data = data; |
14192 | 1518 socklen_t len; |
1519 int error = ETIMEDOUT; | |
14451 | 1520 int ret; |
14192 | 1521 |
1522 gaim_debug_info("socks5 proxy", "Connected.\n"); | |
1523 | |
14262 | 1524 if (connect_data->inpa > 0) |
14192 | 1525 { |
14262 | 1526 gaim_input_remove(connect_data->inpa); |
1527 connect_data->inpa = 0; | |
14192 | 1528 } |
1529 | |
1530 len = sizeof(error); | |
14451 | 1531 ret = getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len); |
1532 if ((ret != 0) || (error != 0)) | |
1533 { | |
1534 if (ret != 0) | |
1535 error = errno; | |
1536 gaim_proxy_connect_data_disconnect(connect_data, strerror(error)); | |
14192 | 1537 return; |
1538 } | |
1539 | |
1540 i = 0; | |
1541 buf[0] = 0x05; /* SOCKS version 5 */ | |
1542 | |
14262 | 1543 if (gaim_proxy_info_get_username(connect_data->gpi) != NULL) { |
14192 | 1544 buf[1] = 0x03; /* three methods */ |
1545 buf[2] = 0x00; /* no authentication */ | |
1546 buf[3] = 0x03; /* CHAP authentication */ | |
1547 buf[4] = 0x02; /* username/password authentication */ | |
1548 i = 5; | |
1549 } | |
1550 else { | |
1551 buf[1] = 0x01; | |
1552 buf[2] = 0x00; | |
1553 i = 3; | |
1554 } | |
1555 | |
14262 | 1556 connect_data->write_buf_len = i; |
1557 connect_data->write_buffer = g_malloc(connect_data->write_buf_len); | |
1558 memcpy(connect_data->write_buffer, buf, i); | |
14192 | 1559 |
14262 | 1560 connect_data->read_cb = s5_canread; |
14192 | 1561 |
14262 | 1562 connect_data->inpa = gaim_input_add(connect_data->fd, GAIM_INPUT_WRITE, proxy_do_write, connect_data); |
1563 proxy_do_write(connect_data, connect_data->fd, GAIM_INPUT_WRITE); | |
14192 | 1564 } |
1565 | |
14451 | 1566 static void |
14262 | 1567 proxy_connect_socks5(GaimProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen) |
14192 | 1568 { |
14451 | 1569 gaim_debug_info("proxy", |
14192 | 1570 "Connecting to %s:%d via %s:%d using SOCKS5\n", |
14262 | 1571 connect_data->host, connect_data->port, |
1572 gaim_proxy_info_get_host(connect_data->gpi), | |
1573 gaim_proxy_info_get_port(connect_data->gpi)); | |
14192 | 1574 |
14262 | 1575 connect_data->fd = socket(addr->sa_family, SOCK_STREAM, 0); |
1576 if (connect_data->fd < 0) | |
14451 | 1577 { |
1578 gaim_proxy_connect_data_disconnect_formatted(connect_data, | |
1579 _("Unable to create socket:\n%s"), strerror(errno)); | |
1580 return; | |
1581 } | |
14192 | 1582 |
14262 | 1583 fcntl(connect_data->fd, F_SETFL, O_NONBLOCK); |
14192 | 1584 #ifndef _WIN32 |
14262 | 1585 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC); |
14192 | 1586 #endif |
1587 | |
14262 | 1588 if (connect(connect_data->fd, addr, addrlen) != 0) |
14192 | 1589 { |
14451 | 1590 if ((errno == EINPROGRESS) || (errno == EINTR)) |
1591 { | |
14192 | 1592 gaim_debug_info("socks5 proxy", "Connection in progress\n"); |
14451 | 1593 connect_data->inpa = gaim_input_add(connect_data->fd, |
1594 GAIM_INPUT_WRITE, s5_canwrite, connect_data); | |
14192 | 1595 } |
14451 | 1596 else |
1597 { | |
1598 gaim_proxy_connect_data_disconnect(connect_data, strerror(errno)); | |
14192 | 1599 } |
1600 } | |
14451 | 1601 else |
1602 { | |
1603 gaim_debug_info("proxy", "Connected immediately.\n"); | |
14192 | 1604 |
14262 | 1605 s5_canwrite(connect_data, connect_data->fd, GAIM_INPUT_WRITE); |
14192 | 1606 } |
1607 } | |
1608 | |
1609 /** | |
14451 | 1610 * This function attempts to connect to the next IP address in the list |
1611 * of IP addresses returned to us by gaim_dnsquery_a() and attemps | |
14192 | 1612 * to connect to each one. This is called after the hostname is |
14451 | 1613 * resolved, and each time a connection attempt fails (assuming there |
1614 * is another IP address to try). | |
14192 | 1615 */ |
14262 | 1616 static void try_connect(GaimProxyConnectData *connect_data) |
14192 | 1617 { |
1618 size_t addrlen; | |
1619 struct sockaddr *addr; | |
14903 | 1620 char ipaddr[INET6_ADDRSTRLEN]; |
14192 | 1621 |
14451 | 1622 addrlen = GPOINTER_TO_INT(connect_data->hosts->data); |
1623 connect_data->hosts = g_slist_remove(connect_data->hosts, connect_data->hosts->data); | |
1624 addr = connect_data->hosts->data; | |
1625 connect_data->hosts = g_slist_remove(connect_data->hosts, connect_data->hosts->data); | |
14192 | 1626 |
14903 | 1627 inet_ntop(addr->sa_family, &((struct sockaddr_in *)addr)->sin_addr, |
1628 ipaddr, sizeof(ipaddr)); | |
1629 gaim_debug_info("proxy", "Attempting connection to %s\n", ipaddr); | |
14192 | 1630 |
14451 | 1631 switch (gaim_proxy_info_get_type(connect_data->gpi)) { |
1632 case GAIM_PROXY_NONE: | |
1633 proxy_connect_none(connect_data, addr, addrlen); | |
1634 break; | |
14192 | 1635 |
14451 | 1636 case GAIM_PROXY_HTTP: |
1637 proxy_connect_http(connect_data, addr, addrlen); | |
1638 break; | |
14192 | 1639 |
14451 | 1640 case GAIM_PROXY_SOCKS4: |
1641 proxy_connect_socks4(connect_data, addr, addrlen); | |
1642 break; | |
14192 | 1643 |
14451 | 1644 case GAIM_PROXY_SOCKS5: |
1645 proxy_connect_socks5(connect_data, addr, addrlen); | |
1646 break; | |
14192 | 1647 |
14451 | 1648 case GAIM_PROXY_USE_ENVVAR: |
1649 proxy_connect_http(connect_data, addr, addrlen); | |
1650 break; | |
14192 | 1651 |
14451 | 1652 default: |
14192 | 1653 break; |
1654 } | |
1655 | |
14451 | 1656 g_free(addr); |
14192 | 1657 } |
1658 | |
1659 static void | |
1660 connection_host_resolved(GSList *hosts, gpointer data, | |
1661 const char *error_message) | |
1662 { | |
14262 | 1663 GaimProxyConnectData *connect_data; |
14192 | 1664 |
14262 | 1665 connect_data = data; |
1666 connect_data->query_data = NULL; | |
14238 | 1667 |
14192 | 1668 if (error_message != NULL) |
1669 { | |
14451 | 1670 gaim_proxy_connect_data_disconnect(connect_data, error_message); |
1671 return; | |
1672 } | |
1673 | |
1674 if (hosts == NULL) | |
1675 { | |
1676 gaim_proxy_connect_data_disconnect(connect_data, _("Could not resolve host name")); | |
14238 | 1677 return; |
14192 | 1678 } |
1679 | |
14262 | 1680 connect_data->hosts = hosts; |
14192 | 1681 |
14262 | 1682 try_connect(connect_data); |
14192 | 1683 } |
1684 | |
1685 GaimProxyInfo * | |
1686 gaim_proxy_get_setup(GaimAccount *account) | |
1687 { | |
14980
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14971
diff
changeset
|
1688 GaimProxyInfo *gpi = NULL; |
14192 | 1689 const gchar *tmp; |
1690 | |
14971
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14954
diff
changeset
|
1691 /* This is used as a fallback so we don't overwrite the selected proxy type */ |
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14954
diff
changeset
|
1692 static GaimProxyInfo *tmp_none_proxy_info = NULL; |
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14954
diff
changeset
|
1693 if (!tmp_none_proxy_info) { |
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14954
diff
changeset
|
1694 tmp_none_proxy_info = gaim_proxy_info_new(); |
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14954
diff
changeset
|
1695 gaim_proxy_info_set_type(tmp_none_proxy_info, GAIM_PROXY_NONE); |
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14954
diff
changeset
|
1696 } |
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14954
diff
changeset
|
1697 |
14980
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14971
diff
changeset
|
1698 if (account && gaim_account_get_proxy_info(account) != NULL) { |
14192 | 1699 gpi = gaim_account_get_proxy_info(account); |
14980
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14971
diff
changeset
|
1700 if (gaim_proxy_info_get_type(gpi) == GAIM_PROXY_USE_GLOBAL) |
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14971
diff
changeset
|
1701 gpi = NULL; |
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14971
diff
changeset
|
1702 } |
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14971
diff
changeset
|
1703 if (gpi == NULL) { |
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14971
diff
changeset
|
1704 if (gaim_running_gnome()) |
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14971
diff
changeset
|
1705 gpi = gaim_gnome_proxy_get_info(); |
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14971
diff
changeset
|
1706 else |
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14971
diff
changeset
|
1707 gpi = gaim_global_proxy_get_info(); |
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14971
diff
changeset
|
1708 } |
14192 | 1709 |
1710 if (gaim_proxy_info_get_type(gpi) == GAIM_PROXY_USE_ENVVAR) { | |
14971
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14954
diff
changeset
|
1711 #ifdef _WIN32 |
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14954
diff
changeset
|
1712 wgaim_check_for_proxy_changes(); |
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14954
diff
changeset
|
1713 #endif |
14192 | 1714 if ((tmp = g_getenv("HTTP_PROXY")) != NULL || |
1715 (tmp = g_getenv("http_proxy")) != NULL || | |
1716 (tmp = g_getenv("HTTPPROXY")) != NULL) { | |
14971
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14954
diff
changeset
|
1717 char *proxyhost, *proxyuser, *proxypasswd; |
14192 | 1718 int proxyport; |
1719 | |
1720 /* http_proxy-format: | |
1721 * export http_proxy="http://user:passwd@your.proxy.server:port/" | |
1722 */ | |
14971
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14954
diff
changeset
|
1723 if(gaim_url_parse(tmp, &proxyhost, &proxyport, NULL, &proxyuser, &proxypasswd)) { |
14192 | 1724 gaim_proxy_info_set_host(gpi, proxyhost); |
1725 g_free(proxyhost); | |
14971
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14954
diff
changeset
|
1726 |
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14954
diff
changeset
|
1727 gaim_proxy_info_set_username(gpi, proxyuser); |
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14954
diff
changeset
|
1728 g_free(proxyuser); |
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14954
diff
changeset
|
1729 |
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14954
diff
changeset
|
1730 gaim_proxy_info_set_password(gpi, proxypasswd); |
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14954
diff
changeset
|
1731 g_free(proxypasswd); |
14192 | 1732 |
1733 /* only for backward compatibility */ | |
1734 if (proxyport == 80 && | |
1735 ((tmp = g_getenv("HTTP_PROXY_PORT")) != NULL || | |
1736 (tmp = g_getenv("http_proxy_port")) != NULL || | |
1737 (tmp = g_getenv("HTTPPROXYPORT")) != NULL)) | |
14971
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14954
diff
changeset
|
1738 proxyport = atoi(tmp); |
14192 | 1739 |
1740 gaim_proxy_info_set_port(gpi, proxyport); | |
14980
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14971
diff
changeset
|
1741 |
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14971
diff
changeset
|
1742 /* XXX: Do we want to skip this step if user/password were part of url? */ |
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14971
diff
changeset
|
1743 if ((tmp = g_getenv("HTTP_PROXY_USER")) != NULL || |
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14971
diff
changeset
|
1744 (tmp = g_getenv("http_proxy_user")) != NULL || |
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14971
diff
changeset
|
1745 (tmp = g_getenv("HTTPPROXYUSER")) != NULL) |
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14971
diff
changeset
|
1746 gaim_proxy_info_set_username(gpi, tmp); |
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14971
diff
changeset
|
1747 |
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14971
diff
changeset
|
1748 if ((tmp = g_getenv("HTTP_PROXY_PASS")) != NULL || |
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14971
diff
changeset
|
1749 (tmp = g_getenv("http_proxy_pass")) != NULL || |
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14971
diff
changeset
|
1750 (tmp = g_getenv("HTTPPROXYPASS")) != NULL) |
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14971
diff
changeset
|
1751 gaim_proxy_info_set_password(gpi, tmp); |
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14971
diff
changeset
|
1752 |
14192 | 1753 } |
1754 } else { | |
1755 /* no proxy environment variable found, don't use a proxy */ | |
1756 gaim_debug_info("proxy", "No environment settings found, not using a proxy\n"); | |
14971
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14954
diff
changeset
|
1757 gpi = tmp_none_proxy_info; |
14192 | 1758 } |
1759 | |
1760 } | |
1761 | |
1762 return gpi; | |
1763 } | |
1764 | |
14262 | 1765 GaimProxyConnectData * |
14837 | 1766 gaim_proxy_connect(void *handle, GaimAccount *account, |
1767 const char *host, int port, | |
14192 | 1768 GaimProxyConnectFunction connect_cb, gpointer data) |
1769 { | |
1770 const char *connecthost = host; | |
1771 int connectport = port; | |
14262 | 1772 GaimProxyConnectData *connect_data; |
14192 | 1773 |
1774 g_return_val_if_fail(host != NULL, NULL); | |
1775 g_return_val_if_fail(port > 0, NULL); | |
1776 g_return_val_if_fail(connect_cb != NULL, NULL); | |
1777 | |
14262 | 1778 connect_data = g_new0(GaimProxyConnectData, 1); |
1779 connect_data->fd = -1; | |
14837 | 1780 connect_data->handle = handle; |
14262 | 1781 connect_data->connect_cb = connect_cb; |
1782 connect_data->data = data; | |
1783 connect_data->host = g_strdup(host); | |
1784 connect_data->port = port; | |
1785 connect_data->gpi = gaim_proxy_get_setup(account); | |
14192 | 1786 |
14262 | 1787 if ((gaim_proxy_info_get_type(connect_data->gpi) != GAIM_PROXY_NONE) && |
1788 (gaim_proxy_info_get_host(connect_data->gpi) == NULL || | |
1789 gaim_proxy_info_get_port(connect_data->gpi) <= 0)) { | |
14192 | 1790 |
1791 gaim_notify_error(NULL, NULL, _("Invalid proxy settings"), _("Either the host name or port number specified for your given proxy type is invalid.")); | |
14262 | 1792 gaim_proxy_connect_data_destroy(connect_data); |
14192 | 1793 return NULL; |
1794 } | |
1795 | |
14262 | 1796 switch (gaim_proxy_info_get_type(connect_data->gpi)) |
14192 | 1797 { |
1798 case GAIM_PROXY_NONE: | |
1799 break; | |
1800 | |
1801 case GAIM_PROXY_HTTP: | |
1802 case GAIM_PROXY_SOCKS4: | |
1803 case GAIM_PROXY_SOCKS5: | |
1804 case GAIM_PROXY_USE_ENVVAR: | |
14262 | 1805 connecthost = gaim_proxy_info_get_host(connect_data->gpi); |
1806 connectport = gaim_proxy_info_get_port(connect_data->gpi); | |
14192 | 1807 break; |
1808 | |
1809 default: | |
14262 | 1810 gaim_proxy_connect_data_destroy(connect_data); |
14192 | 1811 return NULL; |
1812 } | |
1813 | |
14262 | 1814 connect_data->query_data = gaim_dnsquery_a(connecthost, |
1815 connectport, connection_host_resolved, connect_data); | |
1816 if (connect_data->query_data == NULL) | |
14192 | 1817 { |
14262 | 1818 gaim_proxy_connect_data_destroy(connect_data); |
14192 | 1819 return NULL; |
1820 } | |
1821 | |
14837 | 1822 handles = g_slist_prepend(handles, connect_data); |
14192 | 1823 |
14262 | 1824 return connect_data; |
14192 | 1825 } |
1826 | |
1827 /* | |
1828 * Combine some of this code with gaim_proxy_connect() | |
1829 */ | |
14262 | 1830 GaimProxyConnectData * |
14837 | 1831 gaim_proxy_connect_socks5(void *handle, GaimProxyInfo *gpi, |
1832 const char *host, int port, | |
1833 GaimProxyConnectFunction connect_cb, | |
1834 gpointer data) | |
14192 | 1835 { |
14262 | 1836 GaimProxyConnectData *connect_data; |
14192 | 1837 |
1838 g_return_val_if_fail(host != NULL, NULL); | |
14954 | 1839 g_return_val_if_fail(port >= 0, NULL); |
14192 | 1840 g_return_val_if_fail(connect_cb != NULL, NULL); |
1841 | |
14262 | 1842 connect_data = g_new0(GaimProxyConnectData, 1); |
1843 connect_data->fd = -1; | |
14837 | 1844 connect_data->handle = handle; |
14262 | 1845 connect_data->connect_cb = connect_cb; |
1846 connect_data->data = data; | |
1847 connect_data->host = g_strdup(host); | |
1848 connect_data->port = port; | |
1849 connect_data->gpi = gpi; | |
14192 | 1850 |
14262 | 1851 connect_data->query_data = |
14192 | 1852 gaim_dnsquery_a(gaim_proxy_info_get_host(gpi), |
1853 gaim_proxy_info_get_port(gpi), | |
14262 | 1854 connection_host_resolved, connect_data); |
1855 if (connect_data->query_data == NULL) | |
14192 | 1856 { |
14262 | 1857 gaim_proxy_connect_data_destroy(connect_data); |
14192 | 1858 return NULL; |
1859 } | |
1860 | |
14837 | 1861 handles = g_slist_prepend(handles, connect_data); |
14192 | 1862 |
14262 | 1863 return connect_data; |
14192 | 1864 } |
1865 | |
1866 void | |
14262 | 1867 gaim_proxy_connect_cancel(GaimProxyConnectData *connect_data) |
14192 | 1868 { |
14451 | 1869 gaim_proxy_connect_data_disconnect(connect_data, NULL); |
14262 | 1870 gaim_proxy_connect_data_destroy(connect_data); |
14192 | 1871 } |
1872 | |
14837 | 1873 void |
1874 gaim_proxy_connect_cancel_with_handle(void *handle) | |
1875 { | |
1876 GSList *l, *l_next; | |
1877 | |
1878 for (l = handles; l != NULL; l = l_next) { | |
1879 GaimProxyConnectData *connect_data = l->data; | |
1880 | |
1881 l_next = l->next; | |
1882 | |
1883 if (connect_data->handle == handle) | |
1884 gaim_proxy_connect_cancel(connect_data); | |
1885 } | |
1886 } | |
1887 | |
14192 | 1888 static void |
1889 proxy_pref_cb(const char *name, GaimPrefType type, | |
1890 gconstpointer value, gpointer data) | |
1891 { | |
1892 GaimProxyInfo *info = gaim_global_proxy_get_info(); | |
1893 | |
1894 if (!strcmp(name, "/core/proxy/type")) { | |
1895 int proxytype; | |
1896 const char *type = value; | |
1897 | |
1898 if (!strcmp(type, "none")) | |
1899 proxytype = GAIM_PROXY_NONE; | |
1900 else if (!strcmp(type, "http")) | |
1901 proxytype = GAIM_PROXY_HTTP; | |
1902 else if (!strcmp(type, "socks4")) | |
1903 proxytype = GAIM_PROXY_SOCKS4; | |
1904 else if (!strcmp(type, "socks5")) | |
1905 proxytype = GAIM_PROXY_SOCKS5; | |
1906 else if (!strcmp(type, "envvar")) | |
1907 proxytype = GAIM_PROXY_USE_ENVVAR; | |
1908 else | |
1909 proxytype = -1; | |
1910 | |
1911 gaim_proxy_info_set_type(info, proxytype); | |
1912 } else if (!strcmp(name, "/core/proxy/host")) | |
1913 gaim_proxy_info_set_host(info, value); | |
1914 else if (!strcmp(name, "/core/proxy/port")) | |
1915 gaim_proxy_info_set_port(info, GPOINTER_TO_INT(value)); | |
1916 else if (!strcmp(name, "/core/proxy/username")) | |
1917 gaim_proxy_info_set_username(info, value); | |
1918 else if (!strcmp(name, "/core/proxy/password")) | |
1919 gaim_proxy_info_set_password(info, value); | |
1920 } | |
1921 | |
1922 void * | |
1923 gaim_proxy_get_handle() | |
1924 { | |
1925 static int handle; | |
1926 | |
1927 return &handle; | |
1928 } | |
1929 | |
1930 void | |
1931 gaim_proxy_init(void) | |
1932 { | |
1933 void *handle; | |
1934 | |
1935 /* Initialize a default proxy info struct. */ | |
1936 global_proxy_info = gaim_proxy_info_new(); | |
1937 | |
1938 /* Proxy */ | |
1939 gaim_prefs_add_none("/core/proxy"); | |
1940 gaim_prefs_add_string("/core/proxy/type", "none"); | |
1941 gaim_prefs_add_string("/core/proxy/host", ""); | |
1942 gaim_prefs_add_int("/core/proxy/port", 0); | |
1943 gaim_prefs_add_string("/core/proxy/username", ""); | |
1944 gaim_prefs_add_string("/core/proxy/password", ""); | |
1945 | |
1946 /* Setup callbacks for the preferences. */ | |
1947 handle = gaim_proxy_get_handle(); | |
1948 gaim_prefs_connect_callback(handle, "/core/proxy/type", proxy_pref_cb, | |
1949 NULL); | |
1950 gaim_prefs_connect_callback(handle, "/core/proxy/host", proxy_pref_cb, | |
1951 NULL); | |
1952 gaim_prefs_connect_callback(handle, "/core/proxy/port", proxy_pref_cb, | |
1953 NULL); | |
1954 gaim_prefs_connect_callback(handle, "/core/proxy/username", | |
1955 proxy_pref_cb, NULL); | |
1956 gaim_prefs_connect_callback(handle, "/core/proxy/password", | |
1957 proxy_pref_cb, NULL); | |
1958 } | |
1959 | |
1960 void | |
1961 gaim_proxy_uninit(void) | |
1962 { | |
14837 | 1963 while (handles != NULL) |
14451 | 1964 { |
14837 | 1965 gaim_proxy_connect_data_disconnect(handles->data, NULL); |
1966 gaim_proxy_connect_data_destroy(handles->data); | |
14451 | 1967 } |
14192 | 1968 } |