Bug Summary

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'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name to.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.sbin/smtpd/smtpd/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.sbin/smtpd/smtpd/.. -D IO_TLS -D QUEUE_PROFILING -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.sbin/smtpd/smtpd/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.sbin/smtpd/smtpd/../to.c
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
33static int alias_is_filter(struct expandnode *, const char *, size_t);
34static int alias_is_username(struct expandnode *, const char *, size_t);
35static int alias_is_address(struct expandnode *, const char *, size_t);
36static int alias_is_filename(struct expandnode *, const char *, size_t);
37static int alias_is_include(struct expandnode *, const char *, size_t);
38static int alias_is_error(struct expandnode *, const char *, size_t);
39
40const char *
41sockaddr_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
52int
53text_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
91const char *
92mailaddr_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
105const char *
106sa_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
132const char *
133ss_to_text(const struct sockaddr_storage *ss)
134{
135 return (sa_to_text((const struct sockaddr*)ss));
136}
137
138const char *
139time_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
171const char *
172duration_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
217int
218text_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
268int
269text_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
374uint64_t
375text_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
391uint32_t
392text_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
410const char *
411rule_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
504int
505text_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
548error:
549 return 0;
550}
551
552int
553text_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
586int
587text_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
603const char *
604expandnode_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/******/
623static int
624alias_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
648static int
649alias_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
668static int
669alias_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
712static int
713alias_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
727static int
728alias_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
748static int
749alias_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
782const char *
783tls_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