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