2 * Copyright (c) 2009, Sun Microsystems, Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * - Redistributions of source code must retain the above copyright notice,
8 * this list of conditions and the following disclaimer.
9 * - Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
12 * - Neither the name of Sun Microsystems, Inc. nor the names of its
13 * contributors may be used to endorse or promote products derived
14 * from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
30 * Copyright (c) 1989 by Sun Microsystems, Inc.
34 //#include <pthread.h>
35 #include <reentrant.h>
36 //#include <sys/cdefs.h>
39 #include <netconfig.h>
48 // XXX FIXME - this is in wintirpc.c, but that is not currently built
49 static void wintirpc_debug(char *fmt
, ...)
54 #define MSVCRT_CHECK_PMT(x) (x)
55 char * CDECL
strtok_s(char *str
, const char *delim
, char **ctx
)
57 if (!MSVCRT_CHECK_PMT(delim
!= NULL
)) return NULL
;
58 if (!MSVCRT_CHECK_PMT(ctx
!= NULL
)) return NULL
;
59 if (!MSVCRT_CHECK_PMT(str
!= NULL
|| *ctx
!= NULL
)) return NULL
;
64 while(*str
&& strchr(delim
, *str
))
73 while(**ctx
&& !strchr(delim
, **ctx
))
83 * The five library routines in this file provide application access to the
84 * system network configuration database, /etc/netconfig. In addition to the
85 * netconfig database and the routines for accessing it, the environment
86 * variable NETPATH and its corresponding routines in getnetpath.c may also be
87 * used to specify the network transport to be used.
94 #define NC_NONETCONFIG ENOENT
95 #define NC_NOMEM ENOMEM
96 #define NC_NOTINIT EINVAL /* setnetconfig was not called first */
97 #define NC_BADFILE EBADF /* format for netconfig file is bad */
98 #define NC_NOTFOUND WSAENOPROTOOPT /* specified netid was not found */
101 * semantics as strings (should be in netconfig.h)
103 #define NC_TPI_CLTS_S "tpi_clts"
104 #define NC_TPI_COTS_S "tpi_cots"
105 #define NC_TPI_COTS_ORD_S "tpi_cots_ord"
106 #define NC_TPI_RAW_S "tpi_raw"
109 * flags as characters (also should be in netconfig.h)
111 #define NC_NOFLAG_C '-'
112 #define NC_VISIBLE_C 'v'
113 #define NC_BROADCAST_C 'b'
116 * Character used to indicate there is no name-to-address lookup library
118 #define NC_NOLOOKUP "-"
120 static const char * const _nc_errors
[] = {
121 "Netconfig database not found",
124 "Netconfig database has invalid format",
125 "Netid not found in netconfig database"
128 struct netconfig_info
{
129 int eof
; /* all entries has been read */
130 int ref
; /* # of times setnetconfig() has been called */
131 struct netconfig_list
*head
; /* head of the list */
132 struct netconfig_list
*tail
; /* last of the list */
135 struct netconfig_list
{
136 char *linep
; /* hold line read from netconfig */
137 struct netconfig
*ncp
;
138 struct netconfig_list
*next
;
141 struct netconfig_vars
{
142 int valid
; /* token that indicates a valid netconfig_vars */
143 int flag
; /* first time flag */
144 struct netconfig_list
*nc_configs
; /* pointer to the current netconfig entry */
147 #define NC_VALID 0xfeed
148 #define NC_STORAGE 0xf00d
152 static int *__nc_error(void);
153 static int parse_ncp(char *, struct netconfig
*);
154 static struct netconfig
*dup_ncp(struct netconfig
*);
157 static FILE *nc_file
; /* for netconfig db */
158 static struct netconfig_info ni
= { 0, 0, NULL
, NULL
};
160 #define MAXNETCONFIGLINE 1000
165 extern mutex_t nc_lock
;
166 extern thread_key_t nc_key
;
167 static int nc_error
= 0;
171 * Use the static `nc_error' if we are the main thread
172 * (including non-threaded programs), or if an allocation
177 mutex_lock(&nc_lock
);
179 error
= nc_key
= TlsAlloc(); //thr_keycreate(&nc_key, free);
180 mutex_unlock(&nc_lock
);
181 if (error
== TLS_OUT_OF_INDEXES
)
184 if ((nc_addr
= (int *)thr_getspecific(nc_key
)) == NULL
) {
185 nc_addr
= (int *)malloc(sizeof (int *));
186 if (thr_setspecific(nc_key
, (void *) nc_addr
) == 0) {
196 #define nc_error (*(__nc_error()))
198 * A call to setnetconfig() establishes a /etc/netconfig "session". A session
199 * "handle" is returned on a successful call. At the start of a session (after
200 * a call to setnetconfig()) searches through the /etc/netconfig database will
201 * proceed from the start of the file. The session handle must be passed to
202 * getnetconfig() to parse the file. Each call to getnetconfig() using the
203 * current handle will process one subsequent entry in /etc/netconfig.
204 * setnetconfig() must be called before the first call to getnetconfig().
205 * (Handles are used to allow for nested calls to setnetpath()).
207 * A new session is established with each call to setnetconfig(), with a new
208 * handle being returned on each call. Previously established sessions remain
209 * active until endnetconfig() is called with that session's handle as an
212 * setnetconfig() need *not* be called before a call to getnetconfigent().
213 * setnetconfig() returns a NULL pointer on failure (for example, if
214 * the netconfig database is not present).
219 struct netconfig_vars
*nc_vars
;
221 if ((nc_vars
= (struct netconfig_vars
*)malloc(sizeof
222 (struct netconfig_vars
))) == NULL
) {
227 * For multiple calls, i.e. nc_file is not NULL, we just return the
228 * handle without reopening the netconfig db.
231 if ((nc_file
!= NULL
) || (nc_file
= fopen(NETCONFIG
, "r")) != NULL
) {
232 nc_vars
->valid
= NC_VALID
;
234 nc_vars
->nc_configs
= ni
.head
;
235 return ((void *)nc_vars
);
238 nc_error
= NC_NONETCONFIG
;
245 * When first called, getnetconfig() returns a pointer to the first entry in
246 * the netconfig database, formatted as a struct netconfig. On each subsequent
247 * call, getnetconfig() returns a pointer to the next entry in the database.
248 * getnetconfig() can thus be used to search the entire netconfig file.
249 * getnetconfig() returns NULL at end of file.
253 getnetconfig(handlep
)
256 struct netconfig_vars
*ncp
= (struct netconfig_vars
*)handlep
;
257 char *stringp
; /* tmp string pointer */
258 struct netconfig_list
*list
;
259 struct netconfig
*np
;
262 * Verify that handle is valid
264 if (ncp
== NULL
|| nc_file
== NULL
) {
265 nc_error
= NC_NOTINIT
;
269 switch (ncp
->valid
) {
272 * If entry has already been read into the list,
273 * we return the entry in the linked list.
274 * If this is the first time call, check if there are any entries in
275 * linked list. If no entries, we need to read the netconfig db.
276 * If we have been here and the next entry is there, we just return
279 if (ncp
->flag
== 0) { /* first time */
281 ncp
->nc_configs
= ni
.head
;
282 if (ncp
->nc_configs
!= NULL
) /* entry already exist */
283 return(ncp
->nc_configs
->ncp
);
285 else if (ncp
->nc_configs
!= NULL
&& ncp
->nc_configs
->next
!= NULL
) {
286 ncp
->nc_configs
= ncp
->nc_configs
->next
;
287 return(ncp
->nc_configs
->ncp
);
291 * If we cannot find the entry in the list and is end of file,
298 nc_error
= NC_NOTINIT
;
302 stringp
= (char *) malloc(MAXNETCONFIGLINE
);
307 if (malloc_verify() == 0) {
308 fprintf(stderr
, "memory heap corrupted in getnetconfig\n");
314 * Read a line from netconfig file.
317 if (fgets(stringp
, MAXNETCONFIGLINE
, nc_file
) == NULL
) {
322 } while (*stringp
== '#');
324 list
= (struct netconfig_list
*) malloc(sizeof (struct netconfig_list
));
329 np
= (struct netconfig
*) malloc(sizeof (struct netconfig
));
337 list
->ncp
->nc_lookups
= NULL
;
338 list
->linep
= stringp
;
339 wintirpc_debug("%s: before parse: &list->linep %p, list->linep %p, stringp %p\n", __FUNCTION__
, &list
->linep
, list
->linep
, stringp
);
340 if (parse_ncp(stringp
, list
->ncp
) == -1) {
346 wintirpc_debug("%s: after parse: list->linep %p, stringp %p\n", __FUNCTION__
, list
->linep
, stringp
);
348 * If this is the first entry that's been read, it is the head of
349 * the list. If not, put the entry at the end of the list.
350 * Reposition the current pointer of the handle to the last entry
353 if (ni
.head
== NULL
) { /* first entry */
354 ni
.head
= ni
.tail
= list
;
357 ni
.tail
->next
= list
;
358 ni
.tail
= ni
.tail
->next
;
360 ncp
->nc_configs
= ni
.tail
;
361 return(ni
.tail
->ncp
);
366 * endnetconfig() may be called to "unbind" or "close" the netconfig database
367 * when processing is complete, releasing resources for reuse. endnetconfig()
368 * may not be called before setnetconfig(). endnetconfig() returns 0 on
369 * success and -1 on failure (for example, if setnetconfig() was not called
373 endnetconfig(handlep
)
376 struct netconfig_vars
*nc_handlep
= (struct netconfig_vars
*)handlep
;
378 struct netconfig_list
*q
, *p
;
381 * Verify that handle is valid
383 if (nc_handlep
== NULL
|| (nc_handlep
->valid
!= NC_VALID
&&
384 nc_handlep
->valid
!= NC_STORAGE
)) {
385 nc_error
= NC_NOTINIT
;
390 * Return 0 if anyone still needs it.
392 nc_handlep
->valid
= NC_INVALID
;
393 nc_handlep
->flag
= 0;
394 nc_handlep
->nc_configs
= NULL
;
401 * Noone needs these entries anymore, then frees them.
402 * Make sure all info in netconfig_info structure has been reinitialized.
410 if (q
->ncp
->nc_lookups
!= NULL
) free(q
->ncp
->nc_lookups
);
424 * getnetconfigent(netid) returns a pointer to the struct netconfig structure
425 * corresponding to netid. It returns NULL if netid is invalid (that is, does
426 * not name an entry in the netconfig database). It returns NULL and sets
427 * errno in case of failure (for example, if the netconfig database cannot be
432 getnetconfigent(netid
)
435 FILE *file
; /* NETCONFIG db's file pointer */
436 char *linep
; /* holds current netconfig line */
437 char *stringp
; /* temporary string pointer */
438 struct netconfig
*ncp
= NULL
; /* returned value */
439 struct netconfig_list
*list
; /* pointer to cache list */
441 nc_error
= NC_NOTFOUND
; /* default error. */
442 if (netid
== NULL
|| strlen(netid
) == 0) {
446 if (strcmp(netid
, "unix") == 0) {
447 fprintf(stderr
, "The local transport is called \"unix\" ");
448 fprintf(stderr
, "in /etc/netconfig.\n");
449 fprintf(stderr
, "Please change this to \"local\" manually ");
450 fprintf(stderr
, "or run mergemaster(8).\n");
451 fprintf(stderr
, "See UPDATING entry 20021216 for details.\n");
452 fprintf(stderr
, "Continuing in 10 seconds\n\n");
453 fprintf(stderr
, "This warning will be removed 20030301\n");
454 Sleep(10000); // sleep(10);
458 * Look up table if the entries have already been read and parsed in
459 * getnetconfig(), then copy this entry into a buffer and return it.
460 * If we cannot find the entry in the current list and there are more
461 * entries in the netconfig db that has not been read, we then read the
462 * db and try find the match netid.
463 * If all the netconfig db has been read and placed into the list and
464 * there is no match for the netid, return NULL.
466 if (ni
.head
!= NULL
) {
467 for (list
= ni
.head
; list
; list
= list
->next
) {
468 if (strcmp(list
->ncp
->nc_netid
, netid
) == 0) {
469 return(dup_ncp(list
->ncp
));
472 if (ni
.eof
== 1) /* that's all the entries */
477 if ((file
= fopen(NETCONFIG
, "r")) == NULL
) {
478 nc_error
= NC_NONETCONFIG
;
482 if ((linep
= malloc(MAXNETCONFIGLINE
)) == NULL
) {
489 char *tmpp
; /* tmp string pointer */
492 if ((stringp
= fgets(linep
, MAXNETCONFIGLINE
, file
)) == NULL
) {
495 } while (*stringp
== '#');
496 if (stringp
== NULL
) { /* eof */
499 if ((tmpp
= strpbrk(stringp
, "\t ")) == NULL
) { /* can't parse file */
500 nc_error
= NC_BADFILE
;
503 if (strlen(netid
) == (size_t) (len
= tmpp
- stringp
) && /* a match */
504 strncmp(stringp
, netid
, (size_t)len
) == 0) {
505 if ((ncp
= (struct netconfig
*)
506 malloc(sizeof (struct netconfig
))) == NULL
) {
509 ncp
->nc_lookups
= NULL
;
510 if (parse_ncp(linep
, ncp
) == -1) {
516 } while (stringp
!= NULL
);
525 * freenetconfigent(netconfigp) frees the netconfig structure pointed to by
526 * netconfigp (previously returned by getnetconfigent()).
530 freenetconfigent(netconfigp
)
531 struct netconfig
*netconfigp
;
533 if (netconfigp
!= NULL
) {
534 free(netconfigp
->nc_netid
); /* holds all netconfigp's strings */
535 if (netconfigp
->nc_lookups
!= NULL
)
536 free(netconfigp
->nc_lookups
);
543 * Parse line and stuff it in a struct netconfig
544 * Typical line might look like:
545 * udp tpi_cots vb inet udp /dev/udp /usr/lib/ip.so,/usr/local/ip.so
547 * We return -1 if any of the tokens don't parse, or malloc fails.
549 * Note that we modify stringp (putting NULLs after tokens) and
550 * we set the ncp's string field pointers to point to these tokens within
555 parse_ncp(stringp
, ncp
)
556 char *stringp
; /* string to parse */
557 struct netconfig
*ncp
; /* where to put results */
559 char *tokenp
; /* for processing tokens */
562 nc_error
= NC_BADFILE
; /* nearly anything that breaks is for this reason */
563 wintirpc_debug("%s: The last character being chopped is '%02x'\n", __FUNCTION__
, stringp
[strlen(stringp
)-1]);
564 wintirpc_debug("%s: The string before chopping is '%s'\n", __FUNCTION__
, stringp
);
565 stringp
[strlen(stringp
)-1] = '\0'; /* get rid of newline */
566 wintirpc_debug("%s: The last character after chopping is '%02x'\n", __FUNCTION__
, stringp
[strlen(stringp
)-1]);
567 wintirpc_debug("%s: The string after chopping is '%s'\n", __FUNCTION__
, stringp
);
569 if ((ncp
->nc_netid
= strtok_r(stringp
, "\t ", &lasts
)) == NULL
) {
574 if ((tokenp
= strtok_r(NULL
, "\t ", &lasts
)) == NULL
) {
577 if (strcmp(tokenp
, NC_TPI_COTS_ORD_S
) == 0)
578 ncp
->nc_semantics
= NC_TPI_COTS_ORD
;
579 else if (strcmp(tokenp
, NC_TPI_COTS_S
) == 0)
580 ncp
->nc_semantics
= NC_TPI_COTS
;
581 else if (strcmp(tokenp
, NC_TPI_CLTS_S
) == 0)
582 ncp
->nc_semantics
= NC_TPI_CLTS
;
583 else if (strcmp(tokenp
, NC_TPI_RAW_S
) == 0)
584 ncp
->nc_semantics
= NC_TPI_RAW
;
589 if ((tokenp
= strtok_r(NULL
, "\t ", &lasts
)) == NULL
) {
592 for (ncp
->nc_flag
= NC_NOFLAG
; *tokenp
!= '\0';
598 ncp
->nc_flag
|= NC_VISIBLE
;
601 ncp
->nc_flag
|= NC_BROADCAST
;
607 /* protocol family */
608 if ((ncp
->nc_protofmly
= strtok_r(NULL
, "\t ", &lasts
)) == NULL
) {
612 if ((ncp
->nc_proto
= strtok_r(NULL
, "\t ", &lasts
)) == NULL
) {
616 if ((ncp
->nc_device
= strtok_r(NULL
, "\t ", &lasts
)) == NULL
) {
619 if ((tokenp
= strtok_r(NULL
, "\t ", &lasts
)) == NULL
) {
622 if (strcmp(tokenp
, NC_NOLOOKUP
) == 0) {
623 ncp
->nc_nlookups
= 0;
624 ncp
->nc_lookups
= NULL
;
626 char *cp
; /* tmp string */
628 if (ncp
->nc_lookups
!= NULL
) /* from last visit */
629 free(ncp
->nc_lookups
);
630 /* preallocate one string pointer */
631 ncp
->nc_lookups
= (char **)malloc(sizeof (char *));
632 ncp
->nc_nlookups
= 0;
633 while ((cp
= tokenp
) != NULL
) {
634 tokenp
= _get_next_token(cp
, ',');
635 ncp
->nc_lookups
[(size_t)ncp
->nc_nlookups
++] = cp
;
636 ncp
->nc_lookups
= (char **)realloc(ncp
->nc_lookups
,
637 (size_t)(ncp
->nc_nlookups
+1) *sizeof(char *)); /* for next loop */
645 * Returns a string describing the reason for failure.
654 message
= _nc_errors
[0];
657 message
= _nc_errors
[1];
660 message
= _nc_errors
[2];
663 message
= _nc_errors
[3];
666 message
= _nc_errors
[4];
669 message
= "Unknown network selection error";
671 /* LINTED const castaway */
672 return ((char *)message
);
676 * Prints a message onto standard error describing the reason for failure.
682 fprintf(stderr
, "%s: %s\n", s
, nc_sperror());
686 * Duplicates the matched netconfig buffer.
688 static struct netconfig
*
690 struct netconfig
*ncp
;
696 if ((tmp
=malloc(MAXNETCONFIGLINE
)) == NULL
)
698 if ((p
=(struct netconfig
*)malloc(sizeof(struct netconfig
))) == NULL
) {
703 * First we dup all the data from matched netconfig buffer. Then we
704 * adjust some of the member pointer to a pre-allocated buffer where
705 * contains part of the data.
706 * To follow the convention used in parse_ncp(), we store all the
707 * necessary information in the pre-allocated buffer and let each
708 * of the netconfig char pointer member point to the right address
712 p
->nc_netid
= (char *)strcpy(tmp
,ncp
->nc_netid
);
713 tmp
= strchr(tmp
, 0) + 1;
714 p
->nc_protofmly
= (char *)strcpy(tmp
,ncp
->nc_protofmly
);
715 tmp
= strchr(tmp
, 0) + 1;
716 p
->nc_proto
= (char *)strcpy(tmp
,ncp
->nc_proto
);
717 tmp
= strchr(tmp
, 0) + 1;
718 p
->nc_device
= (char *)strcpy(tmp
,ncp
->nc_device
);
719 p
->nc_lookups
= (char **)malloc((size_t)(p
->nc_nlookups
+1) * sizeof(char *));
720 if (p
->nc_lookups
== NULL
) {
724 for (i
=0; i
< p
->nc_nlookups
; i
++) {
725 tmp
= strchr(tmp
, 0) + 1;
726 p
->nc_lookups
[i
] = (char *)strcpy(tmp
,ncp
->nc_lookups
[i
]);