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