comparison libgaim/proxy.c @ 14451:8bc97389bdb2

[gaim-migrate @ 17165] Some proxy love. This is much less crash-happy. I'm having problems getting HTTP proxies to work for me, but my squid might be misconfigured. And I haven't tested socks4 or 5 yet. committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Tue, 05 Sep 2006 06:10:38 +0000
parents 92eb7a040663
children adcdf5e04128
comparison
equal deleted inserted replaced
14450:1a01b1dae517 14451:8bc97389bdb2
40 #include "util.h" 40 #include "util.h"
41 41
42 struct _GaimProxyConnectData { 42 struct _GaimProxyConnectData {
43 GaimProxyConnectFunction connect_cb; 43 GaimProxyConnectFunction connect_cb;
44 gpointer data; 44 gpointer data;
45 char *host; 45 gchar *host;
46 int port; 46 int port;
47 int fd; 47 int fd;
48 guint inpa; 48 guint inpa;
49 GaimProxyInfo *gpi; 49 GaimProxyInfo *gpi;
50 GaimDnsQueryData *query_data; 50 GaimDnsQueryData *query_data;
272 } 272 }
273 /************************************************************************** 273 /**************************************************************************
274 * Proxy API 274 * Proxy API
275 **************************************************************************/ 275 **************************************************************************/
276 276
277 /* 277 /**
278 * This is used when the connection attempt to one particular IP 278 * Whoever calls this needs to have called
279 * address fails. We close the socket, remove the watcher and get 279 * gaim_proxy_connect_data_disconnect() beforehand.
280 * rid of input and output buffers. Normally try_connect() will
281 * be called immediately after this.
282 */ 280 */
283 static void 281 static void
284 gaim_proxy_connect_data_disconnect(GaimProxyConnectData *connect_data)
285 {
286 if (connect_data->inpa > 0)
287 {
288 gaim_input_remove(connect_data->inpa);
289 connect_data->inpa = 0;
290 }
291
292 if (connect_data->fd >= 0)
293 {
294 close(connect_data->fd);
295 connect_data->fd = -1;
296 }
297
298 g_free(connect_data->write_buffer);
299 connect_data->write_buffer = NULL;
300
301 g_free(connect_data->read_buffer);
302 connect_data->read_buffer = NULL;
303 }
304
305 static void
306 gaim_proxy_connect_data_destroy(GaimProxyConnectData *connect_data) 282 gaim_proxy_connect_data_destroy(GaimProxyConnectData *connect_data)
307 { 283 {
308 gaim_proxy_connect_data_disconnect(connect_data);
309
310 connect_datas = g_slist_remove(connect_datas, connect_data); 284 connect_datas = g_slist_remove(connect_datas, connect_data);
311 285
312 if (connect_data->query_data != NULL) 286 if (connect_data->query_data != NULL)
313 gaim_dnsquery_destroy(connect_data->query_data); 287 gaim_dnsquery_destroy(connect_data->query_data);
314 288
321 connect_data->hosts = g_slist_remove(connect_data->hosts, connect_data->hosts->data); 295 connect_data->hosts = g_slist_remove(connect_data->hosts, connect_data->hosts->data);
322 } 296 }
323 297
324 g_free(connect_data->host); 298 g_free(connect_data->host);
325 g_free(connect_data); 299 g_free(connect_data);
300 }
301
302 /**
303 * Free all information dealing with a connection attempt and
304 * reset the connect_data to prepare for it to try to connect
305 * to another IP address.
306 *
307 * If an error message is passed in, then we know the connection
308 * attempt failed. If the connection attempt failed and
309 * connect_data->hosts is not empty then we try the next IP address.
310 * If the connection attempt failed and we have no more hosts
311 * try try then we call the callback with the given error message,
312 * then destroy the connect_data.
313 *
314 * @param error_message An error message explaining why the connection
315 * failed. This will be passed to the callback function
316 * specified in the call to gaim_proxy_connect(). If the
317 * connection was successful then pass in null.
318 */
319 static void
320 gaim_proxy_connect_data_disconnect(GaimProxyConnectData *connect_data, const gchar *error_message)
321 {
322 if (connect_data->inpa > 0)
323 {
324 gaim_input_remove(connect_data->inpa);
325 connect_data->inpa = 0;
326 }
327
328 if (connect_data->fd >= 0)
329 {
330 close(connect_data->fd);
331 connect_data->fd = -1;
332 }
333
334 g_free(connect_data->write_buffer);
335 connect_data->write_buffer = NULL;
336
337 g_free(connect_data->read_buffer);
338 connect_data->read_buffer = NULL;
339
340 if (error_message != NULL)
341 {
342 gaim_debug_info("proxy", "Connection attempt failed: %s\n",
343 error_message);
344 if (connect_data->hosts != NULL)
345 try_connect(connect_data);
346 else
347 {
348 /* Everything failed! Tell the originator of the request. */
349 connect_data->connect_cb(connect_data->data, -1, error_message);
350 gaim_proxy_connect_data_destroy(connect_data);
351 }
352 }
353 }
354
355 /**
356 * This calls gaim_proxy_connect_data_disconnect(), but it lets you
357 * specify the error_message using a printf()-like syntax.
358 */
359 static void
360 gaim_proxy_connect_data_disconnect_formatted(GaimProxyConnectData *connect_data, const char *format, ...)
361 {
362 va_list args;
363 gchar *tmp;
364
365 va_start(args, format);
366 tmp = g_strdup_vprintf(format, args);
367 va_end(args);
368
369 gaim_proxy_connect_data_disconnect(connect_data, tmp);
370 g_free(tmp);
326 } 371 }
327 372
328 static void 373 static void
329 gaim_proxy_connect_data_connected(GaimProxyConnectData *connect_data) 374 gaim_proxy_connect_data_connected(GaimProxyConnectData *connect_data)
330 { 375 {
335 * our responsibility, and we should be careful not to free it when 380 * our responsibility, and we should be careful not to free it when
336 * we destroy the connect_data. 381 * we destroy the connect_data.
337 */ 382 */
338 connect_data->fd = -1; 383 connect_data->fd = -1;
339 384
385 gaim_proxy_connect_data_disconnect(connect_data, NULL);
340 gaim_proxy_connect_data_destroy(connect_data); 386 gaim_proxy_connect_data_destroy(connect_data);
341 } 387 }
342 388
343 /** 389 static void
344 * @param error An error message explaining why the connection 390 socket_ready_cb(gpointer data, gint source, GaimInputCondition cond)
345 * failed. This will be passed to the callback function
346 * specified in the call to gaim_proxy_connect().
347 */
348 /*
349 * TODO: Make sure all callers of this function pass a really really
350 * good error_message.
351 */
352 static void
353 gaim_proxy_connect_data_error(GaimProxyConnectData *connect_data, const char *format, ...)
354 {
355 gchar *error_message;
356 va_list args;
357
358 va_start(args, format);
359 error_message = g_strdup_vprintf(format, args);
360 va_end(args);
361
362 connect_data->connect_cb(connect_data->data, -1, error_message);
363 g_free(error_message);
364
365 gaim_proxy_connect_data_destroy(connect_data);
366 }
367
368 static void
369 no_one_calls(gpointer data, gint source, GaimInputCondition cond)
370 { 391 {
371 GaimProxyConnectData *connect_data = data; 392 GaimProxyConnectData *connect_data = data;
372 socklen_t len; 393 socklen_t len;
373 int error=0, ret; 394 int error = 0;
395 int ret;
374 396
375 gaim_debug_info("proxy", "Connected.\n"); 397 gaim_debug_info("proxy", "Connected.\n");
376
377 len = sizeof(error);
378 398
379 /* 399 /*
380 * getsockopt after a non-blocking connect returns -1 if something is 400 * getsockopt after a non-blocking connect returns -1 if something is
381 * really messed up (bad descriptor, usually). Otherwise, it returns 0 and 401 * really messed up (bad descriptor, usually). Otherwise, it returns 0 and
382 * error holds what connect would have returned if it blocked until now. 402 * error holds what connect would have returned if it blocked until now.
385 * 405 *
386 * (error == EINPROGRESS can happen after a select because the kernel can 406 * (error == EINPROGRESS can happen after a select because the kernel can
387 * be overly optimistic sometimes. select is just a hint that you might be 407 * be overly optimistic sometimes. select is just a hint that you might be
388 * able to do something.) 408 * able to do something.)
389 */ 409 */
410 len = sizeof(error);
390 ret = getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len); 411 ret = getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len);
412
391 if (ret == 0 && error == EINPROGRESS) 413 if (ret == 0 && error == EINPROGRESS)
392 return; /* we'll be called again later */ 414 /* No worries - we'll be called again later */
393 if (ret < 0 || error != 0) { 415 /* TODO: Does this ever happen? */
394 if (ret!=0) 416 return;
417
418 if (ret != 0 || error != 0) {
419 if (ret != 0)
395 error = errno; 420 error = errno;
396 421 gaim_proxy_connect_data_disconnect(connect_data, strerror(error));
397 gaim_debug_error("proxy", 422 return;
398 "getsockopt SO_ERROR check: %s\n", strerror(error)); 423 }
399
400 gaim_proxy_connect_data_disconnect(connect_data);
401 try_connect(connect_data);
402 return;
403 }
404
405 gaim_input_remove(connect_data->inpa);
406 connect_data->inpa = 0;
407 424
408 gaim_proxy_connect_data_connected(connect_data); 425 gaim_proxy_connect_data_connected(connect_data);
409 } 426 }
410 427
411 static gboolean 428 static gboolean
412 clean_connect(gpointer data) 429 clean_connect(gpointer data)
413 { 430 {
414 GaimProxyConnectData *connect_data; 431 gaim_proxy_connect_data_connected(data);
415
416 connect_data = data;
417 gaim_proxy_connect_data_connected(connect_data);
418 432
419 return FALSE; 433 return FALSE;
420 } 434 }
421 435
422 static int 436 static void
423 proxy_connect_none(GaimProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen) 437 proxy_connect_none(GaimProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen)
424 { 438 {
425 gaim_debug_info("proxy", "Connecting to %s:%d with no proxy\n", 439 gaim_debug_info("proxy", "Connecting to %s:%d with no proxy\n",
426 connect_data->host, connect_data->port); 440 connect_data->host, connect_data->port);
427 441
428 connect_data->fd = socket(addr->sa_family, SOCK_STREAM, 0); 442 connect_data->fd = socket(addr->sa_family, SOCK_STREAM, 0);
429 if (connect_data->fd < 0) 443 if (connect_data->fd < 0)
430 { 444 {
431 gaim_debug_error("proxy", 445 gaim_proxy_connect_data_disconnect_formatted(connect_data,
432 "Unable to create socket: %s\n", strerror(errno)); 446 _("Unable to create socket:\n%s"), strerror(errno));
433 return -1; 447 return;
434 } 448 }
449
435 fcntl(connect_data->fd, F_SETFL, O_NONBLOCK); 450 fcntl(connect_data->fd, F_SETFL, O_NONBLOCK);
436 #ifndef _WIN32 451 #ifndef _WIN32
437 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC); 452 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC);
438 #endif 453 #endif
439 454
440 if (connect(connect_data->fd, (struct sockaddr *)addr, addrlen) != 0) 455 if (connect(connect_data->fd, addr, addrlen) != 0)
441 { 456 {
442 if ((errno == EINPROGRESS) || (errno == EINTR)) { 457 if ((errno == EINPROGRESS) || (errno == EINTR))
458 {
443 gaim_debug_info("proxy", "Connection in progress\n"); 459 gaim_debug_info("proxy", "Connection in progress\n");
444 connect_data->inpa = gaim_input_add(connect_data->fd, GAIM_INPUT_WRITE, no_one_calls, connect_data); 460 connect_data->inpa = gaim_input_add(connect_data->fd,
445 } 461 GAIM_INPUT_WRITE, socket_ready_cb, connect_data);
446 else { 462 }
447 gaim_debug_error("proxy", 463 else
448 "Connect failed: %s\n", strerror(errno)); 464 {
449 close(connect_data->fd); 465 gaim_proxy_connect_data_disconnect(connect_data, strerror(errno));
450 connect_data->fd = -1;
451 return -1;
452 } 466 }
453 } 467 }
454 else 468 else
455 { 469 {
456 /* 470 /*
457 * The connection happened IMMEDIATELY... strange, but whatever. 471 * The connection happened IMMEDIATELY... strange, but whatever.
458 */ 472 */
459 socklen_t len; 473 socklen_t len;
460 int error = ETIMEDOUT; 474 int error = ETIMEDOUT;
475 int ret;
476
461 gaim_debug_info("proxy", "Connected immediately.\n"); 477 gaim_debug_info("proxy", "Connected immediately.\n");
478
462 len = sizeof(error); 479 len = sizeof(error);
463 if (getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len) != 0) 480 ret = getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len);
481 if ((ret != 0) || (error != 0))
464 { 482 {
465 gaim_debug_error("proxy", "getsockopt failed.\n"); 483 if (ret != 0)
466 close(connect_data->fd); 484 error = errno;
467 connect_data->fd = -1; 485 gaim_proxy_connect_data_disconnect(connect_data, strerror(error));
468 return -1; 486 return;
469 } 487 }
470 488
471 /* 489 /*
472 * We want to call the "connected" callback eventually, but we 490 * We want to call the "connected" callback eventually, but we
473 * don't want to call it before we return, just in case. 491 * don't want to call it before we return, just in case.
474 */ 492 */
475 gaim_timeout_add(10, clean_connect, connect_data); 493 gaim_timeout_add(10, clean_connect, connect_data);
476 } 494 }
477 495 }
478 return connect_data->fd; 496
479 } 497 /**
480 498 * This is a utility function used by the HTTP, SOCKS4 and SOCKS5
499 * connect functions. It writes data from a buffer to a socket.
500 * When all the data is written it sets up a watcher to read a
501 * response and call a specified function.
502 */
481 static void 503 static void
482 proxy_do_write(gpointer data, gint source, GaimInputCondition cond) 504 proxy_do_write(gpointer data, gint source, GaimInputCondition cond)
483 { 505 {
484 GaimProxyConnectData *connect_data = data; 506 GaimProxyConnectData *connect_data;
485 const guchar *request = connect_data->write_buffer + connect_data->written_len; 507 const guchar *request;
486 gsize request_len = connect_data->write_buf_len - connect_data->written_len; 508 gsize request_len;
487 509 int ret;
488 int ret = write(connect_data->fd, request, request_len); 510
489 511 connect_data = data;
490 if(ret < 0 && errno == EAGAIN) 512 request = connect_data->write_buffer + connect_data->written_len;
491 return; 513 request_len = connect_data->write_buf_len - connect_data->written_len;
492 else if(ret < 0) { 514
493 gaim_proxy_connect_data_disconnect(connect_data); 515 ret = write(connect_data->fd, request, request_len);
494 try_connect(connect_data); 516 if (ret <= 0)
495 return; 517 {
496 } else if (ret < request_len) { 518 if (errno == EAGAIN)
519 /* No worries */
520 return;
521
522 /* Error! */
523 gaim_proxy_connect_data_disconnect(connect_data, strerror(errno));
524 return;
525 }
526 if (ret < request_len) {
497 connect_data->written_len += ret; 527 connect_data->written_len += ret;
498 return; 528 return;
499 } 529 }
500 530
501 gaim_input_remove(connect_data->inpa); 531 /* We're done writing data! Wait for a response. */
502 g_free(connect_data->write_buffer); 532 g_free(connect_data->write_buffer);
503 connect_data->write_buffer = NULL; 533 connect_data->write_buffer = NULL;
504 534 gaim_input_remove(connect_data->inpa);
505 /* register the response handler for the response */ 535 connect_data->inpa = gaim_input_add(connect_data->fd,
506 connect_data->inpa = gaim_input_add(connect_data->fd, GAIM_INPUT_READ, connect_data->read_cb, connect_data); 536 GAIM_INPUT_READ, connect_data->read_cb, connect_data);
507 } 537 }
508 538
509 #define HTTP_GOODSTRING "HTTP/1.0 200" 539 #define HTTP_GOODSTRING "HTTP/1.0 200"
510 #define HTTP_GOODSTRING2 "HTTP/1.1 200" 540 #define HTTP_GOODSTRING2 "HTTP/1.1 200"
511 541
512 /* read the response to the CONNECT request, if we are requesting a non-port-80 tunnel */ 542 /**
543 * We're using an HTTP proxy for a non-port 80 tunnel. Read the
544 * response to the CONNECT request.
545 */
513 static void 546 static void
514 http_canread(gpointer data, gint source, GaimInputCondition cond) 547 http_canread(gpointer data, gint source, GaimInputCondition cond)
515 { 548 {
516 int len, headers_len, status = 0; 549 int len, headers_len, status = 0;
517 gboolean error; 550 gboolean error;
518 GaimProxyConnectData *connect_data = data; 551 GaimProxyConnectData *connect_data = data;
519 guchar *p; 552 guchar *p;
520 gsize max_read; 553 gsize max_read;
521 554
522 if(connect_data->read_buffer == NULL) { 555 if (connect_data->read_buffer == NULL)
556 {
523 connect_data->read_buf_len = 8192; 557 connect_data->read_buf_len = 8192;
524 connect_data->read_buffer = g_malloc(connect_data->read_buf_len); 558 connect_data->read_buffer = g_malloc(connect_data->read_buf_len);
525 connect_data->read_len = 0; 559 connect_data->read_len = 0;
526 } 560 }
527 561
528 p = connect_data->read_buffer + connect_data->read_len; 562 p = connect_data->read_buffer + connect_data->read_len;
529 max_read = connect_data->read_buf_len - connect_data->read_len - 1; 563 max_read = connect_data->read_buf_len - connect_data->read_len - 1;
530 564
531 len = read(connect_data->fd, p, max_read); 565 len = read(connect_data->fd, p, max_read);
532 if(len < 0 && errno == EAGAIN) 566
533 return; 567 if (len == 0)
534 else if(len <= 0) { 568 {
535 gaim_proxy_connect_data_error(connect_data, _("Lost connection with server for an unknown reason.")); 569 gaim_proxy_connect_data_disconnect(connect_data,
536 return; 570 _("Server closed the connection."));
537 } else { 571 return;
538 connect_data->read_len += len; 572 }
539 } 573
574 if (len < 0)
575 {
576 if (errno == EAGAIN)
577 /* No worries */
578 return;
579
580 /* Error! */
581 gaim_proxy_connect_data_disconnect_formatted(connect_data,
582 _("Lost connection with server:\n%s"), strerror(errno));
583 return;
584 }
585
586 connect_data->read_len += len;
540 p[len] = '\0'; 587 p[len] = '\0';
541 588
542 if((p = (guchar *)g_strstr_len((const gchar *)connect_data->read_buffer, connect_data->read_len, "\r\n\r\n"))) { 589 p = (guchar *)g_strstr_len((const gchar *)connect_data->read_buffer,
590 connect_data->read_len, "\r\n\r\n");
591 if (p != NULL) {
543 *p = '\0'; 592 *p = '\0';
544 headers_len = (p - connect_data->read_buffer) + 4; 593 headers_len = (p - connect_data->read_buffer) + 4;
545 } else if(len == max_read) 594 } else if(len == max_read)
546 headers_len = len; 595 headers_len = len;
547 else 596 else
584 633
585 /* Compensate for what has already been read */ 634 /* Compensate for what has already been read */
586 len -= connect_data->read_len - headers_len; 635 len -= connect_data->read_len - headers_len;
587 /* I'm assuming that we're doing this to prevent the server from 636 /* I'm assuming that we're doing this to prevent the server from
588 complaining / breaking since we don't read the whole page */ 637 complaining / breaking since we don't read the whole page */
589 while(len--) { 638 while (len--) {
590 /* TODO: deal with EAGAIN (and other errors) better */ 639 /* TODO: deal with EAGAIN (and other errors) better */
591 if (read(connect_data->fd, &tmpc, 1) < 0 && errno != EAGAIN) 640 if (read(connect_data->fd, &tmpc, 1) < 0 && errno != EAGAIN)
592 break; 641 break;
593 } 642 }
594 } 643 }
595 644
596 if (error) 645 if (error)
597 { 646 {
598 gaim_proxy_connect_data_error(connect_data, 647 gaim_proxy_connect_data_disconnect_formatted(connect_data,
599 _("Unable to parse response from HTTP proxy: %s\n"), 648 _("Unable to parse response from HTTP proxy: %s\n"),
600 connect_data->read_buffer); 649 connect_data->read_buffer);
601 return; 650 return;
602 } 651 }
603 else if (status != 200) 652 else if (status != 200)
604 { 653 {
605 gaim_debug_error("proxy", 654 gaim_debug_error("proxy",
606 "Proxy server replied with:\n%s\n", 655 "Proxy server replied with:\n%s\n",
607 connect_data->read_buffer); 656 connect_data->read_buffer);
608 657
609 658 if (status == 407 /* Proxy Auth */)
610 if(status == 407 /* Proxy Auth */) { 659 {
611 gchar *ntlm; 660 gchar *ntlm;
612 if((ntlm = g_strrstr((const gchar *)connect_data->read_buffer, "Proxy-Authenticate: NTLM "))) { /* Check for Type-2 */ 661 ntlm = g_strrstr((const gchar *)connect_data->read_buffer,
662 "Proxy-Authenticate: NTLM ");
663 if (ntlm != NULL)
664 {
665 /* Check for Type-2 */
613 gchar *tmp = ntlm; 666 gchar *tmp = ntlm;
614 guint8 *nonce; 667 guint8 *nonce;
615 gchar *domain = (gchar*)gaim_proxy_info_get_username(connect_data->gpi); 668 gchar *domain = (gchar*)gaim_proxy_info_get_username(connect_data->gpi);
616 gchar *username; 669 gchar *username;
617 gchar *request; 670 gchar *request;
618 gchar *response; 671 gchar *response;
619 username = strchr(domain, '\\'); 672 username = strchr(domain, '\\');
620 if (username == NULL) 673 if (username == NULL)
621 { 674 {
622 gaim_proxy_connect_data_error(connect_data, 675 gaim_proxy_connect_data_disconnect_formatted(connect_data,
623 _("HTTP proxy connection error %d"), status); 676 _("HTTP proxy connection error %d"), status);
624 return; 677 return;
625 } 678 }
626 *username = '\0'; 679 *username = '\0';
627 username++; 680 username++;
642 "Proxy-Connection: Keep-Alive\r\n\r\n", 695 "Proxy-Connection: Keep-Alive\r\n\r\n",
643 connect_data->host, connect_data->port, connect_data->host, 696 connect_data->host, connect_data->port, connect_data->host,
644 connect_data->port, response); 697 connect_data->port, response);
645 g_free(response); 698 g_free(response);
646 699
647 gaim_input_remove(connect_data->inpa);
648 g_free(connect_data->read_buffer); 700 g_free(connect_data->read_buffer);
649 connect_data->read_buffer = NULL; 701 connect_data->read_buffer = NULL;
650 702
651 connect_data->write_buffer = (guchar *)request; 703 connect_data->write_buffer = (guchar *)request;
652 connect_data->write_buf_len = strlen(request); 704 connect_data->write_buf_len = strlen(request);
653 connect_data->written_len = 0; 705 connect_data->written_len = 0;
654
655 connect_data->read_cb = http_canread; 706 connect_data->read_cb = http_canread;
656 707
708 gaim_input_remove(connect_data->inpa);
657 connect_data->inpa = gaim_input_add(connect_data->fd, 709 connect_data->inpa = gaim_input_add(connect_data->fd,
658 GAIM_INPUT_WRITE, proxy_do_write, connect_data); 710 GAIM_INPUT_WRITE, proxy_do_write, connect_data);
659
660 proxy_do_write(connect_data, connect_data->fd, cond); 711 proxy_do_write(connect_data, connect_data->fd, cond);
661 return; 712 return;
662 } else if((ntlm = g_strrstr((const char *)connect_data->read_buffer, "Proxy-Authenticate: NTLM"))) { /* Empty message */ 713 } else if((ntlm = g_strrstr((const char *)connect_data->read_buffer, "Proxy-Authenticate: NTLM"))) { /* Empty message */
663 gchar request[2048]; 714 gchar request[2048];
664 gchar *domain = (gchar*) gaim_proxy_info_get_username(connect_data->gpi); 715 gchar *domain = (gchar*) gaim_proxy_info_get_username(connect_data->gpi);
665 gchar *username; 716 gchar *username;
666 int request_len; 717 int request_len;
667 username = strchr(domain, '\\'); 718 username = strchr(domain, '\\');
668 if (username == NULL) 719 if (username == NULL)
669 { 720 {
670 gaim_proxy_connect_data_error(connect_data, 721 gaim_proxy_connect_data_disconnect_formatted(connect_data,
671 _("HTTP proxy connection error %d"), status); 722 _("HTTP proxy connection error %d"), status);
672 return; 723 return;
673 } 724 }
674 *username = '\0'; 725 *username = '\0';
675 726
703 GAIM_INPUT_WRITE, proxy_do_write, connect_data); 754 GAIM_INPUT_WRITE, proxy_do_write, connect_data);
704 755
705 proxy_do_write(connect_data, connect_data->fd, cond); 756 proxy_do_write(connect_data, connect_data->fd, cond);
706 return; 757 return;
707 } else { 758 } else {
708 gaim_proxy_connect_data_error(connect_data, 759 gaim_proxy_connect_data_disconnect_formatted(connect_data,
709 _("HTTP proxy connection error %d"), status); 760 _("HTTP proxy connection error %d"), status);
710 return; 761 return;
711 } 762 }
712 } 763 }
713 if(status == 403 /* Forbidden */ ) { 764 if (status == 403)
714 gaim_proxy_connect_data_error(connect_data, 765 {
715 _("Access denied: HTTP proxy server forbids port %d tunnelling."), 766 /* Forbidden */
767 gaim_proxy_connect_data_disconnect_formatted(connect_data,
768 _("Access denied: HTTP proxy server forbids port %d tunneling."),
716 connect_data->port); 769 connect_data->port);
717 } else { 770 } else {
718 gaim_proxy_connect_data_error(connect_data, 771 gaim_proxy_connect_data_disconnect_formatted(connect_data,
719 _("HTTP proxy connection error %d"), status); 772 _("HTTP proxy connection error %d"), status);
720 } 773 }
721 } else { 774 } else {
722 gaim_input_remove(connect_data->inpa); 775 gaim_input_remove(connect_data->inpa);
723 connect_data->inpa = 0; 776 connect_data->inpa = 0;
727 gaim_proxy_connect_data_connected(connect_data); 780 gaim_proxy_connect_data_connected(connect_data);
728 return; 781 return;
729 } 782 }
730 } 783 }
731 784
732
733
734 static void 785 static void
735 http_canwrite(gpointer data, gint source, GaimInputCondition cond) 786 http_canwrite(gpointer data, gint source, GaimInputCondition cond)
736 { 787 {
737 char request[8192]; 788 GString *request;
738 int request_len = 0; 789 GaimProxyConnectData *connect_data;
739 GaimProxyConnectData *connect_data = data;
740 socklen_t len; 790 socklen_t len;
741 int error = ETIMEDOUT; 791 int error = ETIMEDOUT;
742 792 int ret;
743 gaim_debug_info("http proxy", "Connected.\n"); 793
794 gaim_debug_info("proxy", "Connected.\n");
795
796 connect_data = data;
744 797
745 if (connect_data->inpa > 0) 798 if (connect_data->inpa > 0)
746 { 799 {
747 gaim_input_remove(connect_data->inpa); 800 gaim_input_remove(connect_data->inpa);
748 connect_data->inpa = 0; 801 connect_data->inpa = 0;
749 } 802 }
750 803
751 len = sizeof(error); 804 len = sizeof(error);
752 805 ret = getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len);
753 if (getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { 806 if ((ret != 0) || (error != 0))
754 gaim_proxy_connect_data_disconnect(connect_data); 807 {
755 try_connect(connect_data); 808 if (ret != 0)
756 return; 809 error = errno;
757 } 810 gaim_proxy_connect_data_disconnect(connect_data, strerror(error));
758 811 return;
759 gaim_debug_info("proxy", "using CONNECT tunnelling for %s:%d\n", 812 }
813
814 gaim_debug_info("proxy", "Using CONNECT tunneling for %s:%d\n",
760 connect_data->host, connect_data->port); 815 connect_data->host, connect_data->port);
761 request_len = g_snprintf(request, sizeof(request), 816
762 "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n", 817 request = g_string_sized_new(4096);
763 connect_data->host, connect_data->port, connect_data->host, connect_data->port); 818 g_string_append_printf(request,
764 819 "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n",
765 if (gaim_proxy_info_get_username(connect_data->gpi) != NULL) { 820 connect_data->host, connect_data->port,
821 connect_data->host, connect_data->port);
822
823 if (gaim_proxy_info_get_username(connect_data->gpi) != NULL)
824 {
766 char *t1, *t2; 825 char *t1, *t2;
826
767 t1 = g_strdup_printf("%s:%s", 827 t1 = g_strdup_printf("%s:%s",
768 gaim_proxy_info_get_username(connect_data->gpi), 828 gaim_proxy_info_get_username(connect_data->gpi),
769 gaim_proxy_info_get_password(connect_data->gpi) ? 829 gaim_proxy_info_get_password(connect_data->gpi) ?
770 gaim_proxy_info_get_password(connect_data->gpi) : ""); 830 gaim_proxy_info_get_password(connect_data->gpi) : "");
771 t2 = gaim_base64_encode((const guchar *)t1, strlen(t1)); 831 t2 = gaim_base64_encode((const guchar *)t1, strlen(t1));
772 g_free(t1); 832 g_free(t1);
773 g_return_if_fail(request_len < sizeof(request)); 833
774 834 g_string_append_printf(request,
775 request_len += g_snprintf(request + request_len,
776 sizeof(request) - request_len,
777 "Proxy-Authorization: Basic %s\r\n" 835 "Proxy-Authorization: Basic %s\r\n"
778 "Proxy-Authorization: NTLM %s\r\n" 836 "Proxy-Authorization: NTLM %s\r\n"
779 "Proxy-Connection: Keep-Alive\r\n", t2, 837 "Proxy-Connection: Keep-Alive\r\n",
780 gaim_ntlm_gen_type1( 838 t2, gaim_ntlm_gen_type1(
781 (gchar*)gaim_proxy_info_get_host(connect_data->gpi),"")); 839 gaim_proxy_info_get_host(connect_data->gpi), ""));
782 g_free(t2); 840 g_free(t2);
783 } 841 }
784 842
785 g_return_if_fail(request_len < sizeof(request)); 843 g_string_append(request, "\r\n");
786 strcpy(request + request_len, "\r\n"); 844
787 request_len += 2; 845 connect_data->write_buf_len = request->len;
788 connect_data->write_buffer = g_memdup(request, request_len); 846 connect_data->write_buffer = (guchar *)g_string_free(request, FALSE);
789 connect_data->write_buf_len = request_len;
790 connect_data->written_len = 0; 847 connect_data->written_len = 0;
791
792 connect_data->read_cb = http_canread; 848 connect_data->read_cb = http_canread;
793 849
794 connect_data->inpa = gaim_input_add(connect_data->fd, GAIM_INPUT_WRITE, proxy_do_write, 850 connect_data->inpa = gaim_input_add(connect_data->fd,
795 connect_data); 851 GAIM_INPUT_WRITE, proxy_do_write, connect_data);
796
797 proxy_do_write(connect_data, connect_data->fd, cond); 852 proxy_do_write(connect_data, connect_data->fd, cond);
798 } 853 }
799 854
800 static int 855 static void
801 proxy_connect_http(GaimProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen) 856 proxy_connect_http(GaimProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen)
802 { 857 {
803 gaim_debug_info("http proxy", 858 gaim_debug_info("proxy",
804 "Connecting to %s:%d via %s:%d using HTTP\n", 859 "Connecting to %s:%d via %s:%d using HTTP\n",
805 (connect_data->host ? connect_data->host : "(null)"), connect_data->port, 860 connect_data->host, connect_data->port,
806 (gaim_proxy_info_get_host(connect_data->gpi) ? gaim_proxy_info_get_host(connect_data->gpi) : "(null)"), 861 (gaim_proxy_info_get_host(connect_data->gpi) ? gaim_proxy_info_get_host(connect_data->gpi) : "(null)"),
807 gaim_proxy_info_get_port(connect_data->gpi)); 862 gaim_proxy_info_get_port(connect_data->gpi));
808 863
809 connect_data->fd = socket(addr->sa_family, SOCK_STREAM, 0); 864 connect_data->fd = socket(addr->sa_family, SOCK_STREAM, 0);
810 if (connect_data->fd < 0) 865 if (connect_data->fd < 0)
811 return -1; 866 {
867 gaim_proxy_connect_data_disconnect_formatted(connect_data,
868 _("Unable to create socket:\n%s"), strerror(errno));
869 return;
870 }
812 871
813 fcntl(connect_data->fd, F_SETFL, O_NONBLOCK); 872 fcntl(connect_data->fd, F_SETFL, O_NONBLOCK);
814 #ifndef _WIN32 873 #ifndef _WIN32
815 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC); 874 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC);
816 #endif 875 #endif
817 876
818 if (connect(connect_data->fd, addr, addrlen) != 0) 877 if (connect(connect_data->fd, addr, addrlen) != 0)
819 { 878 {
820 if ((errno == EINPROGRESS) || (errno == EINTR)) { 879 if ((errno == EINPROGRESS) || (errno == EINTR)) {
821 gaim_debug_info("http proxy", "Connection in progress\n"); 880 gaim_debug_info("proxy", "Connection in progress\n");
822 881
823 if (connect_data->port != 80) { 882 if (connect_data->port != 80)
883 {
824 /* we need to do CONNECT first */ 884 /* we need to do CONNECT first */
825 connect_data->inpa = gaim_input_add(connect_data->fd, GAIM_INPUT_WRITE, 885 connect_data->inpa = gaim_input_add(connect_data->fd,
826 http_canwrite, connect_data); 886 GAIM_INPUT_WRITE, http_canwrite, connect_data);
827 } else { 887 }
888 else
889 {
890 /*
891 * TODO: Uh, is this a good idea? For something like
892 * oscar, for example, we end up sending binary
893 * oscar protocol data to the proxy server, which
894 * has no idea what we're talking about, since
895 * the proxy server is expecting an HTTP request.
896 */
828 gaim_debug_info("proxy", "HTTP proxy connection established\n"); 897 gaim_debug_info("proxy", "HTTP proxy connection established\n");
829 gaim_proxy_connect_data_connected(connect_data); 898 gaim_proxy_connect_data_connected(connect_data);
830 } 899 }
831 } else { 900 }
832 close(connect_data->fd); 901 else
833 connect_data->fd = -1;
834 return -1;
835 }
836 }
837 else {
838 socklen_t len;
839 int error = ETIMEDOUT;
840
841 gaim_debug_info("http proxy", "Connected immediately.\n");
842
843 len = sizeof(error);
844 if (getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len) != 0)
845 { 902 {
846 close(connect_data->fd); 903 gaim_proxy_connect_data_disconnect(connect_data, strerror(errno));
847 connect_data->fd = -1; 904 }
848 return -1; 905 }
849 } 906 else
907 {
908 gaim_debug_info("proxy", "Connected immediately.\n");
909
850 http_canwrite(connect_data, connect_data->fd, GAIM_INPUT_WRITE); 910 http_canwrite(connect_data, connect_data->fd, GAIM_INPUT_WRITE);
851 } 911 }
852 912 }
853 return connect_data->fd;
854 }
855
856 913
857 static void 914 static void
858 s4_canread(gpointer data, gint source, GaimInputCondition cond) 915 s4_canread(gpointer data, gint source, GaimInputCondition cond)
859 { 916 {
860 GaimProxyConnectData *connect_data = data; 917 GaimProxyConnectData *connect_data = data;
882 gaim_proxy_connect_data_connected(connect_data); 939 gaim_proxy_connect_data_connected(connect_data);
883 return; 940 return;
884 } 941 }
885 } 942 }
886 943
887 gaim_proxy_connect_data_disconnect(connect_data); 944 gaim_proxy_connect_data_disconnect(connect_data, strerror(errno));
888 try_connect(connect_data);
889 } 945 }
890 946
891 static void 947 static void
892 s4_canwrite(gpointer data, gint source, GaimInputCondition cond) 948 s4_canwrite(gpointer data, gint source, GaimInputCondition cond)
893 { 949 {
894 unsigned char packet[9]; 950 unsigned char packet[9];
895 struct hostent *hp; 951 struct hostent *hp;
896 GaimProxyConnectData *connect_data = data; 952 GaimProxyConnectData *connect_data = data;
897 socklen_t len; 953 socklen_t len;
898 int error = ETIMEDOUT; 954 int error = ETIMEDOUT;
955 int ret;
899 956
900 gaim_debug_info("socks4 proxy", "Connected.\n"); 957 gaim_debug_info("socks4 proxy", "Connected.\n");
901 958
902 if (connect_data->inpa > 0) 959 if (connect_data->inpa > 0)
903 { 960 {
904 gaim_input_remove(connect_data->inpa); 961 gaim_input_remove(connect_data->inpa);
905 connect_data->inpa = 0; 962 connect_data->inpa = 0;
906 } 963 }
907 964
908 len = sizeof(error); 965 len = sizeof(error);
909 966 ret = getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len);
910 if (getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { 967 if ((ret != 0) || (error != 0))
911 gaim_proxy_connect_data_disconnect(connect_data); 968 {
912 try_connect(connect_data); 969 if (ret != 0)
970 error = errno;
971 gaim_proxy_connect_data_disconnect(connect_data, strerror(error));
913 return; 972 return;
914 } 973 }
915 974
916 /* 975 /*
917 * The socks4 spec doesn't include support for doing host name 976 * The socks4 spec doesn't include support for doing host name
919 * extensions to the protocol. Since we don't know if a 978 * extensions to the protocol. Since we don't know if a
920 * server supports this, it would need to be implemented 979 * server supports this, it would need to be implemented
921 * with an option, or some detection mechanism - in the 980 * with an option, or some detection mechanism - in the
922 * meantime, stick with plain old SOCKS4. 981 * meantime, stick with plain old SOCKS4.
923 */ 982 */
924 /* TODO: This needs to be non-blocking! */ 983 /* TODO: Use gaim_dnsquery_a() */
925 hp = gethostbyname(connect_data->host); 984 hp = gethostbyname(connect_data->host);
926 if (hp == NULL) { 985 if (hp == NULL) {
927 gaim_proxy_connect_data_disconnect(connect_data); 986 gaim_proxy_connect_data_disconnect_formatted(connect_data,
928 try_connect(connect_data); 987 _("Error resolving %s"), connect_data->host);
929 return; 988 return;
930 } 989 }
931 990
932 packet[0] = 4; 991 packet[0] = 4;
933 packet[1] = 1; 992 packet[1] = 1;
947 connect_data->inpa = gaim_input_add(connect_data->fd, GAIM_INPUT_WRITE, proxy_do_write, connect_data); 1006 connect_data->inpa = gaim_input_add(connect_data->fd, GAIM_INPUT_WRITE, proxy_do_write, connect_data);
948 1007
949 proxy_do_write(connect_data, connect_data->fd, cond); 1008 proxy_do_write(connect_data, connect_data->fd, cond);
950 } 1009 }
951 1010
952 static int 1011 static void
953 proxy_connect_socks4(GaimProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen) 1012 proxy_connect_socks4(GaimProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen)
954 { 1013 {
955 gaim_debug_info("socks4 proxy", 1014 gaim_debug_info("proxy",
956 "Connecting to %s:%d via %s:%d using SOCKS4\n", 1015 "Connecting to %s:%d via %s:%d using SOCKS4\n",
957 connect_data->host, connect_data->port, 1016 connect_data->host, connect_data->port,
958 gaim_proxy_info_get_host(connect_data->gpi), 1017 gaim_proxy_info_get_host(connect_data->gpi),
959 gaim_proxy_info_get_port(connect_data->gpi)); 1018 gaim_proxy_info_get_port(connect_data->gpi));
960 1019
961 connect_data->fd = socket(addr->sa_family, SOCK_STREAM, 0); 1020 connect_data->fd = socket(addr->sa_family, SOCK_STREAM, 0);
962 if (connect_data->fd < 0) 1021 if (connect_data->fd < 0)
963 return -1; 1022 {
1023 gaim_proxy_connect_data_disconnect_formatted(connect_data,
1024 _("Unable to create socket:\n%s"), strerror(errno));
1025 return;
1026 }
964 1027
965 fcntl(connect_data->fd, F_SETFL, O_NONBLOCK); 1028 fcntl(connect_data->fd, F_SETFL, O_NONBLOCK);
966 #ifndef _WIN32 1029 #ifndef _WIN32
967 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC); 1030 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC);
968 #endif 1031 #endif
969 1032
970 if (connect(connect_data->fd, addr, addrlen) != 0) 1033 if (connect(connect_data->fd, addr, addrlen) != 0)
971 { 1034 {
972 if ((errno == EINPROGRESS) || (errno == EINTR)) { 1035 if ((errno == EINPROGRESS) || (errno == EINTR))
973 gaim_debug_info("socks4 proxy", "Connection in progress.\n");
974 connect_data->inpa = gaim_input_add(connect_data->fd, GAIM_INPUT_WRITE, s4_canwrite, connect_data);
975 }
976 else {
977 close(connect_data->fd);
978 connect_data->fd = -1;
979 return -1;
980 }
981 } else {
982 socklen_t len;
983 int error = ETIMEDOUT;
984
985 gaim_debug_info("socks4 proxy", "Connected immediately.\n");
986
987 len = sizeof(error);
988
989 if (getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len) != 0)
990 { 1036 {
991 close(connect_data->fd); 1037 gaim_debug_info("proxy", "Connection in progress.\n");
992 connect_data->fd = -1; 1038 connect_data->inpa = gaim_input_add(connect_data->fd,
993 return -1; 1039 GAIM_INPUT_WRITE, s4_canwrite, connect_data);
994 } 1040 }
1041 else
1042 {
1043 gaim_proxy_connect_data_disconnect(connect_data, strerror(errno));
1044 }
1045 }
1046 else
1047 {
1048 gaim_debug_info("proxy", "Connected immediately.\n");
995 1049
996 s4_canwrite(connect_data, connect_data->fd, GAIM_INPUT_WRITE); 1050 s4_canwrite(connect_data, connect_data->fd, GAIM_INPUT_WRITE);
997 } 1051 }
998
999 return connect_data->fd;
1000 } 1052 }
1001 1053
1002 static void 1054 static void
1003 s5_canread_again(gpointer data, gint source, GaimInputCondition cond) 1055 s5_canread_again(gpointer data, gint source, GaimInputCondition cond)
1004 { 1056 {
1016 buf = connect_data->read_buffer; 1068 buf = connect_data->read_buffer;
1017 1069
1018 gaim_debug_info("socks5 proxy", "Able to read again.\n"); 1070 gaim_debug_info("socks5 proxy", "Able to read again.\n");
1019 1071
1020 len = read(connect_data->fd, dest, (connect_data->read_buf_len - connect_data->read_len)); 1072 len = read(connect_data->fd, dest, (connect_data->read_buf_len - connect_data->read_len));
1021 if(len < 0 && errno == EAGAIN) 1073
1022 return; 1074 if (len == 0)
1023 else if(len <= 0) { 1075 {
1024 gaim_debug_warning("socks5 proxy", "or not...\n"); 1076 gaim_proxy_connect_data_disconnect(connect_data,
1025 gaim_proxy_connect_data_disconnect(connect_data); 1077 _("Server closed the connection."));
1026 try_connect(connect_data); 1078 return;
1027 return; 1079 }
1028 } 1080
1081 if (len < 0)
1082 {
1083 if (errno == EAGAIN)
1084 /* No worries */
1085 return;
1086
1087 /* Error! */
1088 gaim_proxy_connect_data_disconnect_formatted(connect_data,
1089 _("Lost connection with server:\n%s"), strerror(errno));
1090 return;
1091 }
1092
1029 connect_data->read_len += len; 1093 connect_data->read_len += len;
1030 1094
1031 if(connect_data->read_len < 4) 1095 if(connect_data->read_len < 4)
1032 return; 1096 return;
1033 1097
1034 if ((buf[0] != 0x05) || (buf[1] != 0x00)) { 1098 if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
1035 if ((buf[0] == 0x05) && (buf[1] < 0x09)) 1099 if ((buf[0] == 0x05) && (buf[1] < 0x09)) {
1036 gaim_debug_error("socks5 proxy", socks5errors[buf[1]]); 1100 gaim_debug_error("socks5 proxy", socks5errors[buf[1]]);
1037 else 1101 gaim_proxy_connect_data_disconnect(connect_data,
1102 socks5errors[buf[1]]);
1103 } else {
1038 gaim_debug_error("socks5 proxy", "Bad data.\n"); 1104 gaim_debug_error("socks5 proxy", "Bad data.\n");
1039 gaim_proxy_connect_data_disconnect(connect_data); 1105 gaim_proxy_connect_data_disconnect(connect_data,
1040 try_connect(connect_data); 1106 _("Received invalid data on connection with server."));
1107 }
1041 return; 1108 return;
1042 } 1109 }
1043 1110
1044 /* Skip past BND.ADDR */ 1111 /* Skip past BND.ADDR */
1045 switch(buf[3]) { 1112 switch(buf[3]) {
1112 1179
1113 gaim_debug_info("socks5 proxy", "Got auth response.\n"); 1180 gaim_debug_info("socks5 proxy", "Got auth response.\n");
1114 1181
1115 len = read(connect_data->fd, connect_data->read_buffer + connect_data->read_len, 1182 len = read(connect_data->fd, connect_data->read_buffer + connect_data->read_len,
1116 connect_data->read_buf_len - connect_data->read_len); 1183 connect_data->read_buf_len - connect_data->read_len);
1117 if(len < 0 && errno == EAGAIN) 1184
1118 return; 1185 if (len == 0)
1119 else if(len <= 0) { 1186 {
1120 gaim_proxy_connect_data_disconnect(connect_data); 1187 gaim_proxy_connect_data_disconnect(connect_data,
1121 try_connect(connect_data); 1188 _("Server closed the connection."));
1122 return; 1189 return;
1123 } 1190 }
1191
1192 if (len < 0)
1193 {
1194 if (errno == EAGAIN)
1195 /* No worries */
1196 return;
1197
1198 /* Error! */
1199 gaim_proxy_connect_data_disconnect_formatted(connect_data,
1200 _("Lost connection with server:\n%s"), strerror(errno));
1201 return;
1202 }
1203
1124 connect_data->read_len += len; 1204 connect_data->read_len += len;
1125
1126 if (connect_data->read_len < 2) 1205 if (connect_data->read_len < 2)
1127 return; 1206 return;
1128 1207
1129 gaim_input_remove(connect_data->inpa); 1208 gaim_input_remove(connect_data->inpa);
1130 connect_data->inpa = 0; 1209 connect_data->inpa = 0;
1131 1210
1132 if ((connect_data->read_buffer[0] != 0x01) || (connect_data->read_buffer[1] != 0x00)) { 1211 if ((connect_data->read_buffer[0] != 0x01) || (connect_data->read_buffer[1] != 0x00)) {
1133 gaim_proxy_connect_data_disconnect(connect_data); 1212 gaim_proxy_connect_data_disconnect(connect_data,
1134 try_connect(connect_data); 1213 _("Received invalid data on connection with server."));
1135 return; 1214 return;
1136 } 1215 }
1137 1216
1138 g_free(connect_data->read_buffer); 1217 g_free(connect_data->read_buffer);
1139 connect_data->read_buffer = NULL; 1218 connect_data->read_buffer = NULL;
1201 } 1280 }
1202 1281
1203 len = read(connect_data->fd, connect_data->read_buffer + connect_data->read_len, 1282 len = read(connect_data->fd, connect_data->read_buffer + connect_data->read_len,
1204 connect_data->read_buf_len - connect_data->read_len); 1283 connect_data->read_buf_len - connect_data->read_len);
1205 1284
1206 if(len < 0 && errno == EAGAIN) 1285 if (len == 0)
1207 return; 1286 {
1208 else if(len <= 0) { 1287 gaim_proxy_connect_data_disconnect(connect_data,
1209 gaim_proxy_connect_data_disconnect(connect_data); 1288 _("Server closed the connection."));
1210 try_connect(connect_data); 1289 return;
1211 return; 1290 }
1212 } 1291
1292 if (len < 0)
1293 {
1294 if (errno == EAGAIN)
1295 /* No worries */
1296 return;
1297
1298 /* Error! */
1299 gaim_proxy_connect_data_disconnect_formatted(connect_data,
1300 _("Lost connection with server:\n%s"), strerror(errno));
1301 return;
1302 }
1303
1213 connect_data->read_len += len; 1304 connect_data->read_len += len;
1214
1215 if (connect_data->read_len < 2) 1305 if (connect_data->read_len < 2)
1216 return; 1306 return;
1217 1307
1218 cmdbuf = connect_data->read_buffer; 1308 cmdbuf = connect_data->read_buffer;
1219 1309
1220 if (*cmdbuf != 0x01) { 1310 if (*cmdbuf != 0x01) {
1221 gaim_proxy_connect_data_disconnect(connect_data); 1311 gaim_proxy_connect_data_disconnect(connect_data,
1222 try_connect(connect_data); 1312 _("Received invalid data on connection with server."));
1223 return; 1313 return;
1224 } 1314 }
1225 cmdbuf++; 1315 cmdbuf++;
1226 1316
1227 navas = *cmdbuf; 1317 navas = *cmdbuf;
1247 } else { 1337 } else {
1248 /* Failure */ 1338 /* Failure */
1249 gaim_debug_warning("proxy", 1339 gaim_debug_warning("proxy",
1250 "socks5 CHAP authentication " 1340 "socks5 CHAP authentication "
1251 "failed. Disconnecting..."); 1341 "failed. Disconnecting...");
1252 gaim_proxy_connect_data_disconnect(connect_data); 1342 gaim_proxy_connect_data_disconnect(connect_data,
1253 try_connect(connect_data); 1343 _("Authentication failed"));
1254 return; 1344 return;
1255 } 1345 }
1256 break; 1346 break;
1257 case 0x03: 1347 case 0x03:
1258 /* Server wants our credentials */ 1348 /* Server wants our credentials */
1288 "Server tried to select an " 1378 "Server tried to select an "
1289 "algorithm that we did not advertise " 1379 "algorithm that we did not advertise "
1290 "as supporting. This is a violation " 1380 "as supporting. This is a violation "
1291 "of the socks5 CHAP specification. " 1381 "of the socks5 CHAP specification. "
1292 "Disconnecting..."); 1382 "Disconnecting...");
1293 gaim_proxy_connect_data_disconnect(connect_data); 1383 gaim_proxy_connect_data_disconnect(connect_data,
1294 try_connect(connect_data); 1384 _("Received invalid data on connection with server."));
1295 return; 1385 return;
1296 } 1386 }
1297 break; 1387 break;
1298 } 1388 }
1299 cmdbuf = buf + cmdbuf[1]; 1389 cmdbuf = buf + cmdbuf[1];
1317 1407
1318 gaim_debug_info("socks5 proxy", "Able to read.\n"); 1408 gaim_debug_info("socks5 proxy", "Able to read.\n");
1319 1409
1320 len = read(connect_data->fd, connect_data->read_buffer + connect_data->read_len, 1410 len = read(connect_data->fd, connect_data->read_buffer + connect_data->read_len,
1321 connect_data->read_buf_len - connect_data->read_len); 1411 connect_data->read_buf_len - connect_data->read_len);
1322 if(len < 0 && errno == EAGAIN) 1412
1323 return; 1413 if (len == 0)
1324 else if(len <= 0) { 1414 {
1325 gaim_proxy_connect_data_disconnect(connect_data); 1415 gaim_proxy_connect_data_disconnect(connect_data,
1326 try_connect(connect_data); 1416 _("Server closed the connection."));
1327 return; 1417 return;
1328 } 1418 }
1419
1420 if (len < 0)
1421 {
1422 if (errno == EAGAIN)
1423 /* No worries */
1424 return;
1425
1426 /* Error! */
1427 gaim_proxy_connect_data_disconnect_formatted(connect_data,
1428 _("Lost connection with server:\n%s"), strerror(errno));
1429 return;
1430 }
1431
1329 connect_data->read_len += len; 1432 connect_data->read_len += len;
1330
1331 if (connect_data->read_len < 2) 1433 if (connect_data->read_len < 2)
1332 return; 1434 return;
1333 1435
1334 gaim_input_remove(connect_data->inpa); 1436 gaim_input_remove(connect_data->inpa);
1335 connect_data->inpa = 0; 1437 connect_data->inpa = 0;
1336 1438
1337 if ((connect_data->read_buffer[0] != 0x05) || (connect_data->read_buffer[1] == 0xff)) { 1439 if ((connect_data->read_buffer[0] != 0x05) || (connect_data->read_buffer[1] == 0xff)) {
1338 gaim_proxy_connect_data_disconnect(connect_data); 1440 gaim_proxy_connect_data_disconnect(connect_data,
1339 try_connect(connect_data); 1441 _("Received invalid data on connection with server."));
1340 return; 1442 return;
1341 } 1443 }
1342 1444
1343 if (connect_data->read_buffer[1] == 0x02) { 1445 if (connect_data->read_buffer[1] == 0x02) {
1344 gsize i, j; 1446 gsize i, j;
1416 unsigned char buf[5]; 1518 unsigned char buf[5];
1417 int i; 1519 int i;
1418 GaimProxyConnectData *connect_data = data; 1520 GaimProxyConnectData *connect_data = data;
1419 socklen_t len; 1521 socklen_t len;
1420 int error = ETIMEDOUT; 1522 int error = ETIMEDOUT;
1523 int ret;
1421 1524
1422 gaim_debug_info("socks5 proxy", "Connected.\n"); 1525 gaim_debug_info("socks5 proxy", "Connected.\n");
1423 1526
1424 if (connect_data->inpa > 0) 1527 if (connect_data->inpa > 0)
1425 { 1528 {
1426 gaim_input_remove(connect_data->inpa); 1529 gaim_input_remove(connect_data->inpa);
1427 connect_data->inpa = 0; 1530 connect_data->inpa = 0;
1428 } 1531 }
1429 1532
1430 len = sizeof(error); 1533 len = sizeof(error);
1431 if (getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { 1534 ret = getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len);
1432 gaim_proxy_connect_data_disconnect(connect_data); 1535 if ((ret != 0) || (error != 0))
1433 try_connect(connect_data); 1536 {
1537 if (ret != 0)
1538 error = errno;
1539 gaim_proxy_connect_data_disconnect(connect_data, strerror(error));
1434 return; 1540 return;
1435 } 1541 }
1436 1542
1437 i = 0; 1543 i = 0;
1438 buf[0] = 0x05; /* SOCKS version 5 */ 1544 buf[0] = 0x05; /* SOCKS version 5 */
1458 1564
1459 connect_data->inpa = gaim_input_add(connect_data->fd, GAIM_INPUT_WRITE, proxy_do_write, connect_data); 1565 connect_data->inpa = gaim_input_add(connect_data->fd, GAIM_INPUT_WRITE, proxy_do_write, connect_data);
1460 proxy_do_write(connect_data, connect_data->fd, GAIM_INPUT_WRITE); 1566 proxy_do_write(connect_data, connect_data->fd, GAIM_INPUT_WRITE);
1461 } 1567 }
1462 1568
1463 static int 1569 static void
1464 proxy_connect_socks5(GaimProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen) 1570 proxy_connect_socks5(GaimProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen)
1465 { 1571 {
1466 gaim_debug_info("socks5 proxy", 1572 gaim_debug_info("proxy",
1467 "Connecting to %s:%d via %s:%d using SOCKS5\n", 1573 "Connecting to %s:%d via %s:%d using SOCKS5\n",
1468 connect_data->host, connect_data->port, 1574 connect_data->host, connect_data->port,
1469 gaim_proxy_info_get_host(connect_data->gpi), 1575 gaim_proxy_info_get_host(connect_data->gpi),
1470 gaim_proxy_info_get_port(connect_data->gpi)); 1576 gaim_proxy_info_get_port(connect_data->gpi));
1471 1577
1472 connect_data->fd = socket(addr->sa_family, SOCK_STREAM, 0); 1578 connect_data->fd = socket(addr->sa_family, SOCK_STREAM, 0);
1473 if (connect_data->fd < 0) 1579 if (connect_data->fd < 0)
1474 return -1; 1580 {
1581 gaim_proxy_connect_data_disconnect_formatted(connect_data,
1582 _("Unable to create socket:\n%s"), strerror(errno));
1583 return;
1584 }
1475 1585
1476 fcntl(connect_data->fd, F_SETFL, O_NONBLOCK); 1586 fcntl(connect_data->fd, F_SETFL, O_NONBLOCK);
1477 #ifndef _WIN32 1587 #ifndef _WIN32
1478 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC); 1588 fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC);
1479 #endif 1589 #endif
1480 1590
1481 if (connect(connect_data->fd, addr, addrlen) != 0) 1591 if (connect(connect_data->fd, addr, addrlen) != 0)
1482 { 1592 {
1483 if ((errno == EINPROGRESS) || (errno == EINTR)) { 1593 if ((errno == EINPROGRESS) || (errno == EINTR))
1594 {
1484 gaim_debug_info("socks5 proxy", "Connection in progress\n"); 1595 gaim_debug_info("socks5 proxy", "Connection in progress\n");
1485 connect_data->inpa = gaim_input_add(connect_data->fd, GAIM_INPUT_WRITE, s5_canwrite, connect_data); 1596 connect_data->inpa = gaim_input_add(connect_data->fd,
1486 } 1597 GAIM_INPUT_WRITE, s5_canwrite, connect_data);
1487 else { 1598 }
1488 close(connect_data->fd); 1599 else
1489 connect_data->fd = -1;
1490 return -1;
1491 }
1492 }
1493 else {
1494 socklen_t len;
1495 int error = ETIMEDOUT;
1496
1497 gaim_debug_info("socks5 proxy", "Connected immediately.\n");
1498
1499 len = sizeof(error);
1500
1501 if (getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len) != 0)
1502 { 1600 {
1503 close(connect_data->fd); 1601 gaim_proxy_connect_data_disconnect(connect_data, strerror(errno));
1504 connect_data->fd = -1; 1602 }
1505 return -1; 1603 }
1506 } 1604 else
1605 {
1606 gaim_debug_info("proxy", "Connected immediately.\n");
1507 1607
1508 s5_canwrite(connect_data, connect_data->fd, GAIM_INPUT_WRITE); 1608 s5_canwrite(connect_data, connect_data->fd, GAIM_INPUT_WRITE);
1509 } 1609 }
1510
1511 return connect_data->fd;
1512 } 1610 }
1513 1611
1514 /** 1612 /**
1515 * This function iterates through a list of IP addresses and attempts 1613 * This function attempts to connect to the next IP address in the list
1614 * of IP addresses returned to us by gaim_dnsquery_a() and attemps
1516 * to connect to each one. This is called after the hostname is 1615 * to connect to each one. This is called after the hostname is
1517 * resolved, and if a connection attempt fails. 1616 * resolved, and each time a connection attempt fails (assuming there
1617 * is another IP address to try).
1518 */ 1618 */
1519 static void try_connect(GaimProxyConnectData *connect_data) 1619 static void try_connect(GaimProxyConnectData *connect_data)
1520 { 1620 {
1521 size_t addrlen; 1621 size_t addrlen;
1522 struct sockaddr *addr; 1622 struct sockaddr *addr;
1523 int ret = -1; 1623
1524 1624 addrlen = GPOINTER_TO_INT(connect_data->hosts->data);
1525 if (connect_data->hosts == NULL) 1625 connect_data->hosts = g_slist_remove(connect_data->hosts, connect_data->hosts->data);
1526 { 1626 addr = connect_data->hosts->data;
1527 gaim_proxy_connect_data_error(connect_data, _("Could not resolve host name")); 1627 connect_data->hosts = g_slist_remove(connect_data->hosts, connect_data->hosts->data);
1528 return; 1628
1529 } 1629 gaim_debug_info("proxy", "Attempting connection to %s\n", "TODO");
1530 1630
1531 while (connect_data->hosts) 1631 switch (gaim_proxy_info_get_type(connect_data->gpi)) {
1532 { 1632 case GAIM_PROXY_NONE:
1533 addrlen = GPOINTER_TO_INT(connect_data->hosts->data); 1633 proxy_connect_none(connect_data, addr, addrlen);
1534 connect_data->hosts = g_slist_remove(connect_data->hosts, connect_data->hosts->data);
1535 addr = connect_data->hosts->data;
1536 connect_data->hosts = g_slist_remove(connect_data->hosts, connect_data->hosts->data);
1537
1538 switch (gaim_proxy_info_get_type(connect_data->gpi)) {
1539 case GAIM_PROXY_NONE:
1540 ret = proxy_connect_none(connect_data, addr, addrlen);
1541 break;
1542
1543 case GAIM_PROXY_HTTP:
1544 ret = proxy_connect_http(connect_data, addr, addrlen);
1545 break;
1546
1547 case GAIM_PROXY_SOCKS4:
1548 ret = proxy_connect_socks4(connect_data, addr, addrlen);
1549 break;
1550
1551 case GAIM_PROXY_SOCKS5:
1552 ret = proxy_connect_socks5(connect_data, addr, addrlen);
1553 break;
1554
1555 case GAIM_PROXY_USE_ENVVAR:
1556 ret = proxy_connect_http(connect_data, addr, addrlen);
1557 break;
1558
1559 default:
1560 break;
1561 }
1562
1563 g_free(addr);
1564
1565 if (ret >= 0)
1566 break; 1634 break;
1567 } 1635
1568 1636 case GAIM_PROXY_HTTP:
1569 if (ret < 0) { 1637 proxy_connect_http(connect_data, addr, addrlen);
1570 gaim_proxy_connect_data_error(connect_data, _("Unable to establish a connection")); 1638 break;
1571 } 1639
1640 case GAIM_PROXY_SOCKS4:
1641 proxy_connect_socks4(connect_data, addr, addrlen);
1642 break;
1643
1644 case GAIM_PROXY_SOCKS5:
1645 proxy_connect_socks5(connect_data, addr, addrlen);
1646 break;
1647
1648 case GAIM_PROXY_USE_ENVVAR:
1649 proxy_connect_http(connect_data, addr, addrlen);
1650 break;
1651
1652 default:
1653 break;
1654 }
1655
1656 g_free(addr);
1572 } 1657 }
1573 1658
1574 static void 1659 static void
1575 connection_host_resolved(GSList *hosts, gpointer data, 1660 connection_host_resolved(GSList *hosts, gpointer data,
1576 const char *error_message) 1661 const char *error_message)
1580 connect_data = data; 1665 connect_data = data;
1581 connect_data->query_data = NULL; 1666 connect_data->query_data = NULL;
1582 1667
1583 if (error_message != NULL) 1668 if (error_message != NULL)
1584 { 1669 {
1585 gaim_proxy_connect_data_error(connect_data, error_message); 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"));
1586 return; 1677 return;
1587 } 1678 }
1588 1679
1589 connect_data->hosts = hosts; 1680 connect_data->hosts = hosts;
1590 1681
1754 } 1845 }
1755 1846
1756 void 1847 void
1757 gaim_proxy_connect_cancel(GaimProxyConnectData *connect_data) 1848 gaim_proxy_connect_cancel(GaimProxyConnectData *connect_data)
1758 { 1849 {
1850 gaim_proxy_connect_data_disconnect(connect_data, NULL);
1759 gaim_proxy_connect_data_destroy(connect_data); 1851 gaim_proxy_connect_data_destroy(connect_data);
1760 } 1852 }
1761 1853
1762 static void 1854 static void
1763 proxy_pref_cb(const char *name, GaimPrefType type, 1855 proxy_pref_cb(const char *name, GaimPrefType type,
1833 1925
1834 void 1926 void
1835 gaim_proxy_uninit(void) 1927 gaim_proxy_uninit(void)
1836 { 1928 {
1837 while (connect_datas != NULL) 1929 while (connect_datas != NULL)
1930 {
1931 gaim_proxy_connect_data_disconnect(connect_datas->data, NULL);
1838 gaim_proxy_connect_data_destroy(connect_datas->data); 1932 gaim_proxy_connect_data_destroy(connect_datas->data);
1839 } 1933 }
1934 }