xref: /plan9/sys/src/cmd/upas/smtp/spam.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
1*7dd7cddfSDavid du Colombier #include "common.h"
2*7dd7cddfSDavid du Colombier #include "smtpd.h"
3*7dd7cddfSDavid du Colombier #include "ip.h"
4*7dd7cddfSDavid du Colombier 
5*7dd7cddfSDavid du Colombier enum {
6*7dd7cddfSDavid du Colombier 	NORELAY = 0,
7*7dd7cddfSDavid du Colombier 	DNSVERIFY,
8*7dd7cddfSDavid du Colombier 	SAVEBLOCK,
9*7dd7cddfSDavid du Colombier 	DOMNAME,
10*7dd7cddfSDavid du Colombier 	OURNETS,
11*7dd7cddfSDavid du Colombier 	OURDOMS,
12*7dd7cddfSDavid du Colombier 
13*7dd7cddfSDavid du Colombier 	IP = 0,
14*7dd7cddfSDavid du Colombier 	STRING,
15*7dd7cddfSDavid du Colombier };
16*7dd7cddfSDavid du Colombier 
17*7dd7cddfSDavid du Colombier 
18*7dd7cddfSDavid du Colombier typedef struct Keyword Keyword;
19*7dd7cddfSDavid du Colombier 
20*7dd7cddfSDavid du Colombier struct Keyword {
21*7dd7cddfSDavid du Colombier 	char	*name;
22*7dd7cddfSDavid du Colombier 	int	code;
23*7dd7cddfSDavid du Colombier };
24*7dd7cddfSDavid du Colombier 
25*7dd7cddfSDavid du Colombier static Keyword options[] = {
26*7dd7cddfSDavid du Colombier 	"norelay",		NORELAY,
27*7dd7cddfSDavid du Colombier 	"verifysenderdom",	DNSVERIFY,
28*7dd7cddfSDavid du Colombier 	"saveblockedmsg",	SAVEBLOCK,
29*7dd7cddfSDavid du Colombier 	"defaultdomain",	DOMNAME,
30*7dd7cddfSDavid du Colombier 	"ournets",		OURNETS,
31*7dd7cddfSDavid du Colombier 	"ourdomains",		OURDOMS,
32*7dd7cddfSDavid du Colombier 	0,			NONE,
33*7dd7cddfSDavid du Colombier };
34*7dd7cddfSDavid du Colombier 
35*7dd7cddfSDavid du Colombier static Keyword actions[] = {
36*7dd7cddfSDavid du Colombier 	"allow",		ACCEPT,
37*7dd7cddfSDavid du Colombier 	"accept",		ACCEPT,
38*7dd7cddfSDavid du Colombier 	"block",		BLOCKED,
39*7dd7cddfSDavid du Colombier 	"deny",			DENIED,
40*7dd7cddfSDavid du Colombier 	"dial",			DIALUP,
41*7dd7cddfSDavid du Colombier 	"relay",		RELAY,
42*7dd7cddfSDavid du Colombier 	0,			NONE,
43*7dd7cddfSDavid du Colombier };
44*7dd7cddfSDavid du Colombier 
45*7dd7cddfSDavid du Colombier static	char*	getline(Biobuf*);
46*7dd7cddfSDavid du Colombier 
47*7dd7cddfSDavid du Colombier static int
48*7dd7cddfSDavid du Colombier findkey(char *val, Keyword *p)
49*7dd7cddfSDavid du Colombier {
50*7dd7cddfSDavid du Colombier 
51*7dd7cddfSDavid du Colombier 	for(; p->name; p++)
52*7dd7cddfSDavid du Colombier 		if(strcmp(val, p->name) == 0)
53*7dd7cddfSDavid du Colombier 				break;
54*7dd7cddfSDavid du Colombier 	return p->code;
55*7dd7cddfSDavid du Colombier }
56*7dd7cddfSDavid du Colombier 
57*7dd7cddfSDavid du Colombier void
58*7dd7cddfSDavid du Colombier getconf(void)
59*7dd7cddfSDavid du Colombier {
60*7dd7cddfSDavid du Colombier 	Biobuf *bp;
61*7dd7cddfSDavid du Colombier 	char *cp, *p;
62*7dd7cddfSDavid du Colombier 	String *s;
63*7dd7cddfSDavid du Colombier 	char buf[512];
64*7dd7cddfSDavid du Colombier 
65*7dd7cddfSDavid du Colombier 	snprint(buf, sizeof(buf), "%s/smtpd.conf", UPASLIB);
66*7dd7cddfSDavid du Colombier 	bp = sysopen(buf, "r", 0);
67*7dd7cddfSDavid du Colombier 	if(bp == 0)
68*7dd7cddfSDavid du Colombier 		return;
69*7dd7cddfSDavid du Colombier 
70*7dd7cddfSDavid du Colombier 	for(;;){
71*7dd7cddfSDavid du Colombier 		cp = getline(bp);
72*7dd7cddfSDavid du Colombier 		if(cp == 0)
73*7dd7cddfSDavid du Colombier 			break;
74*7dd7cddfSDavid du Colombier 		p = cp+strlen(cp)+1;
75*7dd7cddfSDavid du Colombier 		switch(findkey(cp, options)){
76*7dd7cddfSDavid du Colombier 		case NORELAY:
77*7dd7cddfSDavid du Colombier 			if(fflag == 0 && strcmp(p, "on") == 0)
78*7dd7cddfSDavid du Colombier 				fflag++;
79*7dd7cddfSDavid du Colombier 			break;
80*7dd7cddfSDavid du Colombier 		case DNSVERIFY:
81*7dd7cddfSDavid du Colombier 			if(rflag == 0 && strcmp(p, "on") == 0)
82*7dd7cddfSDavid du Colombier 				rflag++;
83*7dd7cddfSDavid du Colombier 			break;
84*7dd7cddfSDavid du Colombier 		case SAVEBLOCK:
85*7dd7cddfSDavid du Colombier 			if(sflag == 0 && strcmp(p, "on") == 0)
86*7dd7cddfSDavid du Colombier 				sflag++;
87*7dd7cddfSDavid du Colombier 			break;
88*7dd7cddfSDavid du Colombier 		case DOMNAME:
89*7dd7cddfSDavid du Colombier 			if(dom == 0)
90*7dd7cddfSDavid du Colombier 				dom = strdup(p);
91*7dd7cddfSDavid du Colombier 			break;
92*7dd7cddfSDavid du Colombier 		case OURNETS:
93*7dd7cddfSDavid du Colombier 			if (trusted == 0)
94*7dd7cddfSDavid du Colombier 				trusted = cidrcheck(p);
95*7dd7cddfSDavid du Colombier 			break;
96*7dd7cddfSDavid du Colombier 		case OURDOMS:
97*7dd7cddfSDavid du Colombier 			while(*p){
98*7dd7cddfSDavid du Colombier 				s = s_new();
99*7dd7cddfSDavid du Colombier 				s_append(s, p);
100*7dd7cddfSDavid du Colombier 				listadd(&ourdoms, s);
101*7dd7cddfSDavid du Colombier 				p += strlen(p)+1;
102*7dd7cddfSDavid du Colombier 			}
103*7dd7cddfSDavid du Colombier 			break;
104*7dd7cddfSDavid du Colombier 		default:
105*7dd7cddfSDavid du Colombier 			break;
106*7dd7cddfSDavid du Colombier 		}
107*7dd7cddfSDavid du Colombier 	}
108*7dd7cddfSDavid du Colombier 	sysclose(bp);
109*7dd7cddfSDavid du Colombier }
110*7dd7cddfSDavid du Colombier 
111*7dd7cddfSDavid du Colombier /*
112*7dd7cddfSDavid du Colombier  *	match a user name.  the only meta-char is '*' which matches all
113*7dd7cddfSDavid du Colombier  *	characters.  we only allow it as "*", which matches anything or
114*7dd7cddfSDavid du Colombier  *	an * at the end of the name (e.g., "username*") which matches
115*7dd7cddfSDavid du Colombier  *	trailing characters.
116*7dd7cddfSDavid du Colombier  */
117*7dd7cddfSDavid du Colombier static int
118*7dd7cddfSDavid du Colombier usermatch(char *pathuser, char *specuser)
119*7dd7cddfSDavid du Colombier {
120*7dd7cddfSDavid du Colombier 	int n;
121*7dd7cddfSDavid du Colombier 
122*7dd7cddfSDavid du Colombier 	n = strlen(specuser)-1;
123*7dd7cddfSDavid du Colombier 	if(specuser[n] == '*'){
124*7dd7cddfSDavid du Colombier 		if(n == 0)		/* match everything */
125*7dd7cddfSDavid du Colombier 			return 0;
126*7dd7cddfSDavid du Colombier 		return strncmp(pathuser, specuser, n);
127*7dd7cddfSDavid du Colombier 	}
128*7dd7cddfSDavid du Colombier 	return strcmp(pathuser, specuser);
129*7dd7cddfSDavid du Colombier }
130*7dd7cddfSDavid du Colombier 
131*7dd7cddfSDavid du Colombier static int
132*7dd7cddfSDavid du Colombier dommatch(char *pathdom, char *specdom)
133*7dd7cddfSDavid du Colombier {
134*7dd7cddfSDavid du Colombier 	int n;
135*7dd7cddfSDavid du Colombier 
136*7dd7cddfSDavid du Colombier 	if (*specdom == '*'){
137*7dd7cddfSDavid du Colombier 		if (specdom[1] == '.' && specdom[2]){
138*7dd7cddfSDavid du Colombier 			specdom += 2;
139*7dd7cddfSDavid du Colombier 			n = strlen(pathdom)-strlen(specdom);
140*7dd7cddfSDavid du Colombier 			if(n == 0 || (n > 0 && pathdom[n-1] == '.'))
141*7dd7cddfSDavid du Colombier 				return strcmp(pathdom+n, specdom);
142*7dd7cddfSDavid du Colombier 			return n;
143*7dd7cddfSDavid du Colombier 		}
144*7dd7cddfSDavid du Colombier 	}
145*7dd7cddfSDavid du Colombier 	return strcmp(pathdom, specdom);
146*7dd7cddfSDavid du Colombier }
147*7dd7cddfSDavid du Colombier 
148*7dd7cddfSDavid du Colombier /*
149*7dd7cddfSDavid du Colombier  * match path components to prohibited domain & user specifications.  patterns include:
150*7dd7cddfSDavid du Colombier  *	domain, domain! or domain!*	  - all users in domain
151*7dd7cddfSDavid du Colombier  *	*.domain, *.domain! or *.domain!* - all users in domain and its subdomains
152*7dd7cddfSDavid du Colombier  *	!user or *!user			  - user in all domains
153*7dd7cddfSDavid du Colombier  *	domain!user			  - user in domain
154*7dd7cddfSDavid du Colombier  *	*.domain!user			  - user in domain and its subdomains
155*7dd7cddfSDavid du Colombier  *
156*7dd7cddfSDavid du Colombier  *	if "user" has a trailing '*', it matches all user names beginning with "user"
157*7dd7cddfSDavid du Colombier  *
158*7dd7cddfSDavid du Colombier  * there are special semantics for the "domain, domain! or domain!*" specifications:
159*7dd7cddfSDavid du Colombier  * the first two forms match when the domain is anywhere in at list of source-routed
160*7dd7cddfSDavid du Colombier  * domains while the latter matches only when the domain is the last hop.  the same is
161*7dd7cddfSDavid du Colombier  * true for the *.domain!* form of the pattern.
162*7dd7cddfSDavid du Colombier  */
163*7dd7cddfSDavid du Colombier static int
164*7dd7cddfSDavid du Colombier accountmatch(char *spec, List *doms, char *user)
165*7dd7cddfSDavid du Colombier {
166*7dd7cddfSDavid du Colombier 	char *cp, *p;
167*7dd7cddfSDavid du Colombier 	int i, n;
168*7dd7cddfSDavid du Colombier 	Link *l;
169*7dd7cddfSDavid du Colombier 
170*7dd7cddfSDavid du Colombier 	static char *dangerous[] = { "*", "!", "*!", "!*", "*!*", 0 };
171*7dd7cddfSDavid du Colombier 
172*7dd7cddfSDavid du Colombier 	for(; *spec; spec += n){
173*7dd7cddfSDavid du Colombier 		n = strlen(spec)+1;
174*7dd7cddfSDavid du Colombier 
175*7dd7cddfSDavid du Colombier 		/* rule out dangerous patterns */
176*7dd7cddfSDavid du Colombier 		for (i = 0; dangerous[i]; i++)
177*7dd7cddfSDavid du Colombier 			if(strcmp(spec, dangerous[i])== 0)
178*7dd7cddfSDavid du Colombier 				break;
179*7dd7cddfSDavid du Colombier 		if(dangerous[i])
180*7dd7cddfSDavid du Colombier 			continue;
181*7dd7cddfSDavid du Colombier 
182*7dd7cddfSDavid du Colombier 		p = 0;
183*7dd7cddfSDavid du Colombier 		cp = strchr(spec, '!');
184*7dd7cddfSDavid du Colombier 		if(cp){
185*7dd7cddfSDavid du Colombier 			*cp++ = 0;
186*7dd7cddfSDavid du Colombier 			if(*cp)
187*7dd7cddfSDavid du Colombier 				p = cp;
188*7dd7cddfSDavid du Colombier 		}
189*7dd7cddfSDavid du Colombier 
190*7dd7cddfSDavid du Colombier 		if(p == 0){			/* no user field - domain match only */
191*7dd7cddfSDavid du Colombier 			for(l = doms->first; l; l = l->next)
192*7dd7cddfSDavid du Colombier 				if(dommatch(s_to_c(l->p), spec) == 0)
193*7dd7cddfSDavid du Colombier 					return 1;
194*7dd7cddfSDavid du Colombier 		} else {
195*7dd7cddfSDavid du Colombier 			/* check for "!user", "*!user" or "domain!user" */
196*7dd7cddfSDavid du Colombier 			if(usermatch(user, p) == 0){
197*7dd7cddfSDavid du Colombier 				if(*spec == 0 || strcmp(spec, "*") == 0)
198*7dd7cddfSDavid du Colombier 					return 1;
199*7dd7cddfSDavid du Colombier 				if(doms->last && dommatch(s_to_c(doms->last->p), spec) == 0)
200*7dd7cddfSDavid du Colombier 					return 1;
201*7dd7cddfSDavid du Colombier 			}
202*7dd7cddfSDavid du Colombier 		}
203*7dd7cddfSDavid du Colombier 	}
204*7dd7cddfSDavid du Colombier 	return 0;
205*7dd7cddfSDavid du Colombier }
206*7dd7cddfSDavid du Colombier 
207*7dd7cddfSDavid du Colombier /*
208*7dd7cddfSDavid du Colombier  * we risk reparsing the file of blocked addresses if there are
209*7dd7cddfSDavid du Colombier  * multiple senders or if the peer does a transaction and then a RSET
210*7dd7cddfSDavid du Colombier  * followed by another transaction.  we believe this happens rarely,
211*7dd7cddfSDavid du Colombier  * but we cache the last sender to try to minimize the overhead.  we
212*7dd7cddfSDavid du Colombier  * also cache whether we rejected a previous transaction based on
213*7dd7cddfSDavid du Colombier  * ip address, since this never changes.
214*7dd7cddfSDavid du Colombier  */
215*7dd7cddfSDavid du Colombier int
216*7dd7cddfSDavid du Colombier blocked(String *path)
217*7dd7cddfSDavid du Colombier {
218*7dd7cddfSDavid du Colombier 	char buf[512], *cp, *p, *user;
219*7dd7cddfSDavid du Colombier 	Biobuf *bp;
220*7dd7cddfSDavid du Colombier 	int action, type, ipallow;
221*7dd7cddfSDavid du Colombier 	List doms;
222*7dd7cddfSDavid du Colombier 	String *s, *lpath;
223*7dd7cddfSDavid du Colombier 
224*7dd7cddfSDavid du Colombier 	static String *lastsender;
225*7dd7cddfSDavid du Colombier 	static int lastret, blockedbyip;
226*7dd7cddfSDavid du Colombier 
227*7dd7cddfSDavid du Colombier 	if(debug)
228*7dd7cddfSDavid du Colombier 		fprint(2, "blocked(%s)\n", s_to_c(path));
229*7dd7cddfSDavid du Colombier 
230*7dd7cddfSDavid du Colombier 	if(blockedbyip)
231*7dd7cddfSDavid du Colombier 		return lastret;
232*7dd7cddfSDavid du Colombier 
233*7dd7cddfSDavid du Colombier 	if(lastsender){
234*7dd7cddfSDavid du Colombier 		if(strcmp(s_to_c(lastsender), s_to_c(path)) == 0)
235*7dd7cddfSDavid du Colombier 			return lastret;
236*7dd7cddfSDavid du Colombier 		s_free(lastsender);
237*7dd7cddfSDavid du Colombier 		lastsender = 0;
238*7dd7cddfSDavid du Colombier 	}
239*7dd7cddfSDavid du Colombier 
240*7dd7cddfSDavid du Colombier 	snprint(buf, sizeof(buf), "%s/blocked", UPASLIB);
241*7dd7cddfSDavid du Colombier 	bp = sysopen(buf, "r", 0);
242*7dd7cddfSDavid du Colombier 	if(bp == 0)
243*7dd7cddfSDavid du Colombier 		return ACCEPT;
244*7dd7cddfSDavid du Colombier 
245*7dd7cddfSDavid du Colombier 	lpath = s_copy(s_to_c(s_restart(path)));	/* convert to LC */
246*7dd7cddfSDavid du Colombier 	for(cp = s_to_c(lpath); *cp; cp++)
247*7dd7cddfSDavid du Colombier 		*cp = tolower(*cp);
248*7dd7cddfSDavid du Colombier 
249*7dd7cddfSDavid du Colombier 	/* parse the path into a list of domains and a user */
250*7dd7cddfSDavid du Colombier 	doms.first = doms.last = 0;
251*7dd7cddfSDavid du Colombier 	p = s_to_c(lpath);
252*7dd7cddfSDavid du Colombier 	while (cp = strchr(p, '!')){
253*7dd7cddfSDavid du Colombier 		*cp = 0;
254*7dd7cddfSDavid du Colombier 		s = s_new();
255*7dd7cddfSDavid du Colombier 		s_append(s, p);
256*7dd7cddfSDavid du Colombier 		*cp = '!';
257*7dd7cddfSDavid du Colombier 		listadd(&doms, s);
258*7dd7cddfSDavid du Colombier 		p = cp+1;
259*7dd7cddfSDavid du Colombier 	}
260*7dd7cddfSDavid du Colombier 	user = p;
261*7dd7cddfSDavid du Colombier 
262*7dd7cddfSDavid du Colombier 	/*
263*7dd7cddfSDavid du Colombier 	 * line format: [*]VERB param 1, ... param n,  where '*' indicates
264*7dd7cddfSDavid du Colombier 	 * params are path names; if not present, params are ip addresses in
265*7dd7cddfSDavid du Colombier 	 * CIDR format.
266*7dd7cddfSDavid du Colombier 	 */
267*7dd7cddfSDavid du Colombier 	ipallow = 0;
268*7dd7cddfSDavid du Colombier 	for(;;){
269*7dd7cddfSDavid du Colombier 		action = ACCEPT;
270*7dd7cddfSDavid du Colombier 		cp = getline(bp);
271*7dd7cddfSDavid du Colombier 		if(cp == 0)
272*7dd7cddfSDavid du Colombier 			break;
273*7dd7cddfSDavid du Colombier 		type = *cp;
274*7dd7cddfSDavid du Colombier 		if(type == '*') {
275*7dd7cddfSDavid du Colombier 			cp++;
276*7dd7cddfSDavid du Colombier 			if(*cp == 0)
277*7dd7cddfSDavid du Colombier 				cp++;
278*7dd7cddfSDavid du Colombier 		}
279*7dd7cddfSDavid du Colombier 		action = findkey(cp, actions);
280*7dd7cddfSDavid du Colombier 		if (action == NONE)
281*7dd7cddfSDavid du Colombier 			continue;
282*7dd7cddfSDavid du Colombier 
283*7dd7cddfSDavid du Colombier 		cp += strlen(cp)+1;
284*7dd7cddfSDavid du Colombier 		if(type == '*') {
285*7dd7cddfSDavid du Colombier 			if(accountmatch(cp, &doms, user))
286*7dd7cddfSDavid du Colombier 				break;
287*7dd7cddfSDavid du Colombier 		} else {
288*7dd7cddfSDavid du Colombier 			/*
289*7dd7cddfSDavid du Colombier 			 * An ACCEPT match on IP addresses overrides all later
290*7dd7cddfSDavid du Colombier 			 * blocks on IP address but not blocks on domain or user names
291*7dd7cddfSDavid du Colombier 			 */
292*7dd7cddfSDavid du Colombier 			if(ipallow == 0 && cidrcheck(cp))
293*7dd7cddfSDavid du Colombier 				if(action == ACCEPT)
294*7dd7cddfSDavid du Colombier 					ipallow = 1;
295*7dd7cddfSDavid du Colombier 				else {
296*7dd7cddfSDavid du Colombier 					blockedbyip = 1;
297*7dd7cddfSDavid du Colombier 					break;
298*7dd7cddfSDavid du Colombier 				}
299*7dd7cddfSDavid du Colombier 		}
300*7dd7cddfSDavid du Colombier 	}
301*7dd7cddfSDavid du Colombier 	sysclose(bp);
302*7dd7cddfSDavid du Colombier 	listfree(&doms);
303*7dd7cddfSDavid du Colombier 	lastsender = s_copy(s_to_c(s_restart(path)));
304*7dd7cddfSDavid du Colombier 	s_free(lpath);
305*7dd7cddfSDavid du Colombier 	lastret = action;
306*7dd7cddfSDavid du Colombier 	return action;
307*7dd7cddfSDavid du Colombier }
308*7dd7cddfSDavid du Colombier 
309*7dd7cddfSDavid du Colombier /*
310*7dd7cddfSDavid du Colombier  * get a canonicalized line: a string of null-terminated lower-case
311*7dd7cddfSDavid du Colombier  * tokens with a two null bytes at the end.
312*7dd7cddfSDavid du Colombier  */
313*7dd7cddfSDavid du Colombier static char*
314*7dd7cddfSDavid du Colombier getline(Biobuf *bp)
315*7dd7cddfSDavid du Colombier {
316*7dd7cddfSDavid du Colombier 	char c, *cp, *p, *q;
317*7dd7cddfSDavid du Colombier 	int n;
318*7dd7cddfSDavid du Colombier 
319*7dd7cddfSDavid du Colombier 	static char *buf;
320*7dd7cddfSDavid du Colombier 	static int bufsize;
321*7dd7cddfSDavid du Colombier 
322*7dd7cddfSDavid du Colombier 	for(;;){
323*7dd7cddfSDavid du Colombier 		cp = Brdline(bp, '\n');
324*7dd7cddfSDavid du Colombier 		if(cp == 0)
325*7dd7cddfSDavid du Colombier 			return 0;
326*7dd7cddfSDavid du Colombier 		n = Blinelen(bp);
327*7dd7cddfSDavid du Colombier 		cp[n-1] = 0;
328*7dd7cddfSDavid du Colombier 		if(buf == 0 || bufsize < n+1){
329*7dd7cddfSDavid du Colombier 			bufsize += 512;
330*7dd7cddfSDavid du Colombier 			if(bufsize < n+1)
331*7dd7cddfSDavid du Colombier 				bufsize = n+1;
332*7dd7cddfSDavid du Colombier 			buf = realloc(buf, bufsize);
333*7dd7cddfSDavid du Colombier 			if(buf == 0)
334*7dd7cddfSDavid du Colombier 				break;
335*7dd7cddfSDavid du Colombier 		}
336*7dd7cddfSDavid du Colombier 		q = buf;
337*7dd7cddfSDavid du Colombier 		for (p = cp; *p; p++){
338*7dd7cddfSDavid du Colombier 			c = *p;
339*7dd7cddfSDavid du Colombier 			if(c == '\\' && p[1])	/* we don't allow \<newline> */
340*7dd7cddfSDavid du Colombier 				c = *++p;
341*7dd7cddfSDavid du Colombier 			else
342*7dd7cddfSDavid du Colombier 			if(c == '#')
343*7dd7cddfSDavid du Colombier 				break;
344*7dd7cddfSDavid du Colombier 			else
345*7dd7cddfSDavid du Colombier 			if(c == ' ' || c == '\t' || c == ',')
346*7dd7cddfSDavid du Colombier 				if(q == buf || q[-1] == 0)
347*7dd7cddfSDavid du Colombier 					continue;
348*7dd7cddfSDavid du Colombier 				else
349*7dd7cddfSDavid du Colombier 					c = 0;
350*7dd7cddfSDavid du Colombier 			*q++ = tolower(c);
351*7dd7cddfSDavid du Colombier 		}
352*7dd7cddfSDavid du Colombier 		if(q != buf){
353*7dd7cddfSDavid du Colombier 			if(q[-1])
354*7dd7cddfSDavid du Colombier 				*q++ = 0;
355*7dd7cddfSDavid du Colombier 			*q = 0;
356*7dd7cddfSDavid du Colombier 			break;
357*7dd7cddfSDavid du Colombier 		}
358*7dd7cddfSDavid du Colombier 	}
359*7dd7cddfSDavid du Colombier 	return buf;
360*7dd7cddfSDavid du Colombier }
361*7dd7cddfSDavid du Colombier 
362*7dd7cddfSDavid du Colombier int
363*7dd7cddfSDavid du Colombier forwarding(String *path)
364*7dd7cddfSDavid du Colombier {
365*7dd7cddfSDavid du Colombier 	char *cp, *s;
366*7dd7cddfSDavid du Colombier 	String *lpath;
367*7dd7cddfSDavid du Colombier 	Link *l;
368*7dd7cddfSDavid du Colombier 
369*7dd7cddfSDavid du Colombier 	if(debug)
370*7dd7cddfSDavid du Colombier 		fprint(2, "forwarding(%s)\n", s_to_c(path));
371*7dd7cddfSDavid du Colombier 
372*7dd7cddfSDavid du Colombier 		/* first check if they want loopback */
373*7dd7cddfSDavid du Colombier 
374*7dd7cddfSDavid du Colombier 	lpath = s_copy(s_to_c(s_restart(path)));
375*7dd7cddfSDavid du Colombier 	if(hisaddr && *hisaddr){
376*7dd7cddfSDavid du Colombier 		cp = s_to_c(lpath);
377*7dd7cddfSDavid du Colombier 		if(strncmp(cp, "[]!", 3) == 0){
378*7dd7cddfSDavid du Colombier found:
379*7dd7cddfSDavid du Colombier 			s_append(path, "[");
380*7dd7cddfSDavid du Colombier 			s_append(path, hisaddr);
381*7dd7cddfSDavid du Colombier 			s_append(path, "]!");
382*7dd7cddfSDavid du Colombier 			s_append(path, cp+3);
383*7dd7cddfSDavid du Colombier 			s_terminate(path);
384*7dd7cddfSDavid du Colombier 			s_free(lpath);
385*7dd7cddfSDavid du Colombier 			return 0;
386*7dd7cddfSDavid du Colombier 		}
387*7dd7cddfSDavid du Colombier 		cp = strchr(cp,'!');			/* skip our domain and check next */
388*7dd7cddfSDavid du Colombier 		if(cp++ && strncmp(cp, "[]!", 3) == 0)
389*7dd7cddfSDavid du Colombier 			goto found;
390*7dd7cddfSDavid du Colombier 	}
391*7dd7cddfSDavid du Colombier 		/* if mail is from a trusted subnet, allow it to forward*/
392*7dd7cddfSDavid du Colombier 	if(trusted) {
393*7dd7cddfSDavid du Colombier 		s_free(lpath);
394*7dd7cddfSDavid du Colombier 		return 0;
395*7dd7cddfSDavid du Colombier 	}
396*7dd7cddfSDavid du Colombier 
397*7dd7cddfSDavid du Colombier 		/* sender is untrusted; ensure receiver is in one of our domains */
398*7dd7cddfSDavid du Colombier 	for(cp = s_to_c(lpath); *cp; cp++)		/* convert receiver lc */
399*7dd7cddfSDavid du Colombier 		*cp = tolower(*cp);
400*7dd7cddfSDavid du Colombier 
401*7dd7cddfSDavid du Colombier 	for(s = s_to_c(lpath); cp = strchr(s, '!'); s = cp+1){
402*7dd7cddfSDavid du Colombier 		*cp = 0;
403*7dd7cddfSDavid du Colombier 		if(strchr(s, '.')){
404*7dd7cddfSDavid du Colombier 			for(l = ourdoms.first; l; l = l->next){
405*7dd7cddfSDavid du Colombier 				if(dommatch(s, s_to_c(l->p)) == 0)
406*7dd7cddfSDavid du Colombier 					break;
407*7dd7cddfSDavid du Colombier 			}
408*7dd7cddfSDavid du Colombier 			if(l == 0){
409*7dd7cddfSDavid du Colombier 				*cp = '!';
410*7dd7cddfSDavid du Colombier 				s_free(lpath);
411*7dd7cddfSDavid du Colombier 				return 1;
412*7dd7cddfSDavid du Colombier 			}
413*7dd7cddfSDavid du Colombier 		}
414*7dd7cddfSDavid du Colombier 		*cp = '!';
415*7dd7cddfSDavid du Colombier 	}
416*7dd7cddfSDavid du Colombier 	s_free(lpath);
417*7dd7cddfSDavid du Colombier 	return 0;
418*7dd7cddfSDavid du Colombier }
419*7dd7cddfSDavid du Colombier 
420*7dd7cddfSDavid du Colombier int
421*7dd7cddfSDavid du Colombier cidrcheck(char *cp)
422*7dd7cddfSDavid du Colombier {
423*7dd7cddfSDavid du Colombier 	char *p;
424*7dd7cddfSDavid du Colombier 	ulong a, m;
425*7dd7cddfSDavid du Colombier 	uchar addr[IPv4addrlen];
426*7dd7cddfSDavid du Colombier 	uchar mask[IPv4addrlen];
427*7dd7cddfSDavid du Colombier 
428*7dd7cddfSDavid du Colombier 	if(peerip == 0)
429*7dd7cddfSDavid du Colombier 		return 0;
430*7dd7cddfSDavid du Colombier 
431*7dd7cddfSDavid du Colombier 		/* parse a list of CIDR addresses comparing each to the peer IP addr */
432*7dd7cddfSDavid du Colombier 	while(cp && *cp){
433*7dd7cddfSDavid du Colombier 		v4parsecidr(addr, mask, cp);
434*7dd7cddfSDavid du Colombier 		a = nhgetl(addr);
435*7dd7cddfSDavid du Colombier 		m = nhgetl(mask);
436*7dd7cddfSDavid du Colombier 		/*
437*7dd7cddfSDavid du Colombier 		 * if a mask isn't specified, we build a minimal mask
438*7dd7cddfSDavid du Colombier 		 * instead of using the default mask for that net.  in this
439*7dd7cddfSDavid du Colombier 		 * case we never allow a class A mask (0xff000000).
440*7dd7cddfSDavid du Colombier 		 */
441*7dd7cddfSDavid du Colombier 		if(strchr(cp, '/') == 0){
442*7dd7cddfSDavid du Colombier 			m = 0xff000000;
443*7dd7cddfSDavid du Colombier 			p = cp;
444*7dd7cddfSDavid du Colombier 			for(p = strchr(p, '.'); p && p[1]; p = strchr(p+1, '.'))
445*7dd7cddfSDavid du Colombier 					m = (m>>8)|0xff000000;
446*7dd7cddfSDavid du Colombier 
447*7dd7cddfSDavid du Colombier 			/* force at least a class B */
448*7dd7cddfSDavid du Colombier 			m |= 0xffff0000;
449*7dd7cddfSDavid du Colombier 		}
450*7dd7cddfSDavid du Colombier 		if((peerip&m) == a)
451*7dd7cddfSDavid du Colombier 			return 1;
452*7dd7cddfSDavid du Colombier 		cp += strlen(cp)+1;
453*7dd7cddfSDavid du Colombier 	}
454*7dd7cddfSDavid du Colombier 	return 0;
455*7dd7cddfSDavid du Colombier }
456*7dd7cddfSDavid du Colombier 
457*7dd7cddfSDavid du Colombier char*
458*7dd7cddfSDavid du Colombier dumpfile(char *sender)
459*7dd7cddfSDavid du Colombier {
460*7dd7cddfSDavid du Colombier 	int i, fd;
461*7dd7cddfSDavid du Colombier 	ulong h;
462*7dd7cddfSDavid du Colombier 	static char buf[512];
463*7dd7cddfSDavid du Colombier 	char *cp;
464*7dd7cddfSDavid du Colombier 
465*7dd7cddfSDavid du Colombier 	if (sflag == 1){
466*7dd7cddfSDavid du Colombier 		cp = ctime(time(0));
467*7dd7cddfSDavid du Colombier 		cp[7] = 0;
468*7dd7cddfSDavid du Colombier 		if(cp[8] == ' ')
469*7dd7cddfSDavid du Colombier 			sprint(buf, "%s/queue.dump/%s%c", SPOOL, cp+4, cp[9]);
470*7dd7cddfSDavid du Colombier 		else
471*7dd7cddfSDavid du Colombier 			sprint(buf, "%s/queue.dump/%s%c%c", SPOOL, cp+4, cp[8], cp[9]);
472*7dd7cddfSDavid du Colombier 		cp = buf+strlen(buf);
473*7dd7cddfSDavid du Colombier 		if(access(buf, 0) < 0 && sysmkdir(buf, 0777) < 0)
474*7dd7cddfSDavid du Colombier 			return "/dev/null";
475*7dd7cddfSDavid du Colombier 		h = 0;
476*7dd7cddfSDavid du Colombier 		while(*sender)
477*7dd7cddfSDavid du Colombier 			h = h*257 + *sender++;
478*7dd7cddfSDavid du Colombier 		for(i = 0; i < 50; i++){
479*7dd7cddfSDavid du Colombier 			h += lrand();
480*7dd7cddfSDavid du Colombier 			sprint(cp, "/%lud", h);
481*7dd7cddfSDavid du Colombier 			if(access(buf, 0) >= 0)
482*7dd7cddfSDavid du Colombier 				continue;
483*7dd7cddfSDavid du Colombier 			fd = syscreate(buf, ORDWR, 0600);
484*7dd7cddfSDavid du Colombier 			if(fd >= 0){
485*7dd7cddfSDavid du Colombier 				if(debug)
486*7dd7cddfSDavid du Colombier 					fprint(2, "saving in %s\n", buf);
487*7dd7cddfSDavid du Colombier 				close(fd);
488*7dd7cddfSDavid du Colombier 				return buf;
489*7dd7cddfSDavid du Colombier 			}
490*7dd7cddfSDavid du Colombier 		}
491*7dd7cddfSDavid du Colombier 	}
492*7dd7cddfSDavid du Colombier 	return "/dev/null";
493*7dd7cddfSDavid du Colombier }
494