1*ce0e08e2SPeter Avalos /* 2*ce0e08e2SPeter Avalos * Sun RPC is a product of Sun Microsystems, Inc. and is provided for 3*ce0e08e2SPeter Avalos * unrestricted use provided that this legend is included on all tape 4*ce0e08e2SPeter Avalos * media and as a part of the software program in whole or part. Users 5*ce0e08e2SPeter Avalos * may copy or modify Sun RPC without charge, but are not authorized 6*ce0e08e2SPeter Avalos * to license or distribute it to anyone else except as part of a product or 7*ce0e08e2SPeter Avalos * program developed by the user or with the express written consent of 8*ce0e08e2SPeter Avalos * Sun Microsystems, Inc. 9*ce0e08e2SPeter Avalos * 10*ce0e08e2SPeter Avalos * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE 11*ce0e08e2SPeter Avalos * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR 12*ce0e08e2SPeter Avalos * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. 13*ce0e08e2SPeter Avalos * 14*ce0e08e2SPeter Avalos * Sun RPC is provided with no support and without any obligation on the 15*ce0e08e2SPeter Avalos * part of Sun Microsystems, Inc. to assist in its use, correction, 16*ce0e08e2SPeter Avalos * modification or enhancement. 17*ce0e08e2SPeter Avalos * 18*ce0e08e2SPeter Avalos * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE 19*ce0e08e2SPeter Avalos * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC 20*ce0e08e2SPeter Avalos * OR ANY PART THEREOF. 21*ce0e08e2SPeter Avalos * 22*ce0e08e2SPeter Avalos * In no event will Sun Microsystems, Inc. be liable for any lost revenue 23*ce0e08e2SPeter Avalos * or profits or other special, indirect and consequential damages, even if 24*ce0e08e2SPeter Avalos * Sun has been advised of the possibility of such damages. 25*ce0e08e2SPeter Avalos * 26*ce0e08e2SPeter Avalos * Sun Microsystems, Inc. 27*ce0e08e2SPeter Avalos * 2550 Garcia Avenue 28*ce0e08e2SPeter Avalos * Mountain View, California 94043 29*ce0e08e2SPeter Avalos * 30*ce0e08e2SPeter Avalos * @(#)getnetconfig.c 1.12 91/12/19 SMI 31*ce0e08e2SPeter Avalos * $NetBSD: getnetconfig.c,v 1.3 2000/07/06 03:10:34 christos Exp $ 32*ce0e08e2SPeter Avalos * $FreeBSD: src/lib/libc/rpc/getnetconfig.c,v 1.14 2007/09/20 22:35:24 matteo Exp $ 33*ce0e08e2SPeter Avalos * $DragonFly$ 34*ce0e08e2SPeter Avalos */ 35*ce0e08e2SPeter Avalos 36*ce0e08e2SPeter Avalos /* 37*ce0e08e2SPeter Avalos * Copyright (c) 1989 by Sun Microsystems, Inc. 38*ce0e08e2SPeter Avalos */ 39*ce0e08e2SPeter Avalos 40*ce0e08e2SPeter Avalos #include "namespace.h" 41*ce0e08e2SPeter Avalos #include "reentrant.h" 42*ce0e08e2SPeter Avalos #include <stdio.h> 43*ce0e08e2SPeter Avalos #include <errno.h> 44*ce0e08e2SPeter Avalos #include <netconfig.h> 45*ce0e08e2SPeter Avalos #include <stddef.h> 46*ce0e08e2SPeter Avalos #include <stdlib.h> 47*ce0e08e2SPeter Avalos #include <string.h> 48*ce0e08e2SPeter Avalos #include <rpc/rpc.h> 49*ce0e08e2SPeter Avalos #include <unistd.h> 50*ce0e08e2SPeter Avalos #include "un-namespace.h" 51*ce0e08e2SPeter Avalos #include "rpc_com.h" 52*ce0e08e2SPeter Avalos 53*ce0e08e2SPeter Avalos /* 54*ce0e08e2SPeter Avalos * The five library routines in this file provide application access to the 55*ce0e08e2SPeter Avalos * system network configuration database, /etc/netconfig. In addition to the 56*ce0e08e2SPeter Avalos * netconfig database and the routines for accessing it, the environment 57*ce0e08e2SPeter Avalos * variable NETPATH and its corresponding routines in getnetpath.c may also be 58*ce0e08e2SPeter Avalos * used to specify the network transport to be used. 59*ce0e08e2SPeter Avalos */ 60*ce0e08e2SPeter Avalos 61*ce0e08e2SPeter Avalos 62*ce0e08e2SPeter Avalos /* 63*ce0e08e2SPeter Avalos * netconfig errors 64*ce0e08e2SPeter Avalos */ 65*ce0e08e2SPeter Avalos 66*ce0e08e2SPeter Avalos #define NC_NONETCONFIG ENOENT 67*ce0e08e2SPeter Avalos #define NC_NOMEM ENOMEM 68*ce0e08e2SPeter Avalos #define NC_NOTINIT EINVAL /* setnetconfig was not called first */ 69*ce0e08e2SPeter Avalos #define NC_BADFILE EBADF /* format for netconfig file is bad */ 70*ce0e08e2SPeter Avalos #define NC_NOTFOUND ENOPROTOOPT /* specified netid was not found */ 71*ce0e08e2SPeter Avalos 72*ce0e08e2SPeter Avalos /* 73*ce0e08e2SPeter Avalos * semantics as strings (should be in netconfig.h) 74*ce0e08e2SPeter Avalos */ 75*ce0e08e2SPeter Avalos #define NC_TPI_CLTS_S "tpi_clts" 76*ce0e08e2SPeter Avalos #define NC_TPI_COTS_S "tpi_cots" 77*ce0e08e2SPeter Avalos #define NC_TPI_COTS_ORD_S "tpi_cots_ord" 78*ce0e08e2SPeter Avalos #define NC_TPI_RAW_S "tpi_raw" 79*ce0e08e2SPeter Avalos 80*ce0e08e2SPeter Avalos /* 81*ce0e08e2SPeter Avalos * flags as characters (also should be in netconfig.h) 82*ce0e08e2SPeter Avalos */ 83*ce0e08e2SPeter Avalos #define NC_NOFLAG_C '-' 84*ce0e08e2SPeter Avalos #define NC_VISIBLE_C 'v' 85*ce0e08e2SPeter Avalos #define NC_BROADCAST_C 'b' 86*ce0e08e2SPeter Avalos 87*ce0e08e2SPeter Avalos /* 88*ce0e08e2SPeter Avalos * Character used to indicate there is no name-to-address lookup library 89*ce0e08e2SPeter Avalos */ 90*ce0e08e2SPeter Avalos #define NC_NOLOOKUP "-" 91*ce0e08e2SPeter Avalos 92*ce0e08e2SPeter Avalos static const char * const _nc_errors[] = { 93*ce0e08e2SPeter Avalos "Netconfig database not found", 94*ce0e08e2SPeter Avalos "Not enough memory", 95*ce0e08e2SPeter Avalos "Not initialized", 96*ce0e08e2SPeter Avalos "Netconfig database has invalid format", 97*ce0e08e2SPeter Avalos "Netid not found in netconfig database" 98*ce0e08e2SPeter Avalos }; 99*ce0e08e2SPeter Avalos 100*ce0e08e2SPeter Avalos struct netconfig_info { 101*ce0e08e2SPeter Avalos int eof; /* all entries has been read */ 102*ce0e08e2SPeter Avalos int ref; /* # of times setnetconfig() has been called */ 103*ce0e08e2SPeter Avalos struct netconfig_list *head; /* head of the list */ 104*ce0e08e2SPeter Avalos struct netconfig_list *tail; /* last of the list */ 105*ce0e08e2SPeter Avalos }; 106*ce0e08e2SPeter Avalos 107*ce0e08e2SPeter Avalos struct netconfig_list { 108*ce0e08e2SPeter Avalos char *linep; /* hold line read from netconfig */ 109*ce0e08e2SPeter Avalos struct netconfig *ncp; 110*ce0e08e2SPeter Avalos struct netconfig_list *next; 111*ce0e08e2SPeter Avalos }; 112*ce0e08e2SPeter Avalos 113*ce0e08e2SPeter Avalos struct netconfig_vars { 114*ce0e08e2SPeter Avalos int valid; /* token that indicates a valid netconfig_vars */ 115*ce0e08e2SPeter Avalos int flag; /* first time flag */ 116*ce0e08e2SPeter Avalos struct netconfig_list *nc_configs; /* pointer to the current netconfig entry */ 117*ce0e08e2SPeter Avalos }; 118*ce0e08e2SPeter Avalos 119*ce0e08e2SPeter Avalos #define NC_VALID 0xfeed 120*ce0e08e2SPeter Avalos #define NC_STORAGE 0xf00d 121*ce0e08e2SPeter Avalos #define NC_INVALID 0 122*ce0e08e2SPeter Avalos 123*ce0e08e2SPeter Avalos 124*ce0e08e2SPeter Avalos static int *__nc_error(void); 125*ce0e08e2SPeter Avalos static int parse_ncp(char *, struct netconfig *); 126*ce0e08e2SPeter Avalos static struct netconfig *dup_ncp(struct netconfig *); 127*ce0e08e2SPeter Avalos 128*ce0e08e2SPeter Avalos 129*ce0e08e2SPeter Avalos static FILE *nc_file; /* for netconfig db */ 130*ce0e08e2SPeter Avalos static struct netconfig_info ni = { 0, 0, NULL, NULL}; 131*ce0e08e2SPeter Avalos 132*ce0e08e2SPeter Avalos #define MAXNETCONFIGLINE 1000 133*ce0e08e2SPeter Avalos 134*ce0e08e2SPeter Avalos static int * 135*ce0e08e2SPeter Avalos __nc_error(void) 136*ce0e08e2SPeter Avalos { 137*ce0e08e2SPeter Avalos static pthread_mutex_t nc_lock = PTHREAD_MUTEX_INITIALIZER; 138*ce0e08e2SPeter Avalos static thread_key_t nc_key = 0; 139*ce0e08e2SPeter Avalos static int nc_error = 0; 140*ce0e08e2SPeter Avalos int error, *nc_addr; 141*ce0e08e2SPeter Avalos 142*ce0e08e2SPeter Avalos /* 143*ce0e08e2SPeter Avalos * Use the static `nc_error' if we are the main thread 144*ce0e08e2SPeter Avalos * (including non-threaded programs), or if an allocation 145*ce0e08e2SPeter Avalos * fails. 146*ce0e08e2SPeter Avalos */ 147*ce0e08e2SPeter Avalos if (thr_main()) 148*ce0e08e2SPeter Avalos return (&nc_error); 149*ce0e08e2SPeter Avalos if (nc_key == 0) { 150*ce0e08e2SPeter Avalos error = 0; 151*ce0e08e2SPeter Avalos mutex_lock(&nc_lock); 152*ce0e08e2SPeter Avalos if (nc_key == 0) 153*ce0e08e2SPeter Avalos error = thr_keycreate(&nc_key, free); 154*ce0e08e2SPeter Avalos mutex_unlock(&nc_lock); 155*ce0e08e2SPeter Avalos if (error) 156*ce0e08e2SPeter Avalos return (&nc_error); 157*ce0e08e2SPeter Avalos } 158*ce0e08e2SPeter Avalos if ((nc_addr = (int *)thr_getspecific(nc_key)) == NULL) { 159*ce0e08e2SPeter Avalos nc_addr = (int *)malloc(sizeof (int)); 160*ce0e08e2SPeter Avalos if (thr_setspecific(nc_key, (void *) nc_addr) != 0) { 161*ce0e08e2SPeter Avalos if (nc_addr) 162*ce0e08e2SPeter Avalos free(nc_addr); 163*ce0e08e2SPeter Avalos return (&nc_error); 164*ce0e08e2SPeter Avalos } 165*ce0e08e2SPeter Avalos *nc_addr = 0; 166*ce0e08e2SPeter Avalos } 167*ce0e08e2SPeter Avalos return (nc_addr); 168*ce0e08e2SPeter Avalos } 169*ce0e08e2SPeter Avalos 170*ce0e08e2SPeter Avalos #define nc_error (*(__nc_error())) 171*ce0e08e2SPeter Avalos /* 172*ce0e08e2SPeter Avalos * A call to setnetconfig() establishes a /etc/netconfig "session". A session 173*ce0e08e2SPeter Avalos * "handle" is returned on a successful call. At the start of a session (after 174*ce0e08e2SPeter Avalos * a call to setnetconfig()) searches through the /etc/netconfig database will 175*ce0e08e2SPeter Avalos * proceed from the start of the file. The session handle must be passed to 176*ce0e08e2SPeter Avalos * getnetconfig() to parse the file. Each call to getnetconfig() using the 177*ce0e08e2SPeter Avalos * current handle will process one subsequent entry in /etc/netconfig. 178*ce0e08e2SPeter Avalos * setnetconfig() must be called before the first call to getnetconfig(). 179*ce0e08e2SPeter Avalos * (Handles are used to allow for nested calls to setnetpath()). 180*ce0e08e2SPeter Avalos * 181*ce0e08e2SPeter Avalos * A new session is established with each call to setnetconfig(), with a new 182*ce0e08e2SPeter Avalos * handle being returned on each call. Previously established sessions remain 183*ce0e08e2SPeter Avalos * active until endnetconfig() is called with that session's handle as an 184*ce0e08e2SPeter Avalos * argument. 185*ce0e08e2SPeter Avalos * 186*ce0e08e2SPeter Avalos * setnetconfig() need *not* be called before a call to getnetconfigent(). 187*ce0e08e2SPeter Avalos * setnetconfig() returns a NULL pointer on failure (for example, if 188*ce0e08e2SPeter Avalos * the netconfig database is not present). 189*ce0e08e2SPeter Avalos */ 190*ce0e08e2SPeter Avalos void * 191*ce0e08e2SPeter Avalos setnetconfig(void) 192*ce0e08e2SPeter Avalos { 193*ce0e08e2SPeter Avalos struct netconfig_vars *nc_vars; 194*ce0e08e2SPeter Avalos 195*ce0e08e2SPeter Avalos if ((nc_vars = (struct netconfig_vars *)malloc(sizeof 196*ce0e08e2SPeter Avalos (struct netconfig_vars))) == NULL) { 197*ce0e08e2SPeter Avalos return(NULL); 198*ce0e08e2SPeter Avalos } 199*ce0e08e2SPeter Avalos 200*ce0e08e2SPeter Avalos /* 201*ce0e08e2SPeter Avalos * For multiple calls, i.e. nc_file is not NULL, we just return the 202*ce0e08e2SPeter Avalos * handle without reopening the netconfig db. 203*ce0e08e2SPeter Avalos */ 204*ce0e08e2SPeter Avalos ni.ref++; 205*ce0e08e2SPeter Avalos if ((nc_file != NULL) || (nc_file = fopen(NETCONFIG, "r")) != NULL) { 206*ce0e08e2SPeter Avalos nc_vars->valid = NC_VALID; 207*ce0e08e2SPeter Avalos nc_vars->flag = 0; 208*ce0e08e2SPeter Avalos nc_vars->nc_configs = ni.head; 209*ce0e08e2SPeter Avalos return ((void *)nc_vars); 210*ce0e08e2SPeter Avalos } 211*ce0e08e2SPeter Avalos ni.ref--; 212*ce0e08e2SPeter Avalos nc_error = NC_NONETCONFIG; 213*ce0e08e2SPeter Avalos free(nc_vars); 214*ce0e08e2SPeter Avalos return (NULL); 215*ce0e08e2SPeter Avalos } 216*ce0e08e2SPeter Avalos 217*ce0e08e2SPeter Avalos 218*ce0e08e2SPeter Avalos /* 219*ce0e08e2SPeter Avalos * When first called, getnetconfig() returns a pointer to the first entry in 220*ce0e08e2SPeter Avalos * the netconfig database, formatted as a struct netconfig. On each subsequent 221*ce0e08e2SPeter Avalos * call, getnetconfig() returns a pointer to the next entry in the database. 222*ce0e08e2SPeter Avalos * getnetconfig() can thus be used to search the entire netconfig file. 223*ce0e08e2SPeter Avalos * getnetconfig() returns NULL at end of file. 224*ce0e08e2SPeter Avalos */ 225*ce0e08e2SPeter Avalos 226*ce0e08e2SPeter Avalos struct netconfig * 227*ce0e08e2SPeter Avalos getnetconfig(void *handlep) 228*ce0e08e2SPeter Avalos { 229*ce0e08e2SPeter Avalos struct netconfig_vars *ncp = (struct netconfig_vars *)handlep; 230*ce0e08e2SPeter Avalos char *stringp; /* tmp string pointer */ 231*ce0e08e2SPeter Avalos struct netconfig_list *list; 232*ce0e08e2SPeter Avalos struct netconfig *np; 233*ce0e08e2SPeter Avalos 234*ce0e08e2SPeter Avalos /* 235*ce0e08e2SPeter Avalos * Verify that handle is valid 236*ce0e08e2SPeter Avalos */ 237*ce0e08e2SPeter Avalos if (ncp == NULL || nc_file == NULL) { 238*ce0e08e2SPeter Avalos nc_error = NC_NOTINIT; 239*ce0e08e2SPeter Avalos return (NULL); 240*ce0e08e2SPeter Avalos } 241*ce0e08e2SPeter Avalos 242*ce0e08e2SPeter Avalos switch (ncp->valid) { 243*ce0e08e2SPeter Avalos case NC_VALID: 244*ce0e08e2SPeter Avalos /* 245*ce0e08e2SPeter Avalos * If entry has already been read into the list, 246*ce0e08e2SPeter Avalos * we return the entry in the linked list. 247*ce0e08e2SPeter Avalos * If this is the first time call, check if there are any entries in 248*ce0e08e2SPeter Avalos * linked list. If no entries, we need to read the netconfig db. 249*ce0e08e2SPeter Avalos * If we have been here and the next entry is there, we just return 250*ce0e08e2SPeter Avalos * it. 251*ce0e08e2SPeter Avalos */ 252*ce0e08e2SPeter Avalos if (ncp->flag == 0) { /* first time */ 253*ce0e08e2SPeter Avalos ncp->flag = 1; 254*ce0e08e2SPeter Avalos ncp->nc_configs = ni.head; 255*ce0e08e2SPeter Avalos if (ncp->nc_configs != NULL) /* entry already exist */ 256*ce0e08e2SPeter Avalos return(ncp->nc_configs->ncp); 257*ce0e08e2SPeter Avalos } 258*ce0e08e2SPeter Avalos else if (ncp->nc_configs != NULL && ncp->nc_configs->next != NULL) { 259*ce0e08e2SPeter Avalos ncp->nc_configs = ncp->nc_configs->next; 260*ce0e08e2SPeter Avalos return(ncp->nc_configs->ncp); 261*ce0e08e2SPeter Avalos } 262*ce0e08e2SPeter Avalos 263*ce0e08e2SPeter Avalos /* 264*ce0e08e2SPeter Avalos * If we cannot find the entry in the list and is end of file, 265*ce0e08e2SPeter Avalos * we give up. 266*ce0e08e2SPeter Avalos */ 267*ce0e08e2SPeter Avalos if (ni.eof == 1) return(NULL); 268*ce0e08e2SPeter Avalos break; 269*ce0e08e2SPeter Avalos default: 270*ce0e08e2SPeter Avalos nc_error = NC_NOTINIT; 271*ce0e08e2SPeter Avalos return (NULL); 272*ce0e08e2SPeter Avalos } 273*ce0e08e2SPeter Avalos 274*ce0e08e2SPeter Avalos stringp = (char *) malloc(MAXNETCONFIGLINE); 275*ce0e08e2SPeter Avalos if (stringp == NULL) 276*ce0e08e2SPeter Avalos return (NULL); 277*ce0e08e2SPeter Avalos 278*ce0e08e2SPeter Avalos #ifdef MEM_CHK 279*ce0e08e2SPeter Avalos if (malloc_verify() == 0) { 280*ce0e08e2SPeter Avalos fprintf(stderr, "memory heap corrupted in getnetconfig\n"); 281*ce0e08e2SPeter Avalos exit(1); 282*ce0e08e2SPeter Avalos } 283*ce0e08e2SPeter Avalos #endif 284*ce0e08e2SPeter Avalos 285*ce0e08e2SPeter Avalos /* 286*ce0e08e2SPeter Avalos * Read a line from netconfig file. 287*ce0e08e2SPeter Avalos */ 288*ce0e08e2SPeter Avalos do { 289*ce0e08e2SPeter Avalos if (fgets(stringp, MAXNETCONFIGLINE, nc_file) == NULL) { 290*ce0e08e2SPeter Avalos free(stringp); 291*ce0e08e2SPeter Avalos ni.eof = 1; 292*ce0e08e2SPeter Avalos return (NULL); 293*ce0e08e2SPeter Avalos } 294*ce0e08e2SPeter Avalos } while (*stringp == '#'); 295*ce0e08e2SPeter Avalos 296*ce0e08e2SPeter Avalos list = (struct netconfig_list *) malloc(sizeof (struct netconfig_list)); 297*ce0e08e2SPeter Avalos if (list == NULL) { 298*ce0e08e2SPeter Avalos free(stringp); 299*ce0e08e2SPeter Avalos return(NULL); 300*ce0e08e2SPeter Avalos } 301*ce0e08e2SPeter Avalos np = (struct netconfig *) malloc(sizeof (struct netconfig)); 302*ce0e08e2SPeter Avalos if (np == NULL) { 303*ce0e08e2SPeter Avalos free(stringp); 304*ce0e08e2SPeter Avalos free(list); 305*ce0e08e2SPeter Avalos return(NULL); 306*ce0e08e2SPeter Avalos } 307*ce0e08e2SPeter Avalos list->ncp = np; 308*ce0e08e2SPeter Avalos list->next = NULL; 309*ce0e08e2SPeter Avalos list->ncp->nc_lookups = NULL; 310*ce0e08e2SPeter Avalos list->linep = stringp; 311*ce0e08e2SPeter Avalos if (parse_ncp(stringp, list->ncp) == -1) { 312*ce0e08e2SPeter Avalos free(stringp); 313*ce0e08e2SPeter Avalos free(np); 314*ce0e08e2SPeter Avalos free(list); 315*ce0e08e2SPeter Avalos return (NULL); 316*ce0e08e2SPeter Avalos } 317*ce0e08e2SPeter Avalos else { 318*ce0e08e2SPeter Avalos /* 319*ce0e08e2SPeter Avalos * If this is the first entry that's been read, it is the head of 320*ce0e08e2SPeter Avalos * the list. If not, put the entry at the end of the list. 321*ce0e08e2SPeter Avalos * Reposition the current pointer of the handle to the last entry 322*ce0e08e2SPeter Avalos * in the list. 323*ce0e08e2SPeter Avalos */ 324*ce0e08e2SPeter Avalos if (ni.head == NULL) { /* first entry */ 325*ce0e08e2SPeter Avalos ni.head = ni.tail = list; 326*ce0e08e2SPeter Avalos } 327*ce0e08e2SPeter Avalos else { 328*ce0e08e2SPeter Avalos ni.tail->next = list; 329*ce0e08e2SPeter Avalos ni.tail = ni.tail->next; 330*ce0e08e2SPeter Avalos } 331*ce0e08e2SPeter Avalos ncp->nc_configs = ni.tail; 332*ce0e08e2SPeter Avalos return(ni.tail->ncp); 333*ce0e08e2SPeter Avalos } 334*ce0e08e2SPeter Avalos } 335*ce0e08e2SPeter Avalos 336*ce0e08e2SPeter Avalos /* 337*ce0e08e2SPeter Avalos * endnetconfig() may be called to "unbind" or "close" the netconfig database 338*ce0e08e2SPeter Avalos * when processing is complete, releasing resources for reuse. endnetconfig() 339*ce0e08e2SPeter Avalos * may not be called before setnetconfig(). endnetconfig() returns 0 on 340*ce0e08e2SPeter Avalos * success and -1 on failure (for example, if setnetconfig() was not called 341*ce0e08e2SPeter Avalos * previously). 342*ce0e08e2SPeter Avalos */ 343*ce0e08e2SPeter Avalos int 344*ce0e08e2SPeter Avalos endnetconfig(void *handlep) 345*ce0e08e2SPeter Avalos { 346*ce0e08e2SPeter Avalos struct netconfig_vars *nc_handlep = (struct netconfig_vars *)handlep; 347*ce0e08e2SPeter Avalos 348*ce0e08e2SPeter Avalos struct netconfig_list *q, *p; 349*ce0e08e2SPeter Avalos 350*ce0e08e2SPeter Avalos /* 351*ce0e08e2SPeter Avalos * Verify that handle is valid 352*ce0e08e2SPeter Avalos */ 353*ce0e08e2SPeter Avalos if (nc_handlep == NULL || (nc_handlep->valid != NC_VALID && 354*ce0e08e2SPeter Avalos nc_handlep->valid != NC_STORAGE)) { 355*ce0e08e2SPeter Avalos nc_error = NC_NOTINIT; 356*ce0e08e2SPeter Avalos return (-1); 357*ce0e08e2SPeter Avalos } 358*ce0e08e2SPeter Avalos 359*ce0e08e2SPeter Avalos /* 360*ce0e08e2SPeter Avalos * Return 0 if anyone still needs it. 361*ce0e08e2SPeter Avalos */ 362*ce0e08e2SPeter Avalos nc_handlep->valid = NC_INVALID; 363*ce0e08e2SPeter Avalos nc_handlep->flag = 0; 364*ce0e08e2SPeter Avalos nc_handlep->nc_configs = NULL; 365*ce0e08e2SPeter Avalos if (--ni.ref > 0) { 366*ce0e08e2SPeter Avalos free(nc_handlep); 367*ce0e08e2SPeter Avalos return(0); 368*ce0e08e2SPeter Avalos } 369*ce0e08e2SPeter Avalos 370*ce0e08e2SPeter Avalos /* 371*ce0e08e2SPeter Avalos * Noone needs these entries anymore, then frees them. 372*ce0e08e2SPeter Avalos * Make sure all info in netconfig_info structure has been reinitialized. 373*ce0e08e2SPeter Avalos */ 374*ce0e08e2SPeter Avalos q = p = ni.head; 375*ce0e08e2SPeter Avalos ni.eof = ni.ref = 0; 376*ce0e08e2SPeter Avalos ni.head = NULL; 377*ce0e08e2SPeter Avalos ni.tail = NULL; 378*ce0e08e2SPeter Avalos while (q) { 379*ce0e08e2SPeter Avalos p = q->next; 380*ce0e08e2SPeter Avalos if (q->ncp->nc_lookups != NULL) free(q->ncp->nc_lookups); 381*ce0e08e2SPeter Avalos free(q->ncp); 382*ce0e08e2SPeter Avalos free(q->linep); 383*ce0e08e2SPeter Avalos free(q); 384*ce0e08e2SPeter Avalos q = p; 385*ce0e08e2SPeter Avalos } 386*ce0e08e2SPeter Avalos free(nc_handlep); 387*ce0e08e2SPeter Avalos 388*ce0e08e2SPeter Avalos fclose(nc_file); 389*ce0e08e2SPeter Avalos nc_file = NULL; 390*ce0e08e2SPeter Avalos return (0); 391*ce0e08e2SPeter Avalos } 392*ce0e08e2SPeter Avalos 393*ce0e08e2SPeter Avalos /* 394*ce0e08e2SPeter Avalos * getnetconfigent(netid) returns a pointer to the struct netconfig structure 395*ce0e08e2SPeter Avalos * corresponding to netid. It returns NULL if netid is invalid (that is, does 396*ce0e08e2SPeter Avalos * not name an entry in the netconfig database). It returns NULL and sets 397*ce0e08e2SPeter Avalos * errno in case of failure (for example, if the netconfig database cannot be 398*ce0e08e2SPeter Avalos * opened). 399*ce0e08e2SPeter Avalos */ 400*ce0e08e2SPeter Avalos 401*ce0e08e2SPeter Avalos struct netconfig * 402*ce0e08e2SPeter Avalos getnetconfigent(const char *netid) 403*ce0e08e2SPeter Avalos { 404*ce0e08e2SPeter Avalos FILE *file; /* NETCONFIG db's file pointer */ 405*ce0e08e2SPeter Avalos char *linep; /* holds current netconfig line */ 406*ce0e08e2SPeter Avalos char *stringp; /* temporary string pointer */ 407*ce0e08e2SPeter Avalos struct netconfig *ncp = NULL; /* returned value */ 408*ce0e08e2SPeter Avalos struct netconfig_list *list; /* pointer to cache list */ 409*ce0e08e2SPeter Avalos 410*ce0e08e2SPeter Avalos nc_error = NC_NOTFOUND; /* default error. */ 411*ce0e08e2SPeter Avalos if (netid == NULL || strlen(netid) == 0) { 412*ce0e08e2SPeter Avalos return (NULL); 413*ce0e08e2SPeter Avalos } 414*ce0e08e2SPeter Avalos 415*ce0e08e2SPeter Avalos if (strcmp(netid, "unix") == 0) { 416*ce0e08e2SPeter Avalos fprintf(stderr, "The local transport is called \"unix\" "); 417*ce0e08e2SPeter Avalos fprintf(stderr, "in /etc/netconfig.\n"); 418*ce0e08e2SPeter Avalos fprintf(stderr, "Please change this to \"local\" manually "); 419*ce0e08e2SPeter Avalos fprintf(stderr, "or run mergemaster(8).\n"); 420*ce0e08e2SPeter Avalos fprintf(stderr, "See UPDATING entry 20021216 for details.\n"); 421*ce0e08e2SPeter Avalos fprintf(stderr, "Continuing in 10 seconds\n\n"); 422*ce0e08e2SPeter Avalos fprintf(stderr, "This warning will be removed 20030301\n"); 423*ce0e08e2SPeter Avalos sleep(10); 424*ce0e08e2SPeter Avalos 425*ce0e08e2SPeter Avalos } 426*ce0e08e2SPeter Avalos 427*ce0e08e2SPeter Avalos /* 428*ce0e08e2SPeter Avalos * Look up table if the entries have already been read and parsed in 429*ce0e08e2SPeter Avalos * getnetconfig(), then copy this entry into a buffer and return it. 430*ce0e08e2SPeter Avalos * If we cannot find the entry in the current list and there are more 431*ce0e08e2SPeter Avalos * entries in the netconfig db that has not been read, we then read the 432*ce0e08e2SPeter Avalos * db and try find the match netid. 433*ce0e08e2SPeter Avalos * If all the netconfig db has been read and placed into the list and 434*ce0e08e2SPeter Avalos * there is no match for the netid, return NULL. 435*ce0e08e2SPeter Avalos */ 436*ce0e08e2SPeter Avalos if (ni.head != NULL) { 437*ce0e08e2SPeter Avalos for (list = ni.head; list; list = list->next) { 438*ce0e08e2SPeter Avalos if (strcmp(list->ncp->nc_netid, netid) == 0) { 439*ce0e08e2SPeter Avalos return(dup_ncp(list->ncp)); 440*ce0e08e2SPeter Avalos } 441*ce0e08e2SPeter Avalos } 442*ce0e08e2SPeter Avalos if (ni.eof == 1) /* that's all the entries */ 443*ce0e08e2SPeter Avalos return(NULL); 444*ce0e08e2SPeter Avalos } 445*ce0e08e2SPeter Avalos 446*ce0e08e2SPeter Avalos 447*ce0e08e2SPeter Avalos if ((file = fopen(NETCONFIG, "r")) == NULL) { 448*ce0e08e2SPeter Avalos nc_error = NC_NONETCONFIG; 449*ce0e08e2SPeter Avalos return (NULL); 450*ce0e08e2SPeter Avalos } 451*ce0e08e2SPeter Avalos 452*ce0e08e2SPeter Avalos if ((linep = malloc(MAXNETCONFIGLINE)) == NULL) { 453*ce0e08e2SPeter Avalos fclose(file); 454*ce0e08e2SPeter Avalos nc_error = NC_NOMEM; 455*ce0e08e2SPeter Avalos return (NULL); 456*ce0e08e2SPeter Avalos } 457*ce0e08e2SPeter Avalos do { 458*ce0e08e2SPeter Avalos ptrdiff_t len; 459*ce0e08e2SPeter Avalos char *tmpp; /* tmp string pointer */ 460*ce0e08e2SPeter Avalos 461*ce0e08e2SPeter Avalos do { 462*ce0e08e2SPeter Avalos if ((stringp = fgets(linep, MAXNETCONFIGLINE, file)) == NULL) { 463*ce0e08e2SPeter Avalos break; 464*ce0e08e2SPeter Avalos } 465*ce0e08e2SPeter Avalos } while (*stringp == '#'); 466*ce0e08e2SPeter Avalos if (stringp == NULL) { /* eof */ 467*ce0e08e2SPeter Avalos break; 468*ce0e08e2SPeter Avalos } 469*ce0e08e2SPeter Avalos if ((tmpp = strpbrk(stringp, "\t ")) == NULL) { /* can't parse file */ 470*ce0e08e2SPeter Avalos nc_error = NC_BADFILE; 471*ce0e08e2SPeter Avalos break; 472*ce0e08e2SPeter Avalos } 473*ce0e08e2SPeter Avalos if (strlen(netid) == (size_t) (len = tmpp - stringp) && /* a match */ 474*ce0e08e2SPeter Avalos strncmp(stringp, netid, (size_t)len) == 0) { 475*ce0e08e2SPeter Avalos if ((ncp = (struct netconfig *) 476*ce0e08e2SPeter Avalos malloc(sizeof (struct netconfig))) == NULL) { 477*ce0e08e2SPeter Avalos break; 478*ce0e08e2SPeter Avalos } 479*ce0e08e2SPeter Avalos ncp->nc_lookups = NULL; 480*ce0e08e2SPeter Avalos if (parse_ncp(linep, ncp) == -1) { 481*ce0e08e2SPeter Avalos free(ncp); 482*ce0e08e2SPeter Avalos ncp = NULL; 483*ce0e08e2SPeter Avalos } 484*ce0e08e2SPeter Avalos break; 485*ce0e08e2SPeter Avalos } 486*ce0e08e2SPeter Avalos } while (stringp != NULL); 487*ce0e08e2SPeter Avalos if (ncp == NULL) { 488*ce0e08e2SPeter Avalos free(linep); 489*ce0e08e2SPeter Avalos } 490*ce0e08e2SPeter Avalos fclose(file); 491*ce0e08e2SPeter Avalos return(ncp); 492*ce0e08e2SPeter Avalos } 493*ce0e08e2SPeter Avalos 494*ce0e08e2SPeter Avalos /* 495*ce0e08e2SPeter Avalos * freenetconfigent(netconfigp) frees the netconfig structure pointed to by 496*ce0e08e2SPeter Avalos * netconfigp (previously returned by getnetconfigent()). 497*ce0e08e2SPeter Avalos */ 498*ce0e08e2SPeter Avalos 499*ce0e08e2SPeter Avalos void 500*ce0e08e2SPeter Avalos freenetconfigent(struct netconfig *netconfigp) 501*ce0e08e2SPeter Avalos { 502*ce0e08e2SPeter Avalos if (netconfigp != NULL) { 503*ce0e08e2SPeter Avalos free(netconfigp->nc_netid); /* holds all netconfigp's strings */ 504*ce0e08e2SPeter Avalos if (netconfigp->nc_lookups != NULL) 505*ce0e08e2SPeter Avalos free(netconfigp->nc_lookups); 506*ce0e08e2SPeter Avalos free(netconfigp); 507*ce0e08e2SPeter Avalos } 508*ce0e08e2SPeter Avalos return; 509*ce0e08e2SPeter Avalos } 510*ce0e08e2SPeter Avalos 511*ce0e08e2SPeter Avalos /* 512*ce0e08e2SPeter Avalos * Parse line and stuff it in a struct netconfig 513*ce0e08e2SPeter Avalos * Typical line might look like: 514*ce0e08e2SPeter Avalos * udp tpi_cots vb inet udp /dev/udp /usr/lib/ip.so,/usr/local/ip.so 515*ce0e08e2SPeter Avalos * 516*ce0e08e2SPeter Avalos * We return -1 if any of the tokens don't parse, or malloc fails. 517*ce0e08e2SPeter Avalos * 518*ce0e08e2SPeter Avalos * Note that we modify stringp (putting NULLs after tokens) and 519*ce0e08e2SPeter Avalos * we set the ncp's string field pointers to point to these tokens within 520*ce0e08e2SPeter Avalos * stringp. 521*ce0e08e2SPeter Avalos */ 522*ce0e08e2SPeter Avalos 523*ce0e08e2SPeter Avalos static int 524*ce0e08e2SPeter Avalos parse_ncp(char *stringp, /* string to parse */ 525*ce0e08e2SPeter Avalos struct netconfig *ncp) /* where to put results */ 526*ce0e08e2SPeter Avalos { 527*ce0e08e2SPeter Avalos char *tokenp; /* for processing tokens */ 528*ce0e08e2SPeter Avalos char *lasts; 529*ce0e08e2SPeter Avalos char **nc_lookups; 530*ce0e08e2SPeter Avalos 531*ce0e08e2SPeter Avalos nc_error = NC_BADFILE; /* nearly anything that breaks is for this reason */ 532*ce0e08e2SPeter Avalos stringp[strlen(stringp)-1] = '\0'; /* get rid of newline */ 533*ce0e08e2SPeter Avalos /* netid */ 534*ce0e08e2SPeter Avalos if ((ncp->nc_netid = strtok_r(stringp, "\t ", &lasts)) == NULL) { 535*ce0e08e2SPeter Avalos return (-1); 536*ce0e08e2SPeter Avalos } 537*ce0e08e2SPeter Avalos 538*ce0e08e2SPeter Avalos /* semantics */ 539*ce0e08e2SPeter Avalos if ((tokenp = strtok_r(NULL, "\t ", &lasts)) == NULL) { 540*ce0e08e2SPeter Avalos return (-1); 541*ce0e08e2SPeter Avalos } 542*ce0e08e2SPeter Avalos if (strcmp(tokenp, NC_TPI_COTS_ORD_S) == 0) 543*ce0e08e2SPeter Avalos ncp->nc_semantics = NC_TPI_COTS_ORD; 544*ce0e08e2SPeter Avalos else if (strcmp(tokenp, NC_TPI_COTS_S) == 0) 545*ce0e08e2SPeter Avalos ncp->nc_semantics = NC_TPI_COTS; 546*ce0e08e2SPeter Avalos else if (strcmp(tokenp, NC_TPI_CLTS_S) == 0) 547*ce0e08e2SPeter Avalos ncp->nc_semantics = NC_TPI_CLTS; 548*ce0e08e2SPeter Avalos else if (strcmp(tokenp, NC_TPI_RAW_S) == 0) 549*ce0e08e2SPeter Avalos ncp->nc_semantics = NC_TPI_RAW; 550*ce0e08e2SPeter Avalos else 551*ce0e08e2SPeter Avalos return (-1); 552*ce0e08e2SPeter Avalos 553*ce0e08e2SPeter Avalos /* flags */ 554*ce0e08e2SPeter Avalos if ((tokenp = strtok_r(NULL, "\t ", &lasts)) == NULL) { 555*ce0e08e2SPeter Avalos return (-1); 556*ce0e08e2SPeter Avalos } 557*ce0e08e2SPeter Avalos for (ncp->nc_flag = NC_NOFLAG; *tokenp != '\0'; 558*ce0e08e2SPeter Avalos tokenp++) { 559*ce0e08e2SPeter Avalos switch (*tokenp) { 560*ce0e08e2SPeter Avalos case NC_NOFLAG_C: 561*ce0e08e2SPeter Avalos break; 562*ce0e08e2SPeter Avalos case NC_VISIBLE_C: 563*ce0e08e2SPeter Avalos ncp->nc_flag |= NC_VISIBLE; 564*ce0e08e2SPeter Avalos break; 565*ce0e08e2SPeter Avalos case NC_BROADCAST_C: 566*ce0e08e2SPeter Avalos ncp->nc_flag |= NC_BROADCAST; 567*ce0e08e2SPeter Avalos break; 568*ce0e08e2SPeter Avalos default: 569*ce0e08e2SPeter Avalos return (-1); 570*ce0e08e2SPeter Avalos } 571*ce0e08e2SPeter Avalos } 572*ce0e08e2SPeter Avalos /* protocol family */ 573*ce0e08e2SPeter Avalos if ((ncp->nc_protofmly = strtok_r(NULL, "\t ", &lasts)) == NULL) { 574*ce0e08e2SPeter Avalos return (-1); 575*ce0e08e2SPeter Avalos } 576*ce0e08e2SPeter Avalos /* protocol name */ 577*ce0e08e2SPeter Avalos if ((ncp->nc_proto = strtok_r(NULL, "\t ", &lasts)) == NULL) { 578*ce0e08e2SPeter Avalos return (-1); 579*ce0e08e2SPeter Avalos } 580*ce0e08e2SPeter Avalos /* network device */ 581*ce0e08e2SPeter Avalos if ((ncp->nc_device = strtok_r(NULL, "\t ", &lasts)) == NULL) { 582*ce0e08e2SPeter Avalos return (-1); 583*ce0e08e2SPeter Avalos } 584*ce0e08e2SPeter Avalos if ((tokenp = strtok_r(NULL, "\t ", &lasts)) == NULL) { 585*ce0e08e2SPeter Avalos return (-1); 586*ce0e08e2SPeter Avalos } 587*ce0e08e2SPeter Avalos if (strcmp(tokenp, NC_NOLOOKUP) == 0) { 588*ce0e08e2SPeter Avalos ncp->nc_nlookups = 0; 589*ce0e08e2SPeter Avalos ncp->nc_lookups = NULL; 590*ce0e08e2SPeter Avalos } else { 591*ce0e08e2SPeter Avalos char *cp; /* tmp string */ 592*ce0e08e2SPeter Avalos 593*ce0e08e2SPeter Avalos if (ncp->nc_lookups != NULL) /* from last visit */ 594*ce0e08e2SPeter Avalos free(ncp->nc_lookups); 595*ce0e08e2SPeter Avalos ncp->nc_lookups = NULL; 596*ce0e08e2SPeter Avalos ncp->nc_nlookups = 0; 597*ce0e08e2SPeter Avalos while ((cp = tokenp) != NULL) { 598*ce0e08e2SPeter Avalos if ((nc_lookups = realloc(ncp->nc_lookups, 599*ce0e08e2SPeter Avalos (ncp->nc_nlookups + 1) * sizeof *ncp->nc_lookups)) == NULL) { 600*ce0e08e2SPeter Avalos free(ncp->nc_lookups); 601*ce0e08e2SPeter Avalos ncp->nc_lookups = NULL; 602*ce0e08e2SPeter Avalos return (-1); 603*ce0e08e2SPeter Avalos } 604*ce0e08e2SPeter Avalos tokenp = _get_next_token(cp, ','); 605*ce0e08e2SPeter Avalos ncp->nc_lookups = nc_lookups; 606*ce0e08e2SPeter Avalos ncp->nc_lookups[ncp->nc_nlookups++] = cp; 607*ce0e08e2SPeter Avalos } 608*ce0e08e2SPeter Avalos } 609*ce0e08e2SPeter Avalos return (0); 610*ce0e08e2SPeter Avalos } 611*ce0e08e2SPeter Avalos 612*ce0e08e2SPeter Avalos 613*ce0e08e2SPeter Avalos /* 614*ce0e08e2SPeter Avalos * Returns a string describing the reason for failure. 615*ce0e08e2SPeter Avalos */ 616*ce0e08e2SPeter Avalos char * 617*ce0e08e2SPeter Avalos nc_sperror(void) 618*ce0e08e2SPeter Avalos { 619*ce0e08e2SPeter Avalos const char *message; 620*ce0e08e2SPeter Avalos 621*ce0e08e2SPeter Avalos switch(nc_error) { 622*ce0e08e2SPeter Avalos case NC_NONETCONFIG: 623*ce0e08e2SPeter Avalos message = _nc_errors[0]; 624*ce0e08e2SPeter Avalos break; 625*ce0e08e2SPeter Avalos case NC_NOMEM: 626*ce0e08e2SPeter Avalos message = _nc_errors[1]; 627*ce0e08e2SPeter Avalos break; 628*ce0e08e2SPeter Avalos case NC_NOTINIT: 629*ce0e08e2SPeter Avalos message = _nc_errors[2]; 630*ce0e08e2SPeter Avalos break; 631*ce0e08e2SPeter Avalos case NC_BADFILE: 632*ce0e08e2SPeter Avalos message = _nc_errors[3]; 633*ce0e08e2SPeter Avalos break; 634*ce0e08e2SPeter Avalos case NC_NOTFOUND: 635*ce0e08e2SPeter Avalos message = _nc_errors[4]; 636*ce0e08e2SPeter Avalos break; 637*ce0e08e2SPeter Avalos default: 638*ce0e08e2SPeter Avalos message = "Unknown network selection error"; 639*ce0e08e2SPeter Avalos } 640*ce0e08e2SPeter Avalos /* LINTED const castaway */ 641*ce0e08e2SPeter Avalos return ((char *)message); 642*ce0e08e2SPeter Avalos } 643*ce0e08e2SPeter Avalos 644*ce0e08e2SPeter Avalos /* 645*ce0e08e2SPeter Avalos * Prints a message onto standard error describing the reason for failure. 646*ce0e08e2SPeter Avalos */ 647*ce0e08e2SPeter Avalos void 648*ce0e08e2SPeter Avalos nc_perror(const char *s) 649*ce0e08e2SPeter Avalos { 650*ce0e08e2SPeter Avalos fprintf(stderr, "%s: %s\n", s, nc_sperror()); 651*ce0e08e2SPeter Avalos } 652*ce0e08e2SPeter Avalos 653*ce0e08e2SPeter Avalos /* 654*ce0e08e2SPeter Avalos * Duplicates the matched netconfig buffer. 655*ce0e08e2SPeter Avalos */ 656*ce0e08e2SPeter Avalos static struct netconfig * 657*ce0e08e2SPeter Avalos dup_ncp(struct netconfig *ncp) 658*ce0e08e2SPeter Avalos { 659*ce0e08e2SPeter Avalos struct netconfig *p; 660*ce0e08e2SPeter Avalos char *tmp; 661*ce0e08e2SPeter Avalos u_int i; 662*ce0e08e2SPeter Avalos 663*ce0e08e2SPeter Avalos if ((tmp=malloc(MAXNETCONFIGLINE)) == NULL) 664*ce0e08e2SPeter Avalos return(NULL); 665*ce0e08e2SPeter Avalos if ((p=(struct netconfig *)malloc(sizeof(struct netconfig))) == NULL) { 666*ce0e08e2SPeter Avalos free(tmp); 667*ce0e08e2SPeter Avalos return(NULL); 668*ce0e08e2SPeter Avalos } 669*ce0e08e2SPeter Avalos /* 670*ce0e08e2SPeter Avalos * First we dup all the data from matched netconfig buffer. Then we 671*ce0e08e2SPeter Avalos * adjust some of the member pointer to a pre-allocated buffer where 672*ce0e08e2SPeter Avalos * contains part of the data. 673*ce0e08e2SPeter Avalos * To follow the convention used in parse_ncp(), we store all the 674*ce0e08e2SPeter Avalos * necessary information in the pre-allocated buffer and let each 675*ce0e08e2SPeter Avalos * of the netconfig char pointer member point to the right address 676*ce0e08e2SPeter Avalos * in the buffer. 677*ce0e08e2SPeter Avalos */ 678*ce0e08e2SPeter Avalos *p = *ncp; 679*ce0e08e2SPeter Avalos p->nc_netid = (char *)strcpy(tmp,ncp->nc_netid); 680*ce0e08e2SPeter Avalos tmp = strchr(tmp, '\0') + 1; 681*ce0e08e2SPeter Avalos p->nc_protofmly = (char *)strcpy(tmp,ncp->nc_protofmly); 682*ce0e08e2SPeter Avalos tmp = strchr(tmp, '\0') + 1; 683*ce0e08e2SPeter Avalos p->nc_proto = (char *)strcpy(tmp,ncp->nc_proto); 684*ce0e08e2SPeter Avalos tmp = strchr(tmp, '\0') + 1; 685*ce0e08e2SPeter Avalos p->nc_device = (char *)strcpy(tmp,ncp->nc_device); 686*ce0e08e2SPeter Avalos p->nc_lookups = (char **)malloc((size_t)(p->nc_nlookups+1) * sizeof(char *)); 687*ce0e08e2SPeter Avalos if (p->nc_lookups == NULL) { 688*ce0e08e2SPeter Avalos free(p->nc_netid); 689*ce0e08e2SPeter Avalos free(p); 690*ce0e08e2SPeter Avalos return(NULL); 691*ce0e08e2SPeter Avalos } 692*ce0e08e2SPeter Avalos for (i=0; i < p->nc_nlookups; i++) { 693*ce0e08e2SPeter Avalos tmp = strchr(tmp, '\0') + 1; 694*ce0e08e2SPeter Avalos p->nc_lookups[i] = (char *)strcpy(tmp,ncp->nc_lookups[i]); 695*ce0e08e2SPeter Avalos } 696*ce0e08e2SPeter Avalos return(p); 697*ce0e08e2SPeter Avalos } 698