/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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); }