mxdial.c 6.8 KB

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