#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char sccsid[] = "@(#)getnetconfig.c 1.12 91/12/19 SMI";
#else
__RCSID("$NetBSD: getnetconfig.c,v 1.25 2017/06/30 10:03:34 christos Exp $");
#endif
#endif
#include "namespace.h"
#include "reentrant.h"
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <netconfig.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <rpc/rpc.h>
#include "rpc_internal.h"
#ifdef __weak_alias
__weak_alias(getnetconfig,_getnetconfig)
__weak_alias(setnetconfig,_setnetconfig)
__weak_alias(endnetconfig,_endnetconfig)
__weak_alias(getnetconfigent,_getnetconfigent)
__weak_alias(freenetconfigent,_freenetconfigent)
__weak_alias(nc_perror,_nc_perror)
__weak_alias(nc_sperror,_nc_sperror)
#endif
#define NC_NONETCONFIG ENOENT
#define NC_NOMEM ENOMEM
#define NC_NOTINIT EINVAL
#define NC_BADFILE EBADF
#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_NOFLAG_C '-'
#define NC_VISIBLE_C 'v'
#define NC_BROADCAST_C 'b'
#define NC_NOLOOKUP "-"
static const char * const _nc_errors[] = {
"Netconfig database not found",
"Not enough memory",
"Not initialized",
"Netconfig database has invalid format"
};
struct netconfig_info {
int eof;
int ref;
struct netconfig_list *head;
struct netconfig_list *tail;
};
struct netconfig_list {
char *linep;
struct netconfig *ncp;
struct netconfig_list *next;
};
struct netconfig_vars {
int valid;
int flag;
struct netconfig_list *nc_configs;
};
#define NC_VALID 0xfeed
#define NC_STORAGE 0xf00d
#define NC_INVALID 0
static int *__nc_error(void);
static int parse_ncp(char *, struct netconfig *);
static struct netconfig *dup_ncp(struct netconfig *);
static FILE *nc_file;
static struct netconfig_info ni = { 0, 0, NULL, NULL};
#define MAXNETCONFIGLINE 1000
#ifdef _REENTRANT
static thread_key_t nc_key;
static once_t nc_once = ONCE_INITIALIZER;
static void
__nc_error_setup(void)
{
thr_keycreate(&nc_key, free);
}
#endif
static int *
__nc_error(void)
{
#ifdef _REENTRANT
int *nc_addr = NULL;
#endif
static int nc_error = 0;
#ifdef _REENTRANT
if (__isthreaded == 0)
return &nc_error;
thr_once(&nc_once, __nc_error_setup);
nc_addr = thr_getspecific(nc_key) ;
if (nc_addr == NULL) {
nc_addr = malloc(sizeof (int));
if (nc_addr == NULL)
return &nc_error;
if (thr_setspecific(nc_key, nc_addr) != 0) {
if (nc_addr)
free(nc_addr);
return &nc_error;
}
*nc_addr = 0;
}
return nc_addr;
#else
return &nc_error;
#endif
}
#define nc_error (*(__nc_error()))
void *
setnetconfig(void)
{
struct netconfig_vars *nc_vars;
if ((nc_vars = malloc(sizeof(*nc_vars))) == NULL) {
return NULL;
}
ni.ref++;
if ((nc_file != NULL) || (nc_file = fopen(NETCONFIG, "re")) != NULL) {
nc_vars->valid = NC_VALID;
nc_vars->flag = 0;
nc_vars->nc_configs = ni.head;
return nc_vars;
}
ni.ref--;
nc_error = NC_NONETCONFIG;
free(nc_vars);
return NULL;
}
struct netconfig *
getnetconfig(void *handlep)
{
struct netconfig_vars *ncp = handlep;
char *stringp;
struct netconfig_list *list;
struct netconfig *np;
if (ncp == NULL || nc_file == NULL) {
nc_error = NC_NOTINIT;
return NULL;
}
switch (ncp->valid) {
case NC_VALID:
if (ncp->flag == 0) {
ncp->flag = 1;
ncp->nc_configs = ni.head;
if (ncp->nc_configs != NULL)
return ncp->nc_configs->ncp;
}
else if (ncp->nc_configs != NULL &&
ncp->nc_configs->next != NULL) {
ncp->nc_configs = ncp->nc_configs->next;
return ncp->nc_configs->ncp;
}
if (ni.eof == 1)
return NULL;
break;
default:
nc_error = NC_NOTINIT;
return NULL;
}
stringp = malloc(MAXNETCONFIGLINE);
if (stringp == NULL)
return NULL;
#ifdef MEM_CHK
if (malloc_verify() == 0) {
fprintf(stderr, "memory heap corrupted in getnetconfig\n");
exit(1);
}
#endif
do {
if (fgets(stringp, MAXNETCONFIGLINE, nc_file) == NULL) {
free(stringp);
ni.eof = 1;
return NULL;
}
} while (*stringp == '#');
list = malloc(sizeof(*list));
if (list == NULL) {
free(stringp);
return NULL;
}
np = malloc(sizeof(*np));
if (np == NULL) {
free(stringp);
free(list);
return NULL;
}
list->ncp = np;
list->next = NULL;
list->ncp->nc_lookups = NULL;
list->linep = stringp;
if (parse_ncp(stringp, list->ncp) == -1) {
free(stringp);
free(np);
free(list);
return NULL;
} else {
if (ni.head == NULL)
ni.head = ni.tail = list;
else {
ni.tail->next = list;
ni.tail = ni.tail->next;
}
ncp->nc_configs = ni.tail;
return ni.tail->ncp;
}
}
int
endnetconfig(void *handlep)
{
struct netconfig_vars *nc_handlep = handlep;
struct netconfig_list *q, *p;
if (nc_handlep == NULL || (nc_handlep->valid != NC_VALID &&
nc_handlep->valid != NC_STORAGE)) {
nc_error = NC_NOTINIT;
return -1;
}
nc_handlep->valid = NC_INVALID;
nc_handlep->flag = 0;
nc_handlep->nc_configs = NULL;
if (--ni.ref > 0) {
free(nc_handlep);
return 0;
}
q = p = ni.head;
ni.eof = ni.ref = 0;
ni.head = NULL;
ni.tail = NULL;
while (q) {
p = q->next;
if (q->ncp->nc_lookups != NULL) free(q->ncp->nc_lookups);
free(q->ncp);
free(q->linep);
free(q);
q = p;
}
free(nc_handlep);
fclose(nc_file);
nc_file = NULL;
return 0;
}
struct netconfig *
getnetconfigent(const char *netid)
{
FILE *file;
char *linep;
char *stringp;
struct netconfig *ncp = NULL;
struct netconfig_list *list;
if (netid == NULL || strlen(netid) == 0)
return NULL;
if (ni.head != NULL) {
for (list = ni.head; list; list = list->next) {
if (strcmp(list->ncp->nc_netid, netid) == 0)
return dup_ncp(list->ncp);
}
if (ni.eof == 1)
return NULL;
}
if ((file = fopen(NETCONFIG, "re")) == NULL)
return NULL;
if ((linep = malloc(MAXNETCONFIGLINE)) == NULL) {
fclose(file);
return NULL;
}
do {
size_t len;
char *tmpp;
do {
if ((stringp = fgets(linep, MAXNETCONFIGLINE, file))
== NULL)
break;
} while (*stringp == '#');
if (stringp == NULL)
break;
if ((tmpp = strpbrk(stringp, "\t ")) == NULL) {
nc_error = NC_BADFILE;
break;
}
if (strlen(netid) == (len = (size_t)(tmpp - stringp)) &&
strncmp(stringp, netid, len) == 0) {
if ((ncp = malloc(sizeof(*ncp))) == NULL)
break;
ncp->nc_lookups = NULL;
if (parse_ncp(linep, ncp) == -1) {
free(ncp);
ncp = NULL;
}
break;
}
} while (stringp != NULL);
if (ncp == NULL)
free(linep);
fclose(file);
return ncp;
}
void
freenetconfigent(struct netconfig *netconfigp)
{
if (netconfigp != NULL) {
free(netconfigp->nc_netid);
if (netconfigp->nc_lookups != NULL)
free(netconfigp->nc_lookups);
free(netconfigp);
}
}
static int
parse_ncp(
char *stringp,
struct netconfig *ncp)
{
char *tokenp;
char *lasts;
_DIAGASSERT(stringp != NULL);
_DIAGASSERT(ncp != NULL);
nc_error = NC_BADFILE;
stringp[strlen(stringp)-1] = '\0';
if ((ncp->nc_netid = strtok_r(stringp, "\t ", &lasts)) == NULL)
return -1;
if ((tokenp = strtok_r(NULL, "\t ", &lasts)) == NULL)
return -1;
if (strcmp(tokenp, NC_TPI_COTS_ORD_S) == 0)
ncp->nc_semantics = NC_TPI_COTS_ORD;
else if (strcmp(tokenp, NC_TPI_COTS_S) == 0)
ncp->nc_semantics = NC_TPI_COTS;
else if (strcmp(tokenp, NC_TPI_CLTS_S) == 0)
ncp->nc_semantics = NC_TPI_CLTS;
else if (strcmp(tokenp, NC_TPI_RAW_S) == 0)
ncp->nc_semantics = NC_TPI_RAW;
else
return -1;
if ((tokenp = strtok_r(NULL, "\t ", &lasts)) == NULL)
return -1;
for (ncp->nc_flag = NC_NOFLAG; *tokenp != '\0'; tokenp++) {
switch (*tokenp) {
case NC_NOFLAG_C:
break;
case NC_VISIBLE_C:
ncp->nc_flag |= NC_VISIBLE;
break;
case NC_BROADCAST_C:
ncp->nc_flag |= NC_BROADCAST;
break;
default:
return -1;
}
}
if ((ncp->nc_protofmly = strtok_r(NULL, "\t ", &lasts)) == NULL)
return -1;
if ((ncp->nc_proto = strtok_r(NULL, "\t ", &lasts)) == NULL)
return -1;
if ((ncp->nc_device = strtok_r(NULL, "\t ", &lasts)) == NULL)
return -1;
if ((tokenp = strtok_r(NULL, "\t ", &lasts)) == NULL)
return -1;
if (strcmp(tokenp, NC_NOLOOKUP) == 0) {
ncp->nc_nlookups = 0;
ncp->nc_lookups = NULL;
} else {
char *cp;
if (ncp->nc_lookups != NULL)
free(ncp->nc_lookups);
ncp->nc_lookups = malloc(sizeof(*ncp->nc_lookups));
ncp->nc_nlookups = 0;
while ((cp = tokenp) != NULL) {
tokenp = _get_next_token(cp, ',');
ncp->nc_lookups[(size_t)ncp->nc_nlookups++] = cp;
ncp->nc_lookups = (char **)
realloc(ncp->nc_lookups,
(size_t)(ncp->nc_nlookups+1) *sizeof(char *));
}
}
return 0;
}
char *
nc_sperror(void)
{
const char *message;
switch(nc_error) {
case NC_NONETCONFIG:
message = _nc_errors[0];
break;
case NC_NOMEM:
message = _nc_errors[1];
break;
case NC_NOTINIT:
message = _nc_errors[2];
break;
case NC_BADFILE:
message = _nc_errors[3];
break;
default:
message = "Unknown network selection error";
}
return __UNCONST(message);
}
void
nc_perror(const char *s)
{
_DIAGASSERT(s != NULL);
fprintf(stderr, "%s: %s", s, nc_sperror());
}
static struct netconfig *
dup_ncp(struct netconfig *ncp)
{
struct netconfig *p;
char *tmp;
u_int i;
_DIAGASSERT(ncp != NULL);
if ((tmp = malloc(MAXNETCONFIGLINE)) == NULL)
return NULL;
if ((p = malloc(sizeof(*p))) == NULL) {
free(tmp);
return NULL;
}
*p = *ncp;
p->nc_netid = strcpy(tmp, ncp->nc_netid);
tmp = strchr(tmp, '\0') + 1;
p->nc_protofmly = strcpy(tmp, ncp->nc_protofmly);
tmp = strchr(tmp, '\0') + 1;
p->nc_proto = strcpy(tmp, ncp->nc_proto);
tmp = strchr(tmp, '\0') + 1;
p->nc_device = strcpy(tmp, ncp->nc_device);
p->nc_lookups = calloc((size_t)(p->nc_nlookups + 1), sizeof(char *));
if (p->nc_lookups == NULL) {
free(p->nc_netid);
free(p);
return NULL;
}
for (i = 0; i < p->nc_nlookups; i++) {
tmp = strchr(tmp, '\0') + 1;
p->nc_lookups[i] = strcpy(tmp, ncp->nc_lookups[i]);
}
return p;
}