3 #include "smtp.h" /* to publish dial_string_parse */
25 static int mxlookup(DS*, char*);
26 static int compar(const void*, const void*);
27 static int callmx(DS*, char*, char*);
28 static void expand_meta(DS *ds);
29 extern int cistrcmp(char*, char*);
31 /* Taken from imapdial, replaces tlsclient call with stunnel */
33 smtpdial(char *server)
42 fd[0] = dup(p[0], -1);
43 fd[1] = dup(p[0], -1);
46 tmp = smprint("%s:587", server);
47 fpath = searchpath("stunnel3");
49 werrstr("stunnel not found. it is required for tls support.");
52 if(threadspawnl(fd, fpath, "stunnel", "-n", "smtp" , "-c", "-r", tmp, nil) < 0) {
54 tmp = smprint("tcp!%s!587", server);
55 if(threadspawnl(fd, "/bin/tlsclient", "tlsclient", tmp, nil) < 0){
71 mxdial(char *addr, char *ddomain, char *gdomain)
77 addr = netmkaddr(addr, 0, "smtp");
78 dial_string_parse(addr, &ds);
80 /* try connecting to destination or any of it's mail routers */
81 fd = callmx(&ds, addr, ddomain);
83 /* try our mail gateway */
84 rerrstr(err, sizeof(err));
85 if(fd < 0 && gdomain && strstr(err, "can't translate") != 0)
86 fd = dial(netmkaddr(gdomain, 0, "smtp"), 0, 0, 0);
92 timeout(void *v, char *msg)
96 if(strstr(msg, "alarm"))
102 * take an address and return all the mx entries for it,
103 * most preferred first
106 callmx(DS *ds, char *dest, char *domain)
109 char addr[Maxstring];
111 /* get a list of mx entries */
112 nmx = mxlookup(ds, domain);
114 /* dns isn't working, don't just dial */
119 fprint(2, "mxlookup returns nothing\n");
120 return dial(dest, 0, 0, 0);
123 /* refuse to honor loopback addresses given by dns */
124 for(i = 0; i < nmx; i++){
125 if(strcmp(mx[i].ip, "127.0.0.1") == 0){
127 fprint(2, "mxlookup returns loopback\n");
128 werrstr("illegal: domain lists 127.0.0.1 as mail server");
133 /* sort by preference */
135 qsort(mx, nmx, sizeof(Mx), compar);
139 print("%s %d\n", mx[i].host, mx[i].pref);
141 /* dial each one in turn */
142 for(i = 0; i < nmx; i++){
144 snprint(addr, sizeof(addr), "%s", mx[i].host);
146 snprint(addr, sizeof(addr), "%s!%s!%s", ds->proto,
147 mx[i].host, ds->service);
150 fprint(2, "mxdial trying %s (%d)\n", addr, i);
151 atnotify(timeout, 1);
156 fd = dial(addr, 0, 0, 0);
159 atnotify(timeout, 0);
167 * use dns to resolve the mx request
170 mxlookup(DS *ds, char *domain)
173 Ndbtuple *t, *tmx, *tpref, *tip;
175 strcpy(domain, ds->host);
178 if((t = dnsquery(nil, ds->host, "mx")) != nil){
179 for(tmx=t; (tmx=ndbfindattr(tmx->entry, nil, "mx")) != nil && nmx<Nmx; ){
180 for(tpref=tmx->line; tpref != tmx; tpref=tpref->line){
181 if(strcmp(tpref->attr, "pref") == 0){
182 strncpy(mx[nmx].host, tmx->val, sizeof(mx[n].host)-1);
183 mx[nmx].pref = atoi(tpref->val);
193 * no mx record? try name itself.
196 * BUG? If domain has no dots, then we used to look up ds->host
197 * but return domain instead of ds->host in the list. Now we return
198 * ds->host. What will this break?
202 strncpy(mx[0].host, ds->host, sizeof(mx[0].host));
207 * look up all ip addresses
209 for(i = 0; i < nmx; i++){
210 if((t = dnsquery(nil, mx[i].host, "ip")) == nil)
212 if((tip = ndbfindattr(t, nil, "ip")) == nil){
216 strncpy(mx[i].ip, tip->val, sizeof(mx[i].ip)-1);
221 /* remove mx[i] and go around again */
230 compar(const void *a, const void *b)
232 return ((Mx*)a)->pref - ((Mx*)b)->pref;
235 /* break up an address to its component parts */
237 dial_string_parse(char *str, DS *ds)
241 strncpy(ds->buf, str, sizeof(ds->buf));
242 ds->buf[sizeof(ds->buf)-1] = 0;
244 p = strchr(ds->buf, '!');
254 for(p2 = p; *p2 != '/'; p2--)
257 ds->netdir = ds->buf;
263 ds->service = strchr(ds->host, '!');
275 char *sys, *smtpserver;
277 /* can't ask cs, so query database directly. */
281 smtpserver = ndbgetvalue(db, &s, "sys", sys, "smtp", nil);
282 snprint(ds->host, 128, "%s", smtpserver);