Bug Summary

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'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name to.c -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 -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -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/llvm16/lib/clang/16 -I /usr/src/usr.sbin/smtpd/smtpd/.. -D IO_TLS -D QUEUE_PROFILING -internal-isystem /usr/local/llvm16/lib/clang/16/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 -fcf-protection=branch -fno-jump-tables -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/scan/2024-01-11-140451-98009-1 -x c /usr/src/usr.sbin/smtpd/smtpd/../to.c
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
34static int alias_is_filter(struct expandnode *, const char *, size_t);
35static int alias_is_username(struct expandnode *, const char *, size_t);
36static int alias_is_address(struct expandnode *, const char *, size_t);
37static int alias_is_filename(struct expandnode *, const char *, size_t);
38static int alias_is_include(struct expandnode *, const char *, size_t);
39static int alias_is_error(struct expandnode *, const char *, size_t);
40
41const char *
42sockaddr_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
53int
54text_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
92const char *
93mailaddr_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
106const char *
107sa_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
133const char *
134ss_to_text(const struct sockaddr_storage *ss)
135{
136 return (sa_to_text((const struct sockaddr*)ss));
137}
138
139const char *
140time_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
172const char *
173duration_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
218int
219text_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
269int
270text_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
375uint64_t
376text_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
392uint32_t
393text_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
411const char *
412rule_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
505int
506text_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
549error:
550 return 0;
551}
552
553int
554text_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
587int
588text_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
604const char *
605expandnode_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/******/
624static int
625alias_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
649static int
650alias_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
669static int
670alias_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
713static int
714alias_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
728static int
729alias_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
749static int
750alias_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
783const char *
784tls_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