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 /*
  23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  * Copyright 2015 Joyent, Inc.
  26  */
  27 
  28 /*      Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
  29 /*        All Rights Reserved   */
  30 
  31 /*
  32  * Portions of this source code were derived from Berkeley 4.3 BSD
  33  * under license from the Regents of the University of California.
  34  */
  35 
  36 #include "mt.h"
  37 #include "../rpc/rpc_mt.h"              /* for MT declarations only */
  38 #include <rpc/types.h>
  39 #include <stdio.h>
  40 #include <stdlib.h>
  41 #include <string.h>
  42 #include <ctype.h>
  43 #include <netconfig.h>
  44 #include <malloc.h>
  45 #include <libintl.h>
  46 #include <syslog.h>
  47 #include <zone.h>
  48 #include "netcspace.h"
  49 
  50 #define FAILURE  (unsigned)(-1)
  51 
  52 /*
  53  *      Local routines used by the library procedures
  54  */
  55 
  56 static int blank(char *);
  57 static int comment(char *);
  58 static struct netconfig *fgetnetconfig(FILE *, char *);
  59 static void netconfig_free(struct netconfig *);
  60 static unsigned int getflag(char *);
  61 static char **getlookups(char *);
  62 static struct netconfig **getnetlist(void);
  63 static unsigned int getnlookups(char *);
  64 static char *gettoken(char *, int);
  65 static unsigned int getvalue(char *, struct nc_data nc_data[]);
  66 static void shift1left(char *);
  67 static void netlist_free(struct netconfig ***);
  68 static void free_entry(void *);
  69 static struct netconfig *netconfig_dup(struct netconfig *);
  70 
  71 extern const char __nsl_dom[];
  72 
  73 static int (*brand_get_sz)(void) = NULL;
  74 static struct netconfig *(*brand_get_net_ent)(int) = NULL;
  75 
  76 /*
  77  *      Static global variables used by the library procedures:
  78  *
  79  *      netpp - points to the beginning of the list of netconfig
  80  *              entries used by setnetconfig() and setnetpath().
  81  *              Once netpp is initialized, that memory is *never*
  82  *              released.  This was necessary to improve performance.
  83  *
  84  *      linenum - the current line number of the /etc/netconfig
  85  *                file (used for debugging and for nc_perror()).
  86  *
  87  *      fieldnum - the current field number of the current line
  88  *                 of /etc/netconfig (used for debugging and for
  89  *                 nc_perror()).
  90  *
  91  *      nc_error - the error condition encountered.
  92  */
  93 
  94 static struct netconfig **netpp = NULL;
  95 mutex_t netpp_mutex = DEFAULTMUTEX;
  96 /*
  97  * The following two variables are used by the /etc/netconfig parsing
  98  * routines, which will always be executed once, and within the netpp_mutex.
  99  * They are global to allow the nc_sperror routine to provide better
 100  * information to the user about /etc/netconfig file problems.
 101  */
 102 static int linenum = 0;                 /* "owned" by getnetlist() */
 103 static int fieldnum = 0;                /* "owned" by fgetnetconfig() */
 104 
 105 
 106 static int *
 107 __nc_error(void)
 108 {
 109         static pthread_key_t nc_error_key = PTHREAD_ONCE_KEY_NP;
 110         static int nc_error = NC_NOERROR;
 111         int *ret;
 112 
 113         if (thr_main())
 114                 return (&nc_error);
 115         ret = thr_get_storage(&nc_error_key, sizeof (int), free);
 116         /* if thr_get_storage fails we return the address of nc_error */
 117         return (ret ? ret : &nc_error);
 118 }
 119 #define nc_error        (*(__nc_error()))
 120 
 121 /*
 122  *      setnetconfig() has the effect of "initializing" the
 123  *      network configuration database.   It reads in the
 124  *      netcf entries (if not already read in).
 125  */
 126 
 127 void *
 128 setnetconfig(void)
 129 {
 130         NCONF_HANDLE *retp;
 131 
 132         (void) mutex_lock(&netpp_mutex);
 133         if ((netpp == NULL) && ((netpp = getnetlist()) == NULL)) {
 134                 (void) mutex_unlock(&netpp_mutex);
 135                 return (NULL);
 136         }
 137         (void) mutex_unlock(&netpp_mutex);
 138         if ((retp = malloc(sizeof (NCONF_HANDLE))) == NULL) {
 139                 nc_error = NC_NOMEM;
 140                 return (NULL);
 141         }
 142         nc_error = NC_NOERROR;
 143         retp->nc_head = retp->nc_curr = netpp;
 144         return ((void *)retp);
 145 }
 146 
 147 /*
 148  *      endnetconfig() frees up all data allocated by setnetconfig()
 149  */
 150 
 151 int
 152 endnetconfig(void *vdata)
 153 {
 154         NCONF_HANDLE *nconf_handlep = (NCONF_HANDLE *)vdata;
 155 
 156         (void) mutex_lock(&netpp_mutex);
 157         if (netpp == NULL || nconf_handlep == NULL) {
 158                 nc_error = NC_NOSET;
 159                 (void) mutex_unlock(&netpp_mutex);
 160                 return (-1);
 161         }
 162         (void) mutex_unlock(&netpp_mutex);
 163 
 164         nc_error = NC_NOERROR;
 165         free(nconf_handlep);
 166         return (0);
 167 }
 168 
 169 /*
 170  *      getnetconfig() returns the current entry in the list
 171  *      of netconfig structures.  It uses the nconf_handlep argument
 172  *      to determine the current entry. If setnetconfig() was not
 173  *      called previously to set up the list, return failure.
 174  *      It also check if ipv6 interface is present(ipv6_present) and
 175  *      skips udp6 & tcp6 entries if ipv6 is not supported.
 176  */
 177 
 178 struct netconfig *
 179 getnetconfig(void *vdata)
 180 {
 181         NCONF_HANDLE *nconf_handlep = (NCONF_HANDLE *)vdata;
 182         struct netconfig *retp;  /* holds the return value */
 183         int ipv6_present = -1;
 184 
 185         (void) mutex_lock(&netpp_mutex);
 186         if ((netpp == NULL) || (nconf_handlep == NULL)) {
 187                 nc_error = NC_NOSET;
 188                 (void) mutex_unlock(&netpp_mutex);
 189                 return (NULL);
 190         }
 191         (void) mutex_unlock(&netpp_mutex);
 192         for (;;) {
 193                 retp = *(nconf_handlep->nc_curr);
 194                 if (retp && (strcmp(retp->nc_netid, "udp6") == 0 ||
 195                     strcmp(retp->nc_netid, "tcp6") == 0)) {
 196                         if (ipv6_present == -1)
 197                                 ipv6_present = __can_use_af(AF_INET6);
 198                         if (!ipv6_present) {
 199                                 ++(nconf_handlep->nc_curr);
 200                                 continue;
 201                         }
 202                 }
 203                 break;
 204         }
 205         if (retp != NULL) {
 206                 ++(nconf_handlep->nc_curr);
 207                 nc_error = NC_NOERROR;
 208         } else {
 209                 nc_error = NC_NOMOREENTRIES;
 210         }
 211         return (retp);
 212 }
 213 
 214 /*
 215  *      getnetconfig() searches the netconfig database for a
 216  *      given network id.  Returns a pointer to the netconfig
 217  *      structure or a NULL if not found.
 218  *      It also check if ipv6 interface is present(ipv6_present) and
 219  *      skips udp6 & tcp6 entries if ipv6 is not supported.
 220  */
 221 
 222 struct netconfig *
 223 getnetconfigent(const char *netid)
 224 {
 225         struct netconfig **tpp;
 226         int ipv6_present;
 227 
 228         (void) mutex_lock(&netpp_mutex);
 229         if ((netpp == NULL) && ((netpp = getnetlist()) == NULL)) {
 230                 (void) mutex_unlock(&netpp_mutex);
 231                 return (NULL);
 232         }
 233         (void) mutex_unlock(&netpp_mutex);
 234         for (tpp = netpp; *tpp; tpp++) {
 235                 if (strcmp((*tpp)->nc_netid, netid) == 0) {
 236                         if (*tpp && (strcmp((*tpp)->nc_netid, "udp6") == 0 ||
 237                             strcmp((*tpp)->nc_netid, "tcp6") == 0)) {
 238                                 ipv6_present = __can_use_af(AF_INET6);
 239                                 if (!ipv6_present) {
 240                                         nc_error = NC_NOTFOUND;
 241                                         return (NULL);
 242                                 }
 243                         }
 244                         return (netconfig_dup(*tpp));
 245                 }
 246         }
 247         nc_error = NC_NOTFOUND;
 248         return (NULL);
 249 }
 250 
 251 /*
 252  *      freenetconfigent frees the data allocated by getnetconfigent()
 253  */
 254 
 255 void
 256 freenetconfigent(struct netconfig *netp)
 257 {
 258         netconfig_free(netp);
 259 }
 260 
 261 void
 262 _nsl_brand_set_hooks(int (*set_sz_func)(void),
 263     struct netconfig *(*get_ent_func)(int))
 264 {
 265         brand_get_sz = set_sz_func;
 266         brand_get_net_ent = get_ent_func;
 267 }
 268 
 269 /*
 270  *      getnetlist() reads the netconfig file and creates a
 271  *      NULL-terminated list of entries.
 272  *      Returns the pointer to the head of the list or a NULL
 273  *      on failure.
 274  */
 275 
 276 static struct netconfig **
 277 getnetlist(void)
 278 {
 279         FILE *fp = NULL;        /* file stream for NETCONFIG */
 280         struct netconfig **listpp; /* the beginning of the netconfig list */
 281         struct netconfig **tpp; /* used to traverse the netconfig list */
 282         int count;              /* the number of entries in file */
 283         char nc_path[MAXPATHLEN];
 284         const char *zroot = zone_get_nroot();
 285 
 286         /*
 287          * If we are running in a branded zone, ensure we use the "/native"
 288          * prefix when opening the netconfig file:
 289          */
 290         (void) snprintf(nc_path, sizeof (nc_path), "%s%s", zroot != NULL ?
 291             zroot : "", NETCONFIG);
 292 
 293         if (brand_get_sz != NULL) {
 294                 count = brand_get_sz();
 295         } else {
 296                 char line[BUFSIZ];      /* holds each line of NETCONFIG */
 297 
 298                 if ((fp = fopen(nc_path, "rF")) == NULL) {
 299                         nc_error = NC_OPENFAIL;
 300                         return (NULL);
 301                 }
 302 
 303                 count = 0;
 304                 while (fgets(line, BUFSIZ, fp)) {
 305                         if (!(blank(line) || comment(line))) {
 306                                 ++count;
 307                         }
 308                 }
 309                 rewind(fp);
 310         }
 311 
 312         if (count == 0) {
 313                 nc_error = NC_NOTFOUND;
 314                 if (fp != NULL)
 315                         (void) fclose(fp);
 316                 return (NULL);
 317         }
 318 
 319         if ((listpp = malloc((count + 1) *
 320             sizeof (struct netconfig *))) == NULL) {
 321                 nc_error = NC_NOMEM;
 322                 if (fp != NULL)
 323                         (void) fclose(fp);
 324                 return (NULL);
 325         }
 326 
 327         if (brand_get_net_ent != NULL) {
 328                 int i;
 329 
 330                 tpp = listpp;
 331                 for (i = 0; i < count; i++) {
 332                         *tpp = brand_get_net_ent(i);
 333                         tpp++;
 334                 }
 335                 *tpp = NULL;
 336                 nc_error = NC_NOMOREENTRIES;
 337         } else {
 338                 /*
 339                  *      The following loop fills in the list (loops until
 340                  *      fgetnetconfig() returns a NULL) and counts the
 341                  *      number of entries placed in the list.  Note that
 342                  *      when the loop is completed, the last entry in the
 343                  *      list will contain a NULL (signifying the end of
 344                  *      the list).
 345                  */
 346                 linenum = 0;
 347                 for (tpp = listpp; *tpp = fgetnetconfig(fp, NULL); tpp++)
 348                         ;
 349                 (void) fclose(fp);
 350 
 351                 if (nc_error != NC_NOMOREENTRIES) /* Something is screwed up */
 352                         netlist_free(&listpp);
 353         }
 354 
 355         return (listpp);
 356 }
 357 
 358 /*
 359  *      fgetnetconfig() parses a line of the netconfig file into
 360  *      a netconfig structure.  It returns a pointer to the
 361  *      structure of success and a NULL on failure or EOF.
 362  */
 363 
 364 static struct netconfig *
 365 fgetnetconfig(FILE *fp, char *netid)
 366 {
 367         char linep[BUFSIZ];     /* pointer to a line in the file */
 368         struct netconfig *netconfigp; /* holds the new netconfig structure */
 369         char  *tok1, *tok2, *tok3; /* holds a token from the line */
 370         char  *retvalp;         /* the return value of fgets() */
 371         char *entnetid;         /* netid for the current entry */
 372 
 373         /* skip past blank lines and comments. */
 374         while (retvalp = fgets(linep, BUFSIZ, fp)) {
 375                 linenum++;
 376                 if (!(blank(linep) || comment(linep))) {
 377                         break;
 378                 }
 379                 retvalp = NULL;
 380         }
 381         if (retvalp == NULL) {
 382                 nc_error = NC_NOMOREENTRIES;
 383                 return (NULL);
 384         }
 385         fieldnum = 0;
 386         if ((entnetid = gettoken(linep, FALSE)) == NULL) {
 387                 nc_error = NC_BADLINE;
 388                 return (NULL);
 389         }
 390         if (netid && (strcmp(netid, entnetid) != 0)) {
 391                 free(entnetid);
 392                 nc_error = NC_NOTFOUND;
 393                 return (NULL);
 394         }
 395         if ((netconfigp = calloc(1, sizeof (struct netconfig))) == NULL) {
 396                 free(entnetid);
 397                 nc_error = NC_NOMEM;
 398                 return (NULL);
 399         }
 400 
 401         tok1 = tok2 = tok3 = NULL;
 402         netconfigp->nc_netid = entnetid;
 403         if (((tok1 = gettoken(NULL, FALSE)) == NULL) ||
 404             ((netconfigp->nc_semantics =
 405                 getvalue(tok1, nc_semantics)) == FAILURE) ||
 406             ((tok2 = gettoken(NULL, FALSE)) == NULL) ||
 407             ((netconfigp->nc_flag = getflag(tok2)) == FAILURE) ||
 408             ((netconfigp->nc_protofmly = gettoken(NULL, FALSE)) == NULL) ||
 409             ((netconfigp->nc_proto = gettoken(NULL, FALSE)) == NULL) ||
 410             ((netconfigp->nc_device = gettoken(NULL, FALSE)) == NULL) ||
 411             ((tok3 = gettoken(NULL, TRUE)) == NULL) ||
 412             (((netconfigp->nc_nlookups = getnlookups(tok3)) != 0) &&
 413                 ((netconfigp->nc_lookups = getlookups(tok3)) == NULL))) {
 414                 netconfig_free(netconfigp);
 415                 nc_error = NC_BADLINE;
 416                 netconfigp = NULL;
 417         }
 418         free(tok1);
 419         free(tok2);
 420         free(tok3);
 421         return (netconfigp);
 422 }
 423 
 424 /*
 425  *      setnetpath() has the effect of "initializing" the
 426  *      NETPATH variable.  It reads in the netcf entries (if not
 427  *      already read in), creates a list corresponding to the entries
 428  *      in the NETPATH variable (or the "visible" entries og netconfig
 429  *      if NETPATH is not set).
 430  */
 431 
 432 void *
 433 setnetpath(void)
 434 {
 435         int count;                  /* the number of entries in NETPATH     */
 436         char valid_netpath[BUFSIZ]; /* holds the valid entries if NETPATH   */
 437         char templine[BUFSIZ];      /* has value of NETPATH when scanning   */
 438         struct netconfig **curr_pp; /* scans the list from NETPATH          */
 439         struct netconfig **tpp;     /* scans the list from netconfig file   */
 440         struct netconfig **rnetpp;  /* the list of entries from NETPATH     */
 441         char *netpath;              /* value of NETPATH from environment    */
 442         char *netid;                /* holds a component of NETPATH         */
 443         char *tp;                   /* used to scan NETPATH string          */
 444         NCONF_HANDLE *retp;         /* the return value                     */
 445 
 446         /*
 447          *      Read in the netconfig database if not already read in
 448          */
 449         (void) mutex_lock(&netpp_mutex);
 450         if ((netpp == NULL) && ((netpp = getnetlist()) == NULL)) {
 451                 (void) mutex_unlock(&netpp_mutex);
 452                 return (NULL);
 453         }
 454         (void) mutex_unlock(&netpp_mutex);
 455 
 456         if ((retp = malloc(sizeof (NCONF_HANDLE))) == NULL) {
 457                 nc_error = NC_NOMEM;
 458                 return (NULL);
 459         }
 460 
 461         /*
 462          *      Get the valid entries of the NETPATH variable (and
 463          *      count the number of entries while doing it).
 464          *
 465          *      This is done every time the procedure is called just
 466          *      in case NETPATH has changed from call to call.
 467          *
 468          *      If NETPATH is too long, we ignore it altogether as
 469          *      it can only be a buffer overflow attack.
 470          *      Since we add one colon for each entry, but colons only
 471          *      need to exist between entries, we have to subtract one.
 472          */
 473         count = 0;
 474         valid_netpath[0] = '\0';
 475         if ((netpath = getenv(NETPATH)) == NULL ||
 476             strlen(netpath) >= sizeof (templine) - 1) {
 477                 /*
 478                  *      If NETPATH variable is not set or invalid,
 479                  *      the valid NETPATH consist of all "visible"
 480                  *      netids from the netconfig database.
 481                  */
 482 
 483                 for (tpp = netpp; *tpp; tpp++) {
 484                         if ((*tpp)->nc_flag & NC_VISIBLE) {
 485                                 (void) strcat(valid_netpath, (*tpp)->nc_netid);
 486                                 (void) strcat(valid_netpath, ":");
 487                                 count++;
 488                         }
 489                 }
 490         } else {
 491 
 492                 /*
 493                  *      Copy the value of NETPATH (since '\0's will be
 494                  *      put into the string) and create the valid NETPATH
 495                  *      (by throwing away all netids not in the database).
 496                  *      If an entry appears more than one, it *will* be
 497                  *      listed twice in the list of valid netpath entries.
 498                  */
 499 
 500                 (void) strcpy(templine, netpath);
 501                 tp = templine;
 502 
 503                 while (*tp) {
 504                         /* Skip all leading ':'s */
 505                         while (*tp && *tp == ':')
 506                                 tp++;
 507                         if (*tp == NULL)
 508                                 break;  /* last one */
 509                         netid = tp;
 510                         while (*tp && *tp != ':')
 511                                 tp++;
 512                         if (*tp)
 513                                 *tp++ = '\0'; /* isolate netid */
 514 
 515                         for (tpp = netpp; *tpp; tpp++) {
 516                                 if (strcmp(netid, (*tpp)->nc_netid) == 0) {
 517                                         (void) strcat(valid_netpath,
 518                                                 (*tpp)->nc_netid);
 519                                         (void) strcat(valid_netpath, ":");
 520                                         count++;
 521                                         break;
 522                                 }
 523                         }
 524                 }
 525         }
 526 
 527         /* Get space to hold the valid list (+1 for the NULL) */
 528 
 529         if ((rnetpp = malloc((count + 1) *
 530                         sizeof (struct netconfig *))) == NULL) {
 531                 free(retp);
 532                 nc_error = NC_NOMEM;
 533                 return (NULL);
 534         }
 535 
 536         /*
 537          *      Populate the NETPATH list, ending it with a NULL.
 538          *      Each entry in the list points to the structure in the
 539          *      "netpp" list (the entry must exist in the list, otherwise
 540          *      it wouldn't appear in valid_netpath[]).
 541          */
 542 
 543         curr_pp = rnetpp;
 544         netid = tp = valid_netpath;
 545         while (*tp) {
 546                 netid = tp;
 547                 while (*tp && *tp != ':')
 548                         tp++;
 549                 if (*tp)
 550                         *tp++ = '\0';
 551                 for (tpp = netpp; *tpp; tpp++) {
 552                         if (strcmp(netid, (*tpp)->nc_netid) == 0) {
 553                                 *curr_pp++ = *tpp;
 554                                 break;
 555                         }
 556                 }
 557         }
 558         *curr_pp = NULL;
 559 
 560         retp->nc_curr = retp->nc_head = rnetpp;
 561         return ((void *)retp);
 562 }
 563 
 564 /*
 565  *      endnetpath() frees up all of the memory allocated by setnetpath().
 566  *      It returns -1 (error) if setnetpath was never called.
 567  */
 568 
 569 int
 570 endnetpath(void *vdata)
 571 {
 572         /* The argument is really a NCONF_HANDLE;  cast it here */
 573         NCONF_HANDLE *nconf_handlep = (NCONF_HANDLE *)vdata;
 574 
 575         (void) mutex_lock(&netpp_mutex);
 576         if (netpp == NULL || nconf_handlep == NULL) {
 577                 nc_error = NC_NOSET;
 578                 (void) mutex_unlock(&netpp_mutex);
 579                 return (-1);
 580         }
 581         (void) mutex_unlock(&netpp_mutex);
 582 
 583         free(nconf_handlep->nc_head);
 584         free(nconf_handlep);
 585         return (0);
 586 }
 587 
 588 /*
 589  *      getnetpath() returns the current entry in the list
 590  *      from the NETPATH variable.  If setnetpath() was not called
 591  *      previously to set up the list, return NULL.
 592  */
 593 
 594 struct netconfig *
 595 getnetpath(void *vdata)
 596 {
 597         /* The argument is really a NCONF_HANDLE;  cast it here */
 598         NCONF_HANDLE *nconf_handlep = (NCONF_HANDLE *)vdata;
 599         struct netconfig *retp;  /* holds the return value */
 600         int ipv6_present = -1;
 601 
 602         (void) mutex_lock(&netpp_mutex);
 603         if (netpp == NULL) {
 604                 nc_error = NC_NOSET;
 605                 (void) mutex_unlock(&netpp_mutex);
 606                 return (NULL);
 607         }
 608         (void) mutex_unlock(&netpp_mutex);
 609         for (;;) {
 610                 retp = *(nconf_handlep->nc_curr);
 611                 if (retp && (strcmp(retp->nc_netid, "udp6") == 0 ||
 612                     strcmp(retp->nc_netid, "tcp6") == 0)) {
 613                         if (ipv6_present == -1)
 614                                 ipv6_present = __can_use_af(AF_INET6);
 615                         if (!ipv6_present) {
 616                                 ++(nconf_handlep->nc_curr);
 617                                 continue;
 618                         }
 619                 }
 620                 break;
 621         }
 622         if (retp) {
 623                 ++(nconf_handlep->nc_curr);
 624                 nc_error = NC_NOERROR;
 625         } else {
 626                 nc_error = NC_NOMOREENTRIES;
 627         }
 628 
 629         return (retp);
 630 }
 631 
 632 /*
 633  *      blank() returns true if the line is a blank line, 0 otherwise
 634  */
 635 
 636 static int
 637 blank(char *cp)
 638 {
 639         while (*cp && isspace(*cp)) {
 640                 cp++;
 641         }
 642         return (*cp == '\0');
 643 }
 644 
 645 /*
 646  *      comment() returns true if the line is a comment, 0 otherwise.
 647  */
 648 
 649 static int
 650 comment(char *cp)
 651 {
 652         while (*cp && isspace(*cp)) {
 653                 cp++;
 654         }
 655         return (*cp == '#');
 656 }
 657 
 658 /*
 659  *      getvalue() searches for the given string in the given array,
 660  *      and return the integer value associated with the string.
 661  */
 662 
 663 static unsigned int
 664 getvalue(char *cp, struct nc_data nc_data[])
 665 {
 666         int i;  /* used to index through the given struct nc_data array */
 667 
 668         for (i = 0; nc_data[i].string; i++) {
 669                 if (strcmp(nc_data[i].string, cp) == 0) {
 670                         break;
 671                 }
 672         }
 673         return (nc_data[i].value);
 674 }
 675 
 676 /*
 677  *      getflag() creates a bitmap of the one-character flags in
 678  *      the given string.  It uses nc_flags array to get the values.
 679  */
 680 
 681 static unsigned int
 682 getflag(char *cp)
 683 {
 684         int i;                  /* indexs through the nc_flag array */
 685         unsigned int mask = 0; /* holds bitmask of flags */
 686 
 687         while (*cp) {
 688                 for (i = 0; nc_flag[i].string; i++) {
 689                         if (*nc_flag[i].string == *cp) {
 690                                 mask |= nc_flag[i].value;
 691                                 break;
 692                         }
 693                 }
 694                 cp++;
 695         }
 696         return (mask);
 697 }
 698 
 699 /*
 700  *      getlookups() creates and returns an array of string representing
 701  *      the directory lookup libraries, given as a comma-seperated list
 702  *      in the argument "cp".
 703  */
 704 
 705 static char **
 706 getlookups(char *cp)
 707 {
 708         unsigned int num;       /* holds the number of entries in the list   */
 709         char **listpp;          /* the beginning of the list of dir routines */
 710         char **tpp;             /* traverses the list, populating it */
 711         char *start;
 712 
 713         num = getnlookups(cp);
 714         if (num == 0)
 715                 return (NULL);
 716         if ((listpp = malloc((num + 1) * sizeof (char *))) == NULL)
 717                 return (NULL);
 718 
 719         tpp = listpp;
 720         while (num--) {
 721                 start  = cp;
 722 
 723                 /*
 724                  *      Traverse the string looking for the next entry
 725                  *      of the list (i.e, where the ',' or end of the
 726                  *      string appears).  If a "\" is found, shift the
 727                  *      token over 1 to the left (taking the next char
 728                  *      literally).
 729                  */
 730 
 731                 while (*cp && *cp != ',') {
 732                         if (*cp == '\\' && *(cp + 1)) {
 733                                 shift1left(cp);
 734                         }
 735                         cp++;
 736                 }
 737                 if (*cp)
 738                         *cp++ = '\0';
 739                 if ((*tpp++ = strdup(start)) == NULL) {
 740                         for (tpp = listpp; *tpp; tpp++)
 741                                 free(*tpp);
 742                         free(listpp);
 743                         return (NULL);
 744                 }
 745         }
 746         *tpp = NULL;
 747         return (listpp);
 748 }
 749 
 750 /*
 751  *      getnlookups() returns the number of entries in a comma-separated
 752  *      string of tokens.  A "-" means no strings are present.
 753  */
 754 
 755 static unsigned int
 756 getnlookups(char *cp)
 757 {
 758         unsigned int count;     /* the number of tokens in the string */
 759 
 760         if (strcmp(cp, "-") == 0)
 761                 return (0);
 762 
 763         count = 1;
 764         while (*cp) {
 765                 if (*cp == ',') {
 766                         count++;
 767                 }
 768 
 769                 /*
 770                  *      If a "\" is in the string, take the next character
 771                  *      literally.  Onlly skip the character if "\" is
 772                  *      not the last character of the token.
 773                  */
 774                 if (*cp == '\\' && *(cp + 1)) {
 775                         cp++;
 776                 }
 777                 cp++;
 778         }
 779         return (count);
 780 }
 781 
 782 /*
 783  *      gettoken() behaves much like strtok(), except that
 784  *      it knows about escaped space characters (i.e., space characters
 785  *      preceeded by a '\' are taken literally).
 786  */
 787 
 788 static char *
 789 gettoken(char *cp, int skip)
 790 {
 791         static char     *savep; /* the place where we left off    */
 792         char    *p;             /* the beginning of the new token */
 793         char    *retp;          /* the token to be returned       */
 794 
 795         fieldnum++;
 796 
 797         /* Determine if first or subsequent call  */
 798         p = (cp == NULL)? savep: cp;
 799 
 800         /* Return if no tokens remain.  */
 801         if (p == 0)
 802                 return (NULL);
 803 
 804         while (isspace(*p))
 805                 p++;
 806 
 807         if (*p == '\0')
 808                 return (NULL);
 809 
 810         /*
 811          *      Save the location of the token and then skip past it
 812          */
 813 
 814         retp = p;
 815         while (*p) {
 816                 if (isspace(*p))
 817                         if (skip == TRUE) {
 818                                 shift1left(p);
 819                                 continue;
 820                         } else
 821                                 break;
 822                 /*
 823                  *      Only process the escape of the space seperator;
 824                  *      since the token may contain other separators,
 825                  *      let the other routines handle the escape of
 826                  *      specific characters in the token.
 827                  */
 828 
 829                 if (*p == '\\' && *(p + 1) != '\n' && isspace(*(p + 1))) {
 830                         shift1left(p);
 831                 }
 832                 p++;
 833         }
 834         if (*p == '\0') {
 835                 savep = 0;      /* indicate this is last token */
 836         } else {
 837                 *p = '\0';
 838                 savep = ++p;
 839         }
 840         return (strdup(retp));
 841 }
 842 
 843 /*
 844  *      shift1left() moves all characters in the string over 1 to
 845  *      the left.
 846  */
 847 
 848 static void
 849 shift1left(char *p)
 850 {
 851         for (; *p; p++)
 852                 *p = *(p + 1);
 853 }
 854 
 855 char *
 856 nc_sperror(void)
 857 {
 858         static char buf_main[BUFSIZ];
 859         static pthread_key_t perror_key = PTHREAD_ONCE_KEY_NP;
 860         char *retstr = thr_main()?
 861                 buf_main :
 862                 thr_get_storage(&perror_key, BUFSIZ, free);
 863 
 864         if (retstr == NULL) {
 865                 syslog(LOG_WARNING,
 866                 "nc_sperror: malloc failed when trying to create buffer\n");
 867                 return (NULL);
 868         }
 869 
 870         switch (nc_error) {
 871         case NC_NOERROR:
 872                 (void) strlcpy(retstr, dgettext(__nsl_dom, "no error"), BUFSIZ);
 873                 break;
 874         case NC_NOMEM:
 875                 (void) strlcpy(retstr, dgettext(__nsl_dom, "out of memory"),
 876                     BUFSIZ);
 877                 break;
 878         case NC_NOSET:
 879                 (void) strlcpy(retstr, dgettext(__nsl_dom,
 880                     "routine called before calling \
 881                     setnetpath() or setnetconfig()"), BUFSIZ);
 882                 break;
 883         case NC_OPENFAIL:
 884                 (void) strlcpy(retstr,
 885                         dgettext(__nsl_dom, "cannot open /etc/netconfig"),
 886                         BUFSIZ);
 887                 break;
 888         case NC_BADLINE:
 889                 (void) snprintf(retstr, BUFSIZ, dgettext(__nsl_dom,
 890                         "error in /etc/netconfig: field %d of line %d\n"),
 891                                 fieldnum, linenum);
 892                 break;
 893         case NC_NOTFOUND:
 894                 (void) snprintf(retstr, BUFSIZ,
 895                         dgettext(__nsl_dom,
 896                                 "netid not found in /etc/netconfig"));
 897                 break;
 898         case NC_NOMOREENTRIES:
 899                 (void) snprintf(retstr, BUFSIZ,
 900                         dgettext(__nsl_dom,
 901                                 "no more entries in /etc/netconfig"));
 902                 break;
 903         default:
 904                 (void) strlcpy(retstr, dgettext(__nsl_dom, "unknown error"),
 905                     BUFSIZ);
 906                 break;
 907         }
 908         return (retstr);
 909 }
 910 
 911 void
 912 nc_perror(const char *string)
 913 {
 914         if (string)
 915                 (void) fprintf(stderr, "%s: %s\n", string, nc_sperror());
 916         else
 917                 (void) fprintf(stderr, "%s\n", nc_sperror());
 918 }
 919 
 920 static void
 921 netlist_free(struct netconfig ***netppp)
 922 {
 923         struct netconfig **tpp;
 924 
 925         for (tpp = *netppp; *tpp; tpp++) {
 926                 netconfig_free(*tpp);
 927         }
 928         free(*netppp);
 929         *netppp = NULL;
 930 }
 931 
 932 static void
 933 netconfig_free(struct netconfig *netconfigp)
 934 {
 935         int i;
 936 
 937         if (netconfigp == NULL)
 938                 return;
 939         free_entry(netconfigp->nc_netid);
 940         free_entry(netconfigp->nc_protofmly);
 941         free_entry(netconfigp->nc_proto);
 942         free_entry(netconfigp->nc_device);
 943         if (netconfigp->nc_lookups)
 944                 for (i = 0; i < netconfigp->nc_nlookups; i++)
 945                         free_entry(netconfigp->nc_lookups[i]);
 946         free_entry(netconfigp->nc_lookups);
 947         free(netconfigp);
 948 }
 949 
 950 static struct netconfig *
 951 netconfig_dup(struct netconfig *netconfigp)
 952 {
 953         struct netconfig *nconf;
 954         int i;
 955 
 956         nconf = calloc(1, sizeof (struct netconfig));
 957         if (nconf == NULL) {
 958                 nc_error = NC_NOMEM;
 959                 return (NULL);
 960         }
 961         nconf->nc_netid = strdup(netconfigp->nc_netid);
 962         nconf->nc_protofmly = strdup(netconfigp->nc_protofmly);
 963         nconf->nc_proto = strdup(netconfigp->nc_proto);
 964         nconf->nc_device = strdup(netconfigp->nc_device);
 965         nconf->nc_lookups = malloc((netconfigp->nc_nlookups + 1)
 966                                         * sizeof (char *));
 967         if (!(nconf->nc_lookups && nconf->nc_netid &&
 968                 nconf->nc_protofmly && nconf->nc_proto &&
 969                 nconf->nc_device)) {
 970                 nc_error = NC_NOMEM;
 971                 netconfig_free(nconf);
 972                 return (NULL);
 973         }
 974 
 975         for (i = 0; i < netconfigp->nc_nlookups; i++) {
 976                 nconf->nc_lookups[i] = strdup(netconfigp->nc_lookups[i]);
 977                 if (nconf->nc_lookups[i] == NULL) {
 978                         nconf->nc_nlookups = i;
 979                         netconfig_free(nconf);
 980                         nc_error = NC_NOMEM;
 981                         return (NULL);
 982                 }
 983         }
 984         nconf->nc_lookups[i] = NULL;
 985         nconf->nc_nlookups = netconfigp->nc_nlookups;
 986         nconf->nc_flag = netconfigp->nc_flag;
 987         nconf->nc_semantics = netconfigp->nc_semantics;
 988         return (nconf);
 989 }
 990 
 991 static void
 992 free_entry(void *foo)
 993 {
 994         if (foo)
 995                 free(foo);
 996 }