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
, ":");
387 sscanf(d
, "%d", &port
);
393 purple_proxy_info_set_host(info
, host
);
394 purple_proxy_info_set_port(info
, port
);
397 static PurpleProxyInfo
*
398 purple_win32_proxy_get_info(void)
400 static LPFNWINHTTPGETIEPROXYCONFIG MyWinHttpGetIEProxyConfig
= NULL
;
401 static gboolean loaded
= FALSE
;
402 static PurpleProxyInfo info
= {0, NULL
, 0, NULL
, NULL
};
404 WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ie_proxy_config
;
408 MyWinHttpGetIEProxyConfig
= (LPFNWINHTTPGETIEPROXYCONFIG
)
409 wpurple_find_and_loadproc("winhttp.dll", "WinHttpGetIEProxyConfigForCurrentUser");
410 if (!MyWinHttpGetIEProxyConfig
)
411 purple_debug_warning("proxy", "Unable to read Windows Proxy Settings.\n");
414 if (!MyWinHttpGetIEProxyConfig
)
417 ZeroMemory(&ie_proxy_config
, sizeof(ie_proxy_config
));
418 if (!MyWinHttpGetIEProxyConfig(&ie_proxy_config
)) {
419 purple_debug_error("proxy", "Error reading Windows Proxy Settings(%lu).\n", GetLastError());
423 /* We can't do much if it is autodetect*/
424 if (ie_proxy_config
.fAutoDetect
) {
425 purple_debug_error("proxy", "Windows Proxy Settings set to autodetect (not supported).\n");
427 /* TODO: For 3.0.0 we'll revisit this (maybe)*/
431 } else if (ie_proxy_config
.lpszProxy
) {
432 gchar
*proxy_list
= g_utf16_to_utf8(ie_proxy_config
.lpszProxy
, -1,
435 /* We can't do anything about the bypass list, as we don't have the url */
436 /* TODO: For 3.0.0 we'll revisit this*/
438 /* There are proxy settings for several protocols */
439 if (proxy_list
&& *proxy_list
) {
440 char *specific
= NULL
, *tmp
;
442 /* If there is only a global proxy, which means "HTTP" */
443 if (!strchr(proxy_list
, ';') || (specific
= g_strstr_len(proxy_list
, -1, "http=")) != NULL
) {
446 specific
+= strlen("http=");
447 tmp
= strchr(specific
, ';');
450 /* specific now points the proxy server (and port) */
452 specific
= proxy_list
;
454 purple_proxy_info_set_type(&info
, PURPLE_PROXY_HTTP
);
455 _proxy_fill_hostinfo(&info
, specific
, 80);
456 /* TODO: is there a way to set the username/password? */
457 purple_proxy_info_set_username(&info
, NULL
);
458 purple_proxy_info_set_password(&info
, NULL
);
460 purple_debug_info("proxy", "Windows Proxy Settings: HTTP proxy: '%s:%d'.\n",
461 purple_proxy_info_get_host(&info
),
462 purple_proxy_info_get_port(&info
));
464 } else if ((specific
= g_strstr_len(proxy_list
, -1, "socks=")) != NULL
) {
466 specific
+= strlen("socks=");
467 tmp
= strchr(specific
, ';');
470 /* specific now points the proxy server (and port) */
472 purple_proxy_info_set_type(&info
, PURPLE_PROXY_SOCKS5
);
473 _proxy_fill_hostinfo(&info
, specific
, 1080);
474 /* TODO: is there a way to set the username/password? */
475 purple_proxy_info_set_username(&info
, NULL
);
476 purple_proxy_info_set_password(&info
, NULL
);
478 purple_debug_info("proxy", "Windows Proxy Settings: SOCKS5 proxy: '%s:%d'.\n",
479 purple_proxy_info_get_host(&info
),
480 purple_proxy_info_get_port(&info
));
484 purple_debug_info("proxy", "Windows Proxy Settings: No supported proxy specified.\n");
486 purple_proxy_info_set_type(&info
, PURPLE_PROXY_NONE
);
491 /* TODO: Fix API to be able look at proxy bypass settings */
495 purple_debug_info("proxy", "No Windows proxy set.\n");
496 purple_proxy_info_set_type(&info
, PURPLE_PROXY_NONE
);
499 if (ie_proxy_config
.lpszAutoConfigUrl
)
500 GlobalFree(ie_proxy_config
.lpszAutoConfigUrl
);
501 if (ie_proxy_config
.lpszProxy
)
502 GlobalFree(ie_proxy_config
.lpszProxy
);
503 if (ie_proxy_config
.lpszProxyBypass
)
504 GlobalFree(ie_proxy_config
.lpszProxyBypass
);
511 /**************************************************************************
513 **************************************************************************/
516 * Whoever calls this needs to have called
517 * purple_proxy_connect_data_disconnect() beforehand.
520 purple_proxy_connect_data_destroy(PurpleProxyConnectData
*connect_data
)
522 handles
= g_slist_remove(handles
, connect_data
);
524 if (connect_data
->query_data
!= NULL
)
525 purple_dnsquery_destroy(connect_data
->query_data
);
527 while (connect_data
->hosts
!= NULL
)
529 /* Discard the length... */
530 connect_data
->hosts
= g_slist_remove(connect_data
->hosts
, connect_data
->hosts
->data
);
531 /* Free the address... */
532 g_free(connect_data
->hosts
->data
);
533 connect_data
->hosts
= g_slist_remove(connect_data
->hosts
, connect_data
->hosts
->data
);
536 g_free(connect_data
->host
);
537 g_free(connect_data
);
541 * Free all information dealing with a connection attempt and
542 * reset the connect_data to prepare for it to try to connect
543 * to another IP address.
545 * If an error message is passed in, then we know the connection
546 * attempt failed. If the connection attempt failed and
547 * connect_data->hosts is not empty then we try the next IP address.
548 * If the connection attempt failed and we have no more hosts
549 * try try then we call the callback with the given error message,
550 * then destroy the connect_data.
552 * @param error_message An error message explaining why the connection
553 * failed. This will be passed to the callback function
554 * specified in the call to purple_proxy_connect(). If the
555 * connection was successful then pass in null.
558 purple_proxy_connect_data_disconnect(PurpleProxyConnectData
*connect_data
, const gchar
*error_message
)
560 if (connect_data
->inpa
> 0)
562 purple_input_remove(connect_data
->inpa
);
563 connect_data
->inpa
= 0;
566 if (connect_data
->fd
>= 0)
568 close(connect_data
->fd
);
569 connect_data
->fd
= -1;
572 g_free(connect_data
->write_buffer
);
573 connect_data
->write_buffer
= NULL
;
575 g_free(connect_data
->read_buffer
);
576 connect_data
->read_buffer
= NULL
;
578 if (error_message
!= NULL
)
580 purple_debug_error("proxy", "Connection attempt failed: %s\n",
582 if (connect_data
->hosts
!= NULL
)
583 try_connect(connect_data
);
586 /* Everything failed! Tell the originator of the request. */
587 connect_data
->connect_cb(connect_data
->data
, -1, error_message
);
588 purple_proxy_connect_data_destroy(connect_data
);
594 * This calls purple_proxy_connect_data_disconnect(), but it lets you
595 * specify the error_message using a printf()-like syntax.
598 purple_proxy_connect_data_disconnect_formatted(PurpleProxyConnectData
*connect_data
, const char *format
, ...)
603 va_start(args
, format
);
604 tmp
= g_strdup_vprintf(format
, args
);
607 purple_proxy_connect_data_disconnect(connect_data
, tmp
);
612 purple_proxy_connect_data_connected(PurpleProxyConnectData
*connect_data
)
614 purple_debug_info("proxy", "Connected to %s:%d.\n",
615 connect_data
->host
, connect_data
->port
);
617 connect_data
->connect_cb(connect_data
->data
, connect_data
->fd
, NULL
);
620 * We've passed the file descriptor to the protocol, so it's no longer
621 * our responsibility, and we should be careful not to free it when
622 * we destroy the connect_data.
624 connect_data
->fd
= -1;
626 purple_proxy_connect_data_disconnect(connect_data
, NULL
);
627 purple_proxy_connect_data_destroy(connect_data
);
631 socket_ready_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
633 PurpleProxyConnectData
*connect_data
= data
;
637 /* If the socket-connected message had already been triggered when connect_data
638 * was destroyed via purple_proxy_connect_cancel(), we may get here with a freed connect_data.
640 if (!PURPLE_PROXY_CONNECT_DATA_IS_VALID(connect_data
))
643 purple_debug_info("proxy", "Connecting to %s:%d.\n",
644 connect_data
->host
, connect_data
->port
);
647 * purple_input_get_error after a non-blocking connect returns -1 if something is
648 * really messed up (bad descriptor, usually). Otherwise, it returns 0 and
649 * error holds what connect would have returned if it blocked until now.
650 * Thus, error == 0 is success, error == EINPROGRESS means "try again",
651 * and anything else is a real error.
653 * (error == EINPROGRESS can happen after a select because the kernel can
654 * be overly optimistic sometimes. select is just a hint that you might be
655 * able to do something.)
657 ret
= purple_input_get_error(connect_data
->fd
, &error
);
659 if (ret
== 0 && error
== EINPROGRESS
) {
660 /* No worries - we'll be called again later */
661 /* TODO: Does this ever happen? */
662 purple_debug_info("proxy", "(ret == 0 && error == EINPROGRESS)\n");
666 if (ret
!= 0 || error
!= 0) {
669 purple_debug_error("proxy", "Error connecting to %s:%d (%s).\n",
670 connect_data
->host
, connect_data
->port
, g_strerror(error
));
672 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(error
));
676 purple_proxy_connect_data_connected(connect_data
);
680 clean_connect(gpointer data
)
682 purple_proxy_connect_data_connected(data
);
688 proxy_connect_udp_none(PurpleProxyConnectData
*connect_data
, struct sockaddr
*addr
, socklen_t addrlen
)
692 purple_debug_info("proxy", "UDP Connecting to %s:%d with no proxy\n",
693 connect_data
->host
, connect_data
->port
);
695 connect_data
->fd
= socket(addr
->sa_family
, SOCK_DGRAM
, 0);
696 if (connect_data
->fd
< 0)
698 purple_proxy_connect_data_disconnect_formatted(connect_data
,
699 _("Unable to create socket: %s"), g_strerror(errno
));
703 flags
= fcntl(connect_data
->fd
, F_GETFL
);
704 fcntl(connect_data
->fd
, F_SETFL
, flags
| O_NONBLOCK
);
706 fcntl(connect_data
->fd
, F_SETFD
, FD_CLOEXEC
);
709 if (connect(connect_data
->fd
, addr
, addrlen
) != 0)
711 if ((errno
== EINPROGRESS
) || (errno
== EINTR
))
713 purple_debug_info("proxy", "UDP Connection in progress\n");
714 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
715 PURPLE_INPUT_WRITE
, socket_ready_cb
, connect_data
);
719 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(errno
));
725 * The connection happened IMMEDIATELY... strange, but whatever.
727 int error
= ETIMEDOUT
;
730 purple_debug_info("proxy", "UDP Connected immediately.\n");
732 ret
= purple_input_get_error(connect_data
->fd
, &error
);
733 if ((ret
!= 0) || (error
!= 0))
737 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(error
));
742 * We want to call the "connected" callback eventually, but we
743 * don't want to call it before we return, just in case.
745 purple_timeout_add(10, clean_connect
, connect_data
);
750 proxy_connect_none(PurpleProxyConnectData
*connect_data
, struct sockaddr
*addr
, socklen_t addrlen
)
754 purple_debug_info("proxy", "Connecting to %s:%d with no proxy\n",
755 connect_data
->host
, connect_data
->port
);
757 connect_data
->fd
= socket(addr
->sa_family
, SOCK_STREAM
, 0);
758 if (connect_data
->fd
< 0)
760 purple_proxy_connect_data_disconnect_formatted(connect_data
,
761 _("Unable to create socket: %s"), g_strerror(errno
));
765 flags
= fcntl(connect_data
->fd
, F_GETFL
);
766 fcntl(connect_data
->fd
, F_SETFL
, flags
| O_NONBLOCK
);
768 fcntl(connect_data
->fd
, F_SETFD
, FD_CLOEXEC
);
771 if (connect(connect_data
->fd
, addr
, addrlen
) != 0)
773 if ((errno
== EINPROGRESS
) || (errno
== EINTR
))
775 purple_debug_info("proxy", "Connection in progress\n");
776 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
777 PURPLE_INPUT_WRITE
, socket_ready_cb
, connect_data
);
781 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(errno
));
787 * The connection happened IMMEDIATELY... strange, but whatever.
789 int error
= ETIMEDOUT
;
792 purple_debug_info("proxy", "Connected immediately.\n");
794 ret
= purple_input_get_error(connect_data
->fd
, &error
);
795 if ((ret
!= 0) || (error
!= 0))
799 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(error
));
804 * We want to call the "connected" callback eventually, but we
805 * don't want to call it before we return, just in case.
807 purple_timeout_add(10, clean_connect
, connect_data
);
812 * This is a utility function used by the HTTP, SOCKS4 and SOCKS5
813 * connect functions. It writes data from a buffer to a socket.
814 * When all the data is written it sets up a watcher to read a
815 * response and call a specified function.
818 proxy_do_write(gpointer data
, gint source
, PurpleInputCondition cond
)
820 PurpleProxyConnectData
*connect_data
;
821 const guchar
*request
;
826 request
= connect_data
->write_buffer
+ connect_data
->written_len
;
827 request_len
= connect_data
->write_buf_len
- connect_data
->written_len
;
829 ret
= write(connect_data
->fd
, request
, request_len
);
837 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(errno
));
840 if (ret
< request_len
) {
841 connect_data
->written_len
+= ret
;
845 /* We're done writing data! Wait for a response. */
846 g_free(connect_data
->write_buffer
);
847 connect_data
->write_buffer
= NULL
;
848 purple_input_remove(connect_data
->inpa
);
849 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
850 PURPLE_INPUT_READ
, connect_data
->read_cb
, connect_data
);
853 #define HTTP_GOODSTRING "HTTP/1.0 200"
854 #define HTTP_GOODSTRING2 "HTTP/1.1 200"
857 * We're using an HTTP proxy for a non-port 80 tunnel. Read the
858 * response to the CONNECT request.
861 http_canread(gpointer data
, gint source
, PurpleInputCondition cond
)
863 int len
, headers_len
, status
= 0;
865 PurpleProxyConnectData
*connect_data
= data
;
869 if (connect_data
->read_buffer
== NULL
) {
870 connect_data
->read_buf_len
= 8192;
871 connect_data
->read_buffer
= g_malloc(connect_data
->read_buf_len
);
872 connect_data
->read_len
= 0;
875 p
= (char *)connect_data
->read_buffer
+ connect_data
->read_len
;
876 max_read
= connect_data
->read_buf_len
- connect_data
->read_len
- 1;
878 len
= read(connect_data
->fd
, p
, max_read
);
881 purple_proxy_connect_data_disconnect(connect_data
,
882 _("Server closed the connection"));
892 purple_proxy_connect_data_disconnect_formatted(connect_data
,
893 _("Lost connection with server: %s"), g_strerror(errno
));
897 connect_data
->read_len
+= len
;
900 p
= g_strstr_len((const gchar
*)connect_data
->read_buffer
,
901 connect_data
->read_len
, "\r\n\r\n");
904 headers_len
= (p
- (char *)connect_data
->read_buffer
) + 4;
905 } else if(len
== max_read
)
910 error
= strncmp((const char *)connect_data
->read_buffer
, "HTTP/", 5) != 0;
913 p
= (char *)connect_data
->read_buffer
+ 5;
914 major
= strtol(p
, &p
, 10);
915 error
= (major
== 0) || (*p
!= '.');
919 minor
= strtol(p
, &p
, 10);
923 status
= strtol(p
, &p
, 10);
929 /* Read the contents */
930 p
= g_strrstr((const gchar
*)connect_data
->read_buffer
, "Content-Length: ");
935 p
+= strlen("Content-Length: ");
936 tmp
= strchr(p
, '\r');
943 /* Compensate for what has already been read */
944 len
-= connect_data
->read_len
- headers_len
;
945 /* I'm assuming that we're doing this to prevent the server from
946 complaining / breaking since we don't read the whole page */
948 /* TODO: deal with EAGAIN (and other errors) better */
949 if (read(connect_data
->fd
, &tmpc
, 1) < 0 && errno
!= EAGAIN
)
955 purple_proxy_connect_data_disconnect_formatted(connect_data
,
956 _("Unable to parse response from HTTP proxy: %s"),
957 connect_data
->read_buffer
);
960 else if (status
!= 200) {
961 purple_debug_error("proxy",
962 "Proxy server replied with:\n%s\n",
963 connect_data
->read_buffer
);
965 if (status
== 407 /* Proxy Auth */) {
969 header
= g_strrstr((const gchar
*)connect_data
->read_buffer
,
970 "Proxy-Authenticate: NTLM");
971 if (header
!= NULL
) {
972 const char *header_end
= header
+ strlen("Proxy-Authenticate: NTLM");
973 const char *domain
= purple_proxy_info_get_username(connect_data
->gpi
);
974 char *username
= NULL
, hostname
[256];
978 ret
= gethostname(hostname
, sizeof(hostname
));
979 hostname
[sizeof(hostname
) - 1] = '\0';
980 if (ret
< 0 || hostname
[0] == '\0') {
981 purple_debug_warning("proxy", "gethostname() failed -- is your hostname set?");
982 strcpy(hostname
, "localhost");
986 username
= (char*) strchr(domain
, '\\');
987 if (username
== NULL
) {
988 purple_proxy_connect_data_disconnect_formatted(connect_data
,
989 _("HTTP proxy connection error %d"), status
);
994 /* Is there a message? */
995 if (*header_end
== ' ') {
996 /* Check for Type-2 */
997 char *tmp
= (char*) header
;
1002 while(*tmp
!= '\r' && *tmp
!= '\0') tmp
++;
1004 nonce
= purple_ntlm_parse_type2(header_end
, NULL
);
1005 response
= purple_ntlm_gen_type3(username
,
1006 (gchar
*) purple_proxy_info_get_password(connect_data
->gpi
),
1008 domain
, nonce
, NULL
);
1010 } else /* Empty message */
1011 response
= purple_ntlm_gen_type1(hostname
, domain
);
1015 request
= g_strdup_printf(
1016 "CONNECT %s:%d HTTP/1.1\r\n"
1018 "Proxy-Authorization: NTLM %s\r\n"
1019 "Proxy-Connection: Keep-Alive\r\n\r\n",
1020 connect_data
->host
, connect_data
->port
,
1021 connect_data
->host
, connect_data
->port
,
1026 } else if (g_strrstr((const char *)connect_data
->read_buffer
, "Proxy-Authenticate: Basic") != NULL
) {
1028 const char *username
, *password
;
1030 username
= purple_proxy_info_get_username(connect_data
->gpi
);
1031 password
= purple_proxy_info_get_password(connect_data
->gpi
);
1033 t1
= g_strdup_printf("%s:%s",
1034 username
? username
: "",
1035 password
? password
: "");
1036 t2
= purple_base64_encode((guchar
*)t1
, strlen(t1
));
1039 request
= g_strdup_printf(
1040 "CONNECT %s:%d HTTP/1.1\r\n"
1042 "Proxy-Authorization: Basic %s\r\n",
1043 connect_data
->host
, connect_data
->port
,
1044 connect_data
->host
, connect_data
->port
,
1050 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1051 _("HTTP proxy connection error %d"), status
);
1055 purple_input_remove(connect_data
->inpa
);
1056 g_free(connect_data
->read_buffer
);
1057 connect_data
->read_buffer
= NULL
;
1059 connect_data
->write_buffer
= (guchar
*)request
;
1060 connect_data
->write_buf_len
= strlen(request
);
1061 connect_data
->written_len
= 0;
1063 connect_data
->read_cb
= http_canread
;
1065 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
1066 PURPLE_INPUT_WRITE
, proxy_do_write
, connect_data
);
1068 proxy_do_write(connect_data
, connect_data
->fd
, cond
);
1073 if (status
== 403) {
1075 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1076 _("Access denied: HTTP proxy server forbids port %d tunneling"),
1077 connect_data
->port
);
1079 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1080 _("HTTP proxy connection error %d"), status
);
1083 purple_input_remove(connect_data
->inpa
);
1084 connect_data
->inpa
= 0;
1085 g_free(connect_data
->read_buffer
);
1086 connect_data
->read_buffer
= NULL
;
1087 purple_debug_info("proxy", "HTTP proxy connection established\n");
1088 purple_proxy_connect_data_connected(connect_data
);
1094 http_start_connect_tunneling(PurpleProxyConnectData
*connect_data
) {
1098 purple_debug_info("proxy", "Using CONNECT tunneling for %s:%d\n",
1099 connect_data
->host
, connect_data
->port
);
1101 request
= g_string_sized_new(4096);
1102 g_string_append_printf(request
,
1103 "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n",
1104 connect_data
->host
, connect_data
->port
,
1105 connect_data
->host
, connect_data
->port
);
1107 if (purple_proxy_info_get_username(connect_data
->gpi
) != NULL
)
1109 char *t1
, *t2
, *ntlm_type1
;
1112 ret
= gethostname(hostname
, sizeof(hostname
));
1113 hostname
[sizeof(hostname
) - 1] = '\0';
1114 if (ret
< 0 || hostname
[0] == '\0') {
1115 purple_debug_warning("proxy", "gethostname() failed -- is your hostname set?");
1116 strcpy(hostname
, "localhost");
1119 t1
= g_strdup_printf("%s:%s",
1120 purple_proxy_info_get_username(connect_data
->gpi
),
1121 purple_proxy_info_get_password(connect_data
->gpi
) ?
1122 purple_proxy_info_get_password(connect_data
->gpi
) : "");
1123 t2
= purple_base64_encode((const guchar
*)t1
, strlen(t1
));
1126 ntlm_type1
= purple_ntlm_gen_type1(hostname
, "");
1128 g_string_append_printf(request
,
1129 "Proxy-Authorization: Basic %s\r\n"
1130 "Proxy-Authorization: NTLM %s\r\n"
1131 "Proxy-Connection: Keep-Alive\r\n",
1137 g_string_append(request
, "\r\n");
1139 connect_data
->write_buf_len
= request
->len
;
1140 connect_data
->write_buffer
= (guchar
*)g_string_free(request
, FALSE
);
1141 connect_data
->written_len
= 0;
1142 connect_data
->read_cb
= http_canread
;
1144 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
1145 PURPLE_INPUT_WRITE
, proxy_do_write
, connect_data
);
1146 proxy_do_write(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1150 http_canwrite(gpointer data
, gint source
, PurpleInputCondition cond
) {
1151 PurpleProxyConnectData
*connect_data
= data
;
1152 int ret
, error
= ETIMEDOUT
;
1154 purple_debug_info("proxy", "Connected to %s:%d.\n",
1155 connect_data
->host
, connect_data
->port
);
1157 if (connect_data
->inpa
> 0) {
1158 purple_input_remove(connect_data
->inpa
);
1159 connect_data
->inpa
= 0;
1162 ret
= purple_input_get_error(connect_data
->fd
, &error
);
1163 if (ret
!= 0 || error
!= 0) {
1166 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(error
));
1170 if (connect_data
->port
== 80) {
1172 * If we're trying to connect to something running on
1173 * port 80 then we assume the traffic using this
1174 * connection is going to be HTTP traffic. If it's
1175 * not then this will fail (uglily). But it's good
1176 * to avoid using the CONNECT method because it's
1177 * not always allowed.
1179 purple_debug_info("proxy", "HTTP proxy connection established\n");
1180 purple_proxy_connect_data_connected(connect_data
);
1182 http_start_connect_tunneling(connect_data
);
1188 proxy_connect_http(PurpleProxyConnectData
*connect_data
, struct sockaddr
*addr
, socklen_t addrlen
)
1192 purple_debug_info("proxy",
1193 "Connecting to %s:%d via %s:%d using HTTP\n",
1194 connect_data
->host
, connect_data
->port
,
1195 (purple_proxy_info_get_host(connect_data
->gpi
) ? purple_proxy_info_get_host(connect_data
->gpi
) : "(null)"),
1196 purple_proxy_info_get_port(connect_data
->gpi
));
1198 connect_data
->fd
= socket(addr
->sa_family
, SOCK_STREAM
, 0);
1199 if (connect_data
->fd
< 0)
1201 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1202 _("Unable to create socket: %s"), g_strerror(errno
));
1206 flags
= fcntl(connect_data
->fd
, F_GETFL
);
1207 fcntl(connect_data
->fd
, F_SETFL
, flags
| O_NONBLOCK
);
1209 fcntl(connect_data
->fd
, F_SETFD
, FD_CLOEXEC
);
1212 if (connect(connect_data
->fd
, addr
, addrlen
) != 0) {
1213 if (errno
== EINPROGRESS
|| errno
== EINTR
) {
1214 purple_debug_info("proxy", "Connection in progress\n");
1216 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
1217 PURPLE_INPUT_WRITE
, http_canwrite
, connect_data
);
1219 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(errno
));
1221 purple_debug_info("proxy", "Connected immediately.\n");
1223 http_canwrite(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1228 s4_canread(gpointer data
, gint source
, PurpleInputCondition cond
)
1230 PurpleProxyConnectData
*connect_data
= data
;
1234 /* This is really not going to block under normal circumstances, but to
1235 * be correct, we deal with the unlikely scenario */
1237 if (connect_data
->read_buffer
== NULL
) {
1238 connect_data
->read_buf_len
= 12;
1239 connect_data
->read_buffer
= g_malloc(connect_data
->read_buf_len
);
1240 connect_data
->read_len
= 0;
1243 buf
= connect_data
->read_buffer
+ connect_data
->read_len
;
1244 max_read
= connect_data
->read_buf_len
- connect_data
->read_len
;
1246 len
= read(connect_data
->fd
, buf
, max_read
);
1248 if ((len
< 0 && errno
== EAGAIN
) || (len
> 0 && len
+ connect_data
->read_len
< 4))
1250 else if (len
+ connect_data
->read_len
>= 4) {
1251 if (connect_data
->read_buffer
[1] == 90) {
1252 purple_proxy_connect_data_connected(connect_data
);
1257 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(errno
));
1261 s4_host_resolved(GSList
*hosts
, gpointer data
, const char *error_message
)
1263 PurpleProxyConnectData
*connect_data
= data
;
1264 unsigned char packet
[9];
1265 struct sockaddr
*addr
;
1267 connect_data
->query_data
= NULL
;
1269 if (error_message
!= NULL
) {
1270 purple_proxy_connect_data_disconnect(connect_data
, error_message
);
1274 if (hosts
== NULL
) {
1275 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1276 _("Error resolving %s"), connect_data
->host
);
1280 /* Discard the length... */
1281 hosts
= g_slist_delete_link(hosts
, hosts
);
1283 hosts
= g_slist_delete_link(hosts
, hosts
);
1287 packet
[2] = connect_data
->port
>> 8;
1288 packet
[3] = connect_data
->port
& 0xff;
1289 memcpy(packet
+ 4, &((struct sockaddr_in
*)addr
)->sin_addr
.s_addr
, 4);
1294 /* We could try the other hosts, but hopefully that shouldn't be necessary */
1295 while (hosts
!= NULL
) {
1296 /* Discard the length... */
1297 hosts
= g_slist_delete_link(hosts
, hosts
);
1298 /* Free the address... */
1299 g_free(hosts
->data
);
1300 hosts
= g_slist_delete_link(hosts
, hosts
);
1303 connect_data
->write_buffer
= g_memdup(packet
, sizeof(packet
));
1304 connect_data
->write_buf_len
= sizeof(packet
);
1305 connect_data
->written_len
= 0;
1306 connect_data
->read_cb
= s4_canread
;
1308 connect_data
->inpa
= purple_input_add(connect_data
->fd
, PURPLE_INPUT_WRITE
, proxy_do_write
, connect_data
);
1310 proxy_do_write(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1314 s4_canwrite(gpointer data
, gint source
, PurpleInputCondition cond
)
1316 PurpleProxyConnectData
*connect_data
= data
;
1317 int error
= ETIMEDOUT
;
1320 purple_debug_info("socks4 proxy", "Connected.\n");
1322 if (connect_data
->inpa
> 0) {
1323 purple_input_remove(connect_data
->inpa
);
1324 connect_data
->inpa
= 0;
1327 ret
= purple_input_get_error(connect_data
->fd
, &error
);
1328 if ((ret
!= 0) || (error
!= 0)) {
1331 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(error
));
1336 * The socks4 spec doesn't include support for doing host name lookups by
1337 * the proxy. Many socks4 servers do this via the "socks4a" extension to
1338 * the protocol. There doesn't appear to be a way to detect if a server
1339 * supports this, so we require that the user set a global option.
1341 if (purple_prefs_get_bool("/purple/proxy/socks4_remotedns")) {
1342 unsigned char packet
[9];
1345 purple_debug_info("socks4 proxy", "Attempting to use remote DNS.\n");
1349 packet
[2] = connect_data
->port
>> 8;
1350 packet
[3] = connect_data
->port
& 0xff;
1357 len
= sizeof(packet
) + strlen(connect_data
->host
) + 1;
1359 connect_data
->write_buffer
= g_malloc0(len
);
1360 memcpy(connect_data
->write_buffer
, packet
, sizeof(packet
));
1361 memcpy(connect_data
->write_buffer
+ sizeof(packet
), connect_data
->host
, strlen(connect_data
->host
));
1362 connect_data
->write_buf_len
= len
;
1363 connect_data
->written_len
= 0;
1364 connect_data
->read_cb
= s4_canread
;
1366 connect_data
->inpa
= purple_input_add(connect_data
->fd
, PURPLE_INPUT_WRITE
, proxy_do_write
, connect_data
);
1368 proxy_do_write(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1370 connect_data
->query_data
= purple_dnsquery_a(connect_data
->host
,
1371 connect_data
->port
, s4_host_resolved
, connect_data
);
1373 if (connect_data
->query_data
== NULL
) {
1374 purple_debug_error("proxy", "dns query failed unexpectedly.\n");
1375 purple_proxy_connect_data_destroy(connect_data
);
1381 proxy_connect_socks4(PurpleProxyConnectData
*connect_data
, struct sockaddr
*addr
, socklen_t addrlen
)
1385 purple_debug_info("proxy",
1386 "Connecting to %s:%d via %s:%d using SOCKS4\n",
1387 connect_data
->host
, connect_data
->port
,
1388 purple_proxy_info_get_host(connect_data
->gpi
),
1389 purple_proxy_info_get_port(connect_data
->gpi
));
1391 connect_data
->fd
= socket(addr
->sa_family
, SOCK_STREAM
, 0);
1392 if (connect_data
->fd
< 0)
1394 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1395 _("Unable to create socket: %s"), g_strerror(errno
));
1399 flags
= fcntl(connect_data
->fd
, F_GETFL
);
1400 fcntl(connect_data
->fd
, F_SETFL
, flags
| O_NONBLOCK
);
1402 fcntl(connect_data
->fd
, F_SETFD
, FD_CLOEXEC
);
1405 if (connect(connect_data
->fd
, addr
, addrlen
) != 0)
1407 if ((errno
== EINPROGRESS
) || (errno
== EINTR
))
1409 purple_debug_info("proxy", "Connection in progress.\n");
1410 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
1411 PURPLE_INPUT_WRITE
, s4_canwrite
, connect_data
);
1415 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(errno
));
1420 purple_debug_info("proxy", "Connected immediately.\n");
1422 s4_canwrite(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1427 s5_ensure_buffer_length(PurpleProxyConnectData
*connect_data
, int len
)
1429 if(connect_data
->read_len
< len
) {
1430 if(connect_data
->read_buf_len
< len
) {
1431 /* it's not just that we haven't read enough, it's that we haven't tried to read enough yet */
1432 purple_debug_info("s5", "reallocing from %" G_GSIZE_FORMAT
1433 " to %d\n", connect_data
->read_buf_len
, len
);
1434 connect_data
->read_buf_len
= len
;
1435 connect_data
->read_buffer
= g_realloc(connect_data
->read_buffer
, connect_data
->read_buf_len
);
1444 s5_canread_again(gpointer data
, gint source
, PurpleInputCondition cond
)
1447 PurpleProxyConnectData
*connect_data
= data
;
1450 if (connect_data
->read_buffer
== NULL
) {
1451 connect_data
->read_buf_len
= 5;
1452 connect_data
->read_buffer
= g_malloc(connect_data
->read_buf_len
);
1453 connect_data
->read_len
= 0;
1456 dest
= connect_data
->read_buffer
+ connect_data
->read_len
;
1457 buf
= connect_data
->read_buffer
;
1459 len
= read(connect_data
->fd
, dest
, (connect_data
->read_buf_len
- connect_data
->read_len
));
1463 purple_proxy_connect_data_disconnect(connect_data
,
1464 _("Server closed the connection"));
1470 if (errno
== EAGAIN
)
1475 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1476 _("Lost connection with server: %s"), g_strerror(errno
));
1480 connect_data
->read_len
+= len
;
1482 if(connect_data
->read_len
< 4)
1485 if ((buf
[0] != 0x05) || (buf
[1] != 0x00)) {
1486 if ((buf
[0] == 0x05) && (buf
[1] < 0x09)) {
1487 purple_debug_error("socks5 proxy", "%s", socks5errors
[buf
[1]]);
1488 purple_proxy_connect_data_disconnect(connect_data
,
1489 socks5errors
[buf
[1]]);
1491 purple_debug_error("socks5 proxy", "Bad data.\n");
1492 purple_proxy_connect_data_disconnect(connect_data
,
1493 _("Received invalid data on connection with server"));
1498 /* Skip past BND.ADDR */
1500 case 0x01: /* the address is a version-4 IP address, with a length of 4 octets */
1501 if(!s5_ensure_buffer_length(connect_data
, 4 + 4))
1505 case 0x03: /* the address field contains a fully-qualified domain name. The first
1506 octet of the address field contains the number of octets of name that
1507 follow, there is no terminating NUL octet. */
1508 if(!s5_ensure_buffer_length(connect_data
, 4 + 1))
1511 if(!s5_ensure_buffer_length(connect_data
, 4 + 1 + buf
[0]))
1515 case 0x04: /* the address is a version-6 IP address, with a length of 16 octets */
1516 if(!s5_ensure_buffer_length(connect_data
, 4 + 16))
1521 purple_debug_error("socks5 proxy", "Invalid ATYP received (0x%X)\n", buf
[3]);
1522 purple_proxy_connect_data_disconnect(connect_data
,
1523 _("Received invalid data on connection with server"));
1527 /* Skip past BND.PORT */
1528 if(!s5_ensure_buffer_length(connect_data
, (buf
- connect_data
->read_buffer
) + 2))
1531 purple_proxy_connect_data_connected(connect_data
);
1535 s5_sendconnect(gpointer data
, int source
)
1537 PurpleProxyConnectData
*connect_data
= data
;
1538 size_t hlen
= strlen(connect_data
->host
);
1539 connect_data
->write_buf_len
= 5 + hlen
+ 2;
1540 connect_data
->write_buffer
= g_malloc(connect_data
->write_buf_len
);
1541 connect_data
->written_len
= 0;
1543 connect_data
->write_buffer
[0] = 0x05;
1544 connect_data
->write_buffer
[1] = 0x01; /* CONNECT */
1545 connect_data
->write_buffer
[2] = 0x00; /* reserved */
1546 connect_data
->write_buffer
[3] = 0x03; /* address type -- host name */
1547 connect_data
->write_buffer
[4] = hlen
;
1548 memcpy(connect_data
->write_buffer
+ 5, connect_data
->host
, hlen
);
1549 connect_data
->write_buffer
[5 + hlen
] = connect_data
->port
>> 8;
1550 connect_data
->write_buffer
[5 + hlen
+ 1] = connect_data
->port
& 0xff;
1552 connect_data
->read_cb
= s5_canread_again
;
1554 connect_data
->inpa
= purple_input_add(connect_data
->fd
, PURPLE_INPUT_WRITE
, proxy_do_write
, connect_data
);
1555 proxy_do_write(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1559 s5_readauth(gpointer data
, gint source
, PurpleInputCondition cond
)
1561 PurpleProxyConnectData
*connect_data
= data
;
1564 if (connect_data
->read_buffer
== NULL
) {
1565 connect_data
->read_buf_len
= 2;
1566 connect_data
->read_buffer
= g_malloc(connect_data
->read_buf_len
);
1567 connect_data
->read_len
= 0;
1570 purple_debug_info("socks5 proxy", "Got auth response.\n");
1572 len
= read(connect_data
->fd
, connect_data
->read_buffer
+ connect_data
->read_len
,
1573 connect_data
->read_buf_len
- connect_data
->read_len
);
1577 purple_proxy_connect_data_disconnect(connect_data
,
1578 _("Server closed the connection"));
1584 if (errno
== EAGAIN
)
1589 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1590 _("Lost connection with server: %s"), g_strerror(errno
));
1594 connect_data
->read_len
+= len
;
1595 if (connect_data
->read_len
< 2)
1598 purple_input_remove(connect_data
->inpa
);
1599 connect_data
->inpa
= 0;
1601 if ((connect_data
->read_buffer
[0] != 0x01) || (connect_data
->read_buffer
[1] != 0x00)) {
1602 purple_proxy_connect_data_disconnect(connect_data
,
1603 _("Received invalid data on connection with server"));
1607 g_free(connect_data
->read_buffer
);
1608 connect_data
->read_buffer
= NULL
;
1610 s5_sendconnect(connect_data
, connect_data
->fd
);
1614 hmacmd5_chap(const unsigned char * challenge
, int challen
, const char * passwd
, unsigned char * response
)
1616 PurpleCipher
*cipher
;
1617 PurpleCipherContext
*ctx
;
1619 unsigned char Kxoripad
[65];
1620 unsigned char Kxoropad
[65];
1623 cipher
= purple_ciphers_find_cipher("md5");
1624 ctx
= purple_cipher_context_new(cipher
, NULL
);
1626 memset(Kxoripad
,0,sizeof(Kxoripad
));
1627 memset(Kxoropad
,0,sizeof(Kxoropad
));
1629 pwlen
=strlen(passwd
);
1631 purple_cipher_context_append(ctx
, (const guchar
*)passwd
, strlen(passwd
));
1632 purple_cipher_context_digest(ctx
, sizeof(Kxoripad
), Kxoripad
, NULL
);
1635 memcpy(Kxoripad
, passwd
, pwlen
);
1637 memcpy(Kxoropad
,Kxoripad
,pwlen
);
1639 for (i
=0;i
<64;i
++) {
1644 purple_cipher_context_reset(ctx
, NULL
);
1645 purple_cipher_context_append(ctx
, Kxoripad
, 64);
1646 purple_cipher_context_append(ctx
, challenge
, challen
);
1647 purple_cipher_context_digest(ctx
, sizeof(Kxoripad
), Kxoripad
, NULL
);
1649 purple_cipher_context_reset(ctx
, NULL
);
1650 purple_cipher_context_append(ctx
, Kxoropad
, 64);
1651 purple_cipher_context_append(ctx
, Kxoripad
, 16);
1652 purple_cipher_context_digest(ctx
, 16, response
, NULL
);
1654 purple_cipher_context_destroy(ctx
);
1658 s5_readchap(gpointer data
, gint source
, PurpleInputCondition cond
);
1661 * Return how many bytes we processed
1662 * -1 means we've shouldn't keep reading from the buffer
1665 s5_parse_chap_msg(PurpleProxyConnectData
*connect_data
)
1667 guchar
*buf
, *cmdbuf
= connect_data
->read_buffer
;
1668 int len
, navas
, currentav
;
1670 purple_debug_misc("socks5 proxy", "Reading CHAP message: %x\n", *cmdbuf
);
1672 if (*cmdbuf
!= 0x01) {
1673 purple_proxy_connect_data_disconnect(connect_data
,
1674 _("Received invalid data on connection with server"));
1681 purple_debug_misc("socks5 proxy", "Expecting %d attribute(s).\n", navas
);
1685 for (currentav
= 0; currentav
< navas
; currentav
++) {
1687 len
= connect_data
->read_len
- (cmdbuf
- connect_data
->read_buffer
);
1688 /* We don't have enough data to even know how long the next attribute is,
1689 * or we don't have the full length of the next attribute. */
1690 if (len
< 2 || len
< (cmdbuf
[1] + 2)) {
1691 /* Clear out the attributes that have been read - decrease the attribute count */
1692 connect_data
->read_buffer
[1] = navas
- currentav
;
1693 /* Move the unprocessed data into the first attribute position */
1694 memmove((connect_data
->read_buffer
+ 2), cmdbuf
, len
);
1695 /* Decrease the read count accordingly */
1696 connect_data
->read_len
= len
+ 2;
1698 purple_debug_info("socks5 proxy", "Need more data to retrieve attribute %d.\n", currentav
);
1705 if (cmdbuf
[1] == 0) {
1706 purple_debug_error("socks5 proxy", "Attribute %x Value length of 0; ignoring.\n", cmdbuf
[0]);
1711 switch (cmdbuf
[0]) {
1713 purple_debug_info("socks5 proxy", "Received STATUS of %x\n", buf
[0]);
1714 /* Did auth work? */
1715 if (buf
[0] == 0x00) {
1716 purple_input_remove(connect_data
->inpa
);
1717 connect_data
->inpa
= 0;
1718 g_free(connect_data
->read_buffer
);
1719 connect_data
->read_buffer
= NULL
;
1721 s5_sendconnect(connect_data
, connect_data
->fd
);
1724 purple_debug_warning("proxy",
1725 "socks5 CHAP authentication "
1726 "failed. Disconnecting...");
1727 purple_proxy_connect_data_disconnect(connect_data
,
1728 _("Authentication failed"));
1732 /* We've already validated that cmdbuf[1] is sane. */
1733 purple_debug_info("socks5 proxy", "Received TEXT-MESSAGE of '%.*s'\n", (int) cmdbuf
[1], buf
);
1736 purple_debug_info("socks5 proxy", "Received CHALLENGE\n");
1737 /* Server wants our credentials */
1739 connect_data
->write_buf_len
= 16 + 4;
1740 connect_data
->write_buffer
= g_malloc(connect_data
->write_buf_len
);
1741 connect_data
->written_len
= 0;
1743 hmacmd5_chap(buf
, cmdbuf
[1],
1744 purple_proxy_info_get_password(connect_data
->gpi
),
1745 connect_data
->write_buffer
+ 4);
1746 /* TODO: What about USER-IDENTITY? */
1747 connect_data
->write_buffer
[0] = 0x01;
1748 connect_data
->write_buffer
[1] = 0x01;
1749 connect_data
->write_buffer
[2] = 0x04;
1750 connect_data
->write_buffer
[3] = 0x10;
1752 purple_input_remove(connect_data
->inpa
);
1753 g_free(connect_data
->read_buffer
);
1754 connect_data
->read_buffer
= NULL
;
1756 connect_data
->read_cb
= s5_readchap
;
1758 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
1759 PURPLE_INPUT_WRITE
, proxy_do_write
, connect_data
);
1761 proxy_do_write(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1764 purple_debug_info("socks5 proxy", "Received ALGORIGTHMS of %x\n", buf
[0]);
1765 /* Server wants to select an algorithm */
1766 if (buf
[0] != 0x85) {
1767 /* Only currently support HMAC-MD5 */
1768 purple_debug_warning("proxy",
1769 "Server tried to select an "
1770 "algorithm that we did not advertise "
1771 "as supporting. This is a violation "
1772 "of the socks5 CHAP specification. "
1773 "Disconnecting...");
1774 purple_proxy_connect_data_disconnect(connect_data
,
1775 _("Received invalid data on connection with server"));
1780 purple_debug_info("socks5 proxy", "Received unused command %x, length=%d\n", cmdbuf
[0], cmdbuf
[1]);
1782 cmdbuf
= buf
+ cmdbuf
[1];
1785 return (cmdbuf
- connect_data
->read_buffer
);
1789 s5_readchap(gpointer data
, gint source
, PurpleInputCondition cond
)
1792 PurpleProxyConnectData
*connect_data
= data
;
1795 purple_debug(PURPLE_DEBUG_INFO
, "socks5 proxy", "Got CHAP response.\n");
1797 if (connect_data
->read_buffer
== NULL
) {
1798 /* A big enough butfer to read the message header (2 bytes) and at least one complete attribute and value (1 + 1 + 255). */
1799 connect_data
->read_buf_len
= 259;
1800 connect_data
->read_buffer
= g_malloc(connect_data
->read_buf_len
);
1801 connect_data
->read_len
= 0;
1804 if (connect_data
->read_buf_len
- connect_data
->read_len
== 0) {
1805 /*If the stuff below is right, this shouldn't be possible. */
1806 purple_debug_error("socks5 proxy", "This is about to suck because the read buffer is full (shouldn't happen).\n");
1809 len
= read(connect_data
->fd
, connect_data
->read_buffer
+ connect_data
->read_len
,
1810 connect_data
->read_buf_len
- connect_data
->read_len
);
1813 purple_proxy_connect_data_disconnect(connect_data
,
1814 _("Server closed the connection"));
1819 if (errno
== EAGAIN
)
1824 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1825 _("Lost connection with server: %s"), g_strerror(errno
));
1829 connect_data
->read_len
+= len
;
1831 /* We may have read more than one message into the buffer, we need to make sure to process them all */
1834 /* We need more to be able to read this message */
1835 if (connect_data
->read_len
< 2)
1838 msg_ret
= s5_parse_chap_msg(connect_data
);
1843 /* See if we have another message already in the buffer */
1844 if ((len
= connect_data
->read_len
- msg_ret
) > 0) {
1846 /* Move on to the next message */
1847 memmove(connect_data
->read_buffer
, connect_data
->read_buffer
+ msg_ret
, len
);
1848 /* Decrease the read count accordingly */
1849 connect_data
->read_len
= len
;
1851 /* Try to read the message that connect_data->read_buffer now points to */
1858 /* Fell through. We ran out of CHAP events to process, but haven't
1859 * succeeded or failed authentication - there may be more to come.
1860 * If this is the case, come straight back here. */
1862 purple_debug_info("socks5 proxy", "Waiting for another message from which to read CHAP info.\n");
1864 /* We've processed all the available attributes, so get ready for a whole new message */
1865 g_free(connect_data
->read_buffer
);
1866 connect_data
->read_buffer
= NULL
;
1870 s5_canread(gpointer data
, gint source
, PurpleInputCondition cond
)
1872 PurpleProxyConnectData
*connect_data
= data
;
1875 if (connect_data
->read_buffer
== NULL
) {
1876 connect_data
->read_buf_len
= 2;
1877 connect_data
->read_buffer
= g_malloc(connect_data
->read_buf_len
);
1878 connect_data
->read_len
= 0;
1881 purple_debug_info("socks5 proxy", "Able to read.\n");
1883 len
= read(connect_data
->fd
, connect_data
->read_buffer
+ connect_data
->read_len
,
1884 connect_data
->read_buf_len
- connect_data
->read_len
);
1888 purple_proxy_connect_data_disconnect(connect_data
,
1889 _("Server closed the connection"));
1895 if (errno
== EAGAIN
)
1900 purple_proxy_connect_data_disconnect_formatted(connect_data
,
1901 _("Lost connection with server: %s"), g_strerror(errno
));
1905 connect_data
->read_len
+= len
;
1906 if (connect_data
->read_len
< 2)
1909 purple_input_remove(connect_data
->inpa
);
1910 connect_data
->inpa
= 0;
1912 if ((connect_data
->read_buffer
[0] != 0x05) || (connect_data
->read_buffer
[1] == 0xff)) {
1913 purple_proxy_connect_data_disconnect(connect_data
,
1914 _("Received invalid data on connection with server"));
1918 if (connect_data
->read_buffer
[1] == 0x02) {
1922 u
= purple_proxy_info_get_username(connect_data
->gpi
);
1923 p
= purple_proxy_info_get_password(connect_data
->gpi
);
1925 i
= (u
== NULL
) ? 0 : strlen(u
);
1926 j
= (p
== NULL
) ? 0 : strlen(p
);
1928 connect_data
->write_buf_len
= 1 + 1 + i
+ 1 + j
;
1929 connect_data
->write_buffer
= g_malloc(connect_data
->write_buf_len
);
1930 connect_data
->written_len
= 0;
1932 connect_data
->write_buffer
[0] = 0x01; /* version 1 */
1933 connect_data
->write_buffer
[1] = i
;
1935 memcpy(connect_data
->write_buffer
+ 2, u
, i
);
1936 connect_data
->write_buffer
[2 + i
] = j
;
1938 memcpy(connect_data
->write_buffer
+ 2 + i
+ 1, p
, j
);
1940 g_free(connect_data
->read_buffer
);
1941 connect_data
->read_buffer
= NULL
;
1943 connect_data
->read_cb
= s5_readauth
;
1945 connect_data
->inpa
= purple_input_add(connect_data
->fd
, PURPLE_INPUT_WRITE
,
1946 proxy_do_write
, connect_data
);
1948 proxy_do_write(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1951 } else if (connect_data
->read_buffer
[1] == 0x03) {
1953 userlen
= strlen(purple_proxy_info_get_username(connect_data
->gpi
));
1955 connect_data
->write_buf_len
= 7 + userlen
;
1956 connect_data
->write_buffer
= g_malloc(connect_data
->write_buf_len
);
1957 connect_data
->written_len
= 0;
1959 connect_data
->write_buffer
[0] = 0x01;
1960 connect_data
->write_buffer
[1] = 0x02;
1961 connect_data
->write_buffer
[2] = 0x11;
1962 connect_data
->write_buffer
[3] = 0x01;
1963 connect_data
->write_buffer
[4] = 0x85;
1964 connect_data
->write_buffer
[5] = 0x02;
1965 connect_data
->write_buffer
[6] = userlen
;
1966 memcpy(connect_data
->write_buffer
+ 7,
1967 purple_proxy_info_get_username(connect_data
->gpi
), userlen
);
1969 g_free(connect_data
->read_buffer
);
1970 connect_data
->read_buffer
= NULL
;
1972 connect_data
->read_cb
= s5_readchap
;
1974 connect_data
->inpa
= purple_input_add(connect_data
->fd
, PURPLE_INPUT_WRITE
,
1975 proxy_do_write
, connect_data
);
1977 proxy_do_write(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
1981 g_free(connect_data
->read_buffer
);
1982 connect_data
->read_buffer
= NULL
;
1984 s5_sendconnect(connect_data
, connect_data
->fd
);
1989 s5_canwrite(gpointer data
, gint source
, PurpleInputCondition cond
)
1991 unsigned char buf
[5];
1993 PurpleProxyConnectData
*connect_data
= data
;
1994 int error
= ETIMEDOUT
;
1997 purple_debug_info("socks5 proxy", "Connected.\n");
1999 if (connect_data
->inpa
> 0)
2001 purple_input_remove(connect_data
->inpa
);
2002 connect_data
->inpa
= 0;
2005 ret
= purple_input_get_error(connect_data
->fd
, &error
);
2006 if ((ret
!= 0) || (error
!= 0))
2010 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(error
));
2015 buf
[0] = 0x05; /* SOCKS version 5 */
2017 if (purple_proxy_info_get_username(connect_data
->gpi
) != NULL
) {
2018 buf
[1] = 0x03; /* three methods */
2019 buf
[2] = 0x00; /* no authentication */
2020 buf
[3] = 0x03; /* CHAP authentication */
2021 buf
[4] = 0x02; /* username/password authentication */
2030 connect_data
->write_buf_len
= i
;
2031 connect_data
->write_buffer
= g_malloc(connect_data
->write_buf_len
);
2032 memcpy(connect_data
->write_buffer
, buf
, i
);
2034 connect_data
->read_cb
= s5_canread
;
2036 connect_data
->inpa
= purple_input_add(connect_data
->fd
, PURPLE_INPUT_WRITE
, proxy_do_write
, connect_data
);
2037 proxy_do_write(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
2041 proxy_connect_socks5(PurpleProxyConnectData
*connect_data
, struct sockaddr
*addr
, socklen_t addrlen
)
2045 purple_debug_info("proxy",
2046 "Connecting to %s:%d via %s:%d using SOCKS5\n",
2047 connect_data
->host
, connect_data
->port
,
2048 purple_proxy_info_get_host(connect_data
->gpi
),
2049 purple_proxy_info_get_port(connect_data
->gpi
));
2051 connect_data
->fd
= socket(addr
->sa_family
, SOCK_STREAM
, 0);
2052 if (connect_data
->fd
< 0)
2054 purple_proxy_connect_data_disconnect_formatted(connect_data
,
2055 _("Unable to create socket: %s"), g_strerror(errno
));
2059 flags
= fcntl(connect_data
->fd
, F_GETFL
);
2060 fcntl(connect_data
->fd
, F_SETFL
, flags
| O_NONBLOCK
);
2062 fcntl(connect_data
->fd
, F_SETFD
, FD_CLOEXEC
);
2065 if (connect(connect_data
->fd
, addr
, addrlen
) != 0)
2067 if ((errno
== EINPROGRESS
) || (errno
== EINTR
))
2069 purple_debug_info("socks5 proxy", "Connection in progress\n");
2070 connect_data
->inpa
= purple_input_add(connect_data
->fd
,
2071 PURPLE_INPUT_WRITE
, s5_canwrite
, connect_data
);
2075 purple_proxy_connect_data_disconnect(connect_data
, g_strerror(errno
));
2080 purple_debug_info("proxy", "Connected immediately.\n");
2082 s5_canwrite(connect_data
, connect_data
->fd
, PURPLE_INPUT_WRITE
);
2087 * This function attempts to connect to the next IP address in the list
2088 * of IP addresses returned to us by purple_dnsquery_a() and attemps
2089 * to connect to each one. This is called after the hostname is
2090 * resolved, and each time a connection attempt fails (assuming there
2091 * is another IP address to try).
2093 #ifndef INET6_ADDRSTRLEN
2094 #define INET6_ADDRSTRLEN 46
2097 static void try_connect(PurpleProxyConnectData
*connect_data
)
2100 struct sockaddr
*addr
;
2101 char ipaddr
[INET6_ADDRSTRLEN
];
2103 addrlen
= GPOINTER_TO_INT(connect_data
->hosts
->data
);
2104 connect_data
->hosts
= g_slist_remove(connect_data
->hosts
, connect_data
->hosts
->data
);
2105 addr
= connect_data
->hosts
->data
;
2106 connect_data
->hosts
= g_slist_remove(connect_data
->hosts
, connect_data
->hosts
->data
);
2107 #ifdef HAVE_INET_NTOP
2108 if (addr
->sa_family
== AF_INET
)
2109 inet_ntop(addr
->sa_family
, &((struct sockaddr_in
*)addr
)->sin_addr
,
2110 ipaddr
, sizeof(ipaddr
));
2111 else if (addr
->sa_family
== AF_INET6
)
2112 inet_ntop(addr
->sa_family
, &((struct sockaddr_in6
*)addr
)->sin6_addr
,
2113 ipaddr
, sizeof(ipaddr
));
2115 memcpy(ipaddr
, inet_ntoa(((struct sockaddr_in
*)addr
)->sin_addr
),
2118 purple_debug_info("proxy", "Attempting connection to %s\n", ipaddr
);
2120 if (connect_data
->socket_type
== SOCK_DGRAM
) {
2121 proxy_connect_udp_none(connect_data
, addr
, addrlen
);
2126 switch (purple_proxy_info_get_type(connect_data
->gpi
)) {
2127 case PURPLE_PROXY_NONE
:
2128 proxy_connect_none(connect_data
, addr
, addrlen
);
2131 case PURPLE_PROXY_HTTP
:
2132 proxy_connect_http(connect_data
, addr
, addrlen
);
2135 case PURPLE_PROXY_SOCKS4
:
2136 proxy_connect_socks4(connect_data
, addr
, addrlen
);
2139 case PURPLE_PROXY_SOCKS5
:
2140 proxy_connect_socks5(connect_data
, addr
, addrlen
);
2143 case PURPLE_PROXY_USE_ENVVAR
:
2144 proxy_connect_http(connect_data
, addr
, addrlen
);
2155 connection_host_resolved(GSList
*hosts
, gpointer data
,
2156 const char *error_message
)
2158 PurpleProxyConnectData
*connect_data
;
2160 connect_data
= data
;
2161 connect_data
->query_data
= NULL
;
2163 if (error_message
!= NULL
)
2165 purple_proxy_connect_data_disconnect(connect_data
, error_message
);
2171 purple_proxy_connect_data_disconnect(connect_data
, _("Unable to resolve hostname"));
2175 connect_data
->hosts
= hosts
;
2177 try_connect(connect_data
);
2181 purple_proxy_get_setup(PurpleAccount
*account
)
2183 PurpleProxyInfo
*gpi
= NULL
;
2186 /* This is used as a fallback so we don't overwrite the selected proxy type */
2187 static PurpleProxyInfo
*tmp_none_proxy_info
= NULL
;
2188 if (!tmp_none_proxy_info
) {
2189 tmp_none_proxy_info
= purple_proxy_info_new();
2190 purple_proxy_info_set_type(tmp_none_proxy_info
, PURPLE_PROXY_NONE
);
2193 if (account
&& purple_account_get_proxy_info(account
) != NULL
) {
2194 gpi
= purple_account_get_proxy_info(account
);
2195 if (purple_proxy_info_get_type(gpi
) == PURPLE_PROXY_USE_GLOBAL
)
2199 if (purple_running_gnome())
2200 gpi
= purple_gnome_proxy_get_info();
2202 gpi
= purple_global_proxy_get_info();
2205 if (purple_proxy_info_get_type(gpi
) == PURPLE_PROXY_USE_ENVVAR
) {
2206 if ((tmp
= g_getenv("HTTP_PROXY")) != NULL
||
2207 (tmp
= g_getenv("http_proxy")) != NULL
||
2208 (tmp
= g_getenv("HTTPPROXY")) != NULL
) {
2209 char *proxyhost
, *proxyuser
, *proxypasswd
;
2212 /* http_proxy-format:
2213 * export http_proxy="http://user:passwd@your.proxy.server:port/"
2215 if(purple_url_parse(tmp
, &proxyhost
, &proxyport
, NULL
, &proxyuser
, &proxypasswd
)) {
2216 purple_proxy_info_set_host(gpi
, proxyhost
);
2219 purple_proxy_info_set_username(gpi
, proxyuser
);
2222 purple_proxy_info_set_password(gpi
, proxypasswd
);
2223 g_free(proxypasswd
);
2225 /* only for backward compatibility */
2226 if (proxyport
== 80 &&
2227 ((tmp
= g_getenv("HTTP_PROXY_PORT")) != NULL
||
2228 (tmp
= g_getenv("http_proxy_port")) != NULL
||
2229 (tmp
= g_getenv("HTTPPROXYPORT")) != NULL
))
2230 proxyport
= atoi(tmp
);
2232 purple_proxy_info_set_port(gpi
, proxyport
);
2234 /* XXX: Do we want to skip this step if user/password were part of url? */
2235 if ((tmp
= g_getenv("HTTP_PROXY_USER")) != NULL
||
2236 (tmp
= g_getenv("http_proxy_user")) != NULL
||
2237 (tmp
= g_getenv("HTTPPROXYUSER")) != NULL
)
2238 purple_proxy_info_set_username(gpi
, tmp
);
2240 if ((tmp
= g_getenv("HTTP_PROXY_PASS")) != NULL
||
2241 (tmp
= g_getenv("http_proxy_pass")) != NULL
||
2242 (tmp
= g_getenv("HTTPPROXYPASS")) != NULL
)
2243 purple_proxy_info_set_password(gpi
, tmp
);
2248 PurpleProxyInfo
*wgpi
;
2249 if ((wgpi
= purple_win32_proxy_get_info()) != NULL
)
2252 /* no proxy environment variable found, don't use a proxy */
2253 purple_debug_info("proxy", "No environment settings found, not using a proxy\n");
2254 gpi
= tmp_none_proxy_info
;
2262 PurpleProxyConnectData
*
2263 purple_proxy_connect(void *handle
, PurpleAccount
*account
,
2264 const char *host
, int port
,
2265 PurpleProxyConnectFunction connect_cb
, gpointer data
)
2267 const char *connecthost
= host
;
2268 int connectport
= port
;
2269 PurpleProxyConnectData
*connect_data
;
2271 g_return_val_if_fail(host
!= NULL
, NULL
);
2272 g_return_val_if_fail(port
> 0, NULL
);
2273 g_return_val_if_fail(connect_cb
!= NULL
, NULL
);
2275 connect_data
= g_new0(PurpleProxyConnectData
, 1);
2276 connect_data
->fd
= -1;
2277 connect_data
->socket_type
= SOCK_STREAM
;
2278 connect_data
->handle
= handle
;
2279 connect_data
->connect_cb
= connect_cb
;
2280 connect_data
->data
= data
;
2281 connect_data
->host
= g_strdup(host
);
2282 connect_data
->port
= port
;
2283 connect_data
->gpi
= purple_proxy_get_setup(account
);
2285 if ((purple_proxy_info_get_type(connect_data
->gpi
) != PURPLE_PROXY_NONE
) &&
2286 (purple_proxy_info_get_host(connect_data
->gpi
) == NULL
||
2287 purple_proxy_info_get_port(connect_data
->gpi
) <= 0)) {
2289 purple_notify_error(NULL
, NULL
, _("Invalid proxy settings"), _("Either the host name or port number specified for your given proxy type is invalid."));
2290 purple_proxy_connect_data_destroy(connect_data
);
2294 switch (purple_proxy_info_get_type(connect_data
->gpi
))
2296 case PURPLE_PROXY_NONE
:
2299 case PURPLE_PROXY_HTTP
:
2300 case PURPLE_PROXY_SOCKS4
:
2301 case PURPLE_PROXY_SOCKS5
:
2302 case PURPLE_PROXY_USE_ENVVAR
:
2303 connecthost
= purple_proxy_info_get_host(connect_data
->gpi
);
2304 connectport
= purple_proxy_info_get_port(connect_data
->gpi
);
2308 purple_debug_error("proxy", "Invalid Proxy type (%d) specified.\n",
2309 purple_proxy_info_get_type(connect_data
->gpi
));
2310 purple_proxy_connect_data_destroy(connect_data
);
2314 connect_data
->query_data
= purple_dnsquery_a(connecthost
,
2315 connectport
, connection_host_resolved
, connect_data
);
2316 if (connect_data
->query_data
== NULL
)
2318 purple_debug_error("proxy", "dns query failed unexpectedly.\n");
2319 purple_proxy_connect_data_destroy(connect_data
);
2323 handles
= g_slist_prepend(handles
, connect_data
);
2325 return connect_data
;
2328 PurpleProxyConnectData
*
2329 purple_proxy_connect_udp(void *handle
, PurpleAccount
*account
,
2330 const char *host
, int port
,
2331 PurpleProxyConnectFunction connect_cb
, gpointer data
)
2333 const char *connecthost
= host
;
2334 int connectport
= port
;
2335 PurpleProxyConnectData
*connect_data
;
2337 g_return_val_if_fail(host
!= NULL
, NULL
);
2338 g_return_val_if_fail(port
> 0, NULL
);
2339 g_return_val_if_fail(connect_cb
!= NULL
, NULL
);
2341 connect_data
= g_new0(PurpleProxyConnectData
, 1);
2342 connect_data
->fd
= -1;
2343 connect_data
->socket_type
= SOCK_DGRAM
;
2344 connect_data
->handle
= handle
;
2345 connect_data
->connect_cb
= connect_cb
;
2346 connect_data
->data
= data
;
2347 connect_data
->host
= g_strdup(host
);
2348 connect_data
->port
= port
;
2349 connect_data
->gpi
= purple_proxy_get_setup(account
);
2351 if ((purple_proxy_info_get_type(connect_data
->gpi
) != PURPLE_PROXY_NONE
) &&
2352 (purple_proxy_info_get_host(connect_data
->gpi
) == NULL
||
2353 purple_proxy_info_get_port(connect_data
->gpi
) <= 0)) {
2355 purple_notify_error(NULL
, NULL
, _("Invalid proxy settings"), _("Either the host name or port number specified for your given proxy type is invalid."));
2356 purple_proxy_connect_data_destroy(connect_data
);
2360 switch (purple_proxy_info_get_type(connect_data
->gpi
))
2362 case PURPLE_PROXY_NONE
:
2365 case PURPLE_PROXY_HTTP
:
2366 case PURPLE_PROXY_SOCKS4
:
2367 case PURPLE_PROXY_SOCKS5
:
2368 case PURPLE_PROXY_USE_ENVVAR
:
2369 purple_debug_info("proxy", "Ignoring Proxy type (%d) for UDP.\n",
2370 purple_proxy_info_get_type(connect_data
->gpi
));
2374 purple_debug_error("proxy", "Invalid Proxy type (%d) specified.\n",
2375 purple_proxy_info_get_type(connect_data
->gpi
));
2376 purple_proxy_connect_data_destroy(connect_data
);
2380 connect_data
->query_data
= purple_dnsquery_a(connecthost
,
2381 connectport
, connection_host_resolved
, connect_data
);
2382 if (connect_data
->query_data
== NULL
)
2384 purple_proxy_connect_data_destroy(connect_data
);
2388 handles
= g_slist_prepend(handles
, connect_data
);
2390 return connect_data
;
2394 * Combine some of this code with purple_proxy_connect()
2396 PurpleProxyConnectData
*
2397 purple_proxy_connect_socks5(void *handle
, PurpleProxyInfo
*gpi
,
2398 const char *host
, int port
,
2399 PurpleProxyConnectFunction connect_cb
,
2402 PurpleProxyConnectData
*connect_data
;
2404 g_return_val_if_fail(host
!= NULL
, NULL
);
2405 g_return_val_if_fail(port
>= 0, NULL
);
2406 g_return_val_if_fail(connect_cb
!= NULL
, NULL
);
2408 connect_data
= g_new0(PurpleProxyConnectData
, 1);
2409 connect_data
->fd
= -1;
2410 connect_data
->socket_type
= SOCK_STREAM
;
2411 connect_data
->handle
= handle
;
2412 connect_data
->connect_cb
= connect_cb
;
2413 connect_data
->data
= data
;
2414 connect_data
->host
= g_strdup(host
);
2415 connect_data
->port
= port
;
2416 connect_data
->gpi
= gpi
;
2418 connect_data
->query_data
=
2419 purple_dnsquery_a(purple_proxy_info_get_host(gpi
),
2420 purple_proxy_info_get_port(gpi
),
2421 connection_host_resolved
, connect_data
);
2422 if (connect_data
->query_data
== NULL
)
2424 purple_proxy_connect_data_destroy(connect_data
);
2428 handles
= g_slist_prepend(handles
, connect_data
);
2430 return connect_data
;
2434 purple_proxy_connect_cancel(PurpleProxyConnectData
*connect_data
)
2436 purple_proxy_connect_data_disconnect(connect_data
, NULL
);
2437 purple_proxy_connect_data_destroy(connect_data
);
2441 purple_proxy_connect_cancel_with_handle(void *handle
)
2445 for (l
= handles
; l
!= NULL
; l
= l_next
) {
2446 PurpleProxyConnectData
*connect_data
= l
->data
;
2450 if (connect_data
->handle
== handle
)
2451 purple_proxy_connect_cancel(connect_data
);
2456 proxy_pref_cb(const char *name
, PurplePrefType type
,
2457 gconstpointer value
, gpointer data
)
2459 PurpleProxyInfo
*info
= purple_global_proxy_get_info();
2461 if (purple_strequal(name
, "/purple/proxy/type")) {
2463 const char *type
= value
;
2465 if (purple_strequal(type
, "none"))
2466 proxytype
= PURPLE_PROXY_NONE
;
2467 else if (purple_strequal(type
, "http"))
2468 proxytype
= PURPLE_PROXY_HTTP
;
2469 else if (purple_strequal(type
, "socks4"))
2470 proxytype
= PURPLE_PROXY_SOCKS4
;
2471 else if (purple_strequal(type
, "socks5"))
2472 proxytype
= PURPLE_PROXY_SOCKS5
;
2473 else if (purple_strequal(type
, "envvar"))
2474 proxytype
= PURPLE_PROXY_USE_ENVVAR
;
2478 purple_proxy_info_set_type(info
, proxytype
);
2479 } else if (purple_strequal(name
, "/purple/proxy/host"))
2480 purple_proxy_info_set_host(info
, value
);
2481 else if (purple_strequal(name
, "/purple/proxy/port"))
2482 purple_proxy_info_set_port(info
, GPOINTER_TO_INT(value
));
2483 else if (purple_strequal(name
, "/purple/proxy/username"))
2484 purple_proxy_info_set_username(info
, value
);
2485 else if (purple_strequal(name
, "/purple/proxy/password"))
2486 purple_proxy_info_set_password(info
, value
);
2490 purple_proxy_get_handle()
2498 purple_proxy_init(void)
2502 /* Initialize a default proxy info struct. */
2503 global_proxy_info
= purple_proxy_info_new();
2506 purple_prefs_add_none("/purple/proxy");
2507 purple_prefs_add_string("/purple/proxy/type", "none");
2508 purple_prefs_add_string("/purple/proxy/host", "");
2509 purple_prefs_add_int("/purple/proxy/port", 0);
2510 purple_prefs_add_string("/purple/proxy/username", "");
2511 purple_prefs_add_string("/purple/proxy/password", "");
2512 purple_prefs_add_bool("/purple/proxy/socks4_remotedns", FALSE
);
2514 /* Setup callbacks for the preferences. */
2515 handle
= purple_proxy_get_handle();
2516 purple_prefs_connect_callback(handle
, "/purple/proxy/type", proxy_pref_cb
,
2518 purple_prefs_connect_callback(handle
, "/purple/proxy/host", proxy_pref_cb
,
2520 purple_prefs_connect_callback(handle
, "/purple/proxy/port", proxy_pref_cb
,
2522 purple_prefs_connect_callback(handle
, "/purple/proxy/username",
2523 proxy_pref_cb
, NULL
);
2524 purple_prefs_connect_callback(handle
, "/purple/proxy/password",
2525 proxy_pref_cb
, NULL
);
2527 /* Load the initial proxy settings */
2528 purple_prefs_trigger_callback("/purple/proxy/type");
2529 purple_prefs_trigger_callback("/purple/proxy/host");
2530 purple_prefs_trigger_callback("/purple/proxy/port");
2531 purple_prefs_trigger_callback("/purple/proxy/username");
2532 purple_prefs_trigger_callback("/purple/proxy/password");
2536 purple_proxy_uninit(void)
2538 while (handles
!= NULL
)
2540 purple_proxy_connect_data_disconnect(handles
->data
, NULL
);
2541 purple_proxy_connect_data_destroy(handles
->data
);
2544 purple_prefs_disconnect_by_handle(purple_proxy_get_handle());
2546 purple_proxy_info_destroy(global_proxy_info
);
2547 global_proxy_info
= NULL
;