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
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
[ba9edaa]60static gboolean gaim_io_connected(gpointer data, gint source, b_input_condition cond)
[b7d3cc34]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);
[ba9edaa]70                b_event_remove(phb->inpa);
[b7d3cc34]71                if( phb->proxy_func )
[e046390]72                        phb->proxy_func(phb->proxy_data, -1, B_EV_IO_READ);
[b7d3cc34]73                else {
[e046390]74                        phb->func(phb->data, -1, B_EV_IO_READ);
[b7d3cc34]75                        g_free(phb);
76                }
[ba9edaa]77                return FALSE;
[b7d3cc34]78        }
79#endif
[701acdd4]80        sock_make_blocking(source);
[ba9edaa]81        b_event_remove(phb->inpa);
[b7d3cc34]82        if( phb->proxy_func )
[e046390]83                phb->proxy_func(phb->proxy_data, source, B_EV_IO_READ);
[b7d3cc34]84        else {
[e046390]85                phb->func(phb->data, source, B_EV_IO_READ);
[b7d3cc34]86                g_free(phb);
87        }
[ba9edaa]88       
89        return FALSE;
[b7d3cc34]90}
91
[289bd2d]92static int proxy_connect_none(const char *host, unsigned short port_, struct PHB *phb)
[b7d3cc34]93{
[aefaac3a]94        struct sockaddr_in me;
[b7d3cc34]95        int fd = -1;
[289bd2d]96        int ret;
97        char port[6];
98        struct addrinfo hints;
99        struct addrinfo* result;
[b7d3cc34]100
[289bd2d]101        g_snprintf(port, sizeof(port), "%d", port_);
[b7d3cc34]102
[289bd2d]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;
[b7d3cc34]107
[289bd2d]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);
[19ac9c5]132       
[289bd2d]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
[aefaac3a]149        {
[289bd2d]150                event_debug("gai(): %s\n", gai_strerror(ret));
[aefaac3a]151        }
152       
[289bd2d]153        if(fd < 0)
[6f7ac17]154                g_free(phb);
[289bd2d]155
156        return fd;
[b7d3cc34]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
[ba9edaa]165static gboolean http_canread(gpointer data, gint source, b_input_condition cond)
[b7d3cc34]166{
167        int nlc = 0;
168        int pos = 0;
169        struct PHB *phb = data;
170        char inputline[8192];
171
[ba9edaa]172        b_event_remove(phb->inpa);
[b7d3cc34]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)) {
[e046390]184                phb->func(phb->data, source, B_EV_IO_READ);
[b7d3cc34]185                g_free(phb->host);
186                g_free(phb);
[ba9edaa]187                return FALSE;
[b7d3cc34]188        }
189
190        close(source);
[e046390]191        phb->func(phb->data, -1, B_EV_IO_READ);
[b7d3cc34]192        g_free(phb->host);
193        g_free(phb);
[ba9edaa]194       
195        return FALSE;
[b7d3cc34]196}
197
[ba9edaa]198static gboolean http_canwrite(gpointer data, gint source, b_input_condition cond)
[b7d3cc34]199{
200        char cmd[384];
201        struct PHB *phb = data;
202        unsigned int len;
203        int error = ETIMEDOUT;
204        if (phb->inpa > 0)
[ba9edaa]205                b_event_remove(phb->inpa);
[b7d3cc34]206        len = sizeof(error);
207        if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
208                close(source);
[e046390]209                phb->func(phb->data, -1, B_EV_IO_READ);
[b7d3cc34]210                g_free(phb->host);
211                g_free(phb);
[ba9edaa]212                return FALSE;
[b7d3cc34]213        }
[701acdd4]214        sock_make_blocking(source);
[b7d3cc34]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);
[e046390]220                phb->func(phb->data, -1, B_EV_IO_READ);
[b7d3cc34]221                g_free(phb->host);
222                g_free(phb);
[ba9edaa]223                return FALSE;
[b7d3cc34]224        }
225
[f618a4a]226        if (strlen(proxyuser) > 0) {
[b7d3cc34]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);
[e046390]235                        phb->func(phb->data, -1, B_EV_IO_READ);
[b7d3cc34]236                        g_free(phb->host);
237                        g_free(phb);
[ba9edaa]238                        return FALSE;
[b7d3cc34]239                }
240        }
241
242        g_snprintf(cmd, sizeof(cmd), "\r\n");
243        if (send(source, cmd, strlen(cmd), 0) < 0) {
244                close(source);
[e046390]245                phb->func(phb->data, -1, B_EV_IO_READ);
[b7d3cc34]246                g_free(phb->host);
247                g_free(phb);
[ba9edaa]248                return FALSE;
[b7d3cc34]249        }
250
[e046390]251        phb->inpa = b_input_add(source, B_EV_IO_READ, http_canread, phb);
[ba9edaa]252       
253        return FALSE;
[b7d3cc34]254}
255
[c3ffa45]256static int proxy_connect_http(const char *host, unsigned short port, struct PHB *phb)
[b7d3cc34]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
[ba9edaa]269static gboolean s4_canread(gpointer data, gint source, b_input_condition cond)
[b7d3cc34]270{
271        unsigned char packet[12];
272        struct PHB *phb = data;
273
[ba9edaa]274        b_event_remove(phb->inpa);
[b7d3cc34]275
276        memset(packet, 0, sizeof(packet));
277        if (read(source, packet, 9) >= 4 && packet[1] == 90) {
[e046390]278                phb->func(phb->data, source, B_EV_IO_READ);
[b7d3cc34]279                g_free(phb->host);
280                g_free(phb);
[ba9edaa]281                return FALSE;
[b7d3cc34]282        }
283
284        close(source);
[e046390]285        phb->func(phb->data, -1, B_EV_IO_READ);
[b7d3cc34]286        g_free(phb->host);
287        g_free(phb);
[ba9edaa]288       
289        return FALSE;
[b7d3cc34]290}
291
[ba9edaa]292static gboolean s4_canwrite(gpointer data, gint source, b_input_condition cond)
[b7d3cc34]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)
[ba9edaa]300                b_event_remove(phb->inpa);
[b7d3cc34]301        len = sizeof(error);
302        if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
303                close(source);
[e046390]304                phb->func(phb->data, -1, B_EV_IO_READ);
[b7d3cc34]305                g_free(phb->host);
306                g_free(phb);
[ba9edaa]307                return FALSE;
[b7d3cc34]308        }
[701acdd4]309        sock_make_blocking(source);
[b7d3cc34]310
311        /* XXX does socks4 not support host name lookups by the proxy? */
312        if (!(hp = gethostbyname(phb->host))) {
313                close(source);
[e046390]314                phb->func(phb->data, -1, B_EV_IO_READ);
[b7d3cc34]315                g_free(phb->host);
316                g_free(phb);
[ba9edaa]317                return FALSE;
[b7d3cc34]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);
[e046390]331                phb->func(phb->data, -1, B_EV_IO_READ);
[b7d3cc34]332                g_free(phb->host);
333                g_free(phb);
[ba9edaa]334                return FALSE;
[b7d3cc34]335        }
336
[e046390]337        phb->inpa = b_input_add(source, B_EV_IO_READ, s4_canread, phb);
[ba9edaa]338       
339        return FALSE;
[b7d3cc34]340}
341
[c3ffa45]342static int proxy_connect_socks4(const char *host, unsigned short port, struct PHB *phb)
[b7d3cc34]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
[ba9edaa]355static gboolean s5_canread_again(gpointer data, gint source, b_input_condition cond)
[b7d3cc34]356{
357        unsigned char buf[512];
358        struct PHB *phb = data;
359
[ba9edaa]360        b_event_remove(phb->inpa);
[b7d3cc34]361
362        if (read(source, buf, 10) < 10) {
363                close(source);
[e046390]364                phb->func(phb->data, -1, B_EV_IO_READ);
[b7d3cc34]365                g_free(phb->host);
366                g_free(phb);
[ba9edaa]367                return FALSE;
[b7d3cc34]368        }
369        if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
370                close(source);
[e046390]371                phb->func(phb->data, -1, B_EV_IO_READ);
[b7d3cc34]372                g_free(phb->host);
373                g_free(phb);
[ba9edaa]374                return FALSE;
[b7d3cc34]375        }
376
[e046390]377        phb->func(phb->data, source, B_EV_IO_READ);
[b7d3cc34]378        g_free(phb->host);
379        g_free(phb);
[ba9edaa]380       
381        return FALSE;
[b7d3cc34]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);
[ba9edaa]389       
[b7d3cc34]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);
[e046390]401                phb->func(phb->data, -1, B_EV_IO_READ);
[b7d3cc34]402                g_free(phb->host);
403                g_free(phb);
404                return;
405        }
406
[e046390]407        phb->inpa = b_input_add(source, B_EV_IO_READ, s5_canread_again, phb);
[b7d3cc34]408}
409
[ba9edaa]410static gboolean s5_readauth(gpointer data, gint source, b_input_condition cond)
[b7d3cc34]411{
412        unsigned char buf[512];
413        struct PHB *phb = data;
414
[ba9edaa]415        b_event_remove(phb->inpa);
[b7d3cc34]416
417        if (read(source, buf, 2) < 2) {
418                close(source);
[e046390]419                phb->func(phb->data, -1, B_EV_IO_READ);
[b7d3cc34]420                g_free(phb->host);
421                g_free(phb);
[ba9edaa]422                return FALSE;
[b7d3cc34]423        }
424
425        if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
426                close(source);
[e046390]427                phb->func(phb->data, -1, B_EV_IO_READ);
[b7d3cc34]428                g_free(phb->host);
429                g_free(phb);
[ba9edaa]430                return FALSE;
[b7d3cc34]431        }
432
433        s5_sendconnect(phb, source);
[ba9edaa]434       
435        return FALSE;
[b7d3cc34]436}
437
[ba9edaa]438static gboolean s5_canread(gpointer data, gint source, b_input_condition cond)
[b7d3cc34]439{
440        unsigned char buf[512];
441        struct PHB *phb = data;
442
[ba9edaa]443        b_event_remove(phb->inpa);
[b7d3cc34]444
445        if (read(source, buf, 2) < 2) {
446                close(source);
[e046390]447                phb->func(phb->data, -1, B_EV_IO_READ);
[b7d3cc34]448                g_free(phb->host);
449                g_free(phb);
[ba9edaa]450                return FALSE;
[b7d3cc34]451        }
452
453        if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
454                close(source);
[e046390]455                phb->func(phb->data, -1, B_EV_IO_READ);
[b7d3cc34]456                g_free(phb->host);
457                g_free(phb);
[ba9edaa]458                return FALSE;
[b7d3cc34]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);
[e046390]470                        phb->func(phb->data, -1, B_EV_IO_READ);
[b7d3cc34]471                        g_free(phb->host);
472                        g_free(phb);
[ba9edaa]473                        return FALSE;
[b7d3cc34]474                }
475
[e046390]476                phb->inpa = b_input_add(source, B_EV_IO_READ, s5_readauth, phb);
[b7d3cc34]477        } else {
478                s5_sendconnect(phb, source);
479        }
[ba9edaa]480       
481        return FALSE;
[b7d3cc34]482}
483
[ba9edaa]484static gboolean s5_canwrite(gpointer data, gint source, b_input_condition cond)
[b7d3cc34]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)
[ba9edaa]492                b_event_remove(phb->inpa);
[b7d3cc34]493        len = sizeof(error);
494        if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
495                close(source);
[e046390]496                phb->func(phb->data, -1, B_EV_IO_READ);
[b7d3cc34]497                g_free(phb->host);
498                g_free(phb);
[ba9edaa]499                return FALSE;
[b7d3cc34]500        }
[701acdd4]501        sock_make_blocking(source);
[b7d3cc34]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);
[e046390]518                phb->func(phb->data, -1, B_EV_IO_READ);
[b7d3cc34]519                g_free(phb->host);
520                g_free(phb);
[ba9edaa]521                return FALSE;
[b7d3cc34]522        }
523
[e046390]524        phb->inpa = b_input_add(source, B_EV_IO_READ, s5_canread, phb);
[ba9edaa]525       
526        return FALSE;
[b7d3cc34]527}
528
[c3ffa45]529static int proxy_connect_socks5(const char *host, unsigned short port, struct PHB *phb)
[b7d3cc34]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
[ba9edaa]542int proxy_connect(const char *host, int port, b_event_handler func, gpointer data)
[b7d3cc34]543{
544        struct PHB *phb;
545       
[58a1449]546        if (!host || port <= 0 || !func || strlen(host) > 128) {
[b7d3cc34]547                return -1;
548        }
549       
550        phb = g_new0(struct PHB, 1);
551        phb->func = func;
552        phb->data = data;
553       
[58a1449]554        if (proxytype == PROXY_NONE || !proxyhost[0] || proxyport <= 0)
[b7d3cc34]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.