mxdial.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. #include "common.h"
  2. #include <ndb.h>
  3. enum
  4. {
  5. Nmx= 16,
  6. Maxstring= 256,
  7. };
  8. typedef struct Mx Mx;
  9. struct Mx
  10. {
  11. char host[256];
  12. char ip[24];
  13. int pref;
  14. };
  15. static Mx mx[Nmx];
  16. typedef struct DS DS;
  17. struct DS {
  18. /* dist string */
  19. char buf[128];
  20. char *netdir;
  21. char *proto;
  22. char *host;
  23. char *service;
  24. };
  25. Ndb *db;
  26. extern int debug;
  27. static int mxlookup(DS*, char*);
  28. static int mxlookup1(DS*, char*);
  29. static int compar(void*, void*);
  30. static int callmx(DS*, char*, char*);
  31. static void dial_string_parse(char*, DS*);
  32. extern int cistrcmp(char*, char*);
  33. int
  34. mxdial(char *addr, char *ddomain, char *gdomain)
  35. {
  36. int fd;
  37. DS ds;
  38. char err[Errlen];
  39. addr = netmkaddr(addr, 0, "smtp");
  40. dial_string_parse(addr, &ds);
  41. /* try connecting to destination or any of it's mail routers */
  42. 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);
  47. return fd;
  48. }
  49. /*
  50. * take an address and return all the mx entries for it,
  51. * most preferred first
  52. */
  53. static int
  54. callmx(DS *ds, char *dest, char *domain)
  55. {
  56. int fd, i, nmx;
  57. char addr[Maxstring];
  58. /* get a list of mx entries */
  59. nmx = mxlookup(ds, domain);
  60. if(nmx < 0){
  61. /* dns isn't working, don't just dial */
  62. return -1;
  63. }
  64. if(nmx == 0){
  65. if(debug)
  66. fprint(2, "mxlookup returns nothing\n");
  67. return dial(dest, 0, 0, 0);
  68. }
  69. /* refuse to honor loopback addresses given by dns */
  70. for(i = 0; i < nmx; i++){
  71. if(strcmp(mx[i].ip, "127.0.0.1") == 0){
  72. if(debug)
  73. fprint(2, "mxlookup returns loopback\n");
  74. werrstr("illegal: domain lists 127.0.0.1 as mail server");
  75. return -1;
  76. }
  77. }
  78. /* sort by preference */
  79. if(nmx > 1)
  80. qsort(mx, nmx, sizeof(Mx), compar);
  81. /* dial each one in turn */
  82. for(i = 0; i < nmx; i++){
  83. snprint(addr, sizeof(addr), "%s/%s!%s!%s", ds->netdir, ds->proto,
  84. mx[i].host, ds->service);
  85. if(debug)
  86. fprint(2, "mxdial trying %s\n", addr);
  87. fd = dial(addr, 0, 0, 0);
  88. if(fd >= 0)
  89. return fd;
  90. }
  91. return -1;
  92. }
  93. /*
  94. * call the dns process and have it try to resolve the mx request
  95. *
  96. * this routine knows about the firewall and tries inside and outside
  97. * dns's seperately.
  98. */
  99. static int
  100. mxlookup(DS *ds, char *domain)
  101. {
  102. int n;
  103. /* just in case we find no domain name */
  104. strcpy(domain, ds->host);
  105. if(ds->netdir){
  106. n = mxlookup1(ds, domain);
  107. } else {
  108. ds->netdir = "/net";
  109. n = mxlookup1(ds, domain);
  110. if(n == 0) {
  111. ds->netdir = "/net.alt";
  112. n = mxlookup1(ds, domain);
  113. }
  114. }
  115. return n;
  116. }
  117. static int
  118. mxlookup1(DS *ds, char *domain)
  119. {
  120. char buf[1024];
  121. char dnsname[Maxstring];
  122. char *fields[4];
  123. int i, n, fd, nmx;
  124. snprint(dnsname, sizeof dnsname, "%s/dns", ds->netdir);
  125. fd = open(dnsname, ORDWR);
  126. if(fd < 0)
  127. return 0;
  128. nmx = 0;
  129. snprint(buf, sizeof(buf), "%s mx", ds->host);
  130. if(debug)
  131. fprint(2, "sending %s '%s'\n", dnsname, buf);
  132. n = write(fd, buf, strlen(buf));
  133. if(n < 0){
  134. rerrstr(buf, sizeof buf);
  135. if(debug)
  136. fprint(2, "dns: %s\n", buf);
  137. if(strstr(buf, "dns failure")){
  138. /* if dns fails for the mx lookup, we have to stop */
  139. close(fd);
  140. return -1;
  141. }
  142. } else {
  143. /*
  144. * get any mx entries
  145. */
  146. seek(fd, 0, 0);
  147. while(nmx < Nmx && (n = read(fd, buf, sizeof(buf)-1)) > 0){
  148. buf[n] = 0;
  149. if(debug)
  150. fprint(2, "dns mx: %s\n", buf);
  151. n = getfields(buf, fields, 4, 1, " \t");
  152. if(n < 4)
  153. continue;
  154. if(strchr(domain, '.') == 0)
  155. strcpy(domain, fields[0]);
  156. strncpy(mx[nmx].host, fields[3], sizeof(mx[n].host)-1);
  157. mx[nmx].pref = atoi(fields[2]);
  158. nmx++;
  159. }
  160. if(debug)
  161. fprint(2, "dns mx; got %d entries\n", nmx);
  162. }
  163. /*
  164. * no mx record? try name itself.
  165. */
  166. /*
  167. * BUG? If domain has no dots, then we used to look up ds->host
  168. * but return domain instead of ds->host in the list. Now we return
  169. * ds->host. What will this break?
  170. */
  171. if(nmx == 0){
  172. mx[0].pref = 1;
  173. strncpy(mx[0].host, ds->host, sizeof(mx[0].host));
  174. nmx++;
  175. }
  176. /*
  177. * look up all ip addresses
  178. */
  179. for(i = 0; i < nmx; i++){
  180. seek(fd, 0, 0);
  181. snprint(buf, sizeof buf, "%s ip", mx[i].host);
  182. mx[i].ip[0] = 0;
  183. if(write(fd, buf, strlen(buf)) < 0)
  184. goto no;
  185. seek(fd, 0, 0);
  186. if((n = read(fd, buf, sizeof buf-1)) < 0)
  187. goto no;
  188. buf[n] = 0;
  189. if(getfields(buf, fields, 4, 1, " \t") < 3)
  190. goto no;
  191. strncpy(mx[i].ip, fields[2], sizeof(mx[i].ip)-1);
  192. continue;
  193. no:
  194. /* remove mx[i] and go around again */
  195. nmx--;
  196. mx[i] = mx[nmx];
  197. i--;
  198. }
  199. return nmx;
  200. }
  201. static int
  202. compar(void *a, void *b)
  203. {
  204. return ((Mx*)a)->pref - ((Mx*)b)->pref;
  205. }
  206. /* break up an address to its component parts */
  207. static void
  208. dial_string_parse(char *str, DS *ds)
  209. {
  210. char *p, *p2;
  211. strncpy(ds->buf, str, sizeof(ds->buf));
  212. ds->buf[sizeof(ds->buf)-1] = 0;
  213. p = strchr(ds->buf, '!');
  214. if(p == 0) {
  215. ds->netdir = 0;
  216. ds->proto = "net";
  217. ds->host = ds->buf;
  218. } else {
  219. if(*ds->buf != '/'){
  220. ds->netdir = 0;
  221. ds->proto = ds->buf;
  222. } else {
  223. for(p2 = p; *p2 != '/'; p2--)
  224. ;
  225. *p2++ = 0;
  226. ds->netdir = ds->buf;
  227. ds->proto = p2;
  228. }
  229. *p = 0;
  230. ds->host = p + 1;
  231. }
  232. ds->service = strchr(ds->host, '!');
  233. if(ds->service)
  234. *ds->service++ = 0;
  235. }