source: lib/proxy.c @ 07874be

Last change on this file since 07874be was 289bd2d, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-07T14:55:18Z

Applied patch from wahjava (with some modifications) for bug #644. This
lets proxy_connect() connect to IPv6 hosts.

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