Mercurial > pidgin.yaz
diff src/proxy.c @ 1840:00aef397a1fe
[gaim-migrate @ 1850]
reworked some of the proxy stuff so that it's non-blocking now. next thing to do is to get IRC, MSN, Napster, and Jabber to use the new proxy_connect code. After that, Oscar and Yahoo (maybe Zephyr too? not likely)
committer: Tailor Script <tailor@pidgin.im>
| author | Eric Warmenhoven <eric@warmenhoven.org> |
|---|---|
| date | Sat, 12 May 2001 01:38:04 +0000 |
| parents | b4d454e5ee31 |
| children | 4dbd8533d209 |
line wrap: on
line diff
--- a/src/proxy.c Fri May 11 21:54:27 2001 +0000 +++ b/src/proxy.c Sat May 12 01:38:04 2001 +0000 @@ -34,11 +34,40 @@ #include <netdb.h> #include <netinet/in.h> #include <unistd.h> +#include <fcntl.h> +#include <errno.h> #include <gtk/gtk.h> #include "gaim.h" #include "proxy.h" -static int proxy_connect_none(char *host, unsigned short port) +struct PHB { + GdkInputFunction func; + gpointer data; + char *host; + int port; + gint inpa; +}; + +static void no_one_calls(gpointer data, gint source, GdkInputCondition cond) +{ + struct PHB *phb = data; + int len, error = ETIMEDOUT; + debug_printf("Connected\n"); + len = sizeof(error); + if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + close(source); + gdk_input_remove(phb->inpa); + phb->func(phb->data, -1, GDK_INPUT_READ); + g_free(phb); + return; + } + fcntl(source, F_SETFL, 0); + gdk_input_remove(phb->inpa); + phb->func(phb->data, source, GDK_INPUT_READ); + g_free(phb); +} + +static int proxy_connect_none(char *host, unsigned short port, struct PHB *phb) { struct sockaddr_in sin; struct hostent *hp; @@ -46,20 +75,43 @@ debug_printf("connecting to %s:%d with no proxy\n", host, port); - if (!(hp = gethostbyname(host))) + if (!(hp = gethostbyname(host))) { + g_free(phb); return -1; + } 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; sin.sin_port = htons(port); - if ((fd = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0) + if ((fd = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0) { + g_free(phb); return -1; + } + fcntl(fd, F_SETFL, O_NONBLOCK); if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { - close(fd); - return -1; + if ((errno == EINPROGRESS) || (errno == EINTR)) { + debug_printf("Connect would have blocked\n"); + phb->inpa = gdk_input_add(fd, GDK_INPUT_WRITE, no_one_calls, phb); + } else { + close(fd); + g_free(phb); + return -1; + } + } else { + int len, error = ETIMEDOUT; + debug_printf("Connect didn't block\n"); + len = sizeof(error); + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + close(fd); + g_free(phb); + return -1; + } + fcntl(fd, F_SETFL, 0); + phb->func(phb->data, fd, GDK_INPUT_READ); + g_free(phb); } return fd; @@ -68,133 +120,345 @@ #define HTTP_GOODSTRING "HTTP/1.0 200 Connection established" #define HTTP_GOODSTRING2 "HTTP/1.1 200 Connection established" -static int proxy_connect_http(char *host, unsigned short port, char *proxyhost, unsigned short proxyport) +static void http_canread(gpointer data, gint source, GdkInputCondition cond) +{ + int nlc = 0; + int pos = 0; + struct PHB *phb = data; + char inputline[8192]; + + gdk_input_remove(phb->inpa); + + while ((nlc != 2) && (read(source, &inputline[pos++], 1) == 1)) { + if (inputline[pos-1] == '\n') + nlc++; + else if (inputline[pos-1] != '\r') + 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, GDK_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } + + close(source); + phb->func(phb->data, -1, GDK_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; +} + +static void http_canwrite(gpointer data, gint source, GdkInputCondition cond) +{ + char cmd[384]; + struct PHB *phb = data; + int len, error = ETIMEDOUT; + debug_printf("Connected\n"); + if (phb->inpa > 0) + gdk_input_remove(phb->inpa); + len = sizeof(error); + if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + close(source); + phb->func(phb->data, -1, GDK_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } + fcntl(source, F_SETFL, 0); + + snprintf(cmd, sizeof(cmd), "CONNECT %s:%d HTTP/1.1\r\n\r\n", phb->host, phb->port); + if (send(source, cmd, strlen(cmd), 0) < 0) { + close(source); + phb->func(phb->data, -1, GDK_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } + + phb->inpa = gdk_input_add(source, GDK_INPUT_READ, http_canread, phb); +} + +static int proxy_connect_http(char *host, unsigned short port, + char *proxyhost, unsigned short proxyport, + struct PHB *phb) { struct hostent *hp; struct sockaddr_in sin; int fd = -1; - char cmd[384]; - char inputline[8192]; - int nlc = 0; - int pos = 0; debug_printf("connecting to %s:%d via %s:%d using HTTP\n", host, port, proxyhost, proxyport); - if (!(hp = gethostbyname(proxyhost))) + if (!(hp = gethostbyname(proxyhost))) { + g_free(phb); return -1; + } 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; sin.sin_port = htons(proxyport); - if ((fd = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0) - return -1; - - if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { - close(fd); + if ((fd = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0) { + g_free(phb); return -1; } - snprintf(cmd, sizeof(cmd), "CONNECT %s:%d HTTP/1.1\r\n\r\n", host, port); + phb->host = g_strdup(host); + phb->port = port; - if (send(fd, cmd, strlen(cmd), 0) < 0) { - close(fd); - return -1; + fcntl(fd, F_SETFL, O_NONBLOCK); + if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { + if ((errno == EINPROGRESS) || (errno == EINTR)) { + debug_printf("Connect would have blocked\n"); + phb->inpa = gdk_input_add(fd, GDK_INPUT_WRITE, http_canwrite, phb); + } else { + close(fd); + g_free(phb->host); + g_free(phb); + return -1; + } + } else { + int len, error = ETIMEDOUT; + debug_printf("Connect didn't block\n"); + len = sizeof(error); + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + close(fd); + g_free(phb->host); + g_free(phb); + return -1; + } + fcntl(fd, F_SETFL, 0); + http_canwrite(phb, fd, GDK_INPUT_WRITE); } - while ((nlc != 2) && (read(fd, &inputline[pos++], 1) == 1)) { - if (inputline[pos-1] == '\n') - nlc++; - else if (inputline[pos-1] != '\r') - nlc = 0; + + return fd; +} + +static void s4_canread(gpointer data, gint source, GdkInputCondition cond) +{ + unsigned char packet[12]; + struct PHB *phb = data; + + gdk_input_remove(phb->inpa); + + memset(packet, 0, sizeof(packet)); + if (read(source, packet, 9) >= 4 && packet[1] == 90) { + phb->func(phb->data, source, GDK_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; } - debug_printf("Proxy says: %s\n", inputline); + close(source); + phb->func(phb->data, -1, GDK_INPUT_READ); + g_free(phb->host); + g_free(phb); +} - if ((memcmp(HTTP_GOODSTRING, inputline, strlen(HTTP_GOODSTRING)) == 0) || - (memcmp(HTTP_GOODSTRING2, inputline, strlen(HTTP_GOODSTRING2)) == 0)) { - return fd; +static void s4_canwrite(gpointer data, gint source, GdkInputCondition cond) +{ + unsigned char packet[12]; + struct hostent *hp; + struct PHB *phb = data; + int len, error = ETIMEDOUT; + debug_printf("Connected\n"); + if (phb->inpa > 0) + gdk_input_remove(phb->inpa); + len = sizeof(error); + if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + close(source); + phb->func(phb->data, -1, GDK_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } + fcntl(source, F_SETFL, 0); + + /* XXX does socks4 not support host name lookups by the proxy? */ + if (!(hp = gethostbyname(phb->host))) { + close(source); + phb->func(phb->data, -1, GDK_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; } - close(fd); - return -1; + packet[0] = 4; + packet[1] = 1; + packet[2] = phb->port >> 8; + packet[3] = phb->port & 0xff; + packet[4] = (unsigned char)(hp->h_addr_list[0])[0]; + packet[5] = (unsigned char)(hp->h_addr_list[0])[1]; + packet[6] = (unsigned char)(hp->h_addr_list[0])[2]; + packet[7] = (unsigned char)(hp->h_addr_list[0])[3]; + packet[8] = 0; + if (write(source, packet, 9) != 9) { + close(source); + phb->func(phb->data, -1, GDK_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } + + phb->inpa = gdk_input_add(source, GDK_INPUT_READ, s4_canread, phb); } static int proxy_connect_socks4(char *host, unsigned short port, - char *proxyhost, unsigned short proxyport) + char *proxyhost, unsigned short proxyport, + struct PHB *phb) { struct sockaddr_in sin; - unsigned char packet[12]; struct hostent *hp; int fd = -1; debug_printf("connecting to %s:%d via %s:%d using SOCKS4\n", host, port, proxyhost, proxyport); - if (!(hp = gethostbyname(proxyhost))) + if (!(hp = gethostbyname(proxyhost))) { + g_free(phb); return -1; + } 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; sin.sin_port = htons(proxyport); - if ((fd = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0) - return -1; - - if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { - close(fd); + if ((fd = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0) { + g_free(phb); return -1; } - /* XXX does socks4 not support host name lookups by the proxy? */ - if (!(hp = gethostbyname(host))) - 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 ((errno == EINPROGRESS) || (errno == EINTR)) { + debug_printf("Connect would have blocked\n"); + phb->inpa = gdk_input_add(fd, GDK_INPUT_WRITE, s4_canwrite, phb); + } else { + close(fd); + g_free(phb->host); + g_free(phb); + return -1; + } + } else { + int len, error = ETIMEDOUT; + debug_printf("Connect didn't block\n"); + len = sizeof(error); + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + close(fd); + g_free(phb->host); + g_free(phb); + return -1; + } + fcntl(fd, F_SETFL, 0); + s4_canwrite(phb, fd, GDK_INPUT_WRITE); + } - packet[0] = 4; - packet[1] = 1; - packet[2] = port >> 8; - packet[3] = port & 0xff; - packet[4] = (unsigned char)(hp->h_addr_list[0])[0]; - packet[5] = (unsigned char)(hp->h_addr_list[0])[1]; - packet[6] = (unsigned char)(hp->h_addr_list[0])[2]; - packet[7] = (unsigned char)(hp->h_addr_list[0])[3]; - packet[8] = 0; - if (write(fd, packet, 9) == 9) { - memset(packet, 0, sizeof(packet)); - if (read(fd, packet, 9) >= 4 && packet[1] == 90) - return fd; + return fd; +} + +static void s5_canread_again(gpointer data, gint source, GdkInputCondition cond) +{ + unsigned char buf[512]; + struct PHB *phb = data; + + gdk_input_remove(phb->inpa); + debug_printf("able to read again\n"); + + if (read(source, buf, 10) < 10) { + debug_printf("or not...\n"); + close(source); + phb->func(phb->data, -1, GDK_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; } - close(fd); + if ((buf[0] != 0x05) || (buf[1] != 0x00)) { + debug_printf("bad data\n"); + close(source); + phb->func(phb->data, -1, GDK_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } - return -1; + phb->func(phb->data, source, GDK_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; } -static int proxy_connect_socks5(char *host, unsigned short port, - char *proxyhost, unsigned short proxyport) +static void s5_canread(gpointer data, gint source, GdkInputCondition cond) { - int i, fd = -1; unsigned char buf[512]; - struct sockaddr_in sin; - struct hostent *hp; - int hlen = strlen(host); + struct PHB *phb = data; + int hlen = strlen(phb->host); + + gdk_input_remove(phb->inpa); + debug_printf("able to read\n"); - debug_printf("connecting to %s:%d via %s:%d using SOCKS5\n", host, port, proxyhost, proxyport); + if (read(source, buf, 2) < 2) { + close(source); + phb->func(phb->data, -1, GDK_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } + + if ((buf[0] != 0x05) || (buf[1] == 0xff)) { + close(source); + phb->func(phb->data, -1, GDK_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } - if (!(hp = gethostbyname(proxyhost))) - return -1; + buf[0] = 0x05; + buf[1] = 0x01; /* CONNECT */ + buf[2] = 0x00; /* reserved */ + buf[3] = 0x03; /* address type -- host name */ + buf[4] = hlen; + memcpy(buf + 5, phb->host, hlen); + buf[5 + strlen(phb->host)] = phb->port >> 8; + buf[5 + strlen(phb->host) + 1] = phb->port & 0xff; - 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; - sin.sin_port = htons(proxyport); + if (write(source, buf, (5 + strlen(phb->host) + 2)) < (5 + strlen(phb->host) + 2)) { + close(source); + phb->func(phb->data, -1, GDK_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } - if ((fd = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0) - return -1; + phb->inpa = gdk_input_add(source, GDK_INPUT_READ, s5_canread_again, phb); +} - if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { - close(fd); - return -1; +static void s5_canwrite(gpointer data, gint source, GdkInputCondition cond) +{ + unsigned char buf[512]; + int i; + struct PHB *phb = data; + int len, error = ETIMEDOUT; + debug_printf("Connected\n"); + if (phb->inpa > 0) + gdk_input_remove(phb->inpa); + len = sizeof(error); + if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + close(source); + phb->func(phb->data, -1, GDK_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; } + fcntl(source, F_SETFL, 0); i = 0; buf[0] = 0x05; /* SOCKS version 5 */ @@ -202,58 +466,98 @@ buf[2] = 0x00; i = 3; - if (write(fd, buf, i) < i) { - close(fd); - return -1; + if (write(source, buf, i) < i) { + debug_printf("unable to write\n"); + close(source); + phb->func(phb->data, -1, GDK_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; } - if (read(fd, buf, 2) < 2) { - close(fd); - return -1; - } + phb->inpa = gdk_input_add(source, GDK_INPUT_READ, s5_canread, phb); +} - if ((buf[0] != 0x05) || (buf[1] == 0xff)) { - close(fd); +static int proxy_connect_socks5(char *host, unsigned short port, + char *proxyhost, unsigned short proxyport, + struct PHB *phb) +{ + int fd = -1; + struct sockaddr_in sin; + struct hostent *hp; + + debug_printf("connecting to %s:%d via %s:%d using SOCKS5\n", host, port, proxyhost, proxyport); + + if (!(hp = gethostbyname(proxyhost))) { + g_free(phb); return -1; } - buf[0] = 0x05; - buf[1] = 0x01; /* CONNECT */ - buf[2] = 0x00; /* reserved */ - buf[3] = 0x03; /* address type -- host name */ - buf[4] = hlen; - memcpy(buf + 5, host, hlen); - buf[5 + strlen(host)] = port >> 8; - buf[5 + strlen(host) + 1] = port & 0xff; + 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; + sin.sin_port = htons(proxyport); - if (write(fd, buf, (5 + strlen(host) + 2)) < (5 + strlen(host) + 2)) { - close(fd); + if ((fd = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0) { + g_free(phb); return -1; } - if (read(fd, buf, 10) < 10) { - close(fd); - return -1; - } - if ((buf[0] != 0x05) || (buf[1] != 0x00)) { - close(fd); - 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 ((errno == EINPROGRESS) || (errno == EINTR)) { + debug_printf("Connect would have blocked\n"); + phb->inpa = gdk_input_add(fd, GDK_INPUT_WRITE, s5_canwrite, phb); + } else { + close(fd); + g_free(phb->host); + g_free(phb); + return -1; + } + } else { + int len, error = ETIMEDOUT; + debug_printf("Connect didn't block\n"); + len = sizeof(error); + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + close(fd); + g_free(phb->host); + g_free(phb); + return -1; + } + fcntl(fd, F_SETFL, 0); + s5_canwrite(phb, fd, GDK_INPUT_WRITE); } return fd; } -int proxy_connect(char *host, int port, char *proxyhost, int proxyport, int proxytype) +int proxy_connect(char *host, int port, + char *proxyhost, int proxyport, int proxytype, + GdkInputFunction func, gpointer data) { - if (!host || !port || (port == -1)) + struct PHB *phb = g_new0(struct PHB, 1); + phb->func = func; + phb->data = data; + + if (!host || !port || (port == -1) || !func) { + g_free(phb); return -1; - else if ((proxytype == PROXY_NONE) || + } + + if ((proxytype == PROXY_NONE) || !proxyhost || !proxyhost[0] || - !proxyport || (proxyport == -1)) return proxy_connect_none(host, port); + !proxyport || (proxyport == -1)) + return proxy_connect_none(host, port, phb); else if (proxytype == PROXY_HTTP) - return proxy_connect_http(host, port, proxyhost, proxyport); + return proxy_connect_http(host, port, proxyhost, proxyport, phb); else if (proxytype == PROXY_SOCKS4) - return proxy_connect_socks4(host, port, proxyhost, proxyport); + return proxy_connect_socks4(host, port, proxyhost, proxyport, phb); else if (proxytype == PROXY_SOCKS5) - return proxy_connect_socks5(host, port, proxyhost, proxyport); + return proxy_connect_socks5(host, port, proxyhost, proxyport, phb); + + g_free(phb); return -1; }
