1/* 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"
13#include "android/sockets.h"
14#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"
20#include "android/iolooper.h"
21#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
534int
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}
580