source: lib/proxy.c @ a6b00fc

Last change on this file since a6b00fc was 5ebff60, checked in by dequis <dx@…>, at 2015-02-20T22:50:54Z

Reindent everything to K&R style with tabs

Used uncrustify, with the configuration file in ./doc/uncrustify.cfg

Commit author set to "Indent <please@…>" so that it's easier to
skip while doing git blame.

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