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