xref: /illumos-gate/usr/src/lib/smbsrv/libsmb/common/smb_nic.c (revision e688a0bc223983e7c7fedad44c92b8d0292a10f7)
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	"%Z%%M%	%I%	%E% 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 	if (smb_niclist.nl_cnt == 0) {
447 		smb_nic_list_destroy();
448 		return (-1);
449 	}
450 
451 	return (0);
452 }
453 
454 static void
455 smb_nic_list_destroy(void)
456 {
457 	free(smb_niclist.nl_nics);
458 	smb_niclist.nl_nics = NULL;
459 	smb_niclist.nl_cnt = 0;
460 }
461 
462 /*
463  * smb_nic_getinfo
464  *
465  * Get IP info and more for the given interface
466  */
467 static int
468 smb_nic_getinfo(char *interface, smb_nic_t *nc)
469 {
470 	struct lifreq lifrr;
471 	struct sockaddr_in *sa;
472 	int s;
473 
474 	if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) {
475 		return (-1);
476 	}
477 
478 	(void) strlcpy(lifrr.lifr_name, interface, sizeof (lifrr.lifr_name));
479 	if (ioctl(s, SIOCGLIFADDR, &lifrr) < 0) {
480 		(void) close(s);
481 		return (-1);
482 	}
483 	sa = (struct sockaddr_in *)&lifrr.lifr_addr;
484 	nc->nic_ip = (uint32_t)sa->sin_addr.s_addr;
485 
486 	if (nc->nic_ip == 0) {
487 		(void) close(s);
488 		return (-1);
489 	}
490 
491 	if (ioctl(s, SIOCGLIFBRDADDR, &lifrr) < 0) {
492 		(void) close(s);
493 		return (-1);
494 	}
495 	sa = (struct sockaddr_in *)&lifrr.lifr_broadaddr;
496 	nc->nic_bcast = (uint32_t)sa->sin_addr.s_addr;
497 
498 	if (ioctl(s, SIOCGLIFNETMASK, &lifrr) < 0) {
499 		(void) close(s);
500 		return (-1);
501 	}
502 	sa = (struct sockaddr_in *)&lifrr.lifr_addr;
503 	nc->nic_mask = (uint32_t)sa->sin_addr.s_addr;
504 
505 	if (ioctl(s, SIOCGLIFFLAGS, &lifrr) < 0) {
506 		(void) close(s);
507 		return (-1);
508 	}
509 	nc->nic_sysflags = lifrr.lifr_flags;
510 
511 	(void) strlcpy(nc->nic_ifname, interface, sizeof (nc->nic_ifname));
512 
513 	(void) close(s);
514 	return (0);
515 }
516 
517 /*
518  * smb_nic_hlist_create
519  *
520  * Creates a list of hosts and their associated interfaces.
521  * If host database exists the information is retrieved from
522  * the database, otherwise it's retrieved from OS.
523  *
524  * The allocated memories for the returned list should be freed
525  * by calling smb_nic_hlist_destroy()
526  */
527 static int
528 smb_nic_hlist_create(smb_hosts_t *hlist)
529 {
530 	int rc;
531 
532 	list_create(&hlist->h_list, sizeof (smb_hostifs_t),
533 	    offsetof(smb_hostifs_t, if_lnd));
534 	hlist->h_num = 0;
535 	hlist->h_ifnum = 0;
536 
537 	if (smb_nic_dbexists() && smb_nic_dbvalidate()) {
538 		rc = smb_nic_hlist_dbget(hlist);
539 		errno = EBADF;
540 	} else {
541 		rc = smb_nic_hlist_sysget(hlist);
542 	}
543 
544 	if (rc != 0)
545 		smb_nic_hlist_destroy(hlist);
546 
547 	return (rc);
548 }
549 
550 static void
551 smb_nic_hlist_destroy(smb_hosts_t *hlist)
552 {
553 	smb_hostifs_t *iflist;
554 
555 	if (hlist == NULL)
556 		return;
557 
558 	while ((iflist = list_head(&hlist->h_list)) != NULL) {
559 		list_remove(&hlist->h_list, iflist);
560 		smb_nic_iflist_destroy(iflist);
561 	}
562 
563 	list_destroy(&hlist->h_list);
564 }
565 
566 /*
567  * smb_nic_hlist_sysget
568  *
569  * Get the list of currently plumbed and up interface names. The loopback (lo0)
570  * port is ignored
571  */
572 static int
573 smb_nic_hlist_sysget(smb_hosts_t *hlist)
574 {
575 	smb_hostifs_t *iflist;
576 	struct ifconf ifc;
577 	struct ifreq ifr;
578 	struct ifreq *ifrp;
579 	char *ifname;
580 	int ifnum;
581 	int i;
582 	int s;
583 
584 	iflist = malloc(sizeof (smb_hostifs_t));
585 	if (iflist == NULL)
586 		return (-1);
587 
588 	bzero(iflist, sizeof (smb_hostifs_t));
589 
590 	if (smb_gethostname(iflist->if_host, sizeof (iflist->if_host), 0) < 0) {
591 		free(iflist);
592 		return (-1);
593 	}
594 
595 	(void) smb_config_getstr(SMB_CI_SYS_CMNT, iflist->if_cmnt,
596 	    sizeof (iflist->if_cmnt));
597 
598 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
599 		free(iflist);
600 		return (-1);
601 	}
602 
603 	if (ioctl(s, SIOCGIFNUM, (char *)&ifnum) < 0) {
604 		(void) close(s);
605 		free(iflist);
606 		return (-1);
607 	}
608 
609 	ifc.ifc_len = ifnum * sizeof (struct ifreq);
610 	ifc.ifc_buf = malloc(ifc.ifc_len);
611 	if (ifc.ifc_buf == NULL) {
612 		(void) close(s);
613 		free(iflist);
614 		return (-1);
615 	}
616 	bzero(ifc.ifc_buf, ifc.ifc_len);
617 
618 	if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) {
619 		(void) close(s);
620 		free(iflist);
621 		free(ifc.ifc_buf);
622 		return (-1);
623 	}
624 
625 	ifrp = ifc.ifc_req;
626 	ifnum = ifc.ifc_len / sizeof (struct ifreq);
627 
628 	for (i = 0; i < ifnum; i++, ifrp++) {
629 		/*
630 		 * Get the flags so that we can skip the loopback interface
631 		 */
632 		(void) memset(&ifr, 0, sizeof (ifr));
633 		(void) strlcpy(ifr.ifr_name, ifrp->ifr_name,
634 		    sizeof (ifr.ifr_name));
635 
636 		if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
637 			(void) close(s);
638 			free(ifc.ifc_buf);
639 			smb_nic_iflist_destroy(iflist);
640 			return (-1);
641 		}
642 
643 		if (ifr.ifr_flags & IFF_LOOPBACK)
644 			continue;
645 
646 		if ((ifr.ifr_flags & IFF_UP) == 0)
647 			continue;
648 
649 		ifname = strdup(ifrp->ifr_name);
650 		if (ifname == NULL) {
651 			(void) close(s);
652 			free(ifc.ifc_buf);
653 			smb_nic_iflist_destroy(iflist);
654 			return (-1);
655 		}
656 		iflist->if_names[iflist->if_num++] = ifname;
657 	}
658 
659 	(void) close(s);
660 	free(ifc.ifc_buf);
661 
662 	hlist->h_num = 1;
663 	hlist->h_ifnum = iflist->if_num;
664 	list_insert_tail(&hlist->h_list, iflist);
665 
666 	return (0);
667 }
668 
669 static int
670 smb_nic_hlist_dbget(smb_hosts_t *hlist)
671 {
672 	smb_hostifs_t *iflist;
673 	sqlite *db;
674 	sqlite_vm *vm;
675 	boolean_t error = B_FALSE;
676 	const char **values;
677 	char *sql;
678 	char *errmsg = NULL;
679 	int ncol, rc;
680 
681 	sql = sqlite_mprintf("SELECT * FROM hosts");
682 	if (sql == NULL)
683 		return (-1);
684 
685 	db = smb_nic_dbopen(SMB_NIC_DB_ORD);
686 	if (db == NULL) {
687 		sqlite_freemem(sql);
688 		return (-1);
689 	}
690 
691 	rc = sqlite_compile(db, sql, NULL, &vm, &errmsg);
692 	sqlite_freemem(sql);
693 
694 	if (rc != SQLITE_OK) {
695 		smb_nic_dbclose(db);
696 		syslog(LOG_DEBUG, "smb_nic_hlist_dbget: failed to create"
697 		    " VM (%s)", NULL_MSGCHK(errmsg));
698 		return (-1);
699 	}
700 
701 	do {
702 		rc = sqlite_step(vm, &ncol, &values, NULL);
703 		if (rc == SQLITE_ROW) {
704 			if (ncol != SMB_NIC_HTBL_NCOL) {
705 				error = B_TRUE;
706 				break;
707 			}
708 
709 			if ((iflist = smb_nic_iflist_decode(values)) == NULL) {
710 				error = B_TRUE;
711 				break;
712 			}
713 
714 			list_insert_tail(&hlist->h_list, iflist);
715 			hlist->h_num++;
716 			hlist->h_ifnum += iflist->if_num;
717 		}
718 	} while (rc == SQLITE_ROW);
719 
720 	if (rc != SQLITE_DONE)
721 		error = B_TRUE;
722 
723 	rc = sqlite_finalize(vm, &errmsg);
724 	if (rc != SQLITE_OK) {
725 		syslog(LOG_DEBUG, "smb_nic_hlist_dbget: failed to destroy"
726 		    "VM (%s)", NULL_MSGCHK(errmsg));
727 		error = B_TRUE;
728 	}
729 
730 	smb_nic_dbclose(db);
731 
732 	return ((error) ? -1 : 0);
733 }
734 
735 static smb_hostifs_t *
736 smb_nic_iflist_decode(const char **values)
737 {
738 	smb_hostifs_t *iflist;
739 	char *host;
740 	char *cmnt;
741 	char *ifnames;
742 	char *lasts;
743 	char *ifname;
744 	int if_num = 0;
745 
746 	host = (char *)values[SMB_NIC_HTBL_HOST];
747 	cmnt = (char *)values[SMB_NIC_HTBL_CMNT];
748 	ifnames = (char *)values[SMB_NIC_HTBL_IFS];
749 
750 	if ((host == NULL) || (ifnames == NULL))
751 		return (NULL);
752 
753 	iflist = malloc(sizeof (smb_hostifs_t));
754 	if (iflist == NULL)
755 		return (NULL);
756 
757 	bzero(iflist, sizeof (smb_hostifs_t));
758 
759 	(void) strlcpy(iflist->if_host, host, sizeof (iflist->if_host));
760 	(void) strlcpy(iflist->if_cmnt, (cmnt) ? cmnt : "",
761 	    sizeof (iflist->if_cmnt));
762 
763 	if ((ifname = strtok_r(ifnames, ",", &lasts)) == NULL)
764 		return (NULL);
765 
766 	iflist->if_names[if_num++] = strdup(ifname);
767 
768 	while ((ifname = strtok_r(NULL, ",", &lasts)) != NULL)
769 		iflist->if_names[if_num++] = strdup(ifname);
770 
771 	iflist->if_num = if_num;
772 
773 	for (if_num = 0; if_num < iflist->if_num; if_num++) {
774 		if (iflist->if_names[if_num] == NULL) {
775 			smb_nic_iflist_destroy(iflist);
776 			return (NULL);
777 		}
778 	}
779 
780 	return (iflist);
781 }
782 
783 /*
784  * smb_nic_iflist_destroy
785  *
786  * Frees allocated memory for the given IF names lists.
787  */
788 static void
789 smb_nic_iflist_destroy(smb_hostifs_t *iflist)
790 {
791 	int i;
792 
793 	if (iflist == NULL)
794 		return;
795 
796 	for (i = 0; i < iflist->if_num; i++)
797 		free(iflist->if_names[i]);
798 
799 	free(iflist);
800 }
801 
802 /*
803  * Functions to manage host/interface database
804  *
805  * Each entry in the hosts table associates a hostname with a
806  * list of interface names. The host/interface association could
807  * be added by calling smb_nic_addhost() and could be removed by
808  * calling smb_nic_delhost(). If the database exists and it contains
809  * valid information then the inteface list wouldn't be obtained
810  * from system using ioctl.
811  */
812 
813 /*
814  * smb_nic_dbcreate
815  *
816  * Creates the host database based on the defined SQL statement.
817  * It also initializes db_info table.
818  */
819 static int
820 smb_nic_dbcreate(void)
821 {
822 	sqlite *db = NULL;
823 	char *errmsg = NULL;
824 	int rc;
825 
826 	(void) unlink(SMB_NIC_DB_NAME);
827 
828 	db = sqlite_open(SMB_NIC_DB_NAME, 0600, &errmsg);
829 	if (db == NULL) {
830 		syslog(LOG_DEBUG, "failed to create host database (%s)",
831 		    NULL_MSGCHK(errmsg));
832 		sqlite_freemem(errmsg);
833 		return (SQLITE_CANTOPEN);
834 	}
835 
836 	sqlite_busy_timeout(db, SMB_NIC_DB_TIMEOUT);
837 	rc = sqlite_exec(db, "BEGIN TRANSACTION", NULL, NULL, &errmsg);
838 	if (rc != SQLITE_OK) {
839 		syslog(LOG_DEBUG, "failed to begin database transaction (%s)",
840 		    NULL_MSGCHK(errmsg));
841 		sqlite_freemem(errmsg);
842 		sqlite_close(db);
843 		return (rc);
844 	}
845 
846 	if (sqlite_exec(db, SMB_NIC_DB_SQL, NULL, NULL, &errmsg) == SQLITE_OK) {
847 		rc = sqlite_exec(db, "COMMIT TRANSACTION", NULL, NULL,
848 		    &errmsg);
849 		if (rc == SQLITE_OK)
850 			rc = smb_nic_dbsetinfo(db);
851 		if (rc != SQLITE_OK)
852 			rc = sqlite_exec(db, "ROLLBACK TRANSACTION", NULL, NULL,
853 			    &errmsg);
854 	} else {
855 		syslog(LOG_ERR, "failed to initialize host database (%s)",
856 		    errmsg);
857 		sqlite_freemem(errmsg);
858 		rc = sqlite_exec(db, "ROLLBACK TRANSACTION", NULL, NULL,
859 		    &errmsg);
860 	}
861 
862 	if (rc != SQLITE_OK) {
863 		/* this is bad - database may be left in a locked state */
864 		syslog(LOG_DEBUG, "failed to close a transaction (%s)",
865 		    NULL_MSGCHK(errmsg));
866 		sqlite_freemem(errmsg);
867 	}
868 
869 	(void) sqlite_close(db);
870 	return (rc);
871 }
872 
873 /*
874  * smb_nic_dbopen
875  *
876  * Opens host database with the given mode.
877  */
878 static sqlite *
879 smb_nic_dbopen(int mode)
880 {
881 	sqlite *db;
882 	char *errmsg = NULL;
883 
884 	db = sqlite_open(SMB_NIC_DB_NAME, mode, &errmsg);
885 	if (db == NULL) {
886 		syslog(LOG_ERR, "failed to open group database (%s)",
887 		    NULL_MSGCHK(errmsg));
888 		sqlite_freemem(errmsg);
889 	}
890 
891 	return (db);
892 }
893 
894 /*
895  * smb_nic_dbclose
896  *
897  * Closes the given database handle
898  */
899 static void
900 smb_nic_dbclose(sqlite *db)
901 {
902 	if (db) {
903 		sqlite_close(db);
904 	}
905 }
906 
907 static boolean_t
908 smb_nic_dbexists(void)
909 {
910 	return (access(SMB_NIC_DB_NAME, F_OK) == 0);
911 }
912 
913 static boolean_t
914 smb_nic_dbvalidate(void)
915 {
916 	sqlite *db;
917 	char *errmsg = NULL;
918 	char *sql;
919 	char **result;
920 	int nrow, ncol;
921 	boolean_t check = B_TRUE;
922 	int rc;
923 
924 	sql = sqlite_mprintf("SELECT * FROM db_info");
925 	if (sql == NULL)
926 		return (B_FALSE);
927 
928 	db = smb_nic_dbopen(SMB_NIC_DB_ORW);
929 	if (db == NULL) {
930 		sqlite_freemem(sql);
931 		return (B_FALSE);
932 	}
933 
934 	rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
935 	sqlite_freemem(sql);
936 
937 	if (rc != SQLITE_OK) {
938 		syslog(LOG_DEBUG, "smb_nic_dbvalidate: failed to get db_info"
939 		    " (%s)", NULL_MSGCHK(errmsg));
940 		sqlite_freemem(errmsg);
941 		smb_nic_dbclose(db);
942 		return (B_FALSE);
943 	}
944 
945 	if (nrow != 1 || ncol != 3) {
946 		syslog(LOG_DEBUG, "smb_nic_dbvalidate: bad db_info table");
947 		sqlite_free_table(result);
948 		smb_nic_dbclose(db);
949 		return (B_FALSE);
950 	}
951 
952 	if ((atoi(result[3]) != SMB_NIC_DB_VERMAJOR) ||
953 	    (atoi(result[4]) != SMB_NIC_DB_VERMINOR) ||
954 	    (atoi(result[5]) != SMB_NIC_DB_MAGIC)) {
955 		syslog(LOG_DEBUG, "smb_nic_dbvalidate: bad db_info content");
956 		sqlite_free_table(result);
957 		smb_nic_dbclose(db);
958 		return (B_FALSE);
959 	}
960 	sqlite_free_table(result);
961 
962 	sql = sqlite_mprintf("SELECT hostname FROM hosts");
963 	if (sql == NULL) {
964 		smb_nic_dbclose(db);
965 		return (B_FALSE);
966 	}
967 
968 	rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
969 	sqlite_freemem(sql);
970 
971 	if (rc != SQLITE_OK) {
972 		syslog(LOG_DEBUG, "smb_nic_dbvalidate: failed to count (%s)",
973 		    NULL_MSGCHK(errmsg));
974 		sqlite_freemem(errmsg);
975 		smb_nic_dbclose(db);
976 		return (B_FALSE);
977 	}
978 
979 	sqlite_free_table(result);
980 
981 	if (nrow == 0)
982 		/* No hosts in the database */
983 		check = B_FALSE;
984 
985 	smb_nic_dbclose(db);
986 	return (check);
987 }
988 
989 static int
990 smb_nic_dbaddhost(const char *host, const char *cmnt, char *if_list)
991 {
992 	sqlite *db;
993 	char *sql;
994 	char *errmsg;
995 	int rc;
996 
997 	sql = sqlite_mprintf("REPLACE INTO hosts (hostname, comment, ifnames)"
998 	    "VALUES ('%s', '%q', '%s')", host, (cmnt) ? cmnt : "", if_list);
999 	if (sql == NULL)
1000 		return (SQLITE_NOMEM);
1001 
1002 	db = smb_nic_dbopen(SMB_NIC_DB_ORW);
1003 	if (db == NULL) {
1004 		sqlite_freemem(sql);
1005 		return (SQLITE_CANTOPEN);
1006 	}
1007 
1008 	rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1009 	sqlite_freemem(sql);
1010 	smb_nic_dbclose(db);
1011 
1012 	if (rc != SQLITE_OK) {
1013 		syslog(LOG_DEBUG, "smb_nic_dbaddhost: failed to insert %s (%s)",
1014 		    host, NULL_MSGCHK(errmsg));
1015 		sqlite_freemem(errmsg);
1016 	}
1017 
1018 	return (rc);
1019 }
1020 
1021 static int
1022 smb_nic_dbdelhost(const char *host)
1023 {
1024 	sqlite *db;
1025 	char *sql;
1026 	char *errmsg;
1027 	int rc;
1028 
1029 	sql = sqlite_mprintf("DELETE FROM hosts WHERE hostname = '%s'", host);
1030 	if (sql == NULL)
1031 		return (SQLITE_NOMEM);
1032 
1033 	db = smb_nic_dbopen(SMB_NIC_DB_ORW);
1034 	if (db == NULL) {
1035 		sqlite_freemem(sql);
1036 		return (SQLITE_CANTOPEN);
1037 	}
1038 
1039 	rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1040 	sqlite_freemem(sql);
1041 	smb_nic_dbclose(db);
1042 
1043 	if (rc != SQLITE_OK) {
1044 		syslog(LOG_DEBUG, "smb_nic_dbdelhost: failed to delete %s (%s)",
1045 		    host, NULL_MSGCHK(errmsg));
1046 		sqlite_freemem(errmsg);
1047 	}
1048 
1049 	return (rc);
1050 }
1051 
1052 /*
1053  * smb_nic_dbsetinfo
1054  *
1055  * Initializes the db_info table upon database creation.
1056  */
1057 static int
1058 smb_nic_dbsetinfo(sqlite *db)
1059 {
1060 	char *errmsg = NULL;
1061 	char *sql;
1062 	int rc;
1063 
1064 	sql = sqlite_mprintf("INSERT INTO db_info (ver_major, ver_minor,"
1065 	    " magic) VALUES (%d, %d, %d)", SMB_NIC_DB_VERMAJOR,
1066 	    SMB_NIC_DB_VERMINOR, SMB_NIC_DB_MAGIC);
1067 
1068 	if (sql == NULL)
1069 		return (SQLITE_NOMEM);
1070 
1071 	rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1072 	sqlite_freemem(sql);
1073 	if (rc != SQLITE_OK) {
1074 		syslog(LOG_DEBUG, "smb_nic_dbsetinfo: failed to insert database"
1075 		    " information (%s)", NULL_MSGCHK(errmsg));
1076 		sqlite_freemem(errmsg);
1077 	}
1078 
1079 	return (rc);
1080 }
1081