GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/smtpd/smtpd/../table.c Lines: 0 364 0.0 %
Date: 2017-11-07 Branches: 0 273 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: table.c,v 1.24 2017/05/01 09:29:07 gilles 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/types.h>
21
#include <sys/queue.h>
22
#include <sys/tree.h>
23
#include <sys/socket.h>
24
#include <sys/stat.h>
25
26
#include <netinet/in.h>
27
#include <arpa/inet.h>
28
#include <net/if.h>
29
30
#include <ctype.h>
31
#include <err.h>
32
#include <errno.h>
33
#include <event.h>
34
#include <imsg.h>
35
#include <stdio.h>
36
#include <stdlib.h>
37
#include <netdb.h>
38
#include <limits.h>
39
#include <string.h>
40
#include <unistd.h>
41
42
#include "smtpd.h"
43
#include "log.h"
44
45
struct table_backend *table_backend_lookup(const char *);
46
47
extern struct table_backend table_backend_static;
48
extern struct table_backend table_backend_db;
49
extern struct table_backend table_backend_getpwnam;
50
extern struct table_backend table_backend_proc;
51
52
static const char * table_service_name(enum table_service);
53
static const char * table_backend_name(struct table_backend *);
54
static const char * table_dump_lookup(enum table_service, union lookup *);
55
static int parse_sockaddr(struct sockaddr *, int, const char *);
56
57
static unsigned int last_table_id = 0;
58
59
struct table_backend *
60
table_backend_lookup(const char *backend)
61
{
62
	if (!strcmp(backend, "static") || !strcmp(backend, "file"))
63
		return &table_backend_static;
64
	if (!strcmp(backend, "db"))
65
		return &table_backend_db;
66
	if (!strcmp(backend, "getpwnam"))
67
		return &table_backend_getpwnam;
68
	if (!strcmp(backend, "proc"))
69
		return &table_backend_proc;
70
	return NULL;
71
}
72
73
static const char *
74
table_backend_name(struct table_backend *backend)
75
{
76
	if (backend == &table_backend_static)
77
		return "static";
78
	if (backend == &table_backend_db)
79
		return "db";
80
	if (backend == &table_backend_getpwnam)
81
		return "getpwnam";
82
	if (backend == &table_backend_proc)
83
		return "proc";
84
	return "???";
85
}
86
87
static const char *
88
table_service_name(enum table_service s)
89
{
90
	switch (s) {
91
	case K_NONE:		return "NONE";
92
	case K_ALIAS:		return "ALIAS";
93
	case K_DOMAIN:		return "DOMAIN";
94
	case K_CREDENTIALS:	return "CREDENTIALS";
95
	case K_NETADDR:		return "NETADDR";
96
	case K_USERINFO:	return "USERINFO";
97
	case K_SOURCE:		return "SOURCE";
98
	case K_MAILADDR:	return "MAILADDR";
99
	case K_ADDRNAME:	return "ADDRNAME";
100
	case K_MAILADDRMAP:	return "MAILADDRMAP";
101
	default:		return "???";
102
	}
103
}
104
105
struct table *
106
table_find(const char *name, const char *tag)
107
{
108
	char buf[LINE_MAX];
109
110
	if (tag == NULL)
111
		return dict_get(env->sc_tables_dict, name);
112
113
	if ((size_t)snprintf(buf, sizeof(buf), "%s#%s", name, tag) >= sizeof(buf)) {
114
		log_warnx("warn: table name too long: %s#%s", name, tag);
115
		return (NULL);
116
	}
117
118
	return dict_get(env->sc_tables_dict, buf);
119
}
120
121
int
122
table_lookup(struct table *table, struct dict *params, const char *key, enum table_service kind,
123
    union lookup *lk)
124
{
125
	int	r;
126
	char	lkey[1024];
127
128
	if (table->t_backend->lookup == NULL)
129
		return (-1);
130
131
	if (!lowercase(lkey, key, sizeof lkey)) {
132
		log_warnx("warn: lookup key too long: %s", key);
133
		return -1;
134
	}
135
136
	r = table->t_backend->lookup(table->t_handle, params, lkey, kind, lk);
137
138
	if (r == 1)
139
		log_trace(TRACE_LOOKUP, "lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s",
140
		    lk ? "lookup" : "check",
141
		    lkey,
142
		    table_service_name(kind),
143
		    table_backend_name(table->t_backend),
144
		    table->t_name,
145
		    lk ? "\"" : "",
146
		    (lk) ? table_dump_lookup(kind, lk): "found",
147
		    lk ? "\"" : "");
148
	else
149
		log_trace(TRACE_LOOKUP, "lookup: %s \"%s\" as %s in table %s:%s -> %d",
150
		    lk ? "lookup" : "check",
151
		    lkey,
152
		    table_service_name(kind),
153
		    table_backend_name(table->t_backend),
154
		    table->t_name,
155
		    r);
156
157
	return (r);
158
}
159
160
int
161
table_fetch(struct table *table, struct dict *params, enum table_service kind, union lookup *lk)
162
{
163
	int 	r;
164
165
	if (table->t_backend->fetch == NULL)
166
		return (-1);
167
168
	r = table->t_backend->fetch(table->t_handle, params, kind, lk);
169
170
	if (r == 1)
171
		log_trace(TRACE_LOOKUP, "lookup: fetch %s from table %s:%s -> %s%s%s",
172
		    table_service_name(kind),
173
		    table_backend_name(table->t_backend),
174
		    table->t_name,
175
		    lk ? "\"" : "",
176
		    (lk) ? table_dump_lookup(kind, lk): "found",
177
		    lk ? "\"" : "");
178
	else
179
		log_trace(TRACE_LOOKUP, "lookup: fetch %s from table %s:%s -> %d",
180
		    table_service_name(kind),
181
		    table_backend_name(table->t_backend),
182
		    table->t_name,
183
		    r);
184
185
	return (r);
186
}
187
188
struct table *
189
table_create(const char *backend, const char *name, const char *tag,
190
    const char *config)
191
{
192
	struct table		*t;
193
	struct table_backend	*tb;
194
	char			 buf[LINE_MAX];
195
	char			 path[LINE_MAX];
196
	size_t			 n;
197
	struct stat		 sb;
198
199
	if (name && tag) {
200
		if ((size_t)snprintf(buf, sizeof(buf), "%s#%s", name, tag) >=
201
		    sizeof(buf))
202
			fatalx("table_create: name too long \"%s#%s\"",
203
			    name, tag);
204
		name = buf;
205
	}
206
207
	if (name && table_find(name, NULL))
208
		fatalx("table_create: table \"%s\" already defined", name);
209
210
	if ((tb = table_backend_lookup(backend)) == NULL) {
211
		if ((size_t)snprintf(path, sizeof(path), PATH_LIBEXEC"/table-%s",
212
			backend) >= sizeof(path)) {
213
			fatalx("table_create: path too long \""
214
			    PATH_LIBEXEC"/table-%s\"", backend);
215
		}
216
		if (stat(path, &sb) == 0) {
217
			tb = table_backend_lookup("proc");
218
			(void)strlcpy(path, backend, sizeof(path));
219
			if (config) {
220
				(void)strlcat(path, ":", sizeof(path));
221
				if (strlcat(path, config, sizeof(path))
222
				    >= sizeof(path))
223
					fatalx("table_create: config file path too long");
224
			}
225
			config = path;
226
		}
227
	}
228
229
	if (tb == NULL)
230
		fatalx("table_create: backend \"%s\" does not exist", backend);
231
232
	t = xcalloc(1, sizeof(*t), "table_create");
233
	t->t_backend = tb;
234
235
	/* XXX */
236
	/*
237
	 * until people forget about it, "file" really means "static"
238
	 */
239
	if (!strcmp(backend, "file"))
240
		backend = "static";
241
242
	if (config) {
243
		if (strlcpy(t->t_config, config, sizeof t->t_config)
244
		    >= sizeof t->t_config)
245
			fatalx("table_create: table config \"%s\" too large",
246
			    t->t_config);
247
	}
248
249
	if (strcmp(backend, "static") != 0)
250
		t->t_type = T_DYNAMIC;
251
252
	if (name == NULL)
253
		(void)snprintf(t->t_name, sizeof(t->t_name), "<dynamic:%u>",
254
		    last_table_id++);
255
	else {
256
		n = strlcpy(t->t_name, name, sizeof(t->t_name));
257
		if (n >= sizeof(t->t_name))
258
			fatalx("table_create: table name too long");
259
	}
260
261
	dict_init(&t->t_dict);
262
	dict_set(env->sc_tables_dict, t->t_name, t);
263
264
	return (t);
265
}
266
267
void
268
table_destroy(struct table *t)
269
{
270
	void	*p = NULL;
271
272
	while (dict_poproot(&t->t_dict, (void **)&p))
273
		free(p);
274
275
	dict_xpop(env->sc_tables_dict, t->t_name);
276
	free(t);
277
}
278
279
int
280
table_config(struct table *t)
281
{
282
	if (t->t_backend->config == NULL)
283
		return (1);
284
	return (t->t_backend->config(t));
285
}
286
287
void
288
table_add(struct table *t, const char *key, const char *val)
289
{
290
	char	lkey[1024], *old;
291
292
	if (t->t_type & T_DYNAMIC)
293
		fatalx("table_add: cannot add to table");
294
295
	if (!lowercase(lkey, key, sizeof lkey)) {
296
		log_warnx("warn: lookup key too long: %s", key);
297
		return;
298
	}
299
300
	old = dict_set(&t->t_dict, lkey, val ? xstrdup(val, "table_add") : NULL);
301
	if (old) {
302
		log_warnx("warn: duplicate key \"%s\" in static table \"%s\"",
303
		    lkey, t->t_name);
304
		free(old);
305
	}
306
}
307
308
int
309
table_check_type(struct table *t, uint32_t mask)
310
{
311
	return t->t_type & mask;
312
}
313
314
int
315
table_check_service(struct table *t, uint32_t mask)
316
{
317
	return t->t_backend->services & mask;
318
}
319
320
int
321
table_check_use(struct table *t, uint32_t tmask, uint32_t smask)
322
{
323
	return table_check_type(t, tmask) && table_check_service(t, smask);
324
}
325
326
int
327
table_open(struct table *t)
328
{
329
	t->t_handle = NULL;
330
	if (t->t_backend->open == NULL)
331
		return (1);
332
	t->t_handle = t->t_backend->open(t);
333
	if (t->t_handle == NULL)
334
		return (0);
335
	return (1);
336
}
337
338
void
339
table_close(struct table *t)
340
{
341
	if (t->t_backend->close)
342
		t->t_backend->close(t->t_handle);
343
}
344
345
int
346
table_update(struct table *t)
347
{
348
	if (t->t_backend->update == NULL)
349
		return (1);
350
	return (t->t_backend->update(t));
351
}
352
353
354
/*
355
 * quick reminder:
356
 * in *_match() s1 comes from session, s2 comes from table
357
 */
358
359
int
360
table_domain_match(const char *s1, const char *s2)
361
{
362
	return hostname_match(s1, s2);
363
}
364
365
int
366
table_mailaddr_match(const char *s1, const char *s2)
367
{
368
	struct mailaddr m1;
369
	struct mailaddr m2;
370
371
	if (!text_to_mailaddr(&m1, s1))
372
		return 0;
373
	if (!text_to_mailaddr(&m2, s2))
374
		return 0;
375
	return mailaddr_match(&m1, &m2);
376
}
377
378
static int table_match_mask(struct sockaddr_storage *, struct netaddr *);
379
static int table_inet4_match(struct sockaddr_in *, struct netaddr *);
380
static int table_inet6_match(struct sockaddr_in6 *, struct netaddr *);
381
382
int
383
table_netaddr_match(const char *s1, const char *s2)
384
{
385
	struct netaddr n1;
386
	struct netaddr n2;
387
388
	if (strcasecmp(s1, s2) == 0)
389
		return 1;
390
	if (!text_to_netaddr(&n1, s1))
391
		return 0;
392
	if (!text_to_netaddr(&n2, s2))
393
		return 0;
394
	if (n1.ss.ss_family != n2.ss.ss_family)
395
		return 0;
396
	if (n1.ss.ss_len != n2.ss.ss_len)
397
		return 0;
398
	return table_match_mask(&n1.ss, &n2);
399
}
400
401
static int
402
table_match_mask(struct sockaddr_storage *ss, struct netaddr *ssmask)
403
{
404
	if (ss->ss_family == AF_INET)
405
		return table_inet4_match((struct sockaddr_in *)ss, ssmask);
406
407
	if (ss->ss_family == AF_INET6)
408
		return table_inet6_match((struct sockaddr_in6 *)ss, ssmask);
409
410
	return (0);
411
}
412
413
static int
414
table_inet4_match(struct sockaddr_in *ss, struct netaddr *ssmask)
415
{
416
	in_addr_t mask;
417
	int i;
418
419
	/* a.b.c.d/8 -> htonl(0xff000000) */
420
	mask = 0;
421
	for (i = 0; i < ssmask->bits; ++i)
422
		mask = (mask >> 1) | 0x80000000;
423
	mask = htonl(mask);
424
425
	/* (addr & mask) == (net & mask) */
426
	if ((ss->sin_addr.s_addr & mask) ==
427
	    (((struct sockaddr_in *)ssmask)->sin_addr.s_addr & mask))
428
		return 1;
429
430
	return 0;
431
}
432
433
static int
434
table_inet6_match(struct sockaddr_in6 *ss, struct netaddr *ssmask)
435
{
436
	struct in6_addr	*in;
437
	struct in6_addr	*inmask;
438
	struct in6_addr	 mask;
439
	int		 i;
440
441
	memset(&mask, 0, sizeof(mask));
442
	for (i = 0; i < ssmask->bits / 8; i++)
443
		mask.s6_addr[i] = 0xff;
444
	i = ssmask->bits % 8;
445
	if (i)
446
		mask.s6_addr[ssmask->bits / 8] = 0xff00 >> i;
447
448
	in = &ss->sin6_addr;
449
	inmask = &((struct sockaddr_in6 *)&ssmask->ss)->sin6_addr;
450
451
	for (i = 0; i < 16; i++) {
452
		if ((in->s6_addr[i] & mask.s6_addr[i]) !=
453
		    (inmask->s6_addr[i] & mask.s6_addr[i]))
454
			return (0);
455
	}
456
457
	return (1);
458
}
459
460
void
461
table_dump_all(void)
462
{
463
	struct table	*t;
464
	void		*iter, *i2;
465
	const char 	*key, *sep;
466
	char		*value;
467
	char		 buf[1024];
468
469
	iter = NULL;
470
	while (dict_iter(env->sc_tables_dict, &iter, NULL, (void **)&t)) {
471
		i2 = NULL;
472
		sep = "";
473
 		buf[0] = '\0';
474
		if (t->t_type & T_DYNAMIC) {
475
			(void)strlcat(buf, "DYNAMIC", sizeof(buf));
476
			sep = ",";
477
		}
478
		if (t->t_type & T_LIST) {
479
			(void)strlcat(buf, sep, sizeof(buf));
480
			(void)strlcat(buf, "LIST", sizeof(buf));
481
			sep = ",";
482
		}
483
		if (t->t_type & T_HASH) {
484
			(void)strlcat(buf, sep, sizeof(buf));
485
			(void)strlcat(buf, "HASH", sizeof(buf));
486
			sep = ",";
487
		}
488
		log_debug("TABLE \"%s\" type=%s config=\"%s\"",
489
		    t->t_name, buf, t->t_config);
490
		while(dict_iter(&t->t_dict, &i2, &key, (void**)&value)) {
491
			if (value)
492
				log_debug("	\"%s\" -> \"%s\"", key, value);
493
			else
494
				log_debug("	\"%s\"", key);
495
		}
496
	}
497
}
498
499
void
500
table_open_all(void)
501
{
502
	struct table	*t;
503
	void		*iter;
504
505
	iter = NULL;
506
	while (dict_iter(env->sc_tables_dict, &iter, NULL, (void **)&t))
507
		if (!table_open(t))
508
			fatalx("failed to open table %s", t->t_name);
509
}
510
511
void
512
table_close_all(void)
513
{
514
	struct table	*t;
515
	void		*iter;
516
517
	iter = NULL;
518
	while (dict_iter(env->sc_tables_dict, &iter, NULL, (void **)&t))
519
		table_close(t);
520
}
521
522
int
523
table_parse_lookup(enum table_service service, const char *key,
524
    const char *line, union lookup *lk)
525
{
526
	char	buffer[LINE_MAX], *p;
527
	size_t	len;
528
529
	len = strlen(line);
530
531
	switch (service) {
532
	case K_ALIAS:
533
		lk->expand = calloc(1, sizeof(*lk->expand));
534
		if (lk->expand == NULL)
535
			return (-1);
536
		if (!expand_line(lk->expand, line, 1)) {
537
			expand_free(lk->expand);
538
			return (-1);
539
		}
540
		return (1);
541
542
	case K_DOMAIN:
543
		if (strlcpy(lk->domain.name, line, sizeof(lk->domain.name))
544
		    >= sizeof(lk->domain.name))
545
			return (-1);
546
		return (1);
547
548
	case K_CREDENTIALS:
549
550
		/* credentials are stored as user:password */
551
		if (len < 3)
552
			return (-1);
553
554
		/* too big to fit in a smtp session line */
555
		if (len >= LINE_MAX)
556
			return (-1);
557
558
		p = strchr(line, ':');
559
		if (p == NULL) {
560
			if (strlcpy(lk->creds.username, key, sizeof (lk->creds.username))
561
			    >= sizeof (lk->creds.username))
562
				return (-1);
563
			if (strlcpy(lk->creds.password, line, sizeof(lk->creds.password))
564
			    >= sizeof(lk->creds.password))
565
				return (-1);
566
			return (1);
567
		}
568
569
		if (p == line || p == line + len - 1)
570
			return (-1);
571
572
		memmove(lk->creds.username, line, p - line);
573
		lk->creds.username[p - line] = '\0';
574
575
		if (strlcpy(lk->creds.password, p+1, sizeof(lk->creds.password))
576
		    >= sizeof(lk->creds.password))
577
			return (-1);
578
579
		return (1);
580
581
	case K_NETADDR:
582
		if (!text_to_netaddr(&lk->netaddr, line))
583
			return (-1);
584
		return (1);
585
586
	case K_USERINFO:
587
		if (!bsnprintf(buffer, sizeof(buffer), "%s:%s", key, line))
588
			return (-1);
589
		if (!text_to_userinfo(&lk->userinfo, buffer))
590
			return (-1);
591
 		return (1);
592
593
	case K_SOURCE:
594
		if (parse_sockaddr((struct sockaddr *)&lk->source.addr,
595
		    PF_UNSPEC, line) == -1)
596
			return (-1);
597
		return (1);
598
599
	case K_MAILADDR:
600
		if (!text_to_mailaddr(&lk->mailaddr, line))
601
			return (-1);
602
		return (1);
603
604
	case K_MAILADDRMAP:
605
		lk->maddrmap = calloc(1, sizeof(*lk->maddrmap));
606
		if (lk->maddrmap == NULL)
607
			return (-1);
608
		maddrmap_init(lk->maddrmap);
609
		if (!mailaddr_line(lk->maddrmap, line)) {
610
			maddrmap_free(lk->maddrmap);
611
			return (-1);
612
		}
613
		return (1);
614
615
	case K_ADDRNAME:
616
		if (parse_sockaddr((struct sockaddr *)&lk->addrname.addr,
617
		    PF_UNSPEC, key) == -1)
618
			return (-1);
619
		if (strlcpy(lk->addrname.name, line, sizeof(lk->addrname.name))
620
		    >= sizeof(lk->addrname.name))
621
			return (-1);
622
		return (1);
623
624
	default:
625
		return (-1);
626
	}
627
}
628
629
static const char *
630
table_dump_lookup(enum table_service s, union lookup *lk)
631
{
632
	static char	buf[LINE_MAX];
633
	int		ret;
634
635
	switch (s) {
636
	case K_NONE:
637
		break;
638
639
	case K_ALIAS:
640
		expand_to_text(lk->expand, buf, sizeof(buf));
641
		break;
642
643
	case K_DOMAIN:
644
		ret = snprintf(buf, sizeof(buf), "%s", lk->domain.name);
645
		if (ret == -1 || (size_t)ret >= sizeof (buf))
646
			goto err;
647
		break;
648
649
	case K_CREDENTIALS:
650
		ret = snprintf(buf, sizeof(buf), "%s:%s",
651
		    lk->creds.username, lk->creds.password);
652
		if (ret == -1 || (size_t)ret >= sizeof (buf))
653
			goto err;
654
		break;
655
656
	case K_NETADDR:
657
		ret = snprintf(buf, sizeof(buf), "%s/%d",
658
		    sockaddr_to_text((struct sockaddr *)&lk->netaddr.ss),
659
		    lk->netaddr.bits);
660
		if (ret == -1 || (size_t)ret >= sizeof (buf))
661
			goto err;
662
		break;
663
664
	case K_USERINFO:
665
		ret = snprintf(buf, sizeof(buf), "%s:%d:%d:%s",
666
		    lk->userinfo.username,
667
		    lk->userinfo.uid,
668
		    lk->userinfo.gid,
669
		    lk->userinfo.directory);
670
		if (ret == -1 || (size_t)ret >= sizeof (buf))
671
			goto err;
672
		break;
673
674
	case K_SOURCE:
675
		ret = snprintf(buf, sizeof(buf), "%s",
676
		    ss_to_text(&lk->source.addr));
677
		if (ret == -1 || (size_t)ret >= sizeof (buf))
678
			goto err;
679
		break;
680
681
	case K_MAILADDR:
682
		ret = snprintf(buf, sizeof(buf), "%s@%s",
683
		    lk->mailaddr.user,
684
		    lk->mailaddr.domain);
685
		if (ret == -1 || (size_t)ret >= sizeof (buf))
686
			goto err;
687
		break;
688
689
	case K_ADDRNAME:
690
		ret = snprintf(buf, sizeof(buf), "%s",
691
		    lk->addrname.name);
692
		if (ret == -1 || (size_t)ret >= sizeof (buf))
693
			goto err;
694
		break;
695
696
	default:
697
		(void)strlcpy(buf, "???", sizeof(buf));
698
		break;
699
	}
700
701
	return (buf);
702
703
err:
704
	return (NULL);
705
}
706
707
708
static int
709
parse_sockaddr(struct sockaddr *sa, int family, const char *str)
710
{
711
	struct in_addr		 ina;
712
	struct in6_addr		 in6a;
713
	struct sockaddr_in	*sin;
714
	struct sockaddr_in6	*sin6;
715
	char			*cp, *str2;
716
	const char		*errstr;
717
718
	switch (family) {
719
	case PF_UNSPEC:
720
		if (parse_sockaddr(sa, PF_INET, str) == 0)
721
			return (0);
722
		return parse_sockaddr(sa, PF_INET6, str);
723
724
	case PF_INET:
725
		if (inet_pton(PF_INET, str, &ina) != 1)
726
			return (-1);
727
728
		sin = (struct sockaddr_in *)sa;
729
		memset(sin, 0, sizeof *sin);
730
		sin->sin_len = sizeof(struct sockaddr_in);
731
		sin->sin_family = PF_INET;
732
		sin->sin_addr.s_addr = ina.s_addr;
733
		return (0);
734
735
	case PF_INET6:
736
		if (strncasecmp("ipv6:", str, 5) == 0)
737
			str += 5;
738
		cp = strchr(str, SCOPE_DELIMITER);
739
		if (cp) {
740
			str2 = strdup(str);
741
			if (str2 == NULL)
742
				return (-1);
743
			str2[cp - str] = '\0';
744
			if (inet_pton(PF_INET6, str2, &in6a) != 1) {
745
				free(str2);
746
				return (-1);
747
			}
748
			cp++;
749
			free(str2);
750
		} else if (inet_pton(PF_INET6, str, &in6a) != 1)
751
			return (-1);
752
753
		sin6 = (struct sockaddr_in6 *)sa;
754
		memset(sin6, 0, sizeof *sin6);
755
		sin6->sin6_len = sizeof(struct sockaddr_in6);
756
		sin6->sin6_family = PF_INET6;
757
		sin6->sin6_addr = in6a;
758
759
		if (cp == NULL)
760
			return (0);
761
762
		if (IN6_IS_ADDR_LINKLOCAL(&in6a) ||
763
		    IN6_IS_ADDR_MC_LINKLOCAL(&in6a) ||
764
		    IN6_IS_ADDR_MC_INTFACELOCAL(&in6a))
765
			if ((sin6->sin6_scope_id = if_nametoindex(cp)))
766
				return (0);
767
768
		sin6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX, &errstr);
769
		if (errstr)
770
			return (-1);
771
		return (0);
772
773
	default:
774
		break;
775
	}
776
777
	return (-1);
778
}