source: lib/proxy.c @ f485008

Last change on this file since f485008 was e046390, checked in by Wilmer van der Gaast <wilmer@…>, at 2009-10-10T23:25:54Z

Make purple use BitlBee's event handling API. Since the APIs never really
diverged too much this is fairly transparent. I did rename and redefine
GAIM_INPUT_* variables to really make it work without adding another stupid
layer in between.

One problem left, the new libpurple input API doesn't care about return
values. Fixing that in the next CL.

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