%{ /* VTun - Virtual Tunnel over TCP/IP network. Copyright (C) 1998-2008 Maxim Krasnyansky VTun has been derived from VPPP package by Maxim Krasnyansky. This program 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 2 of the License, or (at your option) any later version. This program 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. */ /* * $Id: cfg_file.y,v 1.8.2.3 2008/12/08 13:01:23 mtbishop Exp $ */ #include "config.h" #include #include #include #include #include #include "compat.h" #include "vtun.h" #include "lib.h" int lineno = 1; struct vtun_host *parse_host; extern struct vtun_host default_host; llist *parse_cmds; struct vtun_cmd parse_cmd; llist host_list; int cfg_error(const char *fmt, ...); int add_cmd(llist *cmds, char *prog, char *args, int flags); void *cp_cmd(void *d, void *u); int free_cmd(void *d, void *u); void copy_addr(struct vtun_host *to, struct vtun_host *from); int free_host(void *d, void *u); void free_addr(struct vtun_host *h); void free_host_list(void); int parse_syslog(char *facility); int yyparse(void); int yylex(void); int yyerror(char *s); #define YYERROR_VERBOSE 1 %} %union { char *str; int num; struct { int num1; int num2; } dnum; } %expect 20 %token K_OPTIONS K_DEFAULT K_PORT K_BINDADDR K_PERSIST K_TIMEOUT %token K_PASSWD K_PROG K_PPP K_SPEED K_IFCFG K_FWALL K_ROUTE K_DEVICE %token K_MULTI K_SRCADDR K_IFACE K_ADDR %token K_TYPE K_PROT K_COMPRESS K_ENCRYPT K_KALIVE K_STAT %token K_UP K_DOWN K_SYSLOG K_IPROUTE %token K_HOST K_ERROR %token WORD PATH STRING %token NUM %token DNUM %% config: | config statement ; statement: '\n' | K_OPTIONS '{' options '}' | K_DEFAULT { parse_host = &default_host; } '{' host_options '}' | K_HOST { if( !(parse_host = malloc(sizeof(struct vtun_host))) ){ yyerror("No memory for the host"); YYABORT; } /* Fill new host struct with default values. * MUST dup strings to be able to reread config. */ memcpy(parse_host, &default_host, sizeof(struct vtun_host)); parse_host->host = strdup($1); parse_host->passwd = NULL; /* Copy local address */ copy_addr(parse_host, &default_host); llist_copy(&default_host.up,&parse_host->up,cp_cmd,NULL); llist_copy(&default_host.down,&parse_host->down,cp_cmd,NULL); } '{' host_options '}' { /* Check if session definition is complete */ if (!parse_host->passwd) { cfg_error("Ignored incomplete session definition '%s'", parse_host->host); free_host(parse_host, NULL); free(parse_host); } else { /* Add host to the list */ llist_add(&host_list, (void *)parse_host); } } | K_ERROR { cfg_error("Invalid clause '%s'",$1); YYABORT; } ; options: option | options option ; /* Don't override command line options */ option: '\n' | K_PORT NUM { if(vtun.bind_addr.port == -1) vtun.bind_addr.port = $2; } | K_BINDADDR '{' bindaddr_option '}' | K_IFACE STRING { if(vtun.svr_addr == NULL) vtun.svr_addr = strdup($2); } | K_TYPE NUM { if(vtun.svr_type == -1) vtun.svr_type = $2; } | K_TIMEOUT NUM { if(vtun.timeout == -1) vtun.timeout = $2; } | K_PPP PATH { free(vtun.ppp); vtun.ppp = strdup($2); } | K_IFCFG PATH { free(vtun.ifcfg); vtun.ifcfg = strdup($2); } | K_ROUTE PATH { free(vtun.route); vtun.route = strdup($2); } | K_FWALL PATH { free(vtun.fwall); vtun.fwall = strdup($2); } | K_IPROUTE PATH { free(vtun.iproute); vtun.iproute = strdup($2); } | K_SYSLOG syslog_opt | K_ERROR { cfg_error("Unknown option '%s'",$1); YYABORT; } ; bindaddr_option: K_ADDR WORD { vtun.bind_addr.name = strdup($2); vtun.bind_addr.type = VTUN_ADDR_NAME; } | K_IFACE WORD { vtun.bind_addr.name = strdup($2); vtun.bind_addr.type = VTUN_ADDR_IFACE; } | K_IFACE STRING { vtun.bind_addr.name = strdup($2); vtun.bind_addr.type = VTUN_ADDR_IFACE; } | K_ERROR { cfg_error("Unknown option '%s'",$1); YYABORT; } ; syslog_opt: NUM { vtun.syslog = $1; } | WORD { if (parse_syslog($1)) { cfg_error("Unknown syslog facility '%s'", $1); YYABORT; } } | K_ERROR { cfg_error("Unknown syslog option '%s'",$1); YYABORT; } ; host_options: host_option | host_options host_option ; /* Host options. Must free strings first, because they * could be strduped from default_host */ host_option: '\n' | K_PASSWD WORD { free(parse_host->passwd); parse_host->passwd = strdup($2); } | K_DEVICE WORD { free(parse_host->dev); parse_host->dev = strdup($2); } | K_MULTI NUM { parse_host->multi = $2; } | K_TIMEOUT NUM { parse_host->timeout = $2; } | K_SPEED NUM { if( $2 ){ parse_host->spd_in = parse_host->spd_out = $2; parse_host->flags |= VTUN_SHAPE; } else parse_host->flags &= ~VTUN_SHAPE; } | K_SPEED DNUM { if( yylval.dnum.num1 || yylval.dnum.num2 ){ parse_host->spd_out = yylval.dnum.num1; parse_host->spd_in = yylval.dnum.num2; parse_host->flags |= VTUN_SHAPE; } else parse_host->flags &= ~VTUN_SHAPE; } | K_COMPRESS { parse_host->flags &= ~(VTUN_ZLIB | VTUN_LZO); } compress | K_ENCRYPT NUM { if( $2 ){ parse_host->flags |= VTUN_ENCRYPT; parse_host->cipher = $2; } else parse_host->flags &= ~VTUN_ENCRYPT; } | K_KALIVE { parse_host->flags &= ~VTUN_KEEP_ALIVE; } keepalive | K_STAT NUM { if( $2 ) parse_host->flags |= VTUN_STAT; else parse_host->flags &= ~VTUN_STAT; } | K_PERSIST NUM { parse_host->persist = $2; if(vtun.persist == -1) vtun.persist = $2; } | K_TYPE NUM { parse_host->flags &= ~VTUN_TYPE_MASK; parse_host->flags |= $2; } | K_PROT NUM { parse_host->flags &= ~VTUN_PROT_MASK; parse_host->flags |= $2; } | K_SRCADDR '{' srcaddr_options '}' | K_UP { parse_cmds = &parse_host->up; llist_free(parse_cmds, free_cmd, NULL); } '{' command_options '}' | K_DOWN { parse_cmds = &parse_host->down; llist_free(parse_cmds, free_cmd, NULL); } '{' command_options '}' | K_ERROR { cfg_error("Unknown option '%s'",$1); YYABORT; } ; compress: NUM { if( $1 ){ parse_host->flags |= VTUN_ZLIB; parse_host->zlevel = $1; } } | DNUM { parse_host->flags |= yylval.dnum.num1; parse_host->zlevel = yylval.dnum.num2; } | K_ERROR { cfg_error("Unknown compression '%s'",$1); YYABORT; } ; keepalive: NUM { if( $1 ) parse_host->flags |= VTUN_KEEP_ALIVE; } | DNUM { if( yylval.dnum.num1 ){ parse_host->flags |= VTUN_KEEP_ALIVE; parse_host->ka_interval = yylval.dnum.num1; parse_host->ka_failure = yylval.dnum.num2; } } | K_ERROR { cfg_error("Unknown keepalive option '%s'",$1); YYABORT; } ; srcaddr_options: /* empty */ | srcaddr_option | srcaddr_options srcaddr_option ; srcaddr_option: K_ADDR WORD { free_addr(parse_host); parse_host->src_addr.name = strdup($2); parse_host->src_addr.type = VTUN_ADDR_NAME; } | K_IFACE WORD { free_addr(parse_host); parse_host->src_addr.name = strdup($2); parse_host->src_addr.type = VTUN_ADDR_IFACE; } | K_IFACE STRING { free_addr(parse_host); parse_host->src_addr.name = strdup($2); parse_host->src_addr.type = VTUN_ADDR_IFACE; } | K_PORT NUM { parse_host->src_addr.port = $2; } | K_ERROR { cfg_error("Unknown option '%s'",$1); YYABORT; } ; command_options: /* empty */ | command_option | command_options command_option ; command_option: '\n' | K_PROG { memset(&parse_cmd, 0, sizeof(struct vtun_cmd)); } prog_options { add_cmd(parse_cmds, parse_cmd.prog, parse_cmd.args, parse_cmd.flags); } | K_PPP STRING { add_cmd(parse_cmds, strdup(vtun.ppp), strdup($2), VTUN_CMD_DELAY); } | K_IFCFG STRING { add_cmd(parse_cmds, strdup(vtun.ifcfg),strdup($2), VTUN_CMD_WAIT); } | K_ROUTE STRING { add_cmd(parse_cmds, strdup(vtun.route),strdup($2), VTUN_CMD_WAIT); } | K_FWALL STRING { add_cmd(parse_cmds, strdup(vtun.fwall),strdup($2), VTUN_CMD_WAIT); } | K_IPROUTE STRING { add_cmd(parse_cmds, strdup(vtun.iproute),strdup($2), VTUN_CMD_WAIT); } | K_ERROR { cfg_error("Unknown cmd '%s'",$1); YYABORT; } ; prog_options: prog_option | prog_options prog_option ; prog_option: PATH { parse_cmd.prog = strdup($1); } | STRING { parse_cmd.args = strdup($1); } | NUM { parse_cmd.flags = $1; } ; %% int yyerror(char *s) { vtun_syslog(LOG_ERR, "%s line %d\n", s, lineno); return 0; } int cfg_error(const char *fmt, ...) { char buf[255]; va_list ap; /* print the argument string */ va_start(ap, fmt); vsnprintf(buf,sizeof(buf),fmt,ap); va_end(ap); yyerror(buf); return 0; } int add_cmd(llist *cmds, char *prog, char *args, int flags) { struct vtun_cmd *cmd; if( !(cmd = malloc(sizeof(struct vtun_cmd))) ){ yyerror("No memory for the command"); return -1; } memset(cmd, 0, sizeof(struct vtun_cmd)); cmd->prog = prog; cmd->args = args; cmd->flags = flags; llist_add(cmds, cmd); return 0; } void *cp_cmd(void *d, void *u) { struct vtun_cmd *cmd = d, *cmd_copy; if( !(cmd_copy = malloc(sizeof(struct vtun_cmd))) ){ yyerror("No memory to copy the command"); return NULL; } cmd_copy->prog = strdup(cmd->prog); cmd_copy->args = strdup(cmd->args); cmd_copy->flags = cmd->flags; return cmd_copy; } int free_cmd(void *d, void *u) { struct vtun_cmd *cmd = d; free(cmd->prog); free(cmd->args); free(cmd); return 0; } void copy_addr(struct vtun_host *to, struct vtun_host *from) { if( from->src_addr.type ){ to->src_addr.type = from->src_addr.type; to->src_addr.name = strdup(from->src_addr.name); } to->src_addr.port = from->src_addr.port; } void free_addr(struct vtun_host *h) { if( h->src_addr.type ){ h->src_addr.type = 0; free(h->src_addr.name); } } int free_host(void *d, void *u) { struct vtun_host *h = d; if (u && !strcmp(h->host, u)) return 1; free(h->host); free(h->passwd); llist_free(&h->up, free_cmd, NULL); llist_free(&h->down, free_cmd, NULL); free_addr(h); /* releases only host struct instances which were * allocated in the case of K_HOST except default_host */ if( h->passwd ) free(h); return 0; } /* Find host in the hosts list. * NOTE: This function can be called only once since it deallocates hosts list. */ inline struct vtun_host* find_host(char *host) { return (struct vtun_host *)llist_free(&host_list, free_host, host); } inline void free_host_list(void) { llist_free(&host_list, free_host, NULL); } static struct { char *c_name; int c_val; } syslog_names[] = { { "auth", LOG_AUTH }, { "cron", LOG_CRON }, { "daemon", LOG_DAEMON }, { "kern", LOG_KERN }, { "lpr", LOG_LPR }, { "mail", LOG_MAIL }, { "news", LOG_NEWS }, { "syslog", LOG_SYSLOG }, { "user", LOG_USER }, { "uucp", LOG_UUCP }, { "local0", LOG_LOCAL0 }, { "local1", LOG_LOCAL1 }, { "local2", LOG_LOCAL2 }, { "local3", LOG_LOCAL3 }, { "local4", LOG_LOCAL4 }, { "local5", LOG_LOCAL5 }, { "local6", LOG_LOCAL6 }, { "local7", LOG_LOCAL7 }, { NULL, -1 } }; int parse_syslog(char *facility) { int i; for (i=0; syslog_names[i].c_name;i++) { if (!strcmp(syslog_names[i].c_name, facility)) { vtun.syslog = syslog_names[i].c_val; return(0); } } return -1; } /* * Read config file. */ int read_config(char *file) { static int cfg_loaded = 0; extern FILE *yyin; if( cfg_loaded ){ free_host_list(); vtun_syslog(LOG_INFO,"Reloading configuration file"); } cfg_loaded = 1; llist_init(&host_list); if( !(yyin = fopen(file,"r")) ){ vtun_syslog(LOG_ERR,"Can not open %s", file); return -1; } yyparse(); free_host(&default_host, NULL); fclose(yyin); return !llist_empty(&host_list); }