%{
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pwd.h>
#include "sasyncd.h"
#include "net.h"
struct cfgstate cfgstate;
int conflen = 0;
char *confbuf, *confptr;
int yyparse(void);
int yylex(void);
void yyerror(const char *);
unsigned char x2i(unsigned char *);
%}
%union {
char *string;
int val;
struct {
unsigned char *data;
int len;
} hex;
}
%token MODE INTERFACE INTERVAL LISTEN ON PORT PEER SHAREDKEY
%token Y_SLAVE Y_MASTER INET INET6 FLUSHMODE STARTUP NEVER SYNC
%token GROUP SKIPSLAVE CONTROL
%token <string> STRING
%token <hex> HEX
%token <val> VALUE
%type <val> af port mode flushmode ctlmode
%%
settings :
| settings setting
;
af : { $$ = AF_UNSPEC; }
| INET { $$ = AF_INET; }
| INET6 { $$ = AF_INET6; }
;
port : { $$ = SASYNCD_DEFAULT_PORT; }
| PORT VALUE { $$ = $2; }
;
mode : Y_MASTER { $$ = MASTER; }
| Y_SLAVE { $$ = SLAVE; }
;
modes : SKIPSLAVE
{
cfgstate.flags |= SKIP_LOCAL_SAS;
log_msg(2, "config: not syncing SA to peers");
}
| mode
{
const char *m[] = CARPSTATES;
cfgstate.lockedstate = $1;
log_msg(2, "config: mode set to %s", m[$1]);
}
;
flushmode : STARTUP { $$ = FM_STARTUP; }
| NEVER { $$ = FM_NEVER; }
| SYNC { $$ = FM_SYNC; }
;
key : STRING
{
if (cfgstate.sharedkey)
free(cfgstate.sharedkey);
cfgstate.sharedkey = $1;
cfgstate.sharedkey_len = strlen($1) * 8;
log_msg(2, "config: shared ascii key");
}
| HEX
{
if (cfgstate.sharedkey)
free(cfgstate.sharedkey);
cfgstate.sharedkey = $1.data;
cfgstate.sharedkey_len = $1.len * 8;
log_msg(2, "config: %d byte shared hex key", $1.len);
}
ctlmode : STRING
{
if (strcmp("isakmpd", $1) == 0)
$$ = CTL_ISAKMPD;
else if (strcmp("iked", $1) == 0)
$$ = CTL_IKED;
else if (strcmp("all", $1) == 0)
$$ = CTL_MASK;
else if (strcmp("none", $1) == 0)
$$ = CTL_NONE;
else {
log_err("config: invalid control mode");
free($1);
YYERROR;
}
log_msg(2, "config: control mode set to %s", $1);
free($1);
}
;
setting : INTERFACE STRING
{
if (cfgstate.carp_ifname)
free(cfgstate.carp_ifname);
cfgstate.carp_ifname = $2;
log_msg(2, "config: interface %s",
cfgstate.carp_ifname);
}
| GROUP STRING
{
if (cfgstate.carp_ifgroup)
free(cfgstate.carp_ifgroup);
cfgstate.carp_ifgroup = $2;
log_msg(2, "config: group %s",
cfgstate.carp_ifgroup);
}
| FLUSHMODE flushmode
{
const char *fm[] = { "STARTUP", "NEVER", "SYNC" };
cfgstate.flags |= $2;
log_msg(2, "config: flush mode set to %s", fm[$2]);
}
| PEER STRING
{
struct syncpeer *peer;
int duplicate = 0;
for (peer = LIST_FIRST(&cfgstate.peerlist); peer;
peer = LIST_NEXT(peer, link))
if (strcmp($2, peer->name) == 0) {
duplicate++;
break;
}
if (duplicate)
free($2);
else {
peer = calloc(1, sizeof *peer);
if (!peer) {
log_err("config: calloc(1, %lu) "
"failed", sizeof *peer);
free($2);
YYERROR;
}
peer->name = $2;
}
LIST_INSERT_HEAD(&cfgstate.peerlist, peer, link);
cfgstate.peercnt++;
log_msg(2, "config: add peer %s", peer->name);
}
| LISTEN ON STRING af port
{
char pstr[20];
if (cfgstate.listen_on)
free(cfgstate.listen_on);
cfgstate.listen_on = $3;
cfgstate.listen_family = $4;
cfgstate.listen_port = $5;
if ($5 < 1 || $5 > IPPORT_HILASTAUTO) {
cfgstate.listen_port = SASYNCD_DEFAULT_PORT;
log_msg(0, "config: bad port, listen-port "
"reset to %u", SASYNCD_DEFAULT_PORT);
}
if ($5 != SASYNCD_DEFAULT_PORT)
snprintf(pstr, sizeof pstr, "port %d",$5);
log_msg(2, "config: listen on %s %s%s",
cfgstate.listen_on, $4 == AF_INET6 ? "(IPv6) " :
($4 == AF_INET ? "(IPv4) " : ""),
$5 != SASYNCD_DEFAULT_PORT ? pstr : "");
}
| MODE modes
| SHAREDKEY key
{
int bits;
bits = cfgstate.sharedkey_len;
if (bits != 128 && bits != 192 && bits != 256) {
log_err("config: bad shared key length %d, "
"should be 128, 192 or 256 bits\n", bits);
YYERROR;
}
log_msg(2, "config: shared key set");
}
| CONTROL ctlmode
{
cfgstate.flags &= ~CTL_MASK;
cfgstate.flags |= $2;
}
;
%%
struct keyword {
char *name;
int value;
};
static int
match_cmp(const void *a, const void *b)
{
return strcmp(a, ((const struct keyword *)b)->name);
}
static int
match(char *token)
{
static const struct keyword keywords[] = {
{ "control", CONTROL },
{ "flushmode", FLUSHMODE },
{ "group", GROUP },
{ "inet", INET },
{ "inet6", INET6 },
{ "interface", INTERFACE },
{ "listen", LISTEN },
{ "master", Y_MASTER },
{ "mode", MODE },
{ "never", NEVER },
{ "on", ON },
{ "peer", PEER },
{ "port", PORT },
{ "sharedkey", SHAREDKEY },
{ "skipslave", SKIPSLAVE },
{ "slave", Y_SLAVE },
{ "startup", STARTUP },
{ "sync", SYNC },
};
const struct keyword *k;
k = bsearch(token, keywords, sizeof keywords / sizeof keywords[0],
sizeof keywords[0], match_cmp);
return k ? k->value : STRING;
}
int
yylex(void)
{
char *p;
int v, i, len;
if (!confptr)
confptr = confbuf;
else {
for (p = confptr; p < confbuf + conflen && *p; p++)
;
if (p == confbuf + conflen)
return 0;
p++;
if (!*p)
return 0;
confptr = p;
}
p = confptr;
if (!strncmp(p, "0x", 2)) {
for (p = confptr + 2; *p; p++)
if (!isxdigit(*p))
goto is_string;
p = confptr + 2;
len = strlen(p) / 2;
if ((yylval.hex.data = calloc(len, sizeof(unsigned char)))
== NULL) {
log_err("yylex: calloc()");
exit(1);
}
for (i = 0; i < len; i++)
yylval.hex.data[i] = x2i(p + 2 * i);
yylval.hex.len = len;
return HEX;
}
if (isdigit(*confptr)) {
for (p = confptr; *p; p++)
if (*p == '.' || *p == ':')
goto is_string;
v = (int)strtol(confptr, (char **)NULL, 10);
yylval.val = v;
return VALUE;
}
is_string:
v = match(confptr);
if (v == STRING) {
yylval.string = strdup(confptr);
if (!yylval.string) {
log_err("yylex: strdup()");
exit(1);
}
}
return v;
}
int
conf_parse_file(char *cfgfile)
{
struct stat st;
int fd, r;
char *buf, *s, *d;
struct passwd *pw;
if (stat(cfgfile, &st) != 0)
goto bad;
pw = getpwnam(SASYNCD_USER);
if (pw == NULL) {
log_err("getpwnam(%s) failed", SASYNCD_USER);
return 1;
}
if ((st.st_uid && st.st_uid != pw->pw_uid) ||
((st.st_mode & S_IFMT) != S_IFREG) ||
((st.st_mode & (S_IRWXG | S_IRWXO)) != 0)) {
log_msg(0, "configuration file has bad owner, type or mode");
goto bad;
}
fd = open(cfgfile, O_RDONLY);
if (fd == -1)
goto bad;
conflen = st.st_size;
buf = malloc(conflen + 1);
if (!buf) {
log_err("malloc(%d) failed", conflen + 1);
close(fd);
return 1;
}
if (read(fd, buf, conflen) != conflen) {
log_err("read() failed");
free(buf);
close(fd);
return 1;
}
close(fd);
buf[conflen] = (char)0;
for (s = buf, d = s; s < buf + conflen && *s; s++) {
if (isspace(*s) && isspace(*(s+1)))
continue;
if (*s == '#') {
while (*s != '\n' && s < buf + conflen)
s++;
while (*s == '\n' && s < buf + conflen)
s++;
s--;
continue;
}
if (d == buf && isspace(*s))
continue;
*d++ = *s;
}
*d = (char)0;
for (s = buf; s <= d; s++)
if (isspace(*s))
*s = (char)0;
confbuf = buf;
confptr = NULL;
r = yyparse();
free(buf);
if (!cfgstate.carp_ifgroup)
cfgstate.carp_ifgroup = strdup("carp");
return r;
bad:
log_msg(0, "failed to open \"%s\"", cfgfile);
return 1;
}
unsigned char
x2i(unsigned char *s)
{
char ss[3];
ss[0] = s[0];
ss[1] = s[1];
ss[2] = 0;
return ((unsigned char)strtoul(ss, NULL, 16));
}
void
yyerror(const char *s)
{
fprintf(stderr, "config: %s\n", s);
}