mxdial.c 6.4 KB

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