2 * @file proxy.c Proxy API
8 * Purple is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
28 /* this is a little piece of code to handle proxy connection */
29 /* it is intended to : 1st handle http proxy, using the CONNECT command
30 , 2nd provide an easy way to add socks support
31 , 3rd draw women to it like flies to honey */
43 struct _PurpleProxyConnectData
{
45 PurpleProxyConnectFunction connect_cb
;
53 PurpleDnsQueryData
*query_data
;
56 * This contains alternating length/char* values. The char*
57 * values need to be freed when removed from the linked list.
62 * All of the following variables are used when establishing a
63 * connection through a proxy.
68 PurpleInputFunction read_cb
;
74 static const char * const socks5errors
[] = {
76 "general SOCKS server failure\n",
77 "connection not allowed by ruleset\n",
78 "Network unreachable\n",
80 "Connection refused\n",
82 "Command not supported\n",
83 "Address type not supported\n"
86 static PurpleProxyInfo
*global_proxy_info
= NULL
;
88 static GSList
*handles
= NULL
;
90 static void try_connect(PurpleProxyConnectData
*connect_data
);
93 * TODO: Eventually (GObjectification) this bad boy will be removed, because it is
94 * a gross fix for a crashy problem.
96 #define PURPLE_PROXY_CONNECT_DATA_IS_VALID(connect_data) g_slist_find(handles, connect_data)
98 /**************************************************************************
100 **************************************************************************/
102 purple_proxy_info_new(void)
104 return g_new0(PurpleProxyInfo
, 1);
108 purple_proxy_info_destroy(PurpleProxyInfo
*info
)
110 g_return_if_fail(info
!= NULL
);
113 g_free(info
->username
);
114 g_free(info
->password
);
120 purple_proxy_info_set_type(PurpleProxyInfo
*info
, PurpleProxyType type
)
122 g_return_if_fail(info
!= NULL
);
128 purple_proxy_info_set_host(PurpleProxyInfo
*info
, const char *host
)
130 g_return_if_fail(info
!= NULL
);
133 info
->host
= g_strdup(host
);
137 purple_proxy_info_set_port(PurpleProxyInfo
*info
, int port
)
139 g_return_if_fail(info
!= NULL
);
145 purple_proxy_info_set_username(PurpleProxyInfo
*info
, const char *username
)
147 g_return_if_fail(info
!= NULL
);
149 g_free(info
->username
);
150 info
->username
= g_strdup(username
);
154 purple_proxy_info_set_password(PurpleProxyInfo
*info
, const char *password
)
156 g_return_if_fail(info
!= NULL
);
158 g_free(info
->password
);
159 info
->password
= g_strdup(password
);
163 purple_proxy_info_get_type(const PurpleProxyInfo
*info
)
165 g_return_val_if_fail(info
!= NULL
, PURPLE_PROXY_NONE
);
171 purple_proxy_info_get_host(const PurpleProxyInfo
*info
)
173 g_return_val_if_fail(info
!= NULL
, NULL
);
179 purple_proxy_info_get_port(const PurpleProxyInfo
*info
)
181 g_return_val_if_fail(info
!= NULL
, 0);
187 purple_proxy_info_get_username(const PurpleProxyInfo
*info
)
189 g_return_val_if_fail(info
!= NULL
, NULL
);
191 return info
->username
;
195 purple_proxy_info_get_password(const PurpleProxyInfo
*info
)
197 g_return_val_if_fail(info
!= NULL
, NULL
);
199 return info
->password
;
202 /**************************************************************************
204 **************************************************************************/
206 purple_global_proxy_get_info(void)
208 return global_proxy_info
;
212 purple_global_proxy_set_info(PurpleProxyInfo
*info
)
214 g_return_if_fail(info
!= NULL
);
216 purple_proxy_info_destroy(global_proxy_info
);
218 global_proxy_info
= info
;
221 static PurpleProxyInfo
*
222 purple_gnome_proxy_get_info(void)
224 static PurpleProxyInfo info
= {0, NULL
, 0, NULL
, NULL
};
225 gboolean use_same_proxy
= FALSE
;
226 gchar
*tmp
, *err
= NULL
;
228 tmp
= g_find_program_in_path("gconftool-2");
230 return purple_global_proxy_get_info();
235 /* Check whether to use a proxy. */
236 if (!g_spawn_command_line_sync("gconftool-2 -g /system/proxy/mode",
237 &tmp
, &err
, NULL
, NULL
))
238 return purple_global_proxy_get_info();
242 if (purple_strequal(tmp
, "none\n")) {
243 info
.type
= PURPLE_PROXY_NONE
;
248 if (!purple_strequal(tmp
, "manual\n")) {
249 /* Unknown setting. Fallback to using our global proxy settings. */
251 return purple_global_proxy_get_info();
257 /* Free the old fields */
263 g_free(info
.username
);
264 info
.username
= NULL
;
267 g_free(info
.password
);
268 info
.password
= NULL
;
271 if (!g_spawn_command_line_sync("gconftool-2 -g /system/http_proxy/use_same_proxy",
272 &tmp
, &err
, NULL
, NULL
))
273 return purple_global_proxy_get_info();
277 if (purple_strequal(tmp
, "true\n"))
278 use_same_proxy
= TRUE
;
282 if (!use_same_proxy
) {
283 if (!g_spawn_command_line_sync("gconftool-2 -g /system/proxy/socks_host",
284 &info
.host
, &err
, NULL
, NULL
))
285 return purple_global_proxy_get_info();
290 if(info
.host
!= NULL
)
291 g_strchomp(info
.host
);
293 if (!use_same_proxy
&& (info
.host
!= NULL
) && (*info
.host
!= '\0')) {
294 info
.type
= PURPLE_PROXY_SOCKS5
;
295 if (!g_spawn_command_line_sync("gconftool-2 -g /system/proxy/socks_port",
296 &tmp
, &err
, NULL
, NULL
))
300 return purple_global_proxy_get_info();
303 info
.port
= atoi(tmp
);
307 if (!g_spawn_command_line_sync("gconftool-2 -g /system/http_proxy/host",
308 &info
.host
, &err
, NULL
, NULL
))
309 return purple_global_proxy_get_info();
313 /* If we get this far then we know we're using an HTTP proxy */
314 info
.type
= PURPLE_PROXY_HTTP
;
316 g_strchomp(info
.host
);
317 if (*info
.host
== '\0')
319 purple_debug_info("proxy", "Gnome proxy settings are set to "
320 "'manual' but no suitable proxy server is specified. Using "
321 "Pidgin's proxy settings instead.\n");
324 return purple_global_proxy_get_info();
327 if (!g_spawn_command_line_sync("gconftool-2 -g /system/http_proxy/authentication_user",
328 &info
.username
, &err
, NULL
, NULL
))
332 return purple_global_proxy_get_info();
336 g_strchomp(info
.username
);
338 if (!g_spawn_command_line_sync("gconftool-2 -g /system/http_proxy/authentication_password",
339 &info
.password
, &err
, NULL
, NULL
))
343 g_free(info
.username
);
344 info
.username
= NULL
;
345 return purple_global_proxy_get_info();
349 g_strchomp(info
.password
);
351 if (!g_spawn_command_line_sync("gconftool-2 -g /system/http_proxy/port",
352 &tmp
, &err
, NULL
, NULL
))
356 g_free(info
.username
);
357 info
.username
= NULL
;
358 g_free(info
.password
);
359 info
.password
= NULL
;
360 return purple_global_proxy_get_info();
363 info
.port
= atoi(tmp
);
372 typedef BOOL (CALLBACK
* LPFNWINHTTPGETIEPROXYCONFIG
)(/*IN OUT*/ WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
* pProxyConfig
);
374 /* This modifies "host" in-place evilly */
376 _proxy_fill_hostinfo(PurpleProxyInfo
*info
, char *host
, int default_port
)
378 int port
= default_port
;
381 d
= g_strrstr(host
, ":");
386 sscanf(d
, "%d", &port
);
388 purple_proxy_info_set_host(info
, host
);
389 purple_proxy_info_set_port(info
, port
);
392 static PurpleProxyInfo
*
393 purple_win32_proxy_get_info(void)
395 static LPFNWINHTTPGETIEPROXYCONFIG MyWinHttpGetIEProxyConfig
= NULL
;
396 static gboolean loaded
= FALSE
;
397 static PurpleProxyInfo info
= {0, NULL
, 0, NULL
, NULL
};
399 WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ie_proxy_config
;
403 MyWinHttpGetIEProxyConfig
= (LPFNWINHTTPGETIEPROXYCONFIG
)
404 wpurple_find_and_loadproc("winhttp.dll", "WinHttpGetIEProxyConfigForCurrentUser");
405 if (!MyWinHttpGetIEProxyConfig
)
406 purple_debug_warning("proxy", "Unable to read Windows Proxy Settings.\n");
409 if (!MyWinHttpGetIEProxyConfig
)
412 ZeroMemory(&ie_proxy_config
, sizeof(ie_proxy_config
));
413 if (!MyWinHttpGetIEProxyConfig(&ie_proxy_config
)) {
414 purple_debug_error("proxy", "Error reading Windows Proxy Settings(%lu).\n", GetLastError());
418 /* We can't do much if it is autodetect*/
419 if (ie_proxy_config
.fAutoDetect
) {
420 purple_debug_error("proxy", "Windows Proxy Settings set to autodetect (not supported).\n");
422 /* TODO: For 3.0.0 we'll revisit this (maybe)*/
426 } else if (ie_proxy_config
.lpszProxy
) {
427 gchar
*proxy_list
= g_utf16_to_utf8(ie_proxy_config
.lpszProxy
, -1,
430 /* We can't do anything about the bypass list, as we don't have the url */
431 /* TODO: For 3.0.0 we'll revisit this*/
433 /* There are proxy settings for several protocols */
434 if (proxy_list
&& *proxy_list
) {
435 char *specific
= NULL
, *tmp
;
437 /* If there is only a global proxy, which means "HTTP" */
438 if (!strchr(proxy_list
, ';') || (specific
= g_strstr_len(proxy_list
, -1, "http=")) != NULL
) {
441 specific
+= strlen("http=");
442 tmp
= strchr(specific
, ';');
445 /* specific now points the proxy server (and port) */
447 specific
= proxy_list
;
449 purple_proxy_info_set_type(&info
, PURPLE_PROXY_HTTP
);
450 _proxy_fill_hostinfo(&info
, specific
, 80);
451 /* TODO: is there a way to set the username/password? */
452 purple_proxy_info_set_username(&info
, NULL
);
453 purple_proxy_info_set_password(&info
, NULL
);
455 purple_debug_info("proxy", "Windows Proxy Settings: HTTP proxy: '%s:%d'.\n",
456 purple_proxy_info_get_host(&info
),
457 purple_proxy_info_get_port(&info
));
459 } else if ((specific
= g_strstr_len(proxy_list
, -1, "socks=")) != NULL
) {
461 specific
+= strlen("socks=");
462 tmp
= strchr(specific
, ';');
465 /* specific now points the proxy server (and port) */
467 purple_proxy_info_set_type(&info
, PURPLE_PROXY_SOCKS5
);
468 _proxy_fill_hostinfo(&info
, specific
, 1080);
469 /* TODO: is there a way to set the username/password? */
470 purple_proxy_info_set_username(&info
, NULL
);
471 purple_proxy_info_set_password(&info
, NULL
);
473 purple_debug_info("proxy", "Windows Proxy Settings: SOCKS5 proxy: '%s:%d'.\n",
474 purple_proxy_info_get_host(&info
),
475 purple_proxy_info_get_port(&info
));
479 purple_debug_info("proxy", "Windows Proxy Settings: No supported proxy specified.\n");
481 purple_proxy_info_set_type(&info
, PURPLE_PROXY_NONE
);
486 /* TODO: Fix API to be able look at proxy bypass settings */
490 purple_debug_info("proxy", "No Windows proxy set.\n");
491 purple_proxy_info_set_type(&info
, PURPLE_PROXY_NONE
);
494 if (ie_proxy_config
.lpszAutoConfigUrl
)
495 GlobalFree(ie_proxy_config
.lpszAutoConfigUrl
);
496 if (ie_proxy_config
.lpszProxy
)
497 GlobalFree(ie_proxy_config
.lpszProxy
);
498 if (ie_proxy_config
.lpszProxyBypass
)
499 GlobalFree(ie_proxy_config
.lpszProxyBypass
);
506 /**************************************************************************
508 **************************************************************************/
511 * Whoever calls this needs to have called
512 * purple_proxy_connect_data_disconnect() beforehand.
515 purple_proxy_connect_data_destroy(PurpleProxyConnectData
*connect_data
)
517 handles
= g_slist_remove(handles
, connect_data
);
519 if (connect_data
->query_data
!= NULL
)
520 purple_dnsquery_destroy(connect_data
->query_data
);
522 while (connect_data
->hosts
!= NULL
)
524 /* Discard the length... */
525 connect_data
->hosts
= g_slist_remove(connect_data
->hosts
, connect_data
->hosts
->data
);
526 /* Free the address... */
527 g_free(connect_data
->hosts
->data
);
528 connect_data
->hosts
= g_slist_remove(connect_data
->hosts
, connect_data
->hosts
->data
);
531 g_free(connect_data
->host
);
532 g_free(connect_data
);
536 * Free all information dealing with a connection attempt and
537 * reset the connect_data to prepare for it to try to connect
538 * to another IP address.
540 * If an error message is passed in, then we know the connection
541 * attempt failed. If the connection attempt failed and
542 * connect_data->hosts is not empty then we try the next IP address.
543 * If the connection attempt failed and we have no more hosts
544 * try try then we call the callback with the given error message,
545 * then destroy the connect_data.
547 * @param error_message An error message explaining why the connection
548 * failed. This will be passed to the callback function
549 * specified in the call to purple_proxy_connect(). If the
550 * connection was successful then pass in null.
553 purple_proxy_connect_data_disconnect(PurpleProxyConnectData
*connect_data
, const gchar
*error_message
)
555 if (connect_data
->inpa
> 0)
557 purple_input_remove(connect_data
->inpa
);
558 connect_data
->inpa
= 0;
561 if (connect_data
->fd
>= 0)
563 close(connect_data
->fd
);
564 connect_data
->fd
= -1;
567 g_free(connect_data
->write_buffer
);
568 connect_data
->write_buffer
= NULL
;
570 g_free(connect_data
->read_buffer
);
571 connect_data
->read_buffer
= NULL
;
573 if (error_message
!= NULL
)
575 purple_debug_error("proxy", "Connection attempt failed: %s\n",
577 if (connect_data
->hosts
!= NULL
)
578 try_connect(connect_data
);
581 /* Everything failed! Tell the originator of the request. */
582 connect_data
->connect_cb(connect_data
->data
, -1, error_message
);
583 purple_proxy_connect_data_destroy(connect_data
);
589 * This calls purple_proxy_connect_data_disconnect(), but it lets you
590 * specify the error_message using a printf()-like syntax.
593 purple_proxy_connect_data_disconnect_formatted(PurpleProxyConnectData
*connect_data
, const char *format
, ...)
598 va_start(args
, format
);
599 tmp
= g_strdup_vprintf(format
, args
);
602 purple_proxy_connect_data_disconnect(connect_data
, tmp
);
607 purple_proxy_connect_data_connected(PurpleProxyConnectData
*connect_data
)
609 purple_debug_info("proxy", "Connected to %s:%d.\n",
610 connect_data
->host
, connect_data
->port
);
612 connect_data
->connect_cb(connect_data
->data
, connect_data
->fd
, NULL
);
615 * We've passed the file descriptor to the protocol, so it's no longer
616 * our responsibility, and we should be careful not to free it when
617 * we destroy the connect_data.
619 connect_data
->fd
= -1;
621 purple_proxy_connect_data_disconnect(connect_data
, NULL
);
622 purple_proxy_connect_data_destroy(connect_data
);
626 socket_ready_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
628 PurpleProxyConnectData
*connect_data
= data
;
632 /* If the socket-connected message had already been triggered when connect_data
633 * was destroyed via purple_proxy_connect_cancel(), we may get here with a freed connect_data.
635 if (!PURPLE_PROXY_CONNECT_DATA_IS_VALID(connect_data
))
638 purple_debug_info("proxy", "Connecting to %s:%d.\n",
639 connect_data
->host
, connect_data
->port
);
642 * purple_input_get_error after a non-blocking connect returns -1 if something is
643 * really messed up (bad descriptor, usually). Otherwise, it returns 0 and
644 * error holds what connect would have returned if it blocked until now.
645 * Thus, error == 0 is success, error == EINPROGRESS means "try again",
646 * and anything else is a real error.
648 * (error == EINPROGRESS can happen after a select because the kernel can
649 * be overly optimistic sometimes. select is just a hint that you might be
650 * able to do something.)
652 ret
= purple_input_get_error(connect_data
->fd
, &error
);
654 if (ret
== 0 && error
== EINPROGRESS
) {
655 /* No worries - we'll be called again later */
656 /* TODO: Does this ever happen? */
657 purple_debug_info("proxy", "(ret == 0 && error == EINPROGRESS)\n");
661 if (ret
!= 0 || error
!= 0) {
664 purple_debug_error("proxy", "Error connecting to %s:%d (%s).\n",
665 connect_data
->host
, connect_data
->port
, g_strerror(error
));
667 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(error
));
671 purple_proxy_connect_data_connected(connect_data
);
675 clean_connect(gpointer data
)
677 purple_proxy_connect_data_connected(data
);
683 proxy_connect_udp_none(PurpleProxyConnectData
*connect_data
, struct sockaddr
*addr
, socklen_t addrlen
)
687 purple_debug_info("proxy", "UDP Connecting to %s:%d with no proxy\n",
688 connect_data
->host
, connect_data
->port
);
690 connect_data
->fd
= socket(addr
->sa_family
, SOCK_DGRAM
, 0);
691 if (connect_data
->fd
< 0)
693 purple_proxy_connect_data_disconnect_formatted(connect_data
,
694 _("Unable to create socket: %s"), g_strerror(errno
));
698 flags
= fcntl(connect_data
->fd
, F_GETFL
);
699 fcntl(connect_data
->fd
, F_SETFL
, flags
| O_NONBLOCK
);
701 fcntl(connect_data
->fd
, F_SETFD
, FD_CLOEXEC
);
704 if (connect(connect_data
->fd
, addr
, addrlen
) != 0)
706 if ((errno
== EINPROGRESS
) || (errno
== EINTR
))
708 purple_debug_info("proxy", "UDP Connection in progress\n");
709 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
710 PURPLE_INPUT_WRITE
, socket_ready_cb
, connect_data
);
714 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(errno
));
720 * The connection happened IMMEDIATELY... strange, but whatever.
722 int error
= ETIMEDOUT
;
725 purple_debug_info("proxy", "UDP Connected immediately.\n");
727 ret
= purple_input_get_error(connect_data
->fd
, &error
);
728 if ((ret
!= 0) || (error
!= 0))
732 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(error
));
737 * We want to call the "connected" callback eventually, but we
738 * don't want to call it before we return, just in case.
740 purple_timeout_add(10, clean_connect
, connect_data
);
745 proxy_connect_none(PurpleProxyConnectData
*connect_data
, struct sockaddr
*addr
, socklen_t addrlen
)
749 purple_debug_info("proxy", "Connecting to %s:%d with no proxy\n",
750 connect_data
->host
, connect_data
->port
);
752 connect_data
->fd
= socket(addr
->sa_family
, SOCK_STREAM
, 0);
753 if (connect_data
->fd
< 0)
755 purple_proxy_connect_data_disconnect_formatted(connect_data
,
756 _("Unable to create socket: %s"), g_strerror(errno
));
760 flags
= fcntl(connect_data
->fd
, F_GETFL
);
761 fcntl(connect_data
->fd
, F_SETFL
, flags
| O_NONBLOCK
);
763 fcntl(connect_data
->fd
, F_SETFD
, FD_CLOEXEC
);
766 if (connect(connect_data
->fd
, addr
, addrlen
) != 0)
768 if ((errno
== EINPROGRESS
) || (errno
== EINTR
))
770 purple_debug_info("proxy", "Connection in progress\n");
771 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
772 PURPLE_INPUT_WRITE
, socket_ready_cb
, connect_data
);
776 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(errno
));
782 * The connection happened IMMEDIATELY... strange, but whatever.
784 int error
= ETIMEDOUT
;
787 purple_debug_info("proxy", "Connected immediately.\n");
789 ret
= purple_input_get_error(connect_data
->fd
, &error
);
790 if ((ret
!= 0) || (error
!= 0))
794 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(error
));
799 * We want to call the "connected" callback eventually, but we
800 * don't want to call it before we return, just in case.
802 purple_timeout_add(10, clean_connect
, connect_data
);
807 * This is a utility function used by the HTTP, SOCKS4 and SOCKS5
808 * connect functions. It writes data from a buffer to a socket.
809 * When all the data is written it sets up a watcher to read a
810 * response and call a specified function.
813 proxy_do_write(gpointer data
, gint source
, PurpleInputCondition cond
)
815 PurpleProxyConnectData
*connect_data
;
816 const guchar
*request
;
821 request
= connect_data
->write_buffer
+ connect_data
->written_len
;
822 request_len
= connect_data
->write_buf_len
- connect_data
->written_len
;
824 ret
= write(connect_data
->fd
, request
, request_len
);
832 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(errno
));
835 if (ret
< request_len
) {
836 connect_data
->written_len
+= ret
;
840 /* We're done writing data! Wait for a response. */
841 g_free(connect_data
->write_buffer
);
842 connect_data
->write_buffer
= NULL
;
843 purple_input_remove(connect_data
->inpa
);
844 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
845 PURPLE_INPUT_READ
, connect_data
->read_cb
, connect_data
);
848 #define HTTP_GOODSTRING "HTTP/1.0 200"
849 #define HTTP_GOODSTRING2 "HTTP/1.1 200"
852 * We're using an HTTP proxy for a non-port 80 tunnel. Read the
853 * response to the CONNECT request.
856 http_canread(gpointer data
, gint source
, PurpleInputCondition cond
)
858 int len
, headers_len
, status
= 0;
860 PurpleProxyConnectData
*connect_data
= data
;
864 if (connect_data
->read_buffer
== NULL
) {
865 connect_data
->read_buf_len
= 8192;
866 connect_data
->read_buffer
= g_malloc(connect_data
->read_buf_len
);
867 connect_data
->read_len
= 0;
870 p
= (char *)connect_data
->read_buffer
+ connect_data
->read_len
;
871 max_read
= connect_data
->read_buf_len
- connect_data
->read_len
- 1;
873 len
= read(connect_data
->fd
, p
, max_read
);
876 purple_proxy_connect_data_disconnect(connect_data
,
877 _("Server closed the connection"));
887 purple_proxy_connect_data_disconnect_formatted(connect_data
,
888 _("Lost connection with server: %s"), g_strerror(errno
));
892 connect_data
->read_len
+= len
;
895 p
= g_strstr_len((const gchar
*)connect_data
->read_buffer
,
896 connect_data
->read_len
, "\r\n\r\n");
899 headers_len
= (p
- (char *)connect_data
->read_buffer
) + 4;
900 } else if(len
== max_read
)
905 error
= strncmp((const char *)connect_data
->read_buffer
, "HTTP/", 5) != 0;
908 p
= (char *)connect_data
->read_buffer
+ 5;
909 major
= strtol(p
, &p
, 10);
910 error
= (major
== 0) || (*p
!= '.');
914 minor
= strtol(p
, &p
, 10);
918 status
= strtol(p
, &p
, 10);
924 /* Read the contents */
925 p
= g_strrstr((const gchar
*)connect_data
->read_buffer
, "Content-Length: ");
930 p
+= strlen("Content-Length: ");
931 tmp
= strchr(p
, '\r');
938 /* Compensate for what has already been read */
939 len
-= connect_data
->read_len
- headers_len
;
940 /* I'm assuming that we're doing this to prevent the server from
941 complaining / breaking since we don't read the whole page */
943 /* TODO: deal with EAGAIN (and other errors) better */
944 if (read(connect_data
->fd
, &tmpc
, 1) < 0 && errno
!= EAGAIN
)
950 purple_proxy_connect_data_disconnect_formatted(connect_data
,
951 _("Unable to parse response from HTTP proxy: %s"),
952 connect_data
->read_buffer
);
955 else if (status
!= 200) {
956 purple_debug_error("proxy",
957 "Proxy server replied with:\n%s\n",
958 connect_data
->read_buffer
);
960 if (status
== 407 /* Proxy Auth */) {
964 header
= g_strrstr((const gchar
*)connect_data
->read_buffer
,
965 "Proxy-Authenticate: NTLM");
966 if (header
!= NULL
) {
967 const char *header_end
= header
+ strlen("Proxy-Authenticate: NTLM");
968 const char *domain
= purple_proxy_info_get_username(connect_data
->gpi
);
969 char *username
= NULL
, hostname
[256];
973 ret
= gethostname(hostname
, sizeof(hostname
));
974 hostname
[sizeof(hostname
) - 1] = '\0';
975 if (ret
< 0 || hostname
[0] == '\0') {
976 purple_debug_warning("proxy", "gethostname() failed -- is your hostname set?");
977 strcpy(hostname
, "localhost");
981 username
= (char*) strchr(domain
, '\\');
982 if (username
== NULL
) {
983 purple_proxy_connect_data_disconnect_formatted(connect_data
,
984 _("HTTP proxy connection error %d"), status
);
989 /* Is there a message? */
990 if (*header_end
== ' ') {
991 /* Check for Type-2 */
992 char *tmp
= (char*) header
;
997 while(*tmp
!= '\r' && *tmp
!= '\0') tmp
++;
999 nonce
= purple_ntlm_parse_type2(header_end
, NULL
);
1000 response
= purple_ntlm_gen_type3(username
,
1001 (gchar
*) purple_proxy_info_get_password(connect_data
->gpi
),
1003 domain
, nonce
, NULL
);
1005 } else /* Empty message */
1006 response
= purple_ntlm_gen_type1(hostname
, domain
);
1010 request
= g_strdup_printf(
1011 "CONNECT %s:%d HTTP/1.1\r\n"
1013 "Proxy-Authorization: NTLM %s\r\n"
1014 "Proxy-Connection: Keep-Alive\r\n\r\n",
1015 connect_data
->host
, connect_data
->port
,
1016 connect_data
->host
, connect_data
->port
,
1021 } else if((header
= g_strrstr((const char *)connect_data
->read_buffer
, "Proxy-Authenticate: Basic"))) {
1023 const char *username
, *password
;
1025 username
= purple_proxy_info_get_username(connect_data
->gpi
);
1026 password
= purple_proxy_info_get_password(connect_data
->gpi
);
1028 t1
= g_strdup_printf("%s:%s",
1029 username
? username
: "",
1030 password
? password
: "");
1031 t2
= purple_base64_encode((guchar
*)t1
, strlen(t1
));
1034 request
= g_strdup_printf(
1035 "CONNECT %s:%d HTTP/1.1\r\n"
1037 "Proxy-Authorization: Basic %s\r\n",
1038 connect_data
->host
, connect_data
->port
,
1039 connect_data
->host
, connect_data
->port
,
1045 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1046 _("HTTP proxy connection error %d"), status
);
1050 purple_input_remove(connect_data
->inpa
);
1051 g_free(connect_data
->read_buffer
);
1052 connect_data
->read_buffer
= NULL
;
1054 connect_data
->write_buffer
= (guchar
*)request
;
1055 connect_data
->write_buf_len
= strlen(request
);
1056 connect_data
->written_len
= 0;
1058 connect_data
->read_cb
= http_canread
;
1060 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
1061 PURPLE_INPUT_WRITE
, proxy_do_write
, connect_data
);
1063 proxy_do_write(connect_data
, connect_data
->fd
, cond
);
1068 if (status
== 403) {
1070 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1071 _("Access denied: HTTP proxy server forbids port %d tunneling"),
1072 connect_data
->port
);
1074 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1075 _("HTTP proxy connection error %d"), status
);
1078 purple_input_remove(connect_data
->inpa
);
1079 connect_data
->inpa
= 0;
1080 g_free(connect_data
->read_buffer
);
1081 connect_data
->read_buffer
= NULL
;
1082 purple_debug_info("proxy", "HTTP proxy connection established\n");
1083 purple_proxy_connect_data_connected(connect_data
);
1089 http_start_connect_tunneling(PurpleProxyConnectData
*connect_data
) {
1093 purple_debug_info("proxy", "Using CONNECT tunneling for %s:%d\n",
1094 connect_data
->host
, connect_data
->port
);
1096 request
= g_string_sized_new(4096);
1097 g_string_append_printf(request
,
1098 "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n",
1099 connect_data
->host
, connect_data
->port
,
1100 connect_data
->host
, connect_data
->port
);
1102 if (purple_proxy_info_get_username(connect_data
->gpi
) != NULL
)
1104 char *t1
, *t2
, *ntlm_type1
;
1107 ret
= gethostname(hostname
, sizeof(hostname
));
1108 hostname
[sizeof(hostname
) - 1] = '\0';
1109 if (ret
< 0 || hostname
[0] == '\0') {
1110 purple_debug_warning("proxy", "gethostname() failed -- is your hostname set?");
1111 strcpy(hostname
, "localhost");
1114 t1
= g_strdup_printf("%s:%s",
1115 purple_proxy_info_get_username(connect_data
->gpi
),
1116 purple_proxy_info_get_password(connect_data
->gpi
) ?
1117 purple_proxy_info_get_password(connect_data
->gpi
) : "");
1118 t2
= purple_base64_encode((const guchar
*)t1
, strlen(t1
));
1121 ntlm_type1
= purple_ntlm_gen_type1(hostname
, "");
1123 g_string_append_printf(request
,
1124 "Proxy-Authorization: Basic %s\r\n"
1125 "Proxy-Authorization: NTLM %s\r\n"
1126 "Proxy-Connection: Keep-Alive\r\n",
1132 g_string_append(request
, "\r\n");
1134 connect_data
->write_buf_len
= request
->len
;
1135 connect_data
->write_buffer
= (guchar
*)g_string_free(request
, FALSE
);
1136 connect_data
->written_len
= 0;
1137 connect_data
->read_cb
= http_canread
;
1139 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
1140 PURPLE_INPUT_WRITE
, proxy_do_write
, connect_data
);
1141 proxy_do_write(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1145 http_canwrite(gpointer data
, gint source
, PurpleInputCondition cond
) {
1146 PurpleProxyConnectData
*connect_data
= data
;
1147 int ret
, error
= ETIMEDOUT
;
1149 purple_debug_info("proxy", "Connected to %s:%d.\n",
1150 connect_data
->host
, connect_data
->port
);
1152 if (connect_data
->inpa
> 0) {
1153 purple_input_remove(connect_data
->inpa
);
1154 connect_data
->inpa
= 0;
1157 ret
= purple_input_get_error(connect_data
->fd
, &error
);
1158 if (ret
!= 0 || error
!= 0) {
1161 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(error
));
1165 if (connect_data
->port
== 80) {
1167 * If we're trying to connect to something running on
1168 * port 80 then we assume the traffic using this
1169 * connection is going to be HTTP traffic. If it's
1170 * not then this will fail (uglily). But it's good
1171 * to avoid using the CONNECT method because it's
1172 * not always allowed.
1174 purple_debug_info("proxy", "HTTP proxy connection established\n");
1175 purple_proxy_connect_data_connected(connect_data
);
1177 http_start_connect_tunneling(connect_data
);
1183 proxy_connect_http(PurpleProxyConnectData
*connect_data
, struct sockaddr
*addr
, socklen_t addrlen
)
1187 purple_debug_info("proxy",
1188 "Connecting to %s:%d via %s:%d using HTTP\n",
1189 connect_data
->host
, connect_data
->port
,
1190 (purple_proxy_info_get_host(connect_data
->gpi
) ? purple_proxy_info_get_host(connect_data
->gpi
) : "(null)"),
1191 purple_proxy_info_get_port(connect_data
->gpi
));
1193 connect_data
->fd
= socket(addr
->sa_family
, SOCK_STREAM
, 0);
1194 if (connect_data
->fd
< 0)
1196 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1197 _("Unable to create socket: %s"), g_strerror(errno
));
1201 flags
= fcntl(connect_data
->fd
, F_GETFL
);
1202 fcntl(connect_data
->fd
, F_SETFL
, flags
| O_NONBLOCK
);
1204 fcntl(connect_data
->fd
, F_SETFD
, FD_CLOEXEC
);
1207 if (connect(connect_data
->fd
, addr
, addrlen
) != 0) {
1208 if (errno
== EINPROGRESS
|| errno
== EINTR
) {
1209 purple_debug_info("proxy", "Connection in progress\n");
1211 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
1212 PURPLE_INPUT_WRITE
, http_canwrite
, connect_data
);
1214 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(errno
));
1216 purple_debug_info("proxy", "Connected immediately.\n");
1218 http_canwrite(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1223 s4_canread(gpointer data
, gint source
, PurpleInputCondition cond
)
1225 PurpleProxyConnectData
*connect_data
= data
;
1229 /* This is really not going to block under normal circumstances, but to
1230 * be correct, we deal with the unlikely scenario */
1232 if (connect_data
->read_buffer
== NULL
) {
1233 connect_data
->read_buf_len
= 12;
1234 connect_data
->read_buffer
= g_malloc(connect_data
->read_buf_len
);
1235 connect_data
->read_len
= 0;
1238 buf
= connect_data
->read_buffer
+ connect_data
->read_len
;
1239 max_read
= connect_data
->read_buf_len
- connect_data
->read_len
;
1241 len
= read(connect_data
->fd
, buf
, max_read
);
1243 if ((len
< 0 && errno
== EAGAIN
) || (len
> 0 && len
+ connect_data
->read_len
< 4))
1245 else if (len
+ connect_data
->read_len
>= 4) {
1246 if (connect_data
->read_buffer
[1] == 90) {
1247 purple_proxy_connect_data_connected(connect_data
);
1252 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(errno
));
1256 s4_host_resolved(GSList
*hosts
, gpointer data
, const char *error_message
)
1258 PurpleProxyConnectData
*connect_data
= data
;
1259 unsigned char packet
[9];
1260 struct sockaddr
*addr
;
1262 connect_data
->query_data
= NULL
;
1264 if (error_message
!= NULL
) {
1265 purple_proxy_connect_data_disconnect(connect_data
, error_message
);
1269 if (hosts
== NULL
) {
1270 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1271 _("Error resolving %s"), connect_data
->host
);
1275 /* Discard the length... */
1276 hosts
= g_slist_delete_link(hosts
, hosts
);
1278 hosts
= g_slist_delete_link(hosts
, hosts
);
1282 packet
[2] = connect_data
->port
>> 8;
1283 packet
[3] = connect_data
->port
& 0xff;
1284 memcpy(packet
+ 4, &((struct sockaddr_in
*)addr
)->sin_addr
.s_addr
, 4);
1289 /* We could try the other hosts, but hopefully that shouldn't be necessary */
1290 while (hosts
!= NULL
) {
1291 /* Discard the length... */
1292 hosts
= g_slist_delete_link(hosts
, hosts
);
1293 /* Free the address... */
1294 g_free(hosts
->data
);
1295 hosts
= g_slist_delete_link(hosts
, hosts
);
1298 connect_data
->write_buffer
= g_memdup(packet
, sizeof(packet
));
1299 connect_data
->write_buf_len
= sizeof(packet
);
1300 connect_data
->written_len
= 0;
1301 connect_data
->read_cb
= s4_canread
;
1303 connect_data
->inpa
= purple_input_add(connect_data
->fd
, PURPLE_INPUT_WRITE
, proxy_do_write
, connect_data
);
1305 proxy_do_write(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1309 s4_canwrite(gpointer data
, gint source
, PurpleInputCondition cond
)
1311 PurpleProxyConnectData
*connect_data
= data
;
1312 int error
= ETIMEDOUT
;
1315 purple_debug_info("socks4 proxy", "Connected.\n");
1317 if (connect_data
->inpa
> 0) {
1318 purple_input_remove(connect_data
->inpa
);
1319 connect_data
->inpa
= 0;
1322 ret
= purple_input_get_error(connect_data
->fd
, &error
);
1323 if ((ret
!= 0) || (error
!= 0)) {
1326 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(error
));
1331 * The socks4 spec doesn't include support for doing host name lookups by
1332 * the proxy. Many socks4 servers do this via the "socks4a" extension to
1333 * the protocol. There doesn't appear to be a way to detect if a server
1334 * supports this, so we require that the user set a global option.
1336 if (purple_prefs_get_bool("/purple/proxy/socks4_remotedns")) {
1337 unsigned char packet
[9];
1340 purple_debug_info("socks4 proxy", "Attempting to use remote DNS.\n");
1344 packet
[2] = connect_data
->port
>> 8;
1345 packet
[3] = connect_data
->port
& 0xff;
1352 len
= sizeof(packet
) + strlen(connect_data
->host
) + 1;
1354 connect_data
->write_buffer
= g_malloc0(len
);
1355 memcpy(connect_data
->write_buffer
, packet
, sizeof(packet
));
1356 memcpy(connect_data
->write_buffer
+ sizeof(packet
), connect_data
->host
, strlen(connect_data
->host
));
1357 connect_data
->write_buf_len
= len
;
1358 connect_data
->written_len
= 0;
1359 connect_data
->read_cb
= s4_canread
;
1361 connect_data
->inpa
= purple_input_add(connect_data
->fd
, PURPLE_INPUT_WRITE
, proxy_do_write
, connect_data
);
1363 proxy_do_write(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1365 connect_data
->query_data
= purple_dnsquery_a(connect_data
->host
,
1366 connect_data
->port
, s4_host_resolved
, connect_data
);
1368 if (connect_data
->query_data
== NULL
) {
1369 purple_debug_error("proxy", "dns query failed unexpectedly.\n");
1370 purple_proxy_connect_data_destroy(connect_data
);
1376 proxy_connect_socks4(PurpleProxyConnectData
*connect_data
, struct sockaddr
*addr
, socklen_t addrlen
)
1380 purple_debug_info("proxy",
1381 "Connecting to %s:%d via %s:%d using SOCKS4\n",
1382 connect_data
->host
, connect_data
->port
,
1383 purple_proxy_info_get_host(connect_data
->gpi
),
1384 purple_proxy_info_get_port(connect_data
->gpi
));
1386 connect_data
->fd
= socket(addr
->sa_family
, SOCK_STREAM
, 0);
1387 if (connect_data
->fd
< 0)
1389 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1390 _("Unable to create socket: %s"), g_strerror(errno
));
1394 flags
= fcntl(connect_data
->fd
, F_GETFL
);
1395 fcntl(connect_data
->fd
, F_SETFL
, flags
| O_NONBLOCK
);
1397 fcntl(connect_data
->fd
, F_SETFD
, FD_CLOEXEC
);
1400 if (connect(connect_data
->fd
, addr
, addrlen
) != 0)
1402 if ((errno
== EINPROGRESS
) || (errno
== EINTR
))
1404 purple_debug_info("proxy", "Connection in progress.\n");
1405 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
1406 PURPLE_INPUT_WRITE
, s4_canwrite
, connect_data
);
1410 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(errno
));
1415 purple_debug_info("proxy", "Connected immediately.\n");
1417 s4_canwrite(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1422 s5_ensure_buffer_length(PurpleProxyConnectData
*connect_data
, int len
)
1424 if(connect_data
->read_len
< len
) {
1425 if(connect_data
->read_buf_len
< len
) {
1426 /* it's not just that we haven't read enough, it's that we haven't tried to read enough yet */
1427 purple_debug_info("s5", "reallocing from %" G_GSIZE_FORMAT
1428 " to %d\n", connect_data
->read_buf_len
, len
);
1429 connect_data
->read_buf_len
= len
;
1430 connect_data
->read_buffer
= g_realloc(connect_data
->read_buffer
, connect_data
->read_buf_len
);
1439 s5_canread_again(gpointer data
, gint source
, PurpleInputCondition cond
)
1442 PurpleProxyConnectData
*connect_data
= data
;
1445 if (connect_data
->read_buffer
== NULL
) {
1446 connect_data
->read_buf_len
= 5;
1447 connect_data
->read_buffer
= g_malloc(connect_data
->read_buf_len
);
1448 connect_data
->read_len
= 0;
1451 dest
= connect_data
->read_buffer
+ connect_data
->read_len
;
1452 buf
= connect_data
->read_buffer
;
1454 len
= read(connect_data
->fd
, dest
, (connect_data
->read_buf_len
- connect_data
->read_len
));
1458 purple_proxy_connect_data_disconnect(connect_data
,
1459 _("Server closed the connection"));
1465 if (errno
== EAGAIN
)
1470 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1471 _("Lost connection with server: %s"), g_strerror(errno
));
1475 connect_data
->read_len
+= len
;
1477 if(connect_data
->read_len
< 4)
1480 if ((buf
[0] != 0x05) || (buf
[1] != 0x00)) {
1481 if ((buf
[0] == 0x05) && (buf
[1] < 0x09)) {
1482 purple_debug_error("socks5 proxy", "%s", socks5errors
[buf
[1]]);
1483 purple_proxy_connect_data_disconnect(connect_data
,
1484 socks5errors
[buf
[1]]);
1486 purple_debug_error("socks5 proxy", "Bad data.\n");
1487 purple_proxy_connect_data_disconnect(connect_data
,
1488 _("Received invalid data on connection with server"));
1493 /* Skip past BND.ADDR */
1495 case 0x01: /* the address is a version-4 IP address, with a length of 4 octets */
1496 if(!s5_ensure_buffer_length(connect_data
, 4 + 4))
1500 case 0x03: /* the address field contains a fully-qualified domain name. The first
1501 octet of the address field contains the number of octets of name that
1502 follow, there is no terminating NUL octet. */
1503 if(!s5_ensure_buffer_length(connect_data
, 4 + 1))
1506 if(!s5_ensure_buffer_length(connect_data
, 4 + 1 + buf
[0]))
1510 case 0x04: /* the address is a version-6 IP address, with a length of 16 octets */
1511 if(!s5_ensure_buffer_length(connect_data
, 4 + 16))
1516 purple_debug_error("socks5 proxy", "Invalid ATYP received (0x%X)\n", buf
[3]);
1517 purple_proxy_connect_data_disconnect(connect_data
,
1518 _("Received invalid data on connection with server"));
1522 /* Skip past BND.PORT */
1523 if(!s5_ensure_buffer_length(connect_data
, (buf
- connect_data
->read_buffer
) + 2))
1526 purple_proxy_connect_data_connected(connect_data
);
1530 s5_sendconnect(gpointer data
, int source
)
1532 PurpleProxyConnectData
*connect_data
= data
;
1533 size_t hlen
= strlen(connect_data
->host
);
1534 connect_data
->write_buf_len
= 5 + hlen
+ 2;
1535 connect_data
->write_buffer
= g_malloc(connect_data
->write_buf_len
);
1536 connect_data
->written_len
= 0;
1538 connect_data
->write_buffer
[0] = 0x05;
1539 connect_data
->write_buffer
[1] = 0x01; /* CONNECT */
1540 connect_data
->write_buffer
[2] = 0x00; /* reserved */
1541 connect_data
->write_buffer
[3] = 0x03; /* address type -- host name */
1542 connect_data
->write_buffer
[4] = hlen
;
1543 memcpy(connect_data
->write_buffer
+ 5, connect_data
->host
, hlen
);
1544 connect_data
->write_buffer
[5 + hlen
] = connect_data
->port
>> 8;
1545 connect_data
->write_buffer
[5 + hlen
+ 1] = connect_data
->port
& 0xff;
1547 connect_data
->read_cb
= s5_canread_again
;
1549 connect_data
->inpa
= purple_input_add(connect_data
->fd
, PURPLE_INPUT_WRITE
, proxy_do_write
, connect_data
);
1550 proxy_do_write(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1554 s5_readauth(gpointer data
, gint source
, PurpleInputCondition cond
)
1556 PurpleProxyConnectData
*connect_data
= data
;
1559 if (connect_data
->read_buffer
== NULL
) {
1560 connect_data
->read_buf_len
= 2;
1561 connect_data
->read_buffer
= g_malloc(connect_data
->read_buf_len
);
1562 connect_data
->read_len
= 0;
1565 purple_debug_info("socks5 proxy", "Got auth response.\n");
1567 len
= read(connect_data
->fd
, connect_data
->read_buffer
+ connect_data
->read_len
,
1568 connect_data
->read_buf_len
- connect_data
->read_len
);
1572 purple_proxy_connect_data_disconnect(connect_data
,
1573 _("Server closed the connection"));
1579 if (errno
== EAGAIN
)
1584 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1585 _("Lost connection with server: %s"), g_strerror(errno
));
1589 connect_data
->read_len
+= len
;
1590 if (connect_data
->read_len
< 2)
1593 purple_input_remove(connect_data
->inpa
);
1594 connect_data
->inpa
= 0;
1596 if ((connect_data
->read_buffer
[0] != 0x01) || (connect_data
->read_buffer
[1] != 0x00)) {
1597 purple_proxy_connect_data_disconnect(connect_data
,
1598 _("Received invalid data on connection with server"));
1602 g_free(connect_data
->read_buffer
);
1603 connect_data
->read_buffer
= NULL
;
1605 s5_sendconnect(connect_data
, connect_data
->fd
);
1609 hmacmd5_chap(const unsigned char * challenge
, int challen
, const char * passwd
, unsigned char * response
)
1611 PurpleCipher
*cipher
;
1612 PurpleCipherContext
*ctx
;
1614 unsigned char Kxoripad
[65];
1615 unsigned char Kxoropad
[65];
1618 cipher
= purple_ciphers_find_cipher("md5");
1619 ctx
= purple_cipher_context_new(cipher
, NULL
);
1621 memset(Kxoripad
,0,sizeof(Kxoripad
));
1622 memset(Kxoropad
,0,sizeof(Kxoropad
));
1624 pwlen
=strlen(passwd
);
1626 purple_cipher_context_append(ctx
, (const guchar
*)passwd
, strlen(passwd
));
1627 purple_cipher_context_digest(ctx
, sizeof(Kxoripad
), Kxoripad
, NULL
);
1630 memcpy(Kxoripad
, passwd
, pwlen
);
1632 memcpy(Kxoropad
,Kxoripad
,pwlen
);
1634 for (i
=0;i
<64;i
++) {
1639 purple_cipher_context_reset(ctx
, NULL
);
1640 purple_cipher_context_append(ctx
, Kxoripad
, 64);
1641 purple_cipher_context_append(ctx
, challenge
, challen
);
1642 purple_cipher_context_digest(ctx
, sizeof(Kxoripad
), Kxoripad
, NULL
);
1644 purple_cipher_context_reset(ctx
, NULL
);
1645 purple_cipher_context_append(ctx
, Kxoropad
, 64);
1646 purple_cipher_context_append(ctx
, Kxoripad
, 16);
1647 purple_cipher_context_digest(ctx
, 16, response
, NULL
);
1649 purple_cipher_context_destroy(ctx
);
1653 s5_readchap(gpointer data
, gint source
, PurpleInputCondition cond
);
1656 * Return how many bytes we processed
1657 * -1 means we've shouldn't keep reading from the buffer
1660 s5_parse_chap_msg(PurpleProxyConnectData
*connect_data
)
1662 guchar
*buf
, *cmdbuf
= connect_data
->read_buffer
;
1663 int len
, navas
, currentav
;
1665 purple_debug_misc("socks5 proxy", "Reading CHAP message: %x\n", *cmdbuf
);
1667 if (*cmdbuf
!= 0x01) {
1668 purple_proxy_connect_data_disconnect(connect_data
,
1669 _("Received invalid data on connection with server"));
1676 purple_debug_misc("socks5 proxy", "Expecting %d attribute(s).\n", navas
);
1680 for (currentav
= 0; currentav
< navas
; currentav
++) {
1682 len
= connect_data
->read_len
- (cmdbuf
- connect_data
->read_buffer
);
1683 /* We don't have enough data to even know how long the next attribute is,
1684 * or we don't have the full length of the next attribute. */
1685 if (len
< 2 || len
< (cmdbuf
[1] + 2)) {
1686 /* Clear out the attributes that have been read - decrease the attribute count */
1687 connect_data
->read_buffer
[1] = navas
- currentav
;
1688 /* Move the unprocessed data into the first attribute position */
1689 memmove((connect_data
->read_buffer
+ 2), cmdbuf
, len
);
1690 /* Decrease the read count accordingly */
1691 connect_data
->read_len
= len
+ 2;
1693 purple_debug_info("socks5 proxy", "Need more data to retrieve attribute %d.\n", currentav
);
1700 if (cmdbuf
[1] == 0) {
1701 purple_debug_error("socks5 proxy", "Attribute %x Value length of 0; ignoring.\n", cmdbuf
[0]);
1706 switch (cmdbuf
[0]) {
1708 purple_debug_info("socks5 proxy", "Received STATUS of %x\n", buf
[0]);
1709 /* Did auth work? */
1710 if (buf
[0] == 0x00) {
1711 purple_input_remove(connect_data
->inpa
);
1712 connect_data
->inpa
= 0;
1713 g_free(connect_data
->read_buffer
);
1714 connect_data
->read_buffer
= NULL
;
1716 s5_sendconnect(connect_data
, connect_data
->fd
);
1719 purple_debug_warning("proxy",
1720 "socks5 CHAP authentication "
1721 "failed. Disconnecting...");
1722 purple_proxy_connect_data_disconnect(connect_data
,
1723 _("Authentication failed"));
1727 /* We've already validated that cmdbuf[1] is sane. */
1728 purple_debug_info("socks5 proxy", "Received TEXT-MESSAGE of '%.*s'\n", (int) cmdbuf
[1], buf
);
1731 purple_debug_info("socks5 proxy", "Received CHALLENGE\n");
1732 /* Server wants our credentials */
1734 connect_data
->write_buf_len
= 16 + 4;
1735 connect_data
->write_buffer
= g_malloc(connect_data
->write_buf_len
);
1736 connect_data
->written_len
= 0;
1738 hmacmd5_chap(buf
, cmdbuf
[1],
1739 purple_proxy_info_get_password(connect_data
->gpi
),
1740 connect_data
->write_buffer
+ 4);
1741 /* TODO: What about USER-IDENTITY? */
1742 connect_data
->write_buffer
[0] = 0x01;
1743 connect_data
->write_buffer
[1] = 0x01;
1744 connect_data
->write_buffer
[2] = 0x04;
1745 connect_data
->write_buffer
[3] = 0x10;
1747 purple_input_remove(connect_data
->inpa
);
1748 g_free(connect_data
->read_buffer
);
1749 connect_data
->read_buffer
= NULL
;
1751 connect_data
->read_cb
= s5_readchap
;
1753 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
1754 PURPLE_INPUT_WRITE
, proxy_do_write
, connect_data
);
1756 proxy_do_write(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1759 purple_debug_info("socks5 proxy", "Received ALGORIGTHMS of %x\n", buf
[0]);
1760 /* Server wants to select an algorithm */
1761 if (buf
[0] != 0x85) {
1762 /* Only currently support HMAC-MD5 */
1763 purple_debug_warning("proxy",
1764 "Server tried to select an "
1765 "algorithm that we did not advertise "
1766 "as supporting. This is a violation "
1767 "of the socks5 CHAP specification. "
1768 "Disconnecting...");
1769 purple_proxy_connect_data_disconnect(connect_data
,
1770 _("Received invalid data on connection with server"));
1775 purple_debug_info("socks5 proxy", "Received unused command %x, length=%d\n", cmdbuf
[0], cmdbuf
[1]);
1777 cmdbuf
= buf
+ cmdbuf
[1];
1780 return (cmdbuf
- connect_data
->read_buffer
);
1784 s5_readchap(gpointer data
, gint source
, PurpleInputCondition cond
)
1787 PurpleProxyConnectData
*connect_data
= data
;
1790 purple_debug(PURPLE_DEBUG_INFO
, "socks5 proxy", "Got CHAP response.\n");
1792 if (connect_data
->read_buffer
== NULL
) {
1793 /* A big enough butfer to read the message header (2 bytes) and at least one complete attribute and value (1 + 1 + 255). */
1794 connect_data
->read_buf_len
= 259;
1795 connect_data
->read_buffer
= g_malloc(connect_data
->read_buf_len
);
1796 connect_data
->read_len
= 0;
1799 if (connect_data
->read_buf_len
- connect_data
->read_len
== 0) {
1800 /*If the stuff below is right, this shouldn't be possible. */
1801 purple_debug_error("socks5 proxy", "This is about to suck because the read buffer is full (shouldn't happen).\n");
1804 len
= read(connect_data
->fd
, connect_data
->read_buffer
+ connect_data
->read_len
,
1805 connect_data
->read_buf_len
- connect_data
->read_len
);
1808 purple_proxy_connect_data_disconnect(connect_data
,
1809 _("Server closed the connection"));
1814 if (errno
== EAGAIN
)
1819 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1820 _("Lost connection with server: %s"), g_strerror(errno
));
1824 connect_data
->read_len
+= len
;
1826 /* We may have read more than one message into the buffer, we need to make sure to process them all */
1829 /* We need more to be able to read this message */
1830 if (connect_data
->read_len
< 2)
1833 msg_ret
= s5_parse_chap_msg(connect_data
);
1838 /* See if we have another message already in the buffer */
1839 if ((len
= connect_data
->read_len
- msg_ret
) > 0) {
1841 /* Move on to the next message */
1842 memmove(connect_data
->read_buffer
, connect_data
->read_buffer
+ msg_ret
, len
);
1843 /* Decrease the read count accordingly */
1844 connect_data
->read_len
= len
;
1846 /* Try to read the message that connect_data->read_buffer now points to */
1853 /* Fell through. We ran out of CHAP events to process, but haven't
1854 * succeeded or failed authentication - there may be more to come.
1855 * If this is the case, come straight back here. */
1857 purple_debug_info("socks5 proxy", "Waiting for another message from which to read CHAP info.\n");
1859 /* We've processed all the available attributes, so get ready for a whole new message */
1860 g_free(connect_data
->read_buffer
);
1861 connect_data
->read_buffer
= NULL
;
1865 s5_canread(gpointer data
, gint source
, PurpleInputCondition cond
)
1867 PurpleProxyConnectData
*connect_data
= data
;
1870 if (connect_data
->read_buffer
== NULL
) {
1871 connect_data
->read_buf_len
= 2;
1872 connect_data
->read_buffer
= g_malloc(connect_data
->read_buf_len
);
1873 connect_data
->read_len
= 0;
1876 purple_debug_info("socks5 proxy", "Able to read.\n");
1878 len
= read(connect_data
->fd
, connect_data
->read_buffer
+ connect_data
->read_len
,
1879 connect_data
->read_buf_len
- connect_data
->read_len
);
1883 purple_proxy_connect_data_disconnect(connect_data
,
1884 _("Server closed the connection"));
1890 if (errno
== EAGAIN
)
1895 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1896 _("Lost connection with server: %s"), g_strerror(errno
));
1900 connect_data
->read_len
+= len
;
1901 if (connect_data
->read_len
< 2)
1904 purple_input_remove(connect_data
->inpa
);
1905 connect_data
->inpa
= 0;
1907 if ((connect_data
->read_buffer
[0] != 0x05) || (connect_data
->read_buffer
[1] == 0xff)) {
1908 purple_proxy_connect_data_disconnect(connect_data
,
1909 _("Received invalid data on connection with server"));
1913 if (connect_data
->read_buffer
[1] == 0x02) {
1917 u
= purple_proxy_info_get_username(connect_data
->gpi
);
1918 p
= purple_proxy_info_get_password(connect_data
->gpi
);
1920 i
= (u
== NULL
) ? 0 : strlen(u
);
1921 j
= (p
== NULL
) ? 0 : strlen(p
);
1923 connect_data
->write_buf_len
= 1 + 1 + i
+ 1 + j
;
1924 connect_data
->write_buffer
= g_malloc(connect_data
->write_buf_len
);
1925 connect_data
->written_len
= 0;
1927 connect_data
->write_buffer
[0] = 0x01; /* version 1 */
1928 connect_data
->write_buffer
[1] = i
;
1930 memcpy(connect_data
->write_buffer
+ 2, u
, i
);
1931 connect_data
->write_buffer
[2 + i
] = j
;
1933 memcpy(connect_data
->write_buffer
+ 2 + i
+ 1, p
, j
);
1935 g_free(connect_data
->read_buffer
);
1936 connect_data
->read_buffer
= NULL
;
1938 connect_data
->read_cb
= s5_readauth
;
1940 connect_data
->inpa
= purple_input_add(connect_data
->fd
, PURPLE_INPUT_WRITE
,
1941 proxy_do_write
, connect_data
);
1943 proxy_do_write(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1946 } else if (connect_data
->read_buffer
[1] == 0x03) {
1948 userlen
= strlen(purple_proxy_info_get_username(connect_data
->gpi
));
1950 connect_data
->write_buf_len
= 7 + userlen
;
1951 connect_data
->write_buffer
= g_malloc(connect_data
->write_buf_len
);
1952 connect_data
->written_len
= 0;
1954 connect_data
->write_buffer
[0] = 0x01;
1955 connect_data
->write_buffer
[1] = 0x02;
1956 connect_data
->write_buffer
[2] = 0x11;
1957 connect_data
->write_buffer
[3] = 0x01;
1958 connect_data
->write_buffer
[4] = 0x85;
1959 connect_data
->write_buffer
[5] = 0x02;
1960 connect_data
->write_buffer
[6] = userlen
;
1961 memcpy(connect_data
->write_buffer
+ 7,
1962 purple_proxy_info_get_username(connect_data
->gpi
), userlen
);
1964 g_free(connect_data
->read_buffer
);
1965 connect_data
->read_buffer
= NULL
;
1967 connect_data
->read_cb
= s5_readchap
;
1969 connect_data
->inpa
= purple_input_add(connect_data
->fd
, PURPLE_INPUT_WRITE
,
1970 proxy_do_write
, connect_data
);
1972 proxy_do_write(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1976 g_free(connect_data
->read_buffer
);
1977 connect_data
->read_buffer
= NULL
;
1979 s5_sendconnect(connect_data
, connect_data
->fd
);
1984 s5_canwrite(gpointer data
, gint source
, PurpleInputCondition cond
)
1986 unsigned char buf
[5];
1988 PurpleProxyConnectData
*connect_data
= data
;
1989 int error
= ETIMEDOUT
;
1992 purple_debug_info("socks5 proxy", "Connected.\n");
1994 if (connect_data
->inpa
> 0)
1996 purple_input_remove(connect_data
->inpa
);
1997 connect_data
->inpa
= 0;
2000 ret
= purple_input_get_error(connect_data
->fd
, &error
);
2001 if ((ret
!= 0) || (error
!= 0))
2005 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(error
));
2010 buf
[0] = 0x05; /* SOCKS version 5 */
2012 if (purple_proxy_info_get_username(connect_data
->gpi
) != NULL
) {
2013 buf
[1] = 0x03; /* three methods */
2014 buf
[2] = 0x00; /* no authentication */
2015 buf
[3] = 0x03; /* CHAP authentication */
2016 buf
[4] = 0x02; /* username/password authentication */
2025 connect_data
->write_buf_len
= i
;
2026 connect_data
->write_buffer
= g_malloc(connect_data
->write_buf_len
);
2027 memcpy(connect_data
->write_buffer
, buf
, i
);
2029 connect_data
->read_cb
= s5_canread
;
2031 connect_data
->inpa
= purple_input_add(connect_data
->fd
, PURPLE_INPUT_WRITE
, proxy_do_write
, connect_data
);
2032 proxy_do_write(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
2036 proxy_connect_socks5(PurpleProxyConnectData
*connect_data
, struct sockaddr
*addr
, socklen_t addrlen
)
2040 purple_debug_info("proxy",
2041 "Connecting to %s:%d via %s:%d using SOCKS5\n",
2042 connect_data
->host
, connect_data
->port
,
2043 purple_proxy_info_get_host(connect_data
->gpi
),
2044 purple_proxy_info_get_port(connect_data
->gpi
));
2046 connect_data
->fd
= socket(addr
->sa_family
, SOCK_STREAM
, 0);
2047 if (connect_data
->fd
< 0)
2049 purple_proxy_connect_data_disconnect_formatted(connect_data
,
2050 _("Unable to create socket: %s"), g_strerror(errno
));
2054 flags
= fcntl(connect_data
->fd
, F_GETFL
);
2055 fcntl(connect_data
->fd
, F_SETFL
, flags
| O_NONBLOCK
);
2057 fcntl(connect_data
->fd
, F_SETFD
, FD_CLOEXEC
);
2060 if (connect(connect_data
->fd
, addr
, addrlen
) != 0)
2062 if ((errno
== EINPROGRESS
) || (errno
== EINTR
))
2064 purple_debug_info("socks5 proxy", "Connection in progress\n");
2065 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
2066 PURPLE_INPUT_WRITE
, s5_canwrite
, connect_data
);
2070 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(errno
));
2075 purple_debug_info("proxy", "Connected immediately.\n");
2077 s5_canwrite(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
2082 * This function attempts to connect to the next IP address in the list
2083 * of IP addresses returned to us by purple_dnsquery_a() and attemps
2084 * to connect to each one. This is called after the hostname is
2085 * resolved, and each time a connection attempt fails (assuming there
2086 * is another IP address to try).
2088 #ifndef INET6_ADDRSTRLEN
2089 #define INET6_ADDRSTRLEN 46
2092 static void try_connect(PurpleProxyConnectData
*connect_data
)
2095 struct sockaddr
*addr
;
2096 char ipaddr
[INET6_ADDRSTRLEN
];
2098 addrlen
= GPOINTER_TO_INT(connect_data
->hosts
->data
);
2099 connect_data
->hosts
= g_slist_remove(connect_data
->hosts
, connect_data
->hosts
->data
);
2100 addr
= connect_data
->hosts
->data
;
2101 connect_data
->hosts
= g_slist_remove(connect_data
->hosts
, connect_data
->hosts
->data
);
2102 #ifdef HAVE_INET_NTOP
2103 inet_ntop(addr
->sa_family
, &((struct sockaddr_in
*)addr
)->sin_addr
,
2104 ipaddr
, sizeof(ipaddr
));
2106 memcpy(ipaddr
, inet_ntoa(((struct sockaddr_in
*)addr
)->sin_addr
),
2109 purple_debug_info("proxy", "Attempting connection to %s\n", ipaddr
);
2111 if (connect_data
->socket_type
== SOCK_DGRAM
) {
2112 proxy_connect_udp_none(connect_data
, addr
, addrlen
);
2117 switch (purple_proxy_info_get_type(connect_data
->gpi
)) {
2118 case PURPLE_PROXY_NONE
:
2119 proxy_connect_none(connect_data
, addr
, addrlen
);
2122 case PURPLE_PROXY_HTTP
:
2123 proxy_connect_http(connect_data
, addr
, addrlen
);
2126 case PURPLE_PROXY_SOCKS4
:
2127 proxy_connect_socks4(connect_data
, addr
, addrlen
);
2130 case PURPLE_PROXY_SOCKS5
:
2131 proxy_connect_socks5(connect_data
, addr
, addrlen
);
2134 case PURPLE_PROXY_USE_ENVVAR
:
2135 proxy_connect_http(connect_data
, addr
, addrlen
);
2146 connection_host_resolved(GSList
*hosts
, gpointer data
,
2147 const char *error_message
)
2149 PurpleProxyConnectData
*connect_data
;
2151 connect_data
= data
;
2152 connect_data
->query_data
= NULL
;
2154 if (error_message
!= NULL
)
2156 purple_proxy_connect_data_disconnect(connect_data
, error_message
);
2162 purple_proxy_connect_data_disconnect(connect_data
, _("Unable to resolve hostname"));
2166 connect_data
->hosts
= hosts
;
2168 try_connect(connect_data
);
2172 purple_proxy_get_setup(PurpleAccount
*account
)
2174 PurpleProxyInfo
*gpi
= NULL
;
2177 /* This is used as a fallback so we don't overwrite the selected proxy type */
2178 static PurpleProxyInfo
*tmp_none_proxy_info
= NULL
;
2179 if (!tmp_none_proxy_info
) {
2180 tmp_none_proxy_info
= purple_proxy_info_new();
2181 purple_proxy_info_set_type(tmp_none_proxy_info
, PURPLE_PROXY_NONE
);
2184 if (account
&& purple_account_get_proxy_info(account
) != NULL
) {
2185 gpi
= purple_account_get_proxy_info(account
);
2186 if (purple_proxy_info_get_type(gpi
) == PURPLE_PROXY_USE_GLOBAL
)
2190 if (purple_running_gnome())
2191 gpi
= purple_gnome_proxy_get_info();
2193 gpi
= purple_global_proxy_get_info();
2196 if (purple_proxy_info_get_type(gpi
) == PURPLE_PROXY_USE_ENVVAR
) {
2197 if ((tmp
= g_getenv("HTTP_PROXY")) != NULL
||
2198 (tmp
= g_getenv("http_proxy")) != NULL
||
2199 (tmp
= g_getenv("HTTPPROXY")) != NULL
) {
2200 char *proxyhost
, *proxyuser
, *proxypasswd
;
2203 /* http_proxy-format:
2204 * export http_proxy="http://user:passwd@your.proxy.server:port/"
2206 if(purple_url_parse(tmp
, &proxyhost
, &proxyport
, NULL
, &proxyuser
, &proxypasswd
)) {
2207 purple_proxy_info_set_host(gpi
, proxyhost
);
2210 purple_proxy_info_set_username(gpi
, proxyuser
);
2213 purple_proxy_info_set_password(gpi
, proxypasswd
);
2214 g_free(proxypasswd
);
2216 /* only for backward compatibility */
2217 if (proxyport
== 80 &&
2218 ((tmp
= g_getenv("HTTP_PROXY_PORT")) != NULL
||
2219 (tmp
= g_getenv("http_proxy_port")) != NULL
||
2220 (tmp
= g_getenv("HTTPPROXYPORT")) != NULL
))
2221 proxyport
= atoi(tmp
);
2223 purple_proxy_info_set_port(gpi
, proxyport
);
2225 /* XXX: Do we want to skip this step if user/password were part of url? */
2226 if ((tmp
= g_getenv("HTTP_PROXY_USER")) != NULL
||
2227 (tmp
= g_getenv("http_proxy_user")) != NULL
||
2228 (tmp
= g_getenv("HTTPPROXYUSER")) != NULL
)
2229 purple_proxy_info_set_username(gpi
, tmp
);
2231 if ((tmp
= g_getenv("HTTP_PROXY_PASS")) != NULL
||
2232 (tmp
= g_getenv("http_proxy_pass")) != NULL
||
2233 (tmp
= g_getenv("HTTPPROXYPASS")) != NULL
)
2234 purple_proxy_info_set_password(gpi
, tmp
);
2239 PurpleProxyInfo
*wgpi
;
2240 if ((wgpi
= purple_win32_proxy_get_info()) != NULL
)
2243 /* no proxy environment variable found, don't use a proxy */
2244 purple_debug_info("proxy", "No environment settings found, not using a proxy\n");
2245 gpi
= tmp_none_proxy_info
;
2253 PurpleProxyConnectData
*
2254 purple_proxy_connect(void *handle
, PurpleAccount
*account
,
2255 const char *host
, int port
,
2256 PurpleProxyConnectFunction connect_cb
, gpointer data
)
2258 const char *connecthost
= host
;
2259 int connectport
= port
;
2260 PurpleProxyConnectData
*connect_data
;
2262 g_return_val_if_fail(host
!= NULL
, NULL
);
2263 g_return_val_if_fail(port
> 0, NULL
);
2264 g_return_val_if_fail(connect_cb
!= NULL
, NULL
);
2266 connect_data
= g_new0(PurpleProxyConnectData
, 1);
2267 connect_data
->fd
= -1;
2268 connect_data
->socket_type
= SOCK_STREAM
;
2269 connect_data
->handle
= handle
;
2270 connect_data
->connect_cb
= connect_cb
;
2271 connect_data
->data
= data
;
2272 connect_data
->host
= g_strdup(host
);
2273 connect_data
->port
= port
;
2274 connect_data
->gpi
= purple_proxy_get_setup(account
);
2276 if ((purple_proxy_info_get_type(connect_data
->gpi
) != PURPLE_PROXY_NONE
) &&
2277 (purple_proxy_info_get_host(connect_data
->gpi
) == NULL
||
2278 purple_proxy_info_get_port(connect_data
->gpi
) <= 0)) {
2280 purple_notify_error(NULL
, NULL
, _("Invalid proxy settings"), _("Either the host name or port number specified for your given proxy type is invalid."));
2281 purple_proxy_connect_data_destroy(connect_data
);
2285 switch (purple_proxy_info_get_type(connect_data
->gpi
))
2287 case PURPLE_PROXY_NONE
:
2290 case PURPLE_PROXY_HTTP
:
2291 case PURPLE_PROXY_SOCKS4
:
2292 case PURPLE_PROXY_SOCKS5
:
2293 case PURPLE_PROXY_USE_ENVVAR
:
2294 connecthost
= purple_proxy_info_get_host(connect_data
->gpi
);
2295 connectport
= purple_proxy_info_get_port(connect_data
->gpi
);
2299 purple_debug_error("proxy", "Invalid Proxy type (%d) specified.\n",
2300 purple_proxy_info_get_type(connect_data
->gpi
));
2301 purple_proxy_connect_data_destroy(connect_data
);
2305 connect_data
->query_data
= purple_dnsquery_a(connecthost
,
2306 connectport
, connection_host_resolved
, connect_data
);
2307 if (connect_data
->query_data
== NULL
)
2309 purple_debug_error("proxy", "dns query failed unexpectedly.\n");
2310 purple_proxy_connect_data_destroy(connect_data
);
2314 handles
= g_slist_prepend(handles
, connect_data
);
2316 return connect_data
;
2319 PurpleProxyConnectData
*
2320 purple_proxy_connect_udp(void *handle
, PurpleAccount
*account
,
2321 const char *host
, int port
,
2322 PurpleProxyConnectFunction connect_cb
, gpointer data
)
2324 const char *connecthost
= host
;
2325 int connectport
= port
;
2326 PurpleProxyConnectData
*connect_data
;
2328 g_return_val_if_fail(host
!= NULL
, NULL
);
2329 g_return_val_if_fail(port
> 0, NULL
);
2330 g_return_val_if_fail(connect_cb
!= NULL
, NULL
);
2332 connect_data
= g_new0(PurpleProxyConnectData
, 1);
2333 connect_data
->fd
= -1;
2334 connect_data
->socket_type
= SOCK_DGRAM
;
2335 connect_data
->handle
= handle
;
2336 connect_data
->connect_cb
= connect_cb
;
2337 connect_data
->data
= data
;
2338 connect_data
->host
= g_strdup(host
);
2339 connect_data
->port
= port
;
2340 connect_data
->gpi
= purple_proxy_get_setup(account
);
2342 if ((purple_proxy_info_get_type(connect_data
->gpi
) != PURPLE_PROXY_NONE
) &&
2343 (purple_proxy_info_get_host(connect_data
->gpi
) == NULL
||
2344 purple_proxy_info_get_port(connect_data
->gpi
) <= 0)) {
2346 purple_notify_error(NULL
, NULL
, _("Invalid proxy settings"), _("Either the host name or port number specified for your given proxy type is invalid."));
2347 purple_proxy_connect_data_destroy(connect_data
);
2351 switch (purple_proxy_info_get_type(connect_data
->gpi
))
2353 case PURPLE_PROXY_NONE
:
2356 case PURPLE_PROXY_HTTP
:
2357 case PURPLE_PROXY_SOCKS4
:
2358 case PURPLE_PROXY_SOCKS5
:
2359 case PURPLE_PROXY_USE_ENVVAR
:
2360 purple_debug_info("proxy", "Ignoring Proxy type (%d) for UDP.\n",
2361 purple_proxy_info_get_type(connect_data
->gpi
));
2365 purple_debug_error("proxy", "Invalid Proxy type (%d) specified.\n",
2366 purple_proxy_info_get_type(connect_data
->gpi
));
2367 purple_proxy_connect_data_destroy(connect_data
);
2371 connect_data
->query_data
= purple_dnsquery_a(connecthost
,
2372 connectport
, connection_host_resolved
, connect_data
);
2373 if (connect_data
->query_data
== NULL
)
2375 purple_proxy_connect_data_destroy(connect_data
);
2379 handles
= g_slist_prepend(handles
, connect_data
);
2381 return connect_data
;
2385 * Combine some of this code with purple_proxy_connect()
2387 PurpleProxyConnectData
*
2388 purple_proxy_connect_socks5(void *handle
, PurpleProxyInfo
*gpi
,
2389 const char *host
, int port
,
2390 PurpleProxyConnectFunction connect_cb
,
2393 PurpleProxyConnectData
*connect_data
;
2395 g_return_val_if_fail(host
!= NULL
, NULL
);
2396 g_return_val_if_fail(port
>= 0, NULL
);
2397 g_return_val_if_fail(connect_cb
!= NULL
, NULL
);
2399 connect_data
= g_new0(PurpleProxyConnectData
, 1);
2400 connect_data
->fd
= -1;
2401 connect_data
->socket_type
= SOCK_STREAM
;
2402 connect_data
->handle
= handle
;
2403 connect_data
->connect_cb
= connect_cb
;
2404 connect_data
->data
= data
;
2405 connect_data
->host
= g_strdup(host
);
2406 connect_data
->port
= port
;
2407 connect_data
->gpi
= gpi
;
2409 connect_data
->query_data
=
2410 purple_dnsquery_a(purple_proxy_info_get_host(gpi
),
2411 purple_proxy_info_get_port(gpi
),
2412 connection_host_resolved
, connect_data
);
2413 if (connect_data
->query_data
== NULL
)
2415 purple_proxy_connect_data_destroy(connect_data
);
2419 handles
= g_slist_prepend(handles
, connect_data
);
2421 return connect_data
;
2425 purple_proxy_connect_cancel(PurpleProxyConnectData
*connect_data
)
2427 purple_proxy_connect_data_disconnect(connect_data
, NULL
);
2428 purple_proxy_connect_data_destroy(connect_data
);
2432 purple_proxy_connect_cancel_with_handle(void *handle
)
2436 for (l
= handles
; l
!= NULL
; l
= l_next
) {
2437 PurpleProxyConnectData
*connect_data
= l
->data
;
2441 if (connect_data
->handle
== handle
)
2442 purple_proxy_connect_cancel(connect_data
);
2447 proxy_pref_cb(const char *name
, PurplePrefType type
,
2448 gconstpointer value
, gpointer data
)
2450 PurpleProxyInfo
*info
= purple_global_proxy_get_info();
2452 if (purple_strequal(name
, "/purple/proxy/type")) {
2454 const char *type
= value
;
2456 if (purple_strequal(type
, "none"))
2457 proxytype
= PURPLE_PROXY_NONE
;
2458 else if (purple_strequal(type
, "http"))
2459 proxytype
= PURPLE_PROXY_HTTP
;
2460 else if (purple_strequal(type
, "socks4"))
2461 proxytype
= PURPLE_PROXY_SOCKS4
;
2462 else if (purple_strequal(type
, "socks5"))
2463 proxytype
= PURPLE_PROXY_SOCKS5
;
2464 else if (purple_strequal(type
, "envvar"))
2465 proxytype
= PURPLE_PROXY_USE_ENVVAR
;
2469 purple_proxy_info_set_type(info
, proxytype
);
2470 } else if (purple_strequal(name
, "/purple/proxy/host"))
2471 purple_proxy_info_set_host(info
, value
);
2472 else if (purple_strequal(name
, "/purple/proxy/port"))
2473 purple_proxy_info_set_port(info
, GPOINTER_TO_INT(value
));
2474 else if (purple_strequal(name
, "/purple/proxy/username"))
2475 purple_proxy_info_set_username(info
, value
);
2476 else if (purple_strequal(name
, "/purple/proxy/password"))
2477 purple_proxy_info_set_password(info
, value
);
2481 purple_proxy_get_handle()
2489 purple_proxy_init(void)
2493 /* Initialize a default proxy info struct. */
2494 global_proxy_info
= purple_proxy_info_new();
2497 purple_prefs_add_none("/purple/proxy");
2498 purple_prefs_add_string("/purple/proxy/type", "none");
2499 purple_prefs_add_string("/purple/proxy/host", "");
2500 purple_prefs_add_int("/purple/proxy/port", 0);
2501 purple_prefs_add_string("/purple/proxy/username", "");
2502 purple_prefs_add_string("/purple/proxy/password", "");
2503 purple_prefs_add_bool("/purple/proxy/socks4_remotedns", FALSE
);
2505 /* Setup callbacks for the preferences. */
2506 handle
= purple_proxy_get_handle();
2507 purple_prefs_connect_callback(handle
, "/purple/proxy/type", proxy_pref_cb
,
2509 purple_prefs_connect_callback(handle
, "/purple/proxy/host", proxy_pref_cb
,
2511 purple_prefs_connect_callback(handle
, "/purple/proxy/port", proxy_pref_cb
,
2513 purple_prefs_connect_callback(handle
, "/purple/proxy/username",
2514 proxy_pref_cb
, NULL
);
2515 purple_prefs_connect_callback(handle
, "/purple/proxy/password",
2516 proxy_pref_cb
, NULL
);
2518 /* Load the initial proxy settings */
2519 purple_prefs_trigger_callback("/purple/proxy/type");
2520 purple_prefs_trigger_callback("/purple/proxy/host");
2521 purple_prefs_trigger_callback("/purple/proxy/port");
2522 purple_prefs_trigger_callback("/purple/proxy/username");
2523 purple_prefs_trigger_callback("/purple/proxy/password");
2527 purple_proxy_uninit(void)
2529 while (handles
!= NULL
)
2531 purple_proxy_connect_data_disconnect(handles
->data
, NULL
);
2532 purple_proxy_connect_data_destroy(handles
->data
);
2535 purple_prefs_disconnect_by_handle(purple_proxy_get_handle());
2537 purple_proxy_info_destroy(global_proxy_info
);
2538 global_proxy_info
= NULL
;