From 4b3ea5b714adf374d9ac7af009b8f7b8a65f21f0 Mon Sep 17 00:00:00 2001 From: Christian Pointner Date: Wed, 8 Dec 2010 21:25:26 +0000 Subject: flex/bison vs ragel git-svn-id: https://svn.spreadspace.org/tcpproxy/trunk@38 e61f0598-a718-4e21-a8f0-0aadfa62ad6b --- README | 3 +- src/Makefile | 15 +-- src/cfg_parse.y | 273 ------------------------------------------------------ src/cfg_parser.h | 36 +++++++ src/cfg_parser.rl | 216 ++++++++++++++++++++++++++++++++++++++++++ src/cfg_scan.lex | 62 ------------- src/configure | 3 +- src/tcpproxy.c | 35 ++----- 8 files changed, 268 insertions(+), 375 deletions(-) delete mode 100644 src/cfg_parse.y create mode 100644 src/cfg_parser.h create mode 100644 src/cfg_parser.rl delete mode 100644 src/cfg_scan.lex diff --git a/README b/README index d3e3545..608c43e 100644 --- a/README +++ b/README @@ -14,8 +14,7 @@ Linux core: build-essential - flex - bison + ragel if you want to rebuild the manpage: asciidoc diff --git a/src/Makefile b/src/Makefile index 5a4cebf..387fbec 100644 --- a/src/Makefile +++ b/src/Makefile @@ -33,8 +33,7 @@ EXECUTABLE := tcpproxy C_OBJS := log.o \ options.o \ - cfg_scan.o \ - cfg_parse.o \ + cfg_parser.o \ slist.o \ string_list.o \ sig_handler.o \ @@ -49,13 +48,8 @@ C_SRCS := $(C_OBJS:%.o=%.c) all: $(EXECUTABLE) -cfg_scan.c: cfg_scan.lex cfg_parse.h - $(FLEX) -Cem -o $@ cfg_scan.lex - -cfg_parse.h: cfg_parse.c - -cfg_parse.c: cfg_parse.y - $(BISON) -d -o $@ $< +cfg_parser.c: cfg_parser.rl + $(RAGEL) -C -G2 -o $@ $< %.d: %.c @set -e; rm -f $@; \ @@ -87,8 +81,7 @@ clean: rm -f *.o rm -f *.d rm -f *.d.* - rm -f cfg_scan.c - rm -f cfg_parse.c cfg_parse.h + rm -f cfg_parser.c rm -f $(EXECUTABLE) cleanall: clean diff --git a/src/cfg_parse.y b/src/cfg_parse.y deleted file mode 100644 index a18abd5..0000000 --- a/src/cfg_parse.y +++ /dev/null @@ -1,273 +0,0 @@ -%{ -/* - * tcpproxy - * - * tcpproxy is a simple tcp connection proxy which combines the - * features of rinetd and 6tunnel. tcpproxy supports IPv4 and - * IPv6 and also supports connections from IPv6 to IPv4 - * endpoints and vice versa. - * - * - * Copyright (C) 2010-2011 Christian Pointner - * - * This file is part of tcpproxy. - * - * tcpproxy is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * tcpproxy is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with tcpproxy. If not, see . - */ - -#include -#include - -#include "datatypes.h" -#include "log.h" -#include "options.h" -#include "tcp.h" -#include "listener.h" - -void yyerror(const char *); -int yylex(void); - -int line_cnt = 1; -const char* config_file_; -listeners_t* glisteners; - -struct listener { - char* la_; - resolv_type_t lrt_; - char* lp_; - char* ra_; - resolv_type_t rrt_; - char* rp_; - char* sa_; -}; - -static struct listener* new_listener_struct() -{ - struct listener* l = malloc(sizeof(struct listener)); - l->la_ = NULL; - l->lrt_ = ANY; - l->lp_ = NULL; - l->ra_ = NULL; - l->rrt_ = ANY; - l->rp_ = NULL; - l->sa_ = NULL; -} - -static void clear_listener_struct(struct listener* l) -{ - if(l->la_) - free(l->la_); - if(l->lp_) - free(l->lp_); - if(l->ra_) - free(l->ra_); - if(l->rp_) - free(l->rp_); - if(l->sa_) - free(l->sa_); - - free(l); -} - -static void merge_listener_struct(struct listener* dest, struct listener* src) -{ - if(src->la_) { - if(dest->la_) - free(dest->la_); - dest->la_ = src->la_; - } - - if(src->lrt_ != ANY) - dest->lrt_ = src->lrt_; - - if(src->lp_) { - if(dest->lp_) - free(dest->lp_); - dest->lp_ = src->lp_; - } - - if(src->ra_) { - if(dest->ra_) - free(dest->ra_); - dest->ra_ = src->ra_; - } - - if(src->rrt_ != ANY) - dest->rrt_ = src->rrt_; - - if(src->rp_) { - if(dest->rp_) - free(dest->rp_); - dest->rp_ = src->rp_; - } - - if(src->sa_) { - if(dest->sa_) - free(dest->sa_); - dest->sa_ = src->sa_; - } - - free(src); -} - - -void yyinit(const char* config_file, listeners_t* listeners) -{ - config_file_ = config_file; - glisteners = listeners; -} - -%} - -%union -{ - char null; - char *string; - int rtype; - struct listener* listener; -} - -%token TOK_OPEN; -%token TOK_CLOSE; -%token TOK_COLON; -%token TOK_SEMICOLON; -%token TOK_ASTERISK; -%token TOK_LISTEN; -%token TOK_RESOLV; -%token TOK_REMOTE; -%token TOK_REMOTE_RESOLV; -%token TOK_SOURCE; -%token TOK_IPV4; -%token TOK_IPV6; -%token TOK_NUMBER; -%token TOK_IPV4_ADDR; -%token TOK_IPV6_ADDR; -%token TOK_NAME; -%token TOK_HOSTNAME; - -%type listen -%type listen_head -%type listen_body -%type listen_body_element -%type remote -%type source -%type resolv -%type remote_resolv -%type resolv_type -%type service -%type host_or_addr - -%error-verbose - -%% -cfg: - | cfg listen - ; - -listen: listen_head TOK_OPEN listen_body TOK_CLOSE TOK_SEMICOLON -{ - merge_listener_struct($1, $3); - - int ret = listener_add(glisteners, $1->la_, $1->lrt_, $1->lp_, $1->ra_, $1->rrt_, $1->rp_, $1->sa_); - clear_listener_struct($1); - if(ret) { - YYABORT; - } -} -; - -listen_head: TOK_LISTEN host_or_addr service -{ - struct listener* l = new_listener_struct(); - l->la_ = $2; - l->lp_ = $3; - $$ = l; -} - | TOK_LISTEN TOK_ASTERISK service -{ - struct listener* l = new_listener_struct(); - l->lp_ = $3; - $$ = l; -} -; - -listen_body: listen_body_element - | listen_body listen_body_element -{ - merge_listener_struct($1, $2); - $$ = $1; -} -; - -listen_body_element: resolv TOK_SEMICOLON - | remote TOK_SEMICOLON - | remote_resolv TOK_SEMICOLON - | source TOK_SEMICOLON - ; - -remote: TOK_REMOTE TOK_COLON host_or_addr service -{ - struct listener* l = new_listener_struct(); - l->ra_ = $3; - l->rp_ = $4; - $$ = l; -} -; - -source: TOK_SOURCE TOK_COLON host_or_addr -{ - struct listener* l = new_listener_struct(); - l->sa_ = $3; - $$ = l; -} -; - -resolv: TOK_RESOLV TOK_COLON resolv_type -{ - struct listener* l = new_listener_struct(); - l->lrt_ = $3; - $$ = l; -} -; - -remote_resolv: TOK_REMOTE_RESOLV TOK_COLON resolv_type -{ - struct listener* l = new_listener_struct(); - l->rrt_ = $3; - $$ = l; -} -; - - -resolv_type: TOK_IPV4 - | TOK_IPV6 - ; - -service: TOK_NUMBER - | TOK_NAME - ; - -host_or_addr: TOK_NUMBER - | TOK_NAME - | TOK_HOSTNAME - | TOK_IPV4_ADDR - | TOK_IPV6_ADDR - ; -%% - -void yyerror (const char *string) -{ - log_printf(ERROR, "%s:%d %s\n", config_file_, line_cnt, string); -} - diff --git a/src/cfg_parser.h b/src/cfg_parser.h new file mode 100644 index 0000000..d96c705 --- /dev/null +++ b/src/cfg_parser.h @@ -0,0 +1,36 @@ +/* + * tcpproxy + * + * tcpproxy is a simple tcp connection proxy which combines the + * features of rinetd and 6tunnel. tcpproxy supports IPv4 and + * IPv6 and also supports connections from IPv6 to IPv4 + * endpoints and vice versa. + * + * + * Copyright (C) 2010-2011 Christian Pointner + * + * This file is part of tcpproxy. + * + * tcpproxy is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * tcpproxy is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with tcpproxy. If not, see . + */ + +#ifndef TCPPROXY_cfg_parser_h_INCLUDED +#define TCPPROXY_cfg_parser_h_INCLUDED + +#include "listener.h" + +int parse_listener(char* p, char* pe, listeners_t* listener); +int read_configfile(const char* filename, listeners_t* listener); + +#endif diff --git a/src/cfg_parser.rl b/src/cfg_parser.rl new file mode 100644 index 0000000..a75a580 --- /dev/null +++ b/src/cfg_parser.rl @@ -0,0 +1,216 @@ +/* + * tcpproxy + * + * tcpproxy is a simple tcp connection proxy which combines the + * features of rinetd and 6tunnel. tcpproxy supports IPv4 and + * IPv6 and also supports connections from IPv6 to IPv4 + * endpoints and vice versa. + * + * + * Copyright (C) 2010-2011 Christian Pointner + * + * This file is part of tcpproxy. + * + * tcpproxy is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * tcpproxy is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with tcpproxy. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "datatypes.h" +#include "log.h" +#include "options.h" +#include "tcp.h" +#include "listener.h" + +struct listener { + char* la_; + resolv_type_t lrt_; + char* lp_; + char* ra_; + resolv_type_t rrt_; + char* rp_; + char* sa_; +}; + +static void init_listener_struct(struct listener* l) +{ + if(!l) return; + + l->la_ = NULL; + l->lrt_ = ANY; + l->lp_ = NULL; + l->ra_ = NULL; + l->rrt_ = ANY; + l->rp_ = NULL; + l->sa_ = NULL; +} + +static void clear_listener_struct(struct listener* l) +{ + if(!l) return; + + if(l->la_) + free(l->la_); + if(l->lp_) + free(l->lp_); + if(l->ra_) + free(l->ra_); + if(l->rp_) + free(l->rp_); + if(l->sa_) + free(l->sa_); + + init_listener_struct(l); +} + +static int owrt_string(char** dest, char* start, char* end) +{ + if(!dest || start >= end) + return -1; + + if(*dest) free(*dest); + *dest = strndup(start, end - start); + if(!(*dest)) + return -2; + + return 0; +} + +%%{ + machine cfg_parser; + + action set_cpy_start { cpy_start = fpc; } + action set_local_addr { ret = owrt_string(&(lst.la_), cpy_start, fpc); cpy_start = NULL; } + action set_local_port { ret = owrt_string(&(lst.lp_), cpy_start, fpc); cpy_start = NULL; } + action set_local_resolv4 { lst.lrt_ = IPV4_ONLY; } + action set_local_resolv6 { lst.lrt_ = IPV6_ONLY; } + + action set_remote_addr { ret = owrt_string(&(lst.ra_), cpy_start, fpc); cpy_start = NULL; } + action set_remote_port { ret = owrt_string(&(lst.rp_), cpy_start, fpc); cpy_start = NULL; } + action set_remote_resolv4 { lst.rrt_ = IPV4_ONLY; } + action set_remote_resolv6 { lst.rrt_ = IPV6_ONLY; } + + action set_source_addr { ret = owrt_string(&(lst.sa_), cpy_start, fpc); cpy_start = NULL; } + + action add_listener { + log_printf(DEBUG, "line %d: adding listner: %s : %s -> %s : %s (%s)", cur_line, lst.la_ ? lst.la_ : "(null)", lst.lp_ ? lst.lp_ : "(null)", lst.ra_ ? lst.ra_ : "(null)", lst.rp_ ? lst.rp_ : "(null)", lst.sa_ ? lst.sa_ : "(null)"); + ret = listener_add(listener, lst.la_, lst.lrt_, lst.lp_, lst.ra_, lst.rrt_, lst.rp_, lst.sa_); + clear_listener_struct(&lst); + } + + newline = '\n' @{cur_line++;}; + ws = ( " " | "\t" ); + wsn= ( ws | newline ); + + number = [0-9]+; + ipv4_addr = [0-9.]+; + ipv6_addr = [0-9a-fA-F:]+; + name = [a-zA-Z0-9\-]+; + host_name = [a-zA-Z0-9\-.]+; + tok_ipv4 = "ipv4"; + tok_ipv6 = "ipv6"; + + host_or_addr = ( host_name | name | ipv4_addr | ipv6_addr ); + service = ( number | name ); + + local_addr = ( '*' | host_or_addr >set_cpy_start %set_local_addr ); + local_port = service >set_cpy_start %set_local_port; + lresolv = ( tok_ipv4 @set_local_resolv4 | tok_ipv6 @set_local_resolv6 ); + + remote_addr = host_or_addr >set_cpy_start %set_remote_addr; + remote_port = service >set_cpy_start %set_remote_port; + rresolv = ( tok_ipv4 @set_remote_resolv4 | tok_ipv6 @set_remote_resolv6 ); + + source_addr = host_or_addr >set_cpy_start %set_source_addr; + + resolv = "resolv" ws* ":" ws+ lresolv ws* ";"; + remote = "remote" ws* ":" ws+ remote_addr ws+ remote_port ws* ";"; + remote_resolv = "remote-resolv" ws* ":" ws+ rresolv ws* ";"; + source = "source" ws* ":" ws+ source_addr ws* ";"; + + listen_head = 'listen' ws+ local_addr ws+ local_port; + listen_body = '{' ( wsn+ | resolv | remote | remote_resolv | source )* '};' @add_listener; + + main := ( listen_head wsn* listen_body | wsn+ )*; +}%% + + +int parse_listener(char* p, char* pe, listeners_t* listener) +{ + int cs, ret = 0, cur_line = 1; + + %% write data; + %% write init; + + char* cpy_start = NULL; + struct listener lst; + init_listener_struct(&lst); + + %% write exec; + + if(cs == cfg_parser_error) { + log_printf(ERROR, "syntax error in line %d", cur_line); + } + + clear_listener_struct(&lst); + + return ret; +} + +int read_configfile(const char* filename, listeners_t* listener) +{ + int fd = open(filename, 0); + if(fd < 0) { + log_printf(ERROR, "open('%s') failed: %s", filename, strerror(errno)); + return -1; + } + + struct stat sb; + if(fstat(fd, &sb) == -1) { + log_printf(ERROR, "fstat() error: %s", strerror(errno)); + close(fd); + return -1; + } + + if(!S_ISREG(sb.st_mode)) { + log_printf(ERROR, "%s no regular file", filename); + close(fd); + return -1; + } + + char* p = (char*)mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); + if(p == MAP_FAILED) { + log_printf(ERROR, "mmap() error: %s", strerror(errno)); + close(fd); + return -1; + } + close(fd); + + log_printf(DEBUG, "mapped %ld bytes from file %s", sb.st_size, filename); + int ret = parse_listener(p, p + sb.st_size, listener); + + if(munmap(p, sb.st_size) == -1) { + log_printf(ERROR, "munmap() error: %s", strerror(errno)); + return -1; + } + log_printf(DEBUG, "unmapped file %s", filename); + + return ret; +} diff --git a/src/cfg_scan.lex b/src/cfg_scan.lex deleted file mode 100644 index a63490d..0000000 --- a/src/cfg_scan.lex +++ /dev/null @@ -1,62 +0,0 @@ -%{ -/* - * tcpproxy - * - * tcpproxy is a simple tcp connection proxy which combines the - * features of rinetd and 6tunnel. tcpproxy supports IPv4 and - * IPv6 and also supports connections from IPv6 to IPv4 - * endpoints and vice versa. - * - * - * Copyright (C) 2010-2011 Christian Pointner - * - * This file is part of tcpproxy. - * - * tcpproxy is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * tcpproxy is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with tcpproxy. If not, see . - */ - -#include "cfg_parse.h" - -#include "tcp.h" - -extern int line_cnt; -%} - -%option noyywrap - -%% - -\s*"#".*\n line_cnt++; -\{ return TOK_OPEN; -\} return TOK_CLOSE; -\: return TOK_COLON; -\; return TOK_SEMICOLON; -\* return TOK_ASTERISK; -"listen" return TOK_LISTEN; -"resolv" return TOK_RESOLV; -"remote" return TOK_REMOTE; -"remote-resolv" return TOK_REMOTE_RESOLV; -"source" return TOK_SOURCE; -"ipv4" yylval.rtype = IPV4_ONLY; return TOK_IPV4; -"ipv6" yylval.rtype = IPV6_ONLY; return TOK_IPV6; -[0-9]+ yylval.string = strdup(yytext); return TOK_NUMBER; -[0-9\.]+ yylval.string = strdup(yytext); return TOK_IPV4_ADDR; -[0-9a-fA-F:]+ yylval.string = strdup(yytext); return TOK_IPV6_ADDR; -[a-zA-Z0-9\-]+ yylval.string = strdup(yytext); return TOK_NAME; -[a-zA-Z0-9\-\.]+ yylval.string = strdup(yytext); return TOK_HOSTNAME; -\n line_cnt++; -[ \t]+ /* ignore whitespace */; - -%% - diff --git a/src/configure b/src/configure index b07190b..53d457d 100755 --- a/src/configure +++ b/src/configure @@ -143,8 +143,7 @@ CFLAGS := $CFLAGS LDFLAGS := $LDFLAGS STRIP := strip INSTALL := install -FLEX := flex -BISON := bison +RAGEL := ragel PREFIX := '$PREFIX' BINDIR := '$BINDIR' diff --git a/src/tcpproxy.c b/src/tcpproxy.c index 3e30338..273aad0 100644 --- a/src/tcpproxy.c +++ b/src/tcpproxy.c @@ -1,14 +1,14 @@ /* * tcpproxy * - * tcpproxy is a simple tcp connection proxy which combines the - * features of rinetd and 6tunnel. tcpproxy supports IPv4 and - * IPv6 and also supports connections from IPv6 to IPv4 + * tcpproxy is a simple tcp connection proxy which combines the + * features of rinetd and 6tunnel. tcpproxy supports IPv4 and + * IPv6 and also supports connections from IPv6 to IPv4 * endpoints and vice versa. - * + * * * Copyright (C) 2010-2011 Christian Pointner - * + * * This file is part of tcpproxy. * * tcpproxy is free software: you can redistribute it and/or modify @@ -39,11 +39,7 @@ #include "listener.h" #include "clients.h" - -extern FILE *yyin; -extern void yyinit(const char* config_file, listeners_t* listeners); -extern int yyparse(void); - +#include "cfg_parser.h" int main_loop(options_t* opt, listeners_t* listeners) { @@ -112,7 +108,7 @@ int main(int argc, char* argv[]) options_print_version(); } - if(ret != -2 && ret != -3) + if(ret != -2 && ret != -3) options_print_usage(); if(ret == -1 || ret == -3) @@ -132,7 +128,7 @@ int main(int argc, char* argv[]) case -4: fprintf(stderr, "this log target is only allowed once: '%s', exitting\n", (char*)(tmp->data_)); break; default: fprintf(stderr, "syntax error near: '%s', exitting\n", (char*)(tmp->data_)); break; } - + options_clear(&opt); log_close(); exit(ret); @@ -160,18 +156,7 @@ int main(int argc, char* argv[]) exit(-1); } } else { - yyin = fopen(opt.config_file_, "r"); - if(!yyin) { - log_printf(ERROR, "can't open config file %s: %s", opt.config_file_, strerror(errno)); - listener_clear(&listeners); - options_clear(&opt); - log_close(); - exit(-1); - } - - yyinit(opt.config_file_, &listeners); - int ret = yyparse(); - fclose(yyin); + ret = read_configfile(opt.config_file_, &listeners); if(ret || !slist_length(&listeners)) { if(!ret) log_printf(ERROR, "no listeners defined in config file %s", opt.config_file_); @@ -212,7 +197,7 @@ int main(int argc, char* argv[]) options_clear(&opt); log_close(); exit(-1); - } + } if(opt.daemonize_) { pid_t oldpid = getpid(); -- cgit v1.2.3