1/* 2 * tcpproxy 3 * 4 * tcpproxy is a simple tcp connection proxy which combines the 5 * features of rinetd and 6tunnel. tcpproxy supports IPv4 and 6 * IPv6 and also supports connections from IPv6 to IPv4 7 * endpoints and vice versa. 8 * 9 * 10 * Copyright (C) 2010-2015 Christian Pointner <equinox@spreadspace.org> 11 * 12 * This file is part of tcpproxy. 13 * 14 * tcpproxy is free software: you can redistribute it and/or modify 15 * it under the terms of the GNU General Public License as published by 16 * the Free Software Foundation, either version 3 of the License, or 17 * any later version. 18 * 19 * tcpproxy is distributed in the hope that it will be useful, 20 * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * GNU General Public License for more details. 23 * 24 * You should have received a copy of the GNU General Public License 25 * along with tcpproxy. If not, see <http://www.gnu.org/licenses/>. 26 */ 27 28#include <stdlib.h> 29#include <string.h> 30#include <errno.h> 31#include <sys/types.h> 32#include <sys/stat.h> 33#include <fcntl.h> 34#include <unistd.h> 35#include <sys/mman.h> 36 37#include "datatypes.h" 38#include "log.h" 39#include "options.h" 40#include "tcp.h" 41#include "listener.h" 42 43struct listener { 44 char* la_; 45 resolv_type_t lrt_; 46 char* lp_; 47 char* ra_; 48 resolv_type_t rrt_; 49 char* rp_; 50 char* sa_; 51}; 52 53static void init_listener_struct(struct listener* l) 54{ 55 if(!l) return; 56 57 l->la_ = NULL; 58 l->lrt_ = ANY; 59 l->lp_ = NULL; 60 l->ra_ = NULL; 61 l->rrt_ = ANY; 62 l->rp_ = NULL; 63 l->sa_ = NULL; 64} 65 66static void clear_listener_struct(struct listener* l) 67{ 68 if(!l) return; 69 70 if(l->la_) 71 free(l->la_); 72 if(l->lp_) 73 free(l->lp_); 74 if(l->ra_) 75 free(l->ra_); 76 if(l->rp_) 77 free(l->rp_); 78 if(l->sa_) 79 free(l->sa_); 80 81 init_listener_struct(l); 82} 83 84static int owrt_string(char** dest, char* start, char* end) 85{ 86 if(!dest || start >= end) 87 return -1; 88 89 if(*dest) free(*dest); 90 int n = end - start; 91 *dest = malloc(n+1); 92 if(!(*dest)) 93 return -2; 94 95 memcpy(*dest, start, n); 96 (*dest)[n] = 0; 97 98 return 0; 99} 100 101%%{ 102 machine cfg_parser; 103 104 action set_cpy_start { cpy_start = fpc; } 105 action set_local_addr { ret = owrt_string(&(lst.la_), cpy_start, fpc); cpy_start = NULL; } 106 action set_local_port { ret = owrt_string(&(lst.lp_), cpy_start, fpc); cpy_start = NULL; } 107 action set_local_resolv4 { lst.lrt_ = IPV4_ONLY; } 108 action set_local_resolv6 { lst.lrt_ = IPV6_ONLY; } 109 action set_remote_addr { ret = owrt_string(&(lst.ra_), cpy_start, fpc); cpy_start = NULL; } 110 action set_remote_port { ret = owrt_string(&(lst.rp_), cpy_start, fpc); cpy_start = NULL; } 111 action set_remote_resolv4 { lst.rrt_ = IPV4_ONLY; } 112 action set_remote_resolv6 { lst.rrt_ = IPV6_ONLY; } 113 action set_source_addr { ret = owrt_string(&(lst.sa_), cpy_start, fpc); cpy_start = NULL; } 114 action add_listener { 115 ret = listeners_add(listener, lst.la_, lst.lrt_, lst.lp_, lst.ra_, lst.rrt_, lst.rp_, lst.sa_); 116 clear_listener_struct(&lst); 117 } 118 action logerror { 119 if(fpc == eof) 120 log_printf(ERROR, "config file syntax error: unexpected end of file"); 121 else 122 log_printf(ERROR, "config file syntax error at line %d", cur_line); 123 124 fgoto *cfg_parser_error; 125 } 126 127 newline = '\n' @{cur_line++;}; 128 ws = [ \t]; 129 comment = '#' [^\n]* newline; 130 ign = ( ws | comment | newline | [\v\f\r] ); 131 132 number = [0-9]+; 133 ipv4_addr = [0-9.]+; 134 ipv6_addr = [0-9a-fA-F:]+; 135 name = [a-zA-Z0-9\-]+; 136 host_name = [a-zA-Z0-9\-.]+; 137 tok_ipv4 = "ipv4"i; 138 tok_ipv6 = "ipv6"i; 139 140 host_or_addr = ( host_name | ipv4_addr | ipv6_addr ); 141 service = ( number | name ); 142 143 local_addr = ( '*' | host_or_addr >set_cpy_start %set_local_addr ); 144 local_port = service >set_cpy_start %set_local_port; 145 lresolv = ( tok_ipv4 @set_local_resolv4 | tok_ipv6 @set_local_resolv6 ); 146 147 remote_addr = host_or_addr >set_cpy_start %set_remote_addr; 148 remote_port = service >set_cpy_start %set_remote_port; 149 rresolv = ( tok_ipv4 @set_remote_resolv4 | tok_ipv6 @set_remote_resolv6 ); 150 151 source_addr = host_or_addr >set_cpy_start %set_source_addr; 152 153 resolv = "resolv" ws* ":" ws+ lresolv ws* ";"; 154 remote = "remote" ws* ":" ws+ remote_addr ws+ remote_port ws* ";"; 155 remote_resolv = "remote-resolv" ws* ":" ws+ rresolv ws* ";"; 156 source = "source" ws* ":" ws+ source_addr ws* ";"; 157 158 listen_head = 'listen' ws+ local_addr ws+ local_port; 159 listen_body = '{' ( ign+ | resolv | remote | remote_resolv | source )* '};' @add_listener; 160 161 main := ( listen_head ign* listen_body | ign+ )* $!logerror; 162}%% 163 164 165int parse_listener(char* p, char* pe, listeners_t* listener) 166{ 167 int cs, ret = 0, cur_line = 1; 168 169 %% write data; 170 %% write init; 171 172 char* cpy_start = NULL; 173 struct listener lst; 174 init_listener_struct(&lst); 175 176 char* eof = pe; 177 %% write exec; 178 179 if(cs == cfg_parser_error) { 180 listeners_revert(listener); 181 ret = 1; 182 } 183 else 184 ret = listeners_update(listener); 185 186 clear_listener_struct(&lst); 187 188 return ret; 189} 190 191int read_configfile(const char* filename, listeners_t* listener) 192{ 193 int fd = open(filename, 0); 194 if(fd < 0) { 195 log_printf(ERROR, "open('%s') failed: %s", filename, strerror(errno)); 196 return -1; 197 } 198 199 struct stat sb; 200 if(fstat(fd, &sb) == -1) { 201 log_printf(ERROR, "fstat() error: %s", strerror(errno)); 202 close(fd); 203 return -1; 204 } 205 206 if(!sb.st_size) { 207 log_printf(ERROR, "config file %s is empty", filename); 208 close(fd); 209 return -1; 210 } 211 212 if(!S_ISREG(sb.st_mode)) { 213 log_printf(ERROR, "config file %s is not a regular file", filename); 214 close(fd); 215 return -1; 216 } 217 218 char* p = (char*)mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); 219 if(p == MAP_FAILED) { 220 log_printf(ERROR, "mmap() error: %s", strerror(errno)); 221 close(fd); 222 return -1; 223 } 224 close(fd); 225 226 log_printf(DEBUG, "mapped %ld bytes from file %s at address 0x%08lX", sb.st_size, filename, p); 227 int ret = parse_listener(p, p + sb.st_size, listener); 228 229 if(munmap(p, sb.st_size) == -1) { 230 log_printf(ERROR, "munmap() error: %s", strerror(errno)); 231 return -1; 232 } 233 log_printf(DEBUG, "unmapped file %s", filename); 234 235 return ret; 236} 237