/*
* Copyright (C) 1999-2004 Joachim Wieland <joe@mcknight.de>
*
* 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*/
#include "jftpgw.h" /* this also includes config_header.h */
#include <ctype.h>
#include <time.h>
#include <net/if.h> /* IF_NAMESIZE */
#define WHITESPACE " \r\n\t"
/* for testing */
void config_debug_outputsections();
const struct option_t* config_get_option_list();
void config_debug_output_options ( const struct option_t*, char* );
/* from jftpgw.c */
extern char* conffilename;
extern struct serverinfo srvinfo;
/* from states.c */
extern struct hostent_list* hostcache;
extern struct uidstruct runasuser;
int save_runasuser_uid(void);
/* variables with file scope */
static struct section_t* base_section;
static struct section_t* backup_base_section;
static struct option_t* option_list;
static int debug;
static int config_error;
/* Table of configuration options (identifiers, validity, defaultvalues) */
struct conf_dat {
char* name;
int tag_type;
char* defaultvalue;
int match_type;
int read_limit;
};
/* forward declarations */
const struct hostent_list* config_forward_lookup(struct hostent_list** hl,
const char* name);
const struct hostent_list* config_reverse_lookup(struct hostent_list** hl,
unsigned long int ip);
struct option_t* config_generate_option_list(struct section_t* section,
int config_state);
#define OPTION_EXACT_MATCH 1
#define READ_UP_TO_WS 1
#define READ_FULL_LINE 2
#define EM OPTION_EXACT_MATCH
#define WSP READ_UP_TO_WS
#define FL READ_FULL_LINE
struct conf_dat configuration_data[] = {
{"listen", TAG_STARTUP, "0.0.0.0:2370", EM, FL },
{"runasuser", TAG_STARTUP, (char*) 0, EM, WSP },
{"runasgroup", TAG_STARTUP, (char*) 0, EM, WSP },
{"pidfile", TAG_STARTUP, "/var/run/jftpgw.pid", EM, WSP },
{"changeroot", TAG_STARTUP, "never", EM, WSP },
{"changerootdir", TAG_STARTUP, (char*) 0, EM, WSP },
{"dropprivileges", TAG_STARTUP, "start", EM, WSP },
{"welcomeline", TAG_CONNECTED,
"FTP proxy (v"JFTPGW_VERSION") ready", EM, FL },
{"transparent-forward", TAG_CONNECTED, (char*) 0, EM, WSP },
{"transparent-forward-include-port", TAG_CONNECTED, "on", EM, WSP },
{"transparent-proxy", TAG_CONNECTED, "off", EM, WSP },
{"logintime", TAG_TO | TAG_CONNECTED, "user", EM, WSP },
{"loginprotocolviolations", TAG_ALL, "10", EM, WSP },
{"forward", TAG_ALL, (char*) 0, EM, FL },
{"udpport", TAG_CONNECTED, "2370", EM, FL },
{"getinternalip", TAG_CONNECTED, "udp", EM, FL },
{"controlserveraddress",TAG_ALL, (char*) 0, EM, WSP },
{"dataserveraddress", TAG_ALL, (char*) 0, EM, WSP },
{"dataclientaddress", TAG_ALL, (char*) 0, EM, WSP },
{"access", TAG_ALL, "deny" , EM, WSP },
{"debuglevel", TAG_ALL, "7" , EM, WSP },
{"logstyle", TAG_ALL, "files" , EM, WSP },
{"logfile", TAG_ALL, "/var/log/jftpgw.log", EM, WSP },
{"syslogfacility", TAG_ALL, "daemon", EM, WSP },
{"cmdlogfile", TAG_ALL, (char*) 0, EM, WSP },
{"cmdlogfile-style", TAG_ALL, (char*) 0, EM, FL },
{"cmdlogfile-specs", TAG_ALL, (char*) 0, EM, FL },
{"connectionlogdir", TAG_ALL, (char*) 0, EM, WSP },
{"connectionlogdir-filesuffix", TAG_ALL, (char*) 0, EM, WSP },
{"connectionlogdir-fileprefix", TAG_ALL, (char*) 0, EM, WSP },
{"connectionlogdir-style", TAG_ALL, (char*) 0, EM, FL },
{"connectionlogdir-specs", TAG_ALL, (char*) 0, EM, FL },
{"cacheprefix", TAG_ALL, (char*) 0, EM, WSP },
{"cache", TAG_ALL, "off" , EM, WSP },
{"cachemaxsize", TAG_ALL, "unlimited", EM, WSP },
{"cacheminsize", TAG_ALL, "0", EM, WSP },
{"failedlogins", TAG_ALL, "3", EM, WSP },
{"throughput", TAG_ALL, (char*) 0, EM, WSP },
{"limit", TAG_CONNECTED, (char*) 0, EM, WSP },
{"passcmds", TAG_ALL, "*" , EM, FL },
{"dontpasscmds", TAG_ALL, (char*) 0, EM, FL },
{"activeportrange", TAG_ALL, (char*) 0, EM, FL },
{"passiveportrange", TAG_ALL, (char*) 0, EM, FL },
{"activeportrangeclient", TAG_ALL, (char*) 0, EM, FL },
{"passiveportrangeclient", TAG_ALL, (char*) 0, EM, FL },
{"activeportrangeserver", TAG_ALL, (char*) 0, EM, FL },
{"passiveportrangeserver", TAG_ALL, (char*) 0, EM, FL },
{"defaultmode", TAG_ALL, "asclient", EM, WSP },
{"strictasciiconversion", TAG_ALL, "on", EM, WSP },
{"allowreservedports", TAG_ALL, "no", EM, WSP },
{"allowforeignaddress", TAG_ALL, "no", EM, WSP },
{"serverport", TAG_ALL, "21", EM, WSP },
{"loginstyle", TAG_CONNECTED, "1", EM, WSP },
{"account", TAG_CONNECTED, (char*) 0, EM, FL },
{"initialsyst", TAG_ALL, "yes", EM, WSP },
{"commandtimeout", TAG_ALL, "300", EM, WSP },
{"transfertimeout", TAG_ALL, "300", EM, WSP },
{"reverselookups", TAG_ALL, "yes", EM, WSP },
{"forwardlookups", TAG_ALL, "yes", EM, WSP },
{"dnslookups", TAG_ALL, "yes", EM, WSP },
/* 8 hours */
{"hostcachetimeout", TAG_ALL, "28800", EM, WSP },
{ (char*) 0, 0, (char*) 0, 0, 0 }
};
#define MAXCONFOPTVALS 23 /* facility */
struct conf_opt {
char* name;
char* values[ MAXCONFOPTVALS ];
};
#define TERM ((char*) 0)
#define TRUEFALSE "on", "off", "yes", "no", "true", "false", "1", "0"
struct conf_opt configuration_options[] = {
{"logstyle", {"files", "syslog", TERM} },
/* <---- better */
{"dropprivileges", {"start", "startsetup", "connect",
"connectsetup", "loggedin", "never", TERM} },
/* <---- better */
{"changeroot", {"start", "startsetup", "connect",
"connectsetup", "loggedin", "never", TERM} },
{"logintime", {"connect", "user", "pass", TERM} },
{"access", {"allow", "deny", TERM} },
{"defaultmode", {"asclient", "active", "passive", TERM} },
{"getinternalip", {"udp", "icmp", "configuration", TERM} },
{"transparent-proxy", { TRUEFALSE, TERM } },
{"cache", { TRUEFALSE, TERM } },
{"allowreservedports", { TRUEFALSE, TERM } },
{"allowforeignaddress", { TRUEFALSE, TERM } },
{"reverselookups", { TRUEFALSE, TERM } },
{"forwardlookups", { TRUEFALSE, TERM } },
{"dnslookups", { TRUEFALSE, TERM } },
{"syslogfacility", {
#ifdef HAVE_LOG_FACILITY_LOG_AUTH
"auth",
#endif
#ifdef HAVE_LOG_FACILITY_LOG_AUTHPRIV
"authpriv",
#endif
#ifdef HAVE_LOG_FACILITY_LOG_CRON
"cron",
#endif
#ifdef HAVE_LOG_FACILITY_LOG_DAEMON
"daemon",
#endif
#ifdef HAVE_LOG_FACILITY_LOG_FTP
"ftp",
#endif
#ifdef HAVE_LOG_FACILITY_LOG_KERN
"kern",
#endif
#ifdef HAVE_LOG_FACILITY_LOG_LOCAL0
"local0",
#endif
#ifdef HAVE_LOG_FACILITY_LOG_LOCAL1
"local1",
#endif
#ifdef HAVE_LOG_FACILITY_LOG_LOCAL2
"local2",
#endif
#ifdef HAVE_LOG_FACILITY_LOG_LOCAL3
"local3",
#endif
#ifdef HAVE_LOG_FACILITY_LOG_LOCAL4
"local4",
#endif
#ifdef HAVE_LOG_FACILITY_LOG_LOCAL5
"local5",
#endif
#ifdef HAVE_LOG_FACILITY_LOG_LOCAL6
"local6",
#endif
#ifdef HAVE_LOG_FACILITY_LOG_LOCAL7
"local7",
#endif
#ifdef HAVE_LOG_FACILITY_LOG_LPR
"lpr",
#endif
#ifdef HAVE_LOG_FACILITY_LOG_MAIL
"mail",
#endif
#ifdef HAVE_LOG_FACILITY_LOG_NEWS
"news",
#endif
#ifdef HAVE_LOG_FACILITY_LOG_SYSLOG
"syslog",
#endif
#ifdef HAVE_LOG_FACILITY_LOG_USER
"user",
#endif
#ifdef HAVE_LOG_FACILITY_LOG_UUCP
"uucp",
#endif
#ifdef HAVE_LOG_FACILITY_LOG_CONSOLE
"console",
#endif
#ifdef HAVE_LOG_FACILITY_LOG_SECURITY
"security",
#endif
TERM } },
{ TERM, { TERM } }
};
/* --------begin struct ilist-------------- */
struct ilist_t* ilist_init(int val) {
struct ilist_t* i;
i = (struct ilist_t*) malloc(sizeof(struct ilist_t));
enough_mem(i);
i -> value = val;
i -> next = (struct ilist_t*) 0;
return i;
}
void ilist_destroy(struct ilist_t* il) {
if (! il) { return; }
ilist_destroy(il->next);
free(il);
}
struct ilist_t* ilist_push(struct ilist_t* il, int i) {
if (!il) {
return il;
}
while(il -> next) {
il = il -> next;
}
il -> next = ilist_init(i);
return il->next;
}
int ilist_pop(struct ilist_t* il) {
struct ilist_t* prev = (struct ilist_t*) 0;
int i;
while(il -> next) {
prev = il;
il = il -> next;
}
i = il->value;
if (prev) {
free(il);
prev->next = (struct ilist_t*) 0;
}
return i;
}
int ilist_empty(struct ilist_t* il) {
if (!il) {
return 1;
}
return (il->next == (struct ilist_t*) 0);
}
struct ilist_t* ilist_clone(const struct ilist_t* il) {
struct ilist_t* new;
if (!il) {
return (struct ilist_t*) 0;
}
new = (struct ilist_t*) malloc(sizeof(struct ilist_t));
new->value = il->value;
new->next = ilist_clone(il->next);
return new;
}
/* --------end struct ilist-------------- */
/* --------begin struct ullist-------------- */
struct ullist_t* ullist_init(unsigned long int val) {
struct ullist_t* ul;
ul = (struct ullist_t*) malloc(sizeof(struct ullist_t));
enough_mem(ul);
ul -> value = val;
ul -> next = (struct ullist_t*) 0;
return ul;
}
void ullist_destroy(struct ullist_t* ul) {
if (! ul) { return; }
ullist_destroy(ul->next);
free(ul);
}
struct ullist_t* ullist_push(struct ullist_t* ul, unsigned long int ulval) {
if (!ul) {
return ul;
}
while(ul -> next) {
ul = ul -> next;
}
ul -> next = ullist_init(ulval);
return ul->next;
}
int ullist_pop(struct ullist_t* ul) {
struct ullist_t* prev = (struct ullist_t*) 0;
unsigned long int val;
while(ul -> next) {
prev = ul;
ul = ul -> next;
}
val = ul->value;
if (prev) {
free(ul);
prev->next = (struct ullist_t*) 0;
}
return val;
}
int ullist_empty(struct ullist_t* ul) {
if (!ul) {
return 1;
}
return (ul->next == (struct ullist_t*) 0);
}
struct ullist_t* ullist_conv_ips(char** ip_list) {
int i = 0;
struct ullist_t* base = (struct ullist_t*) 0,
*cur = (struct ullist_t*) 0;
unsigned long int ip;
while (ip_list[i]) {
ip = inet_addr(gethostentip(ip_list[i]));
if (i == 0) {
base = cur = ullist_init(ip);
} else {
cur = ullist_push(cur, ip);
}
i++;
}
return base;
}
/* --------end struct ullist-------------- */
/* --------begin struct slist-------------- */
struct slist_t* slist_init(char* val) {
struct slist_t* s;
s = (struct slist_t*) malloc(sizeof(struct slist_t));
enough_mem(s);
s -> value = val;
s -> next = (struct slist_t*) 0;
return s;
}
/* slist_cinit copies */
struct slist_t* slist_cinit(const char* val) {
struct slist_t* s;
s = slist_init((char*) 0);
s -> value = strdup(val);
enough_mem(s->value);
return s;
}
void slist_destroy(struct slist_t* sl) {
if (! sl) { return; }
slist_destroy(sl->next);
free(sl->value);
free(sl);
}
struct slist_t* slist_push(struct slist_t* sl, char* s) {
if (!sl) {
return sl;
}
while(sl -> next) {
sl = sl -> next;
}
sl -> next = slist_init(s);
return sl->next;
}
/* copy before pushing */
struct slist_t* slist_cpush(struct slist_t* sl, const char* s) {
char* a = strdup(s);
enough_mem(a);
return slist_push(sl, a);
}
struct slist_t* slist_append(struct slist_t* a, struct slist_t* b) {
struct slist_t* orig = a;
if (!a) {
return b;
}
while (a->next) {
a = a->next;
}
a->next = b;
return orig;
}
char* slist_get(struct slist_t* sl) {
return sl->value;
}
char* slist_pop(struct slist_t* sl) {
struct slist_t* prev = (struct slist_t*) 0;
char *s;
while(sl -> next) {
prev = sl;
sl = sl -> next;
}
s = sl->value;
if (prev) {
/* don't free value */
free(sl);
prev->next = (struct slist_t*) 0;
}
return s;
}
int slist_empty(struct slist_t* sl) {
if (!sl) {
return 1;
}
return (sl->next == (struct slist_t*) 0);
}
struct slist_t* slist_conv_dpointer(char** dpointer) {
int i = 0;
struct slist_t* sl;
char* s;
if (dpointer[0]) {
s = strdup(dpointer[0]);
enough_mem(s);
sl = slist_init(s);
} else {
return (struct slist_t*) 0;
}
i = 1;
while (dpointer[i]) {
s = strdup(dpointer[i]);
enough_mem(s);
slist_push(sl, s);
i++;
}
return sl;
}
struct slist_t* slist_clone(const struct slist_t* sl) {
struct slist_t* slret, *slcur;
if ( ! sl ) {
return (struct slist_t*) 0;
}
slret = slcur = slist_cinit(sl->value);
sl = sl->next;
while (sl) {
slist_cpush(slcur, sl->value);
slcur = slcur->next;
sl = sl->next;
}
return slret;
}
struct slist_t* slist_reverse(struct slist_t* sl) {
/* a -> b -> c
*
* becomes
*
* c -> b -> a
*
**/
struct slist_t* head, *curr, *change;
if (! sl) {
return (struct slist_t*) 0;
}
if (! sl->next) {
/* only one element */
return sl;
}
curr = sl;
change = sl->next;
head = sl->next;
curr->next = (struct slist_t*) 0;
while ( head->next ) {
head = head->next;
change->next = curr;
curr = change;
change = head;
};
/* head has reached the end */
head->next = curr;
return head;
}
int slist_case_contains(const struct slist_t* haystack, const char* needle) {
while (haystack) {
if (strcasecmp(haystack->value, needle) == 0) {
return 1;
}
haystack = haystack->next;
}
return 0;
}
int slist_count(const struct slist_t* haystack) {
int counter = 0;
while (haystack) {
counter++;
haystack = haystack->next;
}
return counter;
}
/* --------end struct ilist-------------- */
/* --------begin struct optionlist-------------- */
struct option_t* optionlist_init(const char* key, const char* value) {
struct option_t* o;
o = (struct option_t*) malloc(sizeof(struct option_t));
enough_mem(o);
o->key = strdup(key);
enough_mem(o->key);
o->value = strdup(value);
enough_mem(o->value);
o->next = (struct option_t*) 0;
return o;
}
struct option_t* optionlist_push(struct option_t* o,
const char* key,
const char* value) {
if (! o) {
return (struct option_t*) 0;
}
while (o->next) {
o = o->next;
}
o->next = optionlist_init(key, value);
return o;
}
struct option_t* optionlist_clone(struct option_t* orig) {
struct option_t* ret, *trav;
if ( ! orig ) {
return (struct option_t*) 0;
}
ret = optionlist_init(orig->key, orig->value);
trav = ret;
while ((orig = orig->next)) {
trav->next = optionlist_init(orig->key, orig->value);
trav = trav->next;
}
return ret;
}
struct option_t* optionlist_append(struct option_t* a, struct option_t* b) {
struct option_t* orig = a;
if (!a) {
return b;
}
while (a->next) {
a = a->next;
}
a->next = b;
return orig;
}
void optionlist_destroy(struct option_t* olist) {
if ( !olist ) { return; }
optionlist_destroy( olist->next );
free( olist->key );
free( olist->value );
free( olist );
}
void optionlist_delete_key(struct option_t* olist, const char* key) {
struct option_t* deleted;
while (olist) {
if (olist->next) {
if (strcasecmp(olist->next->key, key) == 0) {
/* found */
deleted = olist->next;
olist->next = olist->next->next;
deleted->next = (struct option_t*) 0;
optionlist_destroy(deleted);
}
}
olist = olist->next;
}
}
/* --------end struct optionlist-------------- */
/* --------begin struct hostent_list-------------- */
struct hostent_list* hostent_init(struct hostent* e,
unsigned long int ip,
const char* name) {
struct hostent_list* h;
h = (struct hostent_list*) malloc(sizeof(struct hostent_list));
enough_mem(h);
if (name) {
/* jlog(9, "adding %s to host cache", name); */
} else {
/* jlog(9, "adding %s to host cache",
inet_ntoa(*((struct in_addr*) &ip))); */
}
h->next = (struct hostent_list*) 0;
if (name) {
h->name = strdup(name);
} else {
h->name = (char*) 0;
}
h->ip = ip;
if (e) {
h->aliases_list = slist_conv_dpointer(e->h_aliases);
h->addr_list = ullist_conv_ips(e->h_addr_list);
if (e->h_name) {
if (h->aliases_list) {
slist_cpush(h->aliases_list, e->h_name);
} else {
h->aliases_list = slist_cinit(e->h_name);
}
} else {
h->aliases_list = (struct slist_t*) 0;
h->addr_list = (struct ullist_t*) 0;
}
} else {
h->aliases_list = (struct slist_t*) 0;
h->addr_list = (struct ullist_t*) 0;
}
h->lookup_time = time(NULL);
return h;
}
struct hostent_list* hostent_push(struct hostent_list* h,
struct hostent* e,
unsigned long int ip,
const char* name) {
if (!h) {
return (struct hostent_list*) 0;
}
while (h->next) {
h = h->next;
}
h->next = hostent_init(e, ip, name);
return h;
}
const struct hostent_list* hostent_get(struct hostent_list* h,
unsigned long int ip,
const char* name) {
while (h) {
if (name && h->name) {
if (strcasecmp(name, h->name) == 0) {
return h;
}
} else {
if (ip != -1 && ip == h->ip) {
return h;
}
}
h = h->next;
}
return (struct hostent_list*) 0;
}
const struct slist_t* hostent_get_aliases(struct hostent_list** h,
unsigned long int ip) {
const struct hostent_list* hl;
hl = config_reverse_lookup(h, ip);
if (!hl) {
return (struct slist_t*) 0;
}
return hl->aliases_list;
}
const struct ullist_t* hostent_get_addr(struct hostent_list** h,
const char* name) {
const struct hostent_list* hl;
hl = config_forward_lookup(h, name);
if (!hl) {
return (struct ullist_t*) 0;
}
return hl->addr_list;
}
const char* hostent_get_name(struct hostent_list** h,
unsigned long int ip) {
const struct slist_t* l = hostent_get_aliases(h, ip);
if ( ! l ) {
return (char*) 0;
}
return l->value;
}
unsigned long int hostent_get_ip(struct hostent_list** h,
const char* name) {
const struct ullist_t* l = hostent_get_addr(h, name);
if ( ! name ) {
return UINT_MAX;
}
if ( ! l ) {
/* Maybe name is already an IP ? */
unsigned long int iptest;
iptest = inet_addr(name);
if (iptest != (unsigned long int) UINT_MAX) {
/* it was an IP */
return iptest;
}
return UINT_MAX;
}
return l->value;
}
void hostent_destroy(struct hostent_list* h) {
if (!h) { return; }
hostent_destroy(h->next);
ullist_destroy(h->addr_list);
slist_destroy(h->aliases_list);
free(h->name);
free(h);
}
void hostent_delete(struct hostent_list* hl, const struct hostent_list* entry){
struct hostent_list* delete;
if (!hl || !entry) {
return;
}
while (hl) {
if (hl->next == entry) {
delete = hl->next;
hl->next = hl->next->next;
delete->next = (struct hostent_list*) 0;
hostent_destroy(delete);
return;
} else {
hl = hl->next;
}
}
}
/* --------end struct hostent_list-------------- */
/* --------begin struct hostlist_t-------------- */
struct hostlist_t* hostlist_init() {
struct hostlist_t* hl;
hl = (struct hostlist_t*) malloc(sizeof(struct hostlist_t));
enough_mem(hl);
hl->next = (struct hostlist_t*) 0;
hl->host.ip.ip = -1;
hl->host.ip.netmask = -1;
hl->host.name = (char*) 0;
return hl;
}
struct hostlist_t* hostlist_push(struct hostlist_t** hl,
char* name,
unsigned long int ip,
unsigned long int netmask) {
struct hostlist_t* h;
if (! hl) {
/* this should NOT happen */
jlog(3, "hostlist was NULL in hostlist_push()");
return (struct hostlist_t*) 0;
}
h = *hl;
if (! h) {
/* new */
h = hostlist_init();
*hl = h;
} else {
while (h->next) {
h = h->next;
}
h->next = hostlist_init();
h = h->next;
}
/* fill with content */
h->next = (struct hostlist_t*) 0;
h->host.ip.ip = ip;
h->host.ip.netmask = netmask;
h->host.name = name;
/* return base address */
return *hl;
}
struct hostlist_t* hostlist_clone(struct hostlist_t* hl) {
struct hostlist_t* new;
if ( ! hl ) {
return (struct hostlist_t*) 0;
}
new = (struct hostlist_t*) malloc(sizeof(struct hostlist_t));
new->host.ip = hl->host.ip;
if (hl->host.name) {
new->host.name = strdup(hl->host.name);
enough_mem(new->host.name);
} else {
new->host.name = (char*) 0;
}
new->next = hostlist_clone(hl->next);
return new;
}
struct hostlist_t* hostlist_ip_push(struct hostlist_t** hl,
unsigned long int ip,
unsigned long int netmask) {
return hostlist_push(hl, (char*) 0, ip, netmask);
}
struct hostlist_t* hostlist_name_push(struct hostlist_t** hl,
char* name) {
return hostlist_push(hl, name, -1, -1);
}
/* --------end struct hostlist_t---------------- */
/* --------begin struct portrangestruct-------------- */
struct portrangestruct* config_port2portrange(unsigned int port) {
static struct portrangestruct prs;
prs.next = (struct portrangestruct*) 0;
prs.startport = prs.endport = port;
return &prs;
}
unsigned int config_count_portrange(const struct portrangestruct* prs) {
if ( ! prs ) {
return 0;
}
return (prs->endport - prs->startport + 1) +
config_count_portrange(prs->next);
}
struct portrangestruct* portrangestruct_clone(struct portrangestruct* prs) {
struct portrangestruct* new;
if ( ! prs ) {
return (struct portrangestruct*) 0;
}
new = (struct portrangestruct*) malloc(sizeof(struct portrangestruct));
enough_mem(new);
new->startport = prs->startport;
new->endport = prs->endport;
new->next = portrangestruct_clone(prs->next);
return new;
}
/* --------end struct portrangestruct---------------- */
/* returns the first characters up to a whitespace character */
/* Checks if PATTERN is the beginning of RESPONSE */
int checkbegin(const char* response, const char* pattern) {
if (strlen(response) < strlen(pattern)) {
return 0;
}
return !strncasecmp(response, pattern, strlen(pattern));
}
char* config_read_line_basic(FILE* file) {
/* read 255 bytes at first */
const int startsize = 5;
/* therafter increase the buffer again by size bytes */
const int increase = startsize;
int times;
unsigned int size = startsize;
static char* line;
char* ret;
if (line) {
free(line);
line = (char*) 0;
}
size = startsize;
times = 0;
do {
if (size > INT_MAX - increase) {
jlog(1, "input in config file too long");
free(line);
line = (char*) 0;
return (char*) 0;
}
if (!line) {
line = (char*) malloc(size + 1);
enough_mem(line);
line[0] = '\0';
} else {
line = (char*) realloc(line, size + 1);
enough_mem(line);
}
/* append the newly read characters to the old ones */
ret = fgets(line + times*increase, increase + 1, file);
if (!ret) {
if (feof(file)) {
break;
}
perror("Error reading a line from the "
"configuration file");
jlog(1, "Error reading a line from the "
"configuration file: %s", strerror(errno));
}
size += increase;
times++;
} while (ret && ret[strlen(ret)-1] != '\n');
if (feof(file)) {
if (line && strlen(line)) {
return line;
}
free(line);
line = (char*) 0;
return (char*) 0;
}
return line;
}
char* config_read_line(FILE* file) {
char* line = (char*) 0;
do {
line = config_read_line_basic(file);
if (line) {
line = trim(line);
}
} while (line && ( !strlen(line) || line[0] == '#' ));
return line;
}
char* trim(char* s) {
size_t start = 0;
size_t end;
size_t i;
char c;
if ( !s || !strlen(s) ) {
return s;
}
end = strlen(s);
while (isspace((int) s[ start ])) {
start++;
if (start >= end) {
s[ 0 ] = '\0';
return s;
}
}
while (isspace((int) s[ end - 1 ])) {
end--;
if (start >= end) {
return s;
}
}
s[ end ] = '\0';
if (start == 0) {
return s;
}
i = 0;
do {
c = s[ start++ ];
s[ i++ ] = c;
} while ( c );
return s;
}
/* quotstrtok parses *s and returns a malloc'ed pointer to the tokens. It
* also respects quotation marks:
*
* bla "foo bar" bar bla
*
* returns bla, "foo bar", bar and bla
*/
char* quotstrtok(const char* s, const char* delim, int *past_offset) {
int i = *past_offset;
const char* q = 0;
char *ret =0, *r =0;
const char* ubound, *lbound;
int length;
const int MAXLENGTH = 65536;
/*while(s[i] && isspace((int)s[i])) {*/
/* move forth if s[i] is one of the delimiters */
while(s[i] && strchr(delim, (int)s[i])) {
i++;
}
if (s[i] == 0)
return 0;
if (s[i] == '"') {
q = strchr(&s[i+1], '"');
if (q) {
q = strpbrk(++q, delim);
} else {
q = strpbrk(&s[i], delim);
}
} else {
q = strpbrk(&s[i], delim);
}
if (!q || !*q) {
if (strlen(&s[i]) == 0) {
return 0;
} else {
q = s + i + strlen(&s[i]);
}
}
/* chop quotation marks on both sides */
if (s[i] == '"' && *(q-1) == '"') {
lbound = &s[i+1];
ubound = q-1;
} else {
lbound = &s[i];
ubound = q;
}
length = MIN_VAL(ubound - lbound, MAXLENGTH);
ret = (char*) malloc( length + 1);
enough_mem(ret);
strncpy(ret, lbound, length);
ret[length] = '\0';
*past_offset = i + (q - &s[i]); /* q - &s[i] is ubound - lbound,
regardless of quotation marks */
if (strlen(ret)) {
r = ret + strlen(ret) - 1;
while (iscntrl((int)*r) && strlen(ret)) {
*r = '\0';
r--;
}
}
return ret;
}
/* the same as quotstrtok with an additional parameter that is prepended to
* the quotstrtok output */
char* quotstrtok_prepend(const char* prefix,
const char* s, const char* delim, int *past_offset) {
char* qstr = quotstrtok(s, delim, past_offset);
char* nstr;
int newsize;
if ( ! qstr ) {
return qstr;
}
newsize = strlen(qstr) + strlen(prefix) + 1;
nstr = (char*) malloc( newsize );
enough_mem(nstr);
snprintf(nstr, newsize, "%s%s", prefix, qstr);
return nstr;
}
struct slist_t* config_split_line(const char *line, const char* delim) {
int offset = 0;
char* buf;
struct slist_t *slistbase =0, *slistcur =0, *ptr =0;
while ((buf = quotstrtok(line, delim, &offset))) {
ptr = (struct slist_t*) malloc (sizeof(struct slist_t));
enough_mem(ptr);
if (!slistbase) {
slistbase = ptr;
}
if (slistcur) {
slistcur->next = ptr;
}
slistcur = ptr;
slistcur->value = buf;
slistcur->next = (struct slist_t*) 0;
}
return slistbase;
}
void config_section_init(struct section_t* section) {
section->hosts = (struct hostlist_t*) 0;
section->hosts_exclude = (struct hostlist_t*) 0;
section->users = (struct slist_t*) 0;
section->users_exclude = (struct slist_t*) 0;
section->forwarded = (struct slist_t*) 0;
section->forwarded_exclude = (struct slist_t*) 0;
section->ports = (struct portrangestruct*) 0;
section->ports_exclude = (struct portrangestruct*) 0;
section->time = (struct timestruct*) 0;
section->time_exclude = (struct timestruct*) 0;
section->options = (struct option_t*) 0;
section->nested = (struct section_t*) 0;
section->next = (struct section_t*) 0;
section->servertype = SERVERTYPE_STANDALONE;
section->connection_counter = 0;
}
/* ---------------------- begin parse functions -------------------- */
void config_append_string(char** s, char* appstr) {
if (! appstr || ! s) {
return;
}
if ( ! *s ) {
*s = (char*) malloc(strlen(appstr) + 1);
enough_mem(s);
*s[0] = '\0';
} else {
*s = (char*) realloc(*s, strlen(*s) + strlen(appstr) + 1);
enough_mem(s);
}
strcat(*s, appstr);
}
static
char* config_get_valid_tags(const char* key) {
int i = 0;
char* retstr = (char*) 0;
while (configuration_data[i].name) {
if (strcasecmp(key, configuration_data[i].name) == 0) {
/* found */
if (TAG_GLOBAL & configuration_data[i].tag_type) {
config_append_string(&retstr, " global");
}
if (TAG_FROM & configuration_data[i].tag_type) {
config_append_string(&retstr, " from");
}
if (TAG_TO & configuration_data[i].tag_type) {
config_append_string(&retstr, " to");
}
if (TAG_PORT & configuration_data[i].tag_type) {
config_append_string(&retstr, " port");
}
if (TAG_USER & configuration_data[i].tag_type) {
config_append_string(&retstr, " user");
}
if (TAG_FORWARDED & configuration_data[i].tag_type) {
config_append_string(&retstr, " forwarded");
}
if (TAG_TIME & configuration_data[i].tag_type) {
config_append_string(&retstr, " time");
}
if (TAG_SERVERTYPE & configuration_data[i].tag_type) {
config_append_string(&retstr, " servertype");
}
if (TAG_PROXYIP & configuration_data[i].tag_type) {
config_append_string(&retstr, " proxyip");
}
if (TAG_PROXYPORT & configuration_data[i].tag_type) {
config_append_string(&retstr, " proxyport");
}
break;
}
i++;
}
return retstr;
}
static
int config_is_option_key_valid(const char* key, int tag_num) {
int i = 0;
int valid = 0;
while (configuration_data[i].name) {
if ((configuration_data[i].match_type == OPTION_EXACT_MATCH
&& strcasecmp(key, configuration_data[i].name) == 0)) {
/* found */
if (tag_num & configuration_data[i].tag_type) {
valid = 1;
}
break;
}
i++;
}
return valid;
}
static
char* config_get_valid_values(const char* key) {
int i = 0, j = 0;
char* retstr = (char*) 0;
while (configuration_options[i].name) {
if (strcasecmp(key, configuration_options[i].name) == 0) {
/* there are limitations for the value */
while(configuration_options[i].values[j]) {
config_append_string(&retstr,
configuration_options[i].values[j]);
config_append_string(&retstr, " ");
j++;
}
break;
}
i++;
}
return retstr;
}
static
int config_is_option_value_valid(const char* key, const char* value) {
int i = 0, j = 0;
while (configuration_options[i].name) {
if (strcasecmp(key, configuration_options[i].name) == 0) {
/* there are limitations for the value */
while(configuration_options[i].values[j]) {
/* option values have to match in case */
if (strcmp(configuration_options[i].values[j],
value) == 0) {
/* found it. it's valid */
return 1;
}
j++;
}
/* didn't find the value */
return 0;
}
i++;
}
/* not found in conf_dat. There are no limitations for the value */
return 1;
}
static
const char* config_get_delimiter(const char* key) {
int i = 0;
while (configuration_data[i].name) {
if (strcasecmp(key, configuration_data[i].name) == 0) {
/* found */
if (configuration_data[i].read_limit == FL) {
return "\n";
} else {
return WHITESPACE;
}
}
i++;
}
return WHITESPACE;
}
static
int config_parse_option(const char* line, int tag_num,
struct option_t** options) {
int pos = 0;
struct option_t* o;
char* key, *value, *tmp;
key = quotstrtok(line, WHITESPACES, &pos);
tmp = strdup(line + pos);
enough_mem(tmp);
tmp = trim(tmp);
pos = 0;
value = quotstrtok(tmp, config_get_delimiter(key), &pos);
value = trim(value);
free(tmp);
if ( ! key || ! value) {
jlog(4, "Could not parse option %s", line);
free(key); free(value);
return -1;
}
if ( ! strlen(key) || ! strlen(value) ) {
jlog(4, "Could not parse option %s", line);
free(key); free(value);
return -1;
}
if ( ! config_is_option_key_valid(key, tag_num) ) {
char* tags = config_get_valid_tags(key);
if (tags) {
jlog(5, "%s only valid in those tags:%s", key, tags);
free(tags);
} else {
jlog(5, "unknown option: %s", key);
}
free(key); free(value);
return -1;
}
if ( ! config_is_option_value_valid(key, value) ) {
char* values = config_get_valid_values(key);
if (values) {
jlog(5, "%s may only take those values: %s",
key, values);
free(values);
return -1;
}
}
if (*options) {
o = optionlist_push(*options, key, value);
} else {
o = optionlist_init(key, value);
*options = o;
}
free(key); /* = freaky ! */
free(value);
return o != (struct option_t*) 0;
}
static
int config_parse_users(char* line, struct slist_t** sl) {
int offset = 0;
char* entry;
if (!line || !strlen(line)) {
*sl = (struct slist_t*) 0;
return 0;
}
while ((entry = quotstrtok(line, WHITESPACES, &offset))) {
if (! *sl) {
*sl = slist_init(entry);
} else {
slist_push(*sl, entry);
}
}
return 0;
}
struct portrangestruct* config_parse_portranges(const char* line) {
int offset = 0;
struct portrangestruct* prsbase, *prscur, *prs;
char* startportstr, *endportstr;
long int startport, endport;
struct slist_t* slistbase, *slistcur, *prevtmp;
if ( ! line ) {
return (struct portrangestruct*) 0;
}
slistcur = slistbase = config_split_line(line, WHITESPACES"\n");
prsbase = prs = prscur = (struct portrangestruct*) 0;
while (slistcur) {
offset = 0;
startportstr = quotstrtok(slistcur->value, ":", &offset);
if (!startportstr) {
jlog(3, "invalid port range: %s", slistcur->value);
slist_destroy(slistbase);
return (struct portrangestruct*) 0;
}
endportstr = quotstrtok(slistcur->value, ":", &offset);
if (!endportstr) {
size_t size = strlen(slistcur->value) * 2 + 1 + 1;
char* newentry = (char*) malloc(size);
snprintf(newentry, size, "%s:%s",
slistcur->value, slistcur->value);
free(slistcur->value);
slistcur->value = newentry;
offset = 0;
if (startportstr) { free(startportstr); }
startportstr = quotstrtok(slistcur->value, ":",
&offset);
endportstr = quotstrtok(slistcur->value, ":",
&offset);
}
if (!*startportstr || !*endportstr) {
jlog(3, "invalid port range: %s", slistcur->value);
if (startportstr) { free(startportstr); }
if (endportstr) { free(endportstr); }
slist_destroy(slistbase);
return (struct portrangestruct*) 0;
}
startport = strtol(startportstr, NULL, 10);
if (errno == ERANGE
&& (startport == LONG_MIN || startport == LONG_MAX)) {
jlog(3, "Error reading the starting port number in %s",
startportstr);
slist_destroy(slistbase);
free(startportstr);
if (endportstr) { free(endportstr); }
return (struct portrangestruct*) 0;
}
free(startportstr);
startportstr = (char*) 0;
endport = strtol(endportstr, NULL, 10);
if (errno == ERANGE
&& (endport == LONG_MIN || endport == LONG_MAX)) {
jlog(3, "Error reading the ending port number in %s",
endportstr);
slist_destroy(slistbase);
free(endportstr);
return (struct portrangestruct*) 0;
}
free(endportstr);
endportstr = (char*) 0;
/* startport really below or equal to endport ? */
if (startport > endport) {
jlog(4, "Port range %d:%d invalid (starting port number"
" %d is _above_ endport %d)",
startport, endport,
startport, endport);
slist_destroy(slistbase);
return (struct portrangestruct*) 0;
}
/* I want only to allow clear ranges. This is better for the
* checking, for the selection of a port and the admin is
* forced to specify his ports in a clear manner :-)
*
* That means that a portrange like:
*
* 1000:2000 1500:1600 is invalid, since 1500:1600 is
* already in 1000:2000
*
* Also invalid:
*
* 1000:2000 1800:2200 (200 portnumbers are the same)
* 1000:2000 2000:3000 (1 portnumber is the same)
* 1000:2000 500:1000 (1 portnumber is the same)
* 1000:2000 799:1200 (200 portnumbers are the same)
*
* criteria: startport and endport => let's say just pno
*
* If pno is between (or equal to) the range of another port
* specification, state that is invalid.
* ------
* What happens if we first specify
*
* 300:400
*
* and then
*
* 200:1000 ?
*
* We have to do the checking for _each_ new range to _all_
* ranges. Compare every range to every other range
*
*/
prs = prsbase;
while (prs) {
if (
(startport >= prs->startport &&
startport <= prs->endport)
||
(endport >= prs->startport &&
endport <= prs->endport)
) {
jlog(4, "Port range %d:%d invalid (parts "
"covered by another range (%d:%d))",
startport, endport,
prs->startport, prs->endport);
slist_destroy(slistbase);
return (struct portrangestruct*) 0;
}
if (
(prs->startport >= startport &&
prs->startport <= endport)
||
(prs->endport >= startport &&
prs->endport <= endport)
) {
jlog(4, "Port range %d:%d invalid (parts "
"covered by another range (%d:%d))",
prs->startport, prs->endport,
startport, endport);
slist_destroy(slistbase);
return (struct portrangestruct*) 0;
}
prs = prs->next;
}
prs = (struct portrangestruct*)
malloc(sizeof(struct portrangestruct));
if (prscur) {
prscur->next = prs;
}
prscur = prs;
if (!prsbase) {
/* the first one */
prsbase = prscur;
}
prscur->startport = startport;
prscur->endport = endport;
prscur->next = (struct portrangestruct*) 0;
/* iterate through and always chop off the first element */
prevtmp = slistcur;
slistcur = slistcur->next;
slistbase = slistcur;
free(prevtmp->value);
free(prevtmp);
prevtmp = 0;
}
return prsbase;
}
static
int config_parse_ports(char* line, struct portrangestruct** pr) {
*pr = config_parse_portranges(line);
return 0;
}
static
struct hostlist_t* config_parse_ips(struct hostlist_t** hl,
const char* line) {
const char* p = line;
int invalid = 0;
char ipbuf[16];
struct hostlist_t* h_list = (struct hostlist_t*) 0;
unsigned long int ip, netmask;
ip = -1;
netmask = -1;
if ( !line ) { *hl = (struct hostlist_t*) '\0'; };
for (;;) {
int i =0;
while (p && *p) {
/* set p to the next part */
if (isdigit((int)*p)) {
if (p == line) {
break;
}
if (isspace((int)*(p-1))) {
break;
}
}
p++;
}
if (!p || !*p) {
break;
}
/* p theoretically points to an ip */
if (isdigit((int)*p)) {
while ((isdigit((int)*p) || *p == '.') && *p != '/') {
if (i < sizeof(ipbuf)-1) {
ipbuf[i++] = *p;
}
p++;
}
ipbuf[i] = '\0';
/* jlog(9, "Found ip %s", ipbuf); */
ip = inet_addr(ipbuf);
if (ip == (unsigned long int) UINT_MAX
&& strcmp(ipbuf, BROADCAST)) {
jlog(4, "Invalid IP: %s", ipbuf);
return (struct hostlist_t *) 0;
}
if (*p == '/') {
p++; i =0;
while (!isspace((int)*p) && *p) {
if (i < sizeof(ipbuf)-1) {
ipbuf[i++] = *p;
}
p++;
}
ipbuf[i] = '\0';
/* jlog(8, "With netmask %s", ipbuf); */
invalid = 0;
if (strlen(ipbuf) < 3 && !strchr(ipbuf, '.')) {
netmask = atoi(ipbuf);
if (netmask < 0 || netmask > 32) {
invalid = 1;
}
else {
netmask = setlastbits(netmask);
}
} else {
netmask = inet_addr(ipbuf);
if (netmask == (unsigned long int)
UINT_MAX
&& strcmp(ipbuf, BROADCAST)) {
invalid = 1;
}
}
if (invalid) {
jlog(4, "Invalid netmask: %s", ipbuf);
/* Set the netmask to
* 255.255.255.255 */
netmask = -1;
}
} else {
/* no netmask specified */
netmask = -1;
}
h_list = hostlist_ip_push(hl, ip, netmask);
}
}
return h_list;
}
#define NAMESIZE 3
static
struct hostlist_t* config_parse_names(struct hostlist_t** hl,
const char* line) {
const char* p = line;
char* buf, *tmp;
char n[NAMESIZE];
int i;
size_t bufsize;
struct hostlist_t* h_list = (struct hostlist_t*) 0;
if ( !line ) { *hl = (struct hostlist_t*) '\0'; };
for (;;) {
while( p && *p && !isalpha((int)*p)) {
/* see if it is a domain like .ibm.com */
if (*p == '.') {
if (p == line) {
/* at the beginning of the line */
break;
}
if (isspace((int)*(p-1))) {
/* the dot is following a space chr */
break;
}
}
p++;
}
if ( !p || !*p ) {
break;
}
buf = (char*) malloc(1);
buf[0] = '\0';
while(*p && !isspace((int)*p)) {
i =0;
while (i < NAMESIZE - 1 && *p && !isspace((int)*p)) {
n[i++] = *p++;
}
n[i] = '\0';
tmp = buf;
bufsize = strlen(buf) + strlen(n) + 1;
buf = (char*) malloc(bufsize);
snprintf(buf, bufsize, "%s%s", tmp, n);
free(tmp);
tmp = 0;
}
/* jlog(9, "Read name %s", buf); */
h_list = hostlist_name_push(hl, buf);
}
return h_list;
}
static
int config_parse_hosts(char* line, struct hostlist_t** hl) {
config_parse_ips(hl, line);
config_parse_names(hl, line);
return 0;
}
static
int day_number(char* day) {
if (! day) {
return -1;
}
if (strcasecmp(day, "sun") == 0) {
return 0;
}
if (strcasecmp(day, "mon") == 0) {
return 1;
}
if (strcasecmp(day, "tue") == 0) {
return 2;
}
if (strcasecmp(day, "wed") == 0) {
return 3;
}
if (strcasecmp(day, "thu") == 0) {
return 4;
}
if (strcasecmp(day, "fri") == 0) {
return 5;
}
if (strcasecmp(day, "sat") == 0) {
return 6;
}
return -1;
}
static
int config_parse_time(char* line, struct timestruct** ts) {
int offset = 0, offset2, offset3;
struct timestruct* ts_end = (struct timestruct*) 0;
struct timestruct* t = (struct timestruct*) 0;
char* entry;
char* dat_str;
char* tm_str;
int i;
*ts = (struct timestruct*) 0;
if (!line || !strlen(line)) {
return 0;
}
while ((entry = quotstrtok(line, ";,", &offset))) {
if (!t) {
t = (struct timestruct*)
malloc(sizeof(struct timestruct));
} else {
/* there was an illegal time and the alllocated t
* was not used. */
}
enough_mem(t);
t->next = (struct timestruct*) 0;
t->days = (struct ilist_t*) 0;
t->start_day = t->end_day = -1;
/* Mon/Tue/Sun 17.00 - 19.00
* Wed 19.00 - Fri 20.00
*/
offset2 = 0;
dat_str = quotstrtok(entry, WHITESPACE, &offset2);
/* is dat_str a range ? */
if (strchr(dat_str, '/')) {
/* yes */
char* wday;
offset3 = 0;
while ((wday = quotstrtok(dat_str, "/", &offset3))) {
if (day_number(wday) < 0) {
jlog(5, "Invalid date (day %s not known): %s", wday, entry);
free(wday); free(dat_str); free(entry);
/* set dat_str to 0 to indicate that
* we this date is invalid */
dat_str = (char*) 0;
break;
}
if ( ! t->days ) {
t->days = ilist_init(day_number(wday));
} else {
ilist_push(t->days, day_number(wday));
}
free(wday);
}
} else {
/* no */
t->start_day = day_number(dat_str);
}
if (!dat_str) {
break;
}
free(dat_str);
tm_str = quotstrtok(entry, WHITESPACE"-", &offset2);
i = sscanf(tm_str, "%d.%d", &t->start_hour, &t->start_minute);
if (i != 2) {
i = sscanf(tm_str, "%d:%d",
&t->start_hour, &t->start_minute);
}
if (i != 2) {
jlog(5, "Illegal start time: %s", entry);
free(entry); free(tm_str);
break;
}
if (t->start_hour < 0 || t->start_hour > 23) {
jlog(5, "Illegal start time: %s", entry);
free(entry); free(tm_str);
break;
}
if (t->start_minute < 0 || t->start_minute > 59) {
jlog(5, "Illegal start time: %s", entry);
free(entry); free(tm_str);
break;
}
free(tm_str);
while(strchr(WHITESPACE"-", entry[offset2])) {
offset2++;
}
/* still have
*
* 19.00
* Fri 20.00
*/
dat_str = quotstrtok(entry, WHITESPACE, &offset2);
if ((t->end_day = day_number(dat_str)) >= 0) {
free(dat_str);
/* was a date */
if (t->days) {
jlog(5, "You can't specify a day for the end time if you specified a range of days for the start time: %s", entry);
free(dat_str); free(entry);
break;
}
dat_str = quotstrtok(entry, "\n", &offset2);
} else {
/* may be a time */
if (strchr(dat_str, '/')) {
jlog(5, "Ranges not allowed for the end time: %s", entry);
free(dat_str); free(entry);
break;
}
}
i = sscanf(dat_str, "%d.%d", &t->end_hour, &t->end_minute);
if (i != 2) {
i = sscanf(dat_str, "%d:%d",
&t->end_hour, &t->end_minute);
}
if (i != 2) {
jlog(5, "Illegal end time: %s", entry);
free(dat_str); free(entry);
break;
}
free(dat_str);
if (!t->days && t->end_day == -1) {
t->end_day = t->start_day;
}
if (t->days || t->start_day == t->end_day) {
/* start and stop time on the same day, check if
* times make sense */
if (t->end_hour*100 + t->end_minute <
t->start_hour*100 + t->start_minute) {
jlog(5, "end time before start time on the same day: %s", entry);
free(entry);
break;
}
}
if (! *ts) {
*ts = t;
ts_end = t;
} else {
ts_end->next = t;
ts_end = t;
}
free(entry);
t = (struct timestruct*) 0;
}
if (t) {
free(t);
}
return 0;
}
/* ---------------------- end parse functions -------------------- */
/* ------------------- begin config_parse_tag_* ------------------ */
static
int config_parse_tag_global(char* line, struct section_t* section) {
section->hosts = (struct hostlist_t*)malloc(sizeof(struct hostlist_t));
enough_mem(section->hosts);
section->hosts->host.ip.ip = 0;
section->hosts->host.ip.netmask = 0;
section->hosts->host.name = (char*) 0;
section->hosts->next = (struct hostlist_t*) 0;
section->hosts_exclude = (struct hostlist_t*) 0;
return 0;
}
static
struct tag_options_t config_parse_tag_options(char* line) {
struct tag_options_t to = { (char*) 0, (char*) 0 };
char* space = strpbrk(line, WHITESPACE);
char* exclude;
char* end;
if (! space) { return to; };
if (! (exclude = strstr(space, "exclude"))) {
end = strchr(space, '>');
if (! end) {
return to;
}
*end = '\0';
to.list_str = strdup(space);
enough_mem(to.list_str);
to.list_exclude_str = (char*) 0;
} else {
*exclude = '\0';
exclude = strpbrk(++exclude, WHITESPACE);
to.list_str = strdup(space);
enough_mem(to.list_str);
if (! exclude) {
jlog(4, "malformed line starting with %s", space);
free(to.list_str);
to.list_str = (char*) 0;
return to;
}
end = strchr(exclude, '>');
if (! end) {
free(to.list_str);
to.list_str = (char*) 0;
return to;
}
*end = '\0';
to.list_exclude_str = strdup(exclude);
enough_mem(to.list_exclude_str);
}
if (to.list_exclude_str) {
to.list_exclude_str = trim(to.list_exclude_str);
}
to.list_str = trim(to.list_str);
return to;
}
static
int config_parse_tag_hostlist(char* line, struct section_t* section) {
struct tag_options_t to = config_parse_tag_options(line);
int ret;
if (to.list_str == (char*) 0) {
return -1;
}
if ((ret = config_parse_hosts(to.list_str, §ion->hosts)) < 0) {
free(to.list_str);
free(to.list_exclude_str);
return ret;
}
free(to.list_str);
if ((ret = config_parse_hosts(to.list_exclude_str,
§ion->hosts_exclude)) < 0) {
free(to.list_exclude_str);
return ret;
}
if (to.list_exclude_str) {
free(to.list_exclude_str);
}
return 0;
}
static
int config_parse_tag_from(char* line, struct section_t* section) {
return config_parse_tag_hostlist(line, section);
}
static
int config_parse_tag_to(char* line, struct section_t* section) {
return config_parse_tag_hostlist(line, section);
}
static
int config_parse_tag_proxyip(char* line, struct section_t* section) {
return config_parse_tag_hostlist(line, section);
}
static
int config_parse_tag_user(char* line, struct section_t* section) {
int ret;
struct tag_options_t to = config_parse_tag_options(line);
if (to.list_str == (char*) 0) {
return -1;
}
if ((ret = config_parse_users(to.list_str, §ion->users)) < 0) {
free(to.list_str);
free(to.list_exclude_str);
return ret;
}
free(to.list_str);
if ((ret = config_parse_users(to.list_exclude_str,
§ion->users_exclude)) < 0) {
free(to.list_exclude_str);
return ret;
}
if (to.list_exclude_str) {
free(to.list_exclude_str);
}
return 0;
}
static
int config_parse_tag_port(char* line, struct section_t* section) {
struct tag_options_t to = config_parse_tag_options(line);
int ret;
if (to.list_str == (char*) 0) {
return -1;
}
if ((ret = config_parse_ports(to.list_str, §ion->ports)) < 0) {
free(to.list_str);
free(to.list_exclude_str);
return ret;
}
free(to.list_str);
if ((ret = config_parse_ports(to.list_exclude_str,
§ion->ports_exclude)) < 0) {
free(to.list_exclude_str);
return ret;
}
if (to.list_exclude_str) {
free(to.list_exclude_str);
}
return 0;
}
static
int config_parse_tag_time(char* line, struct section_t* section) {
struct tag_options_t to = config_parse_tag_options(line);
int ret;
if (to.list_str == (char*) 0) {
return -1;
}
if ((ret = config_parse_time(to.list_str, §ion->time)) < 0) {
free(to.list_str);
free(to.list_exclude_str);
return ret;
}
free(to.list_str);
if ((ret = config_parse_time(to.list_exclude_str,
§ion->time_exclude)) < 0) {
free(to.list_exclude_str);
return ret;
}
if (to.list_exclude_str) {
free(to.list_exclude_str);
}
return 0;
}
static
int config_parse_tag_servertype(char* line, struct section_t* section) {
section->servertype = SERVERTYPE_STANDALONE;
if (!line || !strlen(line)) {
return 0;
}
if (strstr(line, "inetd")) {
section->servertype = SERVERTYPE_INETD;
}
return 0;
}
/* ------- end of config_parse_tag_* ----------- */
static
int config_get_tag_name(const char* line, int begin) {
int tag_name;
if ( !line || strlen(line) < 2 ) {
return TAG_UNKNOWN;
}
if (begin == TAG_CLOSING) {
begin = 2;
} else {
begin = 1;
}
if (checkbegin(&line[ begin ], TAG_GLOBAL_STR)) {
tag_name = TAG_GLOBAL;
} else if (checkbegin(&line[ begin ], TAG_FROM_STR)) {
tag_name = TAG_FROM;
} else if (checkbegin(&line[ begin ], TAG_TO_STR)) {
tag_name = TAG_TO;
} else if (checkbegin(&line[ begin ], TAG_USER_STR)) {
tag_name = TAG_USER;
} else if (checkbegin(&line[ begin ], TAG_PORT_STR)) {
tag_name = TAG_PORT;
} else if (checkbegin(&line[ begin ], TAG_FORWARDED_STR)) {
tag_name = TAG_FORWARDED;
} else if (checkbegin(&line[ begin ], TAG_TIME_STR)) {
tag_name = TAG_TIME;
} else if (checkbegin(&line[ begin ], TAG_SERVERTYPE_STR)) {
tag_name = TAG_SERVERTYPE;
} else if (checkbegin(&line[ begin ], TAG_PROXYIP_STR)) {
tag_name = TAG_PROXYIP;
} else if (checkbegin(&line[ begin ], TAG_PROXYPORT_STR)) {
tag_name = TAG_PROXYPORT;
} else {
tag_name = TAG_UNKNOWN;
}
return tag_name;
}
static
int config_get_tag_num(struct ilist_t* tag_list) {
int ret = 0;
if (tag_list) {
tag_list = tag_list->next;
}
while (tag_list) {
ret = ret | tag_list->value;
tag_list = tag_list->next;
}
return ret;
}
static
int ld(int a) {
/* calculate logarithmus dualis */
int i;
int exp;
if (a == 0) {
return 0;
}
i = 1;
exp = 0;
while (a > i) {
exp++;
i *= 2;
}
return exp;
}
struct section_t* config_parse_section(FILE* file,
char* line,
int tag_name,
int* id,
struct ilist_t* tag_list) {
int tag_type;
int tag_num = config_get_tag_num(tag_list);
int ret;
char* recline;
int (*handler_funcs[ TAG_NUMBER + 1 ])(char*, struct section_t*);
struct section_t *section = (struct section_t*)
malloc(sizeof(struct section_t));
enough_mem(section);
config_section_init(section);
handler_funcs[ ld(TAG_GLOBAL) ] = config_parse_tag_global;
handler_funcs[ ld(TAG_FROM) ] = config_parse_tag_from;
handler_funcs[ ld(TAG_TO) ] = config_parse_tag_to;
handler_funcs[ ld(TAG_USER) ] = config_parse_tag_user;
/* set the handler to anything */
handler_funcs[ ld(TAG_FORWARDED) ] = config_parse_tag_user;
handler_funcs[ ld(TAG_PORT) ] = config_parse_tag_port;
handler_funcs[ ld(TAG_TIME) ] = config_parse_tag_time;
handler_funcs[ ld(TAG_SERVERTYPE) ] = config_parse_tag_servertype;
handler_funcs[ ld(TAG_PROXYIP) ] = config_parse_tag_proxyip;
handler_funcs[ ld(TAG_PROXYPORT) ] = config_parse_tag_port;
ret = (*handler_funcs[ ld(tag_name) ])(line, section);
(*id)++;
section->id = *id;
while ((line = config_read_line(file))) {
if (line[0] == '<') {
/* probably a tag */
if (line[1] == '/') {
tag_type = TAG_CLOSING;
} else {
tag_type = TAG_OPENING;
}
tag_name = config_get_tag_name(line, tag_type);
if (tag_name == TAG_UNKNOWN) {
jlog(3, "invalid tag: %s", line);
config_error = 1;
return (struct section_t*) 0;
}
section->tag_name = tag_name;
if (tag_type == TAG_OPENING) {
struct section_t* s, *t;
ilist_push(tag_list, tag_name);
recline = strdup(line);
enough_mem(recline);
s = config_parse_section(file, recline,
tag_name, id, tag_list);
free(recline);
if (!s) {
break;
}
if (! section->nested) {
section->nested = s;
} else {
/* already a nested */
t = section->nested;
while (t->next) {
t = t->next;
}
t->next = s;
s->next = (struct section_t*) 0;
}
} else {
/* tag_type = TAG_CLOSING */
int last_opened;
if (ilist_empty(tag_list)) {
jlog(3, "Closed a tag where no one "
"was open: %s", line);
config_error = 1;
return (struct section_t*) 0;
}
last_opened = ilist_pop(tag_list);
if (last_opened != tag_name) {
jlog(3, "Closed a tag that was not "
"opened before: %s", line);
config_error = 1;
return (struct section_t*) 0;
}
break; /* go out of while */
}
} else {
/* not a tag: an option */
recline = strdup(line);
enough_mem(recline);
ret = config_parse_option(recline, tag_num,
§ion->options);
free(recline);
if (ret < 0) {
/* don't throw an error - no, do it :-) */
/* return (struct section_t*) 0; */
config_error = 1;
return (struct section_t*) 0;
}
}
/* continue goes here */
}
return section;
}
void config_set_limits(struct section_t* section) {
struct option_t* opt;
if (! section) {
return;
}
config_set_limits(section->next);
config_set_limits(section->nested);
section->limit = LONG_MAX;
opt = section->options;
while (opt) {
if (strcasecmp(opt->key, "limit") == 0) {
section->limit = conv_char2long(opt->value, LONG_MAX);
}
opt = opt->next;
}
}
/* check for all sections if connection_counter is less than or equal to
* limit
*
* return 0 if everything is okay and
* return 1 if somewhere a limit has been exceeded
* */
static
int config_check_limits(const struct section_t* section) {
if ( ! section ) {
return 0;
}
if (section->connection_counter > section->limit) {
return 1;
}
if (config_check_limits(section->nested)) {
return 1;
}
return config_check_limits(section->next);
}
struct timestruct* timestruct_clone(const struct timestruct* orig) {
struct timestruct* new;
if (! orig) {
return (struct timestruct*) 0;
}
new = (struct timestruct*) malloc(sizeof(struct timestruct));
new->days = ilist_clone(orig->days);
new->start_day = orig->start_day;
new->start_hour = orig->start_hour;
new->start_minute = orig->start_minute;
new->end_day = orig->end_day;
new->end_hour = orig->end_hour;
new->end_minute = orig->end_minute;
new->next = timestruct_clone(orig->next);
return new;
}
struct section_t* section_clone(const struct section_t* orig) {
struct section_t* new;
if (! orig) {
return (struct section_t*) 0;
}
new = (struct section_t*) malloc(sizeof(struct section_t));
new->tag_name = orig->tag_name;
new->hosts = hostlist_clone(orig->hosts);
new->hosts_exclude = hostlist_clone(orig->hosts_exclude);
new->users = slist_clone(orig->users);
new->users_exclude = slist_clone(orig->users_exclude);
new->forwarded = slist_clone(orig->forwarded);
new->forwarded_exclude = slist_clone(orig->forwarded_exclude);
new->ports = portrangestruct_clone(orig->ports);
new->ports_exclude = portrangestruct_clone(orig->ports_exclude);
new->time = timestruct_clone(orig->time);
new->time_exclude = timestruct_clone(orig->time_exclude);
new->options = optionlist_clone(orig->options);
new->nested = section_clone(orig->nested);
new->next = section_clone(orig->next);
new->connection_counter = orig->connection_counter;
new->id = orig->id;
new->servertype = orig->servertype;
new->limit = orig->limit;
return new;
}
FILE* open_file(const char* fname) {
FILE* conf;
conf = fopen(fname, "r");
if (!conf) {
perror("Couldn't open the configuration file");
jlog(1, "Couldn't open the configuration file %s: %s", fname,
strerror(errno));
return 0;
}
return conf;
}
int config_read_sections(FILE* file) {
char* line, *recline;
struct ilist_t* tag_list = ilist_init(-1);
struct section_t* section;
int tag_name;
int id = 0;
int counter = 0;
base_section = (struct section_t*) 0;
section = (struct section_t*) 0;
/* init recursion */
do {
do {
line = config_read_line(file);
if ( line && line[0] != '<' ) {
jlog(6, "Garbage in config file: %s", line);
}
} while (line && (line[0] != '<' || line[1] == '/'));
if ( !line ) {
break;
}
counter++;
if ( counter == 1 && ! strncasecmp(&line[1],
"global", strlen("global")) == 0) {
jlog(4, "global section is not the first one. "
"Exiting.");
return -1;
}
while (section && section->next) {
section = section->next;
}
tag_name = config_get_tag_name(line, TAG_OPENING);
if (tag_name == TAG_UNKNOWN) {
jlog(5, "Invalid tag: %s", line);
continue;
}
ilist_push(tag_list, tag_name);
if (section) {
recline = strdup(line);
enough_mem(recline);
section->next = config_parse_section(file,
recline, tag_name, &id, tag_list);
free(recline);
if (!section) {
break;
}
} else {
recline = strdup(line);
enough_mem(recline);
section = config_parse_section(file,
recline, tag_name, &id, tag_list);
free(recline);
if (!section) {
break;
}
section->next = (struct section_t*) 0;
base_section = section;
}
} while (line && !config_error);
if (config_error) {
return -1;
}
if (! base_section) {
jlog(3, "No global section in configuration file");
return -1;
}
if ( ! ilist_empty(tag_list)) {
jlog(3, "Not all tags have been closed");
return -1;
}
ilist_destroy(tag_list);
tag_list = (struct ilist_t*) 0;
/* set limit values */
config_set_limits(base_section);
if (debug) {
config_debug_outputsections();
}
return 0;
}
int read_config(const char* fname) {
int ret;
char* filename = chrooted_path(fname);
FILE* conffile = open_file(filename);
if (!conffile) {
free(filename);
return -1;
}
jlog(9, "opened file %s", filename);
free(filename);
ret = config_read_sections(conffile);
fclose(conffile);
if (ret == -1) {
/* an error - try to activate backup */
if (config_activate_backup() < 0) {
/* failed */
return -1;
}
/* backup successfully activated */
jlog(2, "New configuration NOT active - backup configuration"
" has been re-enabled");
} else {
/* destroy the backup and create a new copy, we've read a
* fresh configuration */
config_create_backup();
}
optionlist_destroy( option_list );
option_list = (struct option_t*) 0;
if (base_section) {
ret = config_shrink_config(-1, /* source IP */
-1, /* dest IP */
(char*) 0, /* dest name */
0, /* dest port */
(char*) 0, /* dest user */
-1, /* forwarded IP */
(char*) 0, /* forwarded destination */
0, /* forwarded destinationport */
(char*) 0, /* forwarded username */
0, /* set no specific time */
-1, /* proxy ip */
0, /* proxy port */
srvinfo.servertype, /* global variable */
&hostcache,
TAG_GLOBAL | TAG_SERVERTYPE);
}
/* switch debug on if there is just one process but leave it if
* we are running from inetd */
debug = srvinfo.servertype == SERVERTYPE_STANDALONE
&& !srvinfo.multithread
&& config_get_ioption("debuglevel", 6) > 8;
if (ret != -1) {
/* cache the UIDs */
ret = save_runasuser_uid();
}
if (ret == 0) {
/* dump configuration */
if (debug) {
config_debug_outputsections();
/* option_list */
printf("\n\nOptionlist:\n");
config_debug_output_options( config_get_option_list(), "" );
}
}
return ret;
}
/* --------- begin destruction of structures --------- */
void config_destroy_hostlist(struct hostlist_t* hlist) {
if (! hlist) { return; }
config_destroy_hostlist( hlist->next );
if (hlist->host.name) { free(hlist->host.name); }
free(hlist);
}
void config_destroy_slist(struct slist_t* slist) {
slist_destroy(slist);
}
void config_destroy_portrange(struct portrangestruct* plist) {
if ( !plist ) { return; }
config_destroy_portrange( plist->next );
free(plist);
}
void config_destroy_optionlist(struct option_t* olist) {
optionlist_destroy(olist);
}
void config_destroy_section(struct section_t* sectlist) {
if ( ! sectlist ) { return; }
config_destroy_section( sectlist->nested );
config_destroy_section( sectlist->next );
config_destroy_hostlist( sectlist->hosts );
config_destroy_hostlist( sectlist->hosts_exclude );
config_destroy_slist( sectlist->users );
config_destroy_slist( sectlist->users_exclude );
config_destroy_portrange( sectlist->ports );
config_destroy_portrange( sectlist->ports_exclude );
config_destroy_optionlist( sectlist->options );
/* finally destroy the section itself */
free(sectlist);
}
void config_destroy_sectionconfig() {
config_destroy_section(base_section);
base_section = (struct section_t*) 0;
}
/* --------- end destruction of structures --------- */
/* this function is called after each final shrink of the configuration. If
* the login fails for some reason, the user will issue another
* authentification and the new shrink operations will deal with a fresh
* configuration.
*
* If the user succeeds instead with login, the optionlist of the former
* configuration is active and the backup will be deleted (some functions
* above :-) */
int config_activate_backup() {
/* move backup into life */
if ( ! backup_base_section ) {
return -1;
}
/* free shrinked config */
config_destroy_section(base_section);
/* the shrinked configuration is still active through the option
* list but it will be regenerated upon a new shrink, i.e. a new
* login */
/* create a new clone of the backup */
base_section = backup_base_section;
backup_base_section = (struct section_t*) 0;
config_create_backup();
jlog(8, "Backup configuration activated");
return 0;
}
/* -------------------- begin lookup functions ------------------ */
int config_host_valid(const struct hostent_list* h) {
long lookup_diff = config_get_loption("hostcachetimeout", 28800);
time_t now = time(NULL);
if (!h) {
return 0;
}
return (now - h->lookup_time) <= (lookup_diff);
}
const struct hostent_list* config_forward_lookup(struct hostent_list** hl,
const char* name) {
const struct hostent_list* h;
struct hostent* he;
unsigned long int addr;
if (config_get_bool("forwardlookups") == 0
||
config_get_bool("dnslookups") == 0) {
return (struct hostent_list*) 0;
}
if (! name || ! hl) {
return (struct hostent_list*) 0;
}
/* jlog(9, "looking up %s", name); */
if (*hl) {
h = hostent_get(*hl, -1, name);
if (config_host_valid(h)) {
/* jlog(9, "Could use cache for forward lookup of %s",
name); */
return h;
} else {
/* jlog(9, "Could not use cache for forward lookup of %s",
name); */
hostent_delete(*hl, h);
}
}
addr = inet_addr(name);
if (addr == UINT_MAX) {
/* it is really a name */
he = gethostbyname(name);
} else {
he = gethostbyaddr((char*) &addr, sizeof(addr), AF_INET);
}
if (*hl) {
hostent_push(*hl, he, -1, name);
} else {
*hl = hostent_init(he, -1, name);
}
return hostent_get(*hl, -1, name);
}
const struct hostent_list* config_reverse_lookup(struct hostent_list** hl,
unsigned long int ip) {
const struct hostent_list* h;
struct hostent* he;
if (config_get_bool("reverselookups") == 0
||
config_get_bool("dnslookups") == 0) {
return (struct hostent_list*) 0;
}
if (! hl) {
return (struct hostent_list*) 0;
}
/* jlog(9, "looking up %s", inet_ntoa(*((struct in_addr*) &ip))); */
if (*hl) {
h = hostent_get(*hl, ip, (char*) 0);
if (config_host_valid(h)) {
/* jlog(9, "Could use cache for reverse lookup of %s",
inet_ntoa(*((struct in_addr*) &ip)));
*/
return h;
} else {
hostent_delete(*hl, h);
/* jlog(9, "Could not use cache for reverse lookup of %s",
inet_ntoa(*((struct in_addr*) &ip)));
*/
}
}
he = gethostbyaddr((char*) &ip, sizeof(ip), AF_INET);
if (*hl) {
hostent_push(*hl, he, ip, (char*) 0);
} else {
*hl = hostent_init(he, ip, (char*) 0);
}
return hostent_get(*hl, ip, (char*) 0);
}
/* -------------------- end lookup functions ------------------ */
/* ------------------ begin matching functions ------------------- */
static
int config_match_domain(const char* name, const char* pattern) {
const char* start;
/* the hostname may not be shorter than the pattern
*
* pattern: .foobar.mit.edu
* name: bla.mit.edu
*
* => won't match
*/
if (strlen(name) < strlen(pattern)) {
return 0;
}
/* pattern has to start with a dot */
if (pattern[0] != '.') {
return 0;
}
start = name + strlen(name) - strlen(pattern);
return !strcasecmp(start, pattern);
}
static
int config_search_ip(struct ullist_t* ul,
unsigned long int ip, unsigned long int netmask) {
while (ul) {
if ((ul->value & netmask) == (ip & netmask)) {
return 1;
}
ul = ul->next;
}
return 0;
}
int config_search_name(struct slist_t* sl, const char* name) {
if ( ! name ) {
return 0;
}
while (sl) {
if(config_match_domain(name, sl->value)) {
return 1;
}
sl = sl->next;
}
return 0;
}
static
int config_search_namepattern(struct slist_t* sl, const char* namepat) {
if ( ! namepat ) {
return 0;
}
while (sl) {
if (config_match_domain(sl->value, namepat)) {
return 1;
}
sl = sl->next;
}
return 0;
}
static
int match_addrs(unsigned long int ip, const char* name,
unsigned long int ipwnet, unsigned long int netmask,
const char* namepat, struct hostent_list** hl) {
/* ip, name want to connect *
* ipwnet/netmask, namepat are allowed to connect */
const struct hostent_list* host;
struct in_addr iaddr;
struct sockaddr_in sin;
if (name) {
if (namepat) {
/* try without lookup */
/* check for exact match */
if (strcasecmp(name, namepat) == 0) {
return 1;
}
/* check for domain equivalence */
if (config_match_domain(name, namepat)) {
return 1;
}
/* see if name is a device */
if (get_interface_ip(name, &sin) == 0) {
struct ullist_t* ul;
int ret;
ul = ullist_init(sin.sin_addr.s_addr);
ret = config_search_ip(ul, ipwnet, netmask);
ullist_destroy(ul);
if (ret == 1) {
return 1;
}
}
/* look up name and try to match the aliases of the
* name's host against the allowed namepattern and
* ipwnet/netmask */
host = config_forward_lookup(hl, name);
if (!host) {
jlog(5, "Could not look up (1) name %s", name);
} else {
/* see if one alias of the host matches the
* allowed pattern */
if (config_search_namepattern(
host->aliases_list, namepat)) {
return 1;
}
/* see if one other IP of the host matches
* the IP/netmask values */
if (config_search_ip(host->addr_list,
ipwnet, netmask)) {
return 1;
}
}
}
}
if (ip != -1) {
#ifndef IF_NAMESIZE
# define IF_NAMESIZE IFNAMSIZ
#endif
char iface[IF_NAMESIZE + 1];
/* try without lookup */
if ((ip & netmask) == (ipwnet & netmask)) {
return 1;
}
/* see if the IP belongs to a device */
memset(&sin, 0, sizeof(struct sockaddr_in));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = ip;
if (get_interface_name(sin, iface) == 0) {
if (namepat && strcasecmp(iface, namepat) == 0) {
return 1;
}
}
/* look up the IP and try to match */
host = config_reverse_lookup(hl, ip);
if (!host) {
iaddr.s_addr = ip;
jlog(5, "Could not look up IP %s", inet_ntoa(iaddr));
} else {
/* see if one IP of the host is allowed by the
* IP/netmask fields */
if (config_search_ip(host->addr_list,
ipwnet, netmask)) {
return 1;
}
/* see if one alias hostname of the host is in the
* allowed pattern */
if (config_search_namepattern(host->aliases_list,
namepat)) {
return 1;
}
}
/* look up the name and try to match */
if (namepat) {
host = config_forward_lookup(hl, namepat);
if (!host) {
jlog(5, "Could not look up (2) name %s",
namepat);
} else {
/* see if one IP of the host is allowed by the
* IP/netmask fields */
if (config_search_ip(host->addr_list,
ip, netmask)) {
return 1;
}
/* see if one alias hostname of the host is
* in the allowed pattern */
if (config_search_namepattern(
host->aliases_list, name)) {
return 1;
}
}
}
}
/* didn't find anything */
return 0;
}
/* ------------------ end matching functions ------------------- */
/* ------------------ begin keep_matching functions ---------------- */
static
struct section_t*
config_process_section_increase_conn_counter(struct section_t* section) {
section->connection_counter++;
return section;
}
static
struct section_t*
config_process_section_decrease_conn_counter(struct section_t* section) {
if (section->connection_counter > 0) {
section->connection_counter--;
}
return section;
}
static
struct section_t*
config_process_section_nothing(struct section_t* section) {
return section;
}
static
struct section_t*
config_process_section_shrink(struct section_t* section) {
struct section_t* pRet;
pRet = section->next;
section->next = (struct section_t*) 0;
config_destroy_section(section);
return pRet;
}
static
int config_matches_hosts(unsigned int ip, const char* name,
struct hostlist_t* hlist,
struct hostent_list** hostc_list) {
while (hlist) {
if (match_addrs(ip, name,
hlist->host.ip.ip, hlist->host.ip.netmask,
hlist->host.name,
hostc_list)
||
match_addrs(hlist->host.ip.ip, hlist->host.name,
ip, hlist->host.ip.netmask,
name,
hostc_list)) {
return 1;
}
hlist = hlist->next;
}
return 0;
}
static
int config_matches_user(const char* name, struct slist_t* ulist) {
while(ulist) {
if (0 == strcmp(name, ulist->value)
|| 0 == strcmp(ulist->value, "*")) {
return 1;
}
ulist = ulist->next;
}
return 0;
}
static
int config_matches_port(int port, struct portrangestruct* plist) {
while(plist) {
if (port >= plist->startport && port <= plist->endport) {
return 1;
}
plist = plist->next;
}
return 0;
}
static
int config_matches_time(struct timestruct* tlist, time_t specific_time) {
time_t nowseconds;
struct tm* now;
int starttime, endtime, nowtime;
int startmatch, endmatch;
struct ilist_t* il;
if (specific_time == 0) {
nowseconds = time(NULL);
} else {
nowseconds = specific_time;
}
now = localtime(&nowseconds);
while(tlist) {
starttime = tlist->start_hour * 100;
starttime += tlist->start_minute;
endtime = tlist->end_hour * 100;
endtime += tlist->end_minute;
nowtime = now->tm_hour * 100;
nowtime += now->tm_min;
/* These combinations match in a range:
*
* Mon Tue Wed Thu Fri Sat Sun
* s e (1)
* e s (2)
*
* (1) means: start < day && day < end
* (2) means: start < day && end < start
*/
if ( ! tlist->days &&
((tlist->start_day <= now->tm_wday
&& now->tm_wday <= tlist->end_day)
||
(tlist->start_day <= now->tm_wday
&& tlist->end_day <= tlist->start_day))) {
/* the day range matches */
startmatch = 0;
if (tlist->start_day == now->tm_wday) {
/* check for start time */
if (starttime <= nowtime) {
startmatch = 1;
}
} else {
/* we are on a day after the start */
startmatch = 1;
}
endmatch = 0;
if (tlist->end_day == now->tm_wday) {
/* check for end time */
if (nowtime <= endtime) {
endmatch = 1;
}
} else {
/* we are on a day before the end */
endmatch = 1;
}
if (startmatch && endmatch) {
return 1;
}
}
/* check for a date list */
il = tlist->days;
while (il) {
if (il->value == now->tm_wday) {
/* the day list matches */
if (tlist->days && starttime <= nowtime
&& nowtime <= endtime) {
return 1;
}
}
il = il->next;
}
tlist = tlist->next;
}
return 0;
}
static
struct section_t* config_match_section(struct section_t* section,
unsigned long int from_ip,
unsigned long int to_ip,
const char* to_name,
unsigned int to_port,
const char* to_user,
unsigned long int forw_ip,
const char* forw_name,
unsigned int forw_port,
const char* forw_user,
time_t specific_time,
unsigned long int proxy_ip,
unsigned int proxy_port,
int c_servertype,
struct hostent_list** hostc_list,
int config_state,
int in_forwarded_tag,
struct section_t* (*section_function_match)
(struct section_t*),
struct section_t* (*section_function_nomatch)
(struct section_t*)
) {
if (in_forwarded_tag) {
to_ip = forw_ip;
to_name = forw_name;
to_port = forw_port;
to_user = forw_user;
}
if (section->next) {
section->next = config_match_section(section->next, from_ip,
to_ip, to_name, to_port, to_user,
forw_ip, forw_name, forw_port, forw_user,
specific_time,
proxy_ip, proxy_port,
c_servertype,
hostc_list,
config_state,
in_forwarded_tag,
section_function_match,
section_function_nomatch);
}
if (section->tag_name == TAG_FROM && (config_state & TAG_FROM)) {
/* if NOT included OR excluded ... delete */
if (
! config_matches_hosts ( from_ip, (char*) 0,
section->hosts, hostc_list)
||
config_matches_hosts ( from_ip, (char*) 0,
section->hosts_exclude, hostc_list)) {
return section_function_nomatch(section);
}
}
if (section->tag_name == TAG_TO && (config_state & TAG_TO)) {
/* if NOT included OR excluded ... delete */
if (
! config_matches_hosts ( to_ip, to_name,
section->hosts, hostc_list)
||
config_matches_hosts ( to_ip, to_name,
section->hosts_exclude, hostc_list)) {
return section_function_nomatch(section);
}
}
if (section->tag_name == TAG_PROXYIP && (config_state & TAG_PROXYIP)) {
/* if NOT included OR excluded ... delete */
if (
! config_matches_hosts ( proxy_ip, (char*) 0,
section->hosts, hostc_list)
||
config_matches_hosts ( proxy_ip, (char*) 0,
section->hosts_exclude, hostc_list)) {
return section_function_nomatch(section);
}
}
if (section->tag_name == TAG_PROXYPORT
&& (config_state & TAG_PROXYPORT)) {
/* if NOT included OR excluded ... delete */
if (
! config_matches_port ( proxy_port,
section->ports)
||
config_matches_port ( proxy_port,
section->ports_exclude)) {
return section_function_nomatch(section);
}
}
if (to_user && section->tag_name == TAG_USER && (config_state & TAG_USER)) {
/* if NOT included OR excluded ... delete */
if (
! config_matches_user ( to_user,
section->users)
||
config_matches_user ( to_user,
section->users_exclude)) {
return section_function_nomatch(section);
}
}
if (section->tag_name == TAG_TIME && (config_state & TAG_TIME)) {
/* if NOT included OR excluded ... delete */
if (
! config_matches_time (section->time, specific_time)
||
config_matches_time (section->time_exclude,
specific_time)) {
return section_function_nomatch(section);
}
}
if (section->tag_name == TAG_FORWARDED
&& (config_state & TAG_FORWARDED)) {
if ( ! forw_user ) {
return section_function_nomatch(section);
}
}
if (to_port != 0 && section->tag_name == TAG_PORT
&& (config_state & TAG_PORT)) {
/* if NOT included OR excluded ... delete */
if (
! config_matches_port ( to_port,
section->ports)
||
config_matches_port ( to_port,
section->ports_exclude)) {
return section_function_nomatch(section);
}
}
if (section->tag_name == TAG_SERVERTYPE
&& (config_state & TAG_SERVERTYPE)) {
if (c_servertype != section->servertype) {
return section_function_nomatch(section);
}
}
if (section->nested && (section->tag_name & config_state)) {
if (section->tag_name == TAG_FORWARDED) {
in_forwarded_tag = 1;
}
section->nested = config_match_section(section->nested,
from_ip,
to_ip, to_name, to_port, to_user,
forw_ip, forw_name, forw_port, forw_user,
specific_time,
proxy_ip, proxy_port,
c_servertype,
hostc_list, config_state,
in_forwarded_tag,
section_function_match,
section_function_nomatch);
}
return section_function_match(section);
}
/* ------------------ end keep matching functions ------------------- */
/* --------------- begin option list creating functions ---------------- */
/* recursively create an optionlist and append the nested and following
* sections */
struct option_t* config_generate_option_list(struct section_t* section,
int config_state) {
struct option_t* opts;
if (section->tag_name & config_state) {
opts = optionlist_clone(section->options);
if (section->nested) {
opts = optionlist_append(opts,
config_generate_option_list(section->nested,
config_state));
}
} else {
opts = (struct option_t*) 0;
}
if (section->next) {
opts = optionlist_append(opts,
config_generate_option_list(section->next,
config_state));
}
return opts;
}
const struct option_t* config_get_option_list() {
return option_list;
}
void config_option_list_delete(const char* key) {
if ( option_list ) {
optionlist_delete_key(option_list, key);
}
}
void config_option_list_add(const char* key, const char* value) {
if ( option_list ) {
optionlist_push(option_list, key, value);
}
}
/* --------------- end option list creating functions ---------------- */
/* ------------------- begin exported functions ------------------- */
int config_shrink_config(unsigned long int from_ip,
unsigned long int to_ip,
const char* to_name,
unsigned int to_port,
const char* to_user,
unsigned long int forw_ip,
const char* forw_name,
unsigned int forw_port,
const char* forw_user,
time_t specific_time,
unsigned long int proxy_ip,
unsigned int proxy_port,
int c_servertype,
struct hostent_list** hostc_list,
int config_state) {
/* transparent proxy, TO and PORT have been read with
* getsockname */
/* ???????
* config_state = TAG_GLOBAL | TAG_FROM | TAG_TO | TAG_PORT;
* jlog(9, "Checking TAG_GLOBAL | TAG_FROM | TAG_TO | TAG_PORT"); */
config_match_section(base_section, from_ip,
to_ip, to_name, to_port, to_user,
forw_ip, forw_name, forw_port, forw_user,
specific_time,
proxy_ip, proxy_port,
c_servertype, hostc_list, config_state,
0, /* not in a forwarded tag at first */
config_process_section_nothing, /* match */
config_process_section_shrink); /* no match */
optionlist_destroy( option_list );
option_list = (struct option_t*) 0;
option_list = config_generate_option_list(base_section, config_state);
/* dump configuration */
if (debug) {
printf("\n--------------------Shrinking------------------\n");
config_debug_outputsections();
printf("\n\nOptionlist:\n");
config_debug_output_options( config_get_option_list(), "");
}
if (srvinfo.ready_to_serve >= SVR_LAUNCH_READY) {
log_detect_log_change();
}
return 0;
}
int config_counter_increase(unsigned long int from_ip,
unsigned long int proxy_ip,
unsigned int proxy_port,
time_t specific_time) {
config_match_section(base_section, from_ip,
-1, /* to ip */
(char*) 0, /* to name */
0, /* to port */
(char*) 0, /* to user */
-1, /* forwarded ip */
(char*) 0, /* forwarded name */
0, /* forwarded port */
(char*) 0, /* forwarded user */
specific_time, /* specific_time */
proxy_ip, /* proxy ip */
proxy_port, /* proxy port */
srvinfo.servertype, /* global variable */
&hostcache,
TAG_CONNECTED,
0, /* not in a forwarded tag */
config_process_section_increase_conn_counter, /*match*/
config_process_section_nothing); /* no match */
return 0;
}
int config_counter_decrease(unsigned long int from_ip,
unsigned long int proxy_ip,
unsigned int proxy_port,
time_t specific_time) {
config_match_section(base_section, from_ip,
-1, /* to ip */
(char*) 0, /* to name */
0, /* to port */
(char*) 0, /* to user */
-1, /* forwarded ip */
(char*) 0, /* forwarded name */
0, /* forwarded port */
(char*) 0, /* forwarded user */
specific_time, /* specific_time */
proxy_ip, /* proxy ip */
proxy_port, /* proxy port */
srvinfo.servertype, /* global variable */
&hostcache,
TAG_CONNECTED,
0, /* not in a forwarded tag */
config_process_section_decrease_conn_counter, /*match*/
config_process_section_nothing); /* no match */
return 0;
}
int config_check_limit_violation() {
return config_check_limits(base_section);
}
void config_counter_add_connected(struct connliststruct* conn_cli) {
struct connliststruct* cls = conn_cli;
while (cls) {
config_counter_increase(cls->from_ip,
cls->proxy_ip,
cls->proxy_port,
cls->start_time);
cls = cls->next;
}
}
const char* config_get_default_value(const char* key) {
const char* val = (char*) 0;
int i = 0;
while (configuration_data[i].name) {
if (strcasecmp(configuration_data[i].name, key) == 0) {
val = configuration_data[i].defaultvalue;
if (val) {
jlog(8, "Did not find configuration "
"entry for \"%s\", using "
"\"%s\" as default", key, val);
/* insert into option list */
config_option_list_add(key, val);
}
break;
}
i++;
}
return val;
}
const char* config_get_option(const char* key) {
const struct option_t* options = config_get_option_list();
const char* val = (char*) 0;
if ( ! key ) {
return (char*) 0;
}
while (key && options) {
if (options->key && strcasecmp(options->key, key) == 0) {
val = options->value;
}
options = options->next;
}
if ( ! val ) {
/* option not found, look for default value */
val = config_get_default_value(key);
}
return val;
}
struct slist_t* config_get_option_array(const char* key) {
const struct option_t* options = config_get_option_list();
struct slist_t* slist = (struct slist_t*) 0;
if ( ! key ) {
return (struct slist_t*) 0;
}
while (key && options) {
if (options->key && strcasecmp(options->key, key) == 0) {
/* element matches */
if (! slist) {
slist = slist_cinit(options->value);
} else {
slist_cpush(slist, options->value);
}
}
options = options->next;
}
return slist;
}
long conv_char2long(const char* s, long err_return) {
long retval;
if ( ! s ) {
return err_return;
}
retval = strtol(s, NULL, 10);
if (errno == ERANGE
&& (retval == LONG_MIN || retval == LONG_MAX)) {
return err_return;
}
return retval;
}
unsigned long conv_char2ulong(const char* s, unsigned long err_return) {
unsigned long retval;
if ( ! s ) {
return err_return;
}
retval = strtoul(s, NULL, 10);
if (errno == ERANGE
&& (retval == LONG_MIN || retval == LONG_MAX)) {
return err_return;
}
return retval;
}
long config_get_loption(const char* key, long err_return) {
const char* optstr = config_get_option(key);
return conv_char2long(optstr, err_return);
}
unsigned long config_get_uloption(const char* key, unsigned long err_return) {
const char* optstr = config_get_option(key);
return conv_char2ulong(optstr, err_return);
}
unsigned long config_get_addroption(const char* key, unsigned long err_return){
const char* optstr = config_get_option(key);
struct sockaddr_in sin;
if (!optstr) {
return err_return;
}
if (get_interface_ip(optstr, &sin) == 0) {
/* there was an interface with this name */
return sin.sin_addr.s_addr;
}
if (inet_aton(optstr, &sin.sin_addr)) {
/* IP was recognized */
return sin.sin_addr.s_addr;
}
jlog(6, "Invalid IP/interface: %s", optstr);
return err_return;
}
int config_get_ioption(const char* key, int err_return) {
return (int) config_get_loption(key, (long) err_return);
}
float config_get_foption(const char* key, float err_return) {
const char* optstr = config_get_option(key);
float retval;
int success;
if ( ! optstr ) {
return err_return;
}
success = sscanf(optstr, "%f", &retval);
if (success) {
return retval;
} else {
return err_return;
}
}
int config_get_bool(const char* key) {
const char* optstr = config_get_option(key);
if (!optstr) {
jlog(5, "There was no value for %s, using \"off\"", key);
return 0;
}
if (strcasecmp(optstr, "on") == 0) {
return 1;
}
if (strcasecmp(optstr, "off") == 0) {
return 0;
}
if (strcasecmp(optstr, "yes") == 0) {
return 1;
}
if (strcasecmp(optstr, "no") == 0) {
return 0;
}
if (strcasecmp(optstr, "true") == 0) {
return 1;
}
if (strcasecmp(optstr, "false") == 0) {
return 0;
}
if (strcmp(optstr, "1") == 0) {
return 1;
}
if (strcmp(optstr, "0") == 0) {
return 0;
}
jlog(5, "There was no value for %s, using \"off\"", key);
return 0;
}
unsigned long int config_get_size(const char* key,
unsigned long int err_return) {
const char* optstr = config_get_option(key);
unsigned long int size;
int i, nondigits;
char multiplier;
if (strcasecmp(optstr, "unlimited") == 0) {
return ULONG_MAX;
}
for (i = 0, nondigits = 0; i < strlen(optstr); i ++) {
if ( ! isdigit((int) optstr[i]) ) {
nondigits = 1;
break;
}
}
if ( ! nondigits ) {
/* only a number was given */
return config_get_uloption(key, err_return);
}
i = sscanf(optstr, "%lu%c", &size, &multiplier);
if (i != 2) {
jlog(5, "%s not a valid size", optstr);
return err_return;
}
multiplier = toupper((int) multiplier);
switch (multiplier) {
case 'B':
return size;
case 'K':
return size * 1024;
case 'M':
return size * 1024 * 1024;
case 'G':
return size * 1024 * 1024 * 1024;
default:
jlog(5, "%s not a valid size", optstr);
}
return err_return;
}
int config_compare_option(const char* key, const char* compare) {
const char* option = config_get_option(key);
if (! option || ! compare) {
return 0;
}
return !strcasecmp(option, compare);
}
void config_delete_config() {
config_destroy_section(base_section);
base_section = (struct section_t*) 0;
optionlist_destroy( option_list );
option_list = (struct option_t*) 0;
hostent_destroy(hostcache);
hostcache = (struct hostent_list*) 0;
}
void config_delete_master() {
config_destroy_section(base_section);
base_section = (struct section_t*) 0;
}
void config_create_backup() {
if (backup_base_section) {
config_destroy_section(backup_base_section);
}
backup_base_section = section_clone(base_section);
}
void config_delete_backup() {
config_destroy_section(backup_base_section);
backup_base_section = (struct section_t*) 0;
}
/* ------------------- end exported functions ------------------- */
/*****-------------------- begin DEBUG ---------------------******/
void config_debug_output_options ( const struct option_t* opt,
char* prefix ) {
while (opt) {
printf("%sKey: %s --> %s\n",
prefix, opt->key, opt->value);
opt = opt->next;
}
}
void config_debug_output_portrange ( struct portrangestruct* pr,
char* prefix ) {
char* toprint = "";
if (pr) {
toprint = "\n";
}
while (pr) {
printf("%s%d:%d ", prefix, pr->startport, pr->endport);
pr = pr->next;
}
printf("%s", toprint);
}
void config_debug_output_users ( struct slist_t* ul, char* prefix ) {
while(ul) {
printf("%s%s\n", prefix, ul->value);
ul = ul->next;
}
}
void config_debug_output_host ( struct host_t host, char* prefix ) {
if (host.name) {
printf("%sname: %s", prefix, host.name);
} else {
/* we have to save one, inet_ntoa uses an internal memory
* that is overwritten by successive calls */
char* tmp = strdup(inet_ntoa(*((struct in_addr*)&host.ip.ip)));
enough_mem(tmp);
printf("%s%s/%s", prefix, tmp,
inet_ntoa(*((struct in_addr*) &host.ip.netmask)));
free(tmp);
}
printf("\n");
}
void config_debug_output_hostlist ( struct hostlist_t* hosts, char* prefix ) {
while(hosts) {
config_debug_output_host(hosts->host, prefix);
hosts = hosts->next;
}
}
char* day_name(int number) {
switch(number) {
case 0:
return("Sun"); break;
case 1:
return("Mon"); break;
case 2:
return("Tue"); break;
case 3:
return("Wed"); break;
case 4:
return("Thu"); break;
case 5:
return("Fri"); break;
case 6:
return("Sat"); break;
default:
return("ERR"); break;
}
}
void config_debug_output_time( struct timestruct* time, char* prefix) {
while(time) {
printf("%s", prefix);
if (time->days) {
struct ilist_t* il = time->days;
printf("List for ");
while(il) {
if (il != time->days) {
printf("/");
}
printf("%s", day_name(il->value));
il = il->next;
}
printf(" from %02d.%02d to %02d.%02d\n",
time->start_hour, time->start_minute,
time->end_hour, time->end_minute);
} else {
printf("Range starting on %s at %02d.%02d, "
"ending on %s at %02d.%02d\n",
day_name(time->start_day),
time->start_hour, time->start_minute,
day_name(time->end_day),
time->end_hour, time->end_minute);
}
time = time->next;
}
}
void config_debug_output_section( struct section_t* section, char* prefix ) {
if (!section) {
printf("%s----empty section\n", prefix);
return;
}
printf("%sid: %d\n", prefix, section->id);
printf("%shosts:\n", prefix);
config_debug_output_hostlist ( section->hosts, prefix );
printf("%shosts exclude:\n", prefix);
config_debug_output_hostlist ( section->hosts_exclude, prefix );
printf("%susers:\n", prefix);
config_debug_output_users ( section->users, prefix );
printf("%susers exclude:\n", prefix);
config_debug_output_users ( section->users_exclude, prefix );
printf("%sports:\n", prefix);
config_debug_output_portrange ( section->ports, prefix );
printf("%sports exclude:\n", prefix);
config_debug_output_portrange ( section->ports_exclude, prefix );
printf("%stime:\n", prefix);
config_debug_output_time ( section->time, prefix );
printf("%stime exclude:\n", prefix);
config_debug_output_time ( section->time_exclude, prefix );
printf("%sservertype:\n", prefix);
if (section->tag_name == TAG_SERVERTYPE) {
if (section->servertype == SERVERTYPE_STANDALONE) {
printf("%sStandalone\n", prefix);
} else {
printf("%sInetd\n", prefix);
}
}
printf("%soptions:\n", prefix);
config_debug_output_options ( section->options, prefix );
if (section->nested) {
char* prefix_new = malloc(strlen(prefix) + 4);
printf("%s----- nested:\n", prefix);
memset(prefix_new, (int) ' ', strlen(prefix) + 3);
prefix_new[strlen(prefix) + 3] = '\0';
config_debug_output_section( section->nested, prefix_new );
free(prefix_new);
}
if (section->next) {
printf("%s----- next:\n", prefix);
config_debug_output_section( section->next, prefix);
}
}
void config_debug_outputsections() {
printf("\n\n");
config_debug_output_section( base_section, "" );
/* printf("\n\n-------Backup-------\n\n");
config_debug_output_section( backup_base_section, "" );
*/
}
/*****---------------------- end DEBUG ---------------------******/
syntax highlighted by Code2HTML, v. 0.9.1