Mercurial > pidgin.yaz
comparison libpurple/proxy.c @ 15374:5fe8042783c1
Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author | Sean Egan <seanegan@gmail.com> |
---|---|
date | Sat, 20 Jan 2007 02:32:10 +0000 |
parents | |
children | 0d4890637238 |
comparison
equal
deleted
inserted
replaced
15373:f79e0f4df793 | 15374:5fe8042783c1 |
---|---|
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 | |
42 struct _GaimProxyConnectData { | |
43 void *handle; | |
44 GaimProxyConnectFunction connect_cb; | |
45 gpointer data; | |
46 gchar *host; | |
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 | |
86 static GSList *handles = NULL; | |
87 | |
88 static void try_connect(GaimProxyConnectData *connect_data); | |
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 | |
273 /** | |
274 * Whoever calls this needs to have called | |
275 * gaim_proxy_connect_data_disconnect() beforehand. | |
276 */ | |
277 static void | |
278 gaim_proxy_connect_data_destroy(GaimProxyConnectData *connect_data) | |
279 { | |
280 handles = g_slist_remove(handles, connect_data); | |
281 | |
282 if (connect_data->query_data != NULL) | |
283 gaim_dnsquery_destroy(connect_data->query_data); | |
284 | |
285 while (connect_data->hosts != NULL) | |
286 { | |
287 /* Discard the length... */ | |
288 connect_data->hosts = g_slist_remove(connect_data->hosts, connect_data->hosts->data); | |
289 /* Free the address... */ | |
290 g_free(connect_data->hosts->data); | |
291 connect_data->hosts = g_slist_remove(connect_data->hosts, connect_data->hosts->data); | |
292 } | |
293 | |
294 g_free(connect_data->host); | |
295 g_free(connect_data); | |
296 } | |
297 | |
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 | |
369 static void | |
370 gaim_proxy_connect_data_connected(GaimProxyConnectData *connect_data) | |
371 { | |
372 connect_data->connect_cb(connect_data->data, connect_data->fd, NULL); | |
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 | |
377 * we destroy the connect_data. | |
378 */ | |
379 connect_data->fd = -1; | |
380 | |
381 gaim_proxy_connect_data_disconnect(connect_data, NULL); | |
382 gaim_proxy_connect_data_destroy(connect_data); | |
383 } | |
384 | |
385 static void | |
386 socket_ready_cb(gpointer data, gint source, GaimInputCondition cond) | |
387 { | |
388 GaimProxyConnectData *connect_data = data; | |
389 socklen_t len; | |
390 int error = 0; | |
391 int ret; | |
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 */ | |
406 len = sizeof(error); | |
407 ret = getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len); | |
408 | |
409 if (ret == 0 && error == EINPROGRESS) | |
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) | |
416 error = errno; | |
417 gaim_proxy_connect_data_disconnect(connect_data, strerror(error)); | |
418 return; | |
419 } | |
420 | |
421 gaim_proxy_connect_data_connected(connect_data); | |
422 } | |
423 | |
424 static gboolean | |
425 clean_connect(gpointer data) | |
426 { | |
427 gaim_proxy_connect_data_connected(data); | |
428 | |
429 return FALSE; | |
430 } | |
431 | |
432 static void | |
433 proxy_connect_none(GaimProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen) | |
434 { | |
435 gaim_debug_info("proxy", "Connecting to %s:%d with no proxy\n", | |
436 connect_data->host, connect_data->port); | |
437 | |
438 connect_data->fd = socket(addr->sa_family, SOCK_STREAM, 0); | |
439 if (connect_data->fd < 0) | |
440 { | |
441 gaim_proxy_connect_data_disconnect_formatted(connect_data, | |
442 _("Unable to create socket:\n%s"), strerror(errno)); | |
443 return; | |
444 } | |
445 | |
446 fcntl(connect_data->fd, F_SETFL, O_NONBLOCK); | |
447 #ifndef _WIN32 | |
448 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC); | |
449 #endif | |
450 | |
451 if (connect(connect_data->fd, addr, addrlen) != 0) | |
452 { | |
453 if ((errno == EINPROGRESS) || (errno == EINTR)) | |
454 { | |
455 gaim_debug_info("proxy", "Connection in progress\n"); | |
456 connect_data->inpa = gaim_input_add(connect_data->fd, | |
457 GAIM_INPUT_WRITE, socket_ready_cb, connect_data); | |
458 } | |
459 else | |
460 { | |
461 gaim_proxy_connect_data_disconnect(connect_data, strerror(errno)); | |
462 } | |
463 } | |
464 else | |
465 { | |
466 /* | |
467 * The connection happened IMMEDIATELY... strange, but whatever. | |
468 */ | |
469 socklen_t len; | |
470 int error = ETIMEDOUT; | |
471 int ret; | |
472 | |
473 gaim_debug_info("proxy", "Connected immediately.\n"); | |
474 | |
475 len = sizeof(error); | |
476 ret = getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len); | |
477 if ((ret != 0) || (error != 0)) | |
478 { | |
479 if (ret != 0) | |
480 error = errno; | |
481 gaim_proxy_connect_data_disconnect(connect_data, strerror(error)); | |
482 return; | |
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 */ | |
489 gaim_timeout_add(10, clean_connect, connect_data); | |
490 } | |
491 } | |
492 | |
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 */ | |
499 static void | |
500 proxy_do_write(gpointer data, gint source, GaimInputCondition cond) | |
501 { | |
502 GaimProxyConnectData *connect_data; | |
503 const guchar *request; | |
504 gsize request_len; | |
505 int ret; | |
506 | |
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; | |
510 | |
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)); | |
520 return; | |
521 } | |
522 if (ret < request_len) { | |
523 connect_data->written_len += ret; | |
524 return; | |
525 } | |
526 | |
527 /* We're done writing data! Wait for a response. */ | |
528 g_free(connect_data->write_buffer); | |
529 connect_data->write_buffer = NULL; | |
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); | |
533 } | |
534 | |
535 #define HTTP_GOODSTRING "HTTP/1.0 200" | |
536 #define HTTP_GOODSTRING2 "HTTP/1.1 200" | |
537 | |
538 /** | |
539 * We're using an HTTP proxy for a non-port 80 tunnel. Read the | |
540 * response to the CONNECT request. | |
541 */ | |
542 static void | |
543 http_canread(gpointer data, gint source, GaimInputCondition cond) | |
544 { | |
545 int len, headers_len, status = 0; | |
546 gboolean error; | |
547 GaimProxyConnectData *connect_data = data; | |
548 guchar *p; | |
549 gsize max_read; | |
550 | |
551 if (connect_data->read_buffer == NULL) | |
552 { | |
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; | |
556 } | |
557 | |
558 p = connect_data->read_buffer + connect_data->read_len; | |
559 max_read = connect_data->read_buf_len - connect_data->read_len - 1; | |
560 | |
561 len = read(connect_data->fd, p, max_read); | |
562 | |
563 if (len == 0) | |
564 { | |
565 gaim_proxy_connect_data_disconnect(connect_data, | |
566 _("Server closed the connection.")); | |
567 return; | |
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)); | |
579 return; | |
580 } | |
581 | |
582 connect_data->read_len += len; | |
583 p[len] = '\0'; | |
584 | |
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) { | |
588 *p = '\0'; | |
589 headers_len = (p - connect_data->read_buffer) + 4; | |
590 } else if(len == max_read) | |
591 headers_len = len; | |
592 else | |
593 return; | |
594 | |
595 error = strncmp((const char *)connect_data->read_buffer, "HTTP/", 5) != 0; | |
596 if (!error) | |
597 { | |
598 int major; | |
599 p = connect_data->read_buffer + 5; | |
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 */ | |
616 p = (guchar *)g_strrstr((const gchar *)connect_data->read_buffer, "Content-Length: "); | |
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 */ | |
631 len -= connect_data->read_len - headers_len; | |
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 */ | |
634 while (len--) { | |
635 /* TODO: deal with EAGAIN (and other errors) better */ | |
636 if (read(connect_data->fd, &tmpc, 1) < 0 && errno != EAGAIN) | |
637 break; | |
638 } | |
639 } | |
640 | |
641 if (error) | |
642 { | |
643 gaim_proxy_connect_data_disconnect_formatted(connect_data, | |
644 _("Unable to parse response from HTTP proxy: %s\n"), | |
645 connect_data->read_buffer); | |
646 return; | |
647 } | |
648 else if (status != 200) | |
649 { | |
650 gaim_debug_error("proxy", | |
651 "Proxy server replied with:\n%s\n", | |
652 connect_data->read_buffer); | |
653 | |
654 if (status == 407 /* Proxy Auth */) | |
655 { | |
656 gchar *ntlm; | |
657 ntlm = g_strrstr((const gchar *)connect_data->read_buffer, | |
658 "Proxy-Authenticate: NTLM "); | |
659 if (ntlm != NULL) | |
660 { | |
661 /* Check for Type-2 */ | |
662 gchar *tmp = ntlm; | |
663 guint8 *nonce; | |
664 gchar *domain = (gchar*)gaim_proxy_info_get_username(connect_data->gpi); | |
665 gchar *username; | |
666 gchar *request; | |
667 gchar *response; | |
668 username = strchr(domain, '\\'); | |
669 if (username == NULL) | |
670 { | |
671 gaim_proxy_connect_data_disconnect_formatted(connect_data, | |
672 _("HTTP proxy connection error %d"), status); | |
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, | |
682 (gchar*) gaim_proxy_info_get_password(connect_data->gpi), | |
683 (gchar*) gaim_proxy_info_get_host(connect_data->gpi), | |
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", | |
692 connect_data->host, connect_data->port, connect_data->host, | |
693 connect_data->port, response); | |
694 g_free(response); | |
695 | |
696 g_free(connect_data->read_buffer); | |
697 connect_data->read_buffer = NULL; | |
698 | |
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; | |
703 | |
704 gaim_input_remove(connect_data->inpa); | |
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); | |
708 return; | |
709 } else if((ntlm = g_strrstr((const char *)connect_data->read_buffer, "Proxy-Authenticate: NTLM"))) { /* Empty message */ | |
710 gchar request[2048]; | |
711 gchar *domain = (gchar*) gaim_proxy_info_get_username(connect_data->gpi); | |
712 gchar *username; | |
713 int request_len; | |
714 username = strchr(domain, '\\'); | |
715 if (username == NULL) | |
716 { | |
717 gaim_proxy_connect_data_disconnect_formatted(connect_data, | |
718 _("HTTP proxy connection error %d"), status); | |
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", | |
726 connect_data->host, connect_data->port, | |
727 connect_data->host, connect_data->port); | |
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( | |
735 (gchar*) gaim_proxy_info_get_host(connect_data->gpi), | |
736 domain)); | |
737 *username = '\\'; | |
738 | |
739 gaim_input_remove(connect_data->inpa); | |
740 g_free(connect_data->read_buffer); | |
741 connect_data->read_buffer = NULL; | |
742 | |
743 connect_data->write_buffer = g_memdup(request, request_len); | |
744 connect_data->write_buf_len = request_len; | |
745 connect_data->written_len = 0; | |
746 | |
747 connect_data->read_cb = http_canread; | |
748 | |
749 connect_data->inpa = gaim_input_add(connect_data->fd, | |
750 GAIM_INPUT_WRITE, proxy_do_write, connect_data); | |
751 | |
752 proxy_do_write(connect_data, connect_data->fd, cond); | |
753 return; | |
754 } else { | |
755 gaim_proxy_connect_data_disconnect_formatted(connect_data, | |
756 _("HTTP proxy connection error %d"), status); | |
757 return; | |
758 } | |
759 } | |
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."), | |
765 connect_data->port); | |
766 } else { | |
767 gaim_proxy_connect_data_disconnect_formatted(connect_data, | |
768 _("HTTP proxy connection error %d"), status); | |
769 } | |
770 } else { | |
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; | |
775 gaim_debug_info("proxy", "HTTP proxy connection established\n"); | |
776 gaim_proxy_connect_data_connected(connect_data); | |
777 return; | |
778 } | |
779 } | |
780 | |
781 static void | |
782 http_canwrite(gpointer data, gint source, GaimInputCondition cond) | |
783 { | |
784 GString *request; | |
785 GaimProxyConnectData *connect_data; | |
786 socklen_t len; | |
787 int error = ETIMEDOUT; | |
788 int ret; | |
789 | |
790 gaim_debug_info("proxy", "Connected.\n"); | |
791 | |
792 connect_data = data; | |
793 | |
794 if (connect_data->inpa > 0) | |
795 { | |
796 gaim_input_remove(connect_data->inpa); | |
797 connect_data->inpa = 0; | |
798 } | |
799 | |
800 len = sizeof(error); | |
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)); | |
807 return; | |
808 } | |
809 | |
810 gaim_debug_info("proxy", "Using CONNECT tunneling for %s:%d\n", | |
811 connect_data->host, connect_data->port); | |
812 | |
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 { | |
821 char *t1, *t2; | |
822 | |
823 t1 = g_strdup_printf("%s:%s", | |
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) : ""); | |
827 t2 = gaim_base64_encode((const guchar *)t1, strlen(t1)); | |
828 g_free(t1); | |
829 | |
830 g_string_append_printf(request, | |
831 "Proxy-Authorization: Basic %s\r\n" | |
832 "Proxy-Authorization: NTLM %s\r\n" | |
833 "Proxy-Connection: Keep-Alive\r\n", | |
834 t2, gaim_ntlm_gen_type1( | |
835 gaim_proxy_info_get_host(connect_data->gpi), "")); | |
836 g_free(t2); | |
837 } | |
838 | |
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); | |
843 connect_data->written_len = 0; | |
844 connect_data->read_cb = http_canread; | |
845 | |
846 connect_data->inpa = gaim_input_add(connect_data->fd, | |
847 GAIM_INPUT_WRITE, proxy_do_write, connect_data); | |
848 proxy_do_write(connect_data, connect_data->fd, cond); | |
849 } | |
850 | |
851 static void | |
852 proxy_connect_http(GaimProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen) | |
853 { | |
854 gaim_debug_info("proxy", | |
855 "Connecting to %s:%d via %s:%d using HTTP\n", | |
856 connect_data->host, connect_data->port, | |
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)); | |
859 | |
860 connect_data->fd = socket(addr->sa_family, SOCK_STREAM, 0); | |
861 if (connect_data->fd < 0) | |
862 { | |
863 gaim_proxy_connect_data_disconnect_formatted(connect_data, | |
864 _("Unable to create socket:\n%s"), strerror(errno)); | |
865 return; | |
866 } | |
867 | |
868 fcntl(connect_data->fd, F_SETFL, O_NONBLOCK); | |
869 #ifndef _WIN32 | |
870 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC); | |
871 #endif | |
872 | |
873 if (connect(connect_data->fd, addr, addrlen) != 0) | |
874 { | |
875 if ((errno == EINPROGRESS) || (errno == EINTR)) { | |
876 gaim_debug_info("proxy", "Connection in progress\n"); | |
877 | |
878 if (connect_data->port != 80) | |
879 { | |
880 /* we need to do CONNECT first */ | |
881 connect_data->inpa = gaim_input_add(connect_data->fd, | |
882 GAIM_INPUT_WRITE, http_canwrite, connect_data); | |
883 } | |
884 else | |
885 { | |
886 /* | |
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. | |
893 */ | |
894 gaim_debug_info("proxy", "HTTP proxy connection established\n"); | |
895 gaim_proxy_connect_data_connected(connect_data); | |
896 } | |
897 } | |
898 else | |
899 { | |
900 gaim_proxy_connect_data_disconnect(connect_data, strerror(errno)); | |
901 } | |
902 } | |
903 else | |
904 { | |
905 gaim_debug_info("proxy", "Connected immediately.\n"); | |
906 | |
907 http_canwrite(connect_data, connect_data->fd, GAIM_INPUT_WRITE); | |
908 } | |
909 } | |
910 | |
911 static void | |
912 s4_canread(gpointer data, gint source, GaimInputCondition cond) | |
913 { | |
914 GaimProxyConnectData *connect_data = data; | |
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 | |
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; | |
925 } | |
926 | |
927 buf = connect_data->read_buffer + connect_data->read_len; | |
928 max_read = connect_data->read_buf_len - connect_data->read_len; | |
929 | |
930 len = read(connect_data->fd, buf, max_read); | |
931 | |
932 if ((len < 0 && errno == EAGAIN) || (len > 0 && len + connect_data->read_len < 4)) | |
933 return; | |
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); | |
937 return; | |
938 } | |
939 } | |
940 | |
941 gaim_proxy_connect_data_disconnect(connect_data, strerror(errno)); | |
942 } | |
943 | |
944 static void | |
945 s4_canwrite(gpointer data, gint source, GaimInputCondition cond) | |
946 { | |
947 unsigned char packet[9]; | |
948 struct hostent *hp; | |
949 GaimProxyConnectData *connect_data = data; | |
950 socklen_t len; | |
951 int error = ETIMEDOUT; | |
952 int ret; | |
953 | |
954 gaim_debug_info("socks4 proxy", "Connected.\n"); | |
955 | |
956 if (connect_data->inpa > 0) | |
957 { | |
958 gaim_input_remove(connect_data->inpa); | |
959 connect_data->inpa = 0; | |
960 } | |
961 | |
962 len = sizeof(error); | |
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)); | |
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 */ | |
980 /* TODO: Use gaim_dnsquery_a() */ | |
981 hp = gethostbyname(connect_data->host); | |
982 if (hp == NULL) { | |
983 gaim_proxy_connect_data_disconnect_formatted(connect_data, | |
984 _("Error resolving %s"), connect_data->host); | |
985 return; | |
986 } | |
987 | |
988 packet[0] = 4; | |
989 packet[1] = 1; | |
990 packet[2] = connect_data->port >> 8; | |
991 packet[3] = connect_data->port & 0xff; | |
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 | |
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; | |
1002 | |
1003 connect_data->inpa = gaim_input_add(connect_data->fd, GAIM_INPUT_WRITE, proxy_do_write, connect_data); | |
1004 | |
1005 proxy_do_write(connect_data, connect_data->fd, cond); | |
1006 } | |
1007 | |
1008 static void | |
1009 proxy_connect_socks4(GaimProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen) | |
1010 { | |
1011 gaim_debug_info("proxy", | |
1012 "Connecting to %s:%d via %s:%d using SOCKS4\n", | |
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)); | |
1016 | |
1017 connect_data->fd = socket(addr->sa_family, SOCK_STREAM, 0); | |
1018 if (connect_data->fd < 0) | |
1019 { | |
1020 gaim_proxy_connect_data_disconnect_formatted(connect_data, | |
1021 _("Unable to create socket:\n%s"), strerror(errno)); | |
1022 return; | |
1023 } | |
1024 | |
1025 fcntl(connect_data->fd, F_SETFL, O_NONBLOCK); | |
1026 #ifndef _WIN32 | |
1027 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC); | |
1028 #endif | |
1029 | |
1030 if (connect(connect_data->fd, addr, addrlen) != 0) | |
1031 { | |
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); | |
1037 } | |
1038 else | |
1039 { | |
1040 gaim_proxy_connect_data_disconnect(connect_data, strerror(errno)); | |
1041 } | |
1042 } | |
1043 else | |
1044 { | |
1045 gaim_debug_info("proxy", "Connected immediately.\n"); | |
1046 | |
1047 s4_canwrite(connect_data, connect_data->fd, GAIM_INPUT_WRITE); | |
1048 } | |
1049 } | |
1050 | |
1051 static void | |
1052 s5_canread_again(gpointer data, gint source, GaimInputCondition cond) | |
1053 { | |
1054 guchar *dest, *buf; | |
1055 GaimProxyConnectData *connect_data = data; | |
1056 int len; | |
1057 | |
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; | |
1062 } | |
1063 | |
1064 dest = connect_data->read_buffer + connect_data->read_len; | |
1065 buf = connect_data->read_buffer; | |
1066 | |
1067 gaim_debug_info("socks5 proxy", "Able to read again.\n"); | |
1068 | |
1069 len = read(connect_data->fd, dest, (connect_data->read_buf_len - connect_data->read_len)); | |
1070 | |
1071 if (len == 0) | |
1072 { | |
1073 gaim_proxy_connect_data_disconnect(connect_data, | |
1074 _("Server closed the connection.")); | |
1075 return; | |
1076 } | |
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 | |
1090 connect_data->read_len += len; | |
1091 | |
1092 if(connect_data->read_len < 4) | |
1093 return; | |
1094 | |
1095 if ((buf[0] != 0x05) || (buf[1] != 0x00)) { | |
1096 if ((buf[0] == 0x05) && (buf[1] < 0x09)) { | |
1097 gaim_debug_error("socks5 proxy", socks5errors[buf[1]]); | |
1098 gaim_proxy_connect_data_disconnect(connect_data, | |
1099 socks5errors[buf[1]]); | |
1100 } else { | |
1101 gaim_debug_error("socks5 proxy", "Bad data.\n"); | |
1102 gaim_proxy_connect_data_disconnect(connect_data, | |
1103 _("Received invalid data on connection with server.")); | |
1104 } | |
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 */ | |
1111 if(connect_data->read_len < 4 + 4) | |
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. */ | |
1118 if(connect_data->read_len < 4 + 1) | |
1119 return; | |
1120 buf += 4 + 1; | |
1121 if(connect_data->read_len < 4 + 1 + buf[0]) | |
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 */ | |
1126 if(connect_data->read_len < 4 + 16) | |
1127 return; | |
1128 buf += 4 + 16; | |
1129 break; | |
1130 } | |
1131 | |
1132 if(connect_data->read_len < (buf - connect_data->read_buffer) + 2) | |
1133 return; | |
1134 | |
1135 /* Skip past BND.PORT */ | |
1136 buf += 2; | |
1137 | |
1138 gaim_proxy_connect_data_connected(connect_data); | |
1139 } | |
1140 | |
1141 static void | |
1142 s5_sendconnect(gpointer data, int source) | |
1143 { | |
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; | |
1149 | |
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; | |
1158 | |
1159 connect_data->read_cb = s5_canread_again; | |
1160 | |
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); | |
1163 } | |
1164 | |
1165 static void | |
1166 s5_readauth(gpointer data, gint source, GaimInputCondition cond) | |
1167 { | |
1168 GaimProxyConnectData *connect_data = data; | |
1169 int len; | |
1170 | |
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; | |
1175 } | |
1176 | |
1177 gaim_debug_info("socks5 proxy", "Got auth response.\n"); | |
1178 | |
1179 len = read(connect_data->fd, connect_data->read_buffer + connect_data->read_len, | |
1180 connect_data->read_buf_len - connect_data->read_len); | |
1181 | |
1182 if (len == 0) | |
1183 { | |
1184 gaim_proxy_connect_data_disconnect(connect_data, | |
1185 _("Server closed the connection.")); | |
1186 return; | |
1187 } | |
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 | |
1201 connect_data->read_len += len; | |
1202 if (connect_data->read_len < 2) | |
1203 return; | |
1204 | |
1205 gaim_input_remove(connect_data->inpa); | |
1206 connect_data->inpa = 0; | |
1207 | |
1208 if ((connect_data->read_buffer[0] != 0x01) || (connect_data->read_buffer[1] != 0x00)) { | |
1209 gaim_proxy_connect_data_disconnect(connect_data, | |
1210 _("Received invalid data on connection with server.")); | |
1211 return; | |
1212 } | |
1213 | |
1214 g_free(connect_data->read_buffer); | |
1215 connect_data->read_buffer = NULL; | |
1216 | |
1217 s5_sendconnect(connect_data, connect_data->fd); | |
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; | |
1268 GaimProxyConnectData *connect_data = data; | |
1269 int len, navas, currentav; | |
1270 | |
1271 gaim_debug(GAIM_DEBUG_INFO, "socks5 proxy", "Got CHAP response.\n"); | |
1272 | |
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; | |
1277 } | |
1278 | |
1279 len = read(connect_data->fd, connect_data->read_buffer + connect_data->read_len, | |
1280 connect_data->read_buf_len - connect_data->read_len); | |
1281 | |
1282 if (len == 0) | |
1283 { | |
1284 gaim_proxy_connect_data_disconnect(connect_data, | |
1285 _("Server closed the connection.")); | |
1286 return; | |
1287 } | |
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 | |
1301 connect_data->read_len += len; | |
1302 if (connect_data->read_len < 2) | |
1303 return; | |
1304 | |
1305 cmdbuf = connect_data->read_buffer; | |
1306 | |
1307 if (*cmdbuf != 0x01) { | |
1308 gaim_proxy_connect_data_disconnect(connect_data, | |
1309 _("Received invalid data on connection with server.")); | |
1310 return; | |
1311 } | |
1312 cmdbuf++; | |
1313 | |
1314 navas = *cmdbuf; | |
1315 cmdbuf++; | |
1316 | |
1317 for (currentav = 0; currentav < navas; currentav++) { | |
1318 if (connect_data->read_len - (cmdbuf - connect_data->read_buffer) < 2) | |
1319 return; | |
1320 if (connect_data->read_len - (cmdbuf - connect_data->read_buffer) < cmdbuf[1]) | |
1321 return; | |
1322 buf = cmdbuf + 2; | |
1323 switch (cmdbuf[0]) { | |
1324 case 0x00: | |
1325 /* Did auth work? */ | |
1326 if (buf[0] == 0x00) { | |
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; | |
1331 /* Success */ | |
1332 s5_sendconnect(connect_data, connect_data->fd); | |
1333 return; | |
1334 } else { | |
1335 /* Failure */ | |
1336 gaim_debug_warning("proxy", | |
1337 "socks5 CHAP authentication " | |
1338 "failed. Disconnecting..."); | |
1339 gaim_proxy_connect_data_disconnect(connect_data, | |
1340 _("Authentication failed")); | |
1341 return; | |
1342 } | |
1343 break; | |
1344 case 0x03: | |
1345 /* Server wants our credentials */ | |
1346 | |
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; | |
1350 | |
1351 hmacmd5_chap(buf, cmdbuf[1], | |
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; | |
1358 | |
1359 gaim_input_remove(connect_data->inpa); | |
1360 g_free(connect_data->read_buffer); | |
1361 connect_data->read_buffer = NULL; | |
1362 | |
1363 connect_data->read_cb = s5_readchap; | |
1364 | |
1365 connect_data->inpa = gaim_input_add(connect_data->fd, | |
1366 GAIM_INPUT_WRITE, proxy_do_write, connect_data); | |
1367 | |
1368 proxy_do_write(connect_data, connect_data->fd, GAIM_INPUT_WRITE); | |
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..."); | |
1380 gaim_proxy_connect_data_disconnect(connect_data, | |
1381 _("Received invalid data on connection with server.")); | |
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 { | |
1396 GaimProxyConnectData *connect_data = data; | |
1397 int len; | |
1398 | |
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; | |
1403 } | |
1404 | |
1405 gaim_debug_info("socks5 proxy", "Able to read.\n"); | |
1406 | |
1407 len = read(connect_data->fd, connect_data->read_buffer + connect_data->read_len, | |
1408 connect_data->read_buf_len - connect_data->read_len); | |
1409 | |
1410 if (len == 0) | |
1411 { | |
1412 gaim_proxy_connect_data_disconnect(connect_data, | |
1413 _("Server closed the connection.")); | |
1414 return; | |
1415 } | |
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 | |
1429 connect_data->read_len += len; | |
1430 if (connect_data->read_len < 2) | |
1431 return; | |
1432 | |
1433 gaim_input_remove(connect_data->inpa); | |
1434 connect_data->inpa = 0; | |
1435 | |
1436 if ((connect_data->read_buffer[0] != 0x05) || (connect_data->read_buffer[1] == 0xff)) { | |
1437 gaim_proxy_connect_data_disconnect(connect_data, | |
1438 _("Received invalid data on connection with server.")); | |
1439 return; | |
1440 } | |
1441 | |
1442 if (connect_data->read_buffer[1] == 0x02) { | |
1443 gsize i, j; | |
1444 const char *u, *p; | |
1445 | |
1446 u = gaim_proxy_info_get_username(connect_data->gpi); | |
1447 p = gaim_proxy_info_get_password(connect_data->gpi); | |
1448 | |
1449 i = (u == NULL) ? 0 : strlen(u); | |
1450 j = (p == NULL) ? 0 : strlen(p); | |
1451 | |
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; | |
1455 | |
1456 connect_data->write_buffer[0] = 0x01; /* version 1 */ | |
1457 connect_data->write_buffer[1] = i; | |
1458 if (u != NULL) | |
1459 memcpy(connect_data->write_buffer + 2, u, i); | |
1460 connect_data->write_buffer[2 + i] = j; | |
1461 if (p != NULL) | |
1462 memcpy(connect_data->write_buffer + 2 + i + 1, p, j); | |
1463 | |
1464 g_free(connect_data->read_buffer); | |
1465 connect_data->read_buffer = NULL; | |
1466 | |
1467 connect_data->read_cb = s5_readauth; | |
1468 | |
1469 connect_data->inpa = gaim_input_add(connect_data->fd, GAIM_INPUT_WRITE, | |
1470 proxy_do_write, connect_data); | |
1471 | |
1472 proxy_do_write(connect_data, connect_data->fd, GAIM_INPUT_WRITE); | |
1473 | |
1474 return; | |
1475 } else if (connect_data->read_buffer[1] == 0x03) { | |
1476 gsize userlen; | |
1477 userlen = strlen(gaim_proxy_info_get_username(connect_data->gpi)); | |
1478 | |
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; | |
1482 | |
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); | |
1492 | |
1493 g_free(connect_data->read_buffer); | |
1494 connect_data->read_buffer = NULL; | |
1495 | |
1496 connect_data->read_cb = s5_readchap; | |
1497 | |
1498 connect_data->inpa = gaim_input_add(connect_data->fd, GAIM_INPUT_WRITE, | |
1499 proxy_do_write, connect_data); | |
1500 | |
1501 proxy_do_write(connect_data, connect_data->fd, GAIM_INPUT_WRITE); | |
1502 | |
1503 return; | |
1504 } else { | |
1505 g_free(connect_data->read_buffer); | |
1506 connect_data->read_buffer = NULL; | |
1507 | |
1508 s5_sendconnect(connect_data, connect_data->fd); | |
1509 } | |
1510 } | |
1511 | |
1512 static void | |
1513 s5_canwrite(gpointer data, gint source, GaimInputCondition cond) | |
1514 { | |
1515 unsigned char buf[5]; | |
1516 int i; | |
1517 GaimProxyConnectData *connect_data = data; | |
1518 socklen_t len; | |
1519 int error = ETIMEDOUT; | |
1520 int ret; | |
1521 | |
1522 gaim_debug_info("socks5 proxy", "Connected.\n"); | |
1523 | |
1524 if (connect_data->inpa > 0) | |
1525 { | |
1526 gaim_input_remove(connect_data->inpa); | |
1527 connect_data->inpa = 0; | |
1528 } | |
1529 | |
1530 len = sizeof(error); | |
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)); | |
1537 return; | |
1538 } | |
1539 | |
1540 i = 0; | |
1541 buf[0] = 0x05; /* SOCKS version 5 */ | |
1542 | |
1543 if (gaim_proxy_info_get_username(connect_data->gpi) != NULL) { | |
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 | |
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); | |
1559 | |
1560 connect_data->read_cb = s5_canread; | |
1561 | |
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); | |
1564 } | |
1565 | |
1566 static void | |
1567 proxy_connect_socks5(GaimProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen) | |
1568 { | |
1569 gaim_debug_info("proxy", | |
1570 "Connecting to %s:%d via %s:%d using SOCKS5\n", | |
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)); | |
1574 | |
1575 connect_data->fd = socket(addr->sa_family, SOCK_STREAM, 0); | |
1576 if (connect_data->fd < 0) | |
1577 { | |
1578 gaim_proxy_connect_data_disconnect_formatted(connect_data, | |
1579 _("Unable to create socket:\n%s"), strerror(errno)); | |
1580 return; | |
1581 } | |
1582 | |
1583 fcntl(connect_data->fd, F_SETFL, O_NONBLOCK); | |
1584 #ifndef _WIN32 | |
1585 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC); | |
1586 #endif | |
1587 | |
1588 if (connect(connect_data->fd, addr, addrlen) != 0) | |
1589 { | |
1590 if ((errno == EINPROGRESS) || (errno == EINTR)) | |
1591 { | |
1592 gaim_debug_info("socks5 proxy", "Connection in progress\n"); | |
1593 connect_data->inpa = gaim_input_add(connect_data->fd, | |
1594 GAIM_INPUT_WRITE, s5_canwrite, connect_data); | |
1595 } | |
1596 else | |
1597 { | |
1598 gaim_proxy_connect_data_disconnect(connect_data, strerror(errno)); | |
1599 } | |
1600 } | |
1601 else | |
1602 { | |
1603 gaim_debug_info("proxy", "Connected immediately.\n"); | |
1604 | |
1605 s5_canwrite(connect_data, connect_data->fd, GAIM_INPUT_WRITE); | |
1606 } | |
1607 } | |
1608 | |
1609 /** | |
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 | |
1612 * to connect to each one. This is called after the hostname is | |
1613 * resolved, and each time a connection attempt fails (assuming there | |
1614 * is another IP address to try). | |
1615 */ | |
1616 static void try_connect(GaimProxyConnectData *connect_data) | |
1617 { | |
1618 size_t addrlen; | |
1619 struct sockaddr *addr; | |
1620 char ipaddr[INET6_ADDRSTRLEN]; | |
1621 | |
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); | |
1626 | |
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); | |
1630 | |
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; | |
1635 | |
1636 case GAIM_PROXY_HTTP: | |
1637 proxy_connect_http(connect_data, addr, addrlen); | |
1638 break; | |
1639 | |
1640 case GAIM_PROXY_SOCKS4: | |
1641 proxy_connect_socks4(connect_data, addr, addrlen); | |
1642 break; | |
1643 | |
1644 case GAIM_PROXY_SOCKS5: | |
1645 proxy_connect_socks5(connect_data, addr, addrlen); | |
1646 break; | |
1647 | |
1648 case GAIM_PROXY_USE_ENVVAR: | |
1649 proxy_connect_http(connect_data, addr, addrlen); | |
1650 break; | |
1651 | |
1652 default: | |
1653 break; | |
1654 } | |
1655 | |
1656 g_free(addr); | |
1657 } | |
1658 | |
1659 static void | |
1660 connection_host_resolved(GSList *hosts, gpointer data, | |
1661 const char *error_message) | |
1662 { | |
1663 GaimProxyConnectData *connect_data; | |
1664 | |
1665 connect_data = data; | |
1666 connect_data->query_data = NULL; | |
1667 | |
1668 if (error_message != NULL) | |
1669 { | |
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")); | |
1677 return; | |
1678 } | |
1679 | |
1680 connect_data->hosts = hosts; | |
1681 | |
1682 try_connect(connect_data); | |
1683 } | |
1684 | |
1685 GaimProxyInfo * | |
1686 gaim_proxy_get_setup(GaimAccount *account) | |
1687 { | |
1688 GaimProxyInfo *gpi = NULL; | |
1689 const gchar *tmp; | |
1690 | |
1691 /* This is used as a fallback so we don't overwrite the selected proxy type */ | |
1692 static GaimProxyInfo *tmp_none_proxy_info = NULL; | |
1693 if (!tmp_none_proxy_info) { | |
1694 tmp_none_proxy_info = gaim_proxy_info_new(); | |
1695 gaim_proxy_info_set_type(tmp_none_proxy_info, GAIM_PROXY_NONE); | |
1696 } | |
1697 | |
1698 if (account && gaim_account_get_proxy_info(account) != NULL) { | |
1699 gpi = gaim_account_get_proxy_info(account); | |
1700 if (gaim_proxy_info_get_type(gpi) == GAIM_PROXY_USE_GLOBAL) | |
1701 gpi = NULL; | |
1702 } | |
1703 if (gpi == NULL) { | |
1704 if (gaim_running_gnome()) | |
1705 gpi = gaim_gnome_proxy_get_info(); | |
1706 else | |
1707 gpi = gaim_global_proxy_get_info(); | |
1708 } | |
1709 | |
1710 if (gaim_proxy_info_get_type(gpi) == GAIM_PROXY_USE_ENVVAR) { | |
1711 #ifdef _WIN32 | |
1712 wgaim_check_for_proxy_changes(); | |
1713 #endif | |
1714 if ((tmp = g_getenv("HTTP_PROXY")) != NULL || | |
1715 (tmp = g_getenv("http_proxy")) != NULL || | |
1716 (tmp = g_getenv("HTTPPROXY")) != NULL) { | |
1717 char *proxyhost, *proxyuser, *proxypasswd; | |
1718 int proxyport; | |
1719 | |
1720 /* http_proxy-format: | |
1721 * export http_proxy="http://user:passwd@your.proxy.server:port/" | |
1722 */ | |
1723 if(gaim_url_parse(tmp, &proxyhost, &proxyport, NULL, &proxyuser, &proxypasswd)) { | |
1724 gaim_proxy_info_set_host(gpi, proxyhost); | |
1725 g_free(proxyhost); | |
1726 | |
1727 gaim_proxy_info_set_username(gpi, proxyuser); | |
1728 g_free(proxyuser); | |
1729 | |
1730 gaim_proxy_info_set_password(gpi, proxypasswd); | |
1731 g_free(proxypasswd); | |
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)) | |
1738 proxyport = atoi(tmp); | |
1739 | |
1740 gaim_proxy_info_set_port(gpi, proxyport); | |
1741 | |
1742 /* XXX: Do we want to skip this step if user/password were part of url? */ | |
1743 if ((tmp = g_getenv("HTTP_PROXY_USER")) != NULL || | |
1744 (tmp = g_getenv("http_proxy_user")) != NULL || | |
1745 (tmp = g_getenv("HTTPPROXYUSER")) != NULL) | |
1746 gaim_proxy_info_set_username(gpi, tmp); | |
1747 | |
1748 if ((tmp = g_getenv("HTTP_PROXY_PASS")) != NULL || | |
1749 (tmp = g_getenv("http_proxy_pass")) != NULL || | |
1750 (tmp = g_getenv("HTTPPROXYPASS")) != NULL) | |
1751 gaim_proxy_info_set_password(gpi, tmp); | |
1752 | |
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"); | |
1757 gpi = tmp_none_proxy_info; | |
1758 } | |
1759 | |
1760 } | |
1761 | |
1762 return gpi; | |
1763 } | |
1764 | |
1765 GaimProxyConnectData * | |
1766 gaim_proxy_connect(void *handle, GaimAccount *account, | |
1767 const char *host, int port, | |
1768 GaimProxyConnectFunction connect_cb, gpointer data) | |
1769 { | |
1770 const char *connecthost = host; | |
1771 int connectport = port; | |
1772 GaimProxyConnectData *connect_data; | |
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 | |
1778 connect_data = g_new0(GaimProxyConnectData, 1); | |
1779 connect_data->fd = -1; | |
1780 connect_data->handle = handle; | |
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); | |
1786 | |
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)) { | |
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.")); | |
1792 gaim_proxy_connect_data_destroy(connect_data); | |
1793 return NULL; | |
1794 } | |
1795 | |
1796 switch (gaim_proxy_info_get_type(connect_data->gpi)) | |
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: | |
1805 connecthost = gaim_proxy_info_get_host(connect_data->gpi); | |
1806 connectport = gaim_proxy_info_get_port(connect_data->gpi); | |
1807 break; | |
1808 | |
1809 default: | |
1810 gaim_proxy_connect_data_destroy(connect_data); | |
1811 return NULL; | |
1812 } | |
1813 | |
1814 connect_data->query_data = gaim_dnsquery_a(connecthost, | |
1815 connectport, connection_host_resolved, connect_data); | |
1816 if (connect_data->query_data == NULL) | |
1817 { | |
1818 gaim_proxy_connect_data_destroy(connect_data); | |
1819 return NULL; | |
1820 } | |
1821 | |
1822 handles = g_slist_prepend(handles, connect_data); | |
1823 | |
1824 return connect_data; | |
1825 } | |
1826 | |
1827 /* | |
1828 * Combine some of this code with gaim_proxy_connect() | |
1829 */ | |
1830 GaimProxyConnectData * | |
1831 gaim_proxy_connect_socks5(void *handle, GaimProxyInfo *gpi, | |
1832 const char *host, int port, | |
1833 GaimProxyConnectFunction connect_cb, | |
1834 gpointer data) | |
1835 { | |
1836 GaimProxyConnectData *connect_data; | |
1837 | |
1838 g_return_val_if_fail(host != NULL, NULL); | |
1839 g_return_val_if_fail(port >= 0, NULL); | |
1840 g_return_val_if_fail(connect_cb != NULL, NULL); | |
1841 | |
1842 connect_data = g_new0(GaimProxyConnectData, 1); | |
1843 connect_data->fd = -1; | |
1844 connect_data->handle = handle; | |
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; | |
1850 | |
1851 connect_data->query_data = | |
1852 gaim_dnsquery_a(gaim_proxy_info_get_host(gpi), | |
1853 gaim_proxy_info_get_port(gpi), | |
1854 connection_host_resolved, connect_data); | |
1855 if (connect_data->query_data == NULL) | |
1856 { | |
1857 gaim_proxy_connect_data_destroy(connect_data); | |
1858 return NULL; | |
1859 } | |
1860 | |
1861 handles = g_slist_prepend(handles, connect_data); | |
1862 | |
1863 return connect_data; | |
1864 } | |
1865 | |
1866 void | |
1867 gaim_proxy_connect_cancel(GaimProxyConnectData *connect_data) | |
1868 { | |
1869 gaim_proxy_connect_data_disconnect(connect_data, NULL); | |
1870 gaim_proxy_connect_data_destroy(connect_data); | |
1871 } | |
1872 | |
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 | |
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 { | |
1963 while (handles != NULL) | |
1964 { | |
1965 gaim_proxy_connect_data_disconnect(handles->data, NULL); | |
1966 gaim_proxy_connect_data_destroy(handles->data); | |
1967 } | |
1968 } |