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