source: protocols/proxy.c @ d1d6776

Last change on this file since d1d6776 was 7b23afd, checked in by Jelmer Vernooij <jelmer@…>, at 2005-11-07T16:16:18Z

Migrate my pluginable branch to use Wilmers' branch as parent

  • Property mode set to 100644
File size: 14.9 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/* this is a little piece of code to handle proxy connection */
24/* it is intended to : 1st handle http proxy, using the CONNECT command
25 , 2nd provide an easy way to add socks support */
26
27#define BITLBEE_CORE
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <sys/types.h>
32#ifndef _WIN32
33#include <sys/socket.h>
34#include <netdb.h>
35#include <netinet/in.h>
36#include <arpa/inet.h>
37#include <unistd.h>
38#else
39#include "sock.h"
40#define ETIMEDOUT WSAETIMEDOUT
41#define EINPROGRESS WSAEINPROGRESS
42#endif
43#include <fcntl.h>
44#include <errno.h>
45#include "nogaim.h"
46#include "proxy.h"
47
48#define GAIM_READ_COND  (G_IO_IN | G_IO_HUP | G_IO_ERR)
49#define GAIM_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL)
50#define GAIM_ERR_COND   (G_IO_HUP | G_IO_ERR | G_IO_NVAL)
51
52char proxyhost[128] = "";
53int proxyport = 0;
54int proxytype = PROXY_NONE;
55char proxyuser[128] = "";
56char proxypass[128] = "";
57
58struct PHB {
59        GaimInputFunction func, proxy_func;
60        gpointer data, proxy_data;
61        char *host;
62        int port;
63        int fd;
64        gint inpa;
65};
66
67typedef struct _GaimIOClosure {
68        GaimInputFunction function;
69        guint result;
70        gpointer data;
71} GaimIOClosure;
72
73
74
75static struct sockaddr_in *gaim_gethostbyname(char *host, int port)
76{
77        static struct sockaddr_in sin;
78
79        if (!inet_aton(host, &sin.sin_addr)) {
80                struct hostent *hp;
81                if (!(hp = gethostbyname(host))) {
82                        return NULL;
83                }
84                memset(&sin, 0, sizeof(struct sockaddr_in));
85                memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length);
86                sin.sin_family = hp->h_addrtype;
87        } else
88                sin.sin_family = AF_INET;
89        sin.sin_port = htons(port);
90
91        return &sin;
92}
93
94static void gaim_io_destroy(gpointer data)
95{
96        g_free(data);
97}
98
99#ifdef PROXYPROFILER
100struct proxyprofiler
101{
102        GaimInputFunction function;
103        gpointer data;
104       
105        int count;
106       
107        struct proxyprofiler *next;
108} *pp = NULL;
109
110void proxyprofiler_dump()
111{
112        struct proxyprofiler *l;
113        char s[128];
114        FILE *fp;
115       
116        sprintf( s, "proxyprofiler.%d", (int) getpid() );
117        fp = fopen( s, "w" );
118       
119        fprintf( fp, "%-18s  %-18s  %10s\n", "Function", "Data", "Count" );
120        for( l = pp; l; l = l->next )
121                fprintf( fp, "0x%-16x  0x%-16x  %10d\n", (int) l->function, (int) l->data, l->count );
122       
123        fclose( fp );
124}
125#endif
126
127static gboolean gaim_io_invoke(GIOChannel *source, GIOCondition condition, gpointer data)
128{
129        GaimIOClosure *closure = data;
130        GaimInputCondition gaim_cond = 0;
131
132#ifdef PROXYPROFILER
133        struct proxyprofiler *l;
134       
135        for( l = pp; l; l = l->next )
136        {
137                if( closure->function == l->function && closure->data == l->data )
138                        break;
139        }
140        if( l )
141        {
142                l->count ++;
143        }
144        else
145        {
146                l = g_new0( struct proxyprofiler, 1 );
147                l->function = closure->function;
148                l->data = closure->data;
149                l->count = 1;
150               
151                l->next = pp;
152                pp = l;
153        }
154#endif
155       
156        count_io_event(source, "proxy");
157       
158        if (condition & GAIM_READ_COND)
159                gaim_cond |= GAIM_INPUT_READ;
160        if (condition & GAIM_WRITE_COND)
161                gaim_cond |= GAIM_INPUT_WRITE;
162//      if (condition & GAIM_ERR_COND)
163//              fprintf( stderr, "ERROR! fd=%d\n", g_io_channel_unix_get_fd( source ) );
164
165        closure->function(closure->data, g_io_channel_unix_get_fd(source), gaim_cond);
166
167        return TRUE;
168}
169
170static void no_one_calls(gpointer data, gint source, GaimInputCondition cond)
171{
172        struct PHB *phb = data;
173        unsigned int len;
174        int error = ETIMEDOUT;
175        len = sizeof(error);
176       
177#ifndef _WIN32
178        if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
179                closesocket(source);
180                gaim_input_remove(phb->inpa);
181                if( phb->proxy_func )
182                        phb->proxy_func(phb->proxy_data, -1, GAIM_INPUT_READ);
183                else {
184                        phb->func(phb->data, -1, GAIM_INPUT_READ);
185                        g_free(phb);
186                }
187                return;
188        }
189        fcntl(source, F_SETFL, 0);
190#endif
191        gaim_input_remove(phb->inpa);
192        if( phb->proxy_func )
193                phb->proxy_func(phb->proxy_data, source, GAIM_INPUT_READ);
194        else {
195                phb->func(phb->data, source, GAIM_INPUT_READ);
196                g_free(phb);
197        }
198}
199
200static int proxy_connect_none(char *host, unsigned short port, struct PHB *phb)
201{
202        struct sockaddr_in *sin;
203        int fd = -1;
204
205        if (!(sin = gaim_gethostbyname(host, port))) {
206                g_free(phb);
207                return -1;
208        }
209
210        if ((fd = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) {
211                g_free(phb);
212                return -1;
213        }
214
215        sock_make_nonblocking(fd);
216
217        if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0) {
218                if (sockerr_again()) {
219                        phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, no_one_calls, phb);
220                        phb->fd = fd;
221                } else {
222                        closesocket(fd);
223                        g_free(phb);
224                        return -1;
225                }
226        }
227
228        return fd;
229}
230
231
232/* Connecting to HTTP proxies */
233
234#define HTTP_GOODSTRING "HTTP/1.0 200 Connection established"
235#define HTTP_GOODSTRING2 "HTTP/1.1 200 Connection established"
236
237static void http_canread(gpointer data, gint source, GaimInputCondition cond)
238{
239        int nlc = 0;
240        int pos = 0;
241        struct PHB *phb = data;
242        char inputline[8192];
243
244        gaim_input_remove(phb->inpa);
245
246        while ((pos < sizeof(inputline)-1) && (nlc != 2) && (read(source, &inputline[pos++], 1) == 1)) {
247                if (inputline[pos - 1] == '\n')
248                        nlc++;
249                else if (inputline[pos - 1] != '\r')
250                        nlc = 0;
251        }
252        inputline[pos] = '\0';
253
254        if ((memcmp(HTTP_GOODSTRING, inputline, strlen(HTTP_GOODSTRING)) == 0) ||
255            (memcmp(HTTP_GOODSTRING2, inputline, strlen(HTTP_GOODSTRING2)) == 0)) {
256                phb->func(phb->data, source, GAIM_INPUT_READ);
257                g_free(phb->host);
258                g_free(phb);
259                return;
260        }
261
262        close(source);
263        phb->func(phb->data, -1, GAIM_INPUT_READ);
264        g_free(phb->host);
265        g_free(phb);
266        return;
267}
268
269static void http_canwrite(gpointer data, gint source, GaimInputCondition cond)
270{
271        char cmd[384];
272        struct PHB *phb = data;
273        unsigned int len;
274        int error = ETIMEDOUT;
275        if (phb->inpa > 0)
276                gaim_input_remove(phb->inpa);
277        len = sizeof(error);
278        if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
279                close(source);
280                phb->func(phb->data, -1, GAIM_INPUT_READ);
281                g_free(phb->host);
282                g_free(phb);
283                return;
284        }
285#ifdef F_SETFL
286        fcntl(source, F_SETFL, 0);
287#endif
288
289        g_snprintf(cmd, sizeof(cmd), "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n", phb->host, phb->port,
290                   phb->host, phb->port);
291        if (send(source, cmd, strlen(cmd), 0) < 0) {
292                close(source);
293                phb->func(phb->data, -1, GAIM_INPUT_READ);
294                g_free(phb->host);
295                g_free(phb);
296                return;
297        }
298
299        if (proxyuser && *proxyuser) {
300                char *t1, *t2;
301                t1 = g_strdup_printf("%s:%s", proxyuser, proxypass);
302                t2 = tobase64(t1);
303                g_free(t1);
304                g_snprintf(cmd, sizeof(cmd), "Proxy-Authorization: Basic %s\r\n", t2);
305                g_free(t2);
306                if (send(source, cmd, strlen(cmd), 0) < 0) {
307                        close(source);
308                        phb->func(phb->data, -1, GAIM_INPUT_READ);
309                        g_free(phb->host);
310                        g_free(phb);
311                        return;
312                }
313        }
314
315        g_snprintf(cmd, sizeof(cmd), "\r\n");
316        if (send(source, cmd, strlen(cmd), 0) < 0) {
317                close(source);
318                phb->func(phb->data, -1, GAIM_INPUT_READ);
319                g_free(phb->host);
320                g_free(phb);
321                return;
322        }
323
324        phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, http_canread, phb);
325}
326
327static int proxy_connect_http(char *host, unsigned short port, struct PHB *phb)
328{
329        phb->host = g_strdup(host);
330        phb->port = port;
331        phb->proxy_func = http_canwrite;
332        phb->proxy_data = phb;
333       
334        return( proxy_connect_none( proxyhost, proxyport, phb ) );
335}
336
337
338/* Connecting to SOCKS4 proxies */
339
340static void s4_canread(gpointer data, gint source, GaimInputCondition cond)
341{
342        unsigned char packet[12];
343        struct PHB *phb = data;
344
345        gaim_input_remove(phb->inpa);
346
347        memset(packet, 0, sizeof(packet));
348        if (read(source, packet, 9) >= 4 && packet[1] == 90) {
349                phb->func(phb->data, source, GAIM_INPUT_READ);
350                g_free(phb->host);
351                g_free(phb);
352                return;
353        }
354
355        close(source);
356        phb->func(phb->data, -1, GAIM_INPUT_READ);
357        g_free(phb->host);
358        g_free(phb);
359}
360
361static void s4_canwrite(gpointer data, gint source, GaimInputCondition cond)
362{
363        unsigned char packet[12];
364        struct hostent *hp;
365        struct PHB *phb = data;
366        unsigned int len;
367        int error = ETIMEDOUT;
368        if (phb->inpa > 0)
369                gaim_input_remove(phb->inpa);
370        len = sizeof(error);
371        if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
372                close(source);
373                phb->func(phb->data, -1, GAIM_INPUT_READ);
374                g_free(phb->host);
375                g_free(phb);
376                return;
377        }
378#ifdef F_SETFL
379        fcntl(source, F_SETFL, 0);
380#endif
381
382        /* XXX does socks4 not support host name lookups by the proxy? */
383        if (!(hp = gethostbyname(phb->host))) {
384                close(source);
385                phb->func(phb->data, -1, GAIM_INPUT_READ);
386                g_free(phb->host);
387                g_free(phb);
388                return;
389        }
390
391        packet[0] = 4;
392        packet[1] = 1;
393        packet[2] = phb->port >> 8;
394        packet[3] = phb->port & 0xff;
395        packet[4] = (unsigned char)(hp->h_addr_list[0])[0];
396        packet[5] = (unsigned char)(hp->h_addr_list[0])[1];
397        packet[6] = (unsigned char)(hp->h_addr_list[0])[2];
398        packet[7] = (unsigned char)(hp->h_addr_list[0])[3];
399        packet[8] = 0;
400        if (write(source, packet, 9) != 9) {
401                close(source);
402                phb->func(phb->data, -1, GAIM_INPUT_READ);
403                g_free(phb->host);
404                g_free(phb);
405                return;
406        }
407
408        phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s4_canread, phb);
409}
410
411static int proxy_connect_socks4(char *host, unsigned short port, struct PHB *phb)
412{
413        phb->host = g_strdup(host);
414        phb->port = port;
415        phb->proxy_func = s4_canwrite;
416        phb->proxy_data = phb;
417       
418        return( proxy_connect_none( proxyhost, proxyport, phb ) );
419}
420
421
422/* Connecting to SOCKS5 proxies */
423
424static void s5_canread_again(gpointer data, gint source, GaimInputCondition cond)
425{
426        unsigned char buf[512];
427        struct PHB *phb = data;
428
429        gaim_input_remove(phb->inpa);
430
431        if (read(source, buf, 10) < 10) {
432                close(source);
433                phb->func(phb->data, -1, GAIM_INPUT_READ);
434                g_free(phb->host);
435                g_free(phb);
436                return;
437        }
438        if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
439                close(source);
440                phb->func(phb->data, -1, GAIM_INPUT_READ);
441                g_free(phb->host);
442                g_free(phb);
443                return;
444        }
445
446        phb->func(phb->data, source, GAIM_INPUT_READ);
447        g_free(phb->host);
448        g_free(phb);
449        return;
450}
451
452static void s5_sendconnect(gpointer data, gint source)
453{
454        unsigned char buf[512];
455        struct PHB *phb = data;
456        int hlen = strlen(phb->host);
457
458        buf[0] = 0x05;
459        buf[1] = 0x01;          /* CONNECT */
460        buf[2] = 0x00;          /* reserved */
461        buf[3] = 0x03;          /* address type -- host name */
462        buf[4] = hlen;
463        memcpy(buf + 5, phb->host, hlen);
464        buf[5 + strlen(phb->host)] = phb->port >> 8;
465        buf[5 + strlen(phb->host) + 1] = phb->port & 0xff;
466
467        if (write(source, buf, (5 + strlen(phb->host) + 2)) < (5 + strlen(phb->host) + 2)) {
468                close(source);
469                phb->func(phb->data, -1, GAIM_INPUT_READ);
470                g_free(phb->host);
471                g_free(phb);
472                return;
473        }
474
475        phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_canread_again, phb);
476}
477
478static void s5_readauth(gpointer data, gint source, GaimInputCondition cond)
479{
480        unsigned char buf[512];
481        struct PHB *phb = data;
482
483        gaim_input_remove(phb->inpa);
484
485        if (read(source, buf, 2) < 2) {
486                close(source);
487                phb->func(phb->data, -1, GAIM_INPUT_READ);
488                g_free(phb->host);
489                g_free(phb);
490                return;
491        }
492
493        if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
494                close(source);
495                phb->func(phb->data, -1, GAIM_INPUT_READ);
496                g_free(phb->host);
497                g_free(phb);
498                return;
499        }
500
501        s5_sendconnect(phb, source);
502}
503
504static void s5_canread(gpointer data, gint source, GaimInputCondition cond)
505{
506        unsigned char buf[512];
507        struct PHB *phb = data;
508
509        gaim_input_remove(phb->inpa);
510
511        if (read(source, buf, 2) < 2) {
512                close(source);
513                phb->func(phb->data, -1, GAIM_INPUT_READ);
514                g_free(phb->host);
515                g_free(phb);
516                return;
517        }
518
519        if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
520                close(source);
521                phb->func(phb->data, -1, GAIM_INPUT_READ);
522                g_free(phb->host);
523                g_free(phb);
524                return;
525        }
526
527        if (buf[1] == 0x02) {
528                unsigned int i = strlen(proxyuser), j = strlen(proxypass);
529                buf[0] = 0x01;  /* version 1 */
530                buf[1] = i;
531                memcpy(buf + 2, proxyuser, i);
532                buf[2 + i] = j;
533                memcpy(buf + 2 + i + 1, proxypass, j);
534                if (write(source, buf, 3 + i + j) < 3 + i + j) {
535                        close(source);
536                        phb->func(phb->data, -1, GAIM_INPUT_READ);
537                        g_free(phb->host);
538                        g_free(phb);
539                        return;
540                }
541
542                phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_readauth, phb);
543        } else {
544                s5_sendconnect(phb, source);
545        }
546}
547
548static void s5_canwrite(gpointer data, gint source, GaimInputCondition cond)
549{
550        unsigned char buf[512];
551        int i;
552        struct PHB *phb = data;
553        unsigned int len;
554        int error = ETIMEDOUT;
555        if (phb->inpa > 0)
556                gaim_input_remove(phb->inpa);
557        len = sizeof(error);
558        if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
559                close(source);
560                phb->func(phb->data, -1, GAIM_INPUT_READ);
561                g_free(phb->host);
562                g_free(phb);
563                return;
564        }
565#ifdef F_SETFL
566        fcntl(source, F_SETFL, 0);
567#endif
568
569        i = 0;
570        buf[0] = 0x05;          /* SOCKS version 5 */
571        if (proxyuser[0]) {
572                buf[1] = 0x02;  /* two methods */
573                buf[2] = 0x00;  /* no authentication */
574                buf[3] = 0x02;  /* username/password authentication */
575                i = 4;
576        } else {
577                buf[1] = 0x01;
578                buf[2] = 0x00;
579                i = 3;
580        }
581
582        if (write(source, buf, i) < i) {
583                close(source);
584                phb->func(phb->data, -1, GAIM_INPUT_READ);
585                g_free(phb->host);
586                g_free(phb);
587                return;
588        }
589
590        phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_canread, phb);
591}
592
593static int proxy_connect_socks5(char *host, unsigned short port, struct PHB *phb)
594{
595        phb->host = g_strdup(host);
596        phb->port = port;
597        phb->proxy_func = s5_canwrite;
598        phb->proxy_data = phb;
599       
600        return( proxy_connect_none( proxyhost, proxyport, phb ) );
601}
602
603
604/* Export functions */
605
606gint gaim_input_add(gint source, GaimInputCondition condition, GaimInputFunction function, gpointer data)
607{
608        GaimIOClosure *closure = g_new0(GaimIOClosure, 1);
609        GIOChannel *channel;
610        GIOCondition cond = 0;
611       
612        closure->function = function;
613        closure->data = data;
614       
615        if (condition & GAIM_INPUT_READ)
616                cond |= GAIM_READ_COND;
617        if (condition & GAIM_INPUT_WRITE)
618                cond |= GAIM_WRITE_COND;
619       
620        channel = g_io_channel_unix_new(source);
621        closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond,
622                                              gaim_io_invoke, closure, gaim_io_destroy);
623       
624        g_io_channel_unref(channel);
625        return closure->result;
626}
627
628void gaim_input_remove(gint tag)
629{
630        if (tag > 0)
631                g_source_remove(tag);
632}
633
634int proxy_connect(char *host, int port, GaimInputFunction func, gpointer data)
635{
636        struct PHB *phb;
637       
638        if (!host || !port || (port == -1) || !func || strlen(host) > 128) {
639                return -1;
640        }
641       
642        phb = g_new0(struct PHB, 1);
643        phb->func = func;
644        phb->data = data;
645       
646#ifndef _WIN32
647        sethostent(1);
648#endif
649       
650        if ((proxytype == PROXY_NONE) || !proxyhost || !proxyhost[0] || !proxyport || (proxyport == -1))
651                return proxy_connect_none(host, port, phb);
652        else if (proxytype == PROXY_HTTP)
653                return proxy_connect_http(host, port, phb);
654        else if (proxytype == PROXY_SOCKS4)
655                return proxy_connect_socks4(host, port, phb);
656        else if (proxytype == PROXY_SOCKS5)
657                return proxy_connect_socks5(host, port, phb);
658       
659        if (phb->host) g_free(phb);
660        g_free(phb);
661        return -1;
662}
Note: See TracBrowser for help on using the repository browser.