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