source: lib/proxy.c @ 4a0614e

Last change on this file since 4a0614e was fd03770, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-06-25T19:43:14Z

Merging from devel/Jelmer.

  • Property mode set to 100644
File size: 12.6 KB
Line 
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"
43#include "base64.h"
44
45char proxyhost[128] = "";
46int proxyport = 0;
47int proxytype = PROXY_NONE;
48char proxyuser[128] = "";
49char proxypass[128] = "";
50
51struct PHB {
52        b_event_handler func, proxy_func;
53        gpointer data, proxy_data;
54        char *host;
55        int port;
56        int fd;
57        gint inpa;
58};
59
60
61
62static struct sockaddr_in *gaim_gethostbyname(const char *host, int port)
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
81static gboolean gaim_io_connected(gpointer data, gint source, b_input_condition cond)
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);
91                b_event_remove(phb->inpa);
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                }
98                return FALSE;
99        }
100#endif
101        sock_make_blocking(source);
102        b_event_remove(phb->inpa);
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        }
109       
110        return FALSE;
111}
112
113static int proxy_connect_none(const char *host, unsigned short port, struct PHB *phb)
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);
129       
130        event_debug("proxy_connect_none( \"%s\", %d ) = %d\n", host, port, fd);
131       
132        if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0) {
133                if (sockerr_again()) {
134                        phb->inpa = b_input_add(fd, GAIM_INPUT_WRITE, gaim_io_connected, phb);
135                        phb->fd = fd;
136                } else {
137                        closesocket(fd);
138                        g_free(phb);
139                        return -1;
140                }
141        }
142
143        return fd;
144}
145
146
147/* Connecting to HTTP proxies */
148
149#define HTTP_GOODSTRING "HTTP/1.0 200 Connection established"
150#define HTTP_GOODSTRING2 "HTTP/1.1 200 Connection established"
151
152static gboolean http_canread(gpointer data, gint source, b_input_condition cond)
153{
154        int nlc = 0;
155        int pos = 0;
156        struct PHB *phb = data;
157        char inputline[8192];
158
159        b_event_remove(phb->inpa);
160
161        while ((pos < sizeof(inputline)-1) && (nlc != 2) && (read(source, &inputline[pos++], 1) == 1)) {
162                if (inputline[pos - 1] == '\n')
163                        nlc++;
164                else if (inputline[pos - 1] != '\r')
165                        nlc = 0;
166        }
167        inputline[pos] = '\0';
168
169        if ((memcmp(HTTP_GOODSTRING, inputline, strlen(HTTP_GOODSTRING)) == 0) ||
170            (memcmp(HTTP_GOODSTRING2, inputline, strlen(HTTP_GOODSTRING2)) == 0)) {
171                phb->func(phb->data, source, GAIM_INPUT_READ);
172                g_free(phb->host);
173                g_free(phb);
174                return FALSE;
175        }
176
177        close(source);
178        phb->func(phb->data, -1, GAIM_INPUT_READ);
179        g_free(phb->host);
180        g_free(phb);
181       
182        return FALSE;
183}
184
185static gboolean http_canwrite(gpointer data, gint source, b_input_condition cond)
186{
187        char cmd[384];
188        struct PHB *phb = data;
189        unsigned int len;
190        int error = ETIMEDOUT;
191        if (phb->inpa > 0)
192                b_event_remove(phb->inpa);
193        len = sizeof(error);
194        if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
195                close(source);
196                phb->func(phb->data, -1, GAIM_INPUT_READ);
197                g_free(phb->host);
198                g_free(phb);
199                return FALSE;
200        }
201        sock_make_blocking(source);
202
203        g_snprintf(cmd, sizeof(cmd), "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n", phb->host, phb->port,
204                   phb->host, phb->port);
205        if (send(source, cmd, strlen(cmd), 0) < 0) {
206                close(source);
207                phb->func(phb->data, -1, GAIM_INPUT_READ);
208                g_free(phb->host);
209                g_free(phb);
210                return FALSE;
211        }
212
213        if (proxyuser && *proxyuser) {
214                char *t1, *t2;
215                t1 = g_strdup_printf("%s:%s", proxyuser, proxypass);
216                t2 = tobase64(t1);
217                g_free(t1);
218                g_snprintf(cmd, sizeof(cmd), "Proxy-Authorization: Basic %s\r\n", t2);
219                g_free(t2);
220                if (send(source, cmd, strlen(cmd), 0) < 0) {
221                        close(source);
222                        phb->func(phb->data, -1, GAIM_INPUT_READ);
223                        g_free(phb->host);
224                        g_free(phb);
225                        return FALSE;
226                }
227        }
228
229        g_snprintf(cmd, sizeof(cmd), "\r\n");
230        if (send(source, cmd, strlen(cmd), 0) < 0) {
231                close(source);
232                phb->func(phb->data, -1, GAIM_INPUT_READ);
233                g_free(phb->host);
234                g_free(phb);
235                return FALSE;
236        }
237
238        phb->inpa = b_input_add(source, GAIM_INPUT_READ, http_canread, phb);
239       
240        return FALSE;
241}
242
243static int proxy_connect_http(const char *host, unsigned short port, struct PHB *phb)
244{
245        phb->host = g_strdup(host);
246        phb->port = port;
247        phb->proxy_func = http_canwrite;
248        phb->proxy_data = phb;
249       
250        return( proxy_connect_none( proxyhost, proxyport, phb ) );
251}
252
253
254/* Connecting to SOCKS4 proxies */
255
256static gboolean s4_canread(gpointer data, gint source, b_input_condition cond)
257{
258        unsigned char packet[12];
259        struct PHB *phb = data;
260
261        b_event_remove(phb->inpa);
262
263        memset(packet, 0, sizeof(packet));
264        if (read(source, packet, 9) >= 4 && packet[1] == 90) {
265                phb->func(phb->data, source, GAIM_INPUT_READ);
266                g_free(phb->host);
267                g_free(phb);
268                return FALSE;
269        }
270
271        close(source);
272        phb->func(phb->data, -1, GAIM_INPUT_READ);
273        g_free(phb->host);
274        g_free(phb);
275       
276        return FALSE;
277}
278
279static gboolean s4_canwrite(gpointer data, gint source, b_input_condition cond)
280{
281        unsigned char packet[12];
282        struct hostent *hp;
283        struct PHB *phb = data;
284        unsigned int len;
285        int error = ETIMEDOUT;
286        if (phb->inpa > 0)
287                b_event_remove(phb->inpa);
288        len = sizeof(error);
289        if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
290                close(source);
291                phb->func(phb->data, -1, GAIM_INPUT_READ);
292                g_free(phb->host);
293                g_free(phb);
294                return FALSE;
295        }
296        sock_make_blocking(source);
297
298        /* XXX does socks4 not support host name lookups by the proxy? */
299        if (!(hp = gethostbyname(phb->host))) {
300                close(source);
301                phb->func(phb->data, -1, GAIM_INPUT_READ);
302                g_free(phb->host);
303                g_free(phb);
304                return FALSE;
305        }
306
307        packet[0] = 4;
308        packet[1] = 1;
309        packet[2] = phb->port >> 8;
310        packet[3] = phb->port & 0xff;
311        packet[4] = (unsigned char)(hp->h_addr_list[0])[0];
312        packet[5] = (unsigned char)(hp->h_addr_list[0])[1];
313        packet[6] = (unsigned char)(hp->h_addr_list[0])[2];
314        packet[7] = (unsigned char)(hp->h_addr_list[0])[3];
315        packet[8] = 0;
316        if (write(source, packet, 9) != 9) {
317                close(source);
318                phb->func(phb->data, -1, GAIM_INPUT_READ);
319                g_free(phb->host);
320                g_free(phb);
321                return FALSE;
322        }
323
324        phb->inpa = b_input_add(source, GAIM_INPUT_READ, s4_canread, phb);
325       
326        return FALSE;
327}
328
329static int proxy_connect_socks4(const char *host, unsigned short port, struct PHB *phb)
330{
331        phb->host = g_strdup(host);
332        phb->port = port;
333        phb->proxy_func = s4_canwrite;
334        phb->proxy_data = phb;
335       
336        return( proxy_connect_none( proxyhost, proxyport, phb ) );
337}
338
339
340/* Connecting to SOCKS5 proxies */
341
342static gboolean s5_canread_again(gpointer data, gint source, b_input_condition cond)
343{
344        unsigned char buf[512];
345        struct PHB *phb = data;
346
347        b_event_remove(phb->inpa);
348
349        if (read(source, buf, 10) < 10) {
350                close(source);
351                phb->func(phb->data, -1, GAIM_INPUT_READ);
352                g_free(phb->host);
353                g_free(phb);
354                return FALSE;
355        }
356        if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
357                close(source);
358                phb->func(phb->data, -1, GAIM_INPUT_READ);
359                g_free(phb->host);
360                g_free(phb);
361                return FALSE;
362        }
363
364        phb->func(phb->data, source, GAIM_INPUT_READ);
365        g_free(phb->host);
366        g_free(phb);
367       
368        return FALSE;
369}
370
371static void s5_sendconnect(gpointer data, gint source)
372{
373        unsigned char buf[512];
374        struct PHB *phb = data;
375        int hlen = strlen(phb->host);
376       
377        buf[0] = 0x05;
378        buf[1] = 0x01;          /* CONNECT */
379        buf[2] = 0x00;          /* reserved */
380        buf[3] = 0x03;          /* address type -- host name */
381        buf[4] = hlen;
382        memcpy(buf + 5, phb->host, hlen);
383        buf[5 + strlen(phb->host)] = phb->port >> 8;
384        buf[5 + strlen(phb->host) + 1] = phb->port & 0xff;
385
386        if (write(source, buf, (5 + strlen(phb->host) + 2)) < (5 + strlen(phb->host) + 2)) {
387                close(source);
388                phb->func(phb->data, -1, GAIM_INPUT_READ);
389                g_free(phb->host);
390                g_free(phb);
391                return;
392        }
393
394        phb->inpa = b_input_add(source, GAIM_INPUT_READ, s5_canread_again, phb);
395}
396
397static gboolean s5_readauth(gpointer data, gint source, b_input_condition cond)
398{
399        unsigned char buf[512];
400        struct PHB *phb = data;
401
402        b_event_remove(phb->inpa);
403
404        if (read(source, buf, 2) < 2) {
405                close(source);
406                phb->func(phb->data, -1, GAIM_INPUT_READ);
407                g_free(phb->host);
408                g_free(phb);
409                return FALSE;
410        }
411
412        if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
413                close(source);
414                phb->func(phb->data, -1, GAIM_INPUT_READ);
415                g_free(phb->host);
416                g_free(phb);
417                return FALSE;
418        }
419
420        s5_sendconnect(phb, source);
421       
422        return FALSE;
423}
424
425static gboolean s5_canread(gpointer data, gint source, b_input_condition cond)
426{
427        unsigned char buf[512];
428        struct PHB *phb = data;
429
430        b_event_remove(phb->inpa);
431
432        if (read(source, buf, 2) < 2) {
433                close(source);
434                phb->func(phb->data, -1, GAIM_INPUT_READ);
435                g_free(phb->host);
436                g_free(phb);
437                return FALSE;
438        }
439
440        if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
441                close(source);
442                phb->func(phb->data, -1, GAIM_INPUT_READ);
443                g_free(phb->host);
444                g_free(phb);
445                return FALSE;
446        }
447
448        if (buf[1] == 0x02) {
449                unsigned int i = strlen(proxyuser), j = strlen(proxypass);
450                buf[0] = 0x01;  /* version 1 */
451                buf[1] = i;
452                memcpy(buf + 2, proxyuser, i);
453                buf[2 + i] = j;
454                memcpy(buf + 2 + i + 1, proxypass, j);
455                if (write(source, buf, 3 + i + j) < 3 + i + j) {
456                        close(source);
457                        phb->func(phb->data, -1, GAIM_INPUT_READ);
458                        g_free(phb->host);
459                        g_free(phb);
460                        return FALSE;
461                }
462
463                phb->inpa = b_input_add(source, GAIM_INPUT_READ, s5_readauth, phb);
464        } else {
465                s5_sendconnect(phb, source);
466        }
467       
468        return FALSE;
469}
470
471static gboolean s5_canwrite(gpointer data, gint source, b_input_condition cond)
472{
473        unsigned char buf[512];
474        int i;
475        struct PHB *phb = data;
476        unsigned int len;
477        int error = ETIMEDOUT;
478        if (phb->inpa > 0)
479                b_event_remove(phb->inpa);
480        len = sizeof(error);
481        if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
482                close(source);
483                phb->func(phb->data, -1, GAIM_INPUT_READ);
484                g_free(phb->host);
485                g_free(phb);
486                return FALSE;
487        }
488        sock_make_blocking(source);
489
490        i = 0;
491        buf[0] = 0x05;          /* SOCKS version 5 */
492        if (proxyuser[0]) {
493                buf[1] = 0x02;  /* two methods */
494                buf[2] = 0x00;  /* no authentication */
495                buf[3] = 0x02;  /* username/password authentication */
496                i = 4;
497        } else {
498                buf[1] = 0x01;
499                buf[2] = 0x00;
500                i = 3;
501        }
502
503        if (write(source, buf, i) < i) {
504                close(source);
505                phb->func(phb->data, -1, GAIM_INPUT_READ);
506                g_free(phb->host);
507                g_free(phb);
508                return FALSE;
509        }
510
511        phb->inpa = b_input_add(source, GAIM_INPUT_READ, s5_canread, phb);
512       
513        return FALSE;
514}
515
516static int proxy_connect_socks5(const char *host, unsigned short port, struct PHB *phb)
517{
518        phb->host = g_strdup(host);
519        phb->port = port;
520        phb->proxy_func = s5_canwrite;
521        phb->proxy_data = phb;
522       
523        return( proxy_connect_none( proxyhost, proxyport, phb ) );
524}
525
526
527/* Export functions */
528
529int proxy_connect(const char *host, int port, b_event_handler func, gpointer data)
530{
531        struct PHB *phb;
532       
533        if (!host || !port || (port == -1) || !func || strlen(host) > 128) {
534                return -1;
535        }
536       
537        phb = g_new0(struct PHB, 1);
538        phb->func = func;
539        phb->data = data;
540       
541        if ((proxytype == PROXY_NONE) || !proxyhost || !proxyhost[0] || !proxyport || (proxyport == -1))
542                return proxy_connect_none(host, port, phb);
543        else if (proxytype == PROXY_HTTP)
544                return proxy_connect_http(host, port, phb);
545        else if (proxytype == PROXY_SOCKS4)
546                return proxy_connect_socks4(host, port, phb);
547        else if (proxytype == PROXY_SOCKS5)
548                return proxy_connect_socks5(host, port, phb);
549       
550        if (phb->host) g_free(phb);
551        g_free(phb);
552        return -1;
553}
Note: See TracBrowser for help on using the repository browser.