1 #include "stralloc.h"
2 #include "str.h"
3 #include "env.h"
4 #include "sig.h"
5 #include "slurp.h"
6 #include "getconf.h"
7 #include "strerr.h"
8 #include "byte.h"
9 #include "case.h"
10 #include "getln.h"
11 #include "substdio.h"
12 #include "error.h"
13 #include "quote.h"
14 #include "readwrite.h"
15 #include "fmt.h"
16 #include "now.h"
17 #include "cookie.h"
18 #include "subscribe.h"
19 #include "issub.h"
20 #include "log.h"
21
22 #define FATAL "ezmlm-return: fatal: "
die_usage()23 void die_usage() { strerr_die1x(100,"ezmlm-return: usage: ezmlm-return dir"); }
die_nomem()24 void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); }
die_badaddr()25 void die_badaddr()
26 {
27 strerr_die2x(100,FATAL,"I do not accept messages at this address (#5.1.1)");
28 }
die_trash()29 void die_trash()
30 {
31 strerr_die1x(0,"ezmlm-return: info: trash address");
32 }
33
34 char outbuf[1024];
35 substdio ssout;
36 char inbuf[1024];
37 substdio ssin;
38
39 char strnum[FMT_ULONG];
40 char hash[COOKIE];
41 char hashcopy[COOKIE];
42 unsigned long cookiedate;
43 stralloc fndate = {0};
44 stralloc fndatenew = {0};
45 stralloc fnhash = {0};
46 stralloc fnhashnew = {0};
47
48 stralloc quoted = {0};
49 char *sender;
50
die_hashnew()51 void die_hashnew()
52 { strerr_die4sys(111,FATAL,"unable to write ",fnhashnew.s,": "); }
die_datenew()53 void die_datenew()
54 { strerr_die4sys(111,FATAL,"unable to write ",fndatenew.s,": "); }
die_msgin()55 void die_msgin()
56 { strerr_die2sys(111,FATAL,"unable to read input: "); }
57
dowit(addr,when,bounce)58 void dowit(addr,when,bounce)
59 char *addr;
60 unsigned long when;
61 stralloc *bounce;
62 {
63 int fd;
64
65 if (!issub(addr)) return;
66
67 if (!stralloc_copys(&fndate,"bounce/w")) die_nomem();
68 if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,when))) die_nomem();
69 if (!stralloc_cats(&fndate,".")) die_nomem();
70 if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
71 if (!stralloc_0(&fndate)) die_nomem();
72 if (!stralloc_copy(&fndatenew,&fndate)) die_nomem();
73 fndatenew.s[7] = 'W';
74
75 fd = open_trunc(fndatenew.s);
76 if (fd == -1) die_datenew();
77 substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
78 if (substdio_puts(&ssout,addr) == -1) die_datenew();
79 if (substdio_put(&ssout,"",1) == -1) die_datenew();
80 if (substdio_puts(&ssout,"Return-Path: <") == -1) die_datenew();
81 if (!quote2("ed,sender)) die_nomem();
82 if (substdio_put(&ssout,quoted.s,quoted.len) == -1) die_datenew();
83 if (substdio_puts(&ssout,">\n") == -1) die_datenew();
84 if (substdio_put(&ssout,bounce->s,bounce->len) == -1) die_datenew();
85 if (substdio_flush(&ssout) == -1) die_datenew();
86 if (fsync(fd) == -1) die_datenew();
87 if (close(fd) == -1) die_datenew(); /* NFS stupidity */
88
89 if (rename(fndatenew.s,fndate.s) == -1)
90 strerr_die6sys(111,FATAL,"unable to rename ",fndatenew.s," to ",fndate.s,": ");
91 }
92
doit(addr,msgnum,when,bounce)93 void doit(addr,msgnum,when,bounce)
94 char *addr;
95 unsigned long msgnum;
96 unsigned long when;
97 stralloc *bounce;
98 {
99 int fd;
100 int fdnew;
101
102 if (!issub(addr)) return;
103
104 if (!stralloc_copys(&fndate,"bounce/d")) die_nomem();
105 if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,when))) die_nomem();
106 if (!stralloc_cats(&fndate,".")) die_nomem();
107 if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
108 if (!stralloc_0(&fndate)) die_nomem();
109 if (!stralloc_copy(&fndatenew,&fndate)) die_nomem();
110 fndatenew.s[7] = 'D';
111
112 fd = open_trunc(fndatenew.s);
113 if (fd == -1) die_datenew();
114 substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
115 if (substdio_puts(&ssout,addr) == -1) die_datenew();
116 if (substdio_put(&ssout,"",1) == -1) die_datenew();
117 if (substdio_puts(&ssout,"Return-Path: <") == -1) die_datenew();
118 if (!quote2("ed,sender)) die_nomem();
119 if (substdio_put(&ssout,quoted.s,quoted.len) == -1) die_datenew();
120 if (substdio_puts(&ssout,">\n") == -1) die_datenew();
121 if (substdio_put(&ssout,bounce->s,bounce->len) == -1) die_datenew();
122 if (substdio_flush(&ssout) == -1) die_datenew();
123 if (fsync(fd) == -1) die_datenew();
124 if (close(fd) == -1) die_datenew(); /* NFS stupidity */
125
126 cookie(hash,"",0,"",addr,"");
127 if (!stralloc_copys(&fnhash,"bounce/h")) die_nomem();
128 if (!stralloc_catb(&fnhash,hash,COOKIE)) die_nomem();
129 if (!stralloc_0(&fnhash)) die_nomem();
130 if (!stralloc_copy(&fnhashnew,&fnhash)) die_nomem();
131 fnhashnew.s[7] = 'H';
132
133 fdnew = open_trunc(fnhashnew.s);
134 if (fdnew == -1) die_hashnew();
135 substdio_fdbuf(&ssout,write,fdnew,outbuf,sizeof(outbuf));
136
137 fd = open_read(fnhash.s);
138 if (fd == -1) {
139 if (errno != error_noent)
140 strerr_die4sys(111,FATAL,"unable to read ",fnhash.s,": ");
141 if (rename(fndatenew.s,fndate.s) == -1)
142 strerr_die6sys(111,FATAL,"unable to rename ",fndatenew.s," to ",fndate.s,": ");
143 }
144 else {
145 substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
146 switch(substdio_copy(&ssout,&ssin)) {
147 case -2: die_msgin();
148 case -3: die_hashnew();
149 }
150 close(fd);
151 if (unlink(fndatenew.s) == -1)
152 strerr_die4sys(111,FATAL,"unable to unlink ",fndatenew.s,": ");
153 }
154 if (substdio_puts(&ssout," ") == -1) die_hashnew();
155 if (substdio_put(&ssout,strnum,fmt_ulong(strnum,msgnum)) == -1) die_hashnew();
156 if (substdio_puts(&ssout,"\n") == -1) die_hashnew();
157 if (substdio_flush(&ssout) == -1) die_hashnew();
158 if (fsync(fdnew) == -1) die_hashnew();
159 if (close(fdnew) == -1) die_hashnew(); /* NFS stupidity */
160
161 if (rename(fnhashnew.s,fnhash.s) == -1)
162 strerr_die6sys(111,FATAL,"unable to rename ",fnhashnew.s," to ",fnhash.s,": ");
163 }
164
165 stralloc bounce = {0};
166 stralloc line = {0};
167 stralloc header = {0};
168 stralloc intro = {0};
169 stralloc failure = {0};
170 stralloc paragraph = {0};
171 int flaghaveheader;
172 int flaghaveintro;
173
174 stralloc key = {0};
175 stralloc inhost = {0};
176 stralloc outhost = {0};
177 stralloc inlocal = {0};
178 stralloc outlocal = {0};
179
180 char msginbuf[1024];
181 substdio ssmsgin;
182
main(argc,argv)183 void main(argc,argv)
184 int argc;
185 char **argv;
186 {
187 char *dir;
188 char *host;
189 char *local;
190 char *action;
191 unsigned long msgnum;
192 unsigned long cookiedate;
193 unsigned long when;
194 int match;
195 int i;
196 int fdlock;
197
198 umask(022);
199 sig_pipeignore();
200 when = (unsigned long) now();
201
202 dir = argv[1];
203 if (!dir) die_usage();
204
205 sender = env_get("SENDER");
206 if (!sender) strerr_die2x(100,FATAL,"SENDER not set");
207 local = env_get("LOCAL");
208 if (!local) strerr_die2x(100,FATAL,"LOCAL not set");
209 host = env_get("HOST");
210 if (!host) strerr_die2x(100,FATAL,"HOST not set");
211
212 if (chdir(dir) == -1)
213 strerr_die4sys(111,FATAL,"unable to switch to ",dir,": ");
214
215 switch(slurp("key",&key,32)) {
216 case -1:
217 strerr_die4sys(111,FATAL,"unable to read ",dir,"/key: ");
218 case 0:
219 strerr_die3x(100,FATAL,dir,"/key does not exist");
220 }
221 getconf_line(&inhost,"inhost",1,FATAL,dir);
222 getconf_line(&inlocal,"inlocal",1,FATAL,dir);
223 getconf_line(&outhost,"outhost",1,FATAL,dir);
224 getconf_line(&outlocal,"outlocal",1,FATAL,dir);
225
226 if (inhost.len != str_len(host)) die_badaddr();
227 if (case_diffb(inhost.s,inhost.len,host)) die_badaddr();
228 if (inlocal.len > str_len(local)) die_badaddr();
229 if (case_diffb(inlocal.s,inlocal.len,local)) die_badaddr();
230
231 action = local + inlocal.len;
232
233 if (!str_start(action,"-return-")) die_badaddr();
234 action += 8;
235
236 if (!*action) die_trash();
237
238 if (str_start(action,"probe-")) {
239 action += 6;
240 action += scan_ulong(action,&cookiedate);
241 if (now() - cookiedate > 3000000) die_trash();
242 if (*action++ != '.') die_trash();
243 i = str_chr(action,'-');
244 if (i != COOKIE) die_trash();
245 byte_copy(hashcopy,COOKIE,action);
246 action += COOKIE;
247 if (*action++ != '-') die_trash();
248 i = str_rchr(action,'=');
249 if (!stralloc_copyb(&line,action,i)) die_nomem();
250 if (action[i]) {
251 if (!stralloc_cats(&line,"@")) die_nomem();
252 if (!stralloc_cats(&line,action + i + 1)) die_nomem();
253 }
254 if (!stralloc_0(&line)) die_nomem();
255 strnum[fmt_ulong(strnum,cookiedate)] = 0;
256 cookie(hash,key.s,key.len,strnum,line.s,"P");
257 if (byte_diff(hash,COOKIE,hashcopy)) die_trash();
258
259 if (subscribe(line.s,0) == 1) log("-probe",line.s);
260 _exit(0);
261 }
262
263 fdlock = open_append("lockbounce");
264 if (fdlock == -1)
265 strerr_die4sys(111,FATAL,"unable to open ",dir,"/lockbounce: ");
266 if (lock_ex(fdlock) == -1)
267 strerr_die4sys(111,FATAL,"unable to lock ",dir,"/lockbounce: ");
268
269 if (str_start(action,"warn-")) {
270 action += 5;
271 action += scan_ulong(action,&cookiedate);
272 if (now() - cookiedate > 3000000) die_trash();
273 if (*action++ != '.') die_trash();
274 i = str_chr(action,'-');
275 if (i != COOKIE) die_trash();
276 byte_copy(hashcopy,COOKIE,action);
277 action += COOKIE;
278 if (*action++ != '-') die_trash();
279 i = str_rchr(action,'=');
280 if (!stralloc_copyb(&line,action,i)) die_nomem();
281 if (action[i]) {
282 if (!stralloc_cats(&line,"@")) die_nomem();
283 if (!stralloc_cats(&line,action + i + 1)) die_nomem();
284 }
285 if (!stralloc_0(&line)) die_nomem();
286 strnum[fmt_ulong(strnum,cookiedate)] = 0;
287 cookie(hash,key.s,key.len,strnum,line.s,"W");
288 if (byte_diff(hash,COOKIE,hashcopy)) die_trash();
289
290 if (slurpclose(0,&bounce,1024) == -1) die_msgin();
291 dowit(line.s,when,&bounce);
292 _exit(0);
293 }
294
295 action += scan_ulong(action,&msgnum);
296 if (*action != '-') die_badaddr();
297 ++action;
298
299 if (*action) {
300 if (slurpclose(0,&bounce,1024) == -1) die_msgin();
301
302 i = str_rchr(action,'=');
303 if (!stralloc_copyb(&line,action,i)) die_nomem();
304 if (action[i]) {
305 if (!stralloc_cats(&line,"@")) die_nomem();
306 if (!stralloc_cats(&line,action + i + 1)) die_nomem();
307 }
308 if (!stralloc_0(&line)) die_nomem();
309 doit(line.s,msgnum,when,&bounce);
310 _exit(0);
311 }
312
313 /* pre-VERP bounce, in QSBMF format */
314
315 substdio_fdbuf(&ssmsgin,read,0,msginbuf,sizeof(msginbuf));
316
317 flaghaveheader = 0;
318 flaghaveintro = 0;
319
320 for (;;) {
321 if (!stralloc_copys(¶graph,"")) die_nomem();
322 for (;;) {
323 if (getln(&ssmsgin,&line,&match,'\n') == -1) die_msgin();
324 if (!match) die_trash();
325 if (!stralloc_cat(¶graph,&line)) die_nomem();
326 if (line.len <= 1) break;
327 }
328
329 if (!flaghaveheader) {
330 if (!stralloc_copy(&header,¶graph)) die_nomem();
331 flaghaveheader = 1;
332 continue;
333 }
334
335 if (!flaghaveintro) {
336 if (paragraph.len < 15) die_trash();
337 if (str_diffn(paragraph.s,"Hi. This is the",15)) die_trash();
338 if (!stralloc_copy(&intro,¶graph)) die_nomem();
339 flaghaveintro = 1;
340 continue;
341 }
342
343 if (paragraph.s[0] == '-')
344 break;
345
346 if (paragraph.s[0] == '<') {
347 if (!stralloc_copy(&failure,¶graph)) die_nomem();
348
349 if (!stralloc_copy(&bounce,&header)) die_nomem();
350 if (!stralloc_cat(&bounce,&intro)) die_nomem();
351 if (!stralloc_cat(&bounce,&failure)) die_nomem();
352
353 i = byte_chr(failure.s,failure.len,'\n');
354 if (i < 3) die_trash();
355
356 if (!stralloc_copyb(&line,failure.s + 1,i - 3)) die_nomem();
357 if (byte_chr(line.s,line.len,'\0') == line.len) {
358 if (!stralloc_0(&line)) die_nomem();
359 doit(line.s,msgnum,when,&bounce);
360 }
361 }
362 }
363
364 _exit(0);
365 }
366