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 */
32 #define _PURPLE_PROXY_C_
44 struct _PurpleProxyConnectData {
46 PurpleProxyConnectFunction connect_cb;
54 PurpleDnsQueryData *query_data;
57 * This contains alternating length/char* values. The char*
58 * values need to be freed when removed from the linked list.
62 PurpleProxyConnectData *child;
65 * All of the following variables are used when establishing a
66 * connection through a proxy.
71 PurpleInputFunction read_cb;
75 PurpleAccount *account;
78 static const char * const socks5errors[] = {
80 "general SOCKS server failure\n",
81 "connection not allowed by ruleset\n",
82 "Network unreachable\n",
84 "Connection refused\n",
86 "Command not supported\n",
87 "Address type not supported\n"
90 static PurpleProxyInfo *global_proxy_info = NULL;
92 static GSList *handles = NULL;
94 static void try_connect(PurpleProxyConnectData *connect_data);
97 * TODO: Eventually (GObjectification) this bad boy will be removed, because it is
98 * a gross fix for a crashy problem.
100 #define PURPLE_PROXY_CONNECT_DATA_IS_VALID(connect_data) g_slist_find(handles, connect_data)
102 /**************************************************************************
103 * Proxy structure API
104 **************************************************************************/
106 purple_proxy_info_new(void)
108 return g_new0(PurpleProxyInfo, 1);
112 purple_proxy_info_destroy(PurpleProxyInfo *info)
114 g_return_if_fail(info != NULL);
117 g_free(info->username);
118 g_free(info->password);
124 purple_proxy_info_set_type(PurpleProxyInfo *info, PurpleProxyType type)
126 g_return_if_fail(info != NULL);
132 purple_proxy_info_set_host(PurpleProxyInfo *info, const char *host)
134 g_return_if_fail(info != NULL);
137 info->host = g_strdup(host);
141 purple_proxy_info_set_port(PurpleProxyInfo *info, int port)
143 g_return_if_fail(info != NULL);
149 purple_proxy_info_set_username(PurpleProxyInfo *info, const char *username)
151 g_return_if_fail(info != NULL);
153 g_free(info->username);
154 info->username = g_strdup(username);
158 purple_proxy_info_set_password(PurpleProxyInfo *info, const char *password)
160 g_return_if_fail(info != NULL);
162 g_free(info->password);
163 info->password = g_strdup(password);
167 purple_proxy_info_get_type(const PurpleProxyInfo *info)
169 g_return_val_if_fail(info != NULL, PURPLE_PROXY_NONE);
175 purple_proxy_info_get_host(const PurpleProxyInfo *info)
177 g_return_val_if_fail(info != NULL, NULL);
183 purple_proxy_info_get_port(const PurpleProxyInfo *info)
185 g_return_val_if_fail(info != NULL, 0);
191 purple_proxy_info_get_username(const PurpleProxyInfo *info)
193 g_return_val_if_fail(info != NULL, NULL);
195 return info->username;
199 purple_proxy_info_get_password(const PurpleProxyInfo *info)
201 g_return_val_if_fail(info != NULL, NULL);
203 return info->password;
206 /**************************************************************************
208 **************************************************************************/
210 purple_global_proxy_get_info(void)
212 return global_proxy_info;
216 purple_global_proxy_set_info(PurpleProxyInfo *info)
218 g_return_if_fail(info != NULL);
220 purple_proxy_info_destroy(global_proxy_info);
222 global_proxy_info = info;
226 /* index in gproxycmds below, keep them in sync */
227 #define GNOME_PROXY_MODE 0
228 #define GNOME_PROXY_USE_SAME_PROXY 1
229 #define GNOME_PROXY_SOCKS_HOST 2
230 #define GNOME_PROXY_SOCKS_PORT 3
231 #define GNOME_PROXY_HTTP_HOST 4
232 #define GNOME_PROXY_HTTP_PORT 5
233 #define GNOME_PROXY_HTTP_USER 6
234 #define GNOME_PROXY_HTTP_PASS 7
235 #define GNOME2_CMDS 0
236 #define GNOME3_CMDS 1
238 /* detect proxy settings for gnome2/gnome3 */
239 static const char* gproxycmds[][2] = {
240 { "gconftool-2 -g /system/proxy/mode" , "gsettings get org.gnome.system.proxy mode" },
241 { "gconftool-2 -g /system/http_proxy/use_same_proxy", "gsettings get org.gnome.system.proxy use-same-proxy" },
242 { "gconftool-2 -g /system/proxy/socks_host", "gsettings get org.gnome.system.proxy.socks host" },
243 { "gconftool-2 -g /system/proxy/socks_port", "gsettings get org.gnome.system.proxy.socks port" },
244 { "gconftool-2 -g /system/http_proxy/host", "gsettings get org.gnome.system.proxy.http host" },
245 { "gconftool-2 -g /system/http_proxy/port", "gsettings get org.gnome.system.proxy.http port"},
246 { "gconftool-2 -g /system/http_proxy/authentication_user", "gsettings get org.gnome.system.proxy.http authentication-user" },
247 { "gconftool-2 -g /system/http_proxy/authentication_password", "gsettings get org.gnome.system.proxy.http authentication-password" },
251 * This is a utility function used to retrieve proxy parameter values from
252 * GNOME 2/3 environment.
254 * @param parameter One of the GNOME_PROXY_x constants defined above
255 * @param gnome_version GNOME2_CMDS or GNOME3_CMDS
257 * @return The value of requested proxy parameter
260 purple_gnome_proxy_get_parameter(guint8 parameter, guint8 gnome_version)
265 if (parameter > GNOME_PROXY_HTTP_PASS)
267 if (gnome_version > GNOME3_CMDS)
270 if (!g_spawn_command_line_sync(gproxycmds[parameter][gnome_version],
271 ¶m, &err, NULL, NULL))
276 if (param[0] == '\'' || param[0] == '\"') {
277 param_len = strlen(param);
278 memmove(param, param + 1, param_len); /* copy last \0 too */
280 if (param_len > 0 && (param[param_len - 1] == '\'' || param[param_len - 1] == '\"'))
281 param[param_len - 1] = '\0';
288 static PurpleProxyInfo *
289 purple_gnome_proxy_get_info(void)
291 static PurpleProxyInfo info = {0, NULL, 0, NULL, NULL};
292 gboolean use_same_proxy = FALSE;
294 guint8 gnome_version = GNOME3_CMDS;
296 tmp = g_find_program_in_path("gsettings");
298 tmp = g_find_program_in_path("gconftool-2");
299 gnome_version = GNOME2_CMDS;
302 return purple_global_proxy_get_info();
306 /* Check whether to use a proxy. */
307 tmp = purple_gnome_proxy_get_parameter(GNOME_PROXY_MODE, gnome_version);
309 return purple_global_proxy_get_info();
311 if (purple_strequal(tmp, "none")) {
312 info.type = PURPLE_PROXY_NONE;
317 if (!purple_strequal(tmp, "manual")) {
318 /* Unknown setting. Fallback to using our global proxy settings. */
320 return purple_global_proxy_get_info();
325 /* Free the old fields */
331 g_free(info.username);
332 info.username = NULL;
335 g_free(info.password);
336 info.password = NULL;
339 tmp = purple_gnome_proxy_get_parameter(GNOME_PROXY_USE_SAME_PROXY, gnome_version);
341 return purple_global_proxy_get_info();
343 if (purple_strequal(tmp, "true"))
344 use_same_proxy = TRUE;
348 if (!use_same_proxy) {
349 info.host = purple_gnome_proxy_get_parameter(GNOME_PROXY_SOCKS_HOST, gnome_version);
351 return purple_global_proxy_get_info();
354 if (!use_same_proxy && (info.host != NULL) && (*info.host != '\0')) {
355 info.type = PURPLE_PROXY_SOCKS5;
356 tmp = purple_gnome_proxy_get_parameter(GNOME_PROXY_SOCKS_PORT, gnome_version);
360 return purple_global_proxy_get_info();
362 info.port = atoi(tmp);
366 info.host = purple_gnome_proxy_get_parameter(GNOME_PROXY_HTTP_HOST, gnome_version);
368 return purple_global_proxy_get_info();
370 /* If we get this far then we know we're using an HTTP proxy */
371 info.type = PURPLE_PROXY_HTTP;
373 if (*info.host == '\0')
375 purple_debug_info("proxy", "Gnome proxy settings are set to "
376 "'manual' but no suitable proxy server is specified. Using "
377 "Pidgin's proxy settings instead.\n");
380 return purple_global_proxy_get_info();
383 info.username = purple_gnome_proxy_get_parameter(GNOME_PROXY_HTTP_USER, gnome_version);
388 return purple_global_proxy_get_info();
391 info.password = purple_gnome_proxy_get_parameter(GNOME_PROXY_HTTP_PASS, gnome_version);
396 g_free(info.username);
397 info.username = NULL;
398 return purple_global_proxy_get_info();
401 tmp = purple_gnome_proxy_get_parameter(GNOME_PROXY_HTTP_PORT, gnome_version);
406 g_free(info.username);
407 info.username = NULL;
408 g_free(info.password);
409 info.password = NULL;
410 return purple_global_proxy_get_info();
412 info.port = atoi(tmp);
421 typedef BOOL (CALLBACK* LPFNWINHTTPGETIEPROXYCONFIG)(/*IN OUT*/ WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* pProxyConfig);
423 /* This modifies "host" in-place evilly */
425 _proxy_fill_hostinfo(PurpleProxyInfo *info, char *host, int default_port)
427 int port = default_port;
430 d = g_strrstr(host, ":");
436 sscanf(d, "%d", &port);
442 purple_proxy_info_set_host(info, host);
443 purple_proxy_info_set_port(info, port);
446 static PurpleProxyInfo *
447 purple_win32_proxy_get_info(void)
449 static LPFNWINHTTPGETIEPROXYCONFIG MyWinHttpGetIEProxyConfig = NULL;
450 static gboolean loaded = FALSE;
451 static PurpleProxyInfo info = {0, NULL, 0, NULL, NULL};
453 WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ie_proxy_config;
457 MyWinHttpGetIEProxyConfig = (LPFNWINHTTPGETIEPROXYCONFIG)
458 wpurple_find_and_loadproc("winhttp.dll", "WinHttpGetIEProxyConfigForCurrentUser");
459 if (!MyWinHttpGetIEProxyConfig)
460 purple_debug_warning("proxy", "Unable to read Windows Proxy Settings.\n");
463 if (!MyWinHttpGetIEProxyConfig)
466 ZeroMemory(&ie_proxy_config, sizeof(ie_proxy_config));
467 if (!MyWinHttpGetIEProxyConfig(&ie_proxy_config)) {
468 purple_debug_error("proxy", "Error reading Windows Proxy Settings(%lu).\n", GetLastError());
472 /* We can't do much if it is autodetect*/
473 if (ie_proxy_config.fAutoDetect) {
474 purple_debug_error("proxy", "Windows Proxy Settings set to autodetect (not supported).\n");
476 /* TODO: For 3.0.0 we'll revisit this (maybe)*/
480 } else if (ie_proxy_config.lpszProxy) {
481 gchar *proxy_list = g_utf16_to_utf8(ie_proxy_config.lpszProxy, -1,
484 /* We can't do anything about the bypass list, as we don't have the url */
485 /* TODO: For 3.0.0 we'll revisit this*/
487 /* There are proxy settings for several protocols */
488 if (proxy_list && *proxy_list) {
489 char *specific = NULL, *tmp;
491 /* If there is only a global proxy, which means "HTTP" */
492 if (!strchr(proxy_list, ';') || (specific = g_strstr_len(proxy_list, -1, "http=")) != NULL) {
495 specific += strlen("http=");
496 tmp = strchr(specific, ';');
499 /* specific now points the proxy server (and port) */
501 specific = proxy_list;
503 purple_proxy_info_set_type(&info, PURPLE_PROXY_HTTP);
504 _proxy_fill_hostinfo(&info, specific, 80);
505 /* TODO: is there a way to set the username/password? */
506 purple_proxy_info_set_username(&info, NULL);
507 purple_proxy_info_set_password(&info, NULL);
509 purple_debug_info("proxy", "Windows Proxy Settings: HTTP proxy: '%s:%d'.\n",
510 purple_proxy_info_get_host(&info),
511 purple_proxy_info_get_port(&info));
513 } else if ((specific = g_strstr_len(proxy_list, -1, "socks=")) != NULL) {
515 specific += strlen("socks=");
516 tmp = strchr(specific, ';');
519 /* specific now points the proxy server (and port) */
521 purple_proxy_info_set_type(&info, PURPLE_PROXY_SOCKS5);
522 _proxy_fill_hostinfo(&info, specific, 1080);
523 /* TODO: is there a way to set the username/password? */
524 purple_proxy_info_set_username(&info, NULL);
525 purple_proxy_info_set_password(&info, NULL);
527 purple_debug_info("proxy", "Windows Proxy Settings: SOCKS5 proxy: '%s:%d'.\n",
528 purple_proxy_info_get_host(&info),
529 purple_proxy_info_get_port(&info));
533 purple_debug_info("proxy", "Windows Proxy Settings: No supported proxy specified.\n");
535 purple_proxy_info_set_type(&info, PURPLE_PROXY_NONE);
540 /* TODO: Fix API to be able look at proxy bypass settings */
544 purple_debug_info("proxy", "No Windows proxy set.\n");
545 purple_proxy_info_set_type(&info, PURPLE_PROXY_NONE);
548 if (ie_proxy_config.lpszAutoConfigUrl)
549 GlobalFree(ie_proxy_config.lpszAutoConfigUrl);
550 if (ie_proxy_config.lpszProxy)
551 GlobalFree(ie_proxy_config.lpszProxy);
552 if (ie_proxy_config.lpszProxyBypass)
553 GlobalFree(ie_proxy_config.lpszProxyBypass);
560 /**************************************************************************
562 **************************************************************************/
565 * Whoever calls this needs to have called
566 * purple_proxy_connect_data_disconnect() beforehand.
569 purple_proxy_connect_data_destroy(PurpleProxyConnectData *connect_data)
571 handles = g_slist_remove(handles, connect_data);
573 if (connect_data->query_data != NULL)
574 purple_dnsquery_destroy(connect_data->query_data);
576 while (connect_data->hosts != NULL)
578 /* Discard the length... */
579 connect_data->hosts = g_slist_remove(connect_data->hosts, connect_data->hosts->data);
580 /* Free the address... */
581 g_free(connect_data->hosts->data);
582 connect_data->hosts = g_slist_remove(connect_data->hosts, connect_data->hosts->data);
585 g_free(connect_data->host);
586 g_free(connect_data);
590 * Free all information dealing with a connection attempt and
591 * reset the connect_data to prepare for it to try to connect
592 * to another IP address.
594 * If an error message is passed in, then we know the connection
595 * attempt failed. If the connection attempt failed and
596 * connect_data->hosts is not empty then we try the next IP address.
597 * If the connection attempt failed and we have no more hosts
598 * try try then we call the callback with the given error message,
599 * then destroy the connect_data.
601 * @param error_message An error message explaining why the connection
602 * failed. This will be passed to the callback function
603 * specified in the call to purple_proxy_connect(). If the
604 * connection was successful then pass in null.
607 purple_proxy_connect_data_disconnect(PurpleProxyConnectData *connect_data, const gchar *error_message)
609 if (connect_data->child != NULL)
611 purple_proxy_connect_cancel(connect_data->child);
612 connect_data->child = NULL;
615 if (connect_data->inpa > 0)
617 purple_input_remove(connect_data->inpa);
618 connect_data->inpa = 0;
621 if (connect_data->fd >= 0)
623 close(connect_data->fd);
624 connect_data->fd = -1;
627 g_free(connect_data->write_buffer);
628 connect_data->write_buffer = NULL;
630 g_free(connect_data->read_buffer);
631 connect_data->read_buffer = NULL;
633 if (error_message != NULL)
635 purple_debug_error("proxy", "Connection attempt failed: %s\n",
637 if (connect_data->hosts != NULL)
638 try_connect(connect_data);
641 /* Everything failed! Tell the originator of the request. */
642 connect_data->connect_cb(connect_data->data, -1, error_message);
643 purple_proxy_connect_data_destroy(connect_data);
649 * This calls purple_proxy_connect_data_disconnect(), but it lets you
650 * specify the error_message using a printf()-like syntax.
653 purple_proxy_connect_data_disconnect_formatted(PurpleProxyConnectData *connect_data, const char *format, ...)
658 va_start(args, format);
659 tmp = g_strdup_vprintf(format, args);
662 purple_proxy_connect_data_disconnect(connect_data, tmp);
667 purple_proxy_connect_data_connected(PurpleProxyConnectData *connect_data)
669 purple_debug_info("proxy", "Connected to %s:%d.\n",
670 connect_data->host, connect_data->port);
672 connect_data->connect_cb(connect_data->data, connect_data->fd, NULL);
675 * We've passed the file descriptor to the protocol, so it's no longer
676 * our responsibility, and we should be careful not to free it when
677 * we destroy the connect_data.
679 connect_data->fd = -1;
681 purple_proxy_connect_data_disconnect(connect_data, NULL);
682 purple_proxy_connect_data_destroy(connect_data);
686 socket_ready_cb(gpointer data, gint source, PurpleInputCondition cond)
688 PurpleProxyConnectData *connect_data = data;
692 /* If the socket-connected message had already been triggered when connect_data
693 * was destroyed via purple_proxy_connect_cancel(), we may get here with a freed connect_data.
695 if (!PURPLE_PROXY_CONNECT_DATA_IS_VALID(connect_data))
698 purple_debug_info("proxy", "Connecting to %s:%d.\n",
699 connect_data->host, connect_data->port);
702 * purple_input_get_error after a non-blocking connect returns -1 if something is
703 * really messed up (bad descriptor, usually). Otherwise, it returns 0 and
704 * error holds what connect would have returned if it blocked until now.
705 * Thus, error == 0 is success, error == EINPROGRESS means "try again",
706 * and anything else is a real error.
708 * (error == EINPROGRESS can happen after a select because the kernel can
709 * be overly optimistic sometimes. select is just a hint that you might be
710 * able to do something.)
712 ret = purple_input_get_error(connect_data->fd, &error);
714 if (ret == 0 && error == EINPROGRESS) {
715 /* No worries - we'll be called again later */
716 /* TODO: Does this ever happen? */
717 purple_debug_info("proxy", "(ret == 0 && error == EINPROGRESS)\n");
721 if (ret != 0 || error != 0) {
724 purple_debug_error("proxy", "Error connecting to %s:%d (%s).\n",
725 connect_data->host, connect_data->port, g_strerror(error));
727 purple_proxy_connect_data_disconnect(connect_data, g_strerror(error));
731 purple_proxy_connect_data_connected(connect_data);
735 clean_connect(gpointer data)
737 purple_proxy_connect_data_connected(data);
743 proxy_connect_udp_none(PurpleProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen)
747 purple_debug_info("proxy", "UDP Connecting to %s:%d with no proxy\n",
748 connect_data->host, connect_data->port);
750 connect_data->fd = socket(addr->sa_family, SOCK_DGRAM, 0);
751 if (connect_data->fd < 0)
753 purple_proxy_connect_data_disconnect_formatted(connect_data,
754 _("Unable to create socket: %s"), g_strerror(errno));
758 flags = fcntl(connect_data->fd, F_GETFL);
759 fcntl(connect_data->fd, F_SETFL, flags | O_NONBLOCK);
761 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC);
764 if (connect(connect_data->fd, addr, addrlen) != 0)
766 if ((errno == EINPROGRESS) || (errno == EINTR))
768 purple_debug_info("proxy", "UDP Connection in progress\n");
769 connect_data->inpa = purple_input_add(connect_data->fd,
770 PURPLE_INPUT_WRITE, socket_ready_cb, connect_data);
774 purple_proxy_connect_data_disconnect(connect_data, g_strerror(errno));
780 * The connection happened IMMEDIATELY... strange, but whatever.
782 int error = ETIMEDOUT;
785 purple_debug_info("proxy", "UDP Connected immediately.\n");
787 ret = purple_input_get_error(connect_data->fd, &error);
788 if ((ret != 0) || (error != 0))
792 purple_proxy_connect_data_disconnect(connect_data, g_strerror(error));
797 * We want to call the "connected" callback eventually, but we
798 * don't want to call it before we return, just in case.
800 purple_timeout_add(10, clean_connect, connect_data);
805 proxy_connect_none(PurpleProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen)
809 purple_debug_info("proxy", "Connecting to %s:%d with no proxy\n",
810 connect_data->host, connect_data->port);
812 connect_data->fd = socket(addr->sa_family, SOCK_STREAM, 0);
813 if (connect_data->fd < 0)
815 purple_proxy_connect_data_disconnect_formatted(connect_data,
816 _("Unable to create socket: %s"), g_strerror(errno));
820 flags = fcntl(connect_data->fd, F_GETFL);
821 fcntl(connect_data->fd, F_SETFL, flags | O_NONBLOCK);
823 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC);
826 if (connect(connect_data->fd, addr, addrlen) != 0)
828 if ((errno == EINPROGRESS) || (errno == EINTR))
830 purple_debug_info("proxy", "Connection in progress\n");
831 connect_data->inpa = purple_input_add(connect_data->fd,
832 PURPLE_INPUT_WRITE, socket_ready_cb, connect_data);
836 purple_proxy_connect_data_disconnect(connect_data, g_strerror(errno));
842 * The connection happened IMMEDIATELY... strange, but whatever.
844 int error = ETIMEDOUT;
847 purple_debug_info("proxy", "Connected immediately.\n");
849 ret = purple_input_get_error(connect_data->fd, &error);
850 if ((ret != 0) || (error != 0))
854 purple_proxy_connect_data_disconnect(connect_data, g_strerror(error));
859 * We want to call the "connected" callback eventually, but we
860 * don't want to call it before we return, just in case.
862 purple_timeout_add(10, clean_connect, connect_data);
867 * This is a utility function used by the HTTP, SOCKS4 and SOCKS5
868 * connect functions. It writes data from a buffer to a socket.
869 * When all the data is written it sets up a watcher to read a
870 * response and call a specified function.
873 proxy_do_write(gpointer data, gint source, PurpleInputCondition cond)
875 PurpleProxyConnectData *connect_data;
876 const guchar *request;
881 request = connect_data->write_buffer + connect_data->written_len;
882 request_len = connect_data->write_buf_len - connect_data->written_len;
884 ret = write(connect_data->fd, request, request_len);
892 purple_proxy_connect_data_disconnect(connect_data, g_strerror(errno));
895 if (ret < request_len) {
896 connect_data->written_len += ret;
900 /* We're done writing data! Wait for a response. */
901 g_free(connect_data->write_buffer);
902 connect_data->write_buffer = NULL;
903 purple_input_remove(connect_data->inpa);
904 connect_data->inpa = purple_input_add(connect_data->fd,
905 PURPLE_INPUT_READ, connect_data->read_cb, connect_data);
908 #define HTTP_GOODSTRING "HTTP/1.0 200"
909 #define HTTP_GOODSTRING2 "HTTP/1.1 200"
912 * We're using an HTTP proxy for a non-port 80 tunnel. Read the
913 * response to the CONNECT request.
916 http_canread(gpointer data, gint source, PurpleInputCondition cond)
918 int len, headers_len, status = 0;
920 PurpleProxyConnectData *connect_data = data;
924 if (connect_data->read_buffer == NULL) {
925 connect_data->read_buf_len = 8192;
926 connect_data->read_buffer = g_malloc(connect_data->read_buf_len);
927 connect_data->read_len = 0;
930 p = (char *)connect_data->read_buffer + connect_data->read_len;
931 max_read = connect_data->read_buf_len - connect_data->read_len - 1;
933 len = read(connect_data->fd, p, max_read);
936 purple_proxy_connect_data_disconnect(connect_data,
937 _("Server closed the connection"));
947 purple_proxy_connect_data_disconnect_formatted(connect_data,
948 _("Lost connection with server: %s"), g_strerror(errno));
952 connect_data->read_len += len;
955 p = g_strstr_len((const gchar *)connect_data->read_buffer,
956 connect_data->read_len, "\r\n\r\n");
959 headers_len = (p - (char *)connect_data->read_buffer) + 4;
960 } else if(len == max_read)
965 error = strncmp((const char *)connect_data->read_buffer, "HTTP/", 5) != 0;
968 p = (char *)connect_data->read_buffer + 5;
969 major = strtol(p, &p, 10);
970 error = (major == 0) || (*p != '.');
974 minor = strtol(p, &p, 10);
978 status = strtol(p, &p, 10);
984 /* Read the contents */
985 p = g_strrstr((const gchar *)connect_data->read_buffer, "Content-Length: ");
990 p += strlen("Content-Length: ");
991 tmp = strchr(p, '\r');
998 /* Compensate for what has already been read */
999 len -= connect_data->read_len - headers_len;
1000 /* I'm assuming that we're doing this to prevent the server from
1001 complaining / breaking since we don't read the whole page */
1003 /* TODO: deal with EAGAIN (and other errors) better */
1004 if (read(connect_data->fd, &tmpc, 1) < 0 && errno != EAGAIN)
1010 purple_proxy_connect_data_disconnect_formatted(connect_data,
1011 _("Unable to parse response from HTTP proxy: %s"),
1012 connect_data->read_buffer);
1015 else if (status != 200) {
1016 purple_debug_error("proxy",
1017 "Proxy server replied with:\n%s\n",
1018 connect_data->read_buffer);
1020 if (status == 407 /* Proxy Auth */) {
1024 header = g_strrstr((const gchar *)connect_data->read_buffer,
1025 "Proxy-Authenticate: NTLM");
1026 if (header != NULL) {
1027 const char *header_end = header + strlen("Proxy-Authenticate: NTLM");
1028 const char *domain = purple_proxy_info_get_username(connect_data->gpi);
1029 char *username = NULL, hostname[256];
1033 ret = gethostname(hostname, sizeof(hostname));
1034 hostname[sizeof(hostname) - 1] = '\0';
1035 if (ret < 0 || hostname[0] == '\0') {
1036 purple_debug_warning("proxy", "gethostname() failed -- is your hostname set?");
1037 g_strlcpy(hostname, "localhost", sizeof(hostname));
1041 username = (char*) strchr(domain, '\\');
1042 if (username == NULL) {
1043 purple_proxy_connect_data_disconnect_formatted(connect_data,
1044 _("HTTP proxy connection error %d"), status);
1049 /* Is there a message? */
1050 if (*header_end == ' ') {
1051 /* Check for Type-2 */
1052 char *tmp = (char*) header;
1057 while(*tmp != '\r' && *tmp != '\0') tmp++;
1059 nonce = purple_ntlm_parse_type2(header_end, NULL);
1060 response = purple_ntlm_gen_type3(username,
1061 (gchar*) purple_proxy_info_get_password(connect_data->gpi),
1063 domain, nonce, NULL);
1065 } else /* Empty message */
1066 response = purple_ntlm_gen_type1(hostname, domain);
1070 request = g_strdup_printf(
1071 "CONNECT %s:%d HTTP/1.1\r\n"
1073 "Proxy-Authorization: NTLM %s\r\n"
1074 "Proxy-Connection: Keep-Alive\r\n\r\n",
1075 connect_data->host, connect_data->port,
1076 connect_data->host, connect_data->port,
1081 } else if (g_strrstr((const char *)connect_data->read_buffer, "Proxy-Authenticate: Basic") != NULL) {
1083 const char *username, *password;
1085 username = purple_proxy_info_get_username(connect_data->gpi);
1086 password = purple_proxy_info_get_password(connect_data->gpi);
1088 t1 = g_strdup_printf("%s:%s",
1089 username ? username : "",
1090 password ? password : "");
1091 t2 = purple_base64_encode((guchar *)t1, strlen(t1));
1094 request = g_strdup_printf(
1095 "CONNECT %s:%d HTTP/1.1\r\n"
1097 "Proxy-Authorization: Basic %s\r\n",
1098 connect_data->host, connect_data->port,
1099 connect_data->host, connect_data->port,
1105 purple_proxy_connect_data_disconnect_formatted(connect_data,
1106 _("HTTP proxy connection error %d"), status);
1110 purple_input_remove(connect_data->inpa);
1111 g_free(connect_data->read_buffer);
1112 connect_data->read_buffer = NULL;
1114 connect_data->write_buffer = (guchar *)request;
1115 connect_data->write_buf_len = strlen(request);
1116 connect_data->written_len = 0;
1118 connect_data->read_cb = http_canread;
1120 connect_data->inpa = purple_input_add(connect_data->fd,
1121 PURPLE_INPUT_WRITE, proxy_do_write, connect_data);
1123 proxy_do_write(connect_data, connect_data->fd, cond);
1128 if (status == 403) {
1130 purple_proxy_connect_data_disconnect_formatted(connect_data,
1131 _("Access denied: HTTP proxy server forbids port %d tunneling"),
1132 connect_data->port);
1134 purple_proxy_connect_data_disconnect_formatted(connect_data,
1135 _("HTTP proxy connection error %d"), status);
1138 purple_input_remove(connect_data->inpa);
1139 connect_data->inpa = 0;
1140 g_free(connect_data->read_buffer);
1141 connect_data->read_buffer = NULL;
1142 purple_debug_info("proxy", "HTTP proxy connection established\n");
1143 purple_proxy_connect_data_connected(connect_data);
1149 http_start_connect_tunneling(PurpleProxyConnectData *connect_data) {
1153 purple_debug_info("proxy", "Using CONNECT tunneling for %s:%d\n",
1154 connect_data->host, connect_data->port);
1156 request = g_string_sized_new(4096);
1157 g_string_append_printf(request,
1158 "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n",
1159 connect_data->host, connect_data->port,
1160 connect_data->host, connect_data->port);
1162 if (purple_proxy_info_get_username(connect_data->gpi) != NULL)
1164 char *t1, *t2, *ntlm_type1;
1167 ret = gethostname(hostname, sizeof(hostname));
1168 hostname[sizeof(hostname) - 1] = '\0';
1169 if (ret < 0 || hostname[0] == '\0') {
1170 purple_debug_warning("proxy", "gethostname() failed -- is your hostname set?");
1171 g_strlcpy(hostname, "localhost", sizeof(hostname));
1174 t1 = g_strdup_printf("%s:%s",
1175 purple_proxy_info_get_username(connect_data->gpi),
1176 purple_proxy_info_get_password(connect_data->gpi) ?
1177 purple_proxy_info_get_password(connect_data->gpi) : "");
1178 t2 = purple_base64_encode((const guchar *)t1, strlen(t1));
1181 ntlm_type1 = purple_ntlm_gen_type1(hostname, "");
1183 g_string_append_printf(request,
1184 "Proxy-Authorization: Basic %s\r\n"
1185 "Proxy-Authorization: NTLM %s\r\n"
1186 "Proxy-Connection: Keep-Alive\r\n",
1192 g_string_append(request, "\r\n");
1194 connect_data->write_buf_len = request->len;
1195 connect_data->write_buffer = (guchar *)g_string_free(request, FALSE);
1196 connect_data->written_len = 0;
1197 connect_data->read_cb = http_canread;
1199 connect_data->inpa = purple_input_add(connect_data->fd,
1200 PURPLE_INPUT_WRITE, proxy_do_write, connect_data);
1201 proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
1205 http_canwrite(gpointer data, gint source, PurpleInputCondition cond) {
1206 PurpleProxyConnectData *connect_data = data;
1207 int ret, error = ETIMEDOUT;
1209 purple_debug_info("proxy", "Connected to %s:%d.\n",
1210 connect_data->host, connect_data->port);
1212 if (connect_data->inpa > 0) {
1213 purple_input_remove(connect_data->inpa);
1214 connect_data->inpa = 0;
1217 ret = purple_input_get_error(connect_data->fd, &error);
1218 if (ret != 0 || error != 0) {
1221 purple_proxy_connect_data_disconnect(connect_data, g_strerror(error));
1225 if (connect_data->port == 80) {
1227 * If we're trying to connect to something running on
1228 * port 80 then we assume the traffic using this
1229 * connection is going to be HTTP traffic. If it's
1230 * not then this will fail (uglily). But it's good
1231 * to avoid using the CONNECT method because it's
1232 * not always allowed.
1234 purple_debug_info("proxy", "HTTP proxy connection established\n");
1235 purple_proxy_connect_data_connected(connect_data);
1237 http_start_connect_tunneling(connect_data);
1243 proxy_connect_http(PurpleProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen)
1247 purple_debug_info("proxy",
1248 "Connecting to %s:%d via %s:%d using HTTP\n",
1249 connect_data->host, connect_data->port,
1250 (purple_proxy_info_get_host(connect_data->gpi) ? purple_proxy_info_get_host(connect_data->gpi) : "(null)"),
1251 purple_proxy_info_get_port(connect_data->gpi));
1253 connect_data->fd = socket(addr->sa_family, SOCK_STREAM, 0);
1254 if (connect_data->fd < 0)
1256 purple_proxy_connect_data_disconnect_formatted(connect_data,
1257 _("Unable to create socket: %s"), g_strerror(errno));
1261 flags = fcntl(connect_data->fd, F_GETFL);
1262 fcntl(connect_data->fd, F_SETFL, flags | O_NONBLOCK);
1264 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC);
1267 if (connect(connect_data->fd, addr, addrlen) != 0) {
1268 if (errno == EINPROGRESS || errno == EINTR) {
1269 purple_debug_info("proxy", "Connection in progress\n");
1271 connect_data->inpa = purple_input_add(connect_data->fd,
1272 PURPLE_INPUT_WRITE, http_canwrite, connect_data);
1274 purple_proxy_connect_data_disconnect(connect_data, g_strerror(errno));
1276 purple_debug_info("proxy", "Connected immediately.\n");
1278 http_canwrite(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
1283 s4_canread(gpointer data, gint source, PurpleInputCondition cond)
1285 PurpleProxyConnectData *connect_data = data;
1289 /* This is really not going to block under normal circumstances, but to
1290 * be correct, we deal with the unlikely scenario */
1292 if (connect_data->read_buffer == NULL) {
1293 connect_data->read_buf_len = 12;
1294 connect_data->read_buffer = g_malloc(connect_data->read_buf_len);
1295 connect_data->read_len = 0;
1298 buf = connect_data->read_buffer + connect_data->read_len;
1299 max_read = connect_data->read_buf_len - connect_data->read_len;
1301 len = read(connect_data->fd, buf, max_read);
1303 if ((len < 0 && errno == EAGAIN) || (len > 0 && len + connect_data->read_len < 4))
1305 else if (len + connect_data->read_len >= 4) {
1306 if (connect_data->read_buffer[1] == 90) {
1307 purple_proxy_connect_data_connected(connect_data);
1312 purple_proxy_connect_data_disconnect(connect_data, g_strerror(errno));
1316 s4_host_resolved(GSList *hosts, gpointer data, const char *error_message)
1318 PurpleProxyConnectData *connect_data = data;
1319 unsigned char packet[9];
1320 struct sockaddr *addr;
1322 connect_data->query_data = NULL;
1324 if (error_message != NULL) {
1325 purple_proxy_connect_data_disconnect(connect_data, error_message);
1329 if (hosts == NULL) {
1330 purple_proxy_connect_data_disconnect_formatted(connect_data,
1331 _("Error resolving %s"), connect_data->host);
1335 /* Discard the length... */
1336 hosts = g_slist_delete_link(hosts, hosts);
1338 hosts = g_slist_delete_link(hosts, hosts);
1342 packet[2] = connect_data->port >> 8;
1343 packet[3] = connect_data->port & 0xff;
1344 memcpy(packet + 4, &((struct sockaddr_in *)addr)->sin_addr.s_addr, 4);
1349 /* We could try the other hosts, but hopefully that shouldn't be necessary */
1350 while (hosts != NULL) {
1351 /* Discard the length... */
1352 hosts = g_slist_delete_link(hosts, hosts);
1353 /* Free the address... */
1354 g_free(hosts->data);
1355 hosts = g_slist_delete_link(hosts, hosts);
1358 connect_data->write_buffer = g_memdup(packet, sizeof(packet));
1359 connect_data->write_buf_len = sizeof(packet);
1360 connect_data->written_len = 0;
1361 connect_data->read_cb = s4_canread;
1363 connect_data->inpa = purple_input_add(connect_data->fd, PURPLE_INPUT_WRITE, proxy_do_write, connect_data);
1365 proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
1369 s4_canwrite(gpointer data, gint source, PurpleInputCondition cond)
1371 PurpleProxyConnectData *connect_data = data;
1372 int error = ETIMEDOUT;
1375 purple_debug_info("socks4 proxy", "Connected.\n");
1377 if (connect_data->inpa > 0) {
1378 purple_input_remove(connect_data->inpa);
1379 connect_data->inpa = 0;
1382 ret = purple_input_get_error(connect_data->fd, &error);
1383 if ((ret != 0) || (error != 0)) {
1386 purple_proxy_connect_data_disconnect(connect_data, g_strerror(error));
1391 * The socks4 spec doesn't include support for doing host name lookups by
1392 * the proxy. Many socks4 servers do this via the "socks4a" extension to
1393 * the protocol. There doesn't appear to be a way to detect if a server
1394 * supports this, so we require that the user set a global option.
1396 if (purple_prefs_get_bool("/purple/proxy/socks4_remotedns")) {
1397 unsigned char packet[9];
1400 purple_debug_info("socks4 proxy", "Attempting to use remote DNS.\n");
1404 packet[2] = connect_data->port >> 8;
1405 packet[3] = connect_data->port & 0xff;
1412 len = sizeof(packet) + strlen(connect_data->host) + 1;
1414 connect_data->write_buffer = g_malloc0(len);
1415 memcpy(connect_data->write_buffer, packet, sizeof(packet));
1416 memcpy(connect_data->write_buffer + sizeof(packet), connect_data->host, strlen(connect_data->host));
1417 connect_data->write_buf_len = len;
1418 connect_data->written_len = 0;
1419 connect_data->read_cb = s4_canread;
1421 connect_data->inpa = purple_input_add(connect_data->fd, PURPLE_INPUT_WRITE, proxy_do_write, connect_data);
1423 proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
1425 connect_data->query_data = purple_dnsquery_a_account(
1426 connect_data->account, connect_data->host,
1427 connect_data->port, s4_host_resolved, connect_data);
1429 if (connect_data->query_data == NULL) {
1430 purple_debug_error("proxy", "dns query failed unexpectedly.\n");
1431 purple_proxy_connect_data_destroy(connect_data);
1437 proxy_connect_socks4(PurpleProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen)
1441 purple_debug_info("proxy",
1442 "Connecting to %s:%d via %s:%d using SOCKS4\n",
1443 connect_data->host, connect_data->port,
1444 purple_proxy_info_get_host(connect_data->gpi),
1445 purple_proxy_info_get_port(connect_data->gpi));
1447 connect_data->fd = socket(addr->sa_family, SOCK_STREAM, 0);
1448 if (connect_data->fd < 0)
1450 purple_proxy_connect_data_disconnect_formatted(connect_data,
1451 _("Unable to create socket: %s"), g_strerror(errno));
1455 flags = fcntl(connect_data->fd, F_GETFL);
1456 fcntl(connect_data->fd, F_SETFL, flags | O_NONBLOCK);
1458 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC);
1461 if (connect(connect_data->fd, addr, addrlen) != 0)
1463 if ((errno == EINPROGRESS) || (errno == EINTR))
1465 purple_debug_info("proxy", "Connection in progress.\n");
1466 connect_data->inpa = purple_input_add(connect_data->fd,
1467 PURPLE_INPUT_WRITE, s4_canwrite, connect_data);
1471 purple_proxy_connect_data_disconnect(connect_data, g_strerror(errno));
1476 purple_debug_info("proxy", "Connected immediately.\n");
1478 s4_canwrite(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
1483 s5_ensure_buffer_length(PurpleProxyConnectData *connect_data, int len)
1485 if(connect_data->read_len < len) {
1486 if(connect_data->read_buf_len < len) {
1487 /* it's not just that we haven't read enough, it's that we haven't tried to read enough yet */
1488 purple_debug_info("s5", "reallocing from %" G_GSIZE_FORMAT
1489 " to %d\n", connect_data->read_buf_len, len);
1490 connect_data->read_buf_len = len;
1491 connect_data->read_buffer = g_realloc(connect_data->read_buffer, connect_data->read_buf_len);
1500 s5_canread_again(gpointer data, gint source, PurpleInputCondition cond)
1503 PurpleProxyConnectData *connect_data = data;
1506 if (connect_data->read_buffer == NULL) {
1507 connect_data->read_buf_len = 5;
1508 connect_data->read_buffer = g_malloc(connect_data->read_buf_len);
1509 connect_data->read_len = 0;
1512 dest = connect_data->read_buffer + connect_data->read_len;
1513 buf = connect_data->read_buffer;
1515 len = read(connect_data->fd, dest, (connect_data->read_buf_len - connect_data->read_len));
1519 purple_proxy_connect_data_disconnect(connect_data,
1520 _("Server closed the connection"));
1526 if (errno == EAGAIN)
1531 purple_proxy_connect_data_disconnect_formatted(connect_data,
1532 _("Lost connection with server: %s"), g_strerror(errno));
1536 connect_data->read_len += len;
1538 if(connect_data->read_len < 4)
1541 if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
1542 if ((buf[0] == 0x05) && (buf[1] < 0x09)) {
1543 purple_debug_error("socks5 proxy", "%s", socks5errors[buf[1]]);
1544 purple_proxy_connect_data_disconnect(connect_data,
1545 socks5errors[buf[1]]);
1547 purple_debug_error("socks5 proxy", "Bad data.\n");
1548 purple_proxy_connect_data_disconnect(connect_data,
1549 _("Received invalid data on connection with server"));
1554 /* Skip past BND.ADDR */
1556 case 0x01: /* the address is a version-4 IP address, with a length of 4 octets */
1557 if(!s5_ensure_buffer_length(connect_data, 4 + 4))
1561 case 0x03: /* the address field contains a fully-qualified domain name. The first
1562 octet of the address field contains the number of octets of name that
1563 follow, there is no terminating NUL octet. */
1564 if(!s5_ensure_buffer_length(connect_data, 4 + 1))
1567 if(!s5_ensure_buffer_length(connect_data, 4 + 1 + buf[0]))
1571 case 0x04: /* the address is a version-6 IP address, with a length of 16 octets */
1572 if(!s5_ensure_buffer_length(connect_data, 4 + 16))
1577 purple_debug_error("socks5 proxy", "Invalid ATYP received (0x%X)\n", buf[3]);
1578 purple_proxy_connect_data_disconnect(connect_data,
1579 _("Received invalid data on connection with server"));
1583 /* Skip past BND.PORT */
1584 if(!s5_ensure_buffer_length(connect_data, (buf - connect_data->read_buffer) + 2))
1587 purple_proxy_connect_data_connected(connect_data);
1591 s5_sendconnect(gpointer data, int source)
1593 PurpleProxyConnectData *connect_data = data;
1594 size_t hlen = strlen(connect_data->host);
1595 connect_data->write_buf_len = 5 + hlen + 2;
1596 connect_data->write_buffer = g_malloc(connect_data->write_buf_len);
1597 connect_data->written_len = 0;
1599 connect_data->write_buffer[0] = 0x05;
1600 connect_data->write_buffer[1] = 0x01; /* CONNECT */
1601 connect_data->write_buffer[2] = 0x00; /* reserved */
1602 connect_data->write_buffer[3] = 0x03; /* address type -- host name */
1603 connect_data->write_buffer[4] = hlen;
1604 memcpy(connect_data->write_buffer + 5, connect_data->host, hlen);
1605 connect_data->write_buffer[5 + hlen] = connect_data->port >> 8;
1606 connect_data->write_buffer[5 + hlen + 1] = connect_data->port & 0xff;
1608 connect_data->read_cb = s5_canread_again;
1610 connect_data->inpa = purple_input_add(connect_data->fd, PURPLE_INPUT_WRITE, proxy_do_write, connect_data);
1611 proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
1615 s5_readauth(gpointer data, gint source, PurpleInputCondition cond)
1617 PurpleProxyConnectData *connect_data = data;
1620 if (connect_data->read_buffer == NULL) {
1621 connect_data->read_buf_len = 2;
1622 connect_data->read_buffer = g_malloc(connect_data->read_buf_len);
1623 connect_data->read_len = 0;
1626 purple_debug_info("socks5 proxy", "Got auth response.\n");
1628 len = read(connect_data->fd, connect_data->read_buffer + connect_data->read_len,
1629 connect_data->read_buf_len - connect_data->read_len);
1633 purple_proxy_connect_data_disconnect(connect_data,
1634 _("Server closed the connection"));
1640 if (errno == EAGAIN)
1645 purple_proxy_connect_data_disconnect_formatted(connect_data,
1646 _("Lost connection with server: %s"), g_strerror(errno));
1650 connect_data->read_len += len;
1651 if (connect_data->read_len < 2)
1654 purple_input_remove(connect_data->inpa);
1655 connect_data->inpa = 0;
1657 if ((connect_data->read_buffer[0] != 0x01) || (connect_data->read_buffer[1] != 0x00)) {
1658 purple_proxy_connect_data_disconnect(connect_data,
1659 _("Received invalid data on connection with server"));
1663 g_free(connect_data->read_buffer);
1664 connect_data->read_buffer = NULL;
1666 s5_sendconnect(connect_data, connect_data->fd);
1670 hmacmd5_chap(const unsigned char * challenge, int challen, const char * passwd, unsigned char * response)
1672 PurpleCipher *cipher;
1673 PurpleCipherContext *ctx;
1675 unsigned char Kxoripad[65];
1676 unsigned char Kxoropad[65];
1679 cipher = purple_ciphers_find_cipher("md5");
1680 ctx = purple_cipher_context_new(cipher, NULL);
1682 memset(Kxoripad,0,sizeof(Kxoripad));
1683 memset(Kxoropad,0,sizeof(Kxoropad));
1685 pwlen=strlen(passwd);
1687 purple_cipher_context_append(ctx, (const guchar *)passwd, strlen(passwd));
1688 purple_cipher_context_digest(ctx, sizeof(Kxoripad), Kxoripad, NULL);
1691 memcpy(Kxoripad, passwd, pwlen);
1693 memcpy(Kxoropad,Kxoripad,pwlen);
1695 for (i=0;i<64;i++) {
1700 purple_cipher_context_reset(ctx, NULL);
1701 purple_cipher_context_append(ctx, Kxoripad, 64);
1702 purple_cipher_context_append(ctx, challenge, challen);
1703 purple_cipher_context_digest(ctx, sizeof(Kxoripad), Kxoripad, NULL);
1705 purple_cipher_context_reset(ctx, NULL);
1706 purple_cipher_context_append(ctx, Kxoropad, 64);
1707 purple_cipher_context_append(ctx, Kxoripad, 16);
1708 purple_cipher_context_digest(ctx, 16, response, NULL);
1710 purple_cipher_context_destroy(ctx);
1714 s5_readchap(gpointer data, gint source, PurpleInputCondition cond);
1717 * Return how many bytes we processed
1718 * -1 means we've shouldn't keep reading from the buffer
1721 s5_parse_chap_msg(PurpleProxyConnectData *connect_data)
1723 guchar *buf, *cmdbuf = connect_data->read_buffer;
1724 int len, navas, currentav;
1726 purple_debug_misc("socks5 proxy", "Reading CHAP message: %x\n", *cmdbuf);
1728 if (*cmdbuf != 0x01) {
1729 purple_proxy_connect_data_disconnect(connect_data,
1730 _("Received invalid data on connection with server"));
1737 purple_debug_misc("socks5 proxy", "Expecting %d attribute(s).\n", navas);
1741 for (currentav = 0; currentav < navas; currentav++) {
1743 len = connect_data->read_len - (cmdbuf - connect_data->read_buffer);
1744 /* We don't have enough data to even know how long the next attribute is,
1745 * or we don't have the full length of the next attribute. */
1746 if (len < 2 || len < (cmdbuf[1] + 2)) {
1747 /* Clear out the attributes that have been read - decrease the attribute count */
1748 connect_data->read_buffer[1] = navas - currentav;
1749 /* Move the unprocessed data into the first attribute position */
1750 memmove((connect_data->read_buffer + 2), cmdbuf, len);
1751 /* Decrease the read count accordingly */
1752 connect_data->read_len = len + 2;
1754 purple_debug_info("socks5 proxy", "Need more data to retrieve attribute %d.\n", currentav);
1761 if (cmdbuf[1] == 0) {
1762 purple_debug_error("socks5 proxy", "Attribute %x Value length of 0; ignoring.\n", cmdbuf[0]);
1767 switch (cmdbuf[0]) {
1769 purple_debug_info("socks5 proxy", "Received STATUS of %x\n", buf[0]);
1770 /* Did auth work? */
1771 if (buf[0] == 0x00) {
1772 purple_input_remove(connect_data->inpa);
1773 connect_data->inpa = 0;
1774 g_free(connect_data->read_buffer);
1775 connect_data->read_buffer = NULL;
1777 s5_sendconnect(connect_data, connect_data->fd);
1780 purple_debug_warning("proxy",
1781 "socks5 CHAP authentication "
1782 "failed. Disconnecting...");
1783 purple_proxy_connect_data_disconnect(connect_data,
1784 _("Authentication failed"));
1788 /* We've already validated that cmdbuf[1] is sane. */
1789 purple_debug_info("socks5 proxy", "Received TEXT-MESSAGE of '%.*s'\n", (int) cmdbuf[1], buf);
1792 purple_debug_info("socks5 proxy", "Received CHALLENGE\n");
1793 /* Server wants our credentials */
1795 connect_data->write_buf_len = 16 + 4;
1796 connect_data->write_buffer = g_malloc(connect_data->write_buf_len);
1797 connect_data->written_len = 0;
1799 hmacmd5_chap(buf, cmdbuf[1],
1800 purple_proxy_info_get_password(connect_data->gpi),
1801 connect_data->write_buffer + 4);
1802 /* TODO: What about USER-IDENTITY? */
1803 connect_data->write_buffer[0] = 0x01;
1804 connect_data->write_buffer[1] = 0x01;
1805 connect_data->write_buffer[2] = 0x04;
1806 connect_data->write_buffer[3] = 0x10;
1808 purple_input_remove(connect_data->inpa);
1809 g_free(connect_data->read_buffer);
1810 connect_data->read_buffer = NULL;
1812 connect_data->read_cb = s5_readchap;
1814 connect_data->inpa = purple_input_add(connect_data->fd,
1815 PURPLE_INPUT_WRITE, proxy_do_write, connect_data);
1817 proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
1820 purple_debug_info("socks5 proxy", "Received ALGORIGTHMS of %x\n", buf[0]);
1821 /* Server wants to select an algorithm */
1822 if (buf[0] != 0x85) {
1823 /* Only currently support HMAC-MD5 */
1824 purple_debug_warning("proxy",
1825 "Server tried to select an "
1826 "algorithm that we did not advertise "
1827 "as supporting. This is a violation "
1828 "of the socks5 CHAP specification. "
1829 "Disconnecting...");
1830 purple_proxy_connect_data_disconnect(connect_data,
1831 _("Received invalid data on connection with server"));
1836 purple_debug_info("socks5 proxy", "Received unused command %x, length=%d\n", cmdbuf[0], cmdbuf[1]);
1838 cmdbuf = buf + cmdbuf[1];
1841 return (cmdbuf - connect_data->read_buffer);
1845 s5_readchap(gpointer data, gint source, PurpleInputCondition cond)
1848 PurpleProxyConnectData *connect_data = data;
1851 purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Got CHAP response.\n");
1853 if (connect_data->read_buffer == NULL) {
1854 /* A big enough butfer to read the message header (2 bytes) and at least one complete attribute and value (1 + 1 + 255). */
1855 connect_data->read_buf_len = 259;
1856 connect_data->read_buffer = g_malloc(connect_data->read_buf_len);
1857 connect_data->read_len = 0;
1860 if (connect_data->read_buf_len - connect_data->read_len == 0) {
1861 /*If the stuff below is right, this shouldn't be possible. */
1862 purple_debug_error("socks5 proxy", "This is about to suck because the read buffer is full (shouldn't happen).\n");
1865 len = read(connect_data->fd, connect_data->read_buffer + connect_data->read_len,
1866 connect_data->read_buf_len - connect_data->read_len);
1869 purple_proxy_connect_data_disconnect(connect_data,
1870 _("Server closed the connection"));
1875 if (errno == EAGAIN)
1880 purple_proxy_connect_data_disconnect_formatted(connect_data,
1881 _("Lost connection with server: %s"), g_strerror(errno));
1885 connect_data->read_len += len;
1887 /* We may have read more than one message into the buffer, we need to make sure to process them all */
1890 /* We need more to be able to read this message */
1891 if (connect_data->read_len < 2)
1894 msg_ret = s5_parse_chap_msg(connect_data);
1899 /* See if we have another message already in the buffer */
1900 if ((len = connect_data->read_len - msg_ret) > 0) {
1902 /* Move on to the next message */
1903 memmove(connect_data->read_buffer, connect_data->read_buffer + msg_ret, len);
1904 /* Decrease the read count accordingly */
1905 connect_data->read_len = len;
1907 /* Try to read the message that connect_data->read_buffer now points to */
1914 /* Fell through. We ran out of CHAP events to process, but haven't
1915 * succeeded or failed authentication - there may be more to come.
1916 * If this is the case, come straight back here. */
1918 purple_debug_info("socks5 proxy", "Waiting for another message from which to read CHAP info.\n");
1920 /* We've processed all the available attributes, so get ready for a whole new message */
1921 g_free(connect_data->read_buffer);
1922 connect_data->read_buffer = NULL;
1926 s5_canread(gpointer data, gint source, PurpleInputCondition cond)
1928 PurpleProxyConnectData *connect_data = data;
1931 if (connect_data->read_buffer == NULL) {
1932 connect_data->read_buf_len = 2;
1933 connect_data->read_buffer = g_malloc(connect_data->read_buf_len);
1934 connect_data->read_len = 0;
1937 purple_debug_info("socks5 proxy", "Able to read.\n");
1939 len = read(connect_data->fd, connect_data->read_buffer + connect_data->read_len,
1940 connect_data->read_buf_len - connect_data->read_len);
1944 purple_proxy_connect_data_disconnect(connect_data,
1945 _("Server closed the connection"));
1951 if (errno == EAGAIN)
1956 purple_proxy_connect_data_disconnect_formatted(connect_data,
1957 _("Lost connection with server: %s"), g_strerror(errno));
1961 connect_data->read_len += len;
1962 if (connect_data->read_len < 2)
1965 purple_input_remove(connect_data->inpa);
1966 connect_data->inpa = 0;
1968 if ((connect_data->read_buffer[0] != 0x05) || (connect_data->read_buffer[1] == 0xff)) {
1969 purple_proxy_connect_data_disconnect(connect_data,
1970 _("Received invalid data on connection with server"));
1974 if (connect_data->read_buffer[1] == 0x02) {
1978 u = purple_proxy_info_get_username(connect_data->gpi);
1979 p = purple_proxy_info_get_password(connect_data->gpi);
1981 i = (u == NULL) ? 0 : strlen(u);
1982 j = (p == NULL) ? 0 : strlen(p);
1984 connect_data->write_buf_len = 1 + 1 + i + 1 + j;
1985 connect_data->write_buffer = g_malloc(connect_data->write_buf_len);
1986 connect_data->written_len = 0;
1988 connect_data->write_buffer[0] = 0x01; /* version 1 */
1989 connect_data->write_buffer[1] = i;
1991 memcpy(connect_data->write_buffer + 2, u, i);
1992 connect_data->write_buffer[2 + i] = j;
1994 memcpy(connect_data->write_buffer + 2 + i + 1, p, j);
1996 g_free(connect_data->read_buffer);
1997 connect_data->read_buffer = NULL;
1999 connect_data->read_cb = s5_readauth;
2001 connect_data->inpa = purple_input_add(connect_data->fd, PURPLE_INPUT_WRITE,
2002 proxy_do_write, connect_data);
2004 proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
2007 } else if (connect_data->read_buffer[1] == 0x03) {
2009 userlen = strlen(purple_proxy_info_get_username(connect_data->gpi));
2011 connect_data->write_buf_len = 7 + userlen;
2012 connect_data->write_buffer = g_malloc(connect_data->write_buf_len);
2013 connect_data->written_len = 0;
2015 connect_data->write_buffer[0] = 0x01;
2016 connect_data->write_buffer[1] = 0x02;
2017 connect_data->write_buffer[2] = 0x11;
2018 connect_data->write_buffer[3] = 0x01;
2019 connect_data->write_buffer[4] = 0x85;
2020 connect_data->write_buffer[5] = 0x02;
2021 connect_data->write_buffer[6] = userlen;
2022 memcpy(connect_data->write_buffer + 7,
2023 purple_proxy_info_get_username(connect_data->gpi), userlen);
2025 g_free(connect_data->read_buffer);
2026 connect_data->read_buffer = NULL;
2028 connect_data->read_cb = s5_readchap;
2030 connect_data->inpa = purple_input_add(connect_data->fd, PURPLE_INPUT_WRITE,
2031 proxy_do_write, connect_data);
2033 proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
2037 g_free(connect_data->read_buffer);
2038 connect_data->read_buffer = NULL;
2040 s5_sendconnect(connect_data, connect_data->fd);
2045 s5_canwrite(gpointer data, gint source, PurpleInputCondition cond)
2047 unsigned char buf[5];
2049 PurpleProxyConnectData *connect_data = data;
2050 int error = ETIMEDOUT;
2053 purple_debug_info("socks5 proxy", "Connected.\n");
2055 if (connect_data->inpa > 0)
2057 purple_input_remove(connect_data->inpa);
2058 connect_data->inpa = 0;
2061 ret = purple_input_get_error(connect_data->fd, &error);
2062 if ((ret != 0) || (error != 0))
2066 purple_proxy_connect_data_disconnect(connect_data, g_strerror(error));
2071 buf[0] = 0x05; /* SOCKS version 5 */
2073 if (purple_proxy_info_get_username(connect_data->gpi) != NULL) {
2074 buf[1] = 0x03; /* three methods */
2075 buf[2] = 0x00; /* no authentication */
2076 buf[3] = 0x03; /* CHAP authentication */
2077 buf[4] = 0x02; /* username/password authentication */
2086 connect_data->write_buf_len = i;
2087 connect_data->write_buffer = g_malloc(connect_data->write_buf_len);
2088 memcpy(connect_data->write_buffer, buf, i);
2090 connect_data->read_cb = s5_canread;
2092 connect_data->inpa = purple_input_add(connect_data->fd, PURPLE_INPUT_WRITE, proxy_do_write, connect_data);
2093 proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
2097 proxy_connect_socks5(PurpleProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen)
2101 purple_debug_info("proxy",
2102 "Connecting to %s:%d via %s:%d using SOCKS5\n",
2103 connect_data->host, connect_data->port,
2104 purple_proxy_info_get_host(connect_data->gpi),
2105 purple_proxy_info_get_port(connect_data->gpi));
2107 connect_data->fd = socket(addr->sa_family, SOCK_STREAM, 0);
2108 if (connect_data->fd < 0)
2110 purple_proxy_connect_data_disconnect_formatted(connect_data,
2111 _("Unable to create socket: %s"), g_strerror(errno));
2115 flags = fcntl(connect_data->fd, F_GETFL);
2116 fcntl(connect_data->fd, F_SETFL, flags | O_NONBLOCK);
2118 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC);
2121 if (connect(connect_data->fd, addr, addrlen) != 0)
2123 if ((errno == EINPROGRESS) || (errno == EINTR))
2125 purple_debug_info("socks5 proxy", "Connection in progress\n");
2126 connect_data->inpa = purple_input_add(connect_data->fd,
2127 PURPLE_INPUT_WRITE, s5_canwrite, connect_data);
2131 purple_proxy_connect_data_disconnect(connect_data, g_strerror(errno));
2136 purple_debug_info("proxy", "Connected immediately.\n");
2138 s5_canwrite(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
2143 * This function attempts to connect to the next IP address in the list
2144 * of IP addresses returned to us by purple_dnsquery_a() and attemps
2145 * to connect to each one. This is called after the hostname is
2146 * resolved, and each time a connection attempt fails (assuming there
2147 * is another IP address to try).
2149 #ifndef INET6_ADDRSTRLEN
2150 #define INET6_ADDRSTRLEN 46
2153 static void try_connect(PurpleProxyConnectData *connect_data)
2156 struct sockaddr *addr;
2157 char ipaddr[INET6_ADDRSTRLEN];
2159 addrlen = GPOINTER_TO_INT(connect_data->hosts->data);
2160 connect_data->hosts = g_slist_remove(connect_data->hosts, connect_data->hosts->data);
2161 addr = connect_data->hosts->data;
2162 connect_data->hosts = g_slist_remove(connect_data->hosts, connect_data->hosts->data);
2163 #ifdef HAVE_INET_NTOP
2164 if (addr->sa_family == AF_INET)
2165 inet_ntop(addr->sa_family, &((struct sockaddr_in *)addr)->sin_addr,
2166 ipaddr, sizeof(ipaddr));
2167 else if (addr->sa_family == AF_INET6)
2168 inet_ntop(addr->sa_family, &((struct sockaddr_in6 *)addr)->sin6_addr,
2169 ipaddr, sizeof(ipaddr));
2171 memcpy(ipaddr, inet_ntoa(((struct sockaddr_in *)addr)->sin_addr),
2174 purple_debug_info("proxy", "Attempting connection to %s\n", ipaddr);
2176 if (connect_data->socket_type == SOCK_DGRAM) {
2177 proxy_connect_udp_none(connect_data, addr, addrlen);
2182 switch (purple_proxy_info_get_type(connect_data->gpi)) {
2183 case PURPLE_PROXY_NONE:
2184 proxy_connect_none(connect_data, addr, addrlen);
2187 case PURPLE_PROXY_HTTP:
2188 proxy_connect_http(connect_data, addr, addrlen);
2191 case PURPLE_PROXY_SOCKS4:
2192 proxy_connect_socks4(connect_data, addr, addrlen);
2195 case PURPLE_PROXY_SOCKS5:
2196 case PURPLE_PROXY_TOR:
2197 proxy_connect_socks5(connect_data, addr, addrlen);
2200 case PURPLE_PROXY_USE_ENVVAR:
2201 proxy_connect_http(connect_data, addr, addrlen);
2212 connection_host_resolved(GSList *hosts, gpointer data,
2213 const char *error_message)
2215 PurpleProxyConnectData *connect_data;
2217 connect_data = data;
2218 connect_data->query_data = NULL;
2220 if (error_message != NULL)
2222 purple_proxy_connect_data_disconnect(connect_data, error_message);
2228 purple_proxy_connect_data_disconnect(connect_data, _("Unable to resolve hostname"));
2232 connect_data->hosts = hosts;
2234 try_connect(connect_data);
2238 purple_proxy_get_setup(PurpleAccount *account)
2240 PurpleProxyInfo *gpi = NULL;
2243 /* This is used as a fallback so we don't overwrite the selected proxy type */
2244 static PurpleProxyInfo *tmp_none_proxy_info = NULL;
2245 if (!tmp_none_proxy_info) {
2246 tmp_none_proxy_info = purple_proxy_info_new();
2247 purple_proxy_info_set_type(tmp_none_proxy_info, PURPLE_PROXY_NONE);
2250 if (account && purple_account_get_proxy_info(account) != NULL) {
2251 gpi = purple_account_get_proxy_info(account);
2252 if (purple_proxy_info_get_type(gpi) == PURPLE_PROXY_USE_GLOBAL)
2256 if (purple_running_gnome())
2257 gpi = purple_gnome_proxy_get_info();
2259 gpi = purple_global_proxy_get_info();
2262 if (purple_proxy_info_get_type(gpi) == PURPLE_PROXY_USE_ENVVAR) {
2263 if ((tmp = g_getenv("HTTP_PROXY")) != NULL ||
2264 (tmp = g_getenv("http_proxy")) != NULL ||
2265 (tmp = g_getenv("HTTPPROXY")) != NULL) {
2266 char *proxyhost, *proxyuser, *proxypasswd;
2269 /* http_proxy-format:
2270 * export http_proxy="http://user:passwd@your.proxy.server:port/"
2272 if(purple_url_parse(tmp, &proxyhost, &proxyport, NULL, &proxyuser, &proxypasswd)) {
2273 purple_proxy_info_set_host(gpi, proxyhost);
2276 purple_proxy_info_set_username(gpi, proxyuser);
2279 purple_proxy_info_set_password(gpi, proxypasswd);
2280 g_free(proxypasswd);
2282 /* only for backward compatibility */
2283 if (proxyport == 80 &&
2284 ((tmp = g_getenv("HTTP_PROXY_PORT")) != NULL ||
2285 (tmp = g_getenv("http_proxy_port")) != NULL ||
2286 (tmp = g_getenv("HTTPPROXYPORT")) != NULL))
2287 proxyport = atoi(tmp);
2289 purple_proxy_info_set_port(gpi, proxyport);
2291 /* XXX: Do we want to skip this step if user/password were part of url? */
2292 if ((tmp = g_getenv("HTTP_PROXY_USER")) != NULL ||
2293 (tmp = g_getenv("http_proxy_user")) != NULL ||
2294 (tmp = g_getenv("HTTPPROXYUSER")) != NULL)
2295 purple_proxy_info_set_username(gpi, tmp);
2297 if ((tmp = g_getenv("HTTP_PROXY_PASS")) != NULL ||
2298 (tmp = g_getenv("http_proxy_pass")) != NULL ||
2299 (tmp = g_getenv("HTTPPROXYPASS")) != NULL)
2300 purple_proxy_info_set_password(gpi, tmp);
2305 PurpleProxyInfo *wgpi;
2306 if ((wgpi = purple_win32_proxy_get_info()) != NULL)
2309 /* no proxy environment variable found, don't use a proxy */
2310 purple_debug_info("proxy", "No environment settings found, not using a proxy\n");
2311 gpi = tmp_none_proxy_info;
2319 PurpleProxyConnectData *
2320 purple_proxy_connect(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_STREAM;
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);
2341 connect_data->account = account;
2343 if ((purple_proxy_info_get_type(connect_data->gpi) != PURPLE_PROXY_NONE) &&
2344 (purple_proxy_info_get_host(connect_data->gpi) == NULL ||
2345 purple_proxy_info_get_port(connect_data->gpi) <= 0)) {
2347 purple_notify_error(NULL, NULL, _("Invalid proxy settings"), _("Either the host name or port number specified for your given proxy type is invalid."));
2348 purple_proxy_connect_data_destroy(connect_data);
2352 switch (purple_proxy_info_get_type(connect_data->gpi))
2354 case PURPLE_PROXY_NONE:
2357 case PURPLE_PROXY_HTTP:
2358 case PURPLE_PROXY_SOCKS4:
2359 case PURPLE_PROXY_SOCKS5:
2360 case PURPLE_PROXY_TOR:
2361 case PURPLE_PROXY_USE_ENVVAR:
2362 connecthost = purple_proxy_info_get_host(connect_data->gpi);
2363 connectport = purple_proxy_info_get_port(connect_data->gpi);
2367 purple_debug_error("proxy", "Invalid Proxy type (%d) specified.\n",
2368 purple_proxy_info_get_type(connect_data->gpi));
2369 purple_proxy_connect_data_destroy(connect_data);
2373 connect_data->query_data = purple_dnsquery_a_account(account, connecthost,
2374 connectport, connection_host_resolved, connect_data);
2375 if (connect_data->query_data == NULL)
2377 purple_debug_error("proxy", "dns query failed unexpectedly.\n");
2378 purple_proxy_connect_data_destroy(connect_data);
2382 handles = g_slist_prepend(handles, connect_data);
2384 return connect_data;
2387 PurpleProxyConnectData *
2388 purple_proxy_connect_udp(void *handle, PurpleAccount *account,
2389 const char *host, int port,
2390 PurpleProxyConnectFunction connect_cb, gpointer data)
2392 const char *connecthost = host;
2393 int connectport = port;
2394 PurpleProxyConnectData *connect_data;
2396 g_return_val_if_fail(host != NULL, NULL);
2397 g_return_val_if_fail(port > 0, NULL);
2398 g_return_val_if_fail(connect_cb != NULL, NULL);
2400 connect_data = g_new0(PurpleProxyConnectData, 1);
2401 connect_data->fd = -1;
2402 connect_data->socket_type = SOCK_DGRAM;
2403 connect_data->handle = handle;
2404 connect_data->connect_cb = connect_cb;
2405 connect_data->data = data;
2406 connect_data->host = g_strdup(host);
2407 connect_data->port = port;
2408 connect_data->gpi = purple_proxy_get_setup(account);
2409 connect_data->account = account;
2411 if ((purple_proxy_info_get_type(connect_data->gpi) != PURPLE_PROXY_NONE) &&
2412 (purple_proxy_info_get_host(connect_data->gpi) == NULL ||
2413 purple_proxy_info_get_port(connect_data->gpi) <= 0)) {
2415 purple_notify_error(NULL, NULL, _("Invalid proxy settings"), _("Either the host name or port number specified for your given proxy type is invalid."));
2416 purple_proxy_connect_data_destroy(connect_data);
2420 switch (purple_proxy_info_get_type(connect_data->gpi))
2422 case PURPLE_PROXY_NONE:
2425 case PURPLE_PROXY_HTTP:
2426 case PURPLE_PROXY_SOCKS4:
2427 case PURPLE_PROXY_SOCKS5:
2428 case PURPLE_PROXY_TOR:
2429 case PURPLE_PROXY_USE_ENVVAR:
2430 purple_debug_info("proxy", "Ignoring Proxy type (%d) for UDP.\n",
2431 purple_proxy_info_get_type(connect_data->gpi));
2435 purple_debug_error("proxy", "Invalid Proxy type (%d) specified.\n",
2436 purple_proxy_info_get_type(connect_data->gpi));
2437 purple_proxy_connect_data_destroy(connect_data);
2441 connect_data->query_data = purple_dnsquery_a_account(account, connecthost,
2442 connectport, connection_host_resolved, connect_data);
2443 if (connect_data->query_data == NULL)
2445 purple_proxy_connect_data_destroy(connect_data);
2449 handles = g_slist_prepend(handles, connect_data);
2451 return connect_data;
2454 PurpleProxyConnectData *
2455 purple_proxy_connect_socks5(void *handle, PurpleProxyInfo *gpi,
2456 const char *host, int port,
2457 PurpleProxyConnectFunction connect_cb,
2460 return purple_proxy_connect_socks5_account(handle, NULL, gpi,
2461 host, port, connect_cb, data);
2465 /* This is called when we connect to the SOCKS5 proxy server (through any
2466 * relevant account proxy)
2468 static void socks5_connected_to_proxy(gpointer data, gint source,
2469 const gchar *error_message) {
2470 /* This is the PurpleProxyConnectData for the overall SOCKS5 connection */
2471 PurpleProxyConnectData *connect_data = data;
2473 purple_debug_error("proxy", "Connect Data is %p\n", connect_data);
2475 /* Check that the overall SOCKS5 connection wasn't cancelled while we were
2476 * connecting to it (we don't have a way of associating the process of
2477 * connecting to the SOCKS5 server to the overall PurpleProxyConnectData)
2479 if (!PURPLE_PROXY_CONNECT_DATA_IS_VALID(connect_data)) {
2480 purple_debug_error("proxy", "Data had gone out of scope :(\n");
2484 /* Break the link between the two PurpleProxyConnectDatas */
2485 connect_data->child = NULL;
2487 if (error_message != NULL) {
2488 purple_debug_error("proxy", "Unable to connect to SOCKS5 host.\n");
2489 connect_data->connect_cb(connect_data->data, source, error_message);
2493 purple_debug_info("proxy", "Initiating SOCKS5 negotiation.\n");
2495 purple_debug_info("proxy",
2496 "Connecting to %s:%d via %s:%d using SOCKS5\n",
2497 connect_data->host, connect_data->port,
2498 purple_proxy_info_get_host(connect_data->gpi),
2499 purple_proxy_info_get_port(connect_data->gpi));
2501 connect_data->fd = source;
2503 s5_canwrite(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
2507 * Combine some of this code with purple_proxy_connect()
2509 PurpleProxyConnectData *
2510 purple_proxy_connect_socks5_account(void *handle, PurpleAccount *account,
2511 PurpleProxyInfo *gpi,
2512 const char *host, int port,
2513 PurpleProxyConnectFunction connect_cb,
2516 PurpleProxyConnectData *connect_data;
2517 PurpleProxyConnectData *account_proxy_conn_data;
2519 g_return_val_if_fail(host != NULL, NULL);
2520 g_return_val_if_fail(port >= 0, NULL);
2521 g_return_val_if_fail(connect_cb != NULL, NULL);
2523 connect_data = g_new0(PurpleProxyConnectData, 1);
2524 connect_data->fd = -1;
2525 connect_data->socket_type = SOCK_STREAM;
2526 connect_data->handle = handle;
2527 connect_data->connect_cb = connect_cb;
2528 connect_data->data = data;
2529 connect_data->host = g_strdup(host);
2530 connect_data->port = port;
2531 connect_data->gpi = gpi;
2532 connect_data->account = account;
2534 /* If there is an account proxy, use it to connect to the desired SOCKS5
2537 account_proxy_conn_data = purple_proxy_connect(connect_data->handle,
2538 connect_data->account,
2539 purple_proxy_info_get_host(connect_data->gpi),
2540 purple_proxy_info_get_port(connect_data->gpi),
2541 socks5_connected_to_proxy, connect_data);
2543 if (account_proxy_conn_data == NULL) {
2544 purple_debug_error("proxy", "Unable to initiate connection to account proxy.\n");
2545 purple_proxy_connect_data_destroy(connect_data);
2549 connect_data->child = account_proxy_conn_data;
2551 handles = g_slist_prepend(handles, connect_data);
2553 return connect_data;
2557 purple_proxy_connect_cancel(PurpleProxyConnectData *connect_data)
2559 g_return_if_fail(connect_data != NULL);
2561 purple_proxy_connect_data_disconnect(connect_data, NULL);
2562 purple_proxy_connect_data_destroy(connect_data);
2566 purple_proxy_connect_cancel_with_handle(void *handle)
2570 for (l = handles; l != NULL; l = l_next) {
2571 PurpleProxyConnectData *connect_data = l->data;
2575 if (connect_data->handle == handle)
2576 purple_proxy_connect_cancel(connect_data);
2581 proxy_pref_cb(const char *name, PurplePrefType type,
2582 gconstpointer value, gpointer data)
2584 PurpleProxyInfo *info = purple_global_proxy_get_info();
2586 if (purple_strequal(name, "/purple/proxy/type")) {
2588 const char *type = value;
2590 if (purple_strequal(type, "none"))
2591 proxytype = PURPLE_PROXY_NONE;
2592 else if (purple_strequal(type, "http"))
2593 proxytype = PURPLE_PROXY_HTTP;
2594 else if (purple_strequal(type, "socks4"))
2595 proxytype = PURPLE_PROXY_SOCKS4;
2596 else if (purple_strequal(type, "socks5"))
2597 proxytype = PURPLE_PROXY_SOCKS5;
2598 else if (purple_strequal(type, "tor"))
2599 proxytype = PURPLE_PROXY_TOR;
2600 else if (purple_strequal(type, "envvar"))
2601 proxytype = PURPLE_PROXY_USE_ENVVAR;
2605 purple_proxy_info_set_type(info, proxytype);
2606 } else if (purple_strequal(name, "/purple/proxy/host"))
2607 purple_proxy_info_set_host(info, value);
2608 else if (purple_strequal(name, "/purple/proxy/port"))
2609 purple_proxy_info_set_port(info, GPOINTER_TO_INT(value));
2610 else if (purple_strequal(name, "/purple/proxy/username"))
2611 purple_proxy_info_set_username(info, value);
2612 else if (purple_strequal(name, "/purple/proxy/password"))
2613 purple_proxy_info_set_password(info, value);
2617 purple_proxy_get_handle()
2625 purple_proxy_init(void)
2629 /* Initialize a default proxy info struct. */
2630 global_proxy_info = purple_proxy_info_new();
2633 purple_prefs_add_none("/purple/proxy");
2634 purple_prefs_add_string("/purple/proxy/type", "none");
2635 purple_prefs_add_string("/purple/proxy/host", "");
2636 purple_prefs_add_int("/purple/proxy/port", 0);
2637 purple_prefs_add_string("/purple/proxy/username", "");
2638 purple_prefs_add_string("/purple/proxy/password", "");
2639 purple_prefs_add_bool("/purple/proxy/socks4_remotedns", FALSE);
2641 /* Setup callbacks for the preferences. */
2642 handle = purple_proxy_get_handle();
2643 purple_prefs_connect_callback(handle, "/purple/proxy/type", proxy_pref_cb,
2645 purple_prefs_connect_callback(handle, "/purple/proxy/host", proxy_pref_cb,
2647 purple_prefs_connect_callback(handle, "/purple/proxy/port", proxy_pref_cb,
2649 purple_prefs_connect_callback(handle, "/purple/proxy/username",
2650 proxy_pref_cb, NULL);
2651 purple_prefs_connect_callback(handle, "/purple/proxy/password",
2652 proxy_pref_cb, NULL);
2654 /* Load the initial proxy settings */
2655 purple_prefs_trigger_callback("/purple/proxy/type");
2656 purple_prefs_trigger_callback("/purple/proxy/host");
2657 purple_prefs_trigger_callback("/purple/proxy/port");
2658 purple_prefs_trigger_callback("/purple/proxy/username");
2659 purple_prefs_trigger_callback("/purple/proxy/password");
2663 purple_proxy_uninit(void)
2665 while (handles != NULL)
2667 purple_proxy_connect_data_disconnect(handles->data, NULL);
2668 purple_proxy_connect_data_destroy(handles->data);
2671 purple_prefs_disconnect_by_handle(purple_proxy_get_handle());
2673 purple_proxy_info_destroy(global_proxy_info);
2674 global_proxy_info = NULL;