2 #include <sys/socket.h>
3 #include <netinet/in.h>
13 #include "auto_qmail.h"
21 #include "gen_alloc.h"
22 #include "gen_allocdefs.h"
28 #include "timeoutconn.h"
29 #include "timeoutread.h"
30 #include "timeoutwrite.h"
32 #define HUGESMTPTEXT 5000
34 #define PORT_SMTP 25 /* silly rabbit, /etc/services is for users */
35 unsigned long port
= PORT_SMTP
;
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};
41 stralloc helohost
= {0};
42 stralloc routes
= {0};
43 struct constmap maproutes
;
45 stralloc sender
= {0};
49 struct ip_address partner
;
51 void out(s
) char *s
; { if (substdio_puts(subfdout
,s
) == -1) _exit(0); }
52 void zero() { if (substdio_put(subfdout
,"\0",1) == -1) _exit(0); }
53 void zerodie() { zero(); substdio_flush(subfdout
); _exit(0); }
55 void outsafe(sa
) stralloc
*sa
; { int i
; char ch
;
56 for (i
= 0;i
< sa
->len
;++i
) {
57 ch
= sa
->s
[i
]; if (ch
< 33) ch
= '?'; if (ch
> 126) ch
= '?';
58 if (substdio_put(subfdout
,&ch
,1) == -1) _exit(0); } }
60 void temp_nomem() { out("ZOut of memory. (#4.3.0)\n"); zerodie(); }
61 void temp_oserr() { out("Z\
62 System resources temporarily unavailable. (#4.3.0)\n"); zerodie(); }
63 void temp_noconn() { out("Z\
64 Sorry, I wasn't able to establish an SMTP connection. (#4.4.1)\n"); zerodie(); }
65 void temp_read() { out("ZUnable to read message. (#4.3.0)\n"); zerodie(); }
66 void temp_dnscanon() { out("Z\
67 CNAME lookup failed temporarily. (#4.4.3)\n"); zerodie(); }
68 void temp_dns() { out("Z\
69 Sorry, I couldn't find any host by that name. (#4.1.2)\n"); zerodie(); }
70 void temp_chdir() { out("Z\
71 Unable to switch to home directory. (#4.3.0)\n"); zerodie(); }
72 void temp_control() { out("Z\
73 Unable to read control files. (#4.3.0)\n"); zerodie(); }
74 void perm_partialline() { out("D\
75 SMTP cannot transfer messages with partial final lines. (#5.6.2)\n"); zerodie(); }
76 void perm_usage() { out("D\
77 I (qmail-remote) was invoked improperly. (#5.3.5)\n"); zerodie(); }
78 void perm_dns() { out("D\
79 Sorry, I couldn't find any host named ");
81 out(". (#5.1.2)\n"); zerodie(); }
82 void perm_nomx() { out("D\
83 Sorry, I couldn't find a mail exchanger or IP address. (#5.4.4)\n");
85 void perm_ambigmx() { out("D\
86 Sorry. Although I'm listed as a best-preference MX or A for that host,\n\
87 it isn't in my control/locals file, so I don't treat it as local. (#5.4.6)\n");
91 int timeoutconnect
= 60;
96 if (control_init() == -1)
97 { if (errno
== error_nomem
) temp_nomem(); temp_control(); }
99 if (control_readint(&timeout
,"control/timeoutremote") == -1)
100 { if (errno
== error_nomem
) temp_nomem(); temp_control(); }
101 if (control_readint(&timeoutconnect
,"control/timeoutconnect") == -1)
102 { if (errno
== error_nomem
) temp_nomem(); temp_control(); }
104 r
= control_rldef(&helohost
,"control/helohost",1,(char *) 0);
105 if (r
== -1) if (errno
== error_nomem
) temp_nomem();
106 if (r
!= 1) temp_control();
108 switch(control_readfile(&routes
,"control/smtproutes",0))
111 if (errno
== error_nomem
) temp_nomem(); temp_control();
113 if (!constmap_init(&maproutes
,"",0,1)) temp_nomem(); break;
115 if (!constmap_init(&maproutes
,routes
.s
,routes
.len
,1)) temp_nomem(); break;
119 char smtptobuf
[1024];
120 char smtpfrombuf
[128];
121 stralloc smtpline
= {0};
122 stralloc smtptext
= {0};
127 if (smtptext
.s
) if (smtptext
.len
) if (smtptext
.len
< HUGESMTPTEXT
)
129 if (substdio_puts(subfdout
,"Remote host said: ") == -1) _exit(0);
130 for (i
= 0;i
< smtptext
.len
;++i
)
131 if (!smtptext
.s
[i
]) smtptext
.s
[i
] = '?';
132 if (substdio_put(subfdout
,smtptext
.s
,smtptext
.len
) == -1) _exit(0);
137 unsigned long smtpcode(ss
)
143 if (!stralloc_copys(&smtptext
,"")) return 421;
146 if (getln(ss
,&smtpline
,&match
,'\n') != 0) return 421;
147 if (!match
) return 421;
148 if ((smtpline
.len
>= 2) && (smtpline
.s
[smtpline
.len
- 2] == '\r'))
150 smtpline
.s
[smtpline
.len
- 2] = '\n';
153 if (!stralloc_cat(&smtptext
,&smtpline
)) return 421;
154 if (scan_nbblong(smtpline
.s
,smtpline
.len
,10,0,&code
) != 3) return 421;
155 if (smtpline
.len
== 3) return code
;
157 while (smtpline
.s
[3] == '-');
166 x
[ip_fmt(x
,&partner
)] = 0;
172 out("ZConnected to "); outhost();
173 out(" but communications failed. (#4.4.2)\n");
177 void quit(ssto
,ssfrom
)
182 if (substdio_putsflush(ssto
,"QUIT\r\n") != -1)
183 smtpcode(ssfrom
); /* protocol design stupidity */
187 stralloc dataline
= {0};
189 void blast(ssto
,ssfrom
)
197 if (getln(ssfrom
,&dataline
,&match
,'\n') != 0) temp_read();
198 if (!match
&& !dataline
.len
) break;
199 if (!match
) perm_partialline();
201 if (dataline
.len
&& (dataline
.s
[0] == '.'))
202 if (substdio_put(ssto
,".",1) == -1) writeerr();
203 if (substdio_put(ssto
,dataline
.s
,dataline
.len
) == -1) writeerr();
204 if (substdio_put(ssto
,"\r\n",2) == -1) writeerr();
206 if (substdio_put(ssto
,".\r\n",3) == -1) writeerr();
207 if (substdio_flush(ssto
) == -1) writeerr();
210 stralloc recip
= {0};
221 substdio_fdbuf(&ssto
,timeoutwrite
,TIMEOUTWRITE(timeout
,fd
),smtptobuf
,sizeof(smtptobuf
));
222 substdio_fdbuf(&ssfrom
,timeoutread
,TIMEOUTREAD(timeout
,fd
),smtpfrombuf
,sizeof(smtpfrombuf
));
224 if (smtpcode(&ssfrom
) != 220)
226 out("ZConnected to "); outhost(); out(" but greeting failed.\n");
230 if (substdio_puts(&ssto
,"HELO ") == -1) writeerr();
231 if (substdio_put(&ssto
,helohost
.s
,helohost
.len
) == -1) writeerr();
232 if (substdio_puts(&ssto
,"\r\n") == -1) writeerr();
233 if (substdio_flush(&ssto
) == -1) writeerr();
235 if (smtpcode(&ssfrom
) != 250)
237 out("ZConnected to "); outhost(); out(" but my name was rejected.\n");
241 if (substdio_puts(&ssto
,"MAIL FROM:<") == -1) writeerr();
242 if (substdio_put(&ssto
,sender
.s
,sender
.len
) == -1) writeerr();
243 if (substdio_puts(&ssto
,">\r\n") == -1) writeerr();
244 if (substdio_flush(&ssto
) == -1) writeerr();
246 code
= smtpcode(&ssfrom
);
249 out("DConnected to "); outhost(); out(" but sender was rejected.\n");
254 out("ZConnected to "); outhost(); out(" but sender was rejected.\n");
259 for (i
= 0;i
< reciplist
.len
;++i
)
261 if (substdio_puts(&ssto
,"RCPT TO:<") == -1) writeerr();
262 if (substdio_put(&ssto
,reciplist
.sa
[i
].s
,reciplist
.sa
[i
].len
) == -1) writeerr();
263 if (substdio_puts(&ssto
,">\r\n") == -1) writeerr();
264 if (substdio_flush(&ssto
) == -1) writeerr();
266 code
= smtpcode(&ssfrom
);
269 out("ZConnected to "); outhost(); out(" but connection died.\n");
274 out("h"); outhost(); out(" does not like recipient.\n");
275 outsmtptext(); zero();
277 else if (code
>= 400)
279 out("s"); outhost(); out(" does not like recipient.\n");
280 outsmtptext(); zero();
291 out("DGiving up.\n");
295 if (substdio_putsflush(&ssto
,"DATA\r\n") == -1) writeerr();
297 code
= smtpcode(&ssfrom
);
300 out("ZConnected to "); outhost(); out(" but connection died.\n");
305 out("D"); outhost(); out(" failed on DATA command.\n");
310 out("Z"); outhost(); out(" failed on DATA command.\n");
314 blast(&ssto
,subfdin
);
316 code
= smtpcode(&ssfrom
);
319 out("ZConnected to "); outhost(); out(" but connection died. Possible duplicate!\n");
324 out("D"); outhost(); out(" failed after I sent the message.\n");
329 out("Z"); outhost(); out(" failed after I sent the message.\n");
333 out("K"); outhost(); out(" accepted message.\n");
337 stralloc canonhost
= {0};
338 stralloc canonbox
= {0};
340 void addrmangle(saout
,s
,flagalias
,flagcname
)
341 stralloc
*saout
; /* host has to be canonical, box has to be quoted */
348 *flagalias
= flagcname
;
353 if (!stralloc_copys(saout
,s
)) temp_nomem();
356 if (!stralloc_copys(&canonbox
,s
)) temp_nomem();
358 if (!quote(saout
,&canonbox
)) temp_nomem();
359 if (!stralloc_cats(saout
,"@")) temp_nomem();
361 if (!stralloc_copys(&canonhost
,s
+ j
+ 1)) temp_nomem();
363 switch(dns_cname(&canonhost
))
365 case 0: *flagalias
= 0; break;
366 case DNS_MEM
: temp_nomem();
367 case DNS_SOFT
: temp_dnscanon();
368 case DNS_HARD
: ; /* alias loop, not our problem */
371 if (!stralloc_cat(saout
,&canonhost
)) temp_nomem();
378 static ipalloc ip
= {0};
380 unsigned long random
;
382 unsigned long prefme
;
388 if (argc
< 4) perm_usage();
389 if (chdir(auto_qmail
) == -1) temp_chdir();
393 if (!stralloc_copys(&host
,argv
[1])) temp_nomem();
396 for (i
= 0;i
<= host
.len
;++i
)
397 if ((i
== 0) || (i
== host
.len
) || (host
.s
[i
] == '.'))
398 if (relayhost
= constmap(&maproutes
,host
.s
+ i
,host
.len
- i
))
400 if (relayhost
&& !*relayhost
) relayhost
= 0;
404 i
= str_chr(relayhost
,':');
407 scan_ulong(relayhost
+ i
+ 1,&port
);
410 if (!stralloc_copys(&host
,relayhost
)) temp_nomem();
414 addrmangle(&sender
,argv
[2],&flagalias
,!relayhost
);
416 if (!saa_readyplus(&reciplist
,0)) temp_nomem();
417 if (ipme_init() != 1) temp_oserr();
423 if (!saa_readyplus(&reciplist
,1)) temp_nomem();
424 reciplist
.sa
[reciplist
.len
] = sauninit
;
425 addrmangle(reciplist
.sa
+ reciplist
.len
,*recips
,&flagalias
,!relayhost
);
426 if (!flagalias
) flagallaliases
= 0;
432 random
= now() + (getpid() << 16);
433 switch (relayhost ?
dns_ip(&ip
,&host
) : dns_mxip(&ip
,&host
,random
))
435 case DNS_MEM
: temp_nomem();
436 case DNS_SOFT
: temp_dns();
437 case DNS_HARD
: perm_dns();
439 if (ip
.len
<= 0) temp_dns();
442 if (ip
.len
<= 0) perm_nomx();
445 for (i
= 0;i
< ip
.len
;++i
)
446 if (ipme_is(&ip
.ix
[i
].ip
))
447 if (ip
.ix
[i
].pref
< prefme
)
448 prefme
= ip
.ix
[i
].pref
;
450 if (relayhost
) prefme
= 300000;
451 if (flagallaliases
) prefme
= 500000;
453 for (i
= 0;i
< ip
.len
;++i
)
454 if (ip
.ix
[i
].pref
< prefme
)
460 for (i
= 0;i
< ip
.len
;++i
) if (ip
.ix
[i
].pref
< prefme
)
464 if (tcpto(&ip
.ix
[i
].ip
)) continue;
466 s
= socket(AF_INET
,SOCK_STREAM
,0);
467 if (s
== -1) temp_oserr();
469 if (timeoutconn(s
,&ip
.ix
[i
].ip
,(unsigned int) port
,timeoutconnect
) == 0)
471 tcpto_err(&ip
.ix
[i
].ip
,0);
472 partner
= ip
.ix
[i
].ip
;
473 smtp(s
); /* does not return */
475 tcpto_err(&ip
.ix
[i
].ip
,errno
== error_timeout
);