URI: 
       tmxdial.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tmxdial.c (5446B)
       ---
            1 #include "common.h"
            2 #include <ndb.h>
            3 #include "smtp.h"        /* to publish dial_string_parse */
            4 #include <ip.h>
            5 #include <thread.h>
            6 
            7 enum
            8 {
            9         Nmx=        16,
           10         Maxstring=        256
           11 };
           12 
           13 typedef struct Mx        Mx;
           14 struct Mx
           15 {
           16         char host[256];
           17         char ip[24];
           18         int pref;
           19 };
           20 static Mx mx[Nmx];
           21 
           22 Ndb *db;
           23 extern int debug;
           24 
           25 static int        mxlookup(DS*, char*);
           26 static int        compar(const void*, const void*);
           27 static int        callmx(DS*, char*, char*);
           28 static void expand_meta(DS *ds);
           29 extern int        cistrcmp(char*, char*);
           30 
           31 /* Taken from imapdial, replaces tlsclient call with stunnel */
           32 static int
           33 smtpdial(char *server)
           34 {
           35         int p[2];
           36         int fd[3];
           37         char *tmp;
           38         char *fpath;
           39 
           40         if(pipe(p) < 0)
           41                 return -1;
           42         fd[0] = dup(p[0], -1);
           43         fd[1] = dup(p[0], -1);
           44         fd[2] = dup(2, -1);
           45 #ifdef PLAN9PORT
           46         tmp = smprint("%s:587", server);
           47         fpath = searchpath("stunnel3");
           48         if (!fpath) {
           49                 werrstr("stunnel not found. it is required for tls support.");
           50                 return -1;
           51         }
           52         if(threadspawnl(fd, fpath, "stunnel", "-n", "smtp" , "-c", "-r", tmp, nil) < 0) {
           53 #else
           54         tmp = smprint("tcp!%s!587", server);
           55         if(threadspawnl(fd, "/bin/tlsclient", "tlsclient", tmp, nil) < 0){
           56 #endif
           57                 free(tmp);
           58                 close(p[0]);
           59                 close(p[1]);
           60                 close(fd[0]);
           61                 close(fd[1]);
           62                 close(fd[2]);
           63                 return -1;
           64         }
           65         free(tmp);
           66         close(p[0]);
           67         return p[1];
           68 }
           69 
           70 int
           71 mxdial(char *addr, char *ddomain, char *gdomain)
           72 {
           73         int fd;
           74         DS ds;
           75         char err[Errlen];
           76 
           77         addr = netmkaddr(addr, 0, "smtp");
           78         dial_string_parse(addr, &ds);
           79 
           80         /* try connecting to destination or any of it's mail routers */
           81         fd = callmx(&ds, addr, ddomain);
           82 
           83         /* try our mail gateway */
           84         rerrstr(err, sizeof(err));
           85         if(fd < 0 && gdomain && strstr(err, "can't translate") != 0)
           86                 fd = dial(netmkaddr(gdomain, 0, "smtp"), 0, 0, 0);
           87 
           88         return fd;
           89 }
           90 
           91 static int
           92 timeout(void *v, char *msg)
           93 {
           94         USED(v);
           95 
           96         if(strstr(msg, "alarm"))
           97                 return 1;
           98         return 0;
           99 }
          100 
          101 /*
          102  *  take an address and return all the mx entries for it,
          103  *  most preferred first
          104  */
          105 static int
          106 callmx(DS *ds, char *dest, char *domain)
          107 {
          108         int fd, i, nmx;
          109         char addr[Maxstring];
          110 
          111         /* get a list of mx entries */
          112         nmx = mxlookup(ds, domain);
          113         if(nmx < 0){
          114                 /* dns isn't working, don't just dial */
          115                 return -1;
          116         }
          117         if(nmx == 0){
          118                 if(debug)
          119                         fprint(2, "mxlookup returns nothing\n");
          120                 return dial(dest, 0, 0, 0);
          121         }
          122 
          123         /* refuse to honor loopback addresses given by dns */
          124         for(i = 0; i < nmx; i++){
          125                 if(strcmp(mx[i].ip, "127.0.0.1") == 0){
          126                         if(debug)
          127                                 fprint(2, "mxlookup returns loopback\n");
          128                         werrstr("illegal: domain lists 127.0.0.1 as mail server");
          129                         return -1;
          130                 }
          131         }
          132 
          133         /* sort by preference */
          134         if(nmx > 1)
          135                 qsort(mx, nmx, sizeof(Mx), compar);
          136 
          137         if(debug){
          138                 for(i=0; i<nmx; i++)
          139                         print("%s %d\n", mx[i].host, mx[i].pref);
          140         }
          141         /* dial each one in turn */
          142         for(i = 0; i < nmx; i++){
          143 #ifdef PLAN9PORT
          144                 snprint(addr, sizeof(addr), "%s", mx[i].host);
          145 #else
          146                 snprint(addr, sizeof(addr), "%s!%s!%s", ds->proto,
          147                         mx[i].host, ds->service);
          148 #endif
          149                 if(debug)
          150                         fprint(2, "mxdial trying %s (%d)\n", addr, i);
          151                 atnotify(timeout, 1);
          152                 alarm(10*1000);
          153 #ifdef PLAN9PORT
          154                 fd = smtpdial(addr);
          155 #else
          156                 fd = dial(addr, 0, 0, 0);
          157 #endif
          158                 alarm(0);
          159                 atnotify(timeout, 0);
          160                 if(fd >= 0)
          161                         return fd;
          162         }
          163         return -1;
          164 }
          165 
          166 /*
          167  *  use dns to resolve the mx request
          168  */
          169 static int
          170 mxlookup(DS *ds, char *domain)
          171 {
          172         int i, n, nmx;
          173         Ndbtuple *t, *tmx, *tpref, *tip;
          174 
          175         strcpy(domain, ds->host);
          176         ds->netdir = "/net";
          177         nmx = 0;
          178         if((t = dnsquery(nil, ds->host, "mx")) != nil){
          179                 for(tmx=t; (tmx=ndbfindattr(tmx->entry, nil, "mx")) != nil && nmx<Nmx; ){
          180                         for(tpref=tmx->line; tpref != tmx; tpref=tpref->line){
          181                                 if(strcmp(tpref->attr, "pref") == 0){
          182                                         strncpy(mx[nmx].host, tmx->val, sizeof(mx[n].host)-1);
          183                                         mx[nmx].pref = atoi(tpref->val);
          184                                         nmx++;
          185                                         break;
          186                                 }
          187                         }
          188                 }
          189                 ndbfree(t);
          190         }
          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         /*
          207          * look up all ip addresses
          208          */
          209         for(i = 0; i < nmx; i++){
          210                 if((t = dnsquery(nil, mx[i].host, "ip")) == nil)
          211                         goto no;
          212                 if((tip = ndbfindattr(t, nil, "ip")) == nil){
          213                         ndbfree(t);
          214                         goto no;
          215                 }
          216                 strncpy(mx[i].ip, tip->val, sizeof(mx[i].ip)-1);
          217                 ndbfree(t);
          218                 continue;
          219 
          220         no:
          221                 /* remove mx[i] and go around again */
          222                 nmx--;
          223                 mx[i] = mx[nmx];
          224                 i--;
          225         }
          226         return nmx;
          227 }
          228 
          229 static int
          230 compar(const void *a, const void *b)
          231 {
          232         return ((Mx*)a)->pref - ((Mx*)b)->pref;
          233 }
          234 
          235 /* break up an address to its component parts */
          236 void
          237 dial_string_parse(char *str, DS *ds)
          238 {
          239         char *p, *p2;
          240 
          241         strncpy(ds->buf, str, sizeof(ds->buf));
          242         ds->buf[sizeof(ds->buf)-1] = 0;
          243 
          244         p = strchr(ds->buf, '!');
          245         if(p == 0) {
          246                 ds->netdir = 0;
          247                 ds->proto = "net";
          248                 ds->host = ds->buf;
          249         } else {
          250                 if(*ds->buf != '/'){
          251                         ds->netdir = 0;
          252                         ds->proto = ds->buf;
          253                 } else {
          254                         for(p2 = p; *p2 != '/'; p2--)
          255                                 ;
          256                         *p2++ = 0;
          257                         ds->netdir = ds->buf;
          258                         ds->proto = p2;
          259                 }
          260                 *p = 0;
          261                 ds->host = p + 1;
          262         }
          263         ds->service = strchr(ds->host, '!');
          264         if(ds->service)
          265                 *ds->service++ = 0;
          266         if(*ds->host == '$')
          267                 expand_meta(ds);
          268 }
          269 
          270 static void
          271 expand_meta(DS *ds)
          272 {
          273         static Ndb *db;
          274         Ndbs s;
          275         char *sys, *smtpserver;
          276 
          277         /* can't ask cs, so query database directly. */
          278         sys = sysname();
          279         if(db == nil)
          280                 db = ndbopen(0);
          281         smtpserver = ndbgetvalue(db, &s, "sys", sys, "smtp", nil);
          282         snprint(ds->host, 128, "%s", smtpserver);
          283 }