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