#include "mt.h"
#include "../rpc/rpc_mt.h"
#include <rpc/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <netconfig.h>
#include <malloc.h>
#include <libintl.h>
#include <syslog.h>
#include "netcspace.h"
#define FAILURE (unsigned)(-1)
static int blank(char *);
static int comment(char *);
static struct netconfig *fgetnetconfig(FILE *, char *);
static void netconfig_free(struct netconfig *);
static unsigned int getflag(char *);
static char **getlookups(char *);
static struct netconfig **getnetlist(void);
static unsigned int getnlookups(char *);
static char *gettoken(char *, int);
static unsigned int getvalue(char *, struct nc_data nc_data[]);
static void shift1left(char *);
static void netlist_free(struct netconfig ***);
static void free_entry(void *);
static struct netconfig *netconfig_dup(struct netconfig *);
extern const char __nsl_dom[];
static struct netconfig **netpp = NULL;
mutex_t netpp_mutex = DEFAULTMUTEX;
static int linenum = 0;
static int fieldnum = 0;
static int *
__nc_error(void)
{
static pthread_key_t nc_error_key = PTHREAD_ONCE_KEY_NP;
static int nc_error = NC_NOERROR;
int *ret;
if (thr_main())
return (&nc_error);
ret = thr_get_storage(&nc_error_key, sizeof (int), free);
return (ret ? ret : &nc_error);
}
#define nc_error (*(__nc_error()))
void *
setnetconfig(void)
{
NCONF_HANDLE *retp;
(void) mutex_lock(&netpp_mutex);
if ((netpp == NULL) && ((netpp = getnetlist()) == NULL)) {
(void) mutex_unlock(&netpp_mutex);
return (NULL);
}
(void) mutex_unlock(&netpp_mutex);
if ((retp = malloc(sizeof (NCONF_HANDLE))) == NULL) {
nc_error = NC_NOMEM;
return (NULL);
}
nc_error = NC_NOERROR;
retp->nc_head = retp->nc_curr = netpp;
return ((void *)retp);
}
int
endnetconfig(void *vdata)
{
NCONF_HANDLE *nconf_handlep = (NCONF_HANDLE *)vdata;
(void) mutex_lock(&netpp_mutex);
if (netpp == NULL || nconf_handlep == NULL) {
nc_error = NC_NOSET;
(void) mutex_unlock(&netpp_mutex);
return (-1);
}
(void) mutex_unlock(&netpp_mutex);
nc_error = NC_NOERROR;
free(nconf_handlep);
return (0);
}
struct netconfig *
getnetconfig(void *vdata)
{
NCONF_HANDLE *nconf_handlep = (NCONF_HANDLE *)vdata;
struct netconfig *retp;
int ipv6_present = -1;
(void) mutex_lock(&netpp_mutex);
if ((netpp == NULL) || (nconf_handlep == NULL)) {
nc_error = NC_NOSET;
(void) mutex_unlock(&netpp_mutex);
return (NULL);
}
(void) mutex_unlock(&netpp_mutex);
for (;;) {
retp = *(nconf_handlep->nc_curr);
if (retp && (strcmp(retp->nc_netid, "udp6") == 0 ||
strcmp(retp->nc_netid, "tcp6") == 0)) {
if (ipv6_present == -1)
ipv6_present = __can_use_af(AF_INET6);
if (!ipv6_present) {
++(nconf_handlep->nc_curr);
continue;
}
}
break;
}
if (retp != NULL) {
++(nconf_handlep->nc_curr);
nc_error = NC_NOERROR;
} else {
nc_error = NC_NOMOREENTRIES;
}
return (retp);
}
struct netconfig *
getnetconfigent(const char *netid)
{
struct netconfig **tpp;
int ipv6_present;
(void) mutex_lock(&netpp_mutex);
if ((netpp == NULL) && ((netpp = getnetlist()) == NULL)) {
(void) mutex_unlock(&netpp_mutex);
return (NULL);
}
(void) mutex_unlock(&netpp_mutex);
for (tpp = netpp; *tpp; tpp++) {
if (strcmp((*tpp)->nc_netid, netid) == 0) {
if (*tpp && (strcmp((*tpp)->nc_netid, "udp6") == 0 ||
strcmp((*tpp)->nc_netid, "tcp6") == 0)) {
ipv6_present = __can_use_af(AF_INET6);
if (!ipv6_present) {
nc_error = NC_NOTFOUND;
return (NULL);
}
}
return (netconfig_dup(*tpp));
}
}
nc_error = NC_NOTFOUND;
return (NULL);
}
void
freenetconfigent(struct netconfig *netp)
{
netconfig_free(netp);
}
static struct netconfig **
getnetlist(void)
{
char line[BUFSIZ];
FILE *fp;
struct netconfig **listpp;
struct netconfig **tpp;
int count;
if ((fp = fopen(NETCONFIG, "rF")) == NULL) {
nc_error = NC_OPENFAIL;
return (NULL);
}
count = 0;
while (fgets(line, BUFSIZ, fp)) {
if (!(blank(line) || comment(line))) {
++count;
}
}
rewind(fp);
if (count == 0) {
nc_error = NC_NOTFOUND;
(void) fclose(fp);
return (NULL);
}
if ((listpp = malloc((count + 1) *
sizeof (struct netconfig *))) == NULL) {
nc_error = NC_NOMEM;
(void) fclose(fp);
return (NULL);
}
linenum = 0;
for (tpp = listpp; *tpp = fgetnetconfig(fp, NULL); tpp++)
;
(void) fclose(fp);
if (nc_error != NC_NOMOREENTRIES)
netlist_free(&listpp);
return (listpp);
}
static struct netconfig *
fgetnetconfig(FILE *fp, char *netid)
{
char linep[BUFSIZ];
struct netconfig *netconfigp;
char *tok1, *tok2, *tok3;
char *retvalp;
char *entnetid;
while (retvalp = fgets(linep, BUFSIZ, fp)) {
linenum++;
if (!(blank(linep) || comment(linep))) {
break;
}
retvalp = NULL;
}
if (retvalp == NULL) {
nc_error = NC_NOMOREENTRIES;
return (NULL);
}
fieldnum = 0;
if ((entnetid = gettoken(linep, FALSE)) == NULL) {
nc_error = NC_BADLINE;
return (NULL);
}
if (netid && (strcmp(netid, entnetid) != 0)) {
free(entnetid);
nc_error = NC_NOTFOUND;
return (NULL);
}
if ((netconfigp = calloc(1, sizeof (struct netconfig))) == NULL) {
free(entnetid);
nc_error = NC_NOMEM;
return (NULL);
}
tok1 = tok2 = tok3 = NULL;
netconfigp->nc_netid = entnetid;
if (((tok1 = gettoken(NULL, FALSE)) == NULL) ||
((netconfigp->nc_semantics =
getvalue(tok1, nc_semantics)) == FAILURE) ||
((tok2 = gettoken(NULL, FALSE)) == NULL) ||
((netconfigp->nc_flag = getflag(tok2)) == FAILURE) ||
((netconfigp->nc_protofmly = gettoken(NULL, FALSE)) == NULL) ||
((netconfigp->nc_proto = gettoken(NULL, FALSE)) == NULL) ||
((netconfigp->nc_device = gettoken(NULL, FALSE)) == NULL) ||
((tok3 = gettoken(NULL, TRUE)) == NULL) ||
(((netconfigp->nc_nlookups = getnlookups(tok3)) != 0) &&
((netconfigp->nc_lookups = getlookups(tok3)) == NULL))) {
netconfig_free(netconfigp);
nc_error = NC_BADLINE;
netconfigp = NULL;
}
free(tok1);
free(tok2);
free(tok3);
return (netconfigp);
}
void *
setnetpath(void)
{
int count;
char valid_netpath[BUFSIZ];
char templine[BUFSIZ];
struct netconfig **curr_pp;
struct netconfig **tpp;
struct netconfig **rnetpp;
char *netpath;
char *netid;
char *tp;
NCONF_HANDLE *retp;
(void) mutex_lock(&netpp_mutex);
if ((netpp == NULL) && ((netpp = getnetlist()) == NULL)) {
(void) mutex_unlock(&netpp_mutex);
return (NULL);
}
(void) mutex_unlock(&netpp_mutex);
if ((retp = malloc(sizeof (NCONF_HANDLE))) == NULL) {
nc_error = NC_NOMEM;
return (NULL);
}
count = 0;
valid_netpath[0] = '\0';
if ((netpath = getenv(NETPATH)) == NULL ||
strlen(netpath) >= sizeof (templine) - 1) {
for (tpp = netpp; *tpp; tpp++) {
if ((*tpp)->nc_flag & NC_VISIBLE) {
(void) strcat(valid_netpath, (*tpp)->nc_netid);
(void) strcat(valid_netpath, ":");
count++;
}
}
} else {
(void) strcpy(templine, netpath);
tp = templine;
while (*tp) {
while (*tp && *tp == ':')
tp++;
if (*tp == '\0')
break;
netid = tp;
while (*tp && *tp != ':')
tp++;
if (*tp)
*tp++ = '\0';
for (tpp = netpp; *tpp; tpp++) {
if (strcmp(netid, (*tpp)->nc_netid) == 0) {
(void) strcat(valid_netpath,
(*tpp)->nc_netid);
(void) strcat(valid_netpath, ":");
count++;
break;
}
}
}
}
if ((rnetpp = malloc((count + 1) *
sizeof (struct netconfig *))) == NULL) {
free(retp);
nc_error = NC_NOMEM;
return (NULL);
}
curr_pp = rnetpp;
netid = tp = valid_netpath;
while (*tp) {
netid = tp;
while (*tp && *tp != ':')
tp++;
if (*tp)
*tp++ = '\0';
for (tpp = netpp; *tpp; tpp++) {
if (strcmp(netid, (*tpp)->nc_netid) == 0) {
*curr_pp++ = *tpp;
break;
}
}
}
*curr_pp = NULL;
retp->nc_curr = retp->nc_head = rnetpp;
return ((void *)retp);
}
int
endnetpath(void *vdata)
{
NCONF_HANDLE *nconf_handlep = (NCONF_HANDLE *)vdata;
(void) mutex_lock(&netpp_mutex);
if (netpp == NULL || nconf_handlep == NULL) {
nc_error = NC_NOSET;
(void) mutex_unlock(&netpp_mutex);
return (-1);
}
(void) mutex_unlock(&netpp_mutex);
free(nconf_handlep->nc_head);
free(nconf_handlep);
return (0);
}
struct netconfig *
getnetpath(void *vdata)
{
NCONF_HANDLE *nconf_handlep = (NCONF_HANDLE *)vdata;
struct netconfig *retp;
int ipv6_present = -1;
(void) mutex_lock(&netpp_mutex);
if (netpp == NULL) {
nc_error = NC_NOSET;
(void) mutex_unlock(&netpp_mutex);
return (NULL);
}
(void) mutex_unlock(&netpp_mutex);
for (;;) {
retp = *(nconf_handlep->nc_curr);
if (retp && (strcmp(retp->nc_netid, "udp6") == 0 ||
strcmp(retp->nc_netid, "tcp6") == 0)) {
if (ipv6_present == -1)
ipv6_present = __can_use_af(AF_INET6);
if (!ipv6_present) {
++(nconf_handlep->nc_curr);
continue;
}
}
break;
}
if (retp) {
++(nconf_handlep->nc_curr);
nc_error = NC_NOERROR;
} else {
nc_error = NC_NOMOREENTRIES;
}
return (retp);
}
static int
blank(char *cp)
{
while (*cp && isspace(*cp)) {
cp++;
}
return (*cp == '\0');
}
static int
comment(char *cp)
{
while (*cp && isspace(*cp)) {
cp++;
}
return (*cp == '#');
}
static unsigned int
getvalue(char *cp, struct nc_data nc_data[])
{
int i;
for (i = 0; nc_data[i].string; i++) {
if (strcmp(nc_data[i].string, cp) == 0) {
break;
}
}
return (nc_data[i].value);
}
static unsigned int
getflag(char *cp)
{
int i;
unsigned int mask = 0;
while (*cp) {
for (i = 0; nc_flag[i].string; i++) {
if (*nc_flag[i].string == *cp) {
mask |= nc_flag[i].value;
break;
}
}
cp++;
}
return (mask);
}
static char **
getlookups(char *cp)
{
unsigned int num;
char **listpp;
char **tpp;
char *start;
num = getnlookups(cp);
if (num == 0)
return (NULL);
if ((listpp = malloc((num + 1) * sizeof (char *))) == NULL)
return (NULL);
tpp = listpp;
while (num--) {
start = cp;
while (*cp && *cp != ',') {
if (*cp == '\\' && *(cp + 1)) {
shift1left(cp);
}
cp++;
}
if (*cp)
*cp++ = '\0';
if ((*tpp++ = strdup(start)) == NULL) {
for (tpp = listpp; *tpp; tpp++)
free(*tpp);
free(listpp);
return (NULL);
}
}
*tpp = NULL;
return (listpp);
}
static unsigned int
getnlookups(char *cp)
{
unsigned int count;
if (strcmp(cp, "-") == 0)
return (0);
count = 1;
while (*cp) {
if (*cp == ',') {
count++;
}
if (*cp == '\\' && *(cp + 1)) {
cp++;
}
cp++;
}
return (count);
}
static char *
gettoken(char *cp, int skip)
{
static char *savep;
char *p;
char *retp;
fieldnum++;
p = (cp == NULL)? savep: cp;
if (p == 0)
return (NULL);
while (isspace(*p))
p++;
if (*p == '\0')
return (NULL);
retp = p;
while (*p) {
if (isspace(*p))
if (skip == TRUE) {
shift1left(p);
continue;
} else
break;
if (*p == '\\' && *(p + 1) != '\n' && isspace(*(p + 1))) {
shift1left(p);
}
p++;
}
if (*p == '\0') {
savep = 0;
} else {
*p = '\0';
savep = ++p;
}
return (strdup(retp));
}
static void
shift1left(char *p)
{
for (; *p; p++)
*p = *(p + 1);
}
char *
nc_sperror(void)
{
static char buf_main[BUFSIZ];
static pthread_key_t perror_key = PTHREAD_ONCE_KEY_NP;
char *retstr = thr_main()?
buf_main :
thr_get_storage(&perror_key, BUFSIZ, free);
if (retstr == NULL) {
syslog(LOG_WARNING,
"nc_sperror: malloc failed when trying to create buffer\n");
return (NULL);
}
switch (nc_error) {
case NC_NOERROR:
(void) strlcpy(retstr, dgettext(__nsl_dom, "no error"), BUFSIZ);
break;
case NC_NOMEM:
(void) strlcpy(retstr, dgettext(__nsl_dom, "out of memory"),
BUFSIZ);
break;
case NC_NOSET:
(void) strlcpy(retstr, dgettext(__nsl_dom,
"routine called before calling \
setnetpath() or setnetconfig()"), BUFSIZ);
break;
case NC_OPENFAIL:
(void) strlcpy(retstr,
dgettext(__nsl_dom, "cannot open /etc/netconfig"),
BUFSIZ);
break;
case NC_BADLINE:
(void) snprintf(retstr, BUFSIZ, dgettext(__nsl_dom,
"error in /etc/netconfig: field %d of line %d\n"),
fieldnum, linenum);
break;
case NC_NOTFOUND:
(void) snprintf(retstr, BUFSIZ,
dgettext(__nsl_dom,
"netid not found in /etc/netconfig"));
break;
case NC_NOMOREENTRIES:
(void) snprintf(retstr, BUFSIZ,
dgettext(__nsl_dom,
"no more entries in /etc/netconfig"));
break;
default:
(void) strlcpy(retstr, dgettext(__nsl_dom, "unknown error"),
BUFSIZ);
break;
}
return (retstr);
}
void
nc_perror(const char *string)
{
if (string)
(void) fprintf(stderr, "%s: %s\n", string, nc_sperror());
else
(void) fprintf(stderr, "%s\n", nc_sperror());
}
static void
netlist_free(struct netconfig ***netppp)
{
struct netconfig **tpp;
for (tpp = *netppp; *tpp; tpp++) {
netconfig_free(*tpp);
}
free(*netppp);
*netppp = NULL;
}
static void
netconfig_free(struct netconfig *netconfigp)
{
int i;
if (netconfigp == NULL)
return;
free_entry(netconfigp->nc_netid);
free_entry(netconfigp->nc_protofmly);
free_entry(netconfigp->nc_proto);
free_entry(netconfigp->nc_device);
if (netconfigp->nc_lookups)
for (i = 0; i < netconfigp->nc_nlookups; i++)
free_entry(netconfigp->nc_lookups[i]);
free_entry(netconfigp->nc_lookups);
free(netconfigp);
}
static struct netconfig *
netconfig_dup(struct netconfig *netconfigp)
{
struct netconfig *nconf;
int i;
nconf = calloc(1, sizeof (struct netconfig));
if (nconf == NULL) {
nc_error = NC_NOMEM;
return (NULL);
}
nconf->nc_netid = strdup(netconfigp->nc_netid);
nconf->nc_protofmly = strdup(netconfigp->nc_protofmly);
nconf->nc_proto = strdup(netconfigp->nc_proto);
nconf->nc_device = strdup(netconfigp->nc_device);
nconf->nc_lookups = malloc((netconfigp->nc_nlookups + 1)
* sizeof (char *));
if (!(nconf->nc_lookups && nconf->nc_netid &&
nconf->nc_protofmly && nconf->nc_proto &&
nconf->nc_device)) {
nc_error = NC_NOMEM;
netconfig_free(nconf);
return (NULL);
}
for (i = 0; i < netconfigp->nc_nlookups; i++) {
nconf->nc_lookups[i] = strdup(netconfigp->nc_lookups[i]);
if (nconf->nc_lookups[i] == NULL) {
nconf->nc_nlookups = i;
netconfig_free(nconf);
nc_error = NC_NOMEM;
return (NULL);
}
}
nconf->nc_lookups[i] = NULL;
nconf->nc_nlookups = netconfigp->nc_nlookups;
nconf->nc_flag = netconfigp->nc_flag;
nconf->nc_semantics = netconfigp->nc_semantics;
return (nconf);
}
static void
free_entry(void *foo)
{
if (foo)
free(foo);
}