xref: /illumos-gate/usr/src/lib/smbsrv/libsmb/common/smb_nic.c (revision c8ec8eea9849cac239663c46be8a7f5d2ba7ca00)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"@(#)smb_nic.c	1.6	08/07/24 SMI"
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <syslog.h>
31 #include <libintl.h>
32 #include <strings.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <synch.h>
36 #include <stropts.h>
37 #include <errno.h>
38 #include <pthread.h>
39 
40 #include <inet/ip.h>
41 #include <net/if.h>
42 #include <netinet/in.h>
43 #include <netdb.h>
44 #include <net/route.h>
45 #include <arpa/inet.h>
46 
47 #include <sys/socket.h>
48 #include <sys/sockio.h>
49 #include <sys/systeminfo.h>
50 
51 #include <smbsrv/libsmb.h>
52 
53 #define	SMB_NIC_DB_NAME		"/var/smb/smbhosts.db"
54 #define	SMB_NIC_DB_TIMEOUT	3000		/* in millisecond */
55 #define	SMB_NIC_DB_VERMAJOR	1
56 #define	SMB_NIC_DB_VERMINOR	0
57 #define	SMB_NIC_DB_MAGIC	0x484F5354	/* HOST */
58 
59 #define	SMB_NIC_DB_ORD		1		/* open read-only */
60 #define	SMB_NIC_DB_ORW		2		/* open read/write */
61 
62 #define	SMB_NIC_DB_SQL \
63 	"CREATE TABLE db_info ("				\
64 	"	ver_major INTEGER,"				\
65 	"	ver_minor INTEGER,"				\
66 	"	magic     INTEGER"				\
67 	");"							\
68 	""							\
69 	"CREATE TABLE hosts ("					\
70 	"	hostname  TEXT PRIMARY KEY,"			\
71 	"	comment   TEXT,"				\
72 	"	ifnames   TEXT"					\
73 	");"
74 
75 #define	SMB_NIC_HTBL_NCOL	3
76 #define	SMB_NIC_HTBL_HOST	0
77 #define	SMB_NIC_HTBL_CMNT	1
78 #define	SMB_NIC_HTBL_IFS	2
79 
80 #define	NULL_MSGCHK(msg)	((msg) ? (msg) : "NULL")
81 
82 #define	SMB_NIC_MAXIFS		256
83 
84 typedef struct smb_hostifs {
85 	list_node_t if_lnd;
86 	char if_host[MAXHOSTNAMELEN];
87 	char if_cmnt[SMB_PI_MAX_COMMENT];
88 	char *if_names[SMB_NIC_MAXIFS];
89 	int if_num;
90 } smb_hostifs_t;
91 
92 typedef struct smb_hosts {
93 	list_t h_list;
94 	int h_num;
95 	int h_ifnum;
96 } smb_hosts_t;
97 
98 typedef struct {
99 	smb_nic_t	*nl_nics;
100 	int		nl_cnt;		/* number of smb_nic_t structures */
101 	int		nl_hcnt;	/* number of host names */
102 	long		nl_seqnum;	/* a random sequence number */
103 	rwlock_t	nl_rwl;
104 } smb_niclist_t;
105 
106 static int smb_nic_list_create(void);
107 static void smb_nic_list_destroy(void);
108 
109 static int smb_nic_hlist_create(smb_hosts_t *);
110 static void smb_nic_hlist_destroy(smb_hosts_t *);
111 static int smb_nic_hlist_dbget(smb_hosts_t *);
112 static int smb_nic_hlist_sysget(smb_hosts_t *);
113 
114 static void smb_nic_iflist_destroy(smb_hostifs_t *);
115 static smb_hostifs_t *smb_nic_iflist_decode(const char **);
116 
117 static int smb_nic_dbcreate(void);
118 static sqlite *smb_nic_dbopen(int);
119 static void smb_nic_dbclose(sqlite *);
120 static boolean_t smb_nic_dbexists(void);
121 static boolean_t smb_nic_dbvalidate(void);
122 static int smb_nic_dbaddhost(const char *, const char *, char *);
123 static int smb_nic_dbdelhost(const char *);
124 static int smb_nic_dbsetinfo(sqlite *);
125 
126 static int smb_nic_getinfo(char *, smb_nic_t *);
127 
128 /* This is the list we will monitor */
129 static smb_niclist_t smb_niclist;
130 
131 /*
132  * smb_nic_init
133  *
134  * Initializes the interface list.
135  */
136 int
137 smb_nic_init(void)
138 {
139 	int rc;
140 
141 	(void) rw_wrlock(&smb_niclist.nl_rwl);
142 	smb_nic_list_destroy();
143 	rc = smb_nic_list_create();
144 	(void) rw_unlock(&smb_niclist.nl_rwl);
145 
146 	return (rc);
147 }
148 
149 /*
150  * smb_nic_fini
151  *
152  * Destroys the interface list.
153  */
154 void
155 smb_nic_fini(void)
156 {
157 	(void) rw_wrlock(&smb_niclist.nl_rwl);
158 	smb_nic_list_destroy();
159 	(void) rw_unlock(&smb_niclist.nl_rwl);
160 }
161 
162 /*
163  * smb_nic_getnum
164  *
165  * Gets the number of interfaces for the specified host.
166  * if host is NULL then total number of interfaces
167  * is returned. It's assumed that given name is a NetBIOS
168  * encoded name.
169  */
170 int
171 smb_nic_getnum(char *nb_hostname)
172 {
173 	int n = 0, i;
174 
175 	(void) rw_rdlock(&smb_niclist.nl_rwl);
176 
177 	if (nb_hostname != NULL) {
178 		for (i = 0; i < smb_niclist.nl_cnt; i++) {
179 			/* ignore the suffix */
180 			if (strncasecmp(smb_niclist.nl_nics[i].nic_nbname,
181 			    nb_hostname, NETBIOS_NAME_SZ - 1) == 0)
182 				n++;
183 		}
184 	} else {
185 		n = smb_niclist.nl_cnt;
186 	}
187 
188 	(void) rw_unlock(&smb_niclist.nl_rwl);
189 
190 	return (n);
191 }
192 
193 /*
194  * smb_nic_getfirst
195  *
196  * Returns the first NIC in the interface list and
197  * initializes the given iterator. To get the rest of
198  * NICs smb_nic_getnext() must be called.
199  *
200  * Returns 0 upon success and -1 if there's no interface
201  * available or if 'ni' is NULL.
202  */
203 int
204 smb_nic_getfirst(smb_niciter_t *ni)
205 {
206 	int rc = 0;
207 
208 	if (ni == NULL)
209 		return (-1);
210 
211 	(void) rw_rdlock(&smb_niclist.nl_rwl);
212 
213 	if (smb_niclist.nl_cnt > 0) {
214 		ni->ni_nic = smb_niclist.nl_nics[0];
215 		ni->ni_cookie = 1;
216 		ni->ni_seqnum = smb_niclist.nl_seqnum;
217 	} else {
218 		rc = -1;
219 	}
220 
221 	(void) rw_unlock(&smb_niclist.nl_rwl);
222 
223 	return (rc);
224 }
225 
226 /*
227  * smb_nic_getnext
228  *
229  * Returns the next NIC information based on the passed
230  * iterator (ni). The iterator must have previously been
231  * initialized by calling smb_nic_getfirst().
232  *
233  * Returns 0 upon successfully finding the specified NIC.
234  * Returns -1 if:
235  * 	- the specified iterator is invalid
236  * 	- reaches the end of the NIC list
237  * 	- sequence number in the iterator is different from
238  *	  the sequence number in the NIC list which means
239  *	  the list has been changed between getfirst/getnext
240  *	  calls.
241  */
242 int
243 smb_nic_getnext(smb_niciter_t *ni)
244 {
245 	int rc = 0;
246 
247 	if ((ni == NULL) || (ni->ni_cookie < 1))
248 		return (-1);
249 
250 	(void) rw_rdlock(&smb_niclist.nl_rwl);
251 
252 	if ((smb_niclist.nl_cnt > ni->ni_cookie) &&
253 	    (smb_niclist.nl_seqnum == ni->ni_seqnum)) {
254 		ni->ni_nic = smb_niclist.nl_nics[ni->ni_cookie];
255 		ni->ni_cookie++;
256 	} else {
257 		rc = -1;
258 	}
259 
260 	(void) rw_unlock(&smb_niclist.nl_rwl);
261 
262 	return (rc);
263 }
264 
265 /*
266  * smb_nic_exists
267  *
268  * Check to see if there's a NIC with the given IP address
269  * in the list. Subnet mask will be applied when comparing the
270  * IPs if the use_mask arg is true.
271  */
272 boolean_t
273 smb_nic_exists(uint32_t ipaddr, boolean_t use_mask)
274 {
275 	smb_nic_t *cfg;
276 	uint32_t mask = 0xFFFFFFFF;
277 	int i;
278 
279 	(void) rw_rdlock(&smb_niclist.nl_rwl);
280 
281 	for (i = 0; i < smb_niclist.nl_cnt; i++) {
282 		cfg = &smb_niclist.nl_nics[i];
283 		if (use_mask)
284 			mask = cfg->nic_mask;
285 
286 		if ((ipaddr & mask) == (cfg->nic_ip & mask)) {
287 			(void) rw_unlock(&smb_niclist.nl_rwl);
288 			return (B_TRUE);
289 		}
290 	}
291 
292 	(void) rw_unlock(&smb_niclist.nl_rwl);
293 
294 	return (B_FALSE);
295 }
296 
297 /*
298  * smb_nic_addhost
299  *
300  * Adds an association between the given host and the specified interface
301  * list (if_names). This function can be called as many times as needed,
302  * the associations will be stored in /var/smb/smbhosts.db, which is sqlite
303  * database. If this file exists and it's not empty NIC list is generated
304  * based on the information stored in this file.
305  *
306  * host: actual system's name (not Netbios name)
307  * cmnt: an optional description for the CIFS server running on
308  *       the specified host. Can be NULL.
309  * if_num: number of interface names in if_names arg
310  * if_names: array of interface names in string format
311  *
312  * Returns 0 upon success and -1 when fails
313  */
314 int
315 smb_nic_addhost(const char *host, const char *cmnt,
316     int if_num, const char **if_names)
317 {
318 	char *if_list;
319 	char *ifname;
320 	int buflen = 0;
321 	int rc, i;
322 
323 	if ((host == NULL) || (if_num <= 0) || (if_names == NULL))
324 		return (-1);
325 
326 	if (!smb_nic_dbexists() || !smb_nic_dbvalidate()) {
327 		if (smb_nic_dbcreate() != SQLITE_OK)
328 			return (-1);
329 	}
330 
331 	ifname = (char *)if_names;
332 	for (i = 0; i < if_num; i++, ifname++) {
333 		if ((ifname == NULL) || (*ifname == '\0'))
334 			return (-1);
335 		buflen += strlen(ifname) + 1;
336 	}
337 
338 	if ((if_list = malloc(buflen)) == NULL)
339 		return (-1);
340 
341 	ifname = if_list;
342 	for (i = 0; i < if_num - 1; i++)
343 		ifname += snprintf(ifname, buflen, "%s,", if_names[i]);
344 
345 	(void) snprintf(ifname, buflen, "%s", if_names[i]);
346 
347 	rc = smb_nic_dbaddhost(host, cmnt, if_list);
348 	free(if_list);
349 
350 	return ((rc == SQLITE_OK) ? 0 : -1);
351 }
352 
353 /*
354  * smb_nic_delhost
355  *
356  * Removes the stored interface association for the specified host
357  */
358 int
359 smb_nic_delhost(const char *host)
360 {
361 	if ((host == NULL) || (*host == '\0'))
362 		return (-1);
363 
364 	if (!smb_nic_dbexists())
365 		return (0);
366 
367 	if (!smb_nic_dbvalidate()) {
368 		(void) unlink(SMB_NIC_DB_NAME);
369 		return (0);
370 	}
371 
372 	if (smb_nic_dbdelhost(host) != SQLITE_OK)
373 		return (-1);
374 
375 	return (0);
376 }
377 
378 /*
379  * smb_nic_list_create
380  *
381  * Creates a NIC list either based on /var/smb/smbhosts.db or
382  * by getting the information from OS.
383  *
384  * Note that the caller of this function should grab the
385  * list lock.
386  */
387 static int
388 smb_nic_list_create(void)
389 {
390 	smb_hosts_t hlist;
391 	smb_hostifs_t *iflist;
392 	smb_nic_t *nc;
393 	char *ifname;
394 	char excludestr[MAX_EXCLUDE_LIST_LEN];
395 	ipaddr_t exclude[SMB_PI_MAX_NETWORKS];
396 	int nexclude;
397 	int i;
398 
399 	if (smb_nic_hlist_create(&hlist) < 0)
400 		return (-1);
401 
402 	smb_niclist.nl_cnt = 0;
403 	smb_niclist.nl_seqnum = random();
404 	smb_niclist.nl_hcnt = hlist.h_num;
405 
406 	smb_niclist.nl_nics = calloc(hlist.h_ifnum, sizeof (smb_nic_t));
407 	if (smb_niclist.nl_nics == NULL) {
408 		smb_nic_hlist_destroy(&hlist);
409 		return (-1);
410 	}
411 
412 	(void) smb_config_getstr(SMB_CI_WINS_EXCL, excludestr,
413 	    sizeof (excludestr));
414 	nexclude = smb_wins_iplist(excludestr, exclude, SMB_PI_MAX_NETWORKS);
415 
416 	nc = smb_niclist.nl_nics;
417 	iflist = list_head(&hlist.h_list);
418 
419 	do {
420 		for (i = 0; i < iflist->if_num; i++) {
421 			ifname = iflist->if_names[i];
422 			if (smb_nic_getinfo(ifname, nc) < 0)
423 				continue;
424 
425 			(void) strlcpy(nc->nic_host, iflist->if_host,
426 			    sizeof (nc->nic_host));
427 			(void) strlcpy(nc->nic_cmnt, iflist->if_cmnt,
428 			    sizeof (nc->nic_cmnt));
429 
430 			smb_tonetbiosname(nc->nic_host, nc->nic_nbname, 0x00);
431 
432 			if (strchr(ifname, ':'))
433 				nc->nic_smbflags |= SMB_NICF_ALIAS;
434 
435 			if (smb_wins_is_excluded(nc->nic_ip,
436 			    (ipaddr_t *)exclude, nexclude))
437 				nc->nic_smbflags |= SMB_NICF_NBEXCL;
438 
439 			smb_niclist.nl_cnt++;
440 			nc++;
441 		}
442 	} while	((iflist = list_next(&hlist.h_list, iflist)) != NULL);
443 
444 	smb_nic_hlist_destroy(&hlist);
445 
446 	return (0);
447 }
448 
449 static void
450 smb_nic_list_destroy(void)
451 {
452 	free(smb_niclist.nl_nics);
453 	smb_niclist.nl_nics = NULL;
454 	smb_niclist.nl_cnt = 0;
455 }
456 
457 /*
458  * smb_nic_getinfo
459  *
460  * Get IP info and more for the given interface
461  */
462 static int
463 smb_nic_getinfo(char *interface, smb_nic_t *nc)
464 {
465 	struct lifreq lifrr;
466 	struct sockaddr_in *sa;
467 	int s;
468 
469 	if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) {
470 		return (-1);
471 	}
472 
473 	(void) strlcpy(lifrr.lifr_name, interface, sizeof (lifrr.lifr_name));
474 	if (ioctl(s, SIOCGLIFADDR, &lifrr) < 0) {
475 		(void) close(s);
476 		return (-1);
477 	}
478 	sa = (struct sockaddr_in *)&lifrr.lifr_addr;
479 	nc->nic_ip = (uint32_t)sa->sin_addr.s_addr;
480 
481 	if (nc->nic_ip == 0) {
482 		(void) close(s);
483 		return (-1);
484 	}
485 
486 	if (ioctl(s, SIOCGLIFBRDADDR, &lifrr) < 0) {
487 		(void) close(s);
488 		return (-1);
489 	}
490 	sa = (struct sockaddr_in *)&lifrr.lifr_broadaddr;
491 	nc->nic_bcast = (uint32_t)sa->sin_addr.s_addr;
492 
493 	if (ioctl(s, SIOCGLIFNETMASK, &lifrr) < 0) {
494 		(void) close(s);
495 		return (-1);
496 	}
497 	sa = (struct sockaddr_in *)&lifrr.lifr_addr;
498 	nc->nic_mask = (uint32_t)sa->sin_addr.s_addr;
499 
500 	if (ioctl(s, SIOCGLIFFLAGS, &lifrr) < 0) {
501 		(void) close(s);
502 		return (-1);
503 	}
504 	nc->nic_sysflags = lifrr.lifr_flags;
505 
506 	(void) strlcpy(nc->nic_ifname, interface, sizeof (nc->nic_ifname));
507 
508 	(void) close(s);
509 	return (0);
510 }
511 
512 /*
513  * smb_nic_hlist_create
514  *
515  * Creates a list of hosts and their associated interfaces.
516  * If host database exists the information is retrieved from
517  * the database, otherwise it's retrieved from OS.
518  *
519  * The allocated memories for the returned list should be freed
520  * by calling smb_nic_hlist_destroy()
521  */
522 static int
523 smb_nic_hlist_create(smb_hosts_t *hlist)
524 {
525 	int rc;
526 
527 	list_create(&hlist->h_list, sizeof (smb_hostifs_t),
528 	    offsetof(smb_hostifs_t, if_lnd));
529 	hlist->h_num = 0;
530 	hlist->h_ifnum = 0;
531 
532 	if (smb_nic_dbexists() && smb_nic_dbvalidate()) {
533 		rc = smb_nic_hlist_dbget(hlist);
534 		errno = EBADF;
535 	} else {
536 		rc = smb_nic_hlist_sysget(hlist);
537 	}
538 
539 	if (rc != 0)
540 		smb_nic_hlist_destroy(hlist);
541 
542 	return (rc);
543 }
544 
545 static void
546 smb_nic_hlist_destroy(smb_hosts_t *hlist)
547 {
548 	smb_hostifs_t *iflist;
549 
550 	if (hlist == NULL)
551 		return;
552 
553 	while ((iflist = list_head(&hlist->h_list)) != NULL) {
554 		list_remove(&hlist->h_list, iflist);
555 		smb_nic_iflist_destroy(iflist);
556 	}
557 
558 	list_destroy(&hlist->h_list);
559 }
560 
561 /*
562  * smb_nic_hlist_sysget
563  *
564  * Get the list of currently plumbed and up interface names. The loopback (lo0)
565  * port is ignored
566  */
567 static int
568 smb_nic_hlist_sysget(smb_hosts_t *hlist)
569 {
570 	smb_hostifs_t *iflist;
571 	struct ifconf ifc;
572 	struct ifreq ifr;
573 	struct ifreq *ifrp;
574 	char *ifname;
575 	int ifnum;
576 	int i;
577 	int s;
578 
579 	iflist = malloc(sizeof (smb_hostifs_t));
580 	if (iflist == NULL)
581 		return (-1);
582 
583 	bzero(iflist, sizeof (smb_hostifs_t));
584 
585 	if (smb_gethostname(iflist->if_host, sizeof (iflist->if_host), 0) < 0) {
586 		free(iflist);
587 		return (-1);
588 	}
589 
590 	(void) smb_config_getstr(SMB_CI_SYS_CMNT, iflist->if_cmnt,
591 	    sizeof (iflist->if_cmnt));
592 
593 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
594 		free(iflist);
595 		return (-1);
596 	}
597 
598 	if (ioctl(s, SIOCGIFNUM, (char *)&ifnum) < 0) {
599 		(void) close(s);
600 		free(iflist);
601 		return (-1);
602 	}
603 
604 	ifc.ifc_len = ifnum * sizeof (struct ifreq);
605 	ifc.ifc_buf = malloc(ifc.ifc_len);
606 	if (ifc.ifc_buf == NULL) {
607 		(void) close(s);
608 		free(iflist);
609 		return (-1);
610 	}
611 	bzero(ifc.ifc_buf, ifc.ifc_len);
612 
613 	if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) {
614 		(void) close(s);
615 		free(iflist);
616 		free(ifc.ifc_buf);
617 		return (-1);
618 	}
619 
620 	ifrp = ifc.ifc_req;
621 	ifnum = ifc.ifc_len / sizeof (struct ifreq);
622 
623 	for (i = 0; i < ifnum; i++, ifrp++) {
624 		/*
625 		 * Get the flags so that we can skip the loopback interface
626 		 */
627 		(void) memset(&ifr, 0, sizeof (ifr));
628 		(void) strlcpy(ifr.ifr_name, ifrp->ifr_name,
629 		    sizeof (ifr.ifr_name));
630 
631 		if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
632 			(void) close(s);
633 			free(ifc.ifc_buf);
634 			smb_nic_iflist_destroy(iflist);
635 			return (-1);
636 		}
637 
638 		if (ifr.ifr_flags & IFF_LOOPBACK)
639 			continue;
640 
641 		if ((ifr.ifr_flags & IFF_UP) == 0)
642 			continue;
643 
644 		ifname = strdup(ifrp->ifr_name);
645 		if (ifname == NULL) {
646 			(void) close(s);
647 			free(ifc.ifc_buf);
648 			smb_nic_iflist_destroy(iflist);
649 			return (-1);
650 		}
651 		iflist->if_names[iflist->if_num++] = ifname;
652 	}
653 
654 	(void) close(s);
655 	free(ifc.ifc_buf);
656 
657 	hlist->h_num = 1;
658 	hlist->h_ifnum = iflist->if_num;
659 	list_insert_tail(&hlist->h_list, iflist);
660 
661 	return (0);
662 }
663 
664 static int
665 smb_nic_hlist_dbget(smb_hosts_t *hlist)
666 {
667 	smb_hostifs_t *iflist;
668 	sqlite *db;
669 	sqlite_vm *vm;
670 	boolean_t error = B_FALSE;
671 	const char **values;
672 	char *sql;
673 	char *errmsg = NULL;
674 	int ncol, rc;
675 
676 	sql = sqlite_mprintf("SELECT * FROM hosts");
677 	if (sql == NULL)
678 		return (-1);
679 
680 	db = smb_nic_dbopen(SMB_NIC_DB_ORD);
681 	if (db == NULL) {
682 		sqlite_freemem(sql);
683 		return (-1);
684 	}
685 
686 	rc = sqlite_compile(db, sql, NULL, &vm, &errmsg);
687 	sqlite_freemem(sql);
688 
689 	if (rc != SQLITE_OK) {
690 		smb_nic_dbclose(db);
691 		syslog(LOG_DEBUG, "smb_nic_hlist_dbget: failed to create"
692 		    " VM (%s)", NULL_MSGCHK(errmsg));
693 		return (-1);
694 	}
695 
696 	do {
697 		rc = sqlite_step(vm, &ncol, &values, NULL);
698 		if (rc == SQLITE_ROW) {
699 			if (ncol != SMB_NIC_HTBL_NCOL) {
700 				error = B_TRUE;
701 				break;
702 			}
703 
704 			if ((iflist = smb_nic_iflist_decode(values)) == NULL) {
705 				error = B_TRUE;
706 				break;
707 			}
708 
709 			list_insert_tail(&hlist->h_list, iflist);
710 			hlist->h_num++;
711 			hlist->h_ifnum += iflist->if_num;
712 		}
713 	} while (rc == SQLITE_ROW);
714 
715 	if (rc != SQLITE_DONE)
716 		error = B_TRUE;
717 
718 	rc = sqlite_finalize(vm, &errmsg);
719 	if (rc != SQLITE_OK) {
720 		syslog(LOG_DEBUG, "smb_nic_hlist_dbget: failed to destroy"
721 		    "VM (%s)", NULL_MSGCHK(errmsg));
722 		error = B_TRUE;
723 	}
724 
725 	smb_nic_dbclose(db);
726 
727 	return ((error) ? -1 : 0);
728 }
729 
730 static smb_hostifs_t *
731 smb_nic_iflist_decode(const char **values)
732 {
733 	smb_hostifs_t *iflist;
734 	char *host;
735 	char *cmnt;
736 	char *ifnames;
737 	char *lasts;
738 	char *ifname;
739 	int if_num = 0;
740 
741 	host = (char *)values[SMB_NIC_HTBL_HOST];
742 	cmnt = (char *)values[SMB_NIC_HTBL_CMNT];
743 	ifnames = (char *)values[SMB_NIC_HTBL_IFS];
744 
745 	if ((host == NULL) || (ifnames == NULL))
746 		return (NULL);
747 
748 	iflist = malloc(sizeof (smb_hostifs_t));
749 	if (iflist == NULL)
750 		return (NULL);
751 
752 	bzero(iflist, sizeof (smb_hostifs_t));
753 
754 	(void) strlcpy(iflist->if_host, host, sizeof (iflist->if_host));
755 	(void) strlcpy(iflist->if_cmnt, (cmnt) ? cmnt : "",
756 	    sizeof (iflist->if_cmnt));
757 
758 	if ((ifname = strtok_r(ifnames, ",", &lasts)) == NULL)
759 		return (NULL);
760 
761 	iflist->if_names[if_num++] = strdup(ifname);
762 
763 	while ((ifname = strtok_r(NULL, ",", &lasts)) != NULL)
764 		iflist->if_names[if_num++] = strdup(ifname);
765 
766 	iflist->if_num = if_num;
767 
768 	for (if_num = 0; if_num < iflist->if_num; if_num++) {
769 		if (iflist->if_names[if_num] == NULL) {
770 			smb_nic_iflist_destroy(iflist);
771 			return (NULL);
772 		}
773 	}
774 
775 	return (iflist);
776 }
777 
778 /*
779  * smb_nic_iflist_destroy
780  *
781  * Frees allocated memory for the given IF names lists.
782  */
783 static void
784 smb_nic_iflist_destroy(smb_hostifs_t *iflist)
785 {
786 	int i;
787 
788 	if (iflist == NULL)
789 		return;
790 
791 	for (i = 0; i < iflist->if_num; i++)
792 		free(iflist->if_names[i]);
793 
794 	free(iflist);
795 }
796 
797 /*
798  * Functions to manage host/interface database
799  *
800  * Each entry in the hosts table associates a hostname with a
801  * list of interface names. The host/interface association could
802  * be added by calling smb_nic_addhost() and could be removed by
803  * calling smb_nic_delhost(). If the database exists and it contains
804  * valid information then the inteface list wouldn't be obtained
805  * from system using ioctl.
806  */
807 
808 /*
809  * smb_nic_dbcreate
810  *
811  * Creates the host database based on the defined SQL statement.
812  * It also initializes db_info table.
813  */
814 static int
815 smb_nic_dbcreate(void)
816 {
817 	sqlite *db = NULL;
818 	char *errmsg = NULL;
819 	int rc;
820 
821 	(void) unlink(SMB_NIC_DB_NAME);
822 
823 	db = sqlite_open(SMB_NIC_DB_NAME, 0600, &errmsg);
824 	if (db == NULL) {
825 		syslog(LOG_DEBUG, "failed to create host database (%s)",
826 		    NULL_MSGCHK(errmsg));
827 		sqlite_freemem(errmsg);
828 		return (SQLITE_CANTOPEN);
829 	}
830 
831 	sqlite_busy_timeout(db, SMB_NIC_DB_TIMEOUT);
832 	rc = sqlite_exec(db, "BEGIN TRANSACTION", NULL, NULL, &errmsg);
833 	if (rc != SQLITE_OK) {
834 		syslog(LOG_DEBUG, "failed to begin database transaction (%s)",
835 		    NULL_MSGCHK(errmsg));
836 		sqlite_freemem(errmsg);
837 		sqlite_close(db);
838 		return (rc);
839 	}
840 
841 	if (sqlite_exec(db, SMB_NIC_DB_SQL, NULL, NULL, &errmsg) == SQLITE_OK) {
842 		rc = sqlite_exec(db, "COMMIT TRANSACTION", NULL, NULL,
843 		    &errmsg);
844 		if (rc == SQLITE_OK)
845 			rc = smb_nic_dbsetinfo(db);
846 		if (rc != SQLITE_OK)
847 			rc = sqlite_exec(db, "ROLLBACK TRANSACTION", NULL, NULL,
848 			    &errmsg);
849 	} else {
850 		syslog(LOG_ERR, "failed to initialize host database (%s)",
851 		    errmsg);
852 		sqlite_freemem(errmsg);
853 		rc = sqlite_exec(db, "ROLLBACK TRANSACTION", NULL, NULL,
854 		    &errmsg);
855 	}
856 
857 	if (rc != SQLITE_OK) {
858 		/* this is bad - database may be left in a locked state */
859 		syslog(LOG_DEBUG, "failed to close a transaction (%s)",
860 		    NULL_MSGCHK(errmsg));
861 		sqlite_freemem(errmsg);
862 	}
863 
864 	(void) sqlite_close(db);
865 	return (rc);
866 }
867 
868 /*
869  * smb_nic_dbopen
870  *
871  * Opens host database with the given mode.
872  */
873 static sqlite *
874 smb_nic_dbopen(int mode)
875 {
876 	sqlite *db;
877 	char *errmsg = NULL;
878 
879 	db = sqlite_open(SMB_NIC_DB_NAME, mode, &errmsg);
880 	if (db == NULL) {
881 		syslog(LOG_ERR, "failed to open group database (%s)",
882 		    NULL_MSGCHK(errmsg));
883 		sqlite_freemem(errmsg);
884 	}
885 
886 	return (db);
887 }
888 
889 /*
890  * smb_nic_dbclose
891  *
892  * Closes the given database handle
893  */
894 static void
895 smb_nic_dbclose(sqlite *db)
896 {
897 	if (db) {
898 		sqlite_close(db);
899 	}
900 }
901 
902 static boolean_t
903 smb_nic_dbexists(void)
904 {
905 	return (access(SMB_NIC_DB_NAME, F_OK) == 0);
906 }
907 
908 static boolean_t
909 smb_nic_dbvalidate(void)
910 {
911 	sqlite *db;
912 	char *errmsg = NULL;
913 	char *sql;
914 	char **result;
915 	int nrow, ncol;
916 	boolean_t check = B_TRUE;
917 	int rc;
918 
919 	sql = sqlite_mprintf("SELECT * FROM db_info");
920 	if (sql == NULL)
921 		return (B_FALSE);
922 
923 	db = smb_nic_dbopen(SMB_NIC_DB_ORW);
924 	if (db == NULL) {
925 		sqlite_freemem(sql);
926 		return (B_FALSE);
927 	}
928 
929 	rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
930 	sqlite_freemem(sql);
931 
932 	if (rc != SQLITE_OK) {
933 		syslog(LOG_DEBUG, "smb_nic_dbvalidate: failed to get db_info"
934 		    " (%s)", NULL_MSGCHK(errmsg));
935 		sqlite_freemem(errmsg);
936 		smb_nic_dbclose(db);
937 		return (B_FALSE);
938 	}
939 
940 	if (nrow != 1 || ncol != 3) {
941 		syslog(LOG_DEBUG, "smb_nic_dbvalidate: bad db_info table");
942 		sqlite_free_table(result);
943 		smb_nic_dbclose(db);
944 		return (B_FALSE);
945 	}
946 
947 	if ((atoi(result[3]) != SMB_NIC_DB_VERMAJOR) ||
948 	    (atoi(result[4]) != SMB_NIC_DB_VERMINOR) ||
949 	    (atoi(result[5]) != SMB_NIC_DB_MAGIC)) {
950 		syslog(LOG_DEBUG, "smb_nic_dbvalidate: bad db_info content");
951 		sqlite_free_table(result);
952 		smb_nic_dbclose(db);
953 		return (B_FALSE);
954 	}
955 	sqlite_free_table(result);
956 
957 	sql = sqlite_mprintf("SELECT hostname FROM hosts");
958 	if (sql == NULL) {
959 		smb_nic_dbclose(db);
960 		return (B_FALSE);
961 	}
962 
963 	rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
964 	sqlite_freemem(sql);
965 
966 	if (rc != SQLITE_OK) {
967 		syslog(LOG_DEBUG, "smb_nic_dbvalidate: failed to count (%s)",
968 		    NULL_MSGCHK(errmsg));
969 		sqlite_freemem(errmsg);
970 		smb_nic_dbclose(db);
971 		return (B_FALSE);
972 	}
973 
974 	sqlite_free_table(result);
975 
976 	if (nrow == 0)
977 		/* No hosts in the database */
978 		check = B_FALSE;
979 
980 	smb_nic_dbclose(db);
981 	return (check);
982 }
983 
984 static int
985 smb_nic_dbaddhost(const char *host, const char *cmnt, char *if_list)
986 {
987 	sqlite *db;
988 	char *sql;
989 	char *errmsg;
990 	int rc;
991 
992 	sql = sqlite_mprintf("REPLACE INTO hosts (hostname, comment, ifnames)"
993 	    "VALUES ('%s', '%q', '%s')", host, (cmnt) ? cmnt : "", if_list);
994 	if (sql == NULL)
995 		return (SQLITE_NOMEM);
996 
997 	db = smb_nic_dbopen(SMB_NIC_DB_ORW);
998 	if (db == NULL) {
999 		sqlite_freemem(sql);
1000 		return (SQLITE_CANTOPEN);
1001 	}
1002 
1003 	rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1004 	sqlite_freemem(sql);
1005 	smb_nic_dbclose(db);
1006 
1007 	if (rc != SQLITE_OK) {
1008 		syslog(LOG_DEBUG, "smb_nic_dbaddhost: failed to insert %s (%s)",
1009 		    host, NULL_MSGCHK(errmsg));
1010 		sqlite_freemem(errmsg);
1011 	}
1012 
1013 	return (rc);
1014 }
1015 
1016 static int
1017 smb_nic_dbdelhost(const char *host)
1018 {
1019 	sqlite *db;
1020 	char *sql;
1021 	char *errmsg;
1022 	int rc;
1023 
1024 	sql = sqlite_mprintf("DELETE FROM hosts WHERE hostname = '%s'", host);
1025 	if (sql == NULL)
1026 		return (SQLITE_NOMEM);
1027 
1028 	db = smb_nic_dbopen(SMB_NIC_DB_ORW);
1029 	if (db == NULL) {
1030 		sqlite_freemem(sql);
1031 		return (SQLITE_CANTOPEN);
1032 	}
1033 
1034 	rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1035 	sqlite_freemem(sql);
1036 	smb_nic_dbclose(db);
1037 
1038 	if (rc != SQLITE_OK) {
1039 		syslog(LOG_DEBUG, "smb_nic_dbdelhost: failed to delete %s (%s)",
1040 		    host, NULL_MSGCHK(errmsg));
1041 		sqlite_freemem(errmsg);
1042 	}
1043 
1044 	return (rc);
1045 }
1046 
1047 /*
1048  * smb_nic_dbsetinfo
1049  *
1050  * Initializes the db_info table upon database creation.
1051  */
1052 static int
1053 smb_nic_dbsetinfo(sqlite *db)
1054 {
1055 	char *errmsg = NULL;
1056 	char *sql;
1057 	int rc;
1058 
1059 	sql = sqlite_mprintf("INSERT INTO db_info (ver_major, ver_minor,"
1060 	    " magic) VALUES (%d, %d, %d)", SMB_NIC_DB_VERMAJOR,
1061 	    SMB_NIC_DB_VERMINOR, SMB_NIC_DB_MAGIC);
1062 
1063 	if (sql == NULL)
1064 		return (SQLITE_NOMEM);
1065 
1066 	rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1067 	sqlite_freemem(sql);
1068 	if (rc != SQLITE_OK) {
1069 		syslog(LOG_DEBUG, "smb_nic_dbsetinfo: failed to insert database"
1070 		    " information (%s)", NULL_MSGCHK(errmsg));
1071 		sqlite_freemem(errmsg);
1072 	}
1073 
1074 	return (rc);
1075 }
1076