Mercurial > pidgin
changeset 4366:7ba9b56a8796
[gaim-migrate @ 4632]
Ok, so this is a patch by Nicolas Lichtmaier (niqueco). You've probably noticed
that when you try to sign an account on, the UI freezes for a while (esp. on poor
connections). This was because gethostbyname() blocks. Nicolas's patch here forks
a new process to resolve host names and returns them in a pipe. It makes things
smoother and faster. Thanks so much, Nicolas.
committer: Tailor Script <tailor@pidgin.im>
author | Sean Egan <seanegan@gmail.com> |
---|---|
date | Mon, 20 Jan 2003 19:26:56 +0000 |
parents | 6e96ced6fb78 |
children | 101fa831f52f |
files | ChangeLog src/protocols/jabber/jabber.c src/protocols/oscar/oscar.c src/proxy.c src/proxy.h |
diffstat | 5 files changed, 581 insertions(+), 137 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Mon Jan 20 18:12:14 2003 +0000 +++ b/ChangeLog Mon Jan 20 19:26:56 2003 +0000 @@ -16,7 +16,9 @@ Robert McQueen) * Conversation backend and UI are now separated. (Thanks, Christian Hammond) - + * Asynchronous, non-blocking, DNS function (Thanks, Nicolas + Lichtmaier) + Plugins: * Tray icon plugin--replaces the old GNOME applet. You'll need the panel Notification Area applet (aka system-tray-applet)
--- a/src/protocols/jabber/jabber.c Mon Jan 20 18:12:14 2003 +0000 +++ b/src/protocols/jabber/jabber.c Mon Jan 20 19:26:56 2003 +0000 @@ -765,8 +765,7 @@ jd = gc->proto_data; gjc = jd->gjc; - if (gjc->fd != source) - gjc->fd = source; + gjc->fd = source; if (source == -1) { STATE_EVT(JCONN_STATE_OFF) @@ -798,7 +797,7 @@ static void gjab_start(gjconn gjc) { struct aim_user *user; - int port; + int port, rc; if (!gjc || gjc->state != JCONN_STATE_OFF) return; @@ -811,8 +810,8 @@ XML_SetElementHandler(gjc->parser, startElement, endElement); XML_SetCharacterDataHandler(gjc->parser, charData); - gjc->fd = proxy_connect(gjc->user->server, port, gjab_connected, GJ_GC(gjc)); - if (!user->gc || (gjc->fd < 0)) { + rc = proxy_connect(gjc->user->server, port, gjab_connected, GJ_GC(gjc)); + if (!user->gc || (rc < 0)) { STATE_EVT(JCONN_STATE_OFF) return; }
--- a/src/protocols/oscar/oscar.c Mon Jan 20 18:12:14 2003 +0000 +++ b/src/protocols/oscar/oscar.c Mon Jan 20 19:26:56 2003 +0000 @@ -620,6 +620,8 @@ odata = gc->proto_data; sess = odata->sess; conn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH); + + conn->fd = source; if (source < 0) { hide_login_progress(gc, _("Couldn't connect to host")); @@ -639,6 +641,7 @@ char buf[256]; struct gaim_connection *gc = new_gaim_conn(user); struct oscar_data *odata = gc->proto_data = g_new0(struct oscar_data, 1); + int rc; if (isdigit(*user->username)) { odata->icq = TRUE; @@ -675,12 +678,12 @@ aim_conn_addhandler(sess, conn, 0x0017, 0x0003, gaim_parse_auth_resp, 0); conn->status |= AIM_CONN_STATUS_INPROGRESS; - conn->fd = proxy_connect(user->proto_opt[USEROPT_AUTH][0] ? + rc = proxy_connect(user->proto_opt[USEROPT_AUTH][0] ? user->proto_opt[USEROPT_AUTH] : FAIM_LOGIN_SERVER, user->proto_opt[USEROPT_AUTHPORT][0] ? atoi(user->proto_opt[USEROPT_AUTHPORT]) : FAIM_LOGIN_PORT, oscar_login_connect, gc); - if (conn->fd < 0) { + if (rc < 0) { hide_login_progress(gc, _("Couldn't connect to host")); signoff(gc); return; @@ -766,7 +769,8 @@ odata = gc->proto_data; sess = odata->sess; - bosconn = odata->conn; + bosconn = odata->conn; + bosconn->fd = source; if (source < 0) { hide_login_progress(gc, _("Could Not Connect")); @@ -797,7 +801,7 @@ static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) { va_list ap; struct aim_authresp_info *info; - int i; char *host; int port; + int i, rc; char *host; int port; struct aim_user *user; aim_conn_t *bosconn; @@ -921,9 +925,9 @@ } host = g_strndup(info->bosip, i); bosconn->status |= AIM_CONN_STATUS_INPROGRESS; - bosconn->fd = proxy_connect(host, port, oscar_bos_connect, gc); + rc = proxy_connect(host, port, oscar_bos_connect, gc); g_free(host); - if (bosconn->fd < 0) { + if (rc < 0) { hide_login_progress(gc, _("Could Not Connect")); od->killme = TRUE; return 0; @@ -988,6 +992,8 @@ struct pieceofcrap *pos = data; char buf[BUF_LONG]; + pos->fd = source; + if (source < 0) { char buf[256]; g_snprintf(buf, sizeof(buf), _("You may be disconnected shortly. You may want to use TOC until " @@ -1079,7 +1085,6 @@ do_error_dialog(_("Gaim was Unable to get valid login hash."), buf, GAIM_WARNING); } - pos->fd = fd; return 1; } @@ -1166,6 +1171,7 @@ odata = gc->proto_data; sess = odata->sess; tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_CHATNAV); + tstconn->fd = source; if (source < 0) { aim_conn_kill(sess, &tstconn); @@ -1194,6 +1200,7 @@ odata = gc->proto_data; sess = odata->sess; tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH); + tstconn->fd = source; if (source < 0) { aim_conn_kill(sess, &tstconn); @@ -1226,6 +1233,7 @@ odata = gc->proto_data; sess = odata->sess; tstconn = ccon->conn; + tstconn->fd = source; if (source < 0) { aim_conn_kill(sess, &tstconn); @@ -1256,6 +1264,7 @@ odata = gc->proto_data; sess = odata->sess; tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_EMAIL); + tstconn->fd = source; if (source < 0) { aim_conn_kill(sess, &tstconn); @@ -1275,7 +1284,7 @@ struct gaim_connection *gc = sess->aux_data; struct aim_user *user = gc->user; aim_conn_t *tstconn; - int i; + int i, rc; char *host; int port; @@ -1309,8 +1318,8 @@ aim_conn_addhandler(sess, tstconn, 0x0007, 0x0007, gaim_account_confirm, 0); tstconn->status |= AIM_CONN_STATUS_INPROGRESS; - tstconn->fd = proxy_connect(host, port, oscar_auth_connect, gc); - if (tstconn->fd < 0) { + rc = proxy_connect(host, port, oscar_auth_connect, gc); + if (rc < 0) { aim_conn_kill(sess, &tstconn); debug_printf("unable to reconnect with authorizer\n"); g_free(host); @@ -1329,8 +1338,8 @@ aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_chatnav, 0); tstconn->status |= AIM_CONN_STATUS_INPROGRESS; - tstconn->fd = proxy_connect(host, port, oscar_chatnav_connect, gc); - if (tstconn->fd < 0) { + rc = proxy_connect(host, port, oscar_chatnav_connect, gc); + if (rc < 0) { aim_conn_kill(sess, &tstconn); debug_printf("unable to connect to chatnav server\n"); g_free(host); @@ -1361,8 +1370,8 @@ ccon->show = extract_name(redir->chat.room); ccon->conn->status |= AIM_CONN_STATUS_INPROGRESS; - ccon->conn->fd = proxy_connect(host, port, oscar_chat_connect, ccon); - if (ccon->conn->fd < 0) { + rc = proxy_connect(host, port, oscar_chat_connect, ccon); + if (rc < 0) { aim_conn_kill(sess, &tstconn); debug_printf("unable to connect to chat server\n"); g_free(host); @@ -1384,8 +1393,8 @@ aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_email, 0); tstconn->status |= AIM_CONN_STATUS_INPROGRESS; - tstconn->fd = proxy_connect(host, port, oscar_email_connect, gc); - if (tstconn->fd < 0) { + rc = proxy_connect(host, port, oscar_email_connect, gc); + if (rc < 0) { aim_conn_kill(sess, &tstconn); debug_printf("unable to connect to email server\n"); g_free(host); @@ -1503,8 +1512,7 @@ return; } - if (dim->conn->fd != source) - dim->conn->fd = source; + dim->conn->fd = source; aim_conn_completeconnect(od->sess, dim->conn); if (!(cnv = gaim_find_conversation(dim->name))) cnv = gaim_conversation_new(GAIM_CONV_IM, dim->name); @@ -1720,7 +1728,7 @@ struct oscar_data *od; struct direct_im *dim; char *host; int port = 4443; - int i; + int i, rc; if (!g_slist_find(connections, gc)) { cancel_direct_im(d); @@ -1760,9 +1768,9 @@ } host = g_strndup(d->ip, i); dim->conn->status |= AIM_CONN_STATUS_INPROGRESS; - dim->conn->fd = proxy_connect(host, port, oscar_directim_callback, dim); + rc = proxy_connect(host, port, oscar_directim_callback, dim); g_free(host); - if (dim->conn->fd < 0) { + if (rc < 0) { aim_conn_kill(od->sess, &dim->conn); g_free(dim); cancel_direct_im(d);
--- a/src/proxy.c Mon Jan 20 18:12:14 2003 +0000 +++ b/src/proxy.c Mon Jan 20 19:26:56 2003 +0000 @@ -37,6 +37,7 @@ #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> +#include <signal.h> #else #include <winsock.h> #endif @@ -55,7 +56,7 @@ char proxyhost[128] = { 0 }; int proxyport = 0; -int proxytype = 0; +proxytype_t proxytype = PROXY_NONE; char proxyuser[128] = { 0 }; char proxypass[128] = { 0 }; @@ -65,6 +66,7 @@ char *host; int port; gint inpa; + proxytype_t proxytype; }; typedef struct _GaimIOClosure { @@ -129,16 +131,416 @@ g_source_remove(tag); } -static struct sockaddr_in *gaim_gethostbyname(char *host, int port) + +typedef void (*dns_callback_t)(struct sockaddr *addr, size_t addrlen, + gpointer data, const char *error_message); + +#ifdef __unix__ + +/* This structure represents both a pending DNS request and + * a free child process. + */ +typedef struct { + char *host; + int port; + int socktype; + dns_callback_t callback; + gpointer data; + gint inpa; + int fd_in, fd_out; + pid_t dns_pid; +} pending_dns_request_t; + +static GSList *free_dns_children = NULL; +static GQueue *queued_requests = NULL; + +static int number_of_dns_children = 0; + +const int MAX_DNS_CHILDREN = 2; + +typedef struct { + char hostname[512]; + int port; + int socktype; +} dns_params_t; + +typedef struct { + dns_params_t params; + dns_callback_t callback; + gpointer data; +} queued_dns_request_t; + +static int send_dns_request_to_child(pending_dns_request_t *req, dns_params_t *dns_params) +{ + char ch; + int rc; + + /* Are you alive? */ + if(kill(req->dns_pid, 0) != 0) { + debug_printf("DNS child %d no longer exists\n", req->dns_pid); + return -1; + } + + /* Let's contact this lost child! */ + rc = write(req->fd_in, dns_params, sizeof(*dns_params)); + if(rc<0) { + debug_printf("Error writing to DNS child %d: %s\n", req->dns_pid, strerror(errno)); + return -1; + } + + g_return_val_if_fail(rc == sizeof(*dns_params), -1); + + /* Did you hear me? (This avoids some race conditions) */ + rc = read(req->fd_out, &ch, 1); + if(rc != 1 || ch!='Y') { + debug_printf("DNS child %d not responding: killing it!\n", req->dns_pid); + kill(req->dns_pid, SIGKILL); + return -1; + } + + debug_printf("Successfuly sent DNS request to child %d\n", req->dns_pid); + return 0; +} + +static void host_resolved(gpointer data, gint source, GaimInputCondition cond); + +static void release_dns_child(pending_dns_request_t *req) { - static struct sockaddr_in sin; + g_free(req->host); + + if(queued_requests && !g_queue_is_empty(queued_requests)) { + queued_dns_request_t *r = g_queue_pop_head(queued_requests); + req->host = g_strdup(r->params.hostname); + req->port = r->params.port; + req->socktype = r->params.socktype; + req->callback = r->callback; + req->data = r->data; + + debug_printf("Processing queued DNS query for '%s' with child %d\n", req->host, req->dns_pid); + + if(send_dns_request_to_child(req, &(r->params)) != 0) { + g_free(req->host); + g_free(req); + req = NULL; + number_of_dns_children--; + + debug_printf("Intent of process queued query of '%s' failed, requeing...\n", + r->params.hostname); + g_queue_push_head(queued_requests, r); + } else { + req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req); + g_free(r); + } + + } else { + req->host = NULL; + req->callback = NULL; + req->data = NULL; + free_dns_children = g_slist_append(free_dns_children, req); + } +} + +static void host_resolved(gpointer data, gint source, GaimInputCondition cond) +{ + pending_dns_request_t *req = (pending_dns_request_t*)data; + int rc, err; + struct sockaddr *addr = NULL; + socklen_t addrlen; + + debug_printf("Host '%s' resolved\n", req->host); + gaim_input_remove(req->inpa); + + rc=read(req->fd_out, &err, sizeof(err)); + if((rc==4) && (err!=0)) { + char message[1024]; + g_snprintf(message, sizeof(message), "DNS error: %s (pid=%d)", +#ifdef HAVE_GETADDRINFO + gai_strerror(err), +#else + hstrerror(err), +#endif + req->dns_pid); + debug_printf("%s\n",message); + req->callback(NULL, 0, req->data, message); + release_dns_child(req); + return; + } + if(G_LIKELY(rc>0)) { + rc=read(req->fd_out, &addrlen, sizeof(addrlen)); + if(G_LIKELY(rc>0)) { + addr=g_malloc(addrlen); + rc=read(req->fd_out, addr, addrlen); + } + } + if(G_UNLIKELY(rc==-1)) { + char message[1024]; + g_snprintf(message, sizeof(message), "Error reading from DNS child: %s",strerror(errno)); + debug_printf("%s\n",message); + close(req->fd_out); + req->callback(NULL, 0, req->data, message); + g_free(req->host); + g_free(req); + number_of_dns_children--; + return; + } + if(G_UNLIKELY(rc==0)) { + char message[1024]; + g_snprintf(message, sizeof(message), "EOF reading from DNS child"); + close(req->fd_out); + debug_printf("%s\n",message); + req->callback(NULL, 0, req->data, message); + g_free(req->host); + g_free(req); + number_of_dns_children--; + return; + } + +/* wait4(req->dns_pid, NULL, WNOHANG, NULL); */ + + req->callback(addr, addrlen, req->data, NULL); + + g_free(addr); + + release_dns_child(req); +} + +static void trap_gdb_bug() +{ + const char *message = + "Gaim's DNS child got a SIGTRAP signal. \n" + "This can be caused by trying to run gaim into gdb.\n" + "There's a known gdb bug which prevent this, supposedly gaim\n" + "should have detected you were using gdb and use an ugly hack,\n" + "check cope_with_gdb_brokenness() in proxy.c.\n\n" + "For more info about the bug check http://sources.redhat.com/ml/gdb/2001-07/msg00349.html\n"; + fputs("\n* * *\n",stderr); + fputs(message,stderr); + fputs("* * *\n\n",stderr); + execlp("xmessage","xmessage","-center", message, NULL); + _exit(1); +} + +static void cope_with_gdb_brokenness() +{ + static gboolean already_done = FALSE; + char s[300], e[300]; + int n; + pid_t ppid; - if (!inet_aton(host, &sin.sin_addr)) { +#ifdef __linux__ + if(already_done) + return; + already_done = TRUE; + ppid = getppid(); + sprintf(s,"/proc/%d/exe", ppid); + n = readlink(s, e, sizeof(e)); + e[MAX(n,sizeof(e)-1)] = '\0'; + + if(strstr(e,"gdb")) { + debug_printf("Debugger detected, performing useless query...\n"); + gethostbyname("x.x.x.x.x"); + } +#endif +} + +int gaim_gethostbyname_async(const char *hostname, int port, int socktype, dns_callback_t callback, gpointer data) +{ + pending_dns_request_t *req = NULL; + dns_params_t dns_params; + + strncpy(dns_params.hostname, hostname, sizeof(dns_params.hostname)-1); + dns_params.hostname[sizeof(dns_params.hostname)-1] = '\0'; + dns_params.port = port; + dns_params.socktype = socktype; + + /* Is there a free available child? */ + while(free_dns_children && !req) { + GSList *l = free_dns_children; + free_dns_children = g_slist_remove_link(free_dns_children, l); + req = l->data; + g_slist_free(l); + + if(send_dns_request_to_child(req, &dns_params) != 0) { + g_free(req); + req = NULL; + number_of_dns_children--; + continue; + } + + } + + if(!req) { + int child_out[2], child_in[2]; + + if(number_of_dns_children >= MAX_DNS_CHILDREN) { + queued_dns_request_t *r = g_new(queued_dns_request_t, 1); + memcpy(&(r->params), &dns_params, sizeof(dns_params)); + r->callback = callback; + r->data = data; + if(!queued_requests) + queued_requests = g_queue_new(); + g_queue_push_tail(queued_requests, r); + debug_printf("DNS query for '%s' queued\n", hostname); + return 0; + } + + if(pipe(child_out) || pipe(child_in)) { + debug_printf("Could not create pipes: %s",strerror(errno)); + return -1; + } + + /* We need to create a new child. */ + req = g_new(pending_dns_request_t,1); + + cope_with_gdb_brokenness(); + + req->dns_pid=fork(); + if(req->dns_pid==0) { + const int zero = 0; +#ifdef HAVE_GETADDRINFO + struct addrinfo hints, *res; + char servname[20]; + int rc; +#else + struct sockaddr_in sin; + const socklen_t addrlen = sizeof(sin); +#endif +#ifdef HAVE_SIGNAL_H + signal(SIGHUP, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGCHLD, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGTRAP, trap_gdb_bug); +#endif + + + close(child_out[0]); + close(child_in[1]); + + while(1) { + if(dns_params.hostname[0] == '\0') { + const char Y = 'Y'; + fd_set fds; + struct timeval tv = { .tv_sec = 40 , .tv_usec = 0 }; + FD_ZERO(&fds); + FD_SET(child_in[0], &fds); + rc = select(child_in[0]+1, &fds, NULL, NULL, &tv); + if(!rc) { + if(opt_debug) + fprintf(stderr,"dns[%d]: nobody needs me... =(\n", getpid()); + break; + } + rc = read(child_in[0], &dns_params, sizeof(dns_params)); + if(rc < 0) { + perror("read()"); + break; + } + if(rc==0) { + if(opt_debug) + fprintf(stderr,"dns[%d]: Ops, father has gone, wait for me, wait...!\n", getpid()); + _exit(0); + } + if(dns_params.hostname[0] == '\0') { + fprintf(stderr, "dns[%d]: hostname = \"\" (port = %d)!!!\n", getpid(), dns_params.port); + _exit(1); + } + write(child_out[1], &Y, 1); + } + +#ifdef HAVE_GETADDRINFO + g_snprintf(servname, sizeof(servname), "%d", dns_params.port); + memset(&hints,0,sizeof(hints)); + hints.ai_socktype = dns_params.socktype; + rc = getaddrinfo(dns_params.hostname, servname, &hints, &res); + if(rc) { + write(child_out[1], &rc, sizeof(int)); + close(child_out[1]); + if(opt_debug) + fprintf(stderr,"dns[%d] Error: getaddrinfo returned %d\n", + getpid(), rc); + dns_params.hostname[0] = '\0'; + continue; + } + write(child_out[1], &zero, sizeof(zero)); + write(child_out[1], &(res->ai_addrlen), sizeof(res->ai_addrlen)); + write(child_out[1], res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); +#else + if (!inet_aton(hostname, &sin.sin_addr)) { + struct hostent *hp; + if(!(hp = gethostbyname(dns_params.hostname))) { + write(child_out[1], &h_errno, sizeof(int)); + close(child_out[1]); + if(opt_debug) + fprintf(stderr,"DNS Error: %s\n",hstrerror(h_errno)); + _exit(0); + } + memset(&sin, 0, sizeof(struct sockaddr_in)); + memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); + sin.sin_family = hp->h_addrtype; + } else + sin.sin_family = AF_INET; + sin.sin_port = htons(dns_params.port); + write(child_out[1], &zero, sizeof(zero)); + write(child_out[1], &addrlen, sizeof(addrlen)); + write(child_out[1], &sin, addrlen); +#endif + dns_params.hostname[0] = '\0'; + } + close(child_out[1]); + close(child_in[0]); + _exit(0); + } + close(child_out[1]); + close(child_in[0]); + if(req->dns_pid==-1) { + debug_printf("Could not create child process for DNS: %s\n",strerror(errno)); + g_free(req); + return -1; + } + req->fd_in = child_in[1]; + req->fd_out = child_out[0]; + number_of_dns_children++; + debug_printf("Created new DNS child %d, there are now %d children.\n", + req->dns_pid, number_of_dns_children); + } + req->host=g_strdup(hostname); + req->port=port; + req->callback=callback; + req->data=data; + req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req); + return 0; +} +#else + +typedef struct { + gpointer data; + size_t addrlen; + struct sockaddr *addr; + dns_callback_t callback; +} pending_dns_request_t; + +static gboolean host_resolved(gpointer data) +{ + pending_dns_request_t *req = (pending_dns_request_t*)data; + req->callback(req->addr, req->addrlen, req->data, NULL); + g_free(req->addr); + g_free(req); + return FALSE; +} + +int gaim_gethostbyname_async(const char *hostname, int port, int socktype, dns_callback_t callback, gpointer data) +{ + struct sockaddr_in sin; + pending_dns_request_t *req; + + if (!inet_aton(hostname, &sin.sin_addr)) { struct hostent *hp; - if(!(hp = gethostbyname(host))) { + if(!(hp = gethostbyname(hostname))) { debug_printf("gaim_gethostbyname(\"%s\", %d) failed: %s", - host, port, hstrerror(h_errno)); - return NULL; + hostname, port, hstrerror(h_errno)); + return -1; } memset(&sin, 0, sizeof(struct sockaddr_in)); memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); @@ -147,9 +549,17 @@ sin.sin_family = AF_INET; sin.sin_port = htons(port); - return &sin; + req = g_new(pending_dns_request_t, 1); + req->addr = (struct sockaddr*) g_memdup(&sin, sizeof(sin)); + req->addrlen = sizeof(sin); + req->data = data; + req->callback = callback; + g_timeout_add(10, host_resolved, req); + return 0; } +#endif + static void no_one_calls(gpointer data, gint source, GaimInputCondition cond) { struct PHB *phb = data; @@ -163,12 +573,14 @@ close(source); gaim_input_remove(phb->inpa); phb->func(phb->data, -1, GAIM_INPUT_READ); + g_free(phb->host); g_free(phb); return; } fcntl(source, F_SETFL, 0); gaim_input_remove(phb->inpa); phb->func(phb->data, source, GAIM_INPUT_READ); + g_free(phb->host); g_free(phb); } @@ -177,39 +589,35 @@ struct PHB *phb = data; phb->func(phb->data, phb->port, GAIM_INPUT_READ); + g_free(phb->host); g_free(phb); return FALSE; } -static int proxy_connect_none(char *host, unsigned short port, struct PHB *phb) +static int proxy_connect_none(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen) { - struct sockaddr_in *sin; int fd = -1; - debug_printf("connecting to %s:%d with no proxy\n", host, port); + debug_printf("connecting to %s:%d with no proxy\n", phb->host, phb->port); - if (!(sin = gaim_gethostbyname(host, port))) { - debug_printf("gethostbyname failed\n"); - g_free(phb); - return -1; - } - - if ((fd = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) { - debug_printf("unable to create socket\n"); + if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0) { + debug_printf("unable to create socket: %s\n", strerror(errno)); + g_free(phb->host); g_free(phb); return -1; } fcntl(fd, F_SETFL, O_NONBLOCK); - if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0) { + if (connect(fd, (struct sockaddr *)addr, addrlen) < 0) { if ((errno == EINPROGRESS) || (errno == EINTR)) { debug_printf("Connect would have blocked\n"); phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, no_one_calls, phb); } else { debug_printf("connect failed (errno %d)\n", errno); close(fd); + g_free(phb->host); g_free(phb); return -1; } @@ -221,6 +629,7 @@ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { debug_printf("getsockopt failed\n"); close(fd); + g_free(phb->host); g_free(phb); return -1; } @@ -241,8 +650,9 @@ { int nlc = 0; int pos = 0; + int minor, major, status, error=0; struct PHB *phb = data; - char inputline[8192]; + char inputline[8192], *p; gaim_input_remove(phb->inpa); @@ -253,19 +663,35 @@ nlc = 0; } inputline[pos] = '\0'; - - debug_printf("Proxy says: %s\n", inputline); - - if ((memcmp(HTTP_GOODSTRING, inputline, strlen(HTTP_GOODSTRING)) == 0) || - (memcmp(HTTP_GOODSTRING2, inputline, strlen(HTTP_GOODSTRING2)) == 0)) { - phb->func(phb->data, source, GAIM_INPUT_READ); - g_free(phb->host); - g_free(phb); - return; + + error = strncmp(inputline, "HTTP/", 5) != 0; + if(!error) { + p = inputline + 5; + major = strtol(p, &p, 10); + error = (major==0) || (*p != '.'); + if(!error) { + p++; + minor = strtol(p, &p, 10); + error = (*p!=' '); + if(!error) { + p++; + status = strtol(p, &p, 10); + error = (*p!=' '); + } + } + } + + if(error) { + debug_printf("Unable to parse proxy's response: %s\n", inputline); + close(source); + source=-1; + } else if(status!=200) { + debug_printf("Proxy reserver replied: (%s)\n", p); + close(source); + source=-1; } - close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, source, GAIM_INPUT_READ); g_free(phb->host); g_free(phb); return; @@ -273,7 +699,8 @@ static void http_canwrite(gpointer data, gint source, GaimInputCondition cond) { - char cmd[384]; + char request[8192]; + int request_len = 0; struct PHB *phb = data; unsigned int len; int error = ETIMEDOUT; @@ -289,35 +716,24 @@ g_free(phb); return; } - fcntl(source, F_SETFL, 0); - g_snprintf(cmd, sizeof(cmd), "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n", phb->host, phb->port, + request_len = g_snprintf(request, sizeof(request), "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n", phb->host, phb->port, phb->host, phb->port); - if (send(source, cmd, strlen(cmd), 0) < 0) { - close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); - g_free(phb->host); - g_free(phb); - return; - } if (proxyuser) { char *t1, *t2; t1 = g_strdup_printf("%s:%s", proxyuser, proxypass); t2 = tobase64(t1); g_free(t1); - g_snprintf(cmd, sizeof(cmd), "Proxy-Authorization: Basic %s\r\n", t2); + g_return_if_fail(request_len < sizeof(request)); + request_len += g_snprintf(request + request_len, sizeof(request) - request_len, "Proxy-Authorization: Basic %s\r\n", t2); g_free(t2); - if (send(source, cmd, strlen(cmd), 0) < 0) { - close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); - g_free(phb->host); - g_free(phb); - return; - } } - g_snprintf(cmd, sizeof(cmd), "\r\n"); - if (send(source, cmd, strlen(cmd), 0) < 0) { + g_return_if_fail(request_len < sizeof(request)); + strcpy(request + request_len, "\r\n"); + request_len += 2; + + if (write(source, request, request_len) < 0) { close(source); phb->func(phb->data, -1, GAIM_INPUT_READ); g_free(phb->host); @@ -328,29 +744,21 @@ phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, http_canread, phb); } -static int proxy_connect_http(char *host, unsigned short port, struct PHB *phb) +static int proxy_connect_http(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen) { - struct sockaddr_in *sin; int fd = -1; - debug_printf("connecting to %s:%d via %s:%d using HTTP\n", host, port, proxyhost, proxyport); + debug_printf("connecting to %s:%d via %s:%d using HTTP\n", phb->host, phb->port, proxyhost, proxyport); - if (!(sin = gaim_gethostbyname(proxyhost, proxyport))) { + if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0) { + g_free(phb->host); g_free(phb); return -1; } - if ((fd = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) { - g_free(phb); - return -1; - } - - phb->host = g_strdup(host); - phb->port = port; - fcntl(fd, F_SETFL, O_NONBLOCK); - if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0) { + if (connect(fd, addr, addrlen) < 0) { if ((errno == EINPROGRESS) || (errno == EINTR)) { debug_printf("Connect would have blocked\n"); phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, http_canwrite, phb); @@ -452,28 +860,20 @@ phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s4_canread, phb); } -static int proxy_connect_socks4(char *host, unsigned short port, struct PHB *phb) +static int proxy_connect_socks4(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen) { - struct sockaddr_in *sin; int fd = -1; - debug_printf("connecting to %s:%d via %s:%d using SOCKS4\n", host, port, proxyhost, proxyport); + debug_printf("connecting to %s:%d via %s:%d using SOCKS4\n", phb->host, phb->port, proxyhost, proxyport); - if (!(sin = gaim_gethostbyname(proxyhost, proxyport))) { + if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0) { + g_free(phb->host); g_free(phb); return -1; } - if ((fd = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) { - g_free(phb); - return -1; - } - - phb->host = g_strdup(host); - phb->port = port; - fcntl(fd, F_SETFL, O_NONBLOCK); - if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0) { + if (connect(fd, addr, addrlen) < 0) { if ((errno == EINPROGRESS) || (errno == EINTR)) { debug_printf("Connect would have blocked\n"); phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, s4_canwrite, phb); @@ -678,28 +1078,20 @@ phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_canread, phb); } -static int proxy_connect_socks5(char *host, unsigned short port, struct PHB *phb) +static int proxy_connect_socks5(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen) { - struct sockaddr_in *sin; int fd = -1; - debug_printf("connecting to %s:%d via %s:%d using SOCKS5\n", host, port, proxyhost, proxyport); + debug_printf("connecting to %s:%d via %s:%d using SOCKS5\n", phb->host, phb->port, proxyhost, proxyport); - if (!(sin = gaim_gethostbyname(proxyhost, proxyport))) { + if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0) { + g_free(phb->host); g_free(phb); return -1; } - if ((fd = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) { - g_free(phb); - return -1; - } - - phb->host = g_strdup(host); - phb->port = port; - fcntl(fd, F_SETFL, O_NONBLOCK); - if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0) { + if (connect(fd, addr, addrlen) < 0) { if ((errno == EINPROGRESS) || (errno == EINTR)) { debug_printf("Connect would have blocked\n"); phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, s5_canwrite, phb); @@ -728,29 +1120,70 @@ return fd; } +static void connection_host_resolved(struct sockaddr *addr, size_t addrlen, gpointer data, const char *error_message) +{ + struct PHB *phb = (struct PHB*)data; + + if(!addr) + { + phb->func(phb->data, -1, GAIM_INPUT_READ); + return; + } + + switch(phb->proxytype) + { + case PROXY_NONE: + proxy_connect_none(phb, addr, addrlen); + break; + case PROXY_HTTP: + proxy_connect_http(phb, addr, addrlen); + break; + case PROXY_SOCKS4: + proxy_connect_socks4(phb, addr, addrlen); + break; + case PROXY_SOCKS5: + proxy_connect_socks5(phb, addr, addrlen); + break; + } +} + int proxy_connect(char *host, int port, GaimInputFunction func, gpointer data) { + char *connecthost = host; + int connectport = port; struct PHB *phb = g_new0(struct PHB, 1); phb->func = func; phb->data = data; + phb->host = g_strdup(host); + phb->port = port; + phb->proxytype = proxytype; if (!host || !port || (port == -1) || !func) { + if(host) + g_free(phb->host); g_free(phb); return -1; } -#ifndef _WIN32 - sethostent(1); -#endif - if ((proxytype == PROXY_NONE) || !proxyhost || !proxyhost[0] || !proxyport || (proxyport == -1)) - return proxy_connect_none(host, port, phb); - else if (proxytype == PROXY_HTTP) - return proxy_connect_http(host, port, phb); - else if (proxytype == PROXY_SOCKS4) - return proxy_connect_socks4(host, port, phb); - else if (proxytype == PROXY_SOCKS5) - return proxy_connect_socks5(host, port, phb); + + if ((phb->proxytype!=PROXY_NONE) && (!proxyhost || !proxyhost[0] || !proxyport || (proxyport == -1))) + phb->proxytype=PROXY_NONE; - g_free(phb); - return -1; + switch(phb->proxytype) + { + case PROXY_NONE: + break; + case PROXY_HTTP: + case PROXY_SOCKS4: + case PROXY_SOCKS5: + connecthost=proxyhost; + connectport=proxyport; + break; + default: + g_free(phb->host); + g_free(phb); + return -1; + } + + gaim_gethostbyname_async(connecthost, connectport, SOCK_STREAM, connection_host_resolved, phb); + return 1; } -
--- a/src/proxy.h Mon Jan 20 18:12:14 2003 +0000 +++ b/src/proxy.h Mon Jan 20 19:26:56 2003 +0000 @@ -39,14 +39,16 @@ #include <glib.h> -#define PROXY_NONE 0 -#define PROXY_HTTP 1 -#define PROXY_SOCKS4 2 -#define PROXY_SOCKS5 3 +typedef enum { + PROXY_NONE = 0, + PROXY_HTTP, + PROXY_SOCKS4, + PROXY_SOCKS5, +} proxytype_t; extern char proxyhost[128]; extern int proxyport; -extern int proxytype; +extern proxytype_t proxytype; extern char proxyuser[128]; extern char proxypass[128]; extern guint proxy_info_is_from_gaimrc;