xref: /plan9/sys/src/cmd/upas/smtp/mxdial.c (revision 7874c72cdf15e7bba44575d64a95358fcedce352)
17dd7cddfSDavid du Colombier #include "common.h"
23e12c5d1SDavid du Colombier #include <ndb.h>
39863c128SDavid du Colombier #include <smtp.h>	/* to publish dial_string_parse */
43e12c5d1SDavid du Colombier 
53e12c5d1SDavid du Colombier enum
63e12c5d1SDavid du Colombier {
73e12c5d1SDavid du Colombier 	Nmx=		16,
87dd7cddfSDavid du Colombier 	Maxstring=	256,
9ab6a5be1SDavid du Colombier 	Maxipstr=	8*5,		/* ipv6 */
103e12c5d1SDavid du Colombier };
113e12c5d1SDavid du Colombier 
123e12c5d1SDavid du Colombier typedef struct Mx	Mx;
133e12c5d1SDavid du Colombier struct Mx
143e12c5d1SDavid du Colombier {
15ab6a5be1SDavid du Colombier 	char	host[Maxstring];
1628684b1dSDavid du Colombier 	char	ip[Maxipstr];		/* this is just the first ip */
173e12c5d1SDavid du Colombier 	int	pref;
183e12c5d1SDavid du Colombier };
193e12c5d1SDavid du Colombier 
20617c0e1eSDavid du Colombier char	*bustedmxs[Maxbustedmx];
217dd7cddfSDavid du Colombier Ndb *db;
227dd7cddfSDavid du Colombier 
23ab6a5be1SDavid du Colombier static Mx mx[Nmx];
24ab6a5be1SDavid du Colombier 
25ab6a5be1SDavid du Colombier static int	callmx(DS*, char*, char*);
26ab6a5be1SDavid du Colombier static int	compar(void*, void*);
27ab6a5be1SDavid du Colombier static void	expand_meta(DS *ds);
287dd7cddfSDavid du Colombier static int	mxlookup(DS*, char*);
297dd7cddfSDavid du Colombier static int	mxlookup1(DS*, char*);
307dd7cddfSDavid du Colombier 
317dd7cddfSDavid du Colombier int
mxdial(char * addr,char * ddomain,char * gdomain)327dd7cddfSDavid du Colombier mxdial(char *addr, char *ddomain, char *gdomain)
337dd7cddfSDavid du Colombier {
347dd7cddfSDavid du Colombier 	int fd;
357dd7cddfSDavid du Colombier 	DS ds;
369a747e4fSDavid du Colombier 	char err[Errlen];
377dd7cddfSDavid du Colombier 
387dd7cddfSDavid du Colombier 	addr = netmkaddr(addr, 0, "smtp");
397dd7cddfSDavid du Colombier 	dial_string_parse(addr, &ds);
407dd7cddfSDavid du Colombier 
417dd7cddfSDavid du Colombier 	/* try connecting to destination or any of it's mail routers */
427dd7cddfSDavid du Colombier 	fd = callmx(&ds, addr, ddomain);
437dd7cddfSDavid du Colombier 
447dd7cddfSDavid du Colombier 	/* try our mail gateway */
459a747e4fSDavid du Colombier 	rerrstr(err, sizeof(err));
467dd7cddfSDavid du Colombier 	if(fd < 0 && gdomain && strstr(err, "can't translate") != 0)
477dd7cddfSDavid du Colombier 		fd = dial(netmkaddr(gdomain, 0, "smtp"), 0, 0, 0);
487dd7cddfSDavid du Colombier 
497dd7cddfSDavid du Colombier 	return fd;
507dd7cddfSDavid du Colombier }
517dd7cddfSDavid du Colombier 
52ab3dc52fSDavid du Colombier static int
busted(char * mx)53617c0e1eSDavid du Colombier busted(char *mx)
54617c0e1eSDavid du Colombier {
55617c0e1eSDavid du Colombier 	char **bmp;
56617c0e1eSDavid du Colombier 
57617c0e1eSDavid du Colombier 	for (bmp = bustedmxs; *bmp != nil; bmp++)
58617c0e1eSDavid du Colombier 		if (strcmp(mx, *bmp) == 0)
59617c0e1eSDavid du Colombier 			return 1;
60617c0e1eSDavid du Colombier 	return 0;
61617c0e1eSDavid du Colombier }
62617c0e1eSDavid du Colombier 
63617c0e1eSDavid du Colombier static int
timeout(void *,char * msg)644de34a7eSDavid du Colombier timeout(void*, char *msg)
65ab3dc52fSDavid du Colombier {
66ab3dc52fSDavid du Colombier 	if(strstr(msg, "alarm"))
67ab3dc52fSDavid du Colombier 		return 1;
68ab3dc52fSDavid du Colombier 	return 0;
69ab3dc52fSDavid du Colombier }
70ab3dc52fSDavid du Colombier 
71254fe3d3SDavid du Colombier long
timedwrite(int fd,void * buf,long len,long ms)72254fe3d3SDavid du Colombier timedwrite(int fd, void *buf, long len, long ms)
73254fe3d3SDavid du Colombier {
74254fe3d3SDavid du Colombier 	long n, oalarm;
75254fe3d3SDavid du Colombier 
76254fe3d3SDavid du Colombier 	atnotify(timeout, 1);
77254fe3d3SDavid du Colombier 	oalarm = alarm(ms);
78254fe3d3SDavid du Colombier 	n = write(fd, buf, len);
79254fe3d3SDavid du Colombier 	alarm(oalarm);
80254fe3d3SDavid du Colombier 	atnotify(timeout, 0);
81254fe3d3SDavid du Colombier 	return n;
82254fe3d3SDavid du Colombier }
83254fe3d3SDavid du Colombier 
84ab6a5be1SDavid du Colombier static int
isloopback(char * ip)85ab6a5be1SDavid du Colombier isloopback(char *ip)
86ab6a5be1SDavid du Colombier {
87ab6a5be1SDavid du Colombier 	return strcmp(ip, "127.0.0.1") == 0 || strcmp(ip, "::1") == 0;
88ab6a5be1SDavid du Colombier }
89ab6a5be1SDavid du Colombier 
903e12c5d1SDavid du Colombier /*
913e12c5d1SDavid du Colombier  *  take an address and return all the mx entries for it,
923e12c5d1SDavid du Colombier  *  most preferred first
933e12c5d1SDavid du Colombier  */
947dd7cddfSDavid du Colombier static int
callmx(DS * ds,char * dest,char * domain)957dd7cddfSDavid du Colombier callmx(DS *ds, char *dest, char *domain)
963e12c5d1SDavid du Colombier {
973e12c5d1SDavid du Colombier 	int fd, i, nmx;
98ab6a5be1SDavid du Colombier 	char *ip;
997dd7cddfSDavid du Colombier 	char addr[Maxstring];
1003e12c5d1SDavid du Colombier 
1013e12c5d1SDavid du Colombier 	/* get a list of mx entries */
1027dd7cddfSDavid du Colombier 	nmx = mxlookup(ds, domain);
103dc5a79c1SDavid du Colombier 	if(nmx < 0){
104dc5a79c1SDavid du Colombier 		/* dns isn't working, don't just dial */
105dc5a79c1SDavid du Colombier 		return -1;
106dc5a79c1SDavid du Colombier 	}
1073e12c5d1SDavid du Colombier 	if(nmx == 0){
108219b2ee8SDavid du Colombier 		if(debug)
109219b2ee8SDavid du Colombier 			fprint(2, "mxlookup returns nothing\n");
1103e12c5d1SDavid du Colombier 		return dial(dest, 0, 0, 0);
1113e12c5d1SDavid du Colombier 	}
1123e12c5d1SDavid du Colombier 
113ab6a5be1SDavid du Colombier 	/* refuse to honor loopback addresses given by dns.  catch \n too. */
114ab6a5be1SDavid du Colombier 	for(i = 0; i < nmx; i++) {
115ab6a5be1SDavid du Colombier 		ip = mx[i].ip;
116ab6a5be1SDavid du Colombier 		if(strchr(ip, '\n') != nil){
117ab6a5be1SDavid du Colombier 			if(debug)
118ab6a5be1SDavid du Colombier 				fprint(2, "mxlookup ip contains newline\n");
119ab6a5be1SDavid du Colombier 			werrstr("illegal: newline in mail server ip");
120ab6a5be1SDavid du Colombier 			return -1;
121ab6a5be1SDavid du Colombier 		}else if(isloopback(ip)){
1222b7fd5adSDavid du Colombier 			if(debug)
1232b7fd5adSDavid du Colombier 				fprint(2, "mxlookup returns loopback\n");
124ab6a5be1SDavid du Colombier 			werrstr("illegal: domain lists %s as mail server", ip);
1252b7fd5adSDavid du Colombier 			return -1;
1262b7fd5adSDavid du Colombier 		}
127ab6a5be1SDavid du Colombier 	}
1282b7fd5adSDavid du Colombier 
1293e12c5d1SDavid du Colombier 	/* sort by preference */
1303e12c5d1SDavid du Colombier 	if(nmx > 1)
1313e12c5d1SDavid du Colombier 		qsort(mx, nmx, sizeof(Mx), compar);
1323e12c5d1SDavid du Colombier 
13328684b1dSDavid du Colombier 	/* dial each one in turn by name, not ip */
1343e12c5d1SDavid du Colombier 	for(i = 0; i < nmx; i++){
135617c0e1eSDavid du Colombier 		if (busted(mx[i].host)) {
136617c0e1eSDavid du Colombier 			if (debug)
137617c0e1eSDavid du Colombier 				fprint(2, "mxdial skipping busted mx %s\n",
138617c0e1eSDavid du Colombier 					mx[i].host);
139617c0e1eSDavid du Colombier 			continue;
140617c0e1eSDavid du Colombier 		}
1417dd7cddfSDavid du Colombier 		snprint(addr, sizeof(addr), "%s/%s!%s!%s", ds->netdir, ds->proto,
1427dd7cddfSDavid du Colombier 			mx[i].host, ds->service);
143219b2ee8SDavid du Colombier 		if(debug)
1447dd7cddfSDavid du Colombier 			fprint(2, "mxdial trying %s\n", addr);
145ab3dc52fSDavid du Colombier 		atnotify(timeout, 1);
146*7874c72cSDavid du Colombier 		/* this was 10 seconds, but oclsc.org at least needs more. */
147*7874c72cSDavid du Colombier 		alarm(60*1000);
1487dd7cddfSDavid du Colombier 		fd = dial(addr, 0, 0, 0);
14928684b1dSDavid du Colombier 		if (debug && fd < 0)
15028684b1dSDavid du Colombier 			fprint(2, "dial: %r\n");
151ab3dc52fSDavid du Colombier 		alarm(0);
152ab3dc52fSDavid du Colombier 		atnotify(timeout, 0);
1537dd7cddfSDavid du Colombier 		if(fd >= 0)
1543e12c5d1SDavid du Colombier 			return fd;
1553e12c5d1SDavid du Colombier 	}
1563e12c5d1SDavid du Colombier 	return -1;
1573e12c5d1SDavid du Colombier }
1583e12c5d1SDavid du Colombier 
1593e12c5d1SDavid du Colombier /*
1603e12c5d1SDavid du Colombier  *  call the dns process and have it try to resolve the mx request
1617dd7cddfSDavid du Colombier  *
1627dd7cddfSDavid du Colombier  *  this routine knows about the firewall and tries inside and outside
1637dd7cddfSDavid du Colombier  *  dns's seperately.
1643e12c5d1SDavid du Colombier  */
1653e12c5d1SDavid du Colombier static int
mxlookup(DS * ds,char * domain)1667dd7cddfSDavid du Colombier mxlookup(DS *ds, char *domain)
1673e12c5d1SDavid du Colombier {
1687dd7cddfSDavid du Colombier 	int n;
1693e12c5d1SDavid du Colombier 
1707dd7cddfSDavid du Colombier 	/* just in case we find no domain name */
1717dd7cddfSDavid du Colombier 	strcpy(domain, ds->host);
1727dd7cddfSDavid du Colombier 
173ccf6439bSDavid du Colombier 	if(ds->netdir)
1747dd7cddfSDavid du Colombier 		n = mxlookup1(ds, domain);
175ccf6439bSDavid du Colombier 	else {
1767dd7cddfSDavid du Colombier 		ds->netdir = "/net";
1777dd7cddfSDavid du Colombier 		n = mxlookup1(ds, domain);
178dc5a79c1SDavid du Colombier 		if(n == 0) {
1797dd7cddfSDavid du Colombier 			ds->netdir = "/net.alt";
1807dd7cddfSDavid du Colombier 			n = mxlookup1(ds, domain);
181219b2ee8SDavid du Colombier 		}
1827dd7cddfSDavid du Colombier 	}
1837dd7cddfSDavid du Colombier 
1847dd7cddfSDavid du Colombier 	return n;
1857dd7cddfSDavid du Colombier }
1867dd7cddfSDavid du Colombier 
1877dd7cddfSDavid du Colombier static int
mxlookup1(DS * ds,char * domain)1887dd7cddfSDavid du Colombier mxlookup1(DS *ds, char *domain)
1897dd7cddfSDavid du Colombier {
1902b7fd5adSDavid du Colombier 	int i, n, fd, nmx;
191ab6a5be1SDavid du Colombier 	char buf[Maxdomain], dnsname[Maxstring];
192ccf6439bSDavid du Colombier 	char *fields[4];
193ab6a5be1SDavid du Colombier 	Mx *mxp;
1947dd7cddfSDavid du Colombier 
1957dd7cddfSDavid du Colombier 	snprint(dnsname, sizeof dnsname, "%s/dns", ds->netdir);
1967dd7cddfSDavid du Colombier 
1977dd7cddfSDavid du Colombier 	fd = open(dnsname, ORDWR);
1983e12c5d1SDavid du Colombier 	if(fd < 0)
1993e12c5d1SDavid du Colombier 		return 0;
2003e12c5d1SDavid du Colombier 
2013e12c5d1SDavid du Colombier 	nmx = 0;
202ccf6439bSDavid du Colombier 	snprint(buf, sizeof buf, "%s mx", ds->host);
203219b2ee8SDavid du Colombier 	if(debug)
2047dd7cddfSDavid du Colombier 		fprint(2, "sending %s '%s'\n", dnsname, buf);
205ccf6439bSDavid du Colombier 	/*
206ccf6439bSDavid du Colombier 	 * don't hang indefinitely in the write to /net/dns.
207ccf6439bSDavid du Colombier 	 */
208254fe3d3SDavid du Colombier 	n = timedwrite(fd, buf, strlen(buf), 60*1000);
209dc5a79c1SDavid du Colombier 	if(n < 0){
210dc5a79c1SDavid du Colombier 		rerrstr(buf, sizeof buf);
2112b7fd5adSDavid du Colombier 		if(debug)
2122b7fd5adSDavid du Colombier 			fprint(2, "dns: %s\n", buf);
213dc5a79c1SDavid du Colombier 		if(strstr(buf, "dns failure")){
214dc5a79c1SDavid du Colombier 			/* if dns fails for the mx lookup, we have to stop */
215dc5a79c1SDavid du Colombier 			close(fd);
216dc5a79c1SDavid du Colombier 			return -1;
217dc5a79c1SDavid du Colombier 		}
218dc5a79c1SDavid du Colombier 	} else {
2197dd7cddfSDavid du Colombier 		/*
2207dd7cddfSDavid du Colombier 		 *  get any mx entries
22128684b1dSDavid du Colombier 		 *  assumes one record per read
2227dd7cddfSDavid du Colombier 		 */
2233e12c5d1SDavid du Colombier 		seek(fd, 0, 0);
224ccf6439bSDavid du Colombier 		while(nmx < Nmx && (n = read(fd, buf, sizeof buf-1)) > 0){
225ab6a5be1SDavid du Colombier 			mxp = &mx[nmx];
2263e12c5d1SDavid du Colombier 			buf[n] = 0;
2272b7fd5adSDavid du Colombier 			if(debug)
2282b7fd5adSDavid du Colombier 				fprint(2, "dns mx: %s\n", buf);
2297dd7cddfSDavid du Colombier 			n = getfields(buf, fields, 4, 1, " \t");
2307dd7cddfSDavid du Colombier 			if(n < 4)
2313e12c5d1SDavid du Colombier 				continue;
2327dd7cddfSDavid du Colombier 
2337dd7cddfSDavid du Colombier 			if(strchr(domain, '.') == 0)
2347dd7cddfSDavid du Colombier 				strcpy(domain, fields[0]);
2357dd7cddfSDavid du Colombier 
236ab6a5be1SDavid du Colombier 			strncpy(mxp->host, fields[3], sizeof mxp->host - 1);
237ab6a5be1SDavid du Colombier 			mxp->host[sizeof mxp->host - 1] = '\0';
238ab6a5be1SDavid du Colombier 			mxp->pref = atoi(fields[2]);
2393e12c5d1SDavid du Colombier 			nmx++;
2403e12c5d1SDavid du Colombier 		}
2412b7fd5adSDavid du Colombier 		if(debug)
24228684b1dSDavid du Colombier 			fprint(2, "dns mx: got %d mx servers\n", nmx);
2433e12c5d1SDavid du Colombier 	}
2447dd7cddfSDavid du Colombier 
2457dd7cddfSDavid du Colombier 	/*
2462b7fd5adSDavid du Colombier 	 * no mx record? try name itself.
24728684b1dSDavid du Colombier 	 *
2482b7fd5adSDavid du Colombier 	 * BUG? If domain has no dots, then we used to look up ds->host
2492b7fd5adSDavid du Colombier 	 * but return domain instead of ds->host in the list.  Now we return
2502b7fd5adSDavid du Colombier 	 * ds->host.  What will this break?
2512b7fd5adSDavid du Colombier 	 */
2522b7fd5adSDavid du Colombier 	if(nmx == 0){
2532b7fd5adSDavid du Colombier 		mx[0].pref = 1;
2542b7fd5adSDavid du Colombier 		strncpy(mx[0].host, ds->host, sizeof(mx[0].host));
2557dd7cddfSDavid du Colombier 		nmx++;
2567dd7cddfSDavid du Colombier 	}
2577dd7cddfSDavid du Colombier 
2582b7fd5adSDavid du Colombier 	/*
25928684b1dSDavid du Colombier 	 * look up first ip address of each mx name.
26028684b1dSDavid du Colombier 	 * should really look at all addresses.
26128684b1dSDavid du Colombier 	 * assumes one record per read.
2622b7fd5adSDavid du Colombier 	 */
2632b7fd5adSDavid du Colombier 	for(i = 0; i < nmx; i++){
264ab6a5be1SDavid du Colombier 		mxp = &mx[i];
2652b7fd5adSDavid du Colombier 		seek(fd, 0, 0);
266ab6a5be1SDavid du Colombier 		snprint(buf, sizeof buf, "%s ip", mxp->host);
267ab6a5be1SDavid du Colombier 		mxp->ip[0] = 0;
268254fe3d3SDavid du Colombier 		/*
269254fe3d3SDavid du Colombier 		 * don't hang indefinitely in the write to /net/dns.
270254fe3d3SDavid du Colombier 		 */
271254fe3d3SDavid du Colombier 		if(timedwrite(fd, buf, strlen(buf), 60*1000) < 0)
2722b7fd5adSDavid du Colombier 			goto no;
2732b7fd5adSDavid du Colombier 		seek(fd, 0, 0);
2742b7fd5adSDavid du Colombier 		if((n = read(fd, buf, sizeof buf-1)) < 0)
2752b7fd5adSDavid du Colombier 			goto no;
2762b7fd5adSDavid du Colombier 		buf[n] = 0;
2772b7fd5adSDavid du Colombier 		if(getfields(buf, fields, 4, 1, " \t") < 3)
2782b7fd5adSDavid du Colombier 			goto no;
279ab6a5be1SDavid du Colombier 		strncpy(mxp->ip, fields[2], sizeof mxp->ip - 1);
280ab6a5be1SDavid du Colombier 		mxp->ip[sizeof mxp->ip - 1] = '\0';
2812b7fd5adSDavid du Colombier 		continue;
2822b7fd5adSDavid du Colombier 
2832b7fd5adSDavid du Colombier 	no:
2842b7fd5adSDavid du Colombier 		/* remove mx[i] and go around again */
2852b7fd5adSDavid du Colombier 		nmx--;
286ab6a5be1SDavid du Colombier 		*mxp = mx[nmx];
2872b7fd5adSDavid du Colombier 		i--;
2882b7fd5adSDavid du Colombier 	}
28928684b1dSDavid du Colombier 	close(fd);
2903e12c5d1SDavid du Colombier 	return nmx;
2913e12c5d1SDavid du Colombier }
2923e12c5d1SDavid du Colombier 
2933e12c5d1SDavid du Colombier static int
compar(void * a,void * b)2943e12c5d1SDavid du Colombier compar(void *a, void *b)
2953e12c5d1SDavid du Colombier {
2963e12c5d1SDavid du Colombier 	return ((Mx*)a)->pref - ((Mx*)b)->pref;
2973e12c5d1SDavid du Colombier }
2987dd7cddfSDavid du Colombier 
2997dd7cddfSDavid du Colombier /* break up an address to its component parts */
3009863c128SDavid du Colombier void
dial_string_parse(char * str,DS * ds)3017dd7cddfSDavid du Colombier dial_string_parse(char *str, DS *ds)
3027dd7cddfSDavid du Colombier {
3037dd7cddfSDavid du Colombier 	char *p, *p2;
3047dd7cddfSDavid du Colombier 
3057dd7cddfSDavid du Colombier 	strncpy(ds->buf, str, sizeof(ds->buf));
3067dd7cddfSDavid du Colombier 	ds->buf[sizeof(ds->buf)-1] = 0;
3077dd7cddfSDavid du Colombier 
3087dd7cddfSDavid du Colombier 	p = strchr(ds->buf, '!');
3097dd7cddfSDavid du Colombier 	if(p == 0) {
3107dd7cddfSDavid du Colombier 		ds->netdir = 0;
3117dd7cddfSDavid du Colombier 		ds->proto = "net";
3127dd7cddfSDavid du Colombier 		ds->host = ds->buf;
3137dd7cddfSDavid du Colombier 	} else {
3147dd7cddfSDavid du Colombier 		if(*ds->buf != '/'){
3157dd7cddfSDavid du Colombier 			ds->netdir = 0;
3167dd7cddfSDavid du Colombier 			ds->proto = ds->buf;
3177dd7cddfSDavid du Colombier 		} else {
3187dd7cddfSDavid du Colombier 			for(p2 = p; *p2 != '/'; p2--)
3197dd7cddfSDavid du Colombier 				;
3207dd7cddfSDavid du Colombier 			*p2++ = 0;
3217dd7cddfSDavid du Colombier 			ds->netdir = ds->buf;
3227dd7cddfSDavid du Colombier 			ds->proto = p2;
3237dd7cddfSDavid du Colombier 		}
3247dd7cddfSDavid du Colombier 		*p = 0;
3257dd7cddfSDavid du Colombier 		ds->host = p + 1;
3267dd7cddfSDavid du Colombier 	}
3277dd7cddfSDavid du Colombier 	ds->service = strchr(ds->host, '!');
3287dd7cddfSDavid du Colombier 	if(ds->service)
3297dd7cddfSDavid du Colombier 		*ds->service++ = 0;
3309863c128SDavid du Colombier 	if(*ds->host == '$')
3319863c128SDavid du Colombier 		expand_meta(ds);
3327dd7cddfSDavid du Colombier }
3333cf081f0SDavid du Colombier 
3349863c128SDavid du Colombier static void
expand_meta(DS * ds)3359863c128SDavid du Colombier expand_meta(DS *ds)
3363cf081f0SDavid du Colombier {
3379863c128SDavid du Colombier 	char buf[128], cs[128], *net, *p;
3383cf081f0SDavid du Colombier 	int fd, n;
3393cf081f0SDavid du Colombier 
3409863c128SDavid du Colombier 	net = ds->netdir;
3419863c128SDavid du Colombier 	if(!net)
3429863c128SDavid du Colombier 		net = "/net";
3433cf081f0SDavid du Colombier 
3449863c128SDavid du Colombier 	if(debug)
3459863c128SDavid du Colombier 		fprint(2, "expanding %s!%s\n", net, ds->host);
3469863c128SDavid du Colombier 	snprint(cs, sizeof(cs), "%s/cs", net);
3473cf081f0SDavid du Colombier 	if((fd = open(cs, ORDWR)) == -1){
3489863c128SDavid du Colombier 		if(debug)
3499863c128SDavid du Colombier 			fprint(2, "open %s: %r\n", cs);
3509863c128SDavid du Colombier 		syslog(0, "smtp", "cannot open %s: %r", cs);
3519863c128SDavid du Colombier 		return;
3523cf081f0SDavid du Colombier 	}
3533cf081f0SDavid du Colombier 
354ccf6439bSDavid du Colombier 	snprint(buf, sizeof buf, "!ipinfo %s", ds->host+1);	// +1 to skip $
3553cf081f0SDavid du Colombier 	if(write(fd, buf, strlen(buf)) <= 0){
3569863c128SDavid du Colombier 		if(debug)
3579863c128SDavid du Colombier 			fprint(2, "write %s: %r\n", cs);
3583cf081f0SDavid du Colombier 		syslog(0, "smtp", "%s to %s - write failed: %r", buf, cs);
3593cf081f0SDavid du Colombier 		close(fd);
3609863c128SDavid du Colombier 		return;
3613cf081f0SDavid du Colombier 	}
3623cf081f0SDavid du Colombier 
3633cf081f0SDavid du Colombier 	seek(fd, 0, 0);
3649863c128SDavid du Colombier 	if((n = read(fd, ds->expand, sizeof(ds->expand)-1)) < 0){
3659863c128SDavid du Colombier 		if(debug)
3669863c128SDavid du Colombier 			fprint(2, "read %s: %r\n", cs);
3673cf081f0SDavid du Colombier 		syslog(0, "smtp", "%s - read failed: %r", cs);
3683cf081f0SDavid du Colombier 		close(fd);
3699863c128SDavid du Colombier 		return;
3703cf081f0SDavid du Colombier 	}
3713cf081f0SDavid du Colombier 	close(fd);
3723cf081f0SDavid du Colombier 
3739863c128SDavid du Colombier 	ds->expand[n] = 0;
3749863c128SDavid du Colombier 	if((p = strchr(ds->expand, '=')) == nil){
3759863c128SDavid du Colombier 		if(debug)
3769863c128SDavid du Colombier 			fprint(2, "response %s: %s\n", cs, ds->expand);
3779863c128SDavid du Colombier 		syslog(0, "smtp", "%q from %s - bad response: %r", ds->expand, cs);
3789863c128SDavid du Colombier 		return;
3793cf081f0SDavid du Colombier 	}
3809863c128SDavid du Colombier 	ds->host = p+1;
3819863c128SDavid du Colombier 
3829863c128SDavid du Colombier 	/* take only first one returned (quasi-bug) */
3839863c128SDavid du Colombier 	if((p = strchr(ds->host, ' ')) != nil)
3849863c128SDavid du Colombier 		*p = 0;
3853cf081f0SDavid du Colombier }
386