1 /* $NetBSD: delivered_hdr.c,v 1.3 2022/10/08 16:12:45 christos Exp $ */
2
3 /*++
4 /* NAME
5 /* delivered_hdr 3
6 /* SUMMARY
7 /* process Delivered-To: headers
8 /* SYNOPSIS
9 /* #include <delivered_hdr.h>
10 /*
11 /* DELIVERED_HDR_INFO *delivered_hdr_init(stream, offset, flags)
12 /* VSTREAM *stream;
13 /* off_t offset;
14 /* int flags;
15 /*
16 /* int delivered_hdr_find(info, address)
17 /* DELIVERED_HDR_INFO *info;
18 /* const char *address;
19 /*
20 /* void delivered_hdr_free(info)
21 /* DELIVERED_HDR_INFO *info;
22 /* DESCRIPTION
23 /* This module processes addresses in Delivered-To: headers.
24 /* These headers are added by some mail delivery systems, for the
25 /* purpose of breaking mail forwarding loops. N.B. This solves
26 /* a different problem than the Received: hop count limit. Hop
27 /* counts are used to limit the impact of mail routing problems.
28 /*
29 /* delivered_hdr_init() extracts Delivered-To: header addresses
30 /* from the specified message, and returns a table with the
31 /* result. The file seek pointer is changed.
32 /*
33 /* delivered_hdr_find() looks up the address in the lookup table,
34 /* and returns non-zero when the address was found. The
35 /* address argument must be in internalized form.
36 /*
37 /* delivered_hdr_free() releases storage that was allocated by
38 /* delivered_hdr_init().
39 /*
40 /* Arguments:
41 /* .IP stream
42 /* The open queue file.
43 /* .IP offset
44 /* Offset of the first message content record.
45 /* .IP flags
46 /* Zero, or the bit-wise OR ot:
47 /* .RS
48 /* .IP FOLD_ADDR_USER
49 /* Case fold the address local part.
50 /* .IP FOLD_ADDR_HOST
51 /* Case fold the address domain part.
52 /* .IP FOLD_ADDR_ALL
53 /* Alias for (FOLD_ADDR_USER | FOLD_ADDR_HOST).
54 /* .RE
55 /* .IP info
56 /* Extracted Delivered-To: addresses information.
57 /* .IP address
58 /* A recipient address, internal form.
59 /* DIAGNOSTICS
60 /* Fatal errors: out of memory.
61 /* SEE ALSO
62 /* mail_copy(3), producer of Delivered-To: and other headers.
63 /* LICENSE
64 /* .ad
65 /* .fi
66 /* The Secure Mailer license must be distributed with this software.
67 /* AUTHOR(S)
68 /* Wietse Venema
69 /* IBM T.J. Watson Research
70 /* P.O. Box 704
71 /* Yorktown Heights, NY 10598, USA
72 /*
73 /* Wietse Venema
74 /* Google, Inc.
75 /* 111 8th Avenue
76 /* New York, NY 10011, USA
77 /*--*/
78
79 /* System library. */
80
81 #include <sys_defs.h>
82 #include <unistd.h>
83 #include <string.h>
84 #include <ctype.h>
85
86 /* Utility library. */
87
88 #include <msg.h>
89 #include <mymalloc.h>
90 #include <htable.h>
91 #include <vstring.h>
92 #include <vstream.h>
93 #include <vstring_vstream.h>
94 #include <stringops.h>
95
96 /* Global library. */
97
98 #include <record.h>
99 #include <rec_type.h>
100 #include <is_header.h>
101 #include <quote_822_local.h>
102 #include <header_opts.h>
103 #include <delivered_hdr.h>
104 #include <fold_addr.h>
105
106 /*
107 * Application-specific.
108 */
109 struct DELIVERED_HDR_INFO {
110 int flags;
111 VSTRING *buf;
112 VSTRING *fold;
113 HTABLE *table;
114 };
115
116 #define STR(x) vstring_str(x)
117
118 /* delivered_hdr_init - extract delivered-to information from the message */
119
delivered_hdr_init(VSTREAM * fp,off_t offset,int flags)120 DELIVERED_HDR_INFO *delivered_hdr_init(VSTREAM *fp, off_t offset, int flags)
121 {
122 char *cp;
123 DELIVERED_HDR_INFO *info;
124 const HEADER_OPTS *hdr;
125 int curr_type;
126 int prev_type;
127
128 /*
129 * Sanity check.
130 */
131 info = (DELIVERED_HDR_INFO *) mymalloc(sizeof(*info));
132 info->flags = flags;
133 info->buf = vstring_alloc(10);
134 info->fold = vstring_alloc(10);
135 info->table = htable_create(0);
136
137 if (vstream_fseek(fp, offset, SEEK_SET) < 0)
138 msg_fatal("seek queue file %s: %m", VSTREAM_PATH(fp));
139
140 /*
141 * XXX Assume that mail_copy() produces delivered-to headers that fit in
142 * a REC_TYPE_NORM or REC_TYPE_CONT record. Lowercase the delivered-to
143 * addresses for consistency.
144 *
145 * XXX Don't get bogged down by gazillions of delivered-to headers.
146 */
147 #define DELIVERED_HDR_LIMIT 1000
148
149 for (prev_type = REC_TYPE_NORM;
150 info->table->used < DELIVERED_HDR_LIMIT
151 && ((curr_type = rec_get(fp, info->buf, 0)) == REC_TYPE_NORM
152 || curr_type == REC_TYPE_CONT);
153 prev_type = curr_type) {
154 if (prev_type == REC_TYPE_CONT)
155 continue;
156 if (is_header(STR(info->buf))) {
157 if ((hdr = header_opts_find(STR(info->buf))) != 0
158 && hdr->type == HDR_DELIVERED_TO) {
159 cp = STR(info->buf) + strlen(hdr->name) + 1;
160 while (ISSPACE(*cp))
161 cp++;
162 cp = fold_addr(info->fold, cp, info->flags);
163 if (msg_verbose)
164 msg_info("delivered_hdr_init: %s", cp);
165 htable_enter(info->table, cp, (void *) 0);
166 }
167 } else if (ISSPACE(STR(info->buf)[0])) {
168 continue;
169 } else {
170 break;
171 }
172 }
173 return (info);
174 }
175
176 /* delivered_hdr_find - look up recipient in delivered table */
177
delivered_hdr_find(DELIVERED_HDR_INFO * info,const char * address)178 int delivered_hdr_find(DELIVERED_HDR_INFO *info, const char *address)
179 {
180 HTABLE_INFO *ht;
181 const char *addr_key;
182
183 /*
184 * mail_copy() uses quote_822_local() when writing the Delivered-To:
185 * header. We must therefore apply the same transformation when looking
186 * up the recipient. Lowercase the delivered-to address for consistency.
187 */
188 quote_822_local(info->buf, address);
189 addr_key = fold_addr(info->fold, STR(info->buf), info->flags);
190 ht = htable_locate(info->table, addr_key);
191 return (ht != 0);
192 }
193
194 /* delivered_hdr_free - destructor */
195
delivered_hdr_free(DELIVERED_HDR_INFO * info)196 void delivered_hdr_free(DELIVERED_HDR_INFO *info)
197 {
198 vstring_free(info->buf);
199 vstring_free(info->fold);
200 htable_free(info->table, (void (*) (void *)) 0);
201 myfree((void *) info);
202 }
203
204 #ifdef TEST
205
206 #include <msg_vstream.h>
207 #include <mail_params.h>
208
209 char *var_drop_hdrs;
210
main(int arc,char ** argv)211 int main(int arc, char **argv)
212 {
213
214 /*
215 * We write test records to a VSTRING, then read with delivered_hdr_init.
216 */
217 VSTRING *mem_bp;
218 VSTREAM *mem_fp;
219 DELIVERED_HDR_INFO *dp;
220 struct test_case {
221 int rec_type;
222 const char *content;
223 int expect_find;
224 };
225 const struct test_case test_cases[] = {
226 REC_TYPE_CONT, "Delivered-To: one", 1,
227 REC_TYPE_NORM, "Delivered-To: two", 0,
228 REC_TYPE_NORM, "Delivered-To: three", 1,
229 0,
230 };
231 const struct test_case *tp;
232 int actual_find;
233 int errors;
234
235 msg_vstream_init(argv[0], VSTREAM_ERR);
236
237 var_drop_hdrs = mystrdup(DEF_DROP_HDRS);
238
239 mem_bp = vstring_alloc(VSTREAM_BUFSIZE);
240 if ((mem_fp = vstream_memopen(mem_bp, O_WRONLY)) == 0)
241 msg_panic("vstream_memopen O_WRONLY failed: %m");
242
243 #define REC_PUT_LIT(fp, type, lit) rec_put((fp), (type), (lit), strlen(lit))
244
245 for (tp = test_cases; tp->content != 0; tp++)
246 REC_PUT_LIT(mem_fp, tp->rec_type, tp->content);
247
248 if (vstream_fclose(mem_fp))
249 msg_panic("vstream_fclose fail: %m");
250
251 if ((mem_fp = vstream_memopen(mem_bp, O_RDONLY)) == 0)
252 msg_panic("vstream_memopen O_RDONLY failed: %m");
253
254 dp = delivered_hdr_init(mem_fp, 0, FOLD_ADDR_ALL);
255
256 for (errors = 0, tp = test_cases; tp->content != 0; tp++) {
257 actual_find =
258 delivered_hdr_find(dp, tp->content + sizeof("Delivered-To:"));
259 msg_info("test case: %c >%s<: %s (expected: %s)",
260 tp->rec_type, tp->content,
261 actual_find == tp->expect_find ? "PASS" : "FAIL",
262 tp->expect_find ? "MATCH" : "NO MATCH");
263 errors += (actual_find != tp->expect_find);;
264 }
265 exit(errors);
266 }
267
268 #endif
269