Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 5 Mar 2012 15:12:49 +0200
From:      Andrey Simonenko <simon@comsys.ntu-kpi.kiev.ua>
To:        FreeBSD-gnats-submit@FreeBSD.org
Subject:   bin/165710: RPC: getnetconfig() and other netconfig's functions correct implementation.
Message-ID:  <20120305131249.GA74922@pm513-1.comsys.ntu-kpi.kiev.ua>
Resent-Message-ID: <201203051320.q25DKAdJ062415@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         165710
>Category:       bin
>Synopsis:       RPC: getnetconfig() and other netconfig's functions correct implementation.
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Mon Mar 05 13:20:09 UTC 2012
>Closed-Date:
>Last-Modified:
>Originator:     Andrey Simonenko
>Release:        FreeBSD 10.0-CURRENT amd64
>Organization:
>Environment:
>Description:

While developing a program that uses RPC I found out that functions
from libc/rpc/getnetconfig.c and getnetpath.c do not worked as expected
or have mistakes:

1.  __nc_error() does not check return value from malloc() and can
    pass NULL pointer to thr_setspecific().

2.  setnetconfig() has a race condition with reference counter when
    several threads call this function and only one thread successfully
    opened database file, while other threads failed in this function.
    If that one thread called endnetconfig(), then it can keep cached data
    in memory, but all threads will not have opened handles.

3.  getnetconfig() should return entries from /etc/netconfig file in
    the same order as they are specified according to netconfig(5).
    If several threads call getnetconfig() using different handlers,
    then entries for each handler will be returned in random order.

4.  getnetconfig() has a race condition that can cause NULL pointer
    dereference when several threads call this function using one handler.

5.  getnetconfig() allows to continue to get entries if database file has
    invalid format, because it does not remember previous state.

6.  endnetconfig() has a race condition with reference count and
    can keep cached data, while all handlers are closed.

7.  getnetconfigent() uses getnetconfig() and has the same mistakes,
    also this function duplicates code from getnetconfig().

8.  getnetconfig() and getnetconfigent() use too much memory for
    entry data, each entry require ~1 kbytes of memory, while usually
    only 50 bytes is needed.

9.  parse_ncp() incorrectly parses flags in netconfig entry and allows
    wrong combinations of flags, it does not allow spaces before entry,
    does not check number of elements in each netconfig entry, does not
    allow empty lines.

10. nc_sperror() is not optimal.

11. dup_ncp() is not optimal, allocates more memory than was used in
    the original structure, call strcpy() several times instead of
    calling memcpy() one time.

12. setnetpath() is not optimal, e.g. it calls setnetconfig() and then
    calls endnetconfig().

13. getnetpath() uses getnetconfig() and getnetconfigent() and has
    the same mistakes.

14. getnetpath() has race conditions when several threads call this
    function using one handler, as a result there are memory leaks
    and not synchronized access with modifications to data if the NETPATH
    environment variable is set.

15. _get_next_token() is too complex, incorrectly understand \-sequences.

16. All functions do not specify error code in all possible cases,
    so nc_sperror() and nc_perror() functions are useless.

I tried to modify getnetconfig.c and getnetpath.c files, but all
above listed mistakes (I could forget few other mistakes) required
complete rewriting of these two files.

So, I created a new file libc/rpc/netconfig.c that contains all
functions described in getnetconfig.3 and getnetpath.3.

Difference between netconfig.c vs getnetconfig.c and getnetpath.c:

1. __nc_error() was corrected, but its implementation is the same,
   this is a standard implementation for thread-specific data handling.
   nc_perror() was taken from getnetconfig.c, it cannot be written
   in other way.

2. Some errors messages were taken from getnetconfig.c.

3. New nc_parse() (old parse_ncp()) was corrected and optimized a bit,
   it just parses white space separated fields in a string.

4. Some variables and macro variables names were taken from getnetconfig.c.

5. All other functions and data structures were rewritten.

Additionally I corrected libc/include/reentrant.h, getnetconfig.3,
and getnetpath.3.

>How-To-Repeat:
>Fix:
diff -ruNp libc.orig/include/reentrant.h libc/include/reentrant.h
--- libc.orig/include/reentrant.h	2010-03-12 14:56:49.000000000 +0200
+++ libc/include/reentrant.h	2012-03-05 11:07:58.000000000 +0200
@@ -95,27 +95,19 @@
 #define ONCE_INITIALIZER	PTHREAD_ONCE_INIT
 
 #define mutex_init(m, a)	_pthread_mutex_init(m, a)
-#define mutex_lock(m)		if (__isthreaded) \
-				_pthread_mutex_lock(m)
-#define mutex_unlock(m)		if (__isthreaded) \
-				_pthread_mutex_unlock(m)
-#define mutex_trylock(m)	(__isthreaded ? 0 : _pthread_mutex_trylock(m))
+#define mutex_lock(m)		(__isthreaded ? _pthread_mutex_lock(m) : 0)
+#define mutex_unlock(m)		(__isthreaded ? _pthread_mutex_unlock(m) : 0)
+#define mutex_trylock(m)	(__isthreaded ? _pthread_mutex_trylock(m) : 0)
 
 #define cond_init(c, a, p)	_pthread_cond_init(c, a)
-#define cond_signal(m)		if (__isthreaded) \
-				_pthread_cond_signal(m)
-#define cond_broadcast(m)	if (__isthreaded) \
-				_pthread_cond_broadcast(m)
-#define cond_wait(c, m)		if (__isthreaded) \
-				_pthread_cond_wait(c, m)
+#define cond_signal(m)		(__isthreaded ? _pthread_cond_signal(m) : 0)
+#define cond_broadcast(m)	(__isthreaded ? _pthread_cond_broadcast(m) : 0)
+#define cond_wait(c, m)		(__isthreaded ? _pthread_cond_wait(c, m) : 0)
 
 #define rwlock_init(l, a)	_pthread_rwlock_init(l, a)
-#define rwlock_rdlock(l)	if (__isthreaded) \
-				_pthread_rwlock_rdlock(l)
-#define rwlock_wrlock(l)	if (__isthreaded) \
-				_pthread_rwlock_wrlock(l)
-#define rwlock_unlock(l)	if (__isthreaded) \
-				_pthread_rwlock_unlock(l)
+#define rwlock_rdlock(l)	(__isthreaded ? _pthread_rwlock_rdlock(l) : 0)
+#define rwlock_wrlock(l)	(__isthreaded ? _pthread_rwlock_wrlock(l) : 0)
+#define rwlock_unlock(l)	(__isthreaded ? _pthread_rwlock_unlock(l) : 0)
 
 #define thr_keycreate(k, d)	_pthread_key_create(k, d)
 #define thr_setspecific(k, p)	_pthread_setspecific(k, p)
diff -ruNp libc.orig/rpc/Makefile.inc libc/rpc/Makefile.inc
--- libc.orig/rpc/Makefile.inc	2012-02-08 11:58:52.000000000 +0200
+++ libc/rpc/Makefile.inc	2012-03-05 13:17:44.000000000 +0200
@@ -4,7 +4,7 @@
 .PATH: ${.CURDIR}/rpc ${.CURDIR}/.
 SRCS+=	auth_none.c auth_unix.c authunix_prot.c bindresvport.c clnt_bcast.c \
 	clnt_dg.c clnt_generic.c clnt_perror.c clnt_raw.c clnt_simple.c \
-	clnt_vc.c rpc_dtablesize.c getnetconfig.c getnetpath.c getrpcent.c \
+	clnt_vc.c rpc_dtablesize.c netconfig.c getrpcent.c \
 	getrpcport.c mt_misc.c pmap_clnt.c pmap_getmaps.c pmap_getport.c \
 	pmap_prot.c pmap_prot2.c pmap_rmt.c rpc_prot.c rpc_commondata.c \
 	rpc_callmsg.c rpc_generic.c rpc_soc.c rpcb_clnt.c rpcb_prot.c \
diff -ruNp libc.orig/rpc/getnetconfig.3 libc/rpc/getnetconfig.3
--- libc.orig/rpc/getnetconfig.3	2002-12-19 11:40:23.000000000 +0200
+++ libc/rpc/getnetconfig.3	2012-03-05 11:08:00.000000000 +0200
@@ -169,12 +169,7 @@ returns a unique handle to be used by
 In the case of an error,
 .Fn setnetconfig
 returns
-.Dv NULL
-and
-.Fn nc_perror
-or
-.Fn nc_sperror
-can be used to print the reason for failure.
+.Dv NULL .
 .Pp
 The
 .Fn getnetconfig
diff -ruNp libc.orig/rpc/getnetpath.3 libc/rpc/getnetpath.3
--- libc.orig/rpc/getnetpath.3	2002-12-18 14:45:10.000000000 +0200
+++ libc/rpc/getnetpath.3	2012-03-05 11:08:00.000000000 +0200
@@ -40,7 +40,10 @@ for other routines that also access the
 network configuration database directly.
 The
 .Ev NETPATH
-variable is a list of colon-separated network identifiers.
+variable is a list of colon-separated network identifiers,
+any character in a network identifier can be escaped by the
+.Ql \e
+character.
 .Pp
 The
 .Fn getnetpath
@@ -103,14 +106,12 @@ variable is unset,
 .Fn getnetpath
 behaves as if
 .Ev NETPATH
-were set to the sequence of
+was set to the sequence of
 .Dq default
 or
 .Dq visible
 networks in the netconfig database, in the
 order in which they are listed.
-.\"This proviso holds also for this
-.\"whole manpage.
 .Pp
 The
 .Fn endnetpath
@@ -143,14 +144,6 @@ returns 0 on success and \-1 on failure
 (for example, if
 .Fn setnetpath
 was not called previously).
-The
-.Fn nc_perror
-or
-.Fn nc_sperror
-function
-can be used to print out the reason for failure.
-See
-.Xr getnetconfig 3 .
 .Pp
 When first called,
 .Fn getnetpath
@@ -164,6 +157,14 @@ has been exhausted,
 .Fn getnetpath
 returns
 .Dv NULL .
+.Pp
+If any of these functions failed, then the
+.Fn nc_perror
+and
+.Fn nc_sperror
+functions can be used to print out the reason for failure.
+See
+.Xr getnetconfig 3 .
 .Sh SEE ALSO
 .Xr getnetconfig 3 ,
 .Xr netconfig 5 ,
diff -ruNp libc.orig/rpc/netconfig.c libc/rpc/netconfig.c
--- libc.orig/rpc/netconfig.c	1970-01-01 03:00:00.000000000 +0300
+++ libc/rpc/netconfig.c	2012-03-05 13:25:44.000000000 +0200
@@ -0,0 +1,744 @@
+/*-
+ * Copyright (c) 2012 Andrey Simonenko
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/queue.h>
+#include <sys/types.h>
+
+#include "namespace.h"
+#include "reentrant.h"
+#include <netconfig.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "un-namespace.h"
+
+/*
+ * Errors codes.
+ */
+#define NC_NOERROR	0
+#define NC_EFILE	1
+#define NC_ENOMEM	2
+#define NC_EINIT	3
+#define NC_EFORMAT	4
+#define NC_ELBIG	5
+#define NC_ENOENT	6
+#define NC_EEOF		7
+#define NC_EIO		8
+
+/*
+ * Semantics as strings.
+ */
+#define NC_TPI_CLTS_S	  "tpi_clts"
+#define	NC_TPI_COTS_S	  "tpi_cots"
+#define	NC_TPI_COTS_ORD_S "tpi_cots_ord"
+#define	NC_TPI_RAW_S	  "tpi_raw"
+
+#define NC_SEMANTICS_TBL_ELEM(x) { .name = x ## _S, .code = x }
+static const struct {
+	const char	*name;
+	u_long		code;
+} nc_semantics_tbl[] = {
+	NC_SEMANTICS_TBL_ELEM(NC_TPI_COTS_ORD),
+	NC_SEMANTICS_TBL_ELEM(NC_TPI_COTS),
+	NC_SEMANTICS_TBL_ELEM(NC_TPI_CLTS),
+	NC_SEMANTICS_TBL_ELEM(NC_TPI_RAW)
+};
+
+#define NC_SEMANTICS_TBL_SIZE \
+	(sizeof(nc_semantics_tbl) / sizeof(nc_semantics_tbl[0]))
+
+/*
+ * Flags as characters.
+ */
+#define	NC_NOFLAG_C	'-'
+#define	NC_VISIBLE_C	'v'
+#define	NC_BROADCAST_C	'b'
+
+/*
+ * Character used to indicate there is no name-to-address lookup library.
+ */
+#define NC_NOLOOKUP	"-"
+
+static const char * const nc_errlist[] = {
+	[NC_NOERROR]		= "No error",
+	[NC_EFILE]		= "Netconfig database cannot be opened",
+	[NC_ENOMEM]		= "Not enough memory",
+	[NC_EINIT]		= "Not initialized",
+	[NC_EFORMAT]		= "Netconfig database has invalid format",
+	[NC_ELBIG]		= "Line in netconfig database is too large",
+	[NC_ENOENT]		= "Netid not found in netconfig database",
+	[NC_EEOF]		= "Reached end of netconfig database",
+	[NC_EIO]		= "Netconfig database read error"
+};
+
+/*
+ * One entry from the NETCONFIG database.
+ */
+struct netconfig_entry {
+	STAILQ_ENTRY(netconfig_entry) link; /* For list building.	*/
+	struct netconfig nc;		/* One entry data.		*/
+};
+
+/*
+ * Handle for the getnetconfig() and endnetconfig() functions.
+ */
+struct netconfig_handle {
+	u_int		error;		/* Current state of session.	*/
+	struct netconfig_entry *nc_entry; /* Current entry in session.	*/
+};
+
+/*
+ * Handle for the getnetpath() and endnetpath() functions.
+ */
+struct netpath_handle {
+	void		*nc_handle;	/* Open netconfig_handle.	*/
+	char		*netpath_cur;	/* Current list in netpath_env.	*/
+	char		*netpath_env;	/* Copy of NETPATH.		*/
+};
+
+/*
+ * This mutex synchronizes access to all netpath_handles that
+ * were called when the NETPATH environment variable was defined.
+ */
+static mutex_t np_handle_lock = MUTEX_INITIALIZER;
+
+/*
+ * The NETCONFIG database file.
+ */
+static FILE *nc_file;
+
+/*
+ * Cache of already read entries from the NETCONFIG database.
+ */
+static struct {
+	STAILQ_HEAD(, netconfig_entry) list; /* List of cached entries.	*/
+	u_int		error;		/* Current state of cache data.	*/
+	u_int		ref;		/* Number of references.	*/
+} nc_cache = {
+	.list	= STAILQ_HEAD_INITIALIZER(nc_cache.list),
+	.error	= NC_EINIT,
+	.ref	= 0
+};
+
+/*
+ * This mutex synchronizes access to nc_cache, nc_file and
+ * all netconfig_handles.
+ */
+static mutex_t nc_cache_lock = MUTEX_INITIALIZER;
+
+static thread_key_t nc_key;
+static once_t nc_once = ONCE_INITIALIZER;
+static int nc_key_error;
+
+/*
+ * Maximum allowed line length in the NETCONFIG database.
+ */
+#define NC_LINE_MAX	1024
+
+/*
+ * Rules of memory allocation for (char *) fields in struct netconfig{}:
+ *   - memory for all strings is allocated in one block;
+ *   - address of allocated block is saved in the nc_netid field;
+ *   - other (char *) fields point to appropriate positions in this block;
+ *   - end of each string (data) is NUL terminated.
+ */
+
+static void
+nc_key_init(void)
+{
+	nc_key_error = thr_keycreate(&nc_key, free);
+}
+
+static u_int *
+__nc_error(void)
+{
+	static u_int nc_error = NC_NOERROR;
+
+	u_int *value;
+
+	if (thr_main())
+		goto one_value;
+	if (thr_once(&nc_once, nc_key_init) != 0 || nc_key_error != 0)
+		goto one_value;
+	value = thr_getspecific(nc_key);
+	if (value == NULL) {
+		value = malloc(sizeof(*value));
+		if (value == NULL)
+			goto one_value;
+		if (thr_setspecific(nc_key, value) != 0) {
+			free(value);
+			goto one_value;
+		}
+		*value = NC_NOERROR;
+	}
+	return (value);
+
+one_value:
+	return (&nc_error);
+}
+
+#define nc_error	(*__nc_error())
+
+/*
+ * Rewind open NETCONFIG database session.
+ */
+static __inline void
+nc_session_rewind(struct netconfig_handle *nc_handle)
+{
+	nc_handle->error = NC_NOERROR;
+	nc_handle->nc_entry = NULL;
+}
+
+/*
+ * Return address of the first non white space character in the given string.
+ */
+static char *
+nc_skip_spaces(char *str)
+{
+	for (;; ++str)
+		if (*str != ' ' && *str != '\t')
+			break;
+	return (str);
+}
+
+/*
+ * Select next field from the line.
+ * On enter *begp is a start for search.  On exit *begp is the next after
+ * the next character after a new field if EOL is not reached, else *begp
+ * will point to the NUL character right after selected field.
+ * The address of successfully selected field (which is finished by a NUL
+ * character) is saved in *fieldp.
+ */
+static int
+nc_select_field(char **begp, char **fieldp)
+{
+	char *beg, *ptr;
+
+	beg = nc_skip_spaces(*begp);
+	for (ptr = beg;; ++ptr) {
+		if (*ptr == ' ' || *ptr == '\t') {
+			*ptr = '\0';
+			if (*(ptr + 1) != '\0')
+				++ptr;
+			break;
+		}
+		if (*ptr == '\0')
+			break;
+	}
+	if (ptr == beg)
+		return (-1);
+	*begp = ptr;
+	*fieldp = beg;
+	return (0);
+}
+
+/*
+ * This function NUL terminates the first token from the given string,
+ * the end of element is marked by the given delimiter.
+ * Return value is the address of the rest of the string or NULL
+ * if there is no more elements.  Any character can be represented
+ * by \-sequence.  On return str will point to just selected token.
+ * Selected token can be empty string, so this case must be checked
+ * in the calling function.
+ */
+static char *
+nc_next_token(char *str, char delim)
+{
+	char *s, *p, *q;
+	bool esc, esc_flag;
+
+	for (s = str, esc = esc_flag = false;; ++s) {
+		if (esc)
+			esc = false;
+		else if (*s == '\\')
+			esc = esc_flag = true;
+		else if (*s == delim) {
+			*s++ = '\0';
+			break;
+		} else if (*s == '\0')
+			break;
+	}
+	if (esc_flag) {
+		for (q = p = str; *q != '\0'; ++p, ++q) {
+			if (*q == '\\') {
+				*p = *++q;
+				if (*p == '\0')
+					break;
+			} else
+				*p = *q;
+		}
+		*p = *q;
+	}
+	return (*s != '\0' ? s : NULL);
+}
+
+/*
+ * Parse the given line with the given length according to the netconfig(5)
+ * format.  The given line is started with some non-space data, does not have
+ * new-line character at the end.
+ */
+static int
+nc_parse(char *line, size_t len, struct netconfig *nc)
+{
+	char **nc_lookups;
+	char *s, *buf, *str, *token;
+	u_int i;
+
+	buf = str = malloc(len + 1);
+	if (buf == NULL) {
+		nc_error = NC_ENOMEM;
+		return (-1);
+	}
+	memcpy(buf, line, len + 1);
+
+	if (nc_select_field(&str, &nc->nc_netid) < 0)
+		goto failed_format;
+
+	if (nc_select_field(&str, &token) < 0)
+		goto failed_format;
+	for (i = 0; i < NC_SEMANTICS_TBL_SIZE; ++i)
+		if (strcmp(token, nc_semantics_tbl[i].name) == 0) {
+			nc->nc_semantics = nc_semantics_tbl[i].code;
+			break;
+		}
+	if (i == NC_SEMANTICS_TBL_SIZE)
+		goto failed_format;
+
+	if (nc_select_field(&str, &token) < 0)
+		goto failed_format;
+	nc->nc_flag = NC_NOFLAG;
+	if (token[0] == NC_NOFLAG_C) {
+		if (token[1] != '\0')
+			goto failed_format;
+	} else {
+		do {
+			switch (*token) {
+			case NC_VISIBLE_C:
+				nc->nc_flag |= NC_VISIBLE;
+				break;
+			case NC_BROADCAST_C:
+				nc->nc_flag |= NC_BROADCAST;
+				break;
+			default:
+				goto failed_format;
+			}
+		} while (*++token != '\0');
+	}
+
+	if (nc_select_field(&str, &nc->nc_protofmly) < 0)
+		goto failed_format;
+
+	if (nc_select_field(&str, &nc->nc_proto) < 0)
+		goto failed_format;
+
+	if (nc_select_field(&str, &nc->nc_device) < 0)
+		goto failed_format;
+
+	if (nc_select_field(&str, &token) < 0)
+		goto failed_format;
+	nc->nc_nlookups = 0;
+	nc->nc_lookups = NULL;
+	if (strcmp(token, NC_NOLOOKUP) != 0) {
+		while ((s = token) != NULL) {
+			token = nc_next_token(s, ',');
+			if (*s != '\0') {
+				nc->nc_nlookups++;
+				nc_lookups = realloc(nc->nc_lookups,
+				    nc->nc_nlookups * sizeof(*nc->nc_lookups));
+				if (nc_lookups == NULL) {
+					free(nc->nc_lookups);
+					nc_error = NC_ENOMEM;
+					goto failed;
+				}
+				nc->nc_lookups = nc_lookups;
+				nc->nc_lookups[nc->nc_nlookups - 1] = s;
+			}
+		}
+	}
+
+	if (*str != '\0') {
+		free(nc->nc_lookups);
+		goto failed_format;
+	}
+	return (0);
+
+failed_format:
+	nc_error = NC_EFORMAT;
+failed:
+	free(buf);
+	return (-1);
+}
+
+/*
+ * Duplicate the given netconfig structure.
+ */
+static struct netconfig *
+nc_dup(const struct netconfig *nc_src)
+{
+	struct netconfig *nc_dst;
+	size_t line_size;
+	u_long i;
+
+	nc_dst = malloc(sizeof(*nc_dst));
+	if (nc_dst == NULL)
+		goto failed;
+	if (nc_src->nc_nlookups != 0) {
+		i = nc_src->nc_nlookups - 1;
+		line_size = nc_src->nc_lookups[i] +
+		    strlen(nc_src->nc_lookups[i]) - nc_src->nc_netid;
+	} else
+		line_size = nc_src->nc_device +
+		    strlen(nc_src->nc_device) - nc_src->nc_netid;
+	++line_size;
+
+	nc_dst->nc_netid = malloc(line_size);
+	if (nc_dst->nc_netid == NULL) {
+		free(nc_dst);
+		goto failed;
+	}
+	memcpy(nc_dst->nc_netid, nc_src->nc_netid, line_size);
+
+	nc_dst->nc_nlookups = nc_src->nc_nlookups;
+	if (nc_dst->nc_nlookups != 0) {
+		nc_dst->nc_lookups = malloc(nc_dst->nc_nlookups *
+		    sizeof(*nc_dst->nc_lookups));
+		if (nc_dst->nc_lookups == NULL) {
+			free(nc_dst->nc_netid);
+			free(nc_dst);
+			goto failed;
+		}
+		for (i = 0; i < nc_dst->nc_nlookups; i++)
+			nc_dst->nc_lookups[i] = nc_dst->nc_netid +
+			    (nc_src->nc_lookups[i] - nc_src->nc_netid);
+	} else
+		nc_dst->nc_lookups = NULL;
+
+	nc_dst->nc_semantics = nc_src->nc_semantics;
+	nc_dst->nc_flag = nc_src->nc_flag;
+	nc_dst->nc_protofmly = nc_dst->nc_netid +
+	    (nc_src->nc_protofmly - nc_src->nc_netid);
+	nc_dst->nc_proto = nc_dst->nc_netid +
+	    (nc_src->nc_proto - nc_src->nc_netid);
+	nc_dst->nc_device = nc_dst->nc_netid +
+	    (nc_src->nc_device - nc_src->nc_netid);
+
+	return (nc_dst);
+
+failed:
+	nc_error = NC_ENOMEM;
+	return (NULL);
+}
+
+void *
+setnetconfig(void)
+{
+	struct netconfig_handle *nc_handle;
+
+	nc_handle = malloc(sizeof(*nc_handle));
+	if (nc_handle == NULL) {
+		nc_error = NC_ENOMEM;
+		return (NULL);
+	}
+
+	mutex_lock(&nc_cache_lock);
+	if (nc_cache.error == NC_EINIT) {
+		nc_file = fopen(NETCONFIG, "r");
+		if (nc_file == NULL) {
+			nc_error = NC_EFILE;
+			mutex_unlock(&nc_cache_lock);
+			free(nc_handle);
+			return (NULL);
+		}
+		nc_cache.error = NC_NOERROR;
+	}
+	nc_cache.ref++;
+	mutex_unlock(&nc_cache_lock);
+
+	nc_session_rewind(nc_handle);
+	return (nc_handle);
+}
+
+struct netconfig *
+getnetconfig(void *handle)
+{
+	struct netconfig_handle *nc_handle;
+	struct netconfig_entry *nc_entry;
+	char *line, *line_data;
+	size_t line_len;
+
+	if (handle == NULL) {
+		nc_error = NC_EINIT;
+		return (NULL);
+	}
+	nc_handle = handle;
+
+	mutex_lock(&nc_cache_lock);
+
+	if (nc_handle->error != NC_NOERROR) {
+		nc_error = nc_handle->error;
+		mutex_unlock(&nc_cache_lock);
+		return (NULL);
+	}
+
+	nc_entry = nc_handle->nc_entry == NULL ?
+	    STAILQ_FIRST(&nc_cache.list) :
+	    STAILQ_NEXT(nc_handle->nc_entry, link);
+	if (nc_entry != NULL)
+		goto done;
+
+	if (nc_cache.error != NC_NOERROR) {
+		nc_error = nc_handle->error = nc_cache.error;
+		goto done;
+	}
+
+	line = malloc(NC_LINE_MAX);
+	nc_entry = malloc(sizeof(*nc_entry));
+	if (line == NULL || nc_entry == NULL) {
+		nc_error = NC_ENOMEM;
+		goto failed;
+	}
+
+	do {
+		if (fgets(line, NC_LINE_MAX, nc_file) == NULL) {
+			nc_error = feof(nc_file) ? NC_EEOF : NC_EIO;
+			goto failed;
+		}
+		line_len = strlen(line);
+		if (line[line_len - 1] != '\n' && !feof(nc_file)) {
+			nc_error = NC_ELBIG;
+			goto failed;
+		}
+		if (line[line_len - 1] == '\n') {
+			--line_len;
+			line[line_len] = '\0';
+		}
+		line_data = nc_skip_spaces(line);
+		line_len -= line_data - line;
+	} while (*line_data == '#' || *line_data == '\0');
+
+	if (nc_parse(line_data, line_len, &nc_entry->nc) < 0)
+		goto failed;
+
+	STAILQ_INSERT_TAIL(&nc_cache.list, nc_entry, link);
+	free(line);
+done:
+	nc_handle->nc_entry = nc_entry;
+	mutex_unlock(&nc_cache_lock);
+	return (nc_entry != NULL ? &nc_entry->nc : NULL);
+
+failed:
+	fclose(nc_file);
+	nc_file = NULL;
+	nc_cache.error = nc_handle->error = nc_error;
+	mutex_unlock(&nc_cache_lock);
+	free(line);
+	free(nc_entry);
+	return (NULL);
+}
+
+void
+freenetconfigent(struct netconfig *nc)
+{
+	if (nc != NULL) {
+		free(nc->nc_netid);
+		free(nc->nc_lookups);
+		free(nc);
+	}
+}
+
+int
+endnetconfig(void *handle)
+{
+	struct netconfig_entry *nc_entry, *nc_entry_next;
+
+	if (handle == NULL) {
+		nc_error = NC_EINIT;
+		return (-1);
+	}
+	free(handle);
+
+	mutex_lock(&nc_cache_lock);
+	if (--nc_cache.ref == 0) {
+		nc_entry = STAILQ_FIRST(&nc_cache.list);
+		STAILQ_INIT(&nc_cache.list);
+		nc_cache.error = NC_EINIT;
+		if (nc_file != NULL) {
+			fclose(nc_file);
+			nc_file = NULL;
+		}
+	} else
+		nc_entry = NULL;
+	mutex_unlock(&nc_cache_lock);
+
+	for (; nc_entry != NULL; nc_entry = nc_entry_next) {
+		nc_entry_next = STAILQ_NEXT(nc_entry, link);
+		free(nc_entry->nc.nc_netid);
+		free(nc_entry->nc.nc_lookups);
+		free(nc_entry);
+	}
+
+	return (0);
+}
+
+struct netconfig *
+getnetconfigent(const char *netid)
+{
+	struct netconfig *nc;
+	void *handle;
+
+	if (netid == NULL) {
+		nc_error = NC_ENOENT;
+		return (NULL);
+	}
+
+	handle = setnetconfig();
+	if (handle == NULL)
+		return (NULL);
+
+	while ((nc = getnetconfig(handle)) != NULL)
+		if (strcmp(nc->nc_netid, netid) == 0) {
+			nc = nc_dup(nc);
+			break;
+		}
+	if (nc == NULL && nc_error == NC_EEOF)
+		nc_error = NC_ENOENT;
+
+	if (endnetconfig(handle) < 0) {
+		freenetconfigent(nc);
+		nc = NULL;
+	}
+
+	return (nc);
+}
+
+char *
+nc_sperror(void)
+{
+	return ((char *)nc_errlist[nc_error]);
+}
+
+void
+nc_perror(const char *s)
+{
+	fprintf(stderr, "%s: %s\n", s, nc_sperror());
+}
+
+void *
+setnetpath(void)
+{
+	const char *env;
+	struct netpath_handle *np_handle;
+
+	np_handle = malloc(sizeof(*np_handle));
+	if (np_handle == NULL) {
+		nc_error = NC_ENOMEM;
+		return (NULL);
+	}
+
+	env = getenv(NETPATH);
+	if (env != NULL) {
+		np_handle->netpath_env = strdup(env);
+		if (np_handle->netpath_env == NULL) {
+			nc_error = NC_ENOMEM;
+			goto failed;
+		}
+	} else
+		np_handle->netpath_env = NULL;
+	np_handle->netpath_cur = np_handle->netpath_env;
+
+	np_handle->nc_handle = setnetconfig();
+	if (np_handle->nc_handle == NULL)
+		goto failed;
+
+	return (np_handle);
+
+failed:
+	free(np_handle);
+	return (NULL);
+}
+
+struct netconfig *
+getnetpath(void *handle)
+{
+	struct netpath_handle *np_handle;
+	struct netconfig *nc;
+	void *nc_handle;
+	char *netid;
+
+	if (handle == NULL) {
+		nc_error = NC_EINIT;
+		return (NULL);
+	}
+	np_handle = handle;
+
+	nc_handle = np_handle->nc_handle;
+	if (np_handle->netpath_env == NULL) {
+		while ((nc = getnetconfig(nc_handle)) != NULL)
+			if (nc->nc_flag & NC_VISIBLE)
+				 break;
+	} else {
+		nc = NULL;
+		nc_error = NC_EEOF;
+		mutex_lock(&np_handle_lock);
+		while ((netid = np_handle->netpath_cur) != NULL) {
+			np_handle->netpath_cur = nc_next_token(netid, ':');
+			if (*netid != '\0') {
+				nc_session_rewind(nc_handle);
+				nc_error = NC_NOERROR;
+				while ((nc = getnetconfig(nc_handle)) != NULL)
+					if (strcmp(nc->nc_netid, netid) == 0)
+						break;
+				if (nc != NULL || nc_error != NC_EEOF)
+					break;
+			}
+		}
+		mutex_unlock(&np_handle_lock);
+	}
+	return (nc);
+}
+
+int
+endnetpath(void *handle)
+{
+	struct netpath_handle *np_handle;
+	void *nc_handle;
+
+	if (handle == NULL) {
+		nc_error = NC_EINIT;
+		return (-1);
+	}
+	np_handle = handle;
+
+	nc_handle = np_handle->nc_handle;
+	free(np_handle->netpath_env);
+	free(np_handle);
+
+	return (endnetconfig(nc_handle));
+}
diff -ruNp libc.orig/rpc/rpc_com.h libc/rpc/rpc_com.h
--- libc.orig/rpc/rpc_com.h	2006-02-28 00:10:59.000000000 +0200
+++ libc/rpc/rpc_com.h	2012-03-05 11:07:58.000000000 +0200
@@ -80,8 +80,6 @@ struct netbuf *__rpcb_findaddr_timed(rpc
 
 bool_t __rpc_control(int,void *);
 
-char *_get_next_token(char *, int);
-
 bool_t __svc_clean_idle(fd_set *, int, bool_t);
 bool_t __xdrrec_setnonblock(XDR *, int);
 bool_t __xdrrec_getrec(XDR *, enum xprt_stat *, bool_t);


>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20120305131249.GA74922>