File: | src/usr.sbin/smtpd/smtpd/../to.c |
Warning: | line 331, column 8 Although the value stored to 'end' is used in the enclosing expression, the value is never actually read from 'end' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: to.c,v 1.50 2023/05/31 16:51:46 op Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> |
5 | * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> |
6 | * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> |
7 | * |
8 | * Permission to use, copy, modify, and distribute this software for any |
9 | * purpose with or without fee is hereby granted, provided that the above |
10 | * copyright notice and this permission notice appear in all copies. |
11 | * |
12 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
13 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
14 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
15 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
16 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
17 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
18 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
19 | */ |
20 | |
21 | #include <arpa/inet.h> |
22 | #include <ctype.h> |
23 | #include <errno(*__errno()).h> |
24 | #include <stdlib.h> |
25 | #include <string.h> |
26 | #include <time.h> |
27 | #if IO_TLS1 |
28 | #include <tls.h> |
29 | #endif |
30 | |
31 | #include "smtpd.h" |
32 | #include "log.h" |
33 | |
34 | static int alias_is_filter(struct expandnode *, const char *, size_t); |
35 | static int alias_is_username(struct expandnode *, const char *, size_t); |
36 | static int alias_is_address(struct expandnode *, const char *, size_t); |
37 | static int alias_is_filename(struct expandnode *, const char *, size_t); |
38 | static int alias_is_include(struct expandnode *, const char *, size_t); |
39 | static int alias_is_error(struct expandnode *, const char *, size_t); |
40 | |
41 | const char * |
42 | sockaddr_to_text(const struct sockaddr *sa) |
43 | { |
44 | static char buf[NI_MAXHOST256]; |
45 | |
46 | if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf), NULL((void *)0), 0, |
47 | NI_NUMERICHOST1)) |
48 | return ("(unknown)"); |
49 | else |
50 | return (buf); |
51 | } |
52 | |
53 | int |
54 | text_to_mailaddr(struct mailaddr *maddr, const char *email) |
55 | { |
56 | char *username; |
57 | char *hostname; |
58 | char buffer[LINE_MAX2048]; |
59 | |
60 | if (strlcpy(buffer, email, sizeof buffer) >= sizeof buffer) |
61 | return 0; |
62 | |
63 | memset(maddr, 0, sizeof *maddr); |
64 | |
65 | username = buffer; |
66 | hostname = strrchr(username, '@'); |
67 | |
68 | if (hostname == NULL((void *)0)) { |
69 | if (strlcpy(maddr->user, username, sizeof maddr->user) |
70 | >= sizeof maddr->user) |
71 | return 0; |
72 | } |
73 | else if (username == hostname) { |
74 | *hostname++ = '\0'; |
75 | if (strlcpy(maddr->domain, hostname, sizeof maddr->domain) |
76 | >= sizeof maddr->domain) |
77 | return 0; |
78 | } |
79 | else { |
80 | *hostname++ = '\0'; |
81 | if (strlcpy(maddr->user, username, sizeof maddr->user) |
82 | >= sizeof maddr->user) |
83 | return 0; |
84 | if (strlcpy(maddr->domain, hostname, sizeof maddr->domain) |
85 | >= sizeof maddr->domain) |
86 | return 0; |
87 | } |
88 | |
89 | return 1; |
90 | } |
91 | |
92 | const char * |
93 | mailaddr_to_text(const struct mailaddr *maddr) |
94 | { |
95 | static char buffer[LINE_MAX2048]; |
96 | |
97 | (void)strlcpy(buffer, maddr->user, sizeof buffer); |
98 | (void)strlcat(buffer, "@", sizeof buffer); |
99 | if (strlcat(buffer, maddr->domain, sizeof buffer) >= sizeof buffer) |
100 | return NULL((void *)0); |
101 | |
102 | return buffer; |
103 | } |
104 | |
105 | |
106 | const char * |
107 | sa_to_text(const struct sockaddr *sa) |
108 | { |
109 | static char buf[NI_MAXHOST256 + 5]; |
110 | char *p; |
111 | |
112 | buf[0] = '\0'; |
113 | p = buf; |
114 | |
115 | if (sa->sa_family == AF_LOCAL1) |
116 | (void)strlcpy(buf, "local", sizeof buf); |
117 | else if (sa->sa_family == AF_INET2) { |
118 | in_addr_t addr; |
119 | |
120 | addr = ((const struct sockaddr_in *)sa)->sin_addr.s_addr; |
121 | addr = ntohl(addr)(__uint32_t)(__builtin_constant_p(addr) ? (__uint32_t)(((__uint32_t )(addr) & 0xff) << 24 | ((__uint32_t)(addr) & 0xff00 ) << 8 | ((__uint32_t)(addr) & 0xff0000) >> 8 | ((__uint32_t)(addr) & 0xff000000) >> 24) : __swap32md (addr)); |
122 | (void)bsnprintf(p, NI_MAXHOST256, "%d.%d.%d.%d", |
123 | (addr >> 24) & 0xff, (addr >> 16) & 0xff, |
124 | (addr >> 8) & 0xff, addr & 0xff); |
125 | } |
126 | else if (sa->sa_family == AF_INET624) { |
127 | (void)bsnprintf(p, NI_MAXHOST256, "[%s]", sockaddr_to_text(sa)); |
128 | } |
129 | |
130 | return (buf); |
131 | } |
132 | |
133 | const char * |
134 | ss_to_text(const struct sockaddr_storage *ss) |
135 | { |
136 | return (sa_to_text((const struct sockaddr*)ss)); |
137 | } |
138 | |
139 | const char * |
140 | time_to_text(time_t when) |
141 | { |
142 | struct tm *lt; |
143 | static char buf[40]; |
144 | const char *day[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; |
145 | const char *month[] = {"Jan","Feb","Mar","Apr","May","Jun", |
146 | "Jul","Aug","Sep","Oct","Nov","Dec"}; |
147 | const char *tz; |
148 | long offset; |
149 | |
150 | lt = localtime(&when); |
151 | if (lt == NULL((void *)0) || when == 0) |
152 | fatalx("time_to_text: localtime"); |
153 | |
154 | offset = lt->tm_gmtoff; |
155 | tz = lt->tm_zone; |
156 | |
157 | /* We do not use strftime because it is subject to locale substitution*/ |
158 | if (!bsnprintf(buf, sizeof(buf), |
159 | "%s, %d %s %d %02d:%02d:%02d %c%02d%02d (%s)", |
160 | day[lt->tm_wday], lt->tm_mday, month[lt->tm_mon], |
161 | lt->tm_year + 1900, |
162 | lt->tm_hour, lt->tm_min, lt->tm_sec, |
163 | offset >= 0 ? '+' : '-', |
164 | abs((int)offset / 3600), |
165 | abs((int)offset % 3600) / 60, |
166 | tz)) |
167 | fatalx("time_to_text: bsnprintf"); |
168 | |
169 | return buf; |
170 | } |
171 | |
172 | const char * |
173 | duration_to_text(time_t t) |
174 | { |
175 | static char dst[64]; |
176 | char buf[64]; |
177 | int h, m, s; |
178 | long long d; |
179 | |
180 | if (t == 0) { |
181 | (void)strlcpy(dst, "0s", sizeof dst); |
182 | return (dst); |
183 | } |
184 | |
185 | dst[0] = '\0'; |
186 | if (t < 0) { |
187 | (void)strlcpy(dst, "-", sizeof dst); |
188 | t = -t; |
189 | } |
190 | |
191 | s = t % 60; |
192 | t /= 60; |
193 | m = t % 60; |
194 | t /= 60; |
195 | h = t % 24; |
196 | d = t / 24; |
197 | |
198 | if (d) { |
199 | (void)snprintf(buf, sizeof buf, "%lldd", d); |
200 | (void)strlcat(dst, buf, sizeof dst); |
201 | } |
202 | if (h) { |
203 | (void)snprintf(buf, sizeof buf, "%dh", h); |
204 | (void)strlcat(dst, buf, sizeof dst); |
205 | } |
206 | if (m) { |
207 | (void)snprintf(buf, sizeof buf, "%dm", m); |
208 | (void)strlcat(dst, buf, sizeof dst); |
209 | } |
210 | if (s) { |
211 | (void)snprintf(buf, sizeof buf, "%ds", s); |
212 | (void)strlcat(dst, buf, sizeof dst); |
213 | } |
214 | |
215 | return (dst); |
216 | } |
217 | |
218 | int |
219 | text_to_netaddr(struct netaddr *netaddr, const char *s) |
220 | { |
221 | struct sockaddr_storage ss; |
222 | struct sockaddr_in ssin; |
223 | struct sockaddr_in6 ssin6; |
224 | int bits; |
225 | char buf[NI_MAXHOST256]; |
226 | size_t len; |
227 | |
228 | memset(&ssin, 0, sizeof(struct sockaddr_in)); |
229 | memset(&ssin6, 0, sizeof(struct sockaddr_in6)); |
230 | |
231 | if (strncasecmp("IPv6:", s, 5) == 0) |
232 | s += 5; |
233 | |
234 | bits = inet_net_pton(AF_INET2, s, &ssin.sin_addr, |
235 | sizeof(struct in_addr)); |
236 | if (bits != -1) { |
237 | ssin.sin_family = AF_INET2; |
238 | memcpy(&ss, &ssin, sizeof(ssin)); |
239 | ss.ss_len = sizeof(struct sockaddr_in); |
240 | } else { |
241 | if (s[0] != '[') { |
242 | if ((len = strlcpy(buf, s, sizeof buf)) >= sizeof buf) |
243 | return 0; |
244 | } |
245 | else { |
246 | s++; |
247 | if (strncasecmp("IPv6:", s, 5) == 0) |
248 | s += 5; |
249 | if ((len = strlcpy(buf, s, sizeof buf)) >= sizeof buf) |
250 | return 0; |
251 | if (buf[len-1] != ']') |
252 | return 0; |
253 | buf[len-1] = 0; |
254 | } |
255 | bits = inet_net_pton(AF_INET624, buf, &ssin6.sin6_addr, |
256 | sizeof(struct in6_addr)); |
257 | if (bits == -1) |
258 | return 0; |
259 | ssin6.sin6_family = AF_INET624; |
260 | memcpy(&ss, &ssin6, sizeof(ssin6)); |
261 | ss.ss_len = sizeof(struct sockaddr_in6); |
262 | } |
263 | |
264 | netaddr->ss = ss; |
265 | netaddr->bits = bits; |
266 | return 1; |
267 | } |
268 | |
269 | int |
270 | text_to_relayhost(struct relayhost *relay, const char *s) |
271 | { |
272 | static const struct schema { |
273 | const char *name; |
274 | int tls; |
275 | uint16_t flags; |
276 | uint16_t port; |
277 | } schemas [] = { |
278 | /* |
279 | * new schemas should be *appended* otherwise the default |
280 | * schema index needs to be updated later in this function. |
281 | */ |
282 | { "smtp://", RELAY_TLS_OPPORTUNISTIC0, 0, 25 }, |
283 | { "smtp+tls://", RELAY_TLS_STARTTLS1, 0, 25 }, |
284 | { "smtp+notls://", RELAY_TLS_NO3, 0, 25 }, |
285 | /* need to specify an explicit port for LMTP */ |
286 | { "lmtp://", RELAY_TLS_NO3, RELAY_LMTP0x80, 0 }, |
287 | { "smtps://", RELAY_TLS_SMTPS2, 0, 465 } |
288 | }; |
289 | const char *errstr = NULL((void *)0); |
290 | char *p, *q; |
291 | char buffer[1024]; |
292 | char *beg, *end; |
293 | size_t i; |
294 | size_t len; |
295 | |
296 | memset(buffer, 0, sizeof buffer); |
297 | if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer) |
298 | return 0; |
299 | |
300 | for (i = 0; i < nitems(schemas)(sizeof((schemas)) / sizeof((schemas)[0])); ++i) |
301 | if (strncasecmp(schemas[i].name, s, |
302 | strlen(schemas[i].name)) == 0) |
303 | break; |
304 | |
305 | if (i == nitems(schemas)(sizeof((schemas)) / sizeof((schemas)[0]))) { |
306 | /* there is a schema, but it's not recognized */ |
307 | if (strstr(buffer, "://")) |
308 | return 0; |
309 | |
310 | /* no schema, default to smtp:// */ |
311 | i = 0; |
312 | p = buffer; |
313 | } |
314 | else |
315 | p = buffer + strlen(schemas[i].name); |
316 | |
317 | relay->tls = schemas[i].tls; |
318 | relay->flags = schemas[i].flags; |
319 | relay->port = schemas[i].port; |
320 | |
321 | /* first, we extract the label if any */ |
322 | if ((q = strchr(p, '@')) != NULL((void *)0)) { |
323 | *q = 0; |
324 | if (strlcpy(relay->authlabel, p, sizeof (relay->authlabel)) |
325 | >= sizeof (relay->authlabel)) |
326 | return 0; |
327 | p = q + 1; |
328 | } |
329 | |
330 | /* then, we extract the mail exchanger */ |
331 | beg = end = p; |
Although the value stored to 'end' is used in the enclosing expression, the value is never actually read from 'end' | |
332 | if (*beg == '[') { |
333 | if ((end = strchr(beg, ']')) == NULL((void *)0)) |
334 | return 0; |
335 | /* skip ']', it has to be included in the relay hostname */ |
336 | ++end; |
337 | len = end - beg; |
338 | } |
339 | else { |
340 | for (end = beg; *end; ++end) |
341 | if (!isalnum((unsigned char)*end) && |
342 | *end != '_' && *end != '.' && *end != '-') |
343 | break; |
344 | len = end - beg; |
345 | } |
346 | if (len >= sizeof relay->hostname) |
347 | return 0; |
348 | for (i = 0; i < len; ++i) |
349 | relay->hostname[i] = beg[i]; |
350 | relay->hostname[i] = 0; |
351 | |
352 | /* finally, we extract the port */ |
353 | p = beg + len; |
354 | if (*p == ':') { |
355 | relay->port = strtonum(p+1, 1, IPPORT_HILASTAUTO65535, &errstr); |
356 | if (errstr) |
357 | return 0; |
358 | } |
359 | |
360 | if (!valid_domainpart(relay->hostname)) |
361 | return 0; |
362 | if ((relay->flags & RELAY_LMTP0x80) && (relay->port == 0)) |
363 | return 0; |
364 | if (relay->authlabel[0]) { |
365 | /* disallow auth on non-tls scheme. */ |
366 | if (relay->tls != RELAY_TLS_STARTTLS1 && |
367 | relay->tls != RELAY_TLS_SMTPS2) |
368 | return 0; |
369 | relay->flags |= RELAY_AUTH0x08; |
370 | } |
371 | |
372 | return 1; |
373 | } |
374 | |
375 | uint64_t |
376 | text_to_evpid(const char *s) |
377 | { |
378 | uint64_t ulval; |
379 | char *ep; |
380 | |
381 | errno(*__errno()) = 0; |
382 | ulval = strtoull(s, &ep, 16); |
383 | if (s[0] == '\0' || *ep != '\0') |
384 | return 0; |
385 | if (errno(*__errno()) == ERANGE34 && ulval == ULLONG_MAX0xffffffffffffffffULL) |
386 | return 0; |
387 | if (ulval == 0) |
388 | return 0; |
389 | return (ulval); |
390 | } |
391 | |
392 | uint32_t |
393 | text_to_msgid(const char *s) |
394 | { |
395 | uint64_t ulval; |
396 | char *ep; |
397 | |
398 | errno(*__errno()) = 0; |
399 | ulval = strtoull(s, &ep, 16); |
400 | if (s[0] == '\0' || *ep != '\0') |
401 | return 0; |
402 | if (errno(*__errno()) == ERANGE34 && ulval == ULLONG_MAX0xffffffffffffffffULL) |
403 | return 0; |
404 | if (ulval == 0) |
405 | return 0; |
406 | if (ulval > 0xffffffff) |
407 | return 0; |
408 | return (ulval & 0xffffffff); |
409 | } |
410 | |
411 | const char * |
412 | rule_to_text(struct rule *r) |
413 | { |
414 | static char buf[4096]; |
415 | |
416 | memset(buf, 0, sizeof buf); |
417 | (void)strlcpy(buf, "match", sizeof buf); |
418 | if (r->flag_tag) { |
419 | if (r->flag_tag < 0) |
420 | (void)strlcat(buf, " !", sizeof buf); |
421 | (void)strlcat(buf, " tag ", sizeof buf); |
422 | (void)strlcat(buf, r->table_tag, sizeof buf); |
423 | } |
424 | |
425 | if (r->flag_from) { |
426 | if (r->flag_from < 0) |
427 | (void)strlcat(buf, " !", sizeof buf); |
428 | if (r->flag_from_socket) |
429 | (void)strlcat(buf, " from socket", sizeof buf); |
430 | else if (r->flag_from_rdns) { |
431 | (void)strlcat(buf, " from rdns", sizeof buf); |
432 | if (r->table_from) { |
433 | (void)strlcat(buf, " ", sizeof buf); |
434 | (void)strlcat(buf, r->table_from, sizeof buf); |
435 | } |
436 | } |
437 | else if (strcmp(r->table_from, "<anyhost>") == 0) |
438 | (void)strlcat(buf, " from any", sizeof buf); |
439 | else if (strcmp(r->table_from, "<localhost>") == 0) |
440 | (void)strlcat(buf, " from local", sizeof buf); |
441 | else { |
442 | (void)strlcat(buf, " from src ", sizeof buf); |
443 | (void)strlcat(buf, r->table_from, sizeof buf); |
444 | } |
445 | } |
446 | |
447 | if (r->flag_for) { |
448 | if (r->flag_for < 0) |
449 | (void)strlcat(buf, " !", sizeof buf); |
450 | if (strcmp(r->table_for, "<anydestination>") == 0) |
451 | (void)strlcat(buf, " for any", sizeof buf); |
452 | else if (strcmp(r->table_for, "<localnames>") == 0) |
453 | (void)strlcat(buf, " for local", sizeof buf); |
454 | else { |
455 | (void)strlcat(buf, " for domain ", sizeof buf); |
456 | (void)strlcat(buf, r->table_for, sizeof buf); |
457 | } |
458 | } |
459 | |
460 | if (r->flag_smtp_helo) { |
461 | if (r->flag_smtp_helo < 0) |
462 | (void)strlcat(buf, " !", sizeof buf); |
463 | (void)strlcat(buf, " helo ", sizeof buf); |
464 | (void)strlcat(buf, r->table_smtp_helo, sizeof buf); |
465 | } |
466 | |
467 | if (r->flag_smtp_auth) { |
468 | if (r->flag_smtp_auth < 0) |
469 | (void)strlcat(buf, " !", sizeof buf); |
470 | (void)strlcat(buf, " auth", sizeof buf); |
471 | if (r->table_smtp_auth) { |
472 | (void)strlcat(buf, " ", sizeof buf); |
473 | (void)strlcat(buf, r->table_smtp_auth, sizeof buf); |
474 | } |
475 | } |
476 | |
477 | if (r->flag_smtp_starttls) { |
478 | if (r->flag_smtp_starttls < 0) |
479 | (void)strlcat(buf, " !", sizeof buf); |
480 | (void)strlcat(buf, " tls", sizeof buf); |
481 | } |
482 | |
483 | if (r->flag_smtp_mail_from) { |
484 | if (r->flag_smtp_mail_from < 0) |
485 | (void)strlcat(buf, " !", sizeof buf); |
486 | (void)strlcat(buf, " mail-from ", sizeof buf); |
487 | (void)strlcat(buf, r->table_smtp_mail_from, sizeof buf); |
488 | } |
489 | |
490 | if (r->flag_smtp_rcpt_to) { |
491 | if (r->flag_smtp_rcpt_to < 0) |
492 | (void)strlcat(buf, " !", sizeof buf); |
493 | (void)strlcat(buf, " rcpt-to ", sizeof buf); |
494 | (void)strlcat(buf, r->table_smtp_rcpt_to, sizeof buf); |
495 | } |
496 | (void)strlcat(buf, " action ", sizeof buf); |
497 | if (r->reject) |
498 | (void)strlcat(buf, "reject", sizeof buf); |
499 | else |
500 | (void)strlcat(buf, r->dispatcher, sizeof buf); |
501 | return buf; |
502 | } |
503 | |
504 | |
505 | int |
506 | text_to_userinfo(struct userinfo *userinfo, const char *s) |
507 | { |
508 | char buf[PATH_MAX1024]; |
509 | char *p; |
510 | const char *errstr; |
511 | |
512 | memset(buf, 0, sizeof buf); |
513 | p = buf; |
514 | while (*s && *s != ':') |
515 | *p++ = *s++; |
516 | if (*s++ != ':') |
517 | goto error; |
518 | |
519 | if (strlcpy(userinfo->username, buf, |
520 | sizeof userinfo->username) >= sizeof userinfo->username) |
521 | goto error; |
522 | |
523 | memset(buf, 0, sizeof buf); |
524 | p = buf; |
525 | while (*s && *s != ':') |
526 | *p++ = *s++; |
527 | if (*s++ != ':') |
528 | goto error; |
529 | userinfo->uid = strtonum(buf, 0, UID_MAX0xffffffffU, &errstr); |
530 | if (errstr) |
531 | goto error; |
532 | |
533 | memset(buf, 0, sizeof buf); |
534 | p = buf; |
535 | while (*s && *s != ':') |
536 | *p++ = *s++; |
537 | if (*s++ != ':') |
538 | goto error; |
539 | userinfo->gid = strtonum(buf, 0, GID_MAX0xffffffffU, &errstr); |
540 | if (errstr) |
541 | goto error; |
542 | |
543 | if (strlcpy(userinfo->directory, s, |
544 | sizeof userinfo->directory) >= sizeof userinfo->directory) |
545 | goto error; |
546 | |
547 | return 1; |
548 | |
549 | error: |
550 | return 0; |
551 | } |
552 | |
553 | int |
554 | text_to_credentials(struct credentials *creds, const char *s) |
555 | { |
556 | char *p; |
557 | char buffer[LINE_MAX2048]; |
558 | size_t offset; |
559 | |
560 | p = strchr(s, ':'); |
561 | if (p == NULL((void *)0)) { |
562 | creds->username[0] = '\0'; |
563 | if (strlcpy(creds->password, s, sizeof creds->password) |
564 | >= sizeof creds->password) |
565 | return 0; |
566 | return 1; |
567 | } |
568 | |
569 | offset = p - s; |
570 | |
571 | memset(buffer, 0, sizeof buffer); |
572 | if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer) |
573 | return 0; |
574 | p = buffer + offset; |
575 | *p = '\0'; |
576 | |
577 | if (strlcpy(creds->username, buffer, sizeof creds->username) |
578 | >= sizeof creds->username) |
579 | return 0; |
580 | if (strlcpy(creds->password, p+1, sizeof creds->password) |
581 | >= sizeof creds->password) |
582 | return 0; |
583 | |
584 | return 1; |
585 | } |
586 | |
587 | int |
588 | text_to_expandnode(struct expandnode *expandnode, const char *s) |
589 | { |
590 | size_t l; |
591 | |
592 | l = strlen(s); |
593 | if (alias_is_error(expandnode, s, l) || |
594 | alias_is_include(expandnode, s, l) || |
595 | alias_is_filter(expandnode, s, l) || |
596 | alias_is_filename(expandnode, s, l) || |
597 | alias_is_address(expandnode, s, l) || |
598 | alias_is_username(expandnode, s, l)) |
599 | return (1); |
600 | |
601 | return (0); |
602 | } |
603 | |
604 | const char * |
605 | expandnode_to_text(struct expandnode *expandnode) |
606 | { |
607 | switch (expandnode->type) { |
608 | case EXPAND_FILTER: |
609 | case EXPAND_FILENAME: |
610 | case EXPAND_INCLUDE: |
611 | case EXPAND_ERROR: |
612 | case EXPAND_USERNAME: |
613 | return expandnode->u.user; |
614 | case EXPAND_ADDRESS: |
615 | return mailaddr_to_text(&expandnode->u.mailaddr); |
616 | case EXPAND_INVALID: |
617 | break; |
618 | } |
619 | |
620 | return NULL((void *)0); |
621 | } |
622 | |
623 | /******/ |
624 | static int |
625 | alias_is_filter(struct expandnode *alias, const char *line, size_t len) |
626 | { |
627 | int v = 0; |
628 | |
629 | if (*line == '"') |
630 | v = 1; |
631 | if (*(line+v) == '|') { |
632 | if (strlcpy(alias->u.buffer, line + v + 1, |
633 | sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer)) |
634 | return 0; |
635 | if (v) { |
636 | v = strlen(alias->u.buffer); |
637 | if (v == 0) |
638 | return (0); |
639 | if (alias->u.buffer[v-1] != '"') |
640 | return (0); |
641 | alias->u.buffer[v-1] = '\0'; |
642 | } |
643 | alias->type = EXPAND_FILTER; |
644 | return (1); |
645 | } |
646 | return (0); |
647 | } |
648 | |
649 | static int |
650 | alias_is_username(struct expandnode *alias, const char *line, size_t len) |
651 | { |
652 | memset(alias, 0, sizeof *alias); |
653 | |
654 | if (strlcpy(alias->u.user, line, |
655 | sizeof(alias->u.user)) >= sizeof(alias->u.user)) |
656 | return 0; |
657 | |
658 | while (*line) { |
659 | if (!isalnum((unsigned char)*line) && |
660 | *line != '_' && *line != '.' && *line != '-' && *line != '+') |
661 | return 0; |
662 | ++line; |
663 | } |
664 | |
665 | alias->type = EXPAND_USERNAME; |
666 | return 1; |
667 | } |
668 | |
669 | static int |
670 | alias_is_address(struct expandnode *alias, const char *line, size_t len) |
671 | { |
672 | char *domain; |
673 | |
674 | memset(alias, 0, sizeof *alias); |
675 | |
676 | if (len < 3) /* x@y */ |
677 | return 0; |
678 | |
679 | domain = strchr(line, '@'); |
680 | if (domain == NULL((void *)0)) |
681 | return 0; |
682 | |
683 | /* @ cannot start or end an address */ |
684 | if (domain == line || domain == line + len - 1) |
685 | return 0; |
686 | |
687 | /* scan pre @ for disallowed chars */ |
688 | *domain++ = '\0'; |
689 | (void)strlcpy(alias->u.mailaddr.user, line, sizeof(alias->u.mailaddr.user)); |
690 | (void)strlcpy(alias->u.mailaddr.domain, domain, |
691 | sizeof(alias->u.mailaddr.domain)); |
692 | |
693 | while (*line) { |
694 | char allowedset[] = "!#$%*/?|^{}`~&'+-=_."; |
695 | if (!isalnum((unsigned char)*line) && |
696 | strchr(allowedset, *line) == NULL((void *)0)) |
697 | return 0; |
698 | ++line; |
699 | } |
700 | |
701 | while (*domain) { |
702 | char allowedset[] = "-."; |
703 | if (!isalnum((unsigned char)*domain) && |
704 | strchr(allowedset, *domain) == NULL((void *)0)) |
705 | return 0; |
706 | ++domain; |
707 | } |
708 | |
709 | alias->type = EXPAND_ADDRESS; |
710 | return 1; |
711 | } |
712 | |
713 | static int |
714 | alias_is_filename(struct expandnode *alias, const char *line, size_t len) |
715 | { |
716 | memset(alias, 0, sizeof *alias); |
717 | |
718 | if (*line != '/') |
719 | return 0; |
720 | |
721 | if (strlcpy(alias->u.buffer, line, |
722 | sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer)) |
723 | return 0; |
724 | alias->type = EXPAND_FILENAME; |
725 | return 1; |
726 | } |
727 | |
728 | static int |
729 | alias_is_include(struct expandnode *alias, const char *line, size_t len) |
730 | { |
731 | size_t skip; |
732 | |
733 | memset(alias, 0, sizeof *alias); |
734 | |
735 | if (strncasecmp(":include:", line, 9) == 0) |
736 | skip = 9; |
737 | else if (strncasecmp("include:", line, 8) == 0) |
738 | skip = 8; |
739 | else |
740 | return 0; |
741 | |
742 | if (!alias_is_filename(alias, line + skip, len - skip)) |
743 | return 0; |
744 | |
745 | alias->type = EXPAND_INCLUDE; |
746 | return 1; |
747 | } |
748 | |
749 | static int |
750 | alias_is_error(struct expandnode *alias, const char *line, size_t len) |
751 | { |
752 | size_t skip; |
753 | |
754 | memset(alias, 0, sizeof *alias); |
755 | |
756 | if (strncasecmp(":error:", line, 7) == 0) |
757 | skip = 7; |
758 | else if (strncasecmp("error:", line, 6) == 0) |
759 | skip = 6; |
760 | else |
761 | return 0; |
762 | |
763 | if (strlcpy(alias->u.buffer, line + skip, |
764 | sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer)) |
765 | return 0; |
766 | |
767 | if (strlen(alias->u.buffer) < 5) |
768 | return 0; |
769 | |
770 | /* [45][0-9]{2} [a-zA-Z0-9].* */ |
771 | if (alias->u.buffer[3] != ' ' || |
772 | !isalnum((unsigned char)alias->u.buffer[4]) || |
773 | (alias->u.buffer[0] != '4' && alias->u.buffer[0] != '5') || |
774 | !isdigit((unsigned char)alias->u.buffer[1]) || |
775 | !isdigit((unsigned char)alias->u.buffer[2])) |
776 | return 0; |
777 | |
778 | alias->type = EXPAND_ERROR; |
779 | return 1; |
780 | } |
781 | |
782 | #if IO_TLS1 |
783 | const char * |
784 | tls_to_text(struct tls *tls) |
785 | { |
786 | static char buf[256]; |
787 | |
788 | (void)snprintf(buf, sizeof buf, "%s:%s:%d", |
789 | tls_conn_version(tls), |
790 | tls_conn_cipher(tls), |
791 | tls_conn_cipher_strength(tls)); |
792 | |
793 | return (buf); |
794 | } |
795 | #endif |