Mercurial > pidgin
annotate libgaim/proxy.c @ 15113:4a8c368df4ea
[gaim-migrate @ 17899]
Some touchups:
* If one of the parallel connection attempts fails immediately (i.e.
does not time out) then don't cancel the other one.
* Make sure we don't continue on to step 2 of the peer connection
process after we kick off the parallel gaim_proxy_connects(). It
looks like this would happen most of the time, because the
connect_timeout_timer would be added for the verified ip, so it
would NOT be added for the client ip, and so we wouldn't hit the
"return" call because it happens to be in the same block as the
second gaim_timeout_add() call.
* Add the connection timeout timer even if the gaim_proxy_connect() to
the verified ip returns NULL for some crazy reason.
I didn't actually test any of this. I should probably do that when
I get home.
committer: Tailor Script <tailor@pidgin.im>
| author | Mark Doliner <mark@kingant.net> |
|---|---|
| date | Wed, 06 Dec 2006 01:29:59 +0000 |
| parents | c157efddc62a |
| children |
| 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 { | |
|
14979
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14970
diff
changeset
|
1688 GaimProxyInfo *gpi = NULL; |
| 14192 | 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 |
|
14979
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14970
diff
changeset
|
1698 if (account && gaim_account_get_proxy_info(account) != NULL) { |
| 14192 | 1699 gpi = gaim_account_get_proxy_info(account); |
|
14979
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14970
diff
changeset
|
1700 if (gaim_proxy_info_get_type(gpi) == GAIM_PROXY_USE_GLOBAL) |
|
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14970
diff
changeset
|
1701 gpi = NULL; |
|
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14970
diff
changeset
|
1702 } |
|
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14970
diff
changeset
|
1703 if (gpi == NULL) { |
|
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14970
diff
changeset
|
1704 if (gaim_running_gnome()) |
|
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14970
diff
changeset
|
1705 gpi = gaim_gnome_proxy_get_info(); |
|
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14970
diff
changeset
|
1706 else |
|
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14970
diff
changeset
|
1707 gpi = gaim_global_proxy_get_info(); |
|
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14970
diff
changeset
|
1708 } |
| 14192 | 1709 |
| 1710 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
|
1711 #ifdef _WIN32 |
|
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14953
diff
changeset
|
1712 wgaim_check_for_proxy_changes(); |
|
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14953
diff
changeset
|
1713 #endif |
| 14192 | 1714 if ((tmp = g_getenv("HTTP_PROXY")) != NULL || |
| 1715 (tmp = g_getenv("http_proxy")) != NULL || | |
| 1716 (tmp = g_getenv("HTTPPROXY")) != NULL) { | |
|
14970
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14953
diff
changeset
|
1717 char *proxyhost, *proxyuser, *proxypasswd; |
| 14192 | 1718 int proxyport; |
| 1719 | |
| 1720 /* http_proxy-format: | |
| 1721 * export http_proxy="http://user:passwd@your.proxy.server:port/" | |
| 1722 */ | |
|
14970
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14953
diff
changeset
|
1723 if(gaim_url_parse(tmp, &proxyhost, &proxyport, NULL, &proxyuser, &proxypasswd)) { |
| 14192 | 1724 gaim_proxy_info_set_host(gpi, proxyhost); |
| 1725 g_free(proxyhost); | |
|
14970
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14953
diff
changeset
|
1726 |
|
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14953
diff
changeset
|
1727 gaim_proxy_info_set_username(gpi, proxyuser); |
|
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14953
diff
changeset
|
1728 g_free(proxyuser); |
|
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14953
diff
changeset
|
1729 |
|
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14953
diff
changeset
|
1730 gaim_proxy_info_set_password(gpi, proxypasswd); |
|
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14953
diff
changeset
|
1731 g_free(proxypasswd); |
| 14192 | 1732 |
| 1733 /* only for backward compatibility */ | |
| 1734 if (proxyport == 80 && | |
| 1735 ((tmp = g_getenv("HTTP_PROXY_PORT")) != NULL || | |
| 1736 (tmp = g_getenv("http_proxy_port")) != NULL || | |
| 1737 (tmp = g_getenv("HTTPPROXYPORT")) != NULL)) | |
|
14970
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14953
diff
changeset
|
1738 proxyport = atoi(tmp); |
| 14192 | 1739 |
| 1740 gaim_proxy_info_set_port(gpi, proxyport); | |
|
14979
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14970
diff
changeset
|
1741 |
|
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14970
diff
changeset
|
1742 /* XXX: Do we want to skip this step if user/password were part of url? */ |
|
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14970
diff
changeset
|
1743 if ((tmp = g_getenv("HTTP_PROXY_USER")) != NULL || |
|
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14970
diff
changeset
|
1744 (tmp = g_getenv("http_proxy_user")) != NULL || |
|
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14970
diff
changeset
|
1745 (tmp = g_getenv("HTTPPROXYUSER")) != NULL) |
|
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14970
diff
changeset
|
1746 gaim_proxy_info_set_username(gpi, tmp); |
|
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14970
diff
changeset
|
1747 |
|
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14970
diff
changeset
|
1748 if ((tmp = g_getenv("HTTP_PROXY_PASS")) != NULL || |
|
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14970
diff
changeset
|
1749 (tmp = g_getenv("http_proxy_pass")) != NULL || |
|
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14970
diff
changeset
|
1750 (tmp = g_getenv("HTTPPROXYPASS")) != NULL) |
|
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14970
diff
changeset
|
1751 gaim_proxy_info_set_password(gpi, tmp); |
|
c157efddc62a
[gaim-migrate @ 17758]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14970
diff
changeset
|
1752 |
| 14192 | 1753 } |
| 1754 } else { | |
| 1755 /* no proxy environment variable found, don't use a proxy */ | |
| 1756 gaim_debug_info("proxy", "No environment settings found, not using a proxy\n"); | |
|
14970
721465a37d4e
[gaim-migrate @ 17749]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14953
diff
changeset
|
1757 gpi = tmp_none_proxy_info; |
| 14192 | 1758 } |
| 1759 | |
| 1760 } | |
| 1761 | |
| 1762 return gpi; | |
| 1763 } | |
| 1764 | |
| 14262 | 1765 GaimProxyConnectData * |
| 14837 | 1766 gaim_proxy_connect(void *handle, GaimAccount *account, |
| 1767 const char *host, int port, | |
| 14192 | 1768 GaimProxyConnectFunction connect_cb, gpointer data) |
| 1769 { | |
| 1770 const char *connecthost = host; | |
| 1771 int connectport = port; | |
| 14262 | 1772 GaimProxyConnectData *connect_data; |
| 14192 | 1773 |
| 1774 g_return_val_if_fail(host != NULL, NULL); | |
| 1775 g_return_val_if_fail(port > 0, NULL); | |
| 1776 g_return_val_if_fail(connect_cb != NULL, NULL); | |
| 1777 | |
| 14262 | 1778 connect_data = g_new0(GaimProxyConnectData, 1); |
| 1779 connect_data->fd = -1; | |
| 14837 | 1780 connect_data->handle = handle; |
| 14262 | 1781 connect_data->connect_cb = connect_cb; |
| 1782 connect_data->data = data; | |
| 1783 connect_data->host = g_strdup(host); | |
| 1784 connect_data->port = port; | |
| 1785 connect_data->gpi = gaim_proxy_get_setup(account); | |
| 14192 | 1786 |
| 14262 | 1787 if ((gaim_proxy_info_get_type(connect_data->gpi) != GAIM_PROXY_NONE) && |
| 1788 (gaim_proxy_info_get_host(connect_data->gpi) == NULL || | |
| 1789 gaim_proxy_info_get_port(connect_data->gpi) <= 0)) { | |
| 14192 | 1790 |
| 1791 gaim_notify_error(NULL, NULL, _("Invalid proxy settings"), _("Either the host name or port number specified for your given proxy type is invalid.")); | |
| 14262 | 1792 gaim_proxy_connect_data_destroy(connect_data); |
| 14192 | 1793 return NULL; |
| 1794 } | |
| 1795 | |
| 14262 | 1796 switch (gaim_proxy_info_get_type(connect_data->gpi)) |
| 14192 | 1797 { |
| 1798 case GAIM_PROXY_NONE: | |
| 1799 break; | |
| 1800 | |
| 1801 case GAIM_PROXY_HTTP: | |
| 1802 case GAIM_PROXY_SOCKS4: | |
| 1803 case GAIM_PROXY_SOCKS5: | |
| 1804 case GAIM_PROXY_USE_ENVVAR: | |
| 14262 | 1805 connecthost = gaim_proxy_info_get_host(connect_data->gpi); |
| 1806 connectport = gaim_proxy_info_get_port(connect_data->gpi); | |
| 14192 | 1807 break; |
| 1808 | |
| 1809 default: | |
| 14262 | 1810 gaim_proxy_connect_data_destroy(connect_data); |
| 14192 | 1811 return NULL; |
| 1812 } | |
| 1813 | |
| 14262 | 1814 connect_data->query_data = gaim_dnsquery_a(connecthost, |
| 1815 connectport, connection_host_resolved, connect_data); | |
| 1816 if (connect_data->query_data == NULL) | |
| 14192 | 1817 { |
| 14262 | 1818 gaim_proxy_connect_data_destroy(connect_data); |
| 14192 | 1819 return NULL; |
| 1820 } | |
| 1821 | |
| 14837 | 1822 handles = g_slist_prepend(handles, connect_data); |
| 14192 | 1823 |
| 14262 | 1824 return connect_data; |
| 14192 | 1825 } |
| 1826 | |
| 1827 /* | |
| 1828 * Combine some of this code with gaim_proxy_connect() | |
| 1829 */ | |
| 14262 | 1830 GaimProxyConnectData * |
| 14837 | 1831 gaim_proxy_connect_socks5(void *handle, GaimProxyInfo *gpi, |
| 1832 const char *host, int port, | |
| 1833 GaimProxyConnectFunction connect_cb, | |
| 1834 gpointer data) | |
| 14192 | 1835 { |
| 14262 | 1836 GaimProxyConnectData *connect_data; |
| 14192 | 1837 |
| 1838 g_return_val_if_fail(host != NULL, NULL); | |
| 14953 | 1839 g_return_val_if_fail(port >= 0, NULL); |
| 14192 | 1840 g_return_val_if_fail(connect_cb != NULL, NULL); |
| 1841 | |
| 14262 | 1842 connect_data = g_new0(GaimProxyConnectData, 1); |
| 1843 connect_data->fd = -1; | |
| 14837 | 1844 connect_data->handle = handle; |
| 14262 | 1845 connect_data->connect_cb = connect_cb; |
| 1846 connect_data->data = data; | |
| 1847 connect_data->host = g_strdup(host); | |
| 1848 connect_data->port = port; | |
| 1849 connect_data->gpi = gpi; | |
| 14192 | 1850 |
| 14262 | 1851 connect_data->query_data = |
| 14192 | 1852 gaim_dnsquery_a(gaim_proxy_info_get_host(gpi), |
| 1853 gaim_proxy_info_get_port(gpi), | |
| 14262 | 1854 connection_host_resolved, connect_data); |
| 1855 if (connect_data->query_data == NULL) | |
| 14192 | 1856 { |
| 14262 | 1857 gaim_proxy_connect_data_destroy(connect_data); |
| 14192 | 1858 return NULL; |
| 1859 } | |
| 1860 | |
| 14837 | 1861 handles = g_slist_prepend(handles, connect_data); |
| 14192 | 1862 |
| 14262 | 1863 return connect_data; |
| 14192 | 1864 } |
| 1865 | |
| 1866 void | |
| 14262 | 1867 gaim_proxy_connect_cancel(GaimProxyConnectData *connect_data) |
| 14192 | 1868 { |
| 14451 | 1869 gaim_proxy_connect_data_disconnect(connect_data, NULL); |
| 14262 | 1870 gaim_proxy_connect_data_destroy(connect_data); |
| 14192 | 1871 } |
| 1872 | |
| 14837 | 1873 void |
| 1874 gaim_proxy_connect_cancel_with_handle(void *handle) | |
| 1875 { | |
| 1876 GSList *l, *l_next; | |
| 1877 | |
| 1878 for (l = handles; l != NULL; l = l_next) { | |
| 1879 GaimProxyConnectData *connect_data = l->data; | |
| 1880 | |
| 1881 l_next = l->next; | |
| 1882 | |
| 1883 if (connect_data->handle == handle) | |
| 1884 gaim_proxy_connect_cancel(connect_data); | |
| 1885 } | |
| 1886 } | |
| 1887 | |
| 14192 | 1888 static void |
| 1889 proxy_pref_cb(const char *name, GaimPrefType type, | |
| 1890 gconstpointer value, gpointer data) | |
| 1891 { | |
| 1892 GaimProxyInfo *info = gaim_global_proxy_get_info(); | |
| 1893 | |
| 1894 if (!strcmp(name, "/core/proxy/type")) { | |
| 1895 int proxytype; | |
| 1896 const char *type = value; | |
| 1897 | |
| 1898 if (!strcmp(type, "none")) | |
| 1899 proxytype = GAIM_PROXY_NONE; | |
| 1900 else if (!strcmp(type, "http")) | |
| 1901 proxytype = GAIM_PROXY_HTTP; | |
| 1902 else if (!strcmp(type, "socks4")) | |
| 1903 proxytype = GAIM_PROXY_SOCKS4; | |
| 1904 else if (!strcmp(type, "socks5")) | |
| 1905 proxytype = GAIM_PROXY_SOCKS5; | |
| 1906 else if (!strcmp(type, "envvar")) | |
| 1907 proxytype = GAIM_PROXY_USE_ENVVAR; | |
| 1908 else | |
| 1909 proxytype = -1; | |
| 1910 | |
| 1911 gaim_proxy_info_set_type(info, proxytype); | |
| 1912 } else if (!strcmp(name, "/core/proxy/host")) | |
| 1913 gaim_proxy_info_set_host(info, value); | |
| 1914 else if (!strcmp(name, "/core/proxy/port")) | |
| 1915 gaim_proxy_info_set_port(info, GPOINTER_TO_INT(value)); | |
| 1916 else if (!strcmp(name, "/core/proxy/username")) | |
| 1917 gaim_proxy_info_set_username(info, value); | |
| 1918 else if (!strcmp(name, "/core/proxy/password")) | |
| 1919 gaim_proxy_info_set_password(info, value); | |
| 1920 } | |
| 1921 | |
| 1922 void * | |
| 1923 gaim_proxy_get_handle() | |
| 1924 { | |
| 1925 static int handle; | |
| 1926 | |
| 1927 return &handle; | |
| 1928 } | |
| 1929 | |
| 1930 void | |
| 1931 gaim_proxy_init(void) | |
| 1932 { | |
| 1933 void *handle; | |
| 1934 | |
| 1935 /* Initialize a default proxy info struct. */ | |
| 1936 global_proxy_info = gaim_proxy_info_new(); | |
| 1937 | |
| 1938 /* Proxy */ | |
| 1939 gaim_prefs_add_none("/core/proxy"); | |
| 1940 gaim_prefs_add_string("/core/proxy/type", "none"); | |
| 1941 gaim_prefs_add_string("/core/proxy/host", ""); | |
| 1942 gaim_prefs_add_int("/core/proxy/port", 0); | |
| 1943 gaim_prefs_add_string("/core/proxy/username", ""); | |
| 1944 gaim_prefs_add_string("/core/proxy/password", ""); | |
| 1945 | |
| 1946 /* Setup callbacks for the preferences. */ | |
| 1947 handle = gaim_proxy_get_handle(); | |
| 1948 gaim_prefs_connect_callback(handle, "/core/proxy/type", proxy_pref_cb, | |
| 1949 NULL); | |
| 1950 gaim_prefs_connect_callback(handle, "/core/proxy/host", proxy_pref_cb, | |
| 1951 NULL); | |
| 1952 gaim_prefs_connect_callback(handle, "/core/proxy/port", proxy_pref_cb, | |
| 1953 NULL); | |
| 1954 gaim_prefs_connect_callback(handle, "/core/proxy/username", | |
| 1955 proxy_pref_cb, NULL); | |
| 1956 gaim_prefs_connect_callback(handle, "/core/proxy/password", | |
| 1957 proxy_pref_cb, NULL); | |
| 1958 } | |
| 1959 | |
| 1960 void | |
| 1961 gaim_proxy_uninit(void) | |
| 1962 { | |
| 14837 | 1963 while (handles != NULL) |
| 14451 | 1964 { |
| 14837 | 1965 gaim_proxy_connect_data_disconnect(handles->data, NULL); |
| 1966 gaim_proxy_connect_data_destroy(handles->data); | |
| 14451 | 1967 } |
| 14192 | 1968 } |
