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