/*
* ImapProxy - a caching IMAP proxy daemon
* Copyright (C) 2002 Steven Van Acker
*
* 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-1307, USA.
*
*/
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <syslog.h>
#include <splitstring.h>
#include <network.h>
#include <output.h>
#include <configfile.h>
#include <defines.h>
/* check the header for more info on these vars */
unsigned long config_remote_address = DEFAULT_REMOTE_ADDRESS,config_local_address = DEFAULT_LOCAL_ADDRESS;
unsigned int config_remote_port = DEFAULT_REMOTE_PORT, config_local_port = DEFAULT_LOCAL_PORT;
int config_enable_debug = DEFAULT_ENABLE_DEBUG;
int config_log_facility = DEFAULT_LOG_FACILITY;
int config_enable_logging = DEFAULT_ENABLE_LOGGING;
int config_client_timeout = DEFAULT_CLIENT_TIMEOUT;
int config_server_timeout = DEFAULT_SERVER_TIMEOUT;
int config_keepalive = DEFAULT_KEEPALIVE;
int config_max_reuse = DEFAULT_MAX_REUSE;
int config_stats_frequency = DEFAULT_STATS_FREQUENCY;
struct access_list_t *config_accesslist = NULL;
unsigned int config_access_count = 0;
/* check_and_test_* :
* parameters:
* var = string containing the variable name
* val = string containing the variable value
* return :
* 0 on success
* -1 on error
* 1 if this is not the right var name
*/
int check_and_set_debug(char *var,char *val)
{
if(strcasecmp(var,"debug")) return 1;
if(!strcasecmp(val,"on"))
config_enable_debug = 1;
else if(!strcasecmp(val,"off"))
config_enable_debug = 0;
else
{
debug("check_and_set_debug(\"%s\",\"%s\"): \"%s\" is not a valid value for \"%s\". Possible values are : \"on\" and \"off\"\n",var,val,val,var);
return -1;
}
return 0;
}
int check_and_set_logging(char *var,char *val)
{
if(strcasecmp(var,"logging")) return 1;
if(!strcasecmp(val,"on"))
config_enable_logging = 1;
else if(!strcasecmp(val,"off"))
config_enable_logging = 0;
else
{
debug("check_and_set_logging(\"%s\",\"%s\"): \"%s\" is not a valid value for \"%s\". Possible values are : \"on\" and \"off\"\n",var,val,val,var);
return -1;
}
return 0;
}
int check_and_set_log_facility(char *var,char *val)
{
#ifdef SOLARIS /* Solaris */
char *values[] = { "LOG_AUTH", "LOG_CRON", "LOG_DAEMON", "LOG_KERN",
"LOG_LOCAL0", "LOG_LOCAL1", "LOG_LOCAL2", "LOG_LOCAL3", "LOG_LOCAL4",
"LOG_LOCAL5", "LOG_LOCAL6", "LOG_LOCAL7", "LOG_LPR", "LOG_MAIL", "LOG_NEWS",
"LOG_SYSLOG", "LOG_USER", "LOG_UUCP" };
int intvalues[] = { LOG_AUTH, LOG_CRON, LOG_DAEMON, LOG_KERN,
LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4,
LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7, LOG_LPR, LOG_MAIL, LOG_NEWS,
LOG_SYSLOG, LOG_USER, LOG_UUCP };
#else
char *values[] = { "LOG_AUTH", "LOG_AUTHPRIV", "LOG_CRON", "LOG_DAEMON", "LOG_KERN",
"LOG_LOCAL0", "LOG_LOCAL1", "LOG_LOCAL2", "LOG_LOCAL3", "LOG_LOCAL4",
"LOG_LOCAL5", "LOG_LOCAL6", "LOG_LOCAL7", "LOG_LPR", "LOG_MAIL", "LOG_NEWS",
"LOG_SYSLOG", "LOG_USER", "LOG_UUCP" };
int intvalues[] = { LOG_AUTH, LOG_AUTHPRIV, LOG_CRON, LOG_DAEMON, LOG_KERN,
LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4,
LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7, LOG_LPR, LOG_MAIL, LOG_NEWS,
LOG_SYSLOG, LOG_USER, LOG_UUCP };
#endif /* Solaris */
int i = 0;
if(strcasecmp(var,"log_facility")) return 1;
for(i = 0;i < sizeof(values)/sizeof(char *);i++)
{
if(!strcasecmp(val,values[i]))
{
config_log_facility = intvalues[i];
return 0;
}
}
debug("check_and_set_log_facility(\"%s\",\"%s\"): \"%s\" is not a valid value for \"%s\". Please specify a valid logfacility (man 3 syslog)\n",var,val,val,var);
return -1;
}
int check_and_set_client_timeout(char *var,char *val)
{
int i;
if(strcasecmp(var,"client_timeout")) return 1;
i = strtol(val,NULL,10);
if(i <= 0)
{
debug("check_and_set_client_timeout(\"%s\",\"%s\"): \"%s\" is not a valid value for \"%s\". Please use a number > 0\n",var,val,val,var);
return -1;
}
config_client_timeout = i;
return 0;
}
int check_and_set_server_timeout(char *var,char *val)
{
int i;
if(strcasecmp(var,"server_timeout")) return 1;
i = strtol(val,NULL,10);
if(i <= 0)
{
debug("check_and_set_server_timeout(\"%s\",\"%s\"): \"%s\" is not a valid value for \"%s\". Please use a number > 0\n",var,val,val,var);
return -1;
}
config_server_timeout = i;
return 0;
}
int check_and_set_keepalive(char *var,char *val)
{
int i;
if(strcasecmp(var,"keepalive")) return 1;
i = strtol(val,NULL,10);
if(i <= 0)
{
debug("check_and_set_keepalive(\"%s\",\"%s\"): \"%s\" is not a valid value for \"%s\". Please use a number > 0\n",var,val,val,var);
return -1;
}
config_keepalive = i;
return 0;
}
int check_and_set_remote_address(char *var,char *val)
{
unsigned long ip = 0;
if(strcasecmp(var,"remote_address"))
{
/* no match, so not our problem. */
return 1;
}
if((ip = my_resolve(val)) == 0)
{
debug("check_and_set_remote_address(\"%s\",\"%s\"): Could not resolve \"%s\".\n",var,val,val);
return -1;
}
config_remote_address = ip;
return 0;
}
int check_and_set_local_address(char *var,char *val)
{
unsigned long ip = 0;
if(strcasecmp(var,"local_address"))
return 1;
if(!strcmp(val,"*"))
{
config_local_address = INADDR_ANY;
return 0;
}
if((ip = my_resolve(val)) == 0)
{
debug("check_and_set_local_address(\"%s\",\"%s\"): Could not resolve \"%s\".\n",var,val,val);
return -1;
}
config_local_address = ip;
return 0;
}
int check_and_set_remote_port(char *var,char *val)
{
if(strcasecmp(var,"remote_port"))
return 1;
config_remote_port = atoi(val);
return 0;
}
int check_and_set_local_port(char *var,char *val)
{
if(strcasecmp(var,"local_port"))
return 1;
config_local_port = atoi(val);
return 0;
}
int check_and_set_accept_deny(char *var,char *val)
{
int action = 0;
unsigned long ip,mask;
char *p;
/* what action ? */
if(!strcasecmp(var,"accept"))
action = CONFIG_ACCESS_LIST_ACCEPT;
else if(!strcasecmp(var,"deny"))
action = CONFIG_ACCESS_LIST_BLOCK;
else
return 1;
/* search for netmask */
if(!(p = strchr(val,'/')))
{
p = "255.255.255.255";
}
else
{
*p++ = '\0';
}
/* what netmask notation ? */
if(!strchr(p,'.'))
{
/* netmask in /n notation */
int n = 32 - atoi(p);
mask = htonl(0xffffffff << n);
}
else
{
/* netmask in ip notation */
mask = my_resolve(p);
}
/* resolve ip part */
if(!(ip = my_resolve(val)))
{
debug("check_and_set_accept_deny(\"\",\"\"): Could not resolve \"%s\".\n",var,val,val);
return -1;
}
/* add to accesslist */
if(!(config_accesslist = (struct access_list_t *)realloc(config_accesslist,sizeof(struct access_list_t)*(config_access_count+1))))
{
debug("check_and_set_accept_deny(\"\",\"\"): Out of memory.\n",var,val);
return -1;
}
config_accesslist[config_access_count].ip = ip;
config_accesslist[config_access_count].mask = mask;
config_accesslist[config_access_count].action = action;
config_access_count++;
return 0;
}
int check_and_set_max_reuse(char *var,char *val)
{
if(strcasecmp(var,"max_reuse"))
return 1;
config_max_reuse = atoi(val);
if(config_max_reuse < 0)
{
debug("check_and_set_max_reuse(\"\",\"\"): Invalid value (Should be >= 0).\n",var,val);
}
return 0;
}
int check_and_set_stats_frequency(char *var,char *val)
{
if(strcasecmp(var,"stats_frequency"))
return 1;
config_stats_frequency = atoi(val);
if(config_stats_frequency < 0)
{
debug("check_and_set_stats_frequency(\"\",\"\"): Invalid value (Should be >= 0).\n",var,val);
}
return 0;
}
ConfigFunction Functions[] =
{
{check_and_set_debug},
{check_and_set_logging},
{check_and_set_log_facility},
{check_and_set_client_timeout},
{check_and_set_server_timeout},
{check_and_set_keepalive},
{check_and_set_remote_address},
{check_and_set_local_address},
{check_and_set_remote_port},
{check_and_set_local_port},
{check_and_set_accept_deny},
{check_and_set_max_reuse},
{check_and_set_stats_frequency}
};
/* read the configfile
* return 0 on success
* -1 on error
*/
int config_file_read(char *file)
{
FILE *fd;
char buf[1024];
int linecount = 0;
int i = 0,r = 0;
Split_string ss;
if(!(fd = fopen(file,"r")))
{
debug("config_file_read(\"%s\"): Could not open configfile (%m).\n",file);
return -1;
}
/* this function may be called on SIGHUP,
* so make sure we don't leak
*/
if(config_accesslist)
{
free(config_accesslist);
config_accesslist = NULL;
}
config_access_count = 0;
while(fgets(buf,sizeof(buf),fd))
{
char *p;
if((p = strchr(buf,'\r'))) *p = '\0';
if((p = strchr(buf,'\n'))) *p = '\0';
/* need no comments either */
if((p = strchr(buf,'#'))) *p = '\0';
linecount++;
init_split_string(&ss);
/* split string in pieces */
if(make_split_string_strip_crlf(buf,&ss," \t"))
{
debug("config_file_read(\"%s\"): Error parsing config file (line %d).\n",file,linecount);
return -1;
}
if(split_string_count_parts(&ss) == 0)
{
goto next_line;
}
if(split_string_count_parts(&ss) != 2)
{
debug("config_file_read(\"%s\"): Line \"%s\" is not a variable/value pair (line %d).\n",file,buf,linecount);
return -1;
}
for(i = 0;i < sizeof(Functions)/sizeof(ConfigFunction); i++)
{
r = (Functions[i].f)(split_string_get(&ss,0),split_string_get(&ss,1));
if(r < 0)
{
debug("config_file_read(\"%s\"): configuration error on line %d (\"%s\").\n",file,linecount,buf);
return -1;
}
else if(r == 0)
{
goto next_line;
}
}
/* if we get here, it means that no function matched any config token
*/
debug("config_file_read(\"%s\"): unknown configuration variable \"%s\" on line %d (\"%s\").\n",file,split_string_get(&ss,0),linecount,buf);
return -1;
next_line:
delete_split_string(&ss);
}
fclose(fd);
return 0;
}
/* check if a certain IP is blocked.
* returns 1 if blocked
* 0 otherwise
*/
int check_if_blocked(unsigned long ip)
{
int i = 0;
struct access_list_t *current = NULL;
for(i = 0; i < config_access_count; i++)
{
current = &config_accesslist[i];
if((current->mask & ip) == (current->ip & current->mask))
{
if(current->action == CONFIG_ACCESS_LIST_BLOCK)
return 1;
return 0;
}
}
/* if IP is not in the accesslist, then deny by default */
return 1;
}
char *log_facility_to_str(int x)
{
#ifdef SOLARIS /* Solaris */
char *values[] = { "LOG_AUTH", "LOG_CRON", "LOG_DAEMON", "LOG_KERN",
"LOG_LOCAL0", "LOG_LOCAL1", "LOG_LOCAL2", "LOG_LOCAL3", "LOG_LOCAL4",
"LOG_LOCAL5", "LOG_LOCAL6", "LOG_LOCAL7", "LOG_LPR", "LOG_MAIL", "LOG_NEWS",
"LOG_SYSLOG", "LOG_USER", "LOG_UUCP" };
int intvalues[] = { LOG_AUTH, LOG_CRON, LOG_DAEMON, LOG_KERN,
LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4,
LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7, LOG_LPR, LOG_MAIL, LOG_NEWS,
LOG_SYSLOG, LOG_USER, LOG_UUCP };
#else
char *values[] = { "LOG_AUTH", "LOG_AUTHPRIV", "LOG_CRON", "LOG_DAEMON", "LOG_KERN",
"LOG_LOCAL0", "LOG_LOCAL1", "LOG_LOCAL2", "LOG_LOCAL3", "LOG_LOCAL4",
"LOG_LOCAL5", "LOG_LOCAL6", "LOG_LOCAL7", "LOG_LPR", "LOG_MAIL", "LOG_NEWS",
"LOG_SYSLOG", "LOG_USER", "LOG_UUCP" };
int intvalues[] = { LOG_AUTH, LOG_AUTHPRIV, LOG_CRON, LOG_DAEMON, LOG_KERN,
LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4,
LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7, LOG_LPR, LOG_MAIL, LOG_NEWS,
LOG_SYSLOG, LOG_USER, LOG_UUCP };
#endif /* Solaris */
int i = 0;
for(i = 0;i < sizeof(values)/sizeof(char *);i++)
{
if(intvalues[i] == x)
{
return values[i];
}
}
return NULL;
}
void configfile_print(char *conffile)
{
int i;
struct in_addr x;
fprintf(stderr,"Configuration (%s):\n",conffile);
x.s_addr = config_remote_address;
fprintf(stderr,"\tremote address: %s\n",inet_ntoa(x));
fprintf(stderr,"\tremote port: %d\n",config_remote_port);
x.s_addr = config_local_address;
fprintf(stderr,"\tlocal address: %s\n",inet_ntoa(x));
fprintf(stderr,"\tlocal port: %d\n",config_local_port);
fprintf(stderr,"\t%d access-rule(s):\n",config_access_count);
for(i = 0;i < config_access_count ; i++)
{
fprintf(stderr,"\t\t%s ",config_accesslist[i].action == CONFIG_ACCESS_LIST_ACCEPT?"accept":"deny");
x.s_addr = config_accesslist[i].ip;
fprintf(stderr,"%s",inet_ntoa(x));
x.s_addr = config_accesslist[i].mask;
fprintf(stderr,"/%s\n",inet_ntoa(x));
}
fprintf(stderr,"\tlogging: %s\n",config_enable_logging?"ON":"OFF");
fprintf(stderr,"\tlogging to: %s\n",log_facility_to_str(config_log_facility));
fprintf(stderr,"\tdebug: %s\n",config_enable_debug?"ON":"OFF");
fprintf(stderr,"\tclient_timeout: %d s\n",config_client_timeout);
fprintf(stderr,"\tserver_timeout: %d s\n",config_server_timeout);
fprintf(stderr,"\tkeepalive: %d s\n",config_keepalive);
fprintf(stderr,"\tstats_frequency: %d s\n",config_stats_frequency);
fprintf(stderr,"\tmax_reuse: %d times\n",config_max_reuse);
}
syntax highlighted by Code2HTML, v. 0.9.1