4 * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
5 * Copyright (C) 2002-2004, Wilmer van der Gaast, Jelmer Vernooij
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.
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.
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
27 #include <sys/types.h>
29 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
36 #define ETIMEDOUT WSAETIMEDOUT
37 #define EINPROGRESS WSAEINPROGRESS
45 char proxyhost
[128] = "";
47 int proxytype
= PROXY_NONE
;
48 char proxyuser
[128] = "";
49 char proxypass
[128] = "";
51 /* Some systems don't know this one. It's not essential, so set it to 0 then. */
52 #ifndef AI_NUMERICSERV
53 #define AI_NUMERICSERV 0
56 #define AI_ADDRCONFIG 0
60 b_event_handler func
, proxy_func
;
61 gpointer data
, proxy_data
;
66 struct addrinfo
*gai
, *gai_cur
;
69 static int proxy_connect_none(const char *host
, unsigned short port_
, struct PHB
*phb
);
71 static gboolean
gaim_io_connected(gpointer data
, gint source
, b_input_condition cond
)
73 struct PHB
*phb
= data
;
75 int error
= ETIMEDOUT
;
79 if (getsockopt(source
, SOL_SOCKET
, SO_ERROR
, &error
, &len
) < 0 || error
) {
80 if ((phb
->gai_cur
= phb
->gai_cur
->ai_next
)) {
82 b_event_remove(phb
->inpa
);
83 if ((new_fd
= proxy_connect_none(NULL
, 0, phb
))) {
84 b_event_remove(phb
->inpa
);
88 phb
->inpa
= b_input_add(source
, B_EV_IO_WRITE
, gaim_io_connected
, phb
);
92 freeaddrinfo(phb
->gai
);
94 b_event_remove(phb
->inpa
);
96 phb
->proxy_func(phb
->proxy_data
, -1, B_EV_IO_READ
);
98 phb
->func(phb
->data
, -1, B_EV_IO_READ
);
104 freeaddrinfo(phb
->gai
);
105 sock_make_blocking(source
);
106 b_event_remove(phb
->inpa
);
107 if( phb
->proxy_func
)
108 phb
->proxy_func(phb
->proxy_data
, source
, B_EV_IO_READ
);
110 phb
->func(phb
->data
, source
, B_EV_IO_READ
);
117 static int proxy_connect_none(const char *host
, unsigned short port_
, struct PHB
*phb
)
119 struct sockaddr_in me
;
122 if (phb
->gai_cur
== NULL
)
126 struct addrinfo hints
;
128 g_snprintf(port
, sizeof(port
), "%d", port_
);
130 memset(&hints
, 0, sizeof(struct addrinfo
));
131 hints
.ai_family
= AF_UNSPEC
;
132 hints
.ai_socktype
= SOCK_STREAM
;
133 hints
.ai_flags
= AI_ADDRCONFIG
| AI_NUMERICSERV
;
135 if (!(ret
= getaddrinfo(host
, port
, &hints
, &phb
->gai
)))
136 phb
->gai_cur
= phb
->gai
;
138 event_debug("gai(): %s\n", gai_strerror(ret
));
141 for (; phb
->gai_cur
; phb
->gai_cur
= phb
->gai_cur
->ai_next
)
143 if ((fd
= socket(phb
->gai_cur
->ai_family
, phb
->gai_cur
->ai_socktype
, phb
->gai_cur
->ai_protocol
)) < 0) {
144 event_debug( "socket failed: %d\n", errno
);
148 sock_make_nonblocking(fd
);
150 if (global
.conf
->iface_out
)
152 me
.sin_family
= AF_INET
;
154 me
.sin_addr
.s_addr
= inet_addr( global
.conf
->iface_out
);
156 if (bind(fd
, (struct sockaddr
*) &me
, sizeof(me
)) != 0)
157 event_debug("bind( %d, \"%s\" ) failure\n", fd
, global
.conf
->iface_out
);
160 event_debug("proxy_connect_none( \"%s\", %d ) = %d\n", host
, port
, fd
);
162 if (connect(fd
, phb
->gai_cur
->ai_addr
, phb
->gai_cur
->ai_addrlen
) < 0 && !sockerr_again()) {
163 event_debug( "connect failed: %s\n", strerror(errno
));
168 phb
->inpa
= b_input_add(fd
, B_EV_IO_WRITE
, gaim_io_connected
, phb
);
182 /* Connecting to HTTP proxies */
184 #define HTTP_GOODSTRING "HTTP/1.0 200 Connection established"
185 #define HTTP_GOODSTRING2 "HTTP/1.1 200 Connection established"
187 static gboolean
http_canread(gpointer data
, gint source
, b_input_condition cond
)
191 struct PHB
*phb
= data
;
192 char inputline
[8192];
194 b_event_remove(phb
->inpa
);
196 while ((pos
< sizeof(inputline
)-1) && (nlc
!= 2) && (read(source
, &inputline
[pos
++], 1) == 1)) {
197 if (inputline
[pos
- 1] == '\n')
199 else if (inputline
[pos
- 1] != '\r')
202 inputline
[pos
] = '\0';
204 if ((memcmp(HTTP_GOODSTRING
, inputline
, strlen(HTTP_GOODSTRING
)) == 0) ||
205 (memcmp(HTTP_GOODSTRING2
, inputline
, strlen(HTTP_GOODSTRING2
)) == 0)) {
206 phb
->func(phb
->data
, source
, B_EV_IO_READ
);
213 phb
->func(phb
->data
, -1, B_EV_IO_READ
);
220 static gboolean
http_canwrite(gpointer data
, gint source
, b_input_condition cond
)
223 struct PHB
*phb
= data
;
225 int error
= ETIMEDOUT
;
227 b_event_remove(phb
->inpa
);
229 if (getsockopt(source
, SOL_SOCKET
, SO_ERROR
, &error
, &len
) < 0) {
231 phb
->func(phb
->data
, -1, B_EV_IO_READ
);
236 sock_make_blocking(source
);
238 g_snprintf(cmd
, sizeof(cmd
), "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n", phb
->host
, phb
->port
,
239 phb
->host
, phb
->port
);
240 if (send(source
, cmd
, strlen(cmd
), 0) < 0) {
242 phb
->func(phb
->data
, -1, B_EV_IO_READ
);
248 if (strlen(proxyuser
) > 0) {
250 t1
= g_strdup_printf("%s:%s", proxyuser
, proxypass
);
253 g_snprintf(cmd
, sizeof(cmd
), "Proxy-Authorization: Basic %s\r\n", t2
);
255 if (send(source
, cmd
, strlen(cmd
), 0) < 0) {
257 phb
->func(phb
->data
, -1, B_EV_IO_READ
);
264 g_snprintf(cmd
, sizeof(cmd
), "\r\n");
265 if (send(source
, cmd
, strlen(cmd
), 0) < 0) {
267 phb
->func(phb
->data
, -1, B_EV_IO_READ
);
273 phb
->inpa
= b_input_add(source
, B_EV_IO_READ
, http_canread
, phb
);
278 static int proxy_connect_http(const char *host
, unsigned short port
, struct PHB
*phb
)
280 phb
->host
= g_strdup(host
);
282 phb
->proxy_func
= http_canwrite
;
283 phb
->proxy_data
= phb
;
285 return( proxy_connect_none( proxyhost
, proxyport
, phb
) );
289 /* Connecting to SOCKS4 proxies */
291 static gboolean
s4_canread(gpointer data
, gint source
, b_input_condition cond
)
293 unsigned char packet
[12];
294 struct PHB
*phb
= data
;
296 b_event_remove(phb
->inpa
);
298 memset(packet
, 0, sizeof(packet
));
299 if (read(source
, packet
, 9) >= 4 && packet
[1] == 90) {
300 phb
->func(phb
->data
, source
, B_EV_IO_READ
);
307 phb
->func(phb
->data
, -1, B_EV_IO_READ
);
314 static gboolean
s4_canwrite(gpointer data
, gint source
, b_input_condition cond
)
316 unsigned char packet
[12];
318 struct PHB
*phb
= data
;
320 int error
= ETIMEDOUT
;
322 b_event_remove(phb
->inpa
);
324 if (getsockopt(source
, SOL_SOCKET
, SO_ERROR
, &error
, &len
) < 0) {
326 phb
->func(phb
->data
, -1, B_EV_IO_READ
);
331 sock_make_blocking(source
);
333 /* XXX does socks4 not support host name lookups by the proxy? */
334 if (!(hp
= gethostbyname(phb
->host
))) {
336 phb
->func(phb
->data
, -1, B_EV_IO_READ
);
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];
351 if (write(source
, packet
, 9) != 9) {
353 phb
->func(phb
->data
, -1, B_EV_IO_READ
);
359 phb
->inpa
= b_input_add(source
, B_EV_IO_READ
, s4_canread
, phb
);
364 static int proxy_connect_socks4(const char *host
, unsigned short port
, struct PHB
*phb
)
366 phb
->host
= g_strdup(host
);
368 phb
->proxy_func
= s4_canwrite
;
369 phb
->proxy_data
= phb
;
371 return( proxy_connect_none( proxyhost
, proxyport
, phb
) );
375 /* Connecting to SOCKS5 proxies */
377 static gboolean
s5_canread_again(gpointer data
, gint source
, b_input_condition cond
)
379 unsigned char buf
[512];
380 struct PHB
*phb
= data
;
382 b_event_remove(phb
->inpa
);
384 if (read(source
, buf
, 10) < 10) {
386 phb
->func(phb
->data
, -1, B_EV_IO_READ
);
391 if ((buf
[0] != 0x05) || (buf
[1] != 0x00)) {
393 phb
->func(phb
->data
, -1, B_EV_IO_READ
);
399 phb
->func(phb
->data
, source
, B_EV_IO_READ
);
406 static void s5_sendconnect(gpointer data
, gint source
)
408 unsigned char buf
[512];
409 struct PHB
*phb
= data
;
410 int hlen
= strlen(phb
->host
);
413 buf
[1] = 0x01; /* CONNECT */
414 buf
[2] = 0x00; /* reserved */
415 buf
[3] = 0x03; /* address type -- host name */
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;
421 if (write(source
, buf
, (5 + strlen(phb
->host
) + 2)) < (5 + strlen(phb
->host
) + 2)) {
423 phb
->func(phb
->data
, -1, B_EV_IO_READ
);
429 phb
->inpa
= b_input_add(source
, B_EV_IO_READ
, s5_canread_again
, phb
);
432 static gboolean
s5_readauth(gpointer data
, gint source
, b_input_condition cond
)
434 unsigned char buf
[512];
435 struct PHB
*phb
= data
;
437 b_event_remove(phb
->inpa
);
439 if (read(source
, buf
, 2) < 2) {
441 phb
->func(phb
->data
, -1, B_EV_IO_READ
);
447 if ((buf
[0] != 0x01) || (buf
[1] != 0x00)) {
449 phb
->func(phb
->data
, -1, B_EV_IO_READ
);
455 s5_sendconnect(phb
, source
);
460 static gboolean
s5_canread(gpointer data
, gint source
, b_input_condition cond
)
462 unsigned char buf
[512];
463 struct PHB
*phb
= data
;
465 b_event_remove(phb
->inpa
);
467 if (read(source
, buf
, 2) < 2) {
469 phb
->func(phb
->data
, -1, B_EV_IO_READ
);
475 if ((buf
[0] != 0x05) || (buf
[1] == 0xff)) {
477 phb
->func(phb
->data
, -1, B_EV_IO_READ
);
483 if (buf
[1] == 0x02) {
484 unsigned int i
= strlen(proxyuser
), j
= strlen(proxypass
);
485 buf
[0] = 0x01; /* version 1 */
487 memcpy(buf
+ 2, proxyuser
, i
);
489 memcpy(buf
+ 2 + i
+ 1, proxypass
, j
);
490 if (write(source
, buf
, 3 + i
+ j
) < 3 + i
+ j
) {
492 phb
->func(phb
->data
, -1, B_EV_IO_READ
);
498 phb
->inpa
= b_input_add(source
, B_EV_IO_READ
, s5_readauth
, phb
);
500 s5_sendconnect(phb
, source
);
506 static gboolean
s5_canwrite(gpointer data
, gint source
, b_input_condition cond
)
508 unsigned char buf
[512];
510 struct PHB
*phb
= data
;
512 int error
= ETIMEDOUT
;
514 b_event_remove(phb
->inpa
);
516 if (getsockopt(source
, SOL_SOCKET
, SO_ERROR
, &error
, &len
) < 0) {
518 phb
->func(phb
->data
, -1, B_EV_IO_READ
);
523 sock_make_blocking(source
);
526 buf
[0] = 0x05; /* SOCKS version 5 */
528 buf
[1] = 0x02; /* two methods */
529 buf
[2] = 0x00; /* no authentication */
530 buf
[3] = 0x02; /* username/password authentication */
538 if (write(source
, buf
, i
) < i
) {
540 phb
->func(phb
->data
, -1, B_EV_IO_READ
);
546 phb
->inpa
= b_input_add(source
, B_EV_IO_READ
, s5_canread
, phb
);
551 static int proxy_connect_socks5(const char *host
, unsigned short port
, struct PHB
*phb
)
553 phb
->host
= g_strdup(host
);
555 phb
->proxy_func
= s5_canwrite
;
556 phb
->proxy_data
= phb
;
558 return( proxy_connect_none( proxyhost
, proxyport
, phb
) );
562 /* Export functions */
564 int proxy_connect(const char *host
, int port
, b_event_handler func
, gpointer data
)
568 if (!host
|| port
<= 0 || !func
|| strlen(host
) > 128) {
572 phb
= g_new0(struct PHB
, 1);
576 if (proxytype
== PROXY_NONE
|| !proxyhost
[0] || proxyport
<= 0)
577 return proxy_connect_none(host
, port
, phb
);
578 else if (proxytype
== PROXY_HTTP
)
579 return proxy_connect_http(host
, port
, phb
);
580 else if (proxytype
== PROXY_SOCKS4
)
581 return proxy_connect_socks4(host
, port
, phb
);
582 else if (proxytype
== PROXY_SOCKS5
)
583 return proxy_connect_socks5(host
, port
, phb
);