source: lib/proxy.c @ 07ff8a2

Last change on this file since 07ff8a2 was 6f7ac17, checked in by Wilmer van der Gaast <wilmer@…>, at 2007-12-28T23:27:45Z

Fixed return value check in proxy_connect(), since on some systems
a non-blocking connect() can return immediately (when connecting to
localhost, for example). Closes bug #233 and #340.

  • Property mode set to 100644
File size: 12.6 KB
RevLine 
[b7d3cc34]1/*
2 * gaim
3 *
4 * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
5 * Copyright (C) 2002-2004, Wilmer van der Gaast, Jelmer Vernooij
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 *
21 */
22
23#define BITLBEE_CORE
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <sys/types.h>
28#ifndef _WIN32
29#include <sys/socket.h>
30#include <netdb.h>
31#include <netinet/in.h>
32#include <arpa/inet.h>
33#include <unistd.h>
34#else
35#include "sock.h"
36#define ETIMEDOUT WSAETIMEDOUT
37#define EINPROGRESS WSAEINPROGRESS
38#endif
39#include <fcntl.h>
40#include <errno.h>
41#include "nogaim.h"
42#include "proxy.h"
[7ed3199]43#include "base64.h"
[b7d3cc34]44
45char proxyhost[128] = "";
46int proxyport = 0;
47int proxytype = PROXY_NONE;
48char proxyuser[128] = "";
49char proxypass[128] = "";
50
51struct PHB {
[ba9edaa]52        b_event_handler func, proxy_func;
[b7d3cc34]53        gpointer data, proxy_data;
54        char *host;
55        int port;
56        int fd;
57        gint inpa;
58};
59
60
61
[c3ffa45]62static struct sockaddr_in *gaim_gethostbyname(const char *host, int port)
[b7d3cc34]63{
64        static struct sockaddr_in sin;
65
66        if (!inet_aton(host, &sin.sin_addr)) {
67                struct hostent *hp;
68                if (!(hp = gethostbyname(host))) {
69                        return NULL;
70                }
71                memset(&sin, 0, sizeof(struct sockaddr_in));
72                memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length);
73                sin.sin_family = hp->h_addrtype;
74        } else
75                sin.sin_family = AF_INET;
76        sin.sin_port = htons(port);
77
78        return &sin;
79}
80
[ba9edaa]81static gboolean gaim_io_connected(gpointer data, gint source, b_input_condition cond)
[b7d3cc34]82{
83        struct PHB *phb = data;
84        unsigned int len;
85        int error = ETIMEDOUT;
86        len = sizeof(error);
87       
88#ifndef _WIN32
89        if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
90                closesocket(source);
[ba9edaa]91                b_event_remove(phb->inpa);
[b7d3cc34]92                if( phb->proxy_func )
93                        phb->proxy_func(phb->proxy_data, -1, GAIM_INPUT_READ);
94                else {
95                        phb->func(phb->data, -1, GAIM_INPUT_READ);
96                        g_free(phb);
97                }
[ba9edaa]98                return FALSE;
[b7d3cc34]99        }
100#endif
[701acdd4]101        sock_make_blocking(source);
[ba9edaa]102        b_event_remove(phb->inpa);
[b7d3cc34]103        if( phb->proxy_func )
104                phb->proxy_func(phb->proxy_data, source, GAIM_INPUT_READ);
105        else {
106                phb->func(phb->data, source, GAIM_INPUT_READ);
107                g_free(phb);
108        }
[ba9edaa]109       
110        return FALSE;
[b7d3cc34]111}
112
[c3ffa45]113static int proxy_connect_none(const char *host, unsigned short port, struct PHB *phb)
[b7d3cc34]114{
115        struct sockaddr_in *sin;
116        int fd = -1;
117
118        if (!(sin = gaim_gethostbyname(host, port))) {
119                g_free(phb);
120                return -1;
121        }
122
123        if ((fd = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) {
124                g_free(phb);
125                return -1;
126        }
127
128        sock_make_nonblocking(fd);
[19ac9c5]129       
130        event_debug("proxy_connect_none( \"%s\", %d ) = %d\n", host, port, fd);
131       
[6f7ac17]132        if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0 && !sockerr_again()) {
133                closesocket(fd);
134                g_free(phb);
135               
136                return -1;
137        } else {
138                phb->inpa = b_input_add(fd, GAIM_INPUT_WRITE, gaim_io_connected, phb);
139                phb->fd = fd;
140               
141                return fd;
[b7d3cc34]142        }
143}
144
145
146/* Connecting to HTTP proxies */
147
148#define HTTP_GOODSTRING "HTTP/1.0 200 Connection established"
149#define HTTP_GOODSTRING2 "HTTP/1.1 200 Connection established"
150
[ba9edaa]151static gboolean http_canread(gpointer data, gint source, b_input_condition cond)
[b7d3cc34]152{
153        int nlc = 0;
154        int pos = 0;
155        struct PHB *phb = data;
156        char inputline[8192];
157
[ba9edaa]158        b_event_remove(phb->inpa);
[b7d3cc34]159
160        while ((pos < sizeof(inputline)-1) && (nlc != 2) && (read(source, &inputline[pos++], 1) == 1)) {
161                if (inputline[pos - 1] == '\n')
162                        nlc++;
163                else if (inputline[pos - 1] != '\r')
164                        nlc = 0;
165        }
166        inputline[pos] = '\0';
167
168        if ((memcmp(HTTP_GOODSTRING, inputline, strlen(HTTP_GOODSTRING)) == 0) ||
169            (memcmp(HTTP_GOODSTRING2, inputline, strlen(HTTP_GOODSTRING2)) == 0)) {
170                phb->func(phb->data, source, GAIM_INPUT_READ);
171                g_free(phb->host);
172                g_free(phb);
[ba9edaa]173                return FALSE;
[b7d3cc34]174        }
175
176        close(source);
177        phb->func(phb->data, -1, GAIM_INPUT_READ);
178        g_free(phb->host);
179        g_free(phb);
[ba9edaa]180       
181        return FALSE;
[b7d3cc34]182}
183
[ba9edaa]184static gboolean http_canwrite(gpointer data, gint source, b_input_condition cond)
[b7d3cc34]185{
186        char cmd[384];
187        struct PHB *phb = data;
188        unsigned int len;
189        int error = ETIMEDOUT;
190        if (phb->inpa > 0)
[ba9edaa]191                b_event_remove(phb->inpa);
[b7d3cc34]192        len = sizeof(error);
193        if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
194                close(source);
195                phb->func(phb->data, -1, GAIM_INPUT_READ);
196                g_free(phb->host);
197                g_free(phb);
[ba9edaa]198                return FALSE;
[b7d3cc34]199        }
[701acdd4]200        sock_make_blocking(source);
[b7d3cc34]201
202        g_snprintf(cmd, sizeof(cmd), "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n", phb->host, phb->port,
203                   phb->host, phb->port);
204        if (send(source, cmd, strlen(cmd), 0) < 0) {
205                close(source);
206                phb->func(phb->data, -1, GAIM_INPUT_READ);
207                g_free(phb->host);
208                g_free(phb);
[ba9edaa]209                return FALSE;
[b7d3cc34]210        }
211
[f618a4a]212        if (strlen(proxyuser) > 0) {
[b7d3cc34]213                char *t1, *t2;
214                t1 = g_strdup_printf("%s:%s", proxyuser, proxypass);
215                t2 = tobase64(t1);
216                g_free(t1);
217                g_snprintf(cmd, sizeof(cmd), "Proxy-Authorization: Basic %s\r\n", t2);
218                g_free(t2);
219                if (send(source, cmd, strlen(cmd), 0) < 0) {
220                        close(source);
221                        phb->func(phb->data, -1, GAIM_INPUT_READ);
222                        g_free(phb->host);
223                        g_free(phb);
[ba9edaa]224                        return FALSE;
[b7d3cc34]225                }
226        }
227
228        g_snprintf(cmd, sizeof(cmd), "\r\n");
229        if (send(source, cmd, strlen(cmd), 0) < 0) {
230                close(source);
231                phb->func(phb->data, -1, GAIM_INPUT_READ);
232                g_free(phb->host);
233                g_free(phb);
[ba9edaa]234                return FALSE;
[b7d3cc34]235        }
236
[ba9edaa]237        phb->inpa = b_input_add(source, GAIM_INPUT_READ, http_canread, phb);
238       
239        return FALSE;
[b7d3cc34]240}
241
[c3ffa45]242static int proxy_connect_http(const char *host, unsigned short port, struct PHB *phb)
[b7d3cc34]243{
244        phb->host = g_strdup(host);
245        phb->port = port;
246        phb->proxy_func = http_canwrite;
247        phb->proxy_data = phb;
248       
249        return( proxy_connect_none( proxyhost, proxyport, phb ) );
250}
251
252
253/* Connecting to SOCKS4 proxies */
254
[ba9edaa]255static gboolean s4_canread(gpointer data, gint source, b_input_condition cond)
[b7d3cc34]256{
257        unsigned char packet[12];
258        struct PHB *phb = data;
259
[ba9edaa]260        b_event_remove(phb->inpa);
[b7d3cc34]261
262        memset(packet, 0, sizeof(packet));
263        if (read(source, packet, 9) >= 4 && packet[1] == 90) {
264                phb->func(phb->data, source, GAIM_INPUT_READ);
265                g_free(phb->host);
266                g_free(phb);
[ba9edaa]267                return FALSE;
[b7d3cc34]268        }
269
270        close(source);
271        phb->func(phb->data, -1, GAIM_INPUT_READ);
272        g_free(phb->host);
273        g_free(phb);
[ba9edaa]274       
275        return FALSE;
[b7d3cc34]276}
277
[ba9edaa]278static gboolean s4_canwrite(gpointer data, gint source, b_input_condition cond)
[b7d3cc34]279{
280        unsigned char packet[12];
281        struct hostent *hp;
282        struct PHB *phb = data;
283        unsigned int len;
284        int error = ETIMEDOUT;
285        if (phb->inpa > 0)
[ba9edaa]286                b_event_remove(phb->inpa);
[b7d3cc34]287        len = sizeof(error);
288        if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
289                close(source);
290                phb->func(phb->data, -1, GAIM_INPUT_READ);
291                g_free(phb->host);
292                g_free(phb);
[ba9edaa]293                return FALSE;
[b7d3cc34]294        }
[701acdd4]295        sock_make_blocking(source);
[b7d3cc34]296
297        /* XXX does socks4 not support host name lookups by the proxy? */
298        if (!(hp = gethostbyname(phb->host))) {
299                close(source);
300                phb->func(phb->data, -1, GAIM_INPUT_READ);
301                g_free(phb->host);
302                g_free(phb);
[ba9edaa]303                return FALSE;
[b7d3cc34]304        }
305
306        packet[0] = 4;
307        packet[1] = 1;
308        packet[2] = phb->port >> 8;
309        packet[3] = phb->port & 0xff;
310        packet[4] = (unsigned char)(hp->h_addr_list[0])[0];
311        packet[5] = (unsigned char)(hp->h_addr_list[0])[1];
312        packet[6] = (unsigned char)(hp->h_addr_list[0])[2];
313        packet[7] = (unsigned char)(hp->h_addr_list[0])[3];
314        packet[8] = 0;
315        if (write(source, packet, 9) != 9) {
316                close(source);
317                phb->func(phb->data, -1, GAIM_INPUT_READ);
318                g_free(phb->host);
319                g_free(phb);
[ba9edaa]320                return FALSE;
[b7d3cc34]321        }
322
[ba9edaa]323        phb->inpa = b_input_add(source, GAIM_INPUT_READ, s4_canread, phb);
324       
325        return FALSE;
[b7d3cc34]326}
327
[c3ffa45]328static int proxy_connect_socks4(const char *host, unsigned short port, struct PHB *phb)
[b7d3cc34]329{
330        phb->host = g_strdup(host);
331        phb->port = port;
332        phb->proxy_func = s4_canwrite;
333        phb->proxy_data = phb;
334       
335        return( proxy_connect_none( proxyhost, proxyport, phb ) );
336}
337
338
339/* Connecting to SOCKS5 proxies */
340
[ba9edaa]341static gboolean s5_canread_again(gpointer data, gint source, b_input_condition cond)
[b7d3cc34]342{
343        unsigned char buf[512];
344        struct PHB *phb = data;
345
[ba9edaa]346        b_event_remove(phb->inpa);
[b7d3cc34]347
348        if (read(source, buf, 10) < 10) {
349                close(source);
350                phb->func(phb->data, -1, GAIM_INPUT_READ);
351                g_free(phb->host);
352                g_free(phb);
[ba9edaa]353                return FALSE;
[b7d3cc34]354        }
355        if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
356                close(source);
357                phb->func(phb->data, -1, GAIM_INPUT_READ);
358                g_free(phb->host);
359                g_free(phb);
[ba9edaa]360                return FALSE;
[b7d3cc34]361        }
362
363        phb->func(phb->data, source, GAIM_INPUT_READ);
364        g_free(phb->host);
365        g_free(phb);
[ba9edaa]366       
367        return FALSE;
[b7d3cc34]368}
369
370static void s5_sendconnect(gpointer data, gint source)
371{
372        unsigned char buf[512];
373        struct PHB *phb = data;
374        int hlen = strlen(phb->host);
[ba9edaa]375       
[b7d3cc34]376        buf[0] = 0x05;
377        buf[1] = 0x01;          /* CONNECT */
378        buf[2] = 0x00;          /* reserved */
379        buf[3] = 0x03;          /* address type -- host name */
380        buf[4] = hlen;
381        memcpy(buf + 5, phb->host, hlen);
382        buf[5 + strlen(phb->host)] = phb->port >> 8;
383        buf[5 + strlen(phb->host) + 1] = phb->port & 0xff;
384
385        if (write(source, buf, (5 + strlen(phb->host) + 2)) < (5 + strlen(phb->host) + 2)) {
386                close(source);
387                phb->func(phb->data, -1, GAIM_INPUT_READ);
388                g_free(phb->host);
389                g_free(phb);
390                return;
391        }
392
[ba9edaa]393        phb->inpa = b_input_add(source, GAIM_INPUT_READ, s5_canread_again, phb);
[b7d3cc34]394}
395
[ba9edaa]396static gboolean s5_readauth(gpointer data, gint source, b_input_condition cond)
[b7d3cc34]397{
398        unsigned char buf[512];
399        struct PHB *phb = data;
400
[ba9edaa]401        b_event_remove(phb->inpa);
[b7d3cc34]402
403        if (read(source, buf, 2) < 2) {
404                close(source);
405                phb->func(phb->data, -1, GAIM_INPUT_READ);
406                g_free(phb->host);
407                g_free(phb);
[ba9edaa]408                return FALSE;
[b7d3cc34]409        }
410
411        if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
412                close(source);
413                phb->func(phb->data, -1, GAIM_INPUT_READ);
414                g_free(phb->host);
415                g_free(phb);
[ba9edaa]416                return FALSE;
[b7d3cc34]417        }
418
419        s5_sendconnect(phb, source);
[ba9edaa]420       
421        return FALSE;
[b7d3cc34]422}
423
[ba9edaa]424static gboolean s5_canread(gpointer data, gint source, b_input_condition cond)
[b7d3cc34]425{
426        unsigned char buf[512];
427        struct PHB *phb = data;
428
[ba9edaa]429        b_event_remove(phb->inpa);
[b7d3cc34]430
431        if (read(source, buf, 2) < 2) {
432                close(source);
433                phb->func(phb->data, -1, GAIM_INPUT_READ);
434                g_free(phb->host);
435                g_free(phb);
[ba9edaa]436                return FALSE;
[b7d3cc34]437        }
438
439        if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
440                close(source);
441                phb->func(phb->data, -1, GAIM_INPUT_READ);
442                g_free(phb->host);
443                g_free(phb);
[ba9edaa]444                return FALSE;
[b7d3cc34]445        }
446
447        if (buf[1] == 0x02) {
448                unsigned int i = strlen(proxyuser), j = strlen(proxypass);
449                buf[0] = 0x01;  /* version 1 */
450                buf[1] = i;
451                memcpy(buf + 2, proxyuser, i);
452                buf[2 + i] = j;
453                memcpy(buf + 2 + i + 1, proxypass, j);
454                if (write(source, buf, 3 + i + j) < 3 + i + j) {
455                        close(source);
456                        phb->func(phb->data, -1, GAIM_INPUT_READ);
457                        g_free(phb->host);
458                        g_free(phb);
[ba9edaa]459                        return FALSE;
[b7d3cc34]460                }
461
[ba9edaa]462                phb->inpa = b_input_add(source, GAIM_INPUT_READ, s5_readauth, phb);
[b7d3cc34]463        } else {
464                s5_sendconnect(phb, source);
465        }
[ba9edaa]466       
467        return FALSE;
[b7d3cc34]468}
469
[ba9edaa]470static gboolean s5_canwrite(gpointer data, gint source, b_input_condition cond)
[b7d3cc34]471{
472        unsigned char buf[512];
473        int i;
474        struct PHB *phb = data;
475        unsigned int len;
476        int error = ETIMEDOUT;
477        if (phb->inpa > 0)
[ba9edaa]478                b_event_remove(phb->inpa);
[b7d3cc34]479        len = sizeof(error);
480        if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
481                close(source);
482                phb->func(phb->data, -1, GAIM_INPUT_READ);
483                g_free(phb->host);
484                g_free(phb);
[ba9edaa]485                return FALSE;
[b7d3cc34]486        }
[701acdd4]487        sock_make_blocking(source);
[b7d3cc34]488
489        i = 0;
490        buf[0] = 0x05;          /* SOCKS version 5 */
491        if (proxyuser[0]) {
492                buf[1] = 0x02;  /* two methods */
493                buf[2] = 0x00;  /* no authentication */
494                buf[3] = 0x02;  /* username/password authentication */
495                i = 4;
496        } else {
497                buf[1] = 0x01;
498                buf[2] = 0x00;
499                i = 3;
500        }
501
502        if (write(source, buf, i) < i) {
503                close(source);
504                phb->func(phb->data, -1, GAIM_INPUT_READ);
505                g_free(phb->host);
506                g_free(phb);
[ba9edaa]507                return FALSE;
[b7d3cc34]508        }
509
[ba9edaa]510        phb->inpa = b_input_add(source, GAIM_INPUT_READ, s5_canread, phb);
511       
512        return FALSE;
[b7d3cc34]513}
514
[c3ffa45]515static int proxy_connect_socks5(const char *host, unsigned short port, struct PHB *phb)
[b7d3cc34]516{
517        phb->host = g_strdup(host);
518        phb->port = port;
519        phb->proxy_func = s5_canwrite;
520        phb->proxy_data = phb;
521       
522        return( proxy_connect_none( proxyhost, proxyport, phb ) );
523}
524
525
526/* Export functions */
527
[ba9edaa]528int proxy_connect(const char *host, int port, b_event_handler func, gpointer data)
[b7d3cc34]529{
530        struct PHB *phb;
531       
532        if (!host || !port || (port == -1) || !func || strlen(host) > 128) {
533                return -1;
534        }
535       
536        phb = g_new0(struct PHB, 1);
537        phb->func = func;
538        phb->data = data;
539       
[f618a4a]540        if ((proxytype == PROXY_NONE) || strlen(proxyhost) > 0 || !proxyport || (proxyport == -1))
[b7d3cc34]541                return proxy_connect_none(host, port, phb);
542        else if (proxytype == PROXY_HTTP)
543                return proxy_connect_http(host, port, phb);
544        else if (proxytype == PROXY_SOCKS4)
545                return proxy_connect_socks4(host, port, phb);
546        else if (proxytype == PROXY_SOCKS5)
547                return proxy_connect_socks5(host, port, phb);
548       
549        if (phb->host) g_free(phb);
550        g_free(phb);
551        return -1;
552}
Note: See TracBrowser for help on using the repository browser.