📄 tunnel.c
字号:
/*tunnel.cCopyright (C) 1999, 2000 Lars Brinkhoff. See COPYING for terms and conditions.See tunnel.h for some documentation about the programming interface.*/#include <time.h>#include <stdio.h>#include <netdb_.h>#include <fcntl.h>#include <stdlib.h>#include <sys/poll_.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/tcp.h>#include "http.h"#include "tunnel.h"#include "common.h"/* #define IO_COUNT_HTTP_HEADER *//* #define USE_SHUTDOWN */#define READ_TRAIL_TIMEOUT (1 * 1000) /* milliseconds */#define ACCEPT_TIMEOUT 10 /* seconds */#define min(a, b) ((a) < (b) ? (a) : (b))#define TUNNEL_IN 1#define TUNNEL_OUT 2#if SIZEOF_CHAR == 1typedef unsigned char Request;#else#error "FIXME: Can't handle SIZEOF_CHAR != 1"#endif#if SIZEOF_SHORT == 2typedef unsigned short Length;#else#error "FIXME: Can't handle SIZEOF_SHORT != 2"#endifenum tunnel_request{ TUNNEL_SIMPLE = 0x40, TUNNEL_OPEN = 0x01, TUNNEL_DATA = 0x02, TUNNEL_PADDING = 0x03, TUNNEL_ERROR = 0x04, TUNNEL_PAD1 = TUNNEL_SIMPLE | 0x05, TUNNEL_CLOSE = TUNNEL_SIMPLE | 0x06, TUNNEL_DISCONNECT = TUNNEL_SIMPLE | 0x07};static inline const char *REQ_TO_STRING (Request request){ switch (request) { case TUNNEL_OPEN: return "TUNNEL_OPEN"; case TUNNEL_DATA: return "TUNNEL_DATA"; case TUNNEL_PADDING: return "TUNNEL_PADDING"; case TUNNEL_ERROR: return "TUNNEL_ERROR"; case TUNNEL_PAD1: return "TUNNEL_PAD1"; case TUNNEL_CLOSE: return "TUNNEL_CLOSE"; case TUNNEL_DISCONNECT: return "TUNNEL_DISCONNECT"; default: return "(unknown)"; }}struct tunnel{ int in_fd, out_fd; int server_socket; Http_destination dest; struct sockaddr_in address; size_t bytes; size_t content_length; char buf[65536]; char *buf_ptr; size_t buf_len; int padding_only; size_t in_total_raw; size_t in_total_data; size_t out_total_raw; size_t out_total_data; time_t out_connect_time; int strict_content_length; int keep_alive; int max_connection_age;};static const size_t sizeof_header = sizeof (Request) + sizeof (Length);static inline inttunnel_is_disconnected (Tunnel *tunnel){ return tunnel->out_fd == -1;}static inline inttunnel_is_connected (Tunnel *tunnel){ return !tunnel_is_disconnected (tunnel);}static inline inttunnel_is_server (Tunnel *tunnel){ return tunnel->dest.host_name == NULL;}static inline inttunnel_is_client (Tunnel *tunnel){ return !tunnel_is_server (tunnel);}#if 1static intget_proto_number (const char *name){ struct protoent *p; int number; p = getprotobyname (name); if (p == NULL) number = -1; else number = p->p_proto; endprotoent (); return number;}#endifstatic inttunnel_in_setsockopts (int fd){#ifdef SO_RCVLOWAT int tcp = get_proto_number ("tcp"); if (tcp != -1) { int i, n; i = 1; if (setsockopt (fd, tcp, SO_RCVLOWAT, (void *)&i, sizeof i) == -1) { log_debug ("tunnel_in_setsockopts: non-fatal SO_RCVLOWAT error: %s", strerror (errno)); } n = sizeof i; getsockopt (fd, tcp, SO_RCVLOWAT, (void *)&i, &n); log_debug ("tunnel_out_setsockopts: SO_RCVLOWAT: %d", i); }#endif /* SO_RCVLOWAT */ return 0;}static inttunnel_out_setsockopts (int fd){#ifdef SO_SNDLOWAT { int tcp = get_proto_number ("tcp"); int i, n; if (tcp != -1) { i = 1; if (setsockopt (fd, tcp, SO_SNDLOWAT, (void *)&i, sizeof i) == -1) { log_debug ("tunnel_out_setsockopts: " "non-fatal SO_SNDLOWAT error: %s", strerror (errno)); } n = sizeof i; getsockopt (fd, tcp, SO_SNDLOWAT, (void *)&i, &n); log_debug ("tunnel_out_setsockopts: non-fatal SO_SNDLOWAT: %d", i); } }#endif /* SO_SNDLOWAT */#ifdef SO_LINGER { struct linger l; int n; l.l_onoff = 1; l.l_linger = 20 * 100; /* linger for 20 seconds */ if (setsockopt (fd, SOL_SOCKET, SO_LINGER, (void *)&l, sizeof l) == -1) { log_debug ("tunnel_out_setsockopts: non-fatal SO_LINGER error: %s", strerror (errno)); } n = sizeof l; getsockopt (fd, SOL_SOCKET, SO_LINGER, (void *)&l, &n); log_debug ("tunnel_out_setsockopts: SO_LINGER: onoff=%d linger=%d", l.l_onoff, l.l_linger); }#endif /* SO_LINGER */#ifdef TCP_NODELAY { int tcp = get_proto_number ("tcp"); int i, n; if (tcp != -1) { i = 1; if (setsockopt (fd, tcp, TCP_NODELAY, (void *)&i, sizeof i) == -1) { log_debug ("tunnel_out_setsockopts: " "non-fatal TCP_NODELAY error: %s", strerror (errno)); } n = sizeof i; getsockopt (fd, tcp, TCP_NODELAY, (void *)&i, &n); log_debug ("tunnel_out_setsockopts: non-fatal TCP_NODELAY: %d", i); } }#else#ifdef SO_SNDBUF { int i, n; i = 0; if (setsockopt (fd, SOL_SOCKET, SO_SNDBUF, (void *)&i, sizeof i) == -1) { log_debug ("tunnel_out_setsockopts: non-fatal SO_SNDBUF error: %s", strerror (errno)); } n = sizeof i; getsockopt (fd, SOL_SOCKET, SO_SNDBUF, (void *)&i, &n); log_debug ("tunnel_out_setsockopts: SO_SNDBUF: %d", i); }#endif /* SO_SNDBUF */#endif /* TCP_NODELAY */#ifdef SO_KEEPALIVE { int i, n; i = 1; if (setsockopt (fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&i, sizeof i) == -1) { log_debug ("tunnel_out_setsockopts: non-fatal SO_KEEPALIVE error: %s", strerror (errno)); } n = sizeof i; getsockopt (fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&i, &n); log_debug ("tunnel_out_setsockopts: SO_KEEPALIVE: %d", i); }#endif /* SO_KEEPALIVE */ return 0;}static voidtunnel_out_disconnect (Tunnel *tunnel){ if (tunnel_is_disconnected (tunnel)) return;#ifdef DEBUG_MODE if (tunnel_is_client (tunnel) && tunnel->bytes != tunnel->content_length + 1) log_error ("tunnel_out_disconnect: warning: " "bytes=%d != content_length=%d", tunnel->bytes, tunnel->content_length + 1);#endif close (tunnel->out_fd); tunnel->out_fd = -1; tunnel->bytes = 0; tunnel->buf_ptr = tunnel->buf; tunnel->buf_len = 0; log_debug ("tunnel_out_disconnect: output disconnected");}static voidtunnel_in_disconnect (Tunnel *tunnel){ if (tunnel->in_fd == -1) return; close (tunnel->in_fd); tunnel->in_fd = -1; log_debug ("tunnel_in_disconnect: input disconnected");}static inttunnel_out_connect (Tunnel *tunnel){ ssize_t n; if (tunnel_is_connected (tunnel)) { log_debug ("tunnel_out_connect: already connected"); tunnel_out_disconnect (tunnel); } tunnel->out_fd = do_connect (&tunnel->address); if (tunnel->out_fd == -1) { log_error ("tunnel_out_connect: do_connect(%d.%d.%d.%d) error: %s", tunnel->address.sin_addr.s_addr >> 24, (tunnel->address.sin_addr.s_addr >> 16) & 0xff, (tunnel->address.sin_addr.s_addr >> 8) & 0xff, tunnel->address.sin_addr.s_addr & 0xff, strerror (errno)); return -1; } tunnel_out_setsockopts (tunnel->out_fd);#ifdef USE_SHUTDOWN shutdown (tunnel->out_fd, 0);#endif /* + 1 to allow for TUNNEL_DISCONNECT */ n = http_post (tunnel->out_fd, &tunnel->dest, tunnel->content_length + 1); if (n == -1) return -1;#ifdef IO_COUNT_HTTP_HEADER tunnel->out_total_raw += n; log_annoying ("tunnel_out_connect: out_total_raw = %u", tunnel->out_total_raw);#endif tunnel->bytes = 0; tunnel->buf_ptr = tunnel->buf; tunnel->buf_len = 0; tunnel->padding_only = TRUE; time (&tunnel->out_connect_time); log_debug ("tunnel_out_connect: output connected"); return 0;}static inttunnel_in_connect (Tunnel *tunnel){ Http_response *response; ssize_t n; log_verbose ("tunnel_in_connect()"); if (tunnel->in_fd != -1) { log_error ("tunnel_in_connect: already connected"); return -1; } tunnel->in_fd = do_connect (&tunnel->address); if (tunnel->in_fd == -1) { log_error ("tunnel_in_connect: do_connect() error: %s", strerror (errno)); return -1; } tunnel_in_setsockopts (tunnel->in_fd); if (http_get (tunnel->in_fd, &tunnel->dest) == -1) return -1;#ifdef USE_SHUTDOWN if (shutdown (tunnel->in_fd, 1) == -1) { log_error ("tunnel_in_connect: shutdown() error: %s", strerror (errno)); return -1; }#endif n = http_parse_response (tunnel->in_fd, &response); if (n <= 0) { if (n == 0) log_error ("tunnel_in_connect: no response; peer " "closed connection"); else log_error ("tunnel_in_connect: no response; error: %s", strerror (errno)); } else if (response->major_version != 1 || (response->minor_version != 1 && response->minor_version != 0)) { log_error ("tunnel_in_connect: unknown HTTP version: %d.%d", response->major_version, response->minor_version); n = -1; } else if (response->status_code != 200) { log_error ("tunnel_in_connect: HTTP error %d", response->status_code); errno = http_error_to_errno (-response->status_code); n = -1; } http_destroy_response (response); if (n > 0) {#ifdef IO_COUNT_HTTP_HEADER tunnel->in_total_raw += n; log_annoying ("tunnel_in_connect: in_total_raw = %u", tunnel->in_total_raw);#endif } else { return n; } log_debug ("tunnel_in_connect: input connected"); return 1;}static inline ssize_t
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -