diff src/context.cpp @ 153:8d7c439bb6fa

add auto whitelisting
author carl
date Sat, 07 Jul 2007 16:10:39 -0700
parents c7fc218686f5
children 89ce226e5383
line wrap: on
line diff
--- a/src/context.cpp	Sat Jul 07 10:26:31 2007 -0700
+++ b/src/context.cpp	Sat Jul 07 16:10:39 2007 -0700
@@ -21,6 +21,7 @@
 
 static char* context_version="$Id$";
 
+char *token_autowhite;
 char *token_black;
 char *token_content;
 char *token_context;
@@ -63,10 +64,16 @@
 #endif
 char myhostname[HOST_NAME_MAX+1];
 
+pthread_mutex_t verifier_mutex; 	// protect the verifier map
 verify_map	verifiers;
+
+pthread_mutex_t whitelister_mutex;	// protect the
+whitelister_map whitelisters;
+
 string_set	all_strings;	// owns all the strings, only modified by the config loader thread
 const int maxlen = 1000;	// used for snprintf buffers
-const int maxage = 120; 	// smtp verify sockets older than this are ancient
+const int maxsmtp_age = 120;// smtp verify sockets older than this are ancient
+const int maxauto_age = 600;// auto whitelister delay before flushing to file
 extern int	  NULL_SOCKET;
 const time_t  ERROR_SMTP_SOCKET_TIME = 600; // number of seconds between attempts to open a socket to an smtp server
 
@@ -243,6 +250,9 @@
 #endif
 
 
+////////////////////////////////////////////////
+// smtp verifier so backup mx machines can see the valid users
+//
 VERIFY::VERIFY(char *h) {
 	host	 = h;
 	last_err = 0;
@@ -261,7 +271,7 @@
 			else {
 				conn = connections.front();
 				time_t now = time(NULL);
-				if ((now - conn->get_stamp()) > maxage) {
+				if ((now - conn->get_stamp()) > maxsmtp_age) {
 					// this connection is ancient, remove it
 					connections.pop_front();
 				}
@@ -372,6 +382,140 @@
 }
 
 
+////////////////////////////////////////////////
+// setup a new smtp verify host
+//
+VERIFYP add_verify_host(char *host);
+VERIFYP add_verify_host(char *host) {
+	VERIFYP rc = NULL;
+	pthread_mutex_lock(&verifier_mutex);
+		verify_map::iterator i = verifiers.find(host);
+		if (i == verifiers.end()) {
+			rc = new VERIFY(host);
+			verifiers[host] = rc;
+		}
+		else rc = (*i).second;
+	pthread_mutex_unlock(&verifier_mutex);
+	return rc;
+}
+
+
+////////////////////////////////////////////////
+// thread to check for verify hosts with old sockets that we can close
+//
+void* verify_closer(void *arg) {
+	while (true) {
+		sleep(maxsmtp_age);
+		pthread_mutex_lock(&verifier_mutex);
+			for (verify_map::iterator i=verifiers.begin(); i!=verifiers.end(); i++) {
+				VERIFYP v = (*i).second;
+				v->closer();
+			}
+		pthread_mutex_unlock(&verifier_mutex);
+	}
+	return NULL;
+}
+
+
+////////////////////////////////////////////////
+// automatic whitelister
+//
+WHITELISTER::WHITELISTER(char *f, int d) {
+	fn		 = f;
+	days	 = d;
+	pthread_mutex_init(&mutex, 0);
+	need	 = false;
+}
+
+
+void WHITELISTER::writer() {
+	pthread_mutex_lock(&mutex);
+		time_t limit = time(NULL) - days*86400;
+		for (autowhite_sent::iterator i=rcpts.begin(); i!=rcpts.end();) {
+			time_t when = (*i).second;
+			if (when < limit) {
+				autowhite_sent::iterator j = i;
+				j++;
+				rcpts.erase(i);
+				i = j;
+				need = true;
+			}
+			else i++;
+		}
+		if (need) {
+			// dump the file
+			ofstream os;
+			os.open(fn);
+			if (!os.fail()) {
+				for (autowhite_sent::iterator i=rcpts.begin(); i!=rcpts.end(); i++) {
+					char *who = (*i).first;
+					int  when = (*i).second;
+					os << who << " " << when << endl;
+				}
+			}
+			os.close();
+		}
+	pthread_mutex_unlock(&mutex);
+}
+
+
+void WHITELISTER::sent(char *to) {
+	pthread_mutex_lock(&mutex);
+		need = true;
+		rcpts[to] = time(NULL);
+	pthread_mutex_unlock(&mutex);
+}
+
+
+bool WHITELISTER::is_white(char *from) {
+	bool rc = false;
+	pthread_mutex_lock(&mutex);
+		autowhite_sent::iterator i = rcpts.find(from);
+		if (i != rcpts.end()) {
+			time_t when = (*i).second;
+			time_t now = time(NULL);
+			rc = (when+(days*8640) > now);
+		}
+	pthread_mutex_unlock(&mutex);
+	return rc;
+}
+
+
+////////////////////////////////////////////////
+// setup a new auto whitelister file
+//
+WHITELISTERP add_whitelister_file(char *fn, int days);
+WHITELISTERP add_whitelister_file(char *fn, int days) {
+	WHITELISTERP rc = NULL;
+	pthread_mutex_lock(&whitelister_mutex);
+		whitelister_map::iterator i = whitelisters.find(fn);
+		if (i == whitelisters.end()) {
+			rc = new WHITELISTER(fn, days);
+			whitelisters[fn] = rc;
+		}
+		else rc = (*i).second;
+	pthread_mutex_unlock(&whitelister_mutex);
+	return rc;
+}
+
+
+////////////////////////////////////////////////
+// thread to check for whitelister hosts with old sockets that we can close
+//
+void* whitelister_writer(void *arg) {
+	while (true) {
+		sleep(maxauto_age);
+		pthread_mutex_lock(&whitelister_mutex);
+			for (whitelister_map::iterator i=whitelisters.begin(); i!=whitelisters.end(); i++) {
+				WHITELISTERP v = (*i).second;
+				v->writer();
+			}
+		pthread_mutex_unlock(&whitelister_mutex);
+	}
+	return NULL;
+}
+
+
 DNSBL::DNSBL(char *n, char *s, char *m) {
 	name	= n;
 	suffix	= s;
@@ -474,6 +618,9 @@
 	parent				= parent_;
 	name				= name_;
 	verify_host 		= NULL;
+	verifier			= NULL;
+	autowhite_file		= NULL;
+	whitelister 		= NULL;
 	env_from_default	= (parent) ? token_inherit : token_unknown;
 	content_filtering	= (parent) ? parent->content_filtering : false;
 	content_suffix		= NULL;
@@ -531,22 +678,22 @@
 
 
 VERIFYP CONTEXT::find_verify(char *to) {
-	if (verify_host && (verify_host != token_myhostname) && cover_env_to(to)) {
-		verify_map::iterator i = verifiers.find(verify_host);
-		if (i == verifiers.end()) {
-			if (debug_syslog) {
-				char buf[maxlen];
-				snprintf(buf, maxlen, "cannot find struc for %s", verify_host);
-				my_syslog(buf);
-			}
+	if (verifier && (verify_host != token_myhostname) && cover_env_to(to))
+		return verifier;
+	else if (parent)
+		return parent->find_verify(to);
+	else
 			return NULL;
 		}
-		VERIFYP v = (*i).second;
+
 
-		return v;
-	}
-	else if (parent) return parent->find_verify(to);
-	else return NULL;
+WHITELISTERP CONTEXT::find_autowhite(char *to) {
+	if (whitelister && cover_env_to(to))
+		return whitelister;
+	else if (parent)
+		return parent->find_autowhite(to);
+	else
+		return NULL;
 }
 
 
@@ -558,6 +705,7 @@
 
 
 char *CONTEXT::find_from(char *from) {
+	if (whitelister && whitelister->is_white(from)) return token_white;
 	char *rc = env_from_default;
 	string_map::iterator i = env_from.find(from);
 	if (i != env_from.end()) rc = (*i).second;	// found user@domain key
@@ -775,6 +923,10 @@
 		printf("%s     verify %s; \n", indent, verify_host);
 	}
 
+	if (autowhite_file && whitelister) {
+		printf("%s     autowhite %d %s; \n", indent, whitelister->get_days(), autowhite_file);
+	}
+
 	for (context_map::iterator i=children.begin(); i!=children.end(); i++) {
 		CONTEXTP c = (*i).second;
 		c->dump(false, level+1);
@@ -1100,7 +1252,20 @@
 	char *host = tok.next();
 	if (!tsa(tok, token_semi)) return false;
 	me.set_verify(host);
-	add_verify_host(host);
+	me.set_verifier(add_verify_host(host));
+	return true;
+}
+
+
+////////////////////////////////////////////////
+//
+bool parse_autowhite(TOKEN &tok, CONFIG &dc, CONTEXT &me);
+bool parse_autowhite(TOKEN &tok, CONFIG &dc, CONTEXT &me) {
+	int days = tok.nextint();
+	char *fn = tok.next();
+	if (!tsa(tok, token_semi)) return false;
+	me.set_autowhite(fn);
+	me.set_whitelister(add_whitelister_file(fn, days));
 	return true;
 }
 
@@ -1233,6 +1398,9 @@
 		else if (have == token_verify) {
 			if (!parse_verify(tok, dc, *con)) return false;
 		}
+		else if (have == token_autowhite) {
+			if (!parse_autowhite(tok, dc, *con)) return false;
+		}
 		else if (have == token_envfrom) {
 			if (!parse_envfrom(tok, dc, *con)) return false;
 		}
@@ -1286,36 +1454,10 @@
 
 
 ////////////////////////////////////////////////
-// setup a new smtp verify host
-//
-void add_verify_host(char *host) {
-	verify_map::iterator i = verifiers.find(host);
-	if (i == verifiers.end()) {
-		VERIFYP v = new VERIFY(host);
-		verifiers[host] = v;
-	}
-}
-
-
-////////////////////////////////////////////////
-// thread to check for verify hosts with old sockets that we can close
-//
-void* verify_closer(void *arg) {
-	while (true) {
-		sleep(maxage);
-		for (verify_map::iterator i=verifiers.begin(); i!=verifiers.end(); i++) {
-			VERIFYP v = (*i).second;
-			v->closer();
-		}
-	}
-	return NULL;
-}
-
-
-////////////////////////////////////////////////
 // init the tokens
 //
 void token_init() {
+	token_autowhite  = register_string("autowhite");
 	token_black 	 = register_string("black");
 	token_cctld 	 = register_string("cctld");
 	token_content	 = register_string("content");