xref: /plan9/sys/src/cmd/upas/smtp/spam.c (revision 59cc4ca53493a3c6d2349fe2b7f7c40f7dce7294)
17dd7cddfSDavid du Colombier #include "common.h"
27dd7cddfSDavid du Colombier #include "smtpd.h"
37dd7cddfSDavid du Colombier #include "ip.h"
47dd7cddfSDavid du Colombier 
57dd7cddfSDavid du Colombier enum {
67dd7cddfSDavid du Colombier 	NORELAY = 0,
77dd7cddfSDavid du Colombier 	DNSVERIFY,
87dd7cddfSDavid du Colombier 	SAVEBLOCK,
97dd7cddfSDavid du Colombier 	DOMNAME,
107dd7cddfSDavid du Colombier 	OURNETS,
117dd7cddfSDavid du Colombier 	OURDOMS,
127dd7cddfSDavid du Colombier 
137dd7cddfSDavid du Colombier 	IP = 0,
147dd7cddfSDavid du Colombier 	STRING,
157dd7cddfSDavid du Colombier };
167dd7cddfSDavid du Colombier 
177dd7cddfSDavid du Colombier 
187dd7cddfSDavid du Colombier typedef struct Keyword Keyword;
197dd7cddfSDavid du Colombier 
207dd7cddfSDavid du Colombier struct Keyword {
217dd7cddfSDavid du Colombier 	char	*name;
227dd7cddfSDavid du Colombier 	int	code;
237dd7cddfSDavid du Colombier };
247dd7cddfSDavid du Colombier 
257dd7cddfSDavid du Colombier static Keyword options[] = {
267dd7cddfSDavid du Colombier 	"norelay",		NORELAY,
277dd7cddfSDavid du Colombier 	"verifysenderdom",	DNSVERIFY,
287dd7cddfSDavid du Colombier 	"saveblockedmsg",	SAVEBLOCK,
297dd7cddfSDavid du Colombier 	"defaultdomain",	DOMNAME,
307dd7cddfSDavid du Colombier 	"ournets",		OURNETS,
317dd7cddfSDavid du Colombier 	"ourdomains",		OURDOMS,
327dd7cddfSDavid du Colombier 	0,			NONE,
337dd7cddfSDavid du Colombier };
347dd7cddfSDavid du Colombier 
357dd7cddfSDavid du Colombier static Keyword actions[] = {
367dd7cddfSDavid du Colombier 	"allow",		ACCEPT,
377dd7cddfSDavid du Colombier 	"accept",		ACCEPT,
387dd7cddfSDavid du Colombier 	"block",		BLOCKED,
397dd7cddfSDavid du Colombier 	"deny",			DENIED,
407dd7cddfSDavid du Colombier 	"dial",			DIALUP,
417dd7cddfSDavid du Colombier 	"relay",		RELAY,
427dd7cddfSDavid du Colombier 	0,			NONE,
437dd7cddfSDavid du Colombier };
447dd7cddfSDavid du Colombier 
457dd7cddfSDavid du Colombier static	char*	getline(Biobuf*);
467dd7cddfSDavid du Colombier 
477dd7cddfSDavid du Colombier static int
487dd7cddfSDavid du Colombier findkey(char *val, Keyword *p)
497dd7cddfSDavid du Colombier {
507dd7cddfSDavid du Colombier 
517dd7cddfSDavid du Colombier 	for(; p->name; p++)
527dd7cddfSDavid du Colombier 		if(strcmp(val, p->name) == 0)
537dd7cddfSDavid du Colombier 				break;
547dd7cddfSDavid du Colombier 	return p->code;
557dd7cddfSDavid du Colombier }
567dd7cddfSDavid du Colombier 
577dd7cddfSDavid du Colombier void
587dd7cddfSDavid du Colombier getconf(void)
597dd7cddfSDavid du Colombier {
607dd7cddfSDavid du Colombier 	Biobuf *bp;
617dd7cddfSDavid du Colombier 	char *cp, *p;
627dd7cddfSDavid du Colombier 	String *s;
637dd7cddfSDavid du Colombier 	char buf[512];
64*59cc4ca5SDavid du Colombier /*
65*59cc4ca5SDavid du Colombier **		let it fail on unix
66*59cc4ca5SDavid du Colombier **
67*59cc4ca5SDavid du Colombier **
68*59cc4ca5SDavid du Colombier **	if(hisaddr && *hisaddr){
69*59cc4ca5SDavid du Colombier **		sprint(buf, "/mail/ratify/trusted/ip/%s#32\n", hisaddr);
70*59cc4ca5SDavid du Colombier **		if(access(buf,0) >= 0)
71*59cc4ca5SDavid du Colombier **			trusted++;
72*59cc4ca5SDavid du Colombier **	}
73*59cc4ca5SDavid du Colombier */
747dd7cddfSDavid du Colombier 
757dd7cddfSDavid du Colombier 	snprint(buf, sizeof(buf), "%s/smtpd.conf", UPASLIB);
767dd7cddfSDavid du Colombier 	bp = sysopen(buf, "r", 0);
777dd7cddfSDavid du Colombier 	if(bp == 0)
787dd7cddfSDavid du Colombier 		return;
797dd7cddfSDavid du Colombier 
807dd7cddfSDavid du Colombier 	for(;;){
817dd7cddfSDavid du Colombier 		cp = getline(bp);
827dd7cddfSDavid du Colombier 		if(cp == 0)
837dd7cddfSDavid du Colombier 			break;
847dd7cddfSDavid du Colombier 		p = cp+strlen(cp)+1;
857dd7cddfSDavid du Colombier 		switch(findkey(cp, options)){
867dd7cddfSDavid du Colombier 		case NORELAY:
877dd7cddfSDavid du Colombier 			if(fflag == 0 && strcmp(p, "on") == 0)
887dd7cddfSDavid du Colombier 				fflag++;
897dd7cddfSDavid du Colombier 			break;
907dd7cddfSDavid du Colombier 		case DNSVERIFY:
917dd7cddfSDavid du Colombier 			if(rflag == 0 && strcmp(p, "on") == 0)
927dd7cddfSDavid du Colombier 				rflag++;
937dd7cddfSDavid du Colombier 			break;
947dd7cddfSDavid du Colombier 		case SAVEBLOCK:
957dd7cddfSDavid du Colombier 			if(sflag == 0 && strcmp(p, "on") == 0)
967dd7cddfSDavid du Colombier 				sflag++;
977dd7cddfSDavid du Colombier 			break;
987dd7cddfSDavid du Colombier 		case DOMNAME:
997dd7cddfSDavid du Colombier 			if(dom == 0)
1007dd7cddfSDavid du Colombier 				dom = strdup(p);
1017dd7cddfSDavid du Colombier 			break;
1027dd7cddfSDavid du Colombier 		case OURNETS:
1037dd7cddfSDavid du Colombier 			if (trusted == 0)
1047dd7cddfSDavid du Colombier 				trusted = cidrcheck(p);
1057dd7cddfSDavid du Colombier 			break;
1067dd7cddfSDavid du Colombier 		case OURDOMS:
1077dd7cddfSDavid du Colombier 			while(*p){
1087dd7cddfSDavid du Colombier 				s = s_new();
1097dd7cddfSDavid du Colombier 				s_append(s, p);
1107dd7cddfSDavid du Colombier 				listadd(&ourdoms, s);
1117dd7cddfSDavid du Colombier 				p += strlen(p)+1;
1127dd7cddfSDavid du Colombier 			}
1137dd7cddfSDavid du Colombier 			break;
1147dd7cddfSDavid du Colombier 		default:
1157dd7cddfSDavid du Colombier 			break;
1167dd7cddfSDavid du Colombier 		}
1177dd7cddfSDavid du Colombier 	}
1187dd7cddfSDavid du Colombier 	sysclose(bp);
1197dd7cddfSDavid du Colombier }
1207dd7cddfSDavid du Colombier 
1217dd7cddfSDavid du Colombier /*
1227dd7cddfSDavid du Colombier  *	match a user name.  the only meta-char is '*' which matches all
1237dd7cddfSDavid du Colombier  *	characters.  we only allow it as "*", which matches anything or
1247dd7cddfSDavid du Colombier  *	an * at the end of the name (e.g., "username*") which matches
1257dd7cddfSDavid du Colombier  *	trailing characters.
1267dd7cddfSDavid du Colombier  */
1277dd7cddfSDavid du Colombier static int
1287dd7cddfSDavid du Colombier usermatch(char *pathuser, char *specuser)
1297dd7cddfSDavid du Colombier {
1307dd7cddfSDavid du Colombier 	int n;
1317dd7cddfSDavid du Colombier 
1327dd7cddfSDavid du Colombier 	n = strlen(specuser)-1;
1337dd7cddfSDavid du Colombier 	if(specuser[n] == '*'){
1347dd7cddfSDavid du Colombier 		if(n == 0)		/* match everything */
1357dd7cddfSDavid du Colombier 			return 0;
1367dd7cddfSDavid du Colombier 		return strncmp(pathuser, specuser, n);
1377dd7cddfSDavid du Colombier 	}
1387dd7cddfSDavid du Colombier 	return strcmp(pathuser, specuser);
1397dd7cddfSDavid du Colombier }
1407dd7cddfSDavid du Colombier 
1417dd7cddfSDavid du Colombier static int
1427dd7cddfSDavid du Colombier dommatch(char *pathdom, char *specdom)
1437dd7cddfSDavid du Colombier {
1447dd7cddfSDavid du Colombier 	int n;
1457dd7cddfSDavid du Colombier 
1467dd7cddfSDavid du Colombier 	if (*specdom == '*'){
1477dd7cddfSDavid du Colombier 		if (specdom[1] == '.' && specdom[2]){
1487dd7cddfSDavid du Colombier 			specdom += 2;
1497dd7cddfSDavid du Colombier 			n = strlen(pathdom)-strlen(specdom);
1507dd7cddfSDavid du Colombier 			if(n == 0 || (n > 0 && pathdom[n-1] == '.'))
1517dd7cddfSDavid du Colombier 				return strcmp(pathdom+n, specdom);
1527dd7cddfSDavid du Colombier 			return n;
1537dd7cddfSDavid du Colombier 		}
1547dd7cddfSDavid du Colombier 	}
1557dd7cddfSDavid du Colombier 	return strcmp(pathdom, specdom);
1567dd7cddfSDavid du Colombier }
1577dd7cddfSDavid du Colombier 
1587dd7cddfSDavid du Colombier /*
1597dd7cddfSDavid du Colombier  * match path components to prohibited domain & user specifications.  patterns include:
1607dd7cddfSDavid du Colombier  *	domain, domain! or domain!*	  - all users in domain
1617dd7cddfSDavid du Colombier  *	*.domain, *.domain! or *.domain!* - all users in domain and its subdomains
1627dd7cddfSDavid du Colombier  *	!user or *!user			  - user in all domains
1637dd7cddfSDavid du Colombier  *	domain!user			  - user in domain
1647dd7cddfSDavid du Colombier  *	*.domain!user			  - user in domain and its subdomains
1657dd7cddfSDavid du Colombier  *
1667dd7cddfSDavid du Colombier  *	if "user" has a trailing '*', it matches all user names beginning with "user"
1677dd7cddfSDavid du Colombier  *
1687dd7cddfSDavid du Colombier  * there are special semantics for the "domain, domain! or domain!*" specifications:
1697dd7cddfSDavid du Colombier  * the first two forms match when the domain is anywhere in at list of source-routed
1707dd7cddfSDavid du Colombier  * domains while the latter matches only when the domain is the last hop.  the same is
1717dd7cddfSDavid du Colombier  * true for the *.domain!* form of the pattern.
1727dd7cddfSDavid du Colombier  */
1737dd7cddfSDavid du Colombier static int
1747dd7cddfSDavid du Colombier accountmatch(char *spec, List *doms, char *user)
1757dd7cddfSDavid du Colombier {
1767dd7cddfSDavid du Colombier 	char *cp, *p;
1777dd7cddfSDavid du Colombier 	int i, n;
1787dd7cddfSDavid du Colombier 	Link *l;
1797dd7cddfSDavid du Colombier 
1807dd7cddfSDavid du Colombier 	static char *dangerous[] = { "*", "!", "*!", "!*", "*!*", 0 };
1817dd7cddfSDavid du Colombier 
1827dd7cddfSDavid du Colombier 	for(; *spec; spec += n){
1837dd7cddfSDavid du Colombier 		n = strlen(spec)+1;
1847dd7cddfSDavid du Colombier 
1857dd7cddfSDavid du Colombier 		/* rule out dangerous patterns */
1867dd7cddfSDavid du Colombier 		for (i = 0; dangerous[i]; i++)
1877dd7cddfSDavid du Colombier 			if(strcmp(spec, dangerous[i])== 0)
1887dd7cddfSDavid du Colombier 				break;
1897dd7cddfSDavid du Colombier 		if(dangerous[i])
1907dd7cddfSDavid du Colombier 			continue;
1917dd7cddfSDavid du Colombier 
1927dd7cddfSDavid du Colombier 		p = 0;
1937dd7cddfSDavid du Colombier 		cp = strchr(spec, '!');
1947dd7cddfSDavid du Colombier 		if(cp){
1957dd7cddfSDavid du Colombier 			*cp++ = 0;
1967dd7cddfSDavid du Colombier 			if(*cp)
197*59cc4ca5SDavid du Colombier 			if(strcmp(cp, "*"))	/* rule out "!*" */
1987dd7cddfSDavid du Colombier 				p = cp;
1997dd7cddfSDavid du Colombier 		}
2007dd7cddfSDavid du Colombier 
2017dd7cddfSDavid du Colombier 		if(p == 0){			/* no user field - domain match only */
2027dd7cddfSDavid du Colombier 			for(l = doms->first; l; l = l->next)
2037dd7cddfSDavid du Colombier 				if(dommatch(s_to_c(l->p), spec) == 0)
2047dd7cddfSDavid du Colombier 					return 1;
2057dd7cddfSDavid du Colombier 		} else {
2067dd7cddfSDavid du Colombier 			/* check for "!user", "*!user" or "domain!user" */
2077dd7cddfSDavid du Colombier 			if(usermatch(user, p) == 0){
2087dd7cddfSDavid du Colombier 				if(*spec == 0 || strcmp(spec, "*") == 0)
2097dd7cddfSDavid du Colombier 					return 1;
2107dd7cddfSDavid du Colombier 				if(doms->last && dommatch(s_to_c(doms->last->p), spec) == 0)
2117dd7cddfSDavid du Colombier 					return 1;
2127dd7cddfSDavid du Colombier 			}
2137dd7cddfSDavid du Colombier 		}
2147dd7cddfSDavid du Colombier 	}
2157dd7cddfSDavid du Colombier 	return 0;
2167dd7cddfSDavid du Colombier }
2177dd7cddfSDavid du Colombier 
2187dd7cddfSDavid du Colombier /*
2197dd7cddfSDavid du Colombier  * we risk reparsing the file of blocked addresses if there are
2207dd7cddfSDavid du Colombier  * multiple senders or if the peer does a transaction and then a RSET
2217dd7cddfSDavid du Colombier  * followed by another transaction.  we believe this happens rarely,
2227dd7cddfSDavid du Colombier  * but we cache the last sender to try to minimize the overhead.  we
2237dd7cddfSDavid du Colombier  * also cache whether we rejected a previous transaction based on
2247dd7cddfSDavid du Colombier  * ip address, since this never changes.
2257dd7cddfSDavid du Colombier  */
2267dd7cddfSDavid du Colombier int
2277dd7cddfSDavid du Colombier blocked(String *path)
2287dd7cddfSDavid du Colombier {
2297dd7cddfSDavid du Colombier 	char buf[512], *cp, *p, *user;
2307dd7cddfSDavid du Colombier 	Biobuf *bp;
2317dd7cddfSDavid du Colombier 	int action, type, ipallow;
2327dd7cddfSDavid du Colombier 	List doms;
2337dd7cddfSDavid du Colombier 	String *s, *lpath;
2347dd7cddfSDavid du Colombier 
2357dd7cddfSDavid du Colombier 	static String *lastsender;
2367dd7cddfSDavid du Colombier 	static int lastret, blockedbyip;
2377dd7cddfSDavid du Colombier 
2387dd7cddfSDavid du Colombier 	if(debug)
2397dd7cddfSDavid du Colombier 		fprint(2, "blocked(%s)\n", s_to_c(path));
2407dd7cddfSDavid du Colombier 
2417dd7cddfSDavid du Colombier 	if(blockedbyip)
2427dd7cddfSDavid du Colombier 		return lastret;
2437dd7cddfSDavid du Colombier 
2447dd7cddfSDavid du Colombier 	if(lastsender){
2457dd7cddfSDavid du Colombier 		if(strcmp(s_to_c(lastsender), s_to_c(path)) == 0)
2467dd7cddfSDavid du Colombier 			return lastret;
2477dd7cddfSDavid du Colombier 		s_free(lastsender);
2487dd7cddfSDavid du Colombier 		lastsender = 0;
2497dd7cddfSDavid du Colombier 	}
2507dd7cddfSDavid du Colombier 
2517dd7cddfSDavid du Colombier 	snprint(buf, sizeof(buf), "%s/blocked", UPASLIB);
2527dd7cddfSDavid du Colombier 	bp = sysopen(buf, "r", 0);
2537dd7cddfSDavid du Colombier 	if(bp == 0)
2547dd7cddfSDavid du Colombier 		return ACCEPT;
2557dd7cddfSDavid du Colombier 
2567dd7cddfSDavid du Colombier 	lpath = s_copy(s_to_c(s_restart(path)));	/* convert to LC */
2577dd7cddfSDavid du Colombier 	for(cp = s_to_c(lpath); *cp; cp++)
2587dd7cddfSDavid du Colombier 		*cp = tolower(*cp);
2597dd7cddfSDavid du Colombier 
2607dd7cddfSDavid du Colombier 	/* parse the path into a list of domains and a user */
2617dd7cddfSDavid du Colombier 	doms.first = doms.last = 0;
2627dd7cddfSDavid du Colombier 	p = s_to_c(lpath);
2637dd7cddfSDavid du Colombier 	while (cp = strchr(p, '!')){
2647dd7cddfSDavid du Colombier 		*cp = 0;
2657dd7cddfSDavid du Colombier 		s = s_new();
2667dd7cddfSDavid du Colombier 		s_append(s, p);
2677dd7cddfSDavid du Colombier 		*cp = '!';
2687dd7cddfSDavid du Colombier 		listadd(&doms, s);
2697dd7cddfSDavid du Colombier 		p = cp+1;
2707dd7cddfSDavid du Colombier 	}
2717dd7cddfSDavid du Colombier 	user = p;
2727dd7cddfSDavid du Colombier 
2737dd7cddfSDavid du Colombier 	/*
2747dd7cddfSDavid du Colombier 	 * line format: [*]VERB param 1, ... param n,  where '*' indicates
2757dd7cddfSDavid du Colombier 	 * params are path names; if not present, params are ip addresses in
2767dd7cddfSDavid du Colombier 	 * CIDR format.
2777dd7cddfSDavid du Colombier 	 */
2787dd7cddfSDavid du Colombier 	ipallow = 0;
2797dd7cddfSDavid du Colombier 	for(;;){
2807dd7cddfSDavid du Colombier 		action = ACCEPT;
2817dd7cddfSDavid du Colombier 		cp = getline(bp);
2827dd7cddfSDavid du Colombier 		if(cp == 0)
2837dd7cddfSDavid du Colombier 			break;
2847dd7cddfSDavid du Colombier 		type = *cp;
2857dd7cddfSDavid du Colombier 		if(type == '*') {
2867dd7cddfSDavid du Colombier 			cp++;
2877dd7cddfSDavid du Colombier 			if(*cp == 0)
2887dd7cddfSDavid du Colombier 				cp++;
2897dd7cddfSDavid du Colombier 		}
2907dd7cddfSDavid du Colombier 		action = findkey(cp, actions);
2917dd7cddfSDavid du Colombier 		if (action == NONE)
2927dd7cddfSDavid du Colombier 			continue;
2937dd7cddfSDavid du Colombier 
2947dd7cddfSDavid du Colombier 		cp += strlen(cp)+1;
2957dd7cddfSDavid du Colombier 		if(type == '*') {
2967dd7cddfSDavid du Colombier 			if(accountmatch(cp, &doms, user))
2977dd7cddfSDavid du Colombier 				break;
2987dd7cddfSDavid du Colombier 		} else {
2997dd7cddfSDavid du Colombier 			/*
3007dd7cddfSDavid du Colombier 			 * An ACCEPT match on IP addresses overrides all later
3017dd7cddfSDavid du Colombier 			 * blocks on IP address but not blocks on domain or user names
3027dd7cddfSDavid du Colombier 			 */
3037dd7cddfSDavid du Colombier 			if(ipallow == 0 && cidrcheck(cp))
3047dd7cddfSDavid du Colombier 				if(action == ACCEPT)
3057dd7cddfSDavid du Colombier 					ipallow = 1;
3067dd7cddfSDavid du Colombier 				else {
3077dd7cddfSDavid du Colombier 					blockedbyip = 1;
3087dd7cddfSDavid du Colombier 					break;
3097dd7cddfSDavid du Colombier 				}
3107dd7cddfSDavid du Colombier 		}
3117dd7cddfSDavid du Colombier 	}
3127dd7cddfSDavid du Colombier 	sysclose(bp);
3137dd7cddfSDavid du Colombier 	listfree(&doms);
3147dd7cddfSDavid du Colombier 	lastsender = s_copy(s_to_c(s_restart(path)));
3157dd7cddfSDavid du Colombier 	s_free(lpath);
3167dd7cddfSDavid du Colombier 	lastret = action;
3177dd7cddfSDavid du Colombier 	return action;
3187dd7cddfSDavid du Colombier }
3197dd7cddfSDavid du Colombier 
3207dd7cddfSDavid du Colombier /*
3217dd7cddfSDavid du Colombier  * get a canonicalized line: a string of null-terminated lower-case
3227dd7cddfSDavid du Colombier  * tokens with a two null bytes at the end.
3237dd7cddfSDavid du Colombier  */
3247dd7cddfSDavid du Colombier static char*
3257dd7cddfSDavid du Colombier getline(Biobuf *bp)
3267dd7cddfSDavid du Colombier {
3277dd7cddfSDavid du Colombier 	char c, *cp, *p, *q;
3287dd7cddfSDavid du Colombier 	int n;
3297dd7cddfSDavid du Colombier 
3307dd7cddfSDavid du Colombier 	static char *buf;
3317dd7cddfSDavid du Colombier 	static int bufsize;
3327dd7cddfSDavid du Colombier 
3337dd7cddfSDavid du Colombier 	for(;;){
3347dd7cddfSDavid du Colombier 		cp = Brdline(bp, '\n');
3357dd7cddfSDavid du Colombier 		if(cp == 0)
3367dd7cddfSDavid du Colombier 			return 0;
3377dd7cddfSDavid du Colombier 		n = Blinelen(bp);
3387dd7cddfSDavid du Colombier 		cp[n-1] = 0;
3397dd7cddfSDavid du Colombier 		if(buf == 0 || bufsize < n+1){
3407dd7cddfSDavid du Colombier 			bufsize += 512;
3417dd7cddfSDavid du Colombier 			if(bufsize < n+1)
3427dd7cddfSDavid du Colombier 				bufsize = n+1;
3437dd7cddfSDavid du Colombier 			buf = realloc(buf, bufsize);
3447dd7cddfSDavid du Colombier 			if(buf == 0)
3457dd7cddfSDavid du Colombier 				break;
3467dd7cddfSDavid du Colombier 		}
3477dd7cddfSDavid du Colombier 		q = buf;
3487dd7cddfSDavid du Colombier 		for (p = cp; *p; p++){
3497dd7cddfSDavid du Colombier 			c = *p;
3507dd7cddfSDavid du Colombier 			if(c == '\\' && p[1])	/* we don't allow \<newline> */
3517dd7cddfSDavid du Colombier 				c = *++p;
3527dd7cddfSDavid du Colombier 			else
3537dd7cddfSDavid du Colombier 			if(c == '#')
3547dd7cddfSDavid du Colombier 				break;
3557dd7cddfSDavid du Colombier 			else
3567dd7cddfSDavid du Colombier 			if(c == ' ' || c == '\t' || c == ',')
3577dd7cddfSDavid du Colombier 				if(q == buf || q[-1] == 0)
3587dd7cddfSDavid du Colombier 					continue;
3597dd7cddfSDavid du Colombier 				else
3607dd7cddfSDavid du Colombier 					c = 0;
3617dd7cddfSDavid du Colombier 			*q++ = tolower(c);
3627dd7cddfSDavid du Colombier 		}
3637dd7cddfSDavid du Colombier 		if(q != buf){
3647dd7cddfSDavid du Colombier 			if(q[-1])
3657dd7cddfSDavid du Colombier 				*q++ = 0;
3667dd7cddfSDavid du Colombier 			*q = 0;
3677dd7cddfSDavid du Colombier 			break;
3687dd7cddfSDavid du Colombier 		}
3697dd7cddfSDavid du Colombier 	}
3707dd7cddfSDavid du Colombier 	return buf;
3717dd7cddfSDavid du Colombier }
3727dd7cddfSDavid du Colombier 
3737dd7cddfSDavid du Colombier int
3747dd7cddfSDavid du Colombier forwarding(String *path)
3757dd7cddfSDavid du Colombier {
3767dd7cddfSDavid du Colombier 	char *cp, *s;
3777dd7cddfSDavid du Colombier 	String *lpath;
3787dd7cddfSDavid du Colombier 	Link *l;
3797dd7cddfSDavid du Colombier 
3807dd7cddfSDavid du Colombier 	if(debug)
3817dd7cddfSDavid du Colombier 		fprint(2, "forwarding(%s)\n", s_to_c(path));
3827dd7cddfSDavid du Colombier 
3837dd7cddfSDavid du Colombier 		/* first check if they want loopback */
3847dd7cddfSDavid du Colombier 
3857dd7cddfSDavid du Colombier 	lpath = s_copy(s_to_c(s_restart(path)));
3867dd7cddfSDavid du Colombier 	if(hisaddr && *hisaddr){
3877dd7cddfSDavid du Colombier 		cp = s_to_c(lpath);
3887dd7cddfSDavid du Colombier 		if(strncmp(cp, "[]!", 3) == 0){
3897dd7cddfSDavid du Colombier found:
3907dd7cddfSDavid du Colombier 			s_append(path, "[");
3917dd7cddfSDavid du Colombier 			s_append(path, hisaddr);
3927dd7cddfSDavid du Colombier 			s_append(path, "]!");
3937dd7cddfSDavid du Colombier 			s_append(path, cp+3);
3947dd7cddfSDavid du Colombier 			s_terminate(path);
3957dd7cddfSDavid du Colombier 			s_free(lpath);
3967dd7cddfSDavid du Colombier 			return 0;
3977dd7cddfSDavid du Colombier 		}
3987dd7cddfSDavid du Colombier 		cp = strchr(cp,'!');			/* skip our domain and check next */
3997dd7cddfSDavid du Colombier 		if(cp++ && strncmp(cp, "[]!", 3) == 0)
4007dd7cddfSDavid du Colombier 			goto found;
4017dd7cddfSDavid du Colombier 	}
4027dd7cddfSDavid du Colombier 		/* if mail is from a trusted subnet, allow it to forward*/
4037dd7cddfSDavid du Colombier 	if(trusted) {
4047dd7cddfSDavid du Colombier 		s_free(lpath);
4057dd7cddfSDavid du Colombier 		return 0;
4067dd7cddfSDavid du Colombier 	}
4077dd7cddfSDavid du Colombier 
4087dd7cddfSDavid du Colombier 		/* sender is untrusted; ensure receiver is in one of our domains */
4097dd7cddfSDavid du Colombier 	for(cp = s_to_c(lpath); *cp; cp++)		/* convert receiver lc */
4107dd7cddfSDavid du Colombier 		*cp = tolower(*cp);
4117dd7cddfSDavid du Colombier 
4127dd7cddfSDavid du Colombier 	for(s = s_to_c(lpath); cp = strchr(s, '!'); s = cp+1){
4137dd7cddfSDavid du Colombier 		*cp = 0;
4147dd7cddfSDavid du Colombier 		if(strchr(s, '.')){
4157dd7cddfSDavid du Colombier 			for(l = ourdoms.first; l; l = l->next){
4167dd7cddfSDavid du Colombier 				if(dommatch(s, s_to_c(l->p)) == 0)
4177dd7cddfSDavid du Colombier 					break;
4187dd7cddfSDavid du Colombier 			}
4197dd7cddfSDavid du Colombier 			if(l == 0){
4207dd7cddfSDavid du Colombier 				*cp = '!';
4217dd7cddfSDavid du Colombier 				s_free(lpath);
4227dd7cddfSDavid du Colombier 				return 1;
4237dd7cddfSDavid du Colombier 			}
4247dd7cddfSDavid du Colombier 		}
4257dd7cddfSDavid du Colombier 		*cp = '!';
4267dd7cddfSDavid du Colombier 	}
4277dd7cddfSDavid du Colombier 	s_free(lpath);
4287dd7cddfSDavid du Colombier 	return 0;
4297dd7cddfSDavid du Colombier }
4307dd7cddfSDavid du Colombier 
4317dd7cddfSDavid du Colombier int
4327dd7cddfSDavid du Colombier cidrcheck(char *cp)
4337dd7cddfSDavid du Colombier {
4347dd7cddfSDavid du Colombier 	char *p;
4357dd7cddfSDavid du Colombier 	ulong a, m;
4367dd7cddfSDavid du Colombier 	uchar addr[IPv4addrlen];
4377dd7cddfSDavid du Colombier 	uchar mask[IPv4addrlen];
4387dd7cddfSDavid du Colombier 
4397dd7cddfSDavid du Colombier 	if(peerip == 0)
4407dd7cddfSDavid du Colombier 		return 0;
4417dd7cddfSDavid du Colombier 
4427dd7cddfSDavid du Colombier 		/* parse a list of CIDR addresses comparing each to the peer IP addr */
4437dd7cddfSDavid du Colombier 	while(cp && *cp){
4447dd7cddfSDavid du Colombier 		v4parsecidr(addr, mask, cp);
4457dd7cddfSDavid du Colombier 		a = nhgetl(addr);
4467dd7cddfSDavid du Colombier 		m = nhgetl(mask);
4477dd7cddfSDavid du Colombier 		/*
4487dd7cddfSDavid du Colombier 		 * if a mask isn't specified, we build a minimal mask
4497dd7cddfSDavid du Colombier 		 * instead of using the default mask for that net.  in this
4507dd7cddfSDavid du Colombier 		 * case we never allow a class A mask (0xff000000).
4517dd7cddfSDavid du Colombier 		 */
4527dd7cddfSDavid du Colombier 		if(strchr(cp, '/') == 0){
4537dd7cddfSDavid du Colombier 			m = 0xff000000;
4547dd7cddfSDavid du Colombier 			p = cp;
4557dd7cddfSDavid du Colombier 			for(p = strchr(p, '.'); p && p[1]; p = strchr(p+1, '.'))
4567dd7cddfSDavid du Colombier 					m = (m>>8)|0xff000000;
4577dd7cddfSDavid du Colombier 
4587dd7cddfSDavid du Colombier 			/* force at least a class B */
4597dd7cddfSDavid du Colombier 			m |= 0xffff0000;
4607dd7cddfSDavid du Colombier 		}
4617dd7cddfSDavid du Colombier 		if((peerip&m) == a)
4627dd7cddfSDavid du Colombier 			return 1;
4637dd7cddfSDavid du Colombier 		cp += strlen(cp)+1;
4647dd7cddfSDavid du Colombier 	}
4657dd7cddfSDavid du Colombier 	return 0;
4667dd7cddfSDavid du Colombier }
4677dd7cddfSDavid du Colombier 
4687dd7cddfSDavid du Colombier char*
4697dd7cddfSDavid du Colombier dumpfile(char *sender)
4707dd7cddfSDavid du Colombier {
4717dd7cddfSDavid du Colombier 	int i, fd;
4727dd7cddfSDavid du Colombier 	ulong h;
4737dd7cddfSDavid du Colombier 	static char buf[512];
4747dd7cddfSDavid du Colombier 	char *cp;
4757dd7cddfSDavid du Colombier 
4767dd7cddfSDavid du Colombier 	if (sflag == 1){
4777dd7cddfSDavid du Colombier 		cp = ctime(time(0));
4787dd7cddfSDavid du Colombier 		cp[7] = 0;
4797dd7cddfSDavid du Colombier 		if(cp[8] == ' ')
4807dd7cddfSDavid du Colombier 			sprint(buf, "%s/queue.dump/%s%c", SPOOL, cp+4, cp[9]);
4817dd7cddfSDavid du Colombier 		else
4827dd7cddfSDavid du Colombier 			sprint(buf, "%s/queue.dump/%s%c%c", SPOOL, cp+4, cp[8], cp[9]);
4837dd7cddfSDavid du Colombier 		cp = buf+strlen(buf);
4847dd7cddfSDavid du Colombier 		if(access(buf, 0) < 0 && sysmkdir(buf, 0777) < 0)
4857dd7cddfSDavid du Colombier 			return "/dev/null";
4867dd7cddfSDavid du Colombier 		h = 0;
4877dd7cddfSDavid du Colombier 		while(*sender)
4887dd7cddfSDavid du Colombier 			h = h*257 + *sender++;
4897dd7cddfSDavid du Colombier 		for(i = 0; i < 50; i++){
4907dd7cddfSDavid du Colombier 			h += lrand();
4917dd7cddfSDavid du Colombier 			sprint(cp, "/%lud", h);
4927dd7cddfSDavid du Colombier 			if(access(buf, 0) >= 0)
4937dd7cddfSDavid du Colombier 				continue;
494*59cc4ca5SDavid du Colombier 			fd = syscreate(buf, ORDWR, 0666);
4957dd7cddfSDavid du Colombier 			if(fd >= 0){
4967dd7cddfSDavid du Colombier 				if(debug)
4977dd7cddfSDavid du Colombier 					fprint(2, "saving in %s\n", buf);
4987dd7cddfSDavid du Colombier 				close(fd);
4997dd7cddfSDavid du Colombier 				return buf;
5007dd7cddfSDavid du Colombier 			}
5017dd7cddfSDavid du Colombier 		}
5027dd7cddfSDavid du Colombier 	}
5037dd7cddfSDavid du Colombier 	return "/dev/null";
5047dd7cddfSDavid du Colombier }
505