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