Warning: Attempt to read property "date" on null in /usr/local/www/websvn.planix.org/blame.php on line 247

Warning: Attempt to read property "msg" on null in /usr/local/www/websvn.planix.org/blame.php on line 247
WebSVN – planix.SVN – Blame – /os/branches/planix-v0/sys/src/cmd/upas/smtp/mxdial.c – Rev 2

Subversion Repositories planix.SVN

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include "common.h"
2
#include <ndb.h>
3
#include <smtp.h>	/* to publish dial_string_parse */
4
 
5
enum
6
{
7
	Nmx=		16,
8
	Maxstring=	256,
9
	Maxipstr=	8*5,		/* ipv6 */
10
};
11
 
12
typedef struct Mx	Mx;
13
struct Mx
14
{
15
	char	host[Maxstring];
16
	char	ip[Maxipstr];		/* this is just the first ip */
17
	int	pref;
18
};
19
 
20
char	*bustedmxs[Maxbustedmx];
21
Ndb *db;
22
 
23
static Mx mx[Nmx];
24
 
25
static int	callmx(DS*, char*, char*);
26
static int	compar(void*, void*);
27
static void	expand_meta(DS *ds);
28
static int	mxlookup(DS*, char*);
29
static int	mxlookup1(DS*, char*);
30
 
31
int
32
mxdial(char *addr, char *ddomain, char *gdomain)
33
{
34
	int fd;
35
	DS ds;
36
	char err[Errlen];
37
 
38
	addr = netmkaddr(addr, 0, "smtp");
39
	dial_string_parse(addr, &ds);
40
 
41
	/* try connecting to destination or any of it's mail routers */
42
	fd = callmx(&ds, addr, ddomain);
43
 
44
	/* try our mail gateway */
45
	rerrstr(err, sizeof(err));
46
	if(fd < 0 && gdomain && strstr(err, "can't translate") != 0)
47
		fd = dial(netmkaddr(gdomain, 0, "smtp"), 0, 0, 0);
48
 
49
	return fd;
50
}
51
 
52
static int
53
busted(char *mx)
54
{
55
	char **bmp;
56
 
57
	for (bmp = bustedmxs; *bmp != nil; bmp++)
58
		if (strcmp(mx, *bmp) == 0)
59
			return 1;
60
	return 0;
61
}
62
 
63
static int
64
timeout(void*, char *msg)
65
{
66
	if(strstr(msg, "alarm"))
67
		return 1;
68
	return 0;
69
}
70
 
71
long
72
timedwrite(int fd, void *buf, long len, long ms)
73
{
74
	long n, oalarm;
75
 
76
	atnotify(timeout, 1);
77
	oalarm = alarm(ms);
78
	n = write(fd, buf, len);
79
	alarm(oalarm);
80
	atnotify(timeout, 0);
81
	return n;
82
}
83
 
84
static int
85
isloopback(char *ip)
86
{
87
	return strcmp(ip, "127.0.0.1") == 0 || strcmp(ip, "::1") == 0;
88
}
89
 
90
/*
91
 *  take an address and return all the mx entries for it,
92
 *  most preferred first
93
 */
94
static int
95
callmx(DS *ds, char *dest, char *domain)
96
{
97
	int fd, i, nmx;
98
	char *ip;
99
	char addr[Maxstring];
100
 
101
	/* get a list of mx entries */
102
	nmx = mxlookup(ds, domain);
103
	if(nmx < 0){
104
		/* dns isn't working, don't just dial */
105
		return -1;
106
	}
107
	if(nmx == 0){
108
		if(debug)
109
			fprint(2, "mxlookup returns nothing\n");
110
		return dial(dest, 0, 0, 0);
111
	}
112
 
113
	/* refuse to honor loopback addresses given by dns.  catch \n too. */
114
	for(i = 0; i < nmx; i++) {
115
		ip = mx[i].ip;
116
		if(strchr(ip, '\n') != nil){
117
			if(debug)
118
				fprint(2, "mxlookup ip contains newline\n");
119
			werrstr("illegal: newline in mail server ip");
120
			return -1;
121
		}else if(isloopback(ip)){
122
			if(debug)
123
				fprint(2, "mxlookup returns loopback\n");
124
			werrstr("illegal: domain lists %s as mail server", ip);
125
			return -1;
126
		}
127
	}
128
 
129
	/* sort by preference */
130
	if(nmx > 1)
131
		qsort(mx, nmx, sizeof(Mx), compar);
132
 
133
	/* dial each one in turn by name, not ip */
134
	for(i = 0; i < nmx; i++){
135
		if (busted(mx[i].host)) {
136
			if (debug)
137
				fprint(2, "mxdial skipping busted mx %s\n",
138
					mx[i].host);
139
			continue;
140
		}
141
		snprint(addr, sizeof(addr), "%s/%s!%s!%s", ds->netdir, ds->proto,
142
			mx[i].host, ds->service);
143
		if(debug)
144
			fprint(2, "mxdial trying %s\n", addr);
145
		atnotify(timeout, 1);
146
		/* this was 10 seconds, but oclsc.org at least needs more. */
147
		alarm(60*1000);
148
		fd = dial(addr, 0, 0, 0);
149
		if (debug && fd < 0)
150
			fprint(2, "dial: %r\n");
151
		alarm(0);
152
		atnotify(timeout, 0);
153
		if(fd >= 0)
154
			return fd;
155
	}
156
	return -1;
157
}
158
 
159
/*
160
 *  call the dns process and have it try to resolve the mx request
161
 *
162
 *  this routine knows about the firewall and tries inside and outside
163
 *  dns's seperately.
164
 */
165
static int
166
mxlookup(DS *ds, char *domain)
167
{
168
	int n;
169
 
170
	/* just in case we find no domain name */
171
	strcpy(domain, ds->host);
172
 
173
	if(ds->netdir)
174
		n = mxlookup1(ds, domain);
175
	else {
176
		ds->netdir = "/net";
177
		n = mxlookup1(ds, domain);
178
		if(n == 0) {
179
			ds->netdir = "/net.alt";
180
			n = mxlookup1(ds, domain);
181
		}
182
	}
183
 
184
	return n;
185
}
186
 
187
static int
188
mxlookup1(DS *ds, char *domain)
189
{
190
	int i, n, fd, nmx;
191
	char buf[Maxdomain], dnsname[Maxstring];
192
	char *fields[4];
193
	Mx *mxp;
194
 
195
	snprint(dnsname, sizeof dnsname, "%s/dns", ds->netdir);
196
 
197
	fd = open(dnsname, ORDWR);
198
	if(fd < 0)
199
		return 0;
200
 
201
	nmx = 0;
202
	snprint(buf, sizeof buf, "%s mx", ds->host);
203
	if(debug)
204
		fprint(2, "sending %s '%s'\n", dnsname, buf);
205
	/*
206
	 * don't hang indefinitely in the write to /net/dns.
207
	 */
208
	n = timedwrite(fd, buf, strlen(buf), 60*1000);
209
	if(n < 0){
210
		rerrstr(buf, sizeof buf);
211
		if(debug)
212
			fprint(2, "dns: %s\n", buf);
213
		if(strstr(buf, "dns failure")){
214
			/* if dns fails for the mx lookup, we have to stop */
215
			close(fd);
216
			return -1;
217
		}
218
	} else {
219
		/*
220
		 *  get any mx entries
221
		 *  assumes one record per read
222
		 */
223
		seek(fd, 0, 0);
224
		while(nmx < Nmx && (n = read(fd, buf, sizeof buf-1)) > 0){
225
			mxp = &mx[nmx];
226
			buf[n] = 0;
227
			if(debug)
228
				fprint(2, "dns mx: %s\n", buf);
229
			n = getfields(buf, fields, 4, 1, " \t");
230
			if(n < 4)
231
				continue;
232
 
233
			if(strchr(domain, '.') == 0)
234
				strcpy(domain, fields[0]);
235
 
236
			strncpy(mxp->host, fields[3], sizeof mxp->host - 1);
237
			mxp->host[sizeof mxp->host - 1] = '\0';
238
			mxp->pref = atoi(fields[2]);
239
			nmx++;
240
		}
241
		if(debug)
242
			fprint(2, "dns mx: got %d mx servers\n", nmx);
243
	}
244
 
245
	/*
246
	 * no mx record? try name itself.
247
	 *
248
	 * BUG? If domain has no dots, then we used to look up ds->host
249
	 * but return domain instead of ds->host in the list.  Now we return
250
	 * ds->host.  What will this break?
251
	 */
252
	if(nmx == 0){
253
		mx[0].pref = 1;
254
		strncpy(mx[0].host, ds->host, sizeof(mx[0].host));
255
		nmx++;
256
	}
257
 
258
	/*
259
	 * look up first ip address of each mx name.
260
	 * should really look at all addresses.
261
	 * assumes one record per read.
262
	 */
263
	for(i = 0; i < nmx; i++){
264
		mxp = &mx[i];
265
		seek(fd, 0, 0);
266
		snprint(buf, sizeof buf, "%s ip", mxp->host);
267
		mxp->ip[0] = 0;
268
		/*
269
		 * don't hang indefinitely in the write to /net/dns.
270
		 */
271
		if(timedwrite(fd, buf, strlen(buf), 60*1000) < 0)
272
			goto no;
273
		seek(fd, 0, 0);
274
		if((n = read(fd, buf, sizeof buf-1)) < 0)
275
			goto no;
276
		buf[n] = 0;
277
		if(getfields(buf, fields, 4, 1, " \t") < 3)
278
			goto no;
279
		strncpy(mxp->ip, fields[2], sizeof mxp->ip - 1);
280
		mxp->ip[sizeof mxp->ip - 1] = '\0';
281
		continue;
282
 
283
	no:
284
		/* remove mx[i] and go around again */
285
		nmx--;
286
		*mxp = mx[nmx];
287
		i--;
288
	}
289
	close(fd);
290
	return nmx;
291
}
292
 
293
static int
294
compar(void *a, void *b)
295
{
296
	return ((Mx*)a)->pref - ((Mx*)b)->pref;
297
}
298
 
299
/* break up an address to its component parts */
300
void
301
dial_string_parse(char *str, DS *ds)
302
{
303
	char *p, *p2;
304
 
305
	strncpy(ds->buf, str, sizeof(ds->buf));
306
	ds->buf[sizeof(ds->buf)-1] = 0;
307
 
308
	p = strchr(ds->buf, '!');
309
	if(p == 0) {
310
		ds->netdir = 0;
311
		ds->proto = "net";
312
		ds->host = ds->buf;
313
	} else {
314
		if(*ds->buf != '/'){
315
			ds->netdir = 0;
316
			ds->proto = ds->buf;
317
		} else {
318
			for(p2 = p; *p2 != '/'; p2--)
319
				;
320
			*p2++ = 0;
321
			ds->netdir = ds->buf;
322
			ds->proto = p2;
323
		}
324
		*p = 0;
325
		ds->host = p + 1;
326
	}
327
	ds->service = strchr(ds->host, '!');
328
	if(ds->service)
329
		*ds->service++ = 0;
330
	if(*ds->host == '$')
331
		expand_meta(ds);
332
}
333
 
334
static void
335
expand_meta(DS *ds)
336
{
337
	char buf[128], cs[128], *net, *p;
338
	int fd, n;
339
 
340
	net = ds->netdir;
341
	if(!net)
342
		net = "/net";
343
 
344
	if(debug)
345
		fprint(2, "expanding %s!%s\n", net, ds->host);
346
	snprint(cs, sizeof(cs), "%s/cs", net);
347
	if((fd = open(cs, ORDWR)) == -1){
348
		if(debug)
349
			fprint(2, "open %s: %r\n", cs);
350
		syslog(0, "smtp", "cannot open %s: %r", cs);
351
		return;
352
	}
353
 
354
	snprint(buf, sizeof buf, "!ipinfo %s", ds->host+1);	// +1 to skip $
355
	if(write(fd, buf, strlen(buf)) <= 0){
356
		if(debug)
357
			fprint(2, "write %s: %r\n", cs);
358
		syslog(0, "smtp", "%s to %s - write failed: %r", buf, cs);
359
		close(fd);
360
		return;
361
	}
362
 
363
	seek(fd, 0, 0);
364
	if((n = read(fd, ds->expand, sizeof(ds->expand)-1)) < 0){
365
		if(debug)
366
			fprint(2, "read %s: %r\n", cs);
367
		syslog(0, "smtp", "%s - read failed: %r", cs);
368
		close(fd);
369
		return;
370
	}
371
	close(fd);
372
 
373
	ds->expand[n] = 0;
374
	if((p = strchr(ds->expand, '=')) == nil){
375
		if(debug)
376
			fprint(2, "response %s: %s\n", cs, ds->expand);
377
		syslog(0, "smtp", "%q from %s - bad response: %r", ds->expand, cs);
378
		return;
379
	}
380
	ds->host = p+1;
381
 
382
	/* take only first one returned (quasi-bug) */
383
	if((p = strchr(ds->host, ' ')) != nil)
384
		*p = 0;
385
}