Blob


1 #include "common.h"
2 #include <ndb.h>
3 #include "smtp.h" /* to publish dial_string_parse */
4 #include <ip.h>
6 enum
7 {
8 Nmx= 16,
9 Maxstring= 256,
10 };
12 typedef struct Mx Mx;
13 struct Mx
14 {
15 char host[256];
16 char ip[24];
17 int pref;
18 };
19 static Mx mx[Nmx];
21 Ndb *db;
22 extern int debug;
24 static int mxlookup(DS*, char*);
25 static int compar(const void*, const 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 fd = dial(netmkaddr(gdomain, 0, "smtp"), 0, 0, 0);
48 return fd;
49 }
51 static int
52 timeout(void *v, char *msg)
53 {
54 USED(v);
56 if(strstr(msg, "alarm"))
57 return 1;
58 return 0;
59 }
61 /*
62 * take an address and return all the mx entries for it,
63 * most preferred first
64 */
65 static int
66 callmx(DS *ds, char *dest, char *domain)
67 {
68 int fd, i, nmx;
69 char addr[Maxstring];
71 /* get a list of mx entries */
72 nmx = mxlookup(ds, domain);
73 if(nmx < 0){
74 /* dns isn't working, don't just dial */
75 return -1;
76 }
77 if(nmx == 0){
78 if(debug)
79 fprint(2, "mxlookup returns nothing\n");
80 return dial(dest, 0, 0, 0);
81 }
83 /* refuse to honor loopback addresses given by dns */
84 for(i = 0; i < nmx; i++){
85 if(strcmp(mx[i].ip, "127.0.0.1") == 0){
86 if(debug)
87 fprint(2, "mxlookup returns loopback\n");
88 werrstr("illegal: domain lists 127.0.0.1 as mail server");
89 return -1;
90 }
91 }
93 /* sort by preference */
94 if(nmx > 1)
95 qsort(mx, nmx, sizeof(Mx), compar);
97 if(debug){
98 for(i=0; i<nmx; i++)
99 print("%s %d\n", mx[i].host, mx[i].pref);
101 /* dial each one in turn */
102 for(i = 0; i < nmx; i++){
103 snprint(addr, sizeof(addr), "%s/%s!%s!%s", ds->netdir, ds->proto,
104 mx[i].host, ds->service);
105 if(debug)
106 fprint(2, "mxdial trying %s (%d)\n", addr, i);
107 atnotify(timeout, 1);
108 alarm(10*1000);
109 fd = dial(addr, 0, 0, 0);
110 alarm(0);
111 atnotify(timeout, 0);
112 if(fd >= 0)
113 return fd;
115 return -1;
118 /*
119 * use dns to resolve the mx request
120 */
121 static int
122 mxlookup(DS *ds, char *domain)
124 int i, n, nmx;
125 Ndbtuple *t, *tmx, *tpref, *tip;
127 ds->netdir = "/net";
128 nmx = 0;
129 if((t = dnsquery(nil, ds->host, "mx")) != nil){
130 for(tmx=t; (tmx=ndbfindattr(tmx->entry, nil, "mx")) != nil && nmx<Nmx; ){
131 for(tpref=tmx->line; tpref != tmx; tpref=tpref->line){
132 if(strcmp(tpref->attr, "pref") == 0){
133 strncpy(mx[nmx].host, tmx->val, sizeof(mx[n].host)-1);
134 mx[nmx].pref = atoi(tpref->val);
135 nmx++;
136 break;
140 ndbfree(t);
143 /*
144 * no mx record? try name itself.
145 */
146 /*
147 * BUG? If domain has no dots, then we used to look up ds->host
148 * but return domain instead of ds->host in the list. Now we return
149 * ds->host. What will this break?
150 */
151 if(nmx == 0){
152 mx[0].pref = 1;
153 strncpy(mx[0].host, ds->host, sizeof(mx[0].host));
154 nmx++;
157 /*
158 * look up all ip addresses
159 */
160 for(i = 0; i < nmx; i++){
161 if((t = dnsquery(nil, mx[i].host, "ip")) == nil)
162 goto no;
163 if((tip = ndbfindattr(t, nil, "ip")) == nil){
164 ndbfree(t);
165 goto no;
167 strncpy(mx[i].ip, tip->val, sizeof(mx[i].ip)-1);
168 ndbfree(t);
169 continue;
171 no:
172 /* remove mx[i] and go around again */
173 nmx--;
174 mx[i] = mx[nmx];
175 i--;
177 return nmx;
180 static int
181 compar(const void *a, const void *b)
183 return ((Mx*)a)->pref - ((Mx*)b)->pref;
186 /* break up an address to its component parts */
187 void
188 dial_string_parse(char *str, DS *ds)
190 char *p, *p2;
192 strncpy(ds->buf, str, sizeof(ds->buf));
193 ds->buf[sizeof(ds->buf)-1] = 0;
195 p = strchr(ds->buf, '!');
196 if(p == 0) {
197 ds->netdir = 0;
198 ds->proto = "net";
199 ds->host = ds->buf;
200 } else {
201 if(*ds->buf != '/'){
202 ds->netdir = 0;
203 ds->proto = ds->buf;
204 } else {
205 for(p2 = p; *p2 != '/'; p2--)
207 *p2++ = 0;
208 ds->netdir = ds->buf;
209 ds->proto = p2;
211 *p = 0;
212 ds->host = p + 1;
214 ds->service = strchr(ds->host, '!');
215 if(ds->service)
216 *ds->service++ = 0;
217 if(*ds->host == '$')
218 expand_meta(ds);
221 static void
222 expand_meta(DS *ds)
224 static Ndb *db;
225 Ndbs s;
226 char *sys, *smtpserver;
228 /* can't ask cs, so query database directly. */
229 sys = sysname();
230 if(db == nil)
231 db = ndbopen(0);
232 smtpserver = ndbgetvalue(db, &s, "sys", sys, "smtp", nil);
233 snprint(ds->host, 128, "%s", smtpserver);