xref: /openbsd/usr.sbin/smtpd/table.c (revision 2ee83f25)
1 /*	$OpenBSD: table.c,v 1.53 2024/05/28 07:10:30 op Exp $	*/
2 
3 /*
4  * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
5  * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/stat.h>
21 
22 #include <net/if.h>
23 
24 #include <arpa/inet.h>
25 #include <errno.h>
26 #include <regex.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "smtpd.h"
31 #include "log.h"
32 
33 struct table_backend *table_backend_lookup(const char *);
34 
35 extern struct table_backend table_backend_static;
36 extern struct table_backend table_backend_db;
37 extern struct table_backend table_backend_getpwnam;
38 extern struct table_backend table_backend_proc;
39 
40 static int table_parse_lookup(enum table_service, const char *, const char *,
41     union lookup *);
42 static int parse_sockaddr(struct sockaddr *, int, const char *);
43 
44 static unsigned int last_table_id = 0;
45 
46 static struct table_backend *backends[] = {
47 	&table_backend_static,
48 	&table_backend_db,
49 	&table_backend_getpwnam,
50 	&table_backend_proc,
51 	NULL
52 };
53 
54 struct table_backend *
table_backend_lookup(const char * backend)55 table_backend_lookup(const char *backend)
56 {
57 	int i;
58 
59 	if (!strcmp(backend, "file"))
60 		backend = "static";
61 
62 	for (i = 0; backends[i]; i++)
63 		if (!strcmp(backends[i]->name, backend))
64 			return (backends[i]);
65 
66 	return NULL;
67 }
68 
69 const char *
table_service_name(enum table_service s)70 table_service_name(enum table_service s)
71 {
72 	switch (s) {
73 	case K_NONE:		return "none";
74 	case K_ALIAS:		return "alias";
75 	case K_DOMAIN:		return "domain";
76 	case K_CREDENTIALS:	return "credentials";
77 	case K_NETADDR:		return "netaddr";
78 	case K_USERINFO:	return "userinfo";
79 	case K_SOURCE:		return "source";
80 	case K_MAILADDR:	return "mailaddr";
81 	case K_ADDRNAME:	return "addrname";
82 	case K_MAILADDRMAP:	return "mailaddrmap";
83 	case K_RELAYHOST:	return "relayhost";
84 	case K_STRING:		return "string";
85 	case K_REGEX:		return "regex";
86 	}
87 	return "???";
88 }
89 
90 int
table_service_from_name(const char * service)91 table_service_from_name(const char *service)
92 {
93 	if (!strcmp(service, "none"))
94 		return K_NONE;
95 	if (!strcmp(service, "alias"))
96 		return K_ALIAS;
97 	if (!strcmp(service, "domain"))
98 		return K_DOMAIN;
99 	if (!strcmp(service, "credentials"))
100 		return K_CREDENTIALS;
101 	if (!strcmp(service, "netaddr"))
102 		return K_NETADDR;
103 	if (!strcmp(service, "userinfo"))
104 		return K_USERINFO;
105 	if (!strcmp(service, "source"))
106 		return K_SOURCE;
107 	if (!strcmp(service, "mailaddr"))
108 		return K_MAILADDR;
109 	if (!strcmp(service, "addrname"))
110 		return K_ADDRNAME;
111 	if (!strcmp(service, "mailaddrmap"))
112 		return K_MAILADDRMAP;
113 	if (!strcmp(service, "relayhost"))
114 		return K_RELAYHOST;
115 	if (!strcmp(service, "string"))
116 		return K_STRING;
117 	if (!strcmp(service, "regex"))
118 		return K_REGEX;
119 	return (-1);
120 }
121 
122 struct table *
table_find(struct smtpd * conf,const char * name)123 table_find(struct smtpd *conf, const char *name)
124 {
125 	return dict_get(conf->sc_tables_dict, name);
126 }
127 
128 int
table_match(struct table * table,enum table_service kind,const char * key)129 table_match(struct table *table, enum table_service kind, const char *key)
130 {
131 	return table_lookup(table, kind, key, NULL);
132 }
133 
134 int
table_lookup(struct table * table,enum table_service kind,const char * key,union lookup * lk)135 table_lookup(struct table *table, enum table_service kind, const char *key,
136     union lookup *lk)
137 {
138 	char lkey[1024], *buf = NULL;
139 	int r;
140 
141 	r = -1;
142 	if (table->t_backend->lookup == NULL)
143 		errno = ENOTSUP;
144 	else if (!lowercase(lkey, key, sizeof lkey)) {
145 		log_warnx("warn: lookup key too long: %s", key);
146 		errno = EINVAL;
147 	}
148 	else
149 		r = table->t_backend->lookup(table, kind, lkey, lk ? &buf : NULL);
150 
151 	if (r == 1) {
152 		log_trace(TRACE_LOOKUP, "lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s",
153 		    lk ? "lookup" : "match",
154 		    key,
155 		    table_service_name(kind),
156 		    table->t_backend->name,
157 		    table->t_name,
158 		    lk ? "\"" : "",
159 		    lk ? buf : "true",
160 		    lk ? "\"" : "");
161 		if (buf)
162 			r = table_parse_lookup(kind, lkey, buf, lk);
163 	}
164 	else
165 		log_trace(TRACE_LOOKUP, "lookup: %s \"%s\" as %s in table %s:%s -> %s%s",
166 		    lk ? "lookup" : "match",
167 		    key,
168 		    table_service_name(kind),
169 		    table->t_backend->name,
170 		    table->t_name,
171 		    (r == -1) ? "error: " : (lk ? "none" : "false"),
172 		    (r == -1) ? strerror(errno) : "");
173 
174 	free(buf);
175 
176 	return (r);
177 }
178 
179 int
table_fetch(struct table * table,enum table_service kind,union lookup * lk)180 table_fetch(struct table *table, enum table_service kind, union lookup *lk)
181 {
182 	char *buf = NULL;
183 	int r;
184 
185 	r = -1;
186 	if (table->t_backend->fetch == NULL)
187 		errno = ENOTSUP;
188 	else
189 		r = table->t_backend->fetch(table, kind, &buf);
190 
191 	if (r == 1) {
192 		log_trace(TRACE_LOOKUP, "lookup: fetch %s from table %s:%s -> \"%s\"",
193 		    table_service_name(kind),
194 		    table->t_backend->name,
195 		    table->t_name,
196 		    buf);
197 		r = table_parse_lookup(kind, NULL, buf, lk);
198 	}
199 	else
200 		log_trace(TRACE_LOOKUP, "lookup: fetch %s from table %s:%s -> %s%s",
201 		    table_service_name(kind),
202 		    table->t_backend->name,
203 		    table->t_name,
204 		    (r == -1) ? "error: " : "none",
205 		    (r == -1) ? strerror(errno) : "");
206 
207 	free(buf);
208 
209 	return (r);
210 }
211 
212 struct table *
table_create(struct smtpd * conf,const char * backend,const char * name,const char * config)213 table_create(struct smtpd *conf, const char *backend, const char *name,
214     const char *config)
215 {
216 	struct table		*t;
217 	struct table_backend	*tb;
218 	char			 path[LINE_MAX];
219 	size_t			 n;
220 	struct stat		 sb;
221 
222 	if (name && table_find(conf, name))
223 		fatalx("table_create: table \"%s\" already defined", name);
224 
225 	if ((tb = table_backend_lookup(backend)) == NULL) {
226 		if ((size_t)snprintf(path, sizeof(path), PATH_LIBEXEC"/table-%s",
227 			backend) >= sizeof(path)) {
228 			fatalx("table_create: path too long \""
229 			    PATH_LIBEXEC"/table-%s\"", backend);
230 		}
231 		if (stat(path, &sb) == 0) {
232 			tb = table_backend_lookup("proc");
233 			(void)strlcpy(path, backend, sizeof(path));
234 			if (config) {
235 				(void)strlcat(path, ":", sizeof(path));
236 				if (strlcat(path, config, sizeof(path))
237 				    >= sizeof(path))
238 					fatalx("table_create: config file path too long");
239 			}
240 			config = path;
241 		}
242 	}
243 
244 	if (tb == NULL)
245 		fatalx("table_create: backend \"%s\" does not exist", backend);
246 
247 	t = xcalloc(1, sizeof(*t));
248 	t->t_services = tb->services;
249 	t->t_backend = tb;
250 
251 	if (config) {
252 		if (strlcpy(t->t_config, config, sizeof t->t_config)
253 		    >= sizeof t->t_config)
254 			fatalx("table_create: table config \"%s\" too large",
255 			    t->t_config);
256 	}
257 
258 	if (strcmp(tb->name, "static") != 0)
259 		t->t_type = T_DYNAMIC;
260 
261 	if (name == NULL)
262 		(void)snprintf(t->t_name, sizeof(t->t_name), "<dynamic:%u>",
263 		    last_table_id++);
264 	else {
265 		n = strlcpy(t->t_name, name, sizeof(t->t_name));
266 		if (n >= sizeof(t->t_name))
267 			fatalx("table_create: table name too long");
268 	}
269 
270 	dict_set(conf->sc_tables_dict, t->t_name, t);
271 
272 	return (t);
273 }
274 
275 void
table_destroy(struct smtpd * conf,struct table * t)276 table_destroy(struct smtpd *conf, struct table *t)
277 {
278 	dict_xpop(conf->sc_tables_dict, t->t_name);
279 	free(t);
280 }
281 
282 int
table_config(struct table * t)283 table_config(struct table *t)
284 {
285 	if (t->t_backend->config == NULL)
286 		return (1);
287 	return (t->t_backend->config(t));
288 }
289 
290 void
table_add(struct table * t,const char * key,const char * val)291 table_add(struct table *t, const char *key, const char *val)
292 {
293 	if (t->t_backend->add == NULL)
294 		fatalx("table_add: cannot add to table");
295 
296 	if (t->t_backend->add(t, key, val) == 0)
297 		log_warnx("warn: failed to add \"%s\" in table \"%s\"", key, t->t_name);
298 }
299 
300 void
table_dump(struct table * t)301 table_dump(struct table *t)
302 {
303 	const char *type;
304 	char buf[LINE_MAX];
305 
306 	switch(t->t_type) {
307 	case T_NONE:
308 		type = "NONE";
309 		break;
310 	case T_DYNAMIC:
311 		type = "DYNAMIC";
312 		break;
313 	case T_LIST:
314 		type = "LIST";
315 		break;
316 	case T_HASH:
317 		type = "HASH";
318 		break;
319 	default:
320 		type = "???";
321 		break;
322 	}
323 
324 	if (t->t_config[0])
325 		snprintf(buf, sizeof(buf), " config=\"%s\"", t->t_config);
326 	else
327 		buf[0] = '\0';
328 
329 	log_debug("TABLE \"%s\" backend=%s type=%s%s", t->t_name,
330 	    t->t_backend->name, type, buf);
331 
332 	if (t->t_backend->dump)
333 		t->t_backend->dump(t);
334 }
335 
336 int
table_check_type(struct table * t,uint32_t mask)337 table_check_type(struct table *t, uint32_t mask)
338 {
339 	return t->t_type & mask;
340 }
341 
342 int
table_check_service(struct table * t,uint32_t mask)343 table_check_service(struct table *t, uint32_t mask)
344 {
345 	return t->t_services & mask;
346 }
347 
348 int
table_check_use(struct table * t,uint32_t tmask,uint32_t smask)349 table_check_use(struct table *t, uint32_t tmask, uint32_t smask)
350 {
351 	return table_check_type(t, tmask) && table_check_service(t, smask);
352 }
353 
354 int
table_open(struct table * t)355 table_open(struct table *t)
356 {
357 	if (t->t_backend->open == NULL)
358 		return (1);
359 	return (t->t_backend->open(t));
360 }
361 
362 void
table_close(struct table * t)363 table_close(struct table *t)
364 {
365 	if (t->t_backend->close)
366 		t->t_backend->close(t);
367 }
368 
369 int
table_update(struct table * t)370 table_update(struct table *t)
371 {
372 	if (t->t_backend->update == NULL)
373 		return (1);
374 	return (t->t_backend->update(t));
375 }
376 
377 
378 /*
379  * quick reminder:
380  * in *_match() s1 comes from session, s2 comes from table
381  */
382 
383 int
table_domain_match(const char * s1,const char * s2)384 table_domain_match(const char *s1, const char *s2)
385 {
386 	return hostname_match(s1, s2);
387 }
388 
389 int
table_mailaddr_match(const char * s1,const char * s2)390 table_mailaddr_match(const char *s1, const char *s2)
391 {
392 	struct mailaddr m1;
393 	struct mailaddr m2;
394 
395 	if (!text_to_mailaddr(&m1, s1))
396 		return 0;
397 	if (!text_to_mailaddr(&m2, s2))
398 		return 0;
399 	return mailaddr_match(&m1, &m2);
400 }
401 
402 static int table_match_mask(struct sockaddr_storage *, struct netaddr *);
403 static int table_inet4_match(struct sockaddr_in *, struct netaddr *);
404 static int table_inet6_match(struct sockaddr_in6 *, struct netaddr *);
405 
406 int
table_netaddr_match(const char * s1,const char * s2)407 table_netaddr_match(const char *s1, const char *s2)
408 {
409 	struct netaddr n1;
410 	struct netaddr n2;
411 
412 	if (strcasecmp(s1, s2) == 0)
413 		return 1;
414 	if (!text_to_netaddr(&n1, s1))
415 		return 0;
416 	if (!text_to_netaddr(&n2, s2))
417 		return 0;
418 	if (n1.ss.ss_family != n2.ss.ss_family)
419 		return 0;
420 	if (n1.ss.ss_len != n2.ss.ss_len)
421 		return 0;
422 	return table_match_mask(&n1.ss, &n2);
423 }
424 
425 static int
table_match_mask(struct sockaddr_storage * ss,struct netaddr * ssmask)426 table_match_mask(struct sockaddr_storage *ss, struct netaddr *ssmask)
427 {
428 	if (ss->ss_family == AF_INET)
429 		return table_inet4_match((struct sockaddr_in *)ss, ssmask);
430 
431 	if (ss->ss_family == AF_INET6)
432 		return table_inet6_match((struct sockaddr_in6 *)ss, ssmask);
433 
434 	return (0);
435 }
436 
437 static int
table_inet4_match(struct sockaddr_in * ss,struct netaddr * ssmask)438 table_inet4_match(struct sockaddr_in *ss, struct netaddr *ssmask)
439 {
440 	in_addr_t mask;
441 	int i;
442 
443 	/* a.b.c.d/8 -> htonl(0xff000000) */
444 	mask = 0;
445 	for (i = 0; i < ssmask->bits; ++i)
446 		mask = (mask >> 1) | 0x80000000;
447 	mask = htonl(mask);
448 
449 	/* (addr & mask) == (net & mask) */
450 	if ((ss->sin_addr.s_addr & mask) ==
451 	    (((struct sockaddr_in *)ssmask)->sin_addr.s_addr & mask))
452 		return 1;
453 
454 	return 0;
455 }
456 
457 static int
table_inet6_match(struct sockaddr_in6 * ss,struct netaddr * ssmask)458 table_inet6_match(struct sockaddr_in6 *ss, struct netaddr *ssmask)
459 {
460 	struct in6_addr	*in;
461 	struct in6_addr	*inmask;
462 	struct in6_addr	 mask;
463 	int		 i;
464 
465 	memset(&mask, 0, sizeof(mask));
466 	for (i = 0; i < ssmask->bits / 8; i++)
467 		mask.s6_addr[i] = 0xff;
468 	i = ssmask->bits % 8;
469 	if (i)
470 		mask.s6_addr[ssmask->bits / 8] = 0xff00 >> i;
471 
472 	in = &ss->sin6_addr;
473 	inmask = &((struct sockaddr_in6 *)&ssmask->ss)->sin6_addr;
474 
475 	for (i = 0; i < 16; i++) {
476 		if ((in->s6_addr[i] & mask.s6_addr[i]) !=
477 		    (inmask->s6_addr[i] & mask.s6_addr[i]))
478 			return (0);
479 	}
480 
481 	return (1);
482 }
483 
484 int
table_regex_match(const char * string,const char * pattern)485 table_regex_match(const char *string, const char *pattern)
486 {
487 	regex_t preg;
488 	int	cflags = REG_EXTENDED|REG_NOSUB;
489 	int ret;
490 
491 	if (strncmp(pattern, "(?i)", 4) == 0) {
492 		cflags |= REG_ICASE;
493 		pattern += 4;
494 	}
495 
496 	if (regcomp(&preg, pattern, cflags) != 0)
497 		return (0);
498 
499 	ret = regexec(&preg, string, 0, NULL, 0);
500 
501 	regfree(&preg);
502 
503 	if (ret != 0)
504 		return (0);
505 
506 	return (1);
507 }
508 
509 void
table_dump_all(struct smtpd * conf)510 table_dump_all(struct smtpd *conf)
511 {
512 	struct table	*t;
513 	void		*iter;
514 
515 	iter = NULL;
516 	while (dict_iter(conf->sc_tables_dict, &iter, NULL, (void **)&t))
517 		table_dump(t);
518 }
519 
520 void
table_open_all(struct smtpd * conf)521 table_open_all(struct smtpd *conf)
522 {
523 	struct table	*t;
524 	void		*iter;
525 
526 	iter = NULL;
527 	while (dict_iter(conf->sc_tables_dict, &iter, NULL, (void **)&t))
528 		if (!table_open(t))
529 			fatalx("failed to open table %s", t->t_name);
530 }
531 
532 void
table_close_all(struct smtpd * conf)533 table_close_all(struct smtpd *conf)
534 {
535 	struct table	*t;
536 	void		*iter;
537 
538 	iter = NULL;
539 	while (dict_iter(conf->sc_tables_dict, &iter, NULL, (void **)&t))
540 		table_close(t);
541 }
542 
543 static int
table_parse_lookup(enum table_service service,const char * key,const char * line,union lookup * lk)544 table_parse_lookup(enum table_service service, const char *key,
545     const char *line, union lookup *lk)
546 {
547 	char	buffer[LINE_MAX], *p;
548 	size_t	len;
549 
550 	len = strlen(line);
551 
552 	switch (service) {
553 	case K_ALIAS:
554 		lk->expand = calloc(1, sizeof(*lk->expand));
555 		if (lk->expand == NULL)
556 			return (-1);
557 		if (!expand_line(lk->expand, line, 1)) {
558 			expand_free(lk->expand);
559 			return (-1);
560 		}
561 		return (1);
562 
563 	case K_DOMAIN:
564 		if (strlcpy(lk->domain.name, line, sizeof(lk->domain.name))
565 		    >= sizeof(lk->domain.name))
566 			return (-1);
567 		return (1);
568 
569 	case K_CREDENTIALS:
570 
571 		/* credentials are stored as user:password */
572 		if (len < 3)
573 			return (-1);
574 
575 		/* too big to fit in a smtp session line */
576 		if (len >= LINE_MAX)
577 			return (-1);
578 
579 		p = strchr(line, ':');
580 		if (p == NULL) {
581 			if (strlcpy(lk->creds.username, key, sizeof (lk->creds.username))
582 			    >= sizeof (lk->creds.username))
583 				return (-1);
584 			if (strlcpy(lk->creds.password, line, sizeof(lk->creds.password))
585 			    >= sizeof(lk->creds.password))
586 				return (-1);
587 			return (1);
588 		}
589 
590 		if (p == line || p == line + len - 1)
591 			return (-1);
592 
593 		memmove(lk->creds.username, line, p - line);
594 		lk->creds.username[p - line] = '\0';
595 
596 		if (strlcpy(lk->creds.password, p+1, sizeof(lk->creds.password))
597 		    >= sizeof(lk->creds.password))
598 			return (-1);
599 
600 		return (1);
601 
602 	case K_NETADDR:
603 		if (!text_to_netaddr(&lk->netaddr, line))
604 			return (-1);
605 		return (1);
606 
607 	case K_USERINFO:
608 		if (!bsnprintf(buffer, sizeof(buffer), "%s:%s", key, line))
609 			return (-1);
610 		if (!text_to_userinfo(&lk->userinfo, buffer))
611 			return (-1);
612  		return (1);
613 
614 	case K_SOURCE:
615 		if (parse_sockaddr((struct sockaddr *)&lk->source.addr,
616 		    PF_UNSPEC, line) == -1)
617 			return (-1);
618 		return (1);
619 
620 	case K_MAILADDR:
621 		if (!text_to_mailaddr(&lk->mailaddr, line))
622 			return (-1);
623 		return (1);
624 
625 	case K_MAILADDRMAP:
626 		lk->maddrmap = calloc(1, sizeof(*lk->maddrmap));
627 		if (lk->maddrmap == NULL)
628 			return (-1);
629 		maddrmap_init(lk->maddrmap);
630 		if (!mailaddr_line(lk->maddrmap, line)) {
631 			maddrmap_free(lk->maddrmap);
632 			return (-1);
633 		}
634 		return (1);
635 
636 	case K_ADDRNAME:
637 		if (parse_sockaddr((struct sockaddr *)&lk->addrname.addr,
638 		    PF_UNSPEC, key) == -1)
639 			return (-1);
640 		if (strlcpy(lk->addrname.name, line, sizeof(lk->addrname.name))
641 		    >= sizeof(lk->addrname.name))
642 			return (-1);
643 		return (1);
644 
645 	case K_RELAYHOST:
646 		if (strlcpy(lk->relayhost, line, sizeof(lk->relayhost))
647 		    >= sizeof(lk->relayhost))
648 			return (-1);
649 		return (1);
650 
651 	default:
652 		return (-1);
653 	}
654 }
655 
656 static int
parse_sockaddr(struct sockaddr * sa,int family,const char * str)657 parse_sockaddr(struct sockaddr *sa, int family, const char *str)
658 {
659 	struct in_addr		 ina;
660 	struct in6_addr		 in6a;
661 	struct sockaddr_in	*sin;
662 	struct sockaddr_in6	*sin6;
663 	char			*cp;
664 	char			 addr[NI_MAXHOST];
665 	const char		*errstr;
666 
667 	switch (family) {
668 	case PF_UNSPEC:
669 		if (parse_sockaddr(sa, PF_INET, str) == 0)
670 			return (0);
671 		return parse_sockaddr(sa, PF_INET6, str);
672 
673 	case PF_INET:
674 		if (inet_pton(PF_INET, str, &ina) != 1)
675 			return (-1);
676 
677 		sin = (struct sockaddr_in *)sa;
678 		memset(sin, 0, sizeof *sin);
679 		sin->sin_len = sizeof(struct sockaddr_in);
680 		sin->sin_family = PF_INET;
681 		sin->sin_addr.s_addr = ina.s_addr;
682 		return (0);
683 
684 	case PF_INET6:
685 		if (*str == '[')
686 			str++;
687 		if (!strncasecmp("ipv6:", str, 5))
688 			str += 5;
689 
690 		if (strlcpy(addr, str, sizeof(addr)) >= sizeof(addr))
691 			return (-1);
692 		if ((cp = strchr(addr, ']')) != NULL)
693 			*cp = '\0';
694 		if ((cp = strchr(addr, SCOPE_DELIMITER)) != NULL)
695 			*cp++ = '\0';
696 
697 		if (inet_pton(PF_INET6, addr, &in6a) != 1)
698 			return (-1);
699 
700 		sin6 = (struct sockaddr_in6 *)sa;
701 		memset(sin6, 0, sizeof *sin6);
702 		sin6->sin6_len = sizeof(struct sockaddr_in6);
703 		sin6->sin6_family = PF_INET6;
704 		sin6->sin6_addr = in6a;
705 
706 		if (cp == NULL)
707 			return (0);
708 
709 		if (IN6_IS_ADDR_LINKLOCAL(&in6a) ||
710 		    IN6_IS_ADDR_MC_LINKLOCAL(&in6a) ||
711 		    IN6_IS_ADDR_MC_INTFACELOCAL(&in6a))
712 			if ((sin6->sin6_scope_id = if_nametoindex(cp)))
713 				return (0);
714 
715 		sin6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX, &errstr);
716 		if (errstr)
717 			return (-1);
718 		return (0);
719 
720 	default:
721 		break;
722 	}
723 
724 	return (-1);
725 }
726