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