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