Blob


1 #include "common.h"
2 #include <ndb.h>
3 #include "smtp.h" /* to publish dial_string_parse */
5 enum
6 {
7 Nmx= 16,
8 Maxstring= 256,
9 };
11 typedef struct Mx Mx;
12 struct Mx
13 {
14 char host[256];
15 char ip[24];
16 int pref;
17 };
18 static Mx mx[Nmx];
20 Ndb *db;
21 extern int debug;
23 static int mxlookup(DS*, char*);
24 static int mxlookup1(DS*, char*);
25 static int compar(void*, void*);
26 static int callmx(DS*, char*, char*);
27 static void expand_meta(DS *ds);
28 extern int cistrcmp(char*, char*);
30 int
31 mxdial(char *addr, char *ddomain, char *gdomain)
32 {
33 int fd;
34 DS ds;
35 char err[Errlen];
37 addr = netmkaddr(addr, 0, "smtp");
38 dial_string_parse(addr, &ds);
40 /* try connecting to destination or any of it's mail routers */
41 fd = callmx(&ds, addr, ddomain);
43 /* try our mail gateway */
44 rerrstr(err, sizeof(err));
45 if(fd < 0 && gdomain && strstr(err, "can't translate") != 0) {
46 fprint(2,"dialing %s\n",gdomain);
47 fd = dial(netmkaddr(gdomain, 0, "smtp"), 0, 0, 0);
48 }
50 return fd;
51 }
53 /*
54 * take an address and return all the mx entries for it,
55 * most preferred first
56 */
57 static int
58 callmx(DS *ds, char *dest, char *domain)
59 {
60 int fd, i, nmx;
61 char addr[Maxstring];
63 /* get a list of mx entries */
64 nmx = mxlookup(ds, domain);
65 if(nmx < 0){
66 /* dns isn't working, don't just dial */
67 return -1;
68 }
69 if(nmx == 0){
70 if(debug)
71 fprint(2, "mxlookup returns nothing\n");
72 return dial(dest, 0, 0, 0);
73 }
75 /* refuse to honor loopback addresses given by dns */
76 for(i = 0; i < nmx; i++){
77 if(strcmp(mx[i].ip, "127.0.0.1") == 0){
78 if(debug)
79 fprint(2, "mxlookup returns loopback\n");
80 werrstr("illegal: domain lists 127.0.0.1 as mail server");
81 return -1;
82 }
83 }
85 /* sort by preference */
86 if(nmx > 1)
87 qsort(mx, nmx, sizeof(Mx), compar);
89 /* dial each one in turn */
90 for(i = 0; i < nmx; i++){
91 snprint(addr, sizeof(addr), "%s/%s!%s!%s", ds->netdir, ds->proto,
92 mx[i].host, ds->service);
93 if(debug)
94 fprint(2, "mxdial trying %s\n", addr);
95 fd = dial(addr, 0, 0, 0);
96 if(fd >= 0)
97 return fd;
98 }
99 return -1;
102 /*
103 * call the dns process and have it try to resolve the mx request
105 * this routine knows about the firewall and tries inside and outside
106 * dns's seperately.
107 */
108 static int
109 mxlookup(DS *ds, char *domain)
111 int n;
113 /* just in case we find no domain name */
114 strcpy(domain, ds->host);
116 if(ds->netdir){
117 n = mxlookup1(ds, domain);
118 } else {
119 ds->netdir = "/net";
120 n = mxlookup1(ds, domain);
121 if(n == 0) {
122 ds->netdir = "/net.alt";
123 n = mxlookup1(ds, domain);
127 return n;
130 static int
131 mxlookup1(DS *ds, char *domain)
133 char buf[1024];
134 char dnsname[Maxstring];
135 char *fields[4];
136 int i, n, fd, nmx;
138 snprint(dnsname, sizeof dnsname, "%s/dns", ds->netdir);
140 fd = open(dnsname, ORDWR);
141 if(fd < 0)
142 return 0;
144 nmx = 0;
145 snprint(buf, sizeof(buf), "%s mx", ds->host);
146 if(debug)
147 fprint(2, "sending %s '%s'\n", dnsname, buf);
148 n = write(fd, buf, strlen(buf));
149 if(n < 0){
150 rerrstr(buf, sizeof buf);
151 if(debug)
152 fprint(2, "dns: %s\n", buf);
153 if(strstr(buf, "dns failure")){
154 /* if dns fails for the mx lookup, we have to stop */
155 close(fd);
156 return -1;
158 } else {
159 /*
160 * get any mx entries
161 */
162 seek(fd, 0, 0);
163 while(nmx < Nmx && (n = read(fd, buf, sizeof(buf)-1)) > 0){
164 buf[n] = 0;
165 if(debug)
166 fprint(2, "dns mx: %s\n", buf);
167 n = getfields(buf, fields, 4, 1, " \t");
168 if(n < 4)
169 continue;
171 if(strchr(domain, '.') == 0)
172 strcpy(domain, fields[0]);
174 strncpy(mx[nmx].host, fields[3], sizeof(mx[n].host)-1);
175 mx[nmx].pref = atoi(fields[2]);
176 nmx++;
178 if(debug)
179 fprint(2, "dns mx; got %d entries\n", nmx);
182 /*
183 * no mx record? try name itself.
184 */
185 /*
186 * BUG? If domain has no dots, then we used to look up ds->host
187 * but return domain instead of ds->host in the list. Now we return
188 * ds->host. What will this break?
189 */
190 if(nmx == 0){
191 mx[0].pref = 1;
192 strncpy(mx[0].host, ds->host, sizeof(mx[0].host));
193 nmx++;
196 /*
197 * look up all ip addresses
198 */
199 for(i = 0; i < nmx; i++){
200 seek(fd, 0, 0);
201 snprint(buf, sizeof buf, "%s ip", mx[i].host);
202 mx[i].ip[0] = 0;
203 if(write(fd, buf, strlen(buf)) < 0)
204 goto no;
205 seek(fd, 0, 0);
206 if((n = read(fd, buf, sizeof buf-1)) < 0)
207 goto no;
208 buf[n] = 0;
209 if(getfields(buf, fields, 4, 1, " \t") < 3)
210 goto no;
211 strncpy(mx[i].ip, fields[2], sizeof(mx[i].ip)-1);
212 continue;
214 no:
215 /* remove mx[i] and go around again */
216 nmx--;
217 mx[i] = mx[nmx];
218 i--;
220 return nmx;
223 static int
224 compar(void *a, void *b)
226 return ((Mx*)a)->pref - ((Mx*)b)->pref;
229 /* break up an address to its component parts */
230 void
231 dial_string_parse(char *str, DS *ds)
233 char *p, *p2;
235 strncpy(ds->buf, str, sizeof(ds->buf));
236 ds->buf[sizeof(ds->buf)-1] = 0;
238 p = strchr(ds->buf, '!');
239 if(p == 0) {
240 ds->netdir = 0;
241 ds->proto = "net";
242 ds->host = ds->buf;
243 } else {
244 if(*ds->buf != '/'){
245 ds->netdir = 0;
246 ds->proto = ds->buf;
247 } else {
248 for(p2 = p; *p2 != '/'; p2--)
250 *p2++ = 0;
251 ds->netdir = ds->buf;
252 ds->proto = p2;
254 *p = 0;
255 ds->host = p + 1;
257 ds->service = strchr(ds->host, '!');
258 if(ds->service)
259 *ds->service++ = 0;
260 if(*ds->host == '$')
261 expand_meta(ds);
264 #if 0 /* jpc */
265 static void
266 expand_meta(DS *ds)
268 char buf[128], cs[128], *net, *p;
269 int fd, n;
271 net = ds->netdir;
272 if(!net)
273 net = "/net";
275 if(debug)
276 fprint(2, "expanding %s!%s\n", net, ds->host);
277 snprint(cs, sizeof(cs), "%s/cs", net);
278 if((fd = open(cs, ORDWR)) == -1){
279 if(debug)
280 fprint(2, "open %s: %r\n", cs);
281 syslog(0, "smtp", "cannot open %s: %r", cs);
282 return;
285 snprint(buf, sizeof(buf), "!ipinfo %s", ds->host+1); // +1 to skip $
286 if(write(fd, buf, strlen(buf)) <= 0){
287 if(debug)
288 fprint(2, "write %s: %r\n", cs);
289 syslog(0, "smtp", "%s to %s - write failed: %r", buf, cs);
290 close(fd);
291 return;
294 seek(fd, 0, 0);
295 if((n = read(fd, ds->expand, sizeof(ds->expand)-1)) < 0){
296 if(debug)
297 fprint(2, "read %s: %r\n", cs);
298 syslog(0, "smtp", "%s - read failed: %r", cs);
299 close(fd);
300 return;
302 close(fd);
304 ds->expand[n] = 0;
305 if((p = strchr(ds->expand, '=')) == nil){
306 if(debug)
307 fprint(2, "response %s: %s\n", cs, ds->expand);
308 syslog(0, "smtp", "%q from %s - bad response: %r", ds->expand, cs);
309 return;
311 ds->host = p+1;
313 /* take only first one returned (quasi-bug) */
314 if((p = strchr(ds->host, ' ')) != nil)
315 *p = 0;
317 #endif /* jpc */
319 static void
320 expand_meta(DS *ds)
322 Ndb *db;
323 Ndbs s;
324 char *sys, *smtpserver;
326 sys = sysname();
327 db = ndbopen(unsharp("#9/ndb/local"));
328 fprint(2,"%s",ds->host);
329 smtpserver = ndbgetvalue(db, &s, "sys", sys, "smtp", nil);
330 snprint(ds->host,128,"%s",smtpserver);
331 fprint(2," exanded to %s\n",ds->host);