Mercurial > pidgin.yaz
changeset 14195:902c3aa4950a
[gaim-migrate @ 16867]
Revamped the QQ proxy code.
Fixed an infinite loop that was occurring if we hit qq_input_pending() with an unexpected value of cond.
Rewrote part of qq_proxy.c so that we use Gaim's non-blocking dns lookups.
Quieted some warnings created by new code in proxy.c and passed appropriate error messages to _qq_got_login().
Added some extra error handling to qq_proxy_write().
I was beginning to do major clean-up on this this code when I realized that once that clean-up is done, I will have duplicated a very large amount of code from proxy.c. Therefore, I am submitting this working code now and will later submit a patch to gaim-devel that will add support for UDP proxying in proxy.c, thus eliminating the need for such code in the individual prpls.
committer: Tailor Script <tailor@pidgin.im>
author | Mark Huetsch <markhuetsch> |
---|---|
date | Sat, 19 Aug 2006 02:32:55 +0000 |
parents | ebe83aee29d2 |
children | ec2cd563da47 |
files | libgaim/protocols/qq/qq.c libgaim/protocols/qq/qq.h libgaim/protocols/qq/qq_proxy.c libgaim/protocols/qq/qq_proxy.h libgaim/protocols/qq/recv_core.c libgaim/protocols/qq/send_core.c libgaim/protocols/qq/udp_proxy_s5.c |
diffstat | 7 files changed, 214 insertions(+), 99 deletions(-) [+] |
line wrap: on
line diff
--- a/libgaim/protocols/qq/qq.c Sat Aug 19 02:26:57 2006 +0000 +++ b/libgaim/protocols/qq/qq.c Sat Aug 19 02:32:55 2006 +0000 @@ -64,25 +64,26 @@ #define QQ_UDP_PORT "8000" const gchar *udp_server_list[] = { - "sz.tencent.com", /* 61.144.238.145 */ - "sz2.tencent.com", /* 61.144.238.146 */ - "sz3.tencent.com", /* 202.104.129.251 */ - "sz4.tencent.com", /* 202.104.129.254 */ - "sz5.tencent.com", /* 61.141.194.203 */ - "sz6.tencent.com", /* 202.104.129.252 */ - "sz7.tencent.com", /* 202.104.129.253 */ - "202.96.170.64", - "64.144.238.155", - "202.104.129.254" + "sz.tencent.com", + "sz2.tencent.com", + "sz3.tencent.com", + "sz4.tencent.com", + "sz5.tencent.com", + "sz6.tencent.com", + "sz7.tencent.com", + "sz8.tencent.com", + "sz9.tencent.com" }; const gint udp_server_amount = (sizeof(udp_server_list) / sizeof(udp_server_list[0])); const gchar *tcp_server_list[] = { - "tcpconn.tencent.com", /* 218.17.209.23 */ - "tcpconn2.tencent.com", /* 218.18.95.153 */ - "tcpconn3.tencent.com", /* 218.17.209.23 */ - "tcpconn4.tencent.com" /* 218.18.95.153 */ + "tcpconn.tencent.com", + "tcpconn2.tencent.com", + "tcpconn3.tencent.com", + "tcpconn4.tencent.com", + "tcpconn5.tencent.com", + "tcpconn6.tencent.com" }; const gint tcp_server_amount = (sizeof(tcp_server_list) / sizeof(tcp_server_list[0])); @@ -102,6 +103,7 @@ gc->flags |= GAIM_CONNECTION_HTML | GAIM_CONNECTION_NO_BGCOLOR | GAIM_CONNECTION_AUTO_RESP; qd = g_new0(qq_data, 1); + qd->gc = gc; gc->proto_data = qd; qq_server = gaim_account_get_string(account, "server", NULL); @@ -122,13 +124,14 @@ if (qq_server == NULL || strlen(qq_server) == 0) qq_server = use_tcp ? - tcp_server_list[random() % tcp_server_amount] : udp_server_list[random() % udp_server_amount]; + tcp_server_list[random() % tcp_server_amount] : + udp_server_list[random() % udp_server_amount]; if (qq_port == NULL || strtol(qq_port, NULL, 10) == 0) qq_port = use_tcp ? QQ_TCP_QUERY_PORT : QQ_UDP_PORT; gaim_connection_update_progress(gc, _("Connecting"), 0, QQ_CONNECT_STEPS); - + if (qq_connect(account, qq_server, strtol(qq_port, NULL, 10), use_tcp, FALSE) < 0) gaim_connection_error(gc, _("Unable to connect.")); }
--- a/libgaim/protocols/qq/qq.h Sat Aug 19 02:26:57 2006 +0000 +++ b/libgaim/protocols/qq/qq.h Sat Aug 19 02:32:55 2006 +0000 @@ -67,7 +67,9 @@ gboolean logged_in; /* used by qq-add_buddy */ gboolean use_tcp; /* network in tcp or udp */ - GaimProxyType proxy_type; /* proxy type */ + GaimProxyType proxy_type; + GaimConnection *gc; + GaimXfer *xfer; /* file transfer handler */ struct sockaddr_in dest_sin;
--- a/libgaim/protocols/qq/qq_proxy.c Sat Aug 19 02:26:57 2006 +0000 +++ b/libgaim/protocols/qq/qq_proxy.c Sat Aug 19 02:32:55 2006 +0000 @@ -43,11 +43,11 @@ #include "udp_proxy_s5.h" #include "utils.h" -/* These functions are used only in development phase - * +/* These functions are used only in development phase */ +/* static void _qq_show_socket(gchar *desc, gint fd) { struct sockaddr_in sin; - gint len = sizeof(sin); + socklen_t len = sizeof(sin); getsockname(fd, (struct sockaddr *)&sin, &len); gaim_debug(GAIM_DEBUG_INFO, desc, "%s:%d\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); @@ -88,22 +88,24 @@ return g_memdup(pwkey_tmp, QQ_KEY_LENGTH); } -gint _qq_fill_host(struct sockaddr_in *addr, const gchar *host, guint16 port) +static gboolean _qq_fill_host(GSList *hosts, struct sockaddr_in *addr, gint *addr_size) { - if (!inet_aton(host, &(addr->sin_addr))) { - struct hostent *hp; - if (!(hp = gethostbyname(host))) { - return -1; - } - memset(addr, 0, sizeof(struct sockaddr_in)); - memcpy(&(addr->sin_addr.s_addr), hp->h_addr, hp->h_length); - addr->sin_family = hp->h_addrtype; - } else { - addr->sin_family = AF_INET; + if (!hosts || !hosts->data) + return FALSE; + + *addr_size = GPOINTER_TO_INT(hosts->data); + + hosts = g_slist_remove(hosts, hosts->data); + memcpy(addr, hosts->data, *addr_size); + g_free(hosts->data); + hosts = g_slist_remove(hosts, hosts->data); + while(hosts) { + hosts = g_slist_remove(hosts, hosts->data); + g_free(hosts->data); + hosts = g_slist_remove(hosts, hosts->data); } - addr->sin_port = htons(port); - return 0; + return TRUE; } /* set up any finalizing start-up stuff */ @@ -136,12 +138,16 @@ g_return_if_fail(gc != NULL && gc->proto_data != NULL); if (source < 0) { /* socket returns -1 */ - gaim_connection_error(gc, _("Unable to connect.")); + gaim_connection_error(gc, error_message); return; } qd = (qq_data *) gc->proto_data; + /* + _qq_show_socket("Got login socket", source); + */ + /* QQ use random seq, to minimize duplicated packets */ srandom(time(NULL)); qd->send_seq = random() & 0x0000ffff; @@ -201,6 +207,54 @@ qq_buddies_list_free(gc->account, qd); } +static void no_one_calls(gpointer data, gint source, GaimInputCondition cond) +{ + struct PHB *phb = data; + socklen_t len; + int error=0, ret; + + gaim_debug_info("proxy", "Connected.\n"); + + len = sizeof(error); + + /* + * getsockopt after a non-blocking connect returns -1 if something is + * really messed up (bad descriptor, usually). Otherwise, it returns 0 and + * error holds what connect would have returned if it blocked until now. + * Thus, error == 0 is success, error == EINPROGRESS means "try again", + * and anything else is a real error. + * + * (error == EINPROGRESS can happen after a select because the kernel can + * be overly optimistic sometimes. select is just a hint that you might be + * able to do something.) + */ + ret = getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len); + if (ret == 0 && error == EINPROGRESS) + return; /* we'll be called again later */ + if (ret < 0 || error != 0) { + if(ret!=0) + error = errno; + close(source); + gaim_input_remove(phb->inpa); + + gaim_debug_error("proxy", "getsockopt SO_ERROR check: %s\n", strerror(error)); + + phb->func(phb->data, -1, _("Unable to connect")); + return; + } + + gaim_input_remove(phb->inpa); + + if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) { + + phb->func(phb->data, source, NULL); + } + + g_free(phb->host); + g_free(phb); +} + +/* returns -1 if fails, otherwise returns the file handle */ static gint _qq_proxy_none(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen) { gint fd = -1; @@ -209,7 +263,8 @@ fd = socket(PF_INET, SOCK_DGRAM, 0); if (fd < 0) { - gaim_debug(GAIM_DEBUG_ERROR, "QQ Redirect", "Unable to create socket: %s\n", strerror(errno)); + gaim_debug(GAIM_DEBUG_ERROR, "QQ Redirect", + "Unable to create socket: %s\n", strerror(errno)); return -1; } @@ -238,8 +293,9 @@ */ if ((errno == EINPROGRESS) || (errno == EINTR)) { gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Connect in asynchronous mode.\n"); + phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, no_one_calls, phb); } else { - gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Faiil connection: %d\n", strerror(errno)); + gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Connection failed: %d\n", strerror(errno)); close(fd); return -1; } /* if errno */ @@ -252,20 +308,80 @@ return fd; } -/* returns the socket handler, or -1 if there is any error */ +static void _qq_proxy_resolved(GSList *hosts, gpointer data, const char *error_message) +{ + struct PHB *phb = (struct PHB *) data; + struct sockaddr_in addr; + gint addr_size, ret = -1; + + if(_qq_fill_host(hosts, &addr, &addr_size)) + ret = qq_proxy_socks5(phb, (struct sockaddr *) &addr, addr_size); + + if (ret < 0) { + phb->func(phb->data, -1, _("Unable to connect")); + g_free(phb->host); + g_free(phb); + } +} + +static void _qq_server_resolved(GSList *hosts, gpointer data, const char *error_message) +{ + struct PHB *phb = (struct PHB *) data; + GaimConnection *gc = (GaimConnection *) phb->data; + qq_data *qd = (qq_data *) gc->proto_data; + struct sockaddr_in addr; + gint addr_size, ret = -1; + + if(_qq_fill_host(hosts, &addr, &addr_size)) { + switch (gaim_proxy_info_get_type(phb->gpi)) { + case GAIM_PROXY_NONE: + ret = _qq_proxy_none(phb, (struct sockaddr *) &addr, addr_size); + break; + case GAIM_PROXY_SOCKS5: + ret = 0; + if (gaim_proxy_info_get_host(phb->gpi) == NULL || + gaim_proxy_info_get_port(phb->gpi) == 0) { + gaim_debug(GAIM_DEBUG_ERROR, "QQ", + "Use of socks5 proxy selected but host or port info doesn't exist.\n"); + ret = -1; + } else { + /* as the destination is always QQ server during the session, + * we can set dest_sin here, instead of _qq_s5_canread_again */ + memcpy(&qd->dest_sin, &addr, addr_size); + if (gaim_dnsquery_a(gaim_proxy_info_get_host(phb->gpi), + gaim_proxy_info_get_port(phb->gpi), + _qq_proxy_resolved, phb) == NULL) + ret = -1; + } + break; + default: + gaim_debug(GAIM_DEBUG_WARNING, "QQ", + "Proxy type %i is unsupported, not using a proxy.\n", + gaim_proxy_info_get_type(phb->gpi)); + ret = _qq_proxy_none(phb, (struct sockaddr *) &addr, addr_size); + } + } + + if (ret < 0) { + phb->func(gc, -1, _("Unable to connect")); + g_free(phb->host); + g_free(phb); + } +} + +/* returns -1 if dns lookup fails, otherwise returns 0 */ static gint _qq_udp_proxy_connect(GaimAccount *account, - const gchar *server, - guint16 port, void callback(gpointer, gint, const gchar *error_message), GaimConnection *gc) + const gchar *server, guint16 port, + void callback(gpointer, gint, const gchar *error_message), + GaimConnection *gc) { - struct sockaddr_in sin; + GaimProxyInfo *info; struct PHB *phb; - GaimProxyInfo *info; - qq_data *qd; + qq_data *qd = (qq_data *) gc->proto_data; - g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); - qd = (qq_data *) gc->proto_data; + g_return_val_if_fail(gc != NULL && qd != NULL, -1); - info = gaim_account_get_proxy_info(account); + info = gaim_proxy_get_setup(account); phb = g_new0(struct PHB, 1); phb->host = g_strdup(server); @@ -274,41 +390,24 @@ phb->gpi = info; phb->func = callback; phb->data = gc; - - if (_qq_fill_host(&sin, server, port) < 0) { - gaim_debug(GAIM_DEBUG_ERROR, "QQ", - "gethostbyname(\"%s\", %d) failed: %s\n", server, port, hstrerror(h_errno)); - return -1; - } + qd->proxy_type = gaim_proxy_info_get_type(phb->gpi); - if (info == NULL) { - qd->proxy_type = GAIM_PROXY_NONE; - return _qq_proxy_none(phb, (struct sockaddr *) &sin, sizeof(sin)); - } - - qd->proxy_type = info->type; - gaim_debug(GAIM_DEBUG_INFO, "QQ", "Choosing proxy type %d\n", info->type); + gaim_debug(GAIM_DEBUG_INFO, "QQ", "Choosing proxy type %d\n", + gaim_proxy_info_get_type(phb->gpi)); - switch (info->type) { - case GAIM_PROXY_NONE: - return _qq_proxy_none(phb, (struct sockaddr *) &sin, sizeof(sin)); - case GAIM_PROXY_SOCKS5: - /* as the destination is always QQ server during the session, - * we can set dest_sin here, instead of _qq_s5_canread_again */ - _qq_fill_host(&qd->dest_sin, phb->host, phb->port); - _qq_fill_host(&sin, phb->gpi->host, phb->gpi->port); - return qq_proxy_socks5(phb, (struct sockaddr *) &sin, sizeof(sin)); - default: - return _qq_proxy_none(phb, (struct sockaddr *) &sin, sizeof(sin)); + if (gaim_dnsquery_a(server, port, _qq_server_resolved, phb) == NULL) { + phb->func(gc, -1, _("Unable to connect")); + g_free(phb->host); + g_free(phb); + return -1; + } else { + return 0; } - - return -1; } /* QQ connection via UDP/TCP. - * I use GAIM proxy function to provide TCP proxy support, - * and qq_udp_proxy.c to add UDP proxy support (thanks henry) - * return the socket handle, -1 means fail */ + * I use Gaim proxy function to provide TCP proxy support, + * and qq_udp_proxy.c to add UDP proxy support (thanks henry) */ static gint _proxy_connect_full (GaimAccount *account, const gchar *host, guint16 port, GaimProxyConnectFunction func, gpointer data, gboolean use_tcp) { @@ -320,18 +419,16 @@ qd->server_ip = g_strdup(host); qd->server_port = port; - if (use_tcp) - /* TCP mode */ + if(use_tcp) return (gaim_proxy_connect(account, host, port, func, data) == NULL); else - /* UDP mode */ return _qq_udp_proxy_connect(account, host, port, func, data); } /* establish a generic QQ connection - * TCP/UDP, and direct/redirected - * return the socket handler, or -1 if there is any error */ -gint qq_connect(GaimAccount *account, const gchar *host, guint16 port, gboolean use_tcp, gboolean is_redirect) + * TCP/UDP, and direct/redirected */ +gint qq_connect(GaimAccount *account, const gchar *host, guint16 port, + gboolean use_tcp, gboolean is_redirect) { GaimConnection *gc; @@ -385,10 +482,19 @@ g_memmove(buf + 4, &(qd->dest_sin.sin_addr.s_addr), 4); g_memmove(buf + 8, &(qd->dest_sin.sin_port), 2); g_memmove(buf + 10, data, len); + errno = 0; ret = send(qd->fd, buf, len + 10, 0); } else { + errno = 0; ret = send(qd->fd, data, len, 0); } + if (ret == -1) { + gaim_connection_error(qd->gc, _("Socket send error")); + return ret; + } else if (errno == ECONNREFUSED) { + gaim_connection_error(qd->gc, _("Connection refused")); + return ret; + } return ret; }
--- a/libgaim/protocols/qq/qq_proxy.h Sat Aug 19 02:26:57 2006 +0000 +++ b/libgaim/protocols/qq/qq_proxy.h Sat Aug 19 02:32:55 2006 +0000 @@ -25,7 +25,9 @@ #define _QQ_PROXY_H #include <glib.h> +#include "dnsquery.h" #include "proxy.h" + #include "qq.h" #define QQ_CONNECT_STEPS 2 /* steps in connection */ @@ -48,8 +50,6 @@ gint qq_connect(GaimAccount *account, const gchar *host, guint16 port, gboolean use_tcp, gboolean is_redirect); void qq_disconnect(GaimConnection *gc); -gint _qq_fill_host(struct sockaddr_in *addr, const gchar *host, guint16 port); - void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len); #endif
--- a/libgaim/protocols/qq/recv_core.c Sat Aug 19 02:26:57 2006 +0000 +++ b/libgaim/protocols/qq/recv_core.c Sat Aug 19 02:32:55 2006 +0000 @@ -296,7 +296,10 @@ gint len; gc = (GaimConnection *) data; - g_return_if_fail(gc != NULL && gc->proto_data != NULL && cond == GAIM_INPUT_READ); + if(gc == NULL || gc->proto_data == NULL || cond != GAIM_INPUT_READ) { + gaim_connection_error(gc, _("Socket error")); + return; + } qd = (qq_data *) gc->proto_data; buf = g_newa(guint8, MAX_PACKET_SIZE);
--- a/libgaim/protocols/qq/send_core.c Sat Aug 19 02:26:57 2006 +0000 +++ b/libgaim/protocols/qq/send_core.c Sat Aug 19 02:32:55 2006 +0000 @@ -73,7 +73,7 @@ /* for those need ack and resend no ack feed back from server * return number of bytes written to the socket, * return -1 if there is any error */ -gint _qq_send_packet(GaimConnection * gc, guint8 *buf, gint len, guint16 cmd) +gint _qq_send_packet(GaimConnection *gc, guint8 *buf, gint len, guint16 cmd) { qq_data *qd; qq_sendpacket *p;
--- a/libgaim/protocols/qq/udp_proxy_s5.c Sat Aug 19 02:26:57 2006 +0000 +++ b/libgaim/protocols/qq/udp_proxy_s5.c Sat Aug 19 02:32:55 2006 +0000 @@ -25,15 +25,13 @@ #include "udp_proxy_s5.h" -extern gint /* defined in qq_proxy.c */ - _qq_fill_host(struct sockaddr_in *addr, const gchar * host, guint16 port); - static void _qq_s5_canread_again(gpointer data, gint source, GaimInputCondition cond) { unsigned char buf[512]; struct PHB *phb = data; struct sockaddr_in sin; int len, error; + socklen_t errlen; gaim_input_remove(phb->inpa); gaim_debug(GAIM_DEBUG_INFO, "socks5 proxy", "Able to read again.\n"); @@ -61,7 +59,7 @@ if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) { - phb->func(phb->data, -1, NULL); + phb->func(phb->data, -1, _("Unable to connect")); } g_free(phb->host); @@ -84,8 +82,8 @@ error = ETIMEDOUT; gaim_debug(GAIM_DEBUG_INFO, "QQ", "Connect didn't block\n"); - len = sizeof(error); - if (getsockopt(phb->udpsock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + errlen = sizeof(error); + if (getsockopt(phb->udpsock, SOL_SOCKET, SO_ERROR, &error, &errlen) < 0) { gaim_debug(GAIM_DEBUG_ERROR, "QQ", "getsockopt failed.\n"); close(phb->udpsock); return; @@ -105,7 +103,8 @@ unsigned char buf[512]; struct PHB *phb = data; struct sockaddr_in sin, ctlsin; - int port, ctllen; + int port; + socklen_t ctllen; gaim_debug(GAIM_DEBUG_INFO, "s5_sendconnect", "remote host is %s:%d\n", phb->host, phb->port); @@ -137,7 +136,9 @@ port = ntohs(ctlsin.sin_port) + 1; while (1) { - _qq_fill_host(&sin, "0.0.0.0", port); + inet_aton("0.0.0.0", &(sin.sin_addr)); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); if (bind(phb->udpsock, (struct sockaddr *) &sin, sizeof(sin)) < 0) { port++; if (port > 65500) { @@ -158,7 +159,7 @@ gaim_debug(GAIM_DEBUG_INFO, "s5_sendconnect", "packet too small\n"); if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) { - phb->func(phb->data, -1, NULL); + phb->func(phb->data, -1, _("Unable to connect")); } g_free(phb->host); @@ -182,7 +183,7 @@ if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) { - phb->func(phb->data, -1, NULL); + phb->func(phb->data, -1, _("Unable to connect")); } g_free(phb->host); @@ -195,7 +196,7 @@ if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) { - phb->func(phb->data, -1, NULL); + phb->func(phb->data, -1, _("Unable to connect")); } g_free(phb->host); @@ -238,7 +239,7 @@ if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) { - phb->func(phb->data, -1, NULL); + phb->func(phb->data, -1, _("Unable to connect")); } g_free(phb->host); @@ -263,7 +264,7 @@ if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) { - phb->func(phb->data, -1, NULL); + phb->func(phb->data, -1, _("Unable to connect")); } g_free(phb->host); @@ -297,7 +298,7 @@ close(source); if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) { - phb->func(phb->data, -1, NULL); + phb->func(phb->data, -1, _("Unable to connect")); } g_free(phb->host); @@ -327,7 +328,7 @@ if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) { - phb->func(phb->data, -1, NULL); + phb->func(phb->data, -1, _("Unable to connect")); } g_free(phb->host);