qmail-remote.c
changeset 0 068428edee47
equal deleted inserted replaced
-1:000000000000 0:068428edee47
       
     1 #include <sys/types.h>
       
     2 #include <sys/socket.h>
       
     3 #include <netinet/in.h>
       
     4 #include <arpa/inet.h>
       
     5 #include "sig.h"
       
     6 #include "stralloc.h"
       
     7 #include "substdio.h"
       
     8 #include "subfd.h"
       
     9 #include "scan.h"
       
    10 #include "case.h"
       
    11 #include "error.h"
       
    12 #include "auto_qmail.h"
       
    13 #include "control.h"
       
    14 #include "dns.h"
       
    15 #include "alloc.h"
       
    16 #include "quote.h"
       
    17 #include "ip.h"
       
    18 #include "ipalloc.h"
       
    19 #include "ipme.h"
       
    20 #include "gen_alloc.h"
       
    21 #include "gen_allocdefs.h"
       
    22 #include "str.h"
       
    23 #include "now.h"
       
    24 #include "exit.h"
       
    25 #include "constmap.h"
       
    26 #include "tcpto.h"
       
    27 #include "readwrite.h"
       
    28 #include "timeoutconn.h"
       
    29 #include "timeoutread.h"
       
    30 #include "timeoutwrite.h"
       
    31 
       
    32 #define HUGESMTPTEXT 5000
       
    33 
       
    34 #define PORT_SMTP 25 /* silly rabbit, /etc/services is for users */
       
    35 unsigned long port = PORT_SMTP;
       
    36 
       
    37 GEN_ALLOC_typedef(saa,stralloc,sa,len,a)
       
    38 GEN_ALLOC_readyplus(saa,stralloc,sa,len,a,i,n,x,10,saa_readyplus)
       
    39 static stralloc sauninit = {0};
       
    40 
       
    41 stralloc helohost = {0};
       
    42 stralloc routes = {0};
       
    43 struct constmap maproutes;
       
    44 stralloc host = {0};
       
    45 stralloc sender = {0};
       
    46 
       
    47 saa reciplist = {0};
       
    48 
       
    49 struct ip_address partner;
       
    50 
       
    51 void out(s) char *s; { if (substdio_puts(subfdoutsmall,s) == -1) _exit(0); }
       
    52 void zero() { if (substdio_put(subfdoutsmall,"\0",1) == -1) _exit(0); }
       
    53 void zerodie() { zero(); substdio_flush(subfdoutsmall); _exit(0); }
       
    54 void outsafe(sa) stralloc *sa; { int i; char ch;
       
    55 for (i = 0;i < sa->len;++i) {
       
    56 ch = sa->s[i]; if (ch < 33) ch = '?'; if (ch > 126) ch = '?';
       
    57 if (substdio_put(subfdoutsmall,&ch,1) == -1) _exit(0); } }
       
    58 
       
    59 void temp_nomem() { out("ZOut of memory. (#4.3.0)\n"); zerodie(); }
       
    60 void temp_oserr() { out("Z\
       
    61 System resources temporarily unavailable. (#4.3.0)\n"); zerodie(); }
       
    62 void temp_noconn() { out("Z\
       
    63 Sorry, I wasn't able to establish an SMTP connection. (#4.4.1)\n"); zerodie(); }
       
    64 void temp_read() { out("ZUnable to read message. (#4.3.0)\n"); zerodie(); }
       
    65 void temp_dnscanon() { out("Z\
       
    66 CNAME lookup failed temporarily. (#4.4.3)\n"); zerodie(); }
       
    67 void temp_dns() { out("Z\
       
    68 Sorry, I couldn't find any host by that name. (#4.1.2)\n"); zerodie(); }
       
    69 void temp_chdir() { out("Z\
       
    70 Unable to switch to home directory. (#4.3.0)\n"); zerodie(); }
       
    71 void temp_control() { out("Z\
       
    72 Unable to read control files. (#4.3.0)\n"); zerodie(); }
       
    73 void perm_partialline() { out("D\
       
    74 SMTP cannot transfer messages with partial final lines. (#5.6.2)\n"); zerodie(); }
       
    75 void perm_usage() { out("D\
       
    76 I (qmail-remote) was invoked improperly. (#5.3.5)\n"); zerodie(); }
       
    77 void perm_dns() { out("D\
       
    78 Sorry, I couldn't find any host named ");
       
    79 outsafe(&host);
       
    80 out(". (#5.1.2)\n"); zerodie(); }
       
    81 void perm_nomx() { out("D\
       
    82 Sorry, I couldn't find a mail exchanger or IP address. (#5.4.4)\n");
       
    83 zerodie(); }
       
    84 void perm_ambigmx() { out("D\
       
    85 Sorry. Although I'm listed as a best-preference MX or A for that host,\n\
       
    86 it isn't in my control/locals file, so I don't treat it as local. (#5.4.6)\n");
       
    87 zerodie(); }
       
    88 
       
    89 void outhost()
       
    90 {
       
    91   char x[IPFMT];
       
    92   if (substdio_put(subfdoutsmall,x,ip_fmt(x,&partner)) == -1) _exit(0);
       
    93 }
       
    94 
       
    95 int flagcritical = 0;
       
    96 
       
    97 void dropped() {
       
    98   out("ZConnected to ");
       
    99   outhost();
       
   100   out(" but connection died. ");
       
   101   if (flagcritical) out("Possible duplicate! ");
       
   102   out("(#4.4.2)\n");
       
   103   zerodie();
       
   104 }
       
   105 
       
   106 int timeoutconnect = 60;
       
   107 int smtpfd;
       
   108 int timeout = 1200;
       
   109 
       
   110 int saferead(fd,buf,len) int fd; char *buf; int len;
       
   111 {
       
   112   int r;
       
   113   r = timeoutread(timeout,smtpfd,buf,len);
       
   114   if (r <= 0) dropped();
       
   115   return r;
       
   116 }
       
   117 int safewrite(fd,buf,len) int fd; char *buf; int len;
       
   118 {
       
   119   int r;
       
   120   r = timeoutwrite(timeout,smtpfd,buf,len);
       
   121   if (r <= 0) dropped();
       
   122   return r;
       
   123 }
       
   124 
       
   125 char inbuf[1024];
       
   126 substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof inbuf);
       
   127 char smtptobuf[1024];
       
   128 substdio smtpto = SUBSTDIO_FDBUF(safewrite,-1,smtptobuf,sizeof smtptobuf);
       
   129 char smtpfrombuf[128];
       
   130 substdio smtpfrom = SUBSTDIO_FDBUF(saferead,-1,smtpfrombuf,sizeof smtpfrombuf);
       
   131 
       
   132 stralloc smtptext = {0};
       
   133 
       
   134 void get(ch)
       
   135 char *ch;
       
   136 {
       
   137   substdio_get(&smtpfrom,ch,1);
       
   138   if (*ch != '\r')
       
   139     if (smtptext.len < HUGESMTPTEXT)
       
   140      if (!stralloc_append(&smtptext,ch)) temp_nomem();
       
   141 }
       
   142 
       
   143 unsigned long smtpcode()
       
   144 {
       
   145   unsigned char ch;
       
   146   unsigned long code;
       
   147 
       
   148   if (!stralloc_copys(&smtptext,"")) temp_nomem();
       
   149 
       
   150   get(&ch); code = ch - '0';
       
   151   get(&ch); code = code * 10 + (ch - '0');
       
   152   get(&ch); code = code * 10 + (ch - '0');
       
   153   for (;;) {
       
   154     get(&ch);
       
   155     if (ch != '-') break;
       
   156     while (ch != '\n') get(&ch);
       
   157     get(&ch);
       
   158     get(&ch);
       
   159     get(&ch);
       
   160   }
       
   161   while (ch != '\n') get(&ch);
       
   162 
       
   163   return code;
       
   164 }
       
   165 
       
   166 void outsmtptext()
       
   167 {
       
   168   int i; 
       
   169   if (smtptext.s) if (smtptext.len) {
       
   170     out("Remote host said: ");
       
   171     for (i = 0;i < smtptext.len;++i)
       
   172       if (!smtptext.s[i]) smtptext.s[i] = '?';
       
   173     if (substdio_put(subfdoutsmall,smtptext.s,smtptext.len) == -1) _exit(0);
       
   174     smtptext.len = 0;
       
   175   }
       
   176 }
       
   177 
       
   178 void quit(prepend,append)
       
   179 char *prepend;
       
   180 char *append;
       
   181 {
       
   182   substdio_putsflush(&smtpto,"QUIT\r\n");
       
   183   /* waiting for remote side is just too ridiculous */
       
   184   out(prepend);
       
   185   outhost();
       
   186   out(append);
       
   187   out(".\n");
       
   188   outsmtptext();
       
   189   zerodie();
       
   190 }
       
   191 
       
   192 void blast()
       
   193 {
       
   194   int r;
       
   195   char ch;
       
   196 
       
   197   for (;;) {
       
   198     r = substdio_get(&ssin,&ch,1);
       
   199     if (r == 0) break;
       
   200     if (r == -1) temp_read();
       
   201     if (ch == '.')
       
   202       substdio_put(&smtpto,".",1);
       
   203     while (ch != '\n') {
       
   204       substdio_put(&smtpto,&ch,1);
       
   205       r = substdio_get(&ssin,&ch,1);
       
   206       if (r == 0) perm_partialline();
       
   207       if (r == -1) temp_read();
       
   208     }
       
   209     substdio_put(&smtpto,"\r\n",2);
       
   210   }
       
   211  
       
   212   flagcritical = 1;
       
   213   substdio_put(&smtpto,".\r\n",3);
       
   214   substdio_flush(&smtpto);
       
   215 }
       
   216 
       
   217 stralloc recip = {0};
       
   218 
       
   219 void smtp()
       
   220 {
       
   221   unsigned long code;
       
   222   int flagbother;
       
   223   int i;
       
   224  
       
   225   if (smtpcode() != 220) quit("ZConnected to "," but greeting failed");
       
   226  
       
   227   substdio_puts(&smtpto,"HELO ");
       
   228   substdio_put(&smtpto,helohost.s,helohost.len);
       
   229   substdio_puts(&smtpto,"\r\n");
       
   230   substdio_flush(&smtpto);
       
   231   if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
       
   232  
       
   233   substdio_puts(&smtpto,"MAIL FROM:<");
       
   234   substdio_put(&smtpto,sender.s,sender.len);
       
   235   substdio_puts(&smtpto,">\r\n");
       
   236   substdio_flush(&smtpto);
       
   237   code = smtpcode();
       
   238   if (code >= 500) quit("DConnected to "," but sender was rejected");
       
   239   if (code >= 400) quit("ZConnected to "," but sender was rejected");
       
   240  
       
   241   flagbother = 0;
       
   242   for (i = 0;i < reciplist.len;++i) {
       
   243     substdio_puts(&smtpto,"RCPT TO:<");
       
   244     substdio_put(&smtpto,reciplist.sa[i].s,reciplist.sa[i].len);
       
   245     substdio_puts(&smtpto,">\r\n");
       
   246     substdio_flush(&smtpto);
       
   247     code = smtpcode();
       
   248     if (code >= 500) {
       
   249       out("h"); outhost(); out(" does not like recipient.\n");
       
   250       outsmtptext(); zero();
       
   251     }
       
   252     else if (code >= 400) {
       
   253       out("s"); outhost(); out(" does not like recipient.\n");
       
   254       outsmtptext(); zero();
       
   255     }
       
   256     else {
       
   257       out("r"); zero();
       
   258       flagbother = 1;
       
   259     }
       
   260   }
       
   261   if (!flagbother) quit("DGiving up on ","");
       
   262  
       
   263   substdio_putsflush(&smtpto,"DATA\r\n");
       
   264   code = smtpcode();
       
   265   if (code >= 500) quit("D"," failed on DATA command");
       
   266   if (code >= 400) quit("Z"," failed on DATA command");
       
   267  
       
   268   blast();
       
   269   code = smtpcode();
       
   270   flagcritical = 0;
       
   271   if (code >= 500) quit("D"," failed after I sent the message");
       
   272   if (code >= 400) quit("Z"," failed after I sent the message");
       
   273   quit("K"," accepted message");
       
   274 }
       
   275 
       
   276 stralloc canonhost = {0};
       
   277 stralloc canonbox = {0};
       
   278 
       
   279 void addrmangle(saout,s,flagalias,flagcname)
       
   280 stralloc *saout; /* host has to be canonical, box has to be quoted */
       
   281 char *s;
       
   282 int *flagalias;
       
   283 int flagcname;
       
   284 {
       
   285   int j;
       
   286  
       
   287   *flagalias = flagcname;
       
   288  
       
   289   j = str_rchr(s,'@');
       
   290   if (!s[j]) {
       
   291     if (!stralloc_copys(saout,s)) temp_nomem();
       
   292     return;
       
   293   }
       
   294   if (!stralloc_copys(&canonbox,s)) temp_nomem();
       
   295   canonbox.len = j;
       
   296   if (!quote(saout,&canonbox)) temp_nomem();
       
   297   if (!stralloc_cats(saout,"@")) temp_nomem();
       
   298  
       
   299   if (!stralloc_copys(&canonhost,s + j + 1)) temp_nomem();
       
   300   if (flagcname)
       
   301     switch(dns_cname(&canonhost)) {
       
   302       case 0: *flagalias = 0; break;
       
   303       case DNS_MEM: temp_nomem();
       
   304       case DNS_SOFT: temp_dnscanon();
       
   305       case DNS_HARD: ; /* alias loop, not our problem */
       
   306     }
       
   307 
       
   308   if (!stralloc_cat(saout,&canonhost)) temp_nomem();
       
   309 }
       
   310 
       
   311 void getcontrols()
       
   312 {
       
   313   if (control_init() == -1) temp_control();
       
   314   if (control_readint(&timeout,"control/timeoutremote") == -1) temp_control();
       
   315   if (control_readint(&timeoutconnect,"control/timeoutconnect") == -1)
       
   316     temp_control();
       
   317   if (control_rldef(&helohost,"control/helohost",1,(char *) 0) != 1)
       
   318     temp_control();
       
   319   switch(control_readfile(&routes,"control/smtproutes",0)) {
       
   320     case -1:
       
   321       temp_control();
       
   322     case 0:
       
   323       if (!constmap_init(&maproutes,"",0,1)) temp_nomem(); break;
       
   324     case 1:
       
   325       if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break;
       
   326   }
       
   327 }
       
   328 
       
   329 void main(argc,argv)
       
   330 int argc;
       
   331 char **argv;
       
   332 {
       
   333   static ipalloc ip = {0};
       
   334   int i;
       
   335   unsigned long random;
       
   336   char **recips;
       
   337   unsigned long prefme;
       
   338   int flagallaliases;
       
   339   int flagalias;
       
   340   char *relayhost;
       
   341  
       
   342   sig_pipeignore();
       
   343   if (argc < 4) perm_usage();
       
   344   if (chdir(auto_qmail) == -1) temp_chdir();
       
   345   getcontrols();
       
   346  
       
   347  
       
   348   if (!stralloc_copys(&host,argv[1])) temp_nomem();
       
   349  
       
   350   relayhost = 0;
       
   351   for (i = 0;i <= host.len;++i)
       
   352     if ((i == 0) || (i == host.len) || (host.s[i] == '.'))
       
   353       if (relayhost = constmap(&maproutes,host.s + i,host.len - i))
       
   354         break;
       
   355   if (relayhost && !*relayhost) relayhost = 0;
       
   356  
       
   357   if (relayhost) {
       
   358     i = str_chr(relayhost,':');
       
   359     if (relayhost[i]) {
       
   360       scan_ulong(relayhost + i + 1,&port);
       
   361       relayhost[i] = 0;
       
   362     }
       
   363     if (!stralloc_copys(&host,relayhost)) temp_nomem();
       
   364   }
       
   365 
       
   366 
       
   367   addrmangle(&sender,argv[2],&flagalias,0);
       
   368  
       
   369   if (!saa_readyplus(&reciplist,0)) temp_nomem();
       
   370   if (ipme_init() != 1) temp_oserr();
       
   371  
       
   372   flagallaliases = 1;
       
   373   recips = argv + 3;
       
   374   while (*recips) {
       
   375     if (!saa_readyplus(&reciplist,1)) temp_nomem();
       
   376     reciplist.sa[reciplist.len] = sauninit;
       
   377     addrmangle(reciplist.sa + reciplist.len,*recips,&flagalias,!relayhost);
       
   378     if (!flagalias) flagallaliases = 0;
       
   379     ++reciplist.len;
       
   380     ++recips;
       
   381   }
       
   382 
       
   383  
       
   384   random = now() + (getpid() << 16);
       
   385   switch (relayhost ? dns_ip(&ip,&host) : dns_mxip(&ip,&host,random)) {
       
   386     case DNS_MEM: temp_nomem();
       
   387     case DNS_SOFT: temp_dns();
       
   388     case DNS_HARD: perm_dns();
       
   389     case 1:
       
   390       if (ip.len <= 0) temp_dns();
       
   391   }
       
   392  
       
   393   if (ip.len <= 0) perm_nomx();
       
   394  
       
   395   prefme = 100000;
       
   396   for (i = 0;i < ip.len;++i)
       
   397     if (ipme_is(&ip.ix[i].ip))
       
   398       if (ip.ix[i].pref < prefme)
       
   399         prefme = ip.ix[i].pref;
       
   400  
       
   401   if (relayhost) prefme = 300000;
       
   402   if (flagallaliases) prefme = 500000;
       
   403  
       
   404   for (i = 0;i < ip.len;++i)
       
   405     if (ip.ix[i].pref < prefme)
       
   406       break;
       
   407  
       
   408   if (i >= ip.len)
       
   409     perm_ambigmx();
       
   410  
       
   411   for (i = 0;i < ip.len;++i) if (ip.ix[i].pref < prefme) {
       
   412     if (tcpto(&ip.ix[i].ip)) continue;
       
   413  
       
   414     smtpfd = socket(AF_INET,SOCK_STREAM,0);
       
   415     if (smtpfd == -1) temp_oserr();
       
   416  
       
   417     if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) {
       
   418       tcpto_err(&ip.ix[i].ip,0);
       
   419       partner = ip.ix[i].ip;
       
   420       smtp(); /* does not return */
       
   421     }
       
   422     tcpto_err(&ip.ix[i].ip,errno == error_timeout);
       
   423     close(smtpfd);
       
   424   }
       
   425   
       
   426   temp_noconn();
       
   427 }