blob: 543433b89aa53281b1ddaa48923505a986600c3c [file] [log] [blame]
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001/* Copyright (C) 2007-2008 The Android Open Source Project
2**
3** This software is licensed under the terms of the GNU General Public
4** License version 2, as published by the Free Software Foundation, and
5** may be copied, distributed, and modified under those terms.
6**
7** This program is distributed in the hope that it will be useful,
8** but WITHOUT ANY WARRANTY; without even the implied warranty of
9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10** GNU General Public License for more details.
11*/
12#include "proxy_int.h"
David 'Digit' Turnercc330d42013-12-14 23:26:42 +010013#include "android/sockets.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080014#include <stdarg.h>
15#include <stdio.h>
16#include <string.h>
17#include <errno.h>
18#include "android/utils/misc.h"
19#include "android/utils/system.h"
David 'Digit' Turnerd413fa52013-12-14 23:35:20 +010020#include "android/iolooper.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080021#include <stdlib.h>
22
23int proxy_log = 0;
24
25void
26proxy_LOG(const char* fmt, ...)
27{
28 va_list args;
29 va_start(args, fmt);
30 vfprintf(stderr, fmt, args);
31 va_end(args);
32 fprintf(stderr, "\n");
33}
34
35void
36proxy_set_verbose(int mode)
37{
38 proxy_log = mode;
39}
40
41/** Global connection list
42 **/
43
44static ProxyConnection s_connections[1];
45
46#define MAX_HEX_DUMP 512
47
48static void
49hex_dump( void* base, int size, const char* prefix )
50{
51 STRALLOC_DEFINE(s);
52 if (size > MAX_HEX_DUMP)
53 size = MAX_HEX_DUMP;
54 stralloc_add_hexdump(s, base, size, prefix);
55 proxy_LOG( "%s", stralloc_cstr(s) );
56 stralloc_reset(s);
57}
58
59void
60proxy_connection_init( ProxyConnection* conn,
61 int socket,
62 SockAddress* address,
63 ProxyService* service,
64 ProxyConnectionFreeFunc conn_free,
65 ProxyConnectionSelectFunc conn_select,
66 ProxyConnectionPollFunc conn_poll )
67{
68 conn->socket = socket;
69 conn->address = address[0];
70 conn->service = service;
71 conn->next = NULL;
72
73 conn->conn_free = conn_free;
74 conn->conn_select = conn_select;
75 conn->conn_poll = conn_poll;
76
77 socket_set_nonblock(socket);
78
79 {
80 SocketType type = socket_get_type(socket);
81
82 snprintf( conn->name, sizeof(conn->name),
83 "%s:%s(%d)",
84 (type == SOCKET_STREAM) ? "tcp" : "udp",
85 sock_address_to_string(address), socket );
86
87 /* just in case */
88 conn->name[sizeof(conn->name)-1] = 0;
89 }
90
91 stralloc_reset(conn->str);
92 conn->str_pos = 0;
93}
94
95void
96proxy_connection_done( ProxyConnection* conn )
97{
98 stralloc_reset( conn->str );
99 if (conn->socket >= 0) {
100 socket_close(conn->socket);
101 conn->socket = -1;
102 }
103}
104
105
106void
107proxy_connection_rewind( ProxyConnection* conn )
108{
109 stralloc_t* str = conn->str;
110
111 /* only keep a small buffer in the heap */
112 conn->str_pos = 0;
113 str->n = 0;
114 if (str->a > 1024)
115 stralloc_reset(str);
116}
117
118DataStatus
119proxy_connection_send( ProxyConnection* conn, int fd )
120{
121 stralloc_t* str = conn->str;
122 int avail = str->n - conn->str_pos;
123
124 conn->str_sent = 0;
125
126 if (avail <= 0)
127 return 1;
128
129 if (proxy_log) {
130 PROXY_LOG("%s: sending %d bytes:", conn->name, avail );
131 hex_dump( str->s + conn->str_pos, avail, ">> " );
132 }
133
134 while (avail > 0) {
135 int n = socket_send(fd, str->s + conn->str_pos, avail);
136 if (n == 0) {
137 PROXY_LOG("%s: connection reset by peer (send)",
138 conn->name);
139 return DATA_ERROR;
140 }
141 if (n < 0) {
142 if (errno == EWOULDBLOCK || errno == EAGAIN)
143 return DATA_NEED_MORE;
144
145 PROXY_LOG("%s: error: %s", conn->name, errno_str);
146 return DATA_ERROR;
147 }
148 conn->str_pos += n;
149 conn->str_sent += n;
150 avail -= n;
151 }
152
153 proxy_connection_rewind(conn);
154 return DATA_COMPLETED;
155}
156
157
158DataStatus
159proxy_connection_receive( ProxyConnection* conn, int fd, int wanted )
160{
161 stralloc_t* str = conn->str;
162
163 conn->str_recv = 0;
164
165 while (wanted > 0) {
166 int n;
167
168 stralloc_readyplus( str, wanted );
169 n = socket_recv(fd, str->s + str->n, wanted);
170 if (n == 0) {
171 PROXY_LOG("%s: connection reset by peer (receive)",
172 conn->name);
173 return DATA_ERROR;
174 }
175 if (n < 0) {
176 if (errno == EWOULDBLOCK || errno == EAGAIN)
177 return DATA_NEED_MORE;
178
179 PROXY_LOG("%s: error: %s", conn->name, errno_str);
180 return DATA_ERROR;
181 }
182
183 if (proxy_log) {
184 PROXY_LOG("%s: received %d bytes:", conn->name, n );
185 hex_dump( str->s + str->n, n, "<< " );
186 }
187
188 str->n += n;
189 wanted -= n;
190 conn->str_recv += n;
191 }
192 return DATA_COMPLETED;
193}
194
195
196DataStatus
197proxy_connection_receive_line( ProxyConnection* conn, int fd )
198{
199 stralloc_t* str = conn->str;
200
201 for (;;) {
202 char c;
203 int n = socket_recv(fd, &c, 1);
204 if (n == 0) {
205 PROXY_LOG("%s: disconnected from server", conn->name );
206 return DATA_ERROR;
207 }
208 if (n < 0) {
209 if (errno == EWOULDBLOCK || errno == EAGAIN) {
210 PROXY_LOG("%s: blocked", conn->name);
211 return DATA_NEED_MORE;
212 }
213 PROXY_LOG("%s: error: %s", conn->name, errno_str);
214 return DATA_ERROR;
215 }
216
217 stralloc_add_c(str, c);
218 if (c == '\n') {
219 str->s[--str->n] = 0;
220 if (str->n > 0 && str->s[str->n-1] == '\r')
221 str->s[--str->n] = 0;
222
223 PROXY_LOG("%s: received '%s'", conn->name,
224 quote_bytes(str->s, str->n));
225 return DATA_COMPLETED;
226 }
227 }
228}
229
230static void
231proxy_connection_insert( ProxyConnection* conn, ProxyConnection* after )
232{
233 conn->next = after->next;
234 after->next->prev = conn;
235 after->next = conn;
236 conn->prev = after;
237}
238
239static void
240proxy_connection_remove( ProxyConnection* conn )
241{
242 conn->prev->next = conn->next;
243 conn->next->prev = conn->prev;
244
245 conn->next = conn->prev = conn;
246}
247
248/** Global service list
249 **/
250
251#define MAX_SERVICES 4
252
253static ProxyService* s_services[ MAX_SERVICES ];
254static int s_num_services;
255static int s_init;
256
257static void proxy_manager_atexit( void );
258
259static void
260proxy_manager_init(void)
261{
262 s_init = 1;
263 s_connections->next = s_connections;
264 s_connections->prev = s_connections;
265 atexit( proxy_manager_atexit );
266}
267
268
269extern int
270proxy_manager_add_service( ProxyService* service )
271{
272 if (!service || s_num_services >= MAX_SERVICES)
273 return -1;
274
275 if (!s_init)
276 proxy_manager_init();
277
278 s_services[s_num_services++] = service;
279 return 0;
280}
281
282
283extern void
284proxy_manager_atexit( void )
285{
286 ProxyConnection* conn = s_connections->next;
287 int n;
288
289 /* free all proxy connections */
290 while (conn != s_connections) {
291 ProxyConnection* next = conn->next;
292 conn->conn_free( conn );
293 conn = next;
294 }
295 conn->next = conn;
296 conn->prev = conn;
297
298 /* free all proxy services */
299 for (n = s_num_services; n-- > 0;) {
300 ProxyService* service = s_services[n];
301 service->serv_free( service->opaque );
302 }
303 s_num_services = 0;
304}
305
306
307void
308proxy_connection_free( ProxyConnection* conn,
309 int keep_alive,
310 ProxyEvent event )
311{
312 if (conn) {
313 int fd = conn->socket;
314
315 proxy_connection_remove(conn);
316
317 if (event != PROXY_EVENT_NONE)
318 conn->ev_func( conn->ev_opaque, fd, event );
319
320 if (keep_alive)
321 conn->socket = -1;
322
323 conn->conn_free(conn);
324 }
325}
326
327
328int
329proxy_manager_add( SockAddress* address,
330 SocketType sock_type,
331 ProxyEventFunc ev_func,
332 void* ev_opaque )
333{
334 int n;
335
336 if (!s_init) {
337 proxy_manager_init();
338 }
339
340 for (n = 0; n < s_num_services; n++) {
341 ProxyService* service = s_services[n];
342 ProxyConnection* conn = service->serv_connect( service->opaque,
343 sock_type,
344 address );
345 if (conn != NULL) {
346 conn->ev_func = ev_func;
347 conn->ev_opaque = ev_opaque;
348 proxy_connection_insert(conn, s_connections->prev);
349 return 0;
350 }
351 }
352 return -1;
353}
354
355
356/* remove an on-going proxified socket connection from the manager's list.
357 * this is only necessary when the socket connection must be canceled before
358 * the connection accept/refusal occured
359 */
360void
361proxy_manager_del( void* ev_opaque )
362{
363 ProxyConnection* conn = s_connections->next;
364 for ( ; conn != s_connections; conn = conn->next ) {
365 if (conn->ev_opaque == ev_opaque) {
366 proxy_connection_remove(conn);
367 conn->conn_free(conn);
368 return;
369 }
370 }
371}
372
373void
374proxy_select_set( ProxySelect* sel,
375 int fd,
376 unsigned flags )
377{
378 if (fd < 0 || !flags)
379 return;
380
381 if (*sel->pcount < fd+1)
382 *sel->pcount = fd+1;
383
384 if (flags & PROXY_SELECT_READ) {
385 FD_SET( fd, sel->reads );
386 } else {
387 FD_CLR( fd, sel->reads );
388 }
389 if (flags & PROXY_SELECT_WRITE) {
390 FD_SET( fd, sel->writes );
391 } else {
392 FD_CLR( fd, sel->writes );
393 }
394 if (flags & PROXY_SELECT_ERROR) {
395 FD_SET( fd, sel->errors );
396 } else {
397 FD_CLR( fd, sel->errors );
398 }
399}
400
401unsigned
402proxy_select_poll( ProxySelect* sel, int fd )
403{
404 unsigned flags = 0;
405
406 if (fd >= 0) {
407 if ( FD_ISSET(fd, sel->reads) )
408 flags |= PROXY_SELECT_READ;
409 if ( FD_ISSET(fd, sel->writes) )
410 flags |= PROXY_SELECT_WRITE;
411 if ( FD_ISSET(fd, sel->errors) )
412 flags |= PROXY_SELECT_ERROR;
413 }
414 return flags;
415}
416
417/* this function is called to update the select file descriptor sets
418 * with those of the proxified connection sockets that are currently managed */
419void
420proxy_manager_select_fill( int *pcount, fd_set* read_fds, fd_set* write_fds, fd_set* err_fds)
421{
422 ProxyConnection* conn;
423 ProxySelect sel[1];
424
425 if (!s_init)
426 proxy_manager_init();
427
428 sel->pcount = pcount;
429 sel->reads = read_fds;
430 sel->writes = write_fds;
431 sel->errors = err_fds;
432
433 conn = s_connections->next;
434 while (conn != s_connections) {
435 ProxyConnection* next = conn->next;
436 conn->conn_select(conn, sel);
437 conn = next;
438 }
439}
440
441/* this function is called to act on proxified connection sockets when network events arrive */
442void
443proxy_manager_poll( fd_set* read_fds, fd_set* write_fds, fd_set* err_fds )
444{
445 ProxyConnection* conn = s_connections->next;
446 ProxySelect sel[1];
447
448 sel->pcount = NULL;
449 sel->reads = read_fds;
450 sel->writes = write_fds;
451 sel->errors = err_fds;
452
453 while (conn != s_connections) {
454 ProxyConnection* next = conn->next;
455 conn->conn_poll( conn, sel );
456 conn = next;
457 }
458}
459
460
461int
462proxy_base64_encode( const char* src, int srclen,
463 char* dst, int dstlen )
464{
465 static const char cb64[64]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
466 const char* srcend = src + srclen;
467 int result = 0;
468
469 while (src+3 <= srcend && result+4 <= dstlen)
470 {
471 dst[result+0] = cb64[ src[0] >> 2 ];
472 dst[result+1] = cb64[ ((src[0] & 3) << 4) | ((src[1] & 0xf0) >> 4) ];
473 dst[result+2] = cb64[ ((src[1] & 0xf) << 2) | ((src[2] & 0xc0) >> 6) ];
474 dst[result+3] = cb64[ src[2] & 0x3f ];
475 src += 3;
476 result += 4;
477 }
478
479 if (src < srcend) {
480 unsigned char in[4];
481
482 if (result+4 > dstlen)
483 return -1;
484
485 in[0] = src[0];
486 in[1] = src+1 < srcend ? src[1] : 0;
487 in[2] = src+2 < srcend ? src[2] : 0;
488
489 dst[result+0] = cb64[ in[0] >> 2 ];
490 dst[result+1] = cb64[ ((in[0] & 3) << 4) | ((in[1] & 0xf0) >> 4) ];
491 dst[result+2] = (unsigned char) (src+1 < srcend ? cb64[ ((in[1] & 0xf) << 2) | ((in[2] & 0xc0) >> 6) ] : '=');
492 dst[result+3] = (unsigned char) (src+2 < srcend ? cb64[ in[2] & 0x3f ] : '=');
493 result += 4;
494 }
495 return result;
496}
497
498int
499proxy_resolve_server( SockAddress* addr,
500 const char* servername,
501 int servernamelen,
502 int serverport )
503{
504 char name0[64], *name = name0;
505 int result = -1;
506
507 if (servernamelen < 0)
508 servernamelen = strlen(servername);
509
510 if (servernamelen >= sizeof(name0)) {
511 AARRAY_NEW(name, servernamelen+1);
512 }
513
514 memcpy(name, servername, servernamelen);
515 name[servernamelen] = 0;
516
517 if (sock_address_init_resolve( addr, name, serverport, 0 ) < 0) {
518 PROXY_LOG("%s: can't resolve proxy server name '%s'",
519 __FUNCTION__, name);
520 goto Exit;
521 }
522
523 PROXY_LOG("server name '%s' resolved to %s", name, sock_address_to_string(addr));
524 result = 0;
525
526Exit:
527 if (name != name0)
528 AFREE(name);
529
530 return result;
531}
532
533
David 'Digit' Turner9b98dbd2010-07-30 15:35:00 -0700534int
535proxy_check_connection( const char* proxyname,
536 int proxyname_len,
537 int proxyport,
538 int timeout_ms )
539{
540 SockAddress addr;
541 int sock;
542 IoLooper* looper;
543 int ret;
544
545 if (proxy_resolve_server(&addr, proxyname, proxyname_len, proxyport) < 0) {
546 return -1;
547 }
548
549 sock = socket_create(addr.family, SOCKET_STREAM);
550 if (sock < 0) {
551 PROXY_LOG("%s: Could not create socket !?: %s", __FUNCTION__, errno_str);
552 return -1;
553 }
554
555 socket_set_nonblock(sock);
556
557 /* An immediate connection is very unlikely, but deal with it, just in case */
558 if (socket_connect(sock, &addr) == 0) {
559 PROXY_LOG("%s: Immediate connection to %.*s:%d: %s !",
560 __FUNCTION__, proxyname_len, proxyname, proxyport);
561 socket_close(sock);
562 return 0;
563 }
564
565 /* Ok, create an IoLooper object to wait for the connection */
566 looper = iolooper_new();
567 iolooper_add_write(looper, sock);
568
569 ret = iolooper_wait(looper, timeout_ms);
570
571 iolooper_free(looper);
572 socket_close(sock);
573
574 if (ret == 0) {
575 errno = ETIMEDOUT;
576 ret = -1;
577 }
578 return ret;
579}