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