+/*
+ * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
+ * Reserved. This file contains Original Code and/or Modifications of
+ * Original Code as defined in and that are subject to the Apple Public
+ * Source License Version 1.1 (the "License"). You may not use this file
+ * except in compliance with the License. Please obtain a copy of the
+ * License at http://www.apple.com/publicsource and read it before using
+ * this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+/*
+ * Glues the library routines to the stub routines
+ * Copyright (C) 1989 by NeXT, Inc.
+ */
+#include <libc.h>
+#include <syslog.h>
+#include <netinfo/ni.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/xdr.h>
+#include <net/if.h>
+#include <ctype.h>
+#include "clib.h"
+
+#define NI_TIMEOUT_SHORT 5 /* 5 second timeout for transactions */
+#define NI_TIMEOUT_LONG 60 /* 60 second timeout for writes */
+#define NI_TRIES 5 /* number of retries per timeout (udp only) */
+#define NI_SLEEPTIME 4 /* 4 second sleeptime, in case of errors */
+#define NI_MAXSLEEPTIME 64 /* 64 second max sleep time */
+#define NI_MAXCONNTRIES 2 /* Try to form a connection twice before sleeping */
+
+/* Hack for determining if an IP address is a broadcast address. -GRS */
+/* Note that addr is network byte order (big endian) - BKM */
+
+#define IS_BROADCASTADDR(addr) (((unsigned char *) &addr)[0] == 0xFF)
+
+#ifndef INADDR_LOOPBACK
+#define INADDR_LOOPBACK (u_long)0x7f000001
+#endif
+#define debug(msg) syslog(LOG_ERR, msg)
+
+#define clnt_debug(ni, msg) /* do nothing */
+
+typedef struct ni_private {
+ int naddrs; /* number of addresses */
+ struct in_addr *addrs; /* addresses of servers - network byte order */
+ int whichwrite; /* which one of the above is the master */
+ ni_name *tags; /* tags of servers */
+ int pid; /* pid, to detect forks */
+ int tsock; /* tcp socket */
+ int tport; /* tcp local port name - host byte order */
+ CLIENT *tc; /* tcp client */
+ long tv_sec; /* timeout for this call */
+ long rtv_sec; /* read timeout - 0 if default */
+ long wtv_sec; /* write timeout - 0 if default */
+ int abort; /* abort on timeout? */
+ int needwrite; /* need to lock writes? */
+ int uid; /* user id */
+ ni_name passwd; /* password */
+} ni_private;
+
+#define NIP(ni) ((ni_private *)(ni))
+
+static const ni_name NAME_NAME = "name";
+static const ni_name NAME_SERVES = "serves";
+static const ni_name NAME_MACHINES = "machines";
+static const ni_name NAME_IP_ADDRESS = "ip_address";
+static const ni_name NAME_MASTER = "master";
+static const ni_name NAME_USERS = "users";
+static const ni_name NAME_UID = "uid";
+
+typedef struct getreg_stuff {
+ nibind_getregister_res res;
+ ni_private *ni;
+} getreg_stuff;
+
+
+static int socket_open(struct sockaddr_in *raddr, int, int, int, int, int);
+
+
+/*
+ * Keep track of our port, in case somebody closes our socket
+ * on us.
+ */
+static int
+getmyport(
+ int sock
+ )
+{
+ struct sockaddr_in sin;
+ int sinlen;
+
+ sinlen = sizeof(sin);
+ if (getsockname(sock, (struct sockaddr *)&sin, &sinlen) == 0) {
+ if (sin.sin_port == 0) {
+ (void)bind(sock, (struct sockaddr *)&sin, sizeof(sin));
+ sinlen = sizeof(sin);
+ (void)getsockname(sock, (struct sockaddr *)&sin,
+ &sinlen);
+ }
+ return (ntohs(sin.sin_port));
+ }
+ return (-1);
+}
+
+
+/*
+ * Is the NetInfo binder running?
+ */
+static int
+nibind_up(
+ ni_private *ni
+ )
+{
+ int sock;
+ struct sockaddr_in sin;
+ int res;
+
+ sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sock < 0) {
+ return (0);
+ }
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(PMAPPORT);
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ bzero(sin.sin_zero, sizeof(sin.sin_zero));
+ res = connect(sock, (struct sockaddr *)&sin, sizeof(sin));
+ close(sock);
+ if (res != 0) {
+ return (0);
+ }
+ sin.sin_port = htons(pmap_getport(&sin, NIBIND_PROG, NIBIND_VERS,
+ IPPROTO_TCP));
+ if (sin.sin_port == 0) {
+ return (0);
+ }
+ sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sock < 0) {
+ return (0);
+ }
+ res = connect(sock, (struct sockaddr *)&sin, sizeof(sin));
+ close(sock);
+ return (res == 0);
+}
+
+
+static void
+createauth(
+ ni_private *ni
+ )
+{
+ if (ni->passwd != NULL && ni->tc != NULL) {
+ auth_destroy(ni->tc->cl_auth);
+ ni->tc->cl_auth = authunix_create(ni->passwd, ni->uid, 0, 0,
+ NULL);
+ }
+}
+
+
+static void
+fixtimeout(
+ struct timeval *tv,
+ long sec,
+ int tries
+ )
+{
+ tv->tv_sec = sec / tries;
+ tv->tv_usec = ((sec % tries) * 1000000) / tries;
+}
+
+
+static void
+ni_settimeout(
+ ni_private *ni,
+ int timeout
+ )
+{
+ struct timeval tv;
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ ni->tv_sec = timeout;
+ if (ni->tc != NULL) {
+ clnt_control(ni->tc, CLSET_TIMEOUT, &tv);
+ }
+}
+
+
+/*
+ * Connect to a given address/tag
+ */
+static int
+connectit(
+ ni_private *ni
+ )
+{
+ struct sockaddr_in sin;
+ int sock;
+ CLIENT *cl;
+ struct timeval tv;
+ enum clnt_stat stat;
+ nibind_getregister_res res;
+
+ bzero(&sin, sizeof(sin));
+ sin.sin_port = 0;
+ sin.sin_family = AF_INET;
+ sin.sin_addr = ni->addrs[0];
+ ni_settimeout(ni, ni->rtv_sec == 0 ? NI_TIMEOUT_SHORT : ni->rtv_sec);
+ fixtimeout(&tv, ni->tv_sec, NI_TRIES);
+ sock = socket_open(&sin, NIBIND_PROG, NIBIND_VERS, ni->tv_sec,
+ NI_TRIES, IPPROTO_UDP);
+ if (sock < 0) {
+ return (0);
+ }
+ cl = clntudp_create(&sin, NIBIND_PROG, NIBIND_VERS, tv,
+ &sock);
+ if (cl == NULL) {
+ close(sock);
+ return (0);
+ }
+ tv.tv_sec = ni->rtv_sec == 0 ? NI_TIMEOUT_SHORT : ni->rtv_sec;
+ tv.tv_usec = 0;
+ stat = clnt_call(cl, NIBIND_GETREGISTER, xdr_ni_name, &ni->tags[0],
+ xdr_nibind_getregister_res, &res, tv);
+ clnt_destroy(cl);
+ close(sock);
+ if (stat != RPC_SUCCESS || res.status != NI_OK) {
+ return (0);
+ }
+
+ /*
+ * Found the address, now connect to it.
+ */
+ sin.sin_port = htons(res.nibind_getregister_res_u.addrs.tcp_port);
+ sock = socket_open(&sin, NI_PROG, NI_VERS, ni->tv_sec, NI_TRIES,
+ IPPROTO_TCP);
+ if (sock < 0) {
+ return (0);
+ }
+ cl = clnttcp_create(&sin, NI_PROG, NI_VERS, &sock, 0, 0);
+ if (cl == NULL) {
+ close(sock);
+ return (0);
+ }
+ clnt_control(cl, CLSET_TIMEOUT, &tv);
+ ni->tc = cl;
+ ni->tsock = sock;
+ ni->tport = getmyport(sock);
+ createauth(ni);
+ (void) fcntl(ni->tsock, F_SETFD, 1);
+ return (1);
+}
+
+
+void
+ni_setabort(
+ void *ni,
+ int abort
+ )
+{
+ ((ni_private *)ni)->abort = abort;
+}
+
+
+void
+ni_setwritetimeout(
+ void *ni,
+ int timeout
+ )
+{
+ ((ni_private *)ni)->wtv_sec = timeout;
+}
+
+
+void
+ni_setreadtimeout(
+ void *ni,
+ int timeout
+ )
+{
+ ((ni_private *)ni)->rtv_sec = timeout;
+}
+
+
+void
+ni_needwrite(
+ void *ni,
+ int needwrite
+ )
+{
+ ((ni_private *)ni)->needwrite = needwrite;
+}
+
+
+static unsigned long
+sys_netmask(void)
+{
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ char buf[1024]; /* XXX */
+ int i, len, ifreq_size, offset, sockaddr_size, size;
+ int sock;
+ struct sockaddr_in *sin;
+ unsigned long n_addr;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (sock < 0) return (htonl(IN_CLASSA_NET));
+
+ ifc.ifc_len = sizeof(buf);
+ ifc.ifc_buf = buf;
+
+ if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0)
+ {
+ close(sock);
+ return (htonl(IN_CLASSA_NET));
+ }
+
+ ifreq_size = sizeof(struct ifreq);
+ sockaddr_size = sizeof(struct sockaddr);
+
+ offset = 0;
+ len = ifc.ifc_len / ifreq_size;
+ for (i = 0; i < len; i++)
+ {
+ ifr = (struct ifreq *)(ifc.ifc_buf + offset);
+ offset += IFNAMSIZ;
+ offset += sockaddr_size;
+
+ size = ifr->ifr_addr.sa_len;
+ if (size > sockaddr_size) offset += (size - sockaddr_size);
+
+ if (ifr->ifr_addr.sa_family != AF_INET) continue;
+ if (ioctl(sock, SIOCGIFFLAGS, (char *)ifr) < 0) continue;
+
+ sin = (struct sockaddr_in *)&ifr->ifr_addr;
+ if ((ifr->ifr_flags & IFF_UP) &&
+ !(ifr->ifr_flags & IFF_LOOPBACK) &&
+ (sin->sin_addr.s_addr != 0))
+ {
+ ioctl(sock, SIOCGIFNETMASK, (char *)ifr);
+ n_addr = ((struct sockaddr_in *)&(ifr->ifr_addr))->sin_addr.s_addr;
+ close(sock);
+ return (n_addr);
+ }
+ }
+
+ close(sock);
+ return (htonl(IN_CLASSA_NET));
+}
+
+
+static unsigned long
+sys_address(void)
+{
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ char buf[1024]; /* XXX */
+ int i, len, ifreq_size, offset, sockaddr_size, size;
+ int sock;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (sock < 0)
+ {
+ return (htonl(INADDR_LOOPBACK));
+ }
+
+ ifc.ifc_len = sizeof(buf);
+ ifc.ifc_buf = buf;
+
+ if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0)
+ {
+ close(sock);
+ return (htonl(INADDR_LOOPBACK));
+ }
+
+ ifreq_size = sizeof(struct ifreq);
+ sockaddr_size = sizeof(struct sockaddr);
+
+ offset = 0;
+ len = ifc.ifc_len / ifreq_size;
+ for (i = 0; i < len; i++)
+ {
+ ifr = (struct ifreq *)(ifc.ifc_buf + offset);
+ offset += IFNAMSIZ;
+ offset += sockaddr_size;
+
+ size = ifr->ifr_addr.sa_len;
+ if (size > sockaddr_size) offset += (size - sockaddr_size);
+
+ if (ifr->ifr_addr.sa_family != AF_INET) continue;
+ if (ioctl(sock, SIOCGIFFLAGS, ifr) < 0) continue;
+
+ if ((ifr->ifr_flags & IFF_UP) && (!(ifr->ifr_flags & IFF_LOOPBACK)))
+ {
+ close(sock);
+ return (((struct sockaddr_in *)&(ifr->ifr_addr))->sin_addr.s_addr);
+ }
+ }
+
+ close(sock);
+ return (htonl(INADDR_LOOPBACK));
+}
+
+
+/*
+ * Returns a client handle to the NetInfo server, if it's running
+ */
+static int
+connectlocal(ni_private *ni)
+{
+ int printed = 0;
+
+ if (!nibind_up(ni)) {
+ return (0);
+ }
+ ni->naddrs = 1;
+ ni->addrs = (struct in_addr *)malloc(sizeof(struct in_addr));
+ ni->addrs[0].s_addr = htonl(INADDR_LOOPBACK);
+ ni->tags = (ni_name *)malloc(sizeof(ni_name));
+ ni->tags[0] = ni_name_dup("local");
+ ni->whichwrite = 0;
+ while (!connectit(ni)) {
+ if (!printed) {
+ syslog(LOG_ERR, "NetInfo timeout connecting to local domain, sleeping");
+ printed++;
+ }
+ sleep(NI_SLEEPTIME);
+ /* wait forever */
+ }
+ if (printed) {
+ syslog(LOG_ERR, "NetInfo connection to local domain waking");
+ }
+ return (1);
+}
+
+
+/*
+ * Destroy the client handle
+ */
+static void
+clnt_kill(
+ CLIENT *cl,
+ int sock,
+ int port
+ )
+{
+ int save = 0;
+
+ if (sock >= 0 && getmyport(sock) != port) {
+ /*
+ * Somebody else has the descriptor open. Do not close it,
+ * it's not ours.
+ */
+ save++;
+ }
+ if (cl != NULL) {
+ auth_destroy(cl->cl_auth);
+ clnt_destroy(cl);
+ }
+ if (!save) {
+ /*
+ * It's ours and we can close it
+ */
+ (void)close(sock);
+ }
+}
+
+
+/*
+ * Reinitialize everything
+ */
+static void
+reinit(
+ ni_private *ni
+ )
+{
+ if (ni->tc != NULL) {
+ clnt_kill(ni->tc, ni->tsock, ni->tport);
+ ni->tc = NULL;
+ }
+ ni->tsock = -1;
+ ni->tport = -1;
+ ni->pid = getpid();
+}
+
+
+/*
+ * Switch to a new server
+ */
+static void
+ni_switch(
+ ni_private *ni,
+ ni_index which
+ )
+{
+ struct in_addr tmp_addr;
+ ni_name tmp_tag;
+
+ if (which == 0) {
+ return;
+ }
+ reinit(ni);
+ tmp_addr = ni->addrs[0];
+ tmp_tag = ni->tags[0];
+
+ ni->addrs[0] = ni->addrs[which];
+ ni->tags[0] = ni->tags[which];
+
+ ni->addrs[which] = tmp_addr;
+ ni->tags[which] = tmp_tag;
+
+ if (ni->whichwrite == 0) {
+ ni->whichwrite = which;
+ }
+ else if (ni->whichwrite == which) {
+ ni->whichwrite = 0;
+ }
+}
+
+
+/*
+ * Swap two servers' positions
+ */
+static void
+ni_swap(
+ ni_private *ni,
+ ni_index a,
+ ni_index b
+ )
+{
+ struct in_addr tmp_addr;
+ ni_name tmp_tag;
+
+ tmp_addr = ni->addrs[a];
+ tmp_tag = ni->tags[a];
+
+ ni->addrs[a] = ni->addrs[b];
+ ni->tags[a] = ni->tags[b];
+
+ ni->addrs[b] = tmp_addr;
+ ni->tags[b] = tmp_tag;
+
+ if (ni->whichwrite == a) {
+ ni->whichwrite = b;
+ }
+ else if (ni->whichwrite == b) {
+ ni->whichwrite = a;
+ }
+}
+
+
+/*
+ * Callback routine for multi_call
+ * XXX: should save returned port numbers
+ */
+static bool_t
+eachresult(
+ void *vstuff,
+ struct sockaddr_in *sin,
+ int which
+ )
+{
+ getreg_stuff *stuff = (getreg_stuff *)vstuff;
+
+ if (stuff->res.status != NI_OK) {
+ return (FALSE);
+ }
+ ni_switch(stuff->ni, which);
+ return (TRUE);
+}
+
+
+static int
+rebind(
+ ni_private *ni
+ )
+{
+ enum clnt_stat stat;
+ getreg_stuff stuff;
+ int sleeptime = NI_SLEEPTIME;
+ int printed = 0;
+ int nlocal;
+ int nnetwork;
+ unsigned long myaddr;
+ unsigned long mynetmask;
+ unsigned long mynetwork;
+ int i;
+
+ if (ni->naddrs == 1) {
+ ni->whichwrite = 0;
+ return (1);
+ }
+
+ /*
+ * Majka - 1994.04.27
+ * re-order the servers so that:
+ * servers on the local host are at the start of the list, then
+ * servers on the local network are next, then
+ * all other servers are next
+ */
+
+ myaddr = sys_address();
+ mynetmask = sys_netmask();
+ mynetwork = myaddr & mynetmask;
+
+ /*
+ * move local servers to the head of the list
+ */
+ nlocal = 0;
+ for (i = nlocal; i < ni->naddrs; i++) {
+ if ((ni->addrs[i].s_addr == myaddr) ||
+ (ni->addrs[i].s_addr == htonl(INADDR_LOOPBACK)))
+ {
+ ni_swap(ni, nlocal, i);
+ nlocal++;
+ }
+ }
+
+ /*
+ * move servers on this network to follow local servers
+ */
+ nnetwork = nlocal;
+ for (i = nnetwork; i < ni->naddrs; i++) {
+ if (((ni->addrs[i].s_addr & mynetmask) == mynetwork) ||
+ IS_BROADCASTADDR(ni->addrs[i].s_addr))
+ {
+ ni_swap(ni, nnetwork, i);
+ nnetwork++;
+ }
+ }
+
+ stuff.ni = ni;
+ for (;;) {
+ /*
+ * call local servers first
+ */
+ if (nlocal > 0) {
+ for (i = 0; i < nlocal; i++) {
+ syslog(LOG_DEBUG, "NetInfo connect call to: %s/%s (local %d)",
+ inet_ntoa(ni->addrs[i]), ni->tags[i], i);
+ }
+ stat = multi_call(nlocal, ni->addrs,
+ NIBIND_PROG, NIBIND_VERS, NIBIND_GETREGISTER,
+ xdr_ni_name, ni->tags,
+ sizeof(ni_name),
+ xdr_nibind_getregister_res,
+ &stuff, eachresult,
+ NI_TIMEOUT_SHORT);
+ if (stat == RPC_SUCCESS) {
+ break;
+ }
+ }
+
+ /*
+ * call local servers and this network's servers
+ */
+ if (nnetwork > nlocal) {
+ for (i = 0; i < nnetwork; i++) {
+ syslog(LOG_DEBUG, "NetInfo connect call to: %s/%s (network %d)",
+ inet_ntoa(ni->addrs[i]), ni->tags[i], i);
+ }
+ stat = multi_call(nnetwork, ni->addrs,
+ NIBIND_PROG, NIBIND_VERS, NIBIND_GETREGISTER,
+ xdr_ni_name, ni->tags,
+ sizeof(ni_name),
+ xdr_nibind_getregister_res,
+ &stuff, eachresult,
+ NI_TIMEOUT_SHORT);
+ if (stat == RPC_SUCCESS) {
+ break;
+ }
+ }
+
+ /*
+ * call all servers
+ */
+ for (i = 0; i < ni->naddrs; i++) {
+ syslog(LOG_DEBUG, "NetInfo connect call to: %s/%s (world %d)",
+ inet_ntoa(ni->addrs[i]), ni->tags[i], i);
+ }
+ stat = multi_call(ni->naddrs,
+ ni->addrs, NIBIND_PROG, NIBIND_VERS,
+ NIBIND_GETREGISTER,
+ xdr_ni_name, ni->tags,
+ sizeof(ni_name),
+ xdr_nibind_getregister_res,
+ &stuff, eachresult,
+ ni->rtv_sec == 0 ? NI_TIMEOUT_SHORT : ni->rtv_sec);
+ if (stat == RPC_SUCCESS) {
+ break;
+ }
+
+ if (ni->abort) {
+ return (0);
+ }
+ if (!printed) {
+ if (ni->whichwrite >= 0) {
+ syslog(LOG_ERR,
+ "NetInfo connect timeout (domain with master %s/%s), sleeping",
+ inet_ntoa(ni->addrs[ni->whichwrite]), ni->tags[ni->whichwrite]);
+ }
+ else {
+ syslog(LOG_ERR, "NetInfo connect timeout (domain with server %s/%s), sleeping",
+ inet_ntoa(ni->addrs[0]), ni->tags[0]);
+ }
+ printed++;
+ }
+ sleep(sleeptime);
+ if (sleeptime < NI_MAXSLEEPTIME) {
+ sleeptime *= 2; /* backoff */
+ }
+ }
+
+ syslog(LOG_INFO, "NetInfo connected to %s/%s", inet_ntoa(ni->addrs[0]), ni->tags[0]);
+
+ if (printed) {
+ syslog(LOG_ERR, "NetInfo connected to %s/%s", inet_ntoa(ni->addrs[0]), ni->tags[0]);
+ }
+ return (1);
+}
+
+
+/*
+ * Confirm that our tcp socket is still valid
+ */
+static int
+confirm_tcp(
+ ni_private *ni,
+ int needwrite
+ )
+{
+ if (ni->tsock != -1) {
+ if (getmyport(ni->tsock) == ni->tport) {
+ return (1);
+ }
+ /*
+ * Somebody closed our socket. Do not close it, it could
+ * be owned by somebody else now.
+ */
+ auth_destroy(ni->tc->cl_auth);
+ clnt_destroy(ni->tc);
+ ni->tc = NULL;
+ }
+ if (!needwrite && !rebind(ni) && ni->abort) {
+ return (0);
+ }
+ return (connectit(ni));
+}
+
+
+static int
+setmaster(
+ ni_private *ni
+ )
+{
+ ni_id root;
+ ni_namelist nl;
+ ni_name sep;
+ ni_idlist idl;
+ ni_name master;
+ ni_index i;
+ ni_index j;
+ ni_id id;
+ struct in_addr addr;
+ int needwrite;
+
+ if (ni->naddrs == 1) {
+ /*
+ * One server - must be the master
+ */
+ ni->whichwrite = 0;
+ return (1);
+ }
+ needwrite = ni->needwrite;
+ ni->needwrite = 0;
+ if (ni_root(ni, &root) != NI_OK) {
+ ni->needwrite = needwrite;
+ return (0);
+ }
+ if (ni_lookupprop(ni, &root, NAME_MASTER, &nl) != NI_OK) {
+ ni->needwrite = needwrite;
+ return (0);
+ }
+ if (nl.ninl_len == 0) {
+ ni->needwrite = needwrite;
+ return (0);
+ }
+ sep = index(nl.ninl_val[0], '/');
+ if (sep == NULL) {
+ ni->needwrite = needwrite;
+ return (0);
+ }
+ *sep++ = 0;
+ master = nl.ninl_val[0];
+ if (ni_lookup(ni, &root, NAME_NAME, NAME_MACHINES, &idl) != NI_OK) {
+ ni->needwrite = needwrite;
+ ni_namelist_free(&nl);
+ return (0);
+ }
+ if (idl.niil_len < 1) {
+ ni->needwrite = needwrite;
+ return (0);
+ }
+ id.nii_object = idl.niil_val[0];
+ ni_idlist_free(&idl);
+ if (ni_lookup(ni, &id, NAME_NAME, master, &idl) != NI_OK) {
+ ni_namelist_free(&nl);
+ ni->needwrite = needwrite;
+ return (0);
+ }
+ ni_namelist_free(&nl);
+ if (idl.niil_len < 1) {
+ ni->needwrite = needwrite;
+ return (0);
+ }
+ id.nii_object = idl.niil_val[0];
+ ni_idlist_free(&idl);
+ if (ni_lookupprop(ni, &id, NAME_IP_ADDRESS, &nl) != NI_OK) {
+ return (0);
+ }
+ for (i = 0; i < nl.ninl_len; i++) {
+ addr.s_addr = inet_addr(nl.ninl_val[i]);
+ for (j = 0; j < ni->naddrs; j++) {
+ if (addr.s_addr == ni->addrs[j].s_addr) {
+ ni->whichwrite = j;
+ ni_namelist_free(&nl);
+ ni->needwrite = needwrite;
+ return (1);
+ }
+ }
+ }
+ ni->needwrite = needwrite;
+ ni_namelist_free(&nl);
+ return (0);
+}
+
+
+static void *
+callit(
+ ni_private *ni,
+ void *(*stub)(),
+ void *args,
+ int needwrite
+ )
+{
+ void *resp;
+ struct rpc_err err;
+ int i;
+ int sleeptime = 0;
+ int printed = 0;
+
+ if (getpid() != ni->pid) {
+ reinit(ni);
+ }
+ if (needwrite || ni->needwrite) {
+ if (ni->whichwrite >= 0) {
+ ni_switch(ni, ni->whichwrite);
+ } else {
+ if (!setmaster(ni)) {
+ return (NULL);
+ }
+ ni_switch(ni, ni->whichwrite);
+ }
+ if (!needwrite) {
+ ni_settimeout(ni, (ni->rtv_sec == 0 ?
+ NI_TIMEOUT_SHORT : ni->rtv_sec));
+ needwrite = 1;
+ } else {
+ ni_settimeout(ni, (ni->wtv_sec == 0 ?
+ NI_TIMEOUT_LONG : ni->wtv_sec));
+ }
+ } else {
+ ni_settimeout(ni, (ni->rtv_sec == 0 ?
+ NI_TIMEOUT_SHORT : ni->rtv_sec));
+ }
+ for (;;) {
+ /*
+ * Try more than once, in case server closed connection.
+ */
+ for (i = 0; i < NI_MAXCONNTRIES; i++) {
+ if (!confirm_tcp(ni, needwrite)) {
+ break;
+ }
+ if ((resp = (*stub)(args, ni->tc)) != NULL) {
+ if (printed) {
+ syslog(LOG_ERR, "NetInfo connected to %s/%s",
+ inet_ntoa(ni->addrs[0]), ni->tags[0]);
+ }
+ return (resp);
+ }
+ clnt_geterr(ni->tc, &err);
+ if (err.re_status != RPC_CANTRECV) {
+ break;
+ }
+ if (i + 1 < NI_MAXCONNTRIES) {
+ /*
+ * Server closed connection. Reinit and try
+ * again.
+ */
+ reinit(ni);
+ }
+ }
+ if (err.re_status == RPC_PROCUNAVAIL) {
+ return (NULL);
+ }
+ if (needwrite || ni->abort) {
+ /*
+ * We time out for writes or if it is explicitly
+ * requested.
+ */
+ if (ni->abort) {
+ reinit(ni);
+ }
+ syslog(LOG_ERR,
+ "NetInfo connection failed for server %s/%s",
+ inet_ntoa(ni->addrs[0]), ni->tags[0]);
+ return (NULL);
+ }
+ if (!printed) {
+ if (ni->tc != NULL) {
+ if (!(sleeptime == 0 &&
+ err.re_status == RPC_TIMEDOUT)) {
+ /*
+ * Do not print message on
+ * first timeout. It is likely
+ * we will find another server soon.
+ * Let's not needlessly alarm the
+ * poor user!
+ */
+ syslog(LOG_ERR, "%s on connection to %s/%s",
+ clnt_sperror(ni->tc,"NetInfo connection timeout"),
+ inet_ntoa(ni->addrs[0]), ni->tags[0]);
+ printed++;
+ }
+ else {
+ /* first attempt failed */
+ syslog(LOG_ERR, "%s on initial connection to %s/%s",
+ clnt_sperror(ni->tc,"NetInfo connection timeout"),
+ inet_ntoa(ni->addrs[0]), ni->tags[0]);
+ }
+ } else {
+ syslog(LOG_ERR,
+ "NetInfo connection failed for server %s/%s",
+ inet_ntoa(ni->addrs[0]), ni->tags[0]);
+ printed++;
+ }
+ }
+ if (sleeptime > 0) {
+ sleep(sleeptime);
+ if (sleeptime < NI_MAXSLEEPTIME) {
+ sleeptime *= 2; /* backoff */
+ }
+ } else {
+ /*
+ * Do not sleep on the first timeout.
+ * It is likely we will find another server quickly.
+ */
+ sleeptime = NI_SLEEPTIME;
+ }
+ reinit(ni);
+ (void)rebind(ni);
+ }
+}
+
+
+#define RCALLIT(a, b, c) callit((ni_private *)(a), (void *(*)())(b), \
+ (void *)c, 0)
+
+
+#define WCALLIT(a, b, c) callit((ni_private *)(a), (void *(*)())(b), \
+ (void *)c, 1)
+
+
+static void
+ni_clear(
+ ni_private *ni
+ )
+{
+ ni->needwrite = 0;
+ ni->naddrs = 0;
+ ni->addrs = NULL;
+ ni->tags = NULL;
+ ni->tc = NULL;
+ ni->tsock = -1;
+ ni->tport = -1;
+ ni->whichwrite = -1;
+ ni->passwd = NULL;
+}
+
+
+static void *
+ni_alloc(
+ void
+ )
+{
+ ni_private *ni;
+
+ ni = (ni_private *)malloc(sizeof(*ni));
+ ni->naddrs = 0;
+ ni->whichwrite = -1;
+ ni->pid = getpid();
+ ni->tsock = -1;
+ ni->tport = -1;
+ ni->tc = NULL;
+ ni->tv_sec = NI_TIMEOUT_SHORT;
+ ni->rtv_sec = 0;
+ ni->wtv_sec = 0;
+ ni->abort = 0;
+ ni->passwd = NULL;
+ ni->uid = getuid();
+ ni->needwrite = 0;
+ return ((void *)ni);
+}
+
+
+void *
+_ni_dup(
+ void *ni
+ )
+{
+ ni_private *dupni;
+ ni_index i;
+
+ dupni = (ni_private *)ni_alloc();
+ *dupni = *NIP(ni);
+ ni_clear(dupni);
+ dupni->naddrs = NIP(ni)->naddrs;
+ dupni->whichwrite = NIP(ni)->whichwrite;
+ if (dupni->naddrs > 0) {
+ dupni->addrs = ((struct in_addr *)
+ malloc(NIP(ni)->naddrs * sizeof(struct in_addr)));
+ bcopy(NIP(ni)->addrs, dupni->addrs,
+ NIP(ni)->naddrs * sizeof(struct in_addr));
+ dupni->tags = ((ni_name *)
+ malloc(NIP(ni)->naddrs * sizeof(ni_name)));
+ for (i = 0; i < NIP(ni)->naddrs; i++) {
+ dupni->tags[i] = ni_name_dup(NIP(ni)->tags[i]);
+ }
+ }
+ if (NIP(ni)->passwd != NULL) {
+ dupni->passwd = ni_name_dup(NIP(ni)->passwd);
+ }
+ return ((void *)dupni);
+}
+
+
+static int
+match(
+ ni_name domain,
+ ni_name domtag,
+ ni_name *tag
+ )
+{
+ int len = strlen(domain);
+ ni_name sep;
+
+ sep = index(domtag, '/');
+ if (sep == NULL) {
+ return (0);
+ }
+ if (strncmp(domain, domtag, len) == 0 &&
+ domtag[len] == '/') {
+ *tag = ni_name_dup(sep + 1);
+ return (1);
+ }
+ return (0);
+}
+
+
+static int
+addaddr(
+ void *ni,
+ ni_index ido,
+ ni_name tag,
+ ni_private *target_ni
+ )
+{
+ ni_id id;
+ ni_namelist nl;
+ struct in_addr addr;
+ int i;
+
+ id.nii_object = ido;
+ if (ni_lookupprop(ni, &id, NAME_IP_ADDRESS, &nl) != NI_OK) {
+ return (0);
+ }
+ if (nl.ninl_len == 0) {
+ return(0);
+ }
+
+ if (target_ni->naddrs == 0) {
+ target_ni->addrs =
+ (struct in_addr *)malloc(nl.ninl_len * sizeof(struct in_addr));
+ target_ni->tags =
+ (ni_name *)malloc(nl.ninl_len * sizeof(ni_name));
+ } else {
+ target_ni->addrs =
+ (struct in_addr *)realloc(target_ni->addrs,
+ ((target_ni->naddrs + nl.ninl_len) * sizeof(struct in_addr)));
+ target_ni->tags =
+ (ni_name *)realloc(target_ni->tags,
+ ((target_ni->naddrs + nl.ninl_len) * sizeof(ni_name)));
+ }
+
+ for (i=0; i<nl.ninl_len; i++) {
+ addr.s_addr = inet_addr(nl.ninl_val[i]);
+ target_ni->addrs[target_ni->naddrs] = addr;
+ target_ni->tags[target_ni->naddrs] = ni_name_dup(tag);
+ target_ni->naddrs++;
+ }
+
+ ni_namelist_free(&nl);
+ return (1);
+}
+
+
+static int
+get_daddr(
+ ni_private *ni,
+ ni_name dom,
+ ni_private *target_ni
+ )
+{
+ ni_id id;
+ ni_idlist ids;
+ ni_namelist nl;
+ ni_entrylist entries;
+ ni_index i;
+ ni_index j;
+ ni_name tag;
+
+ if (ni_root(ni, &id) != NI_OK) {
+ return(0);
+ }
+
+ if (ni_lookup(ni, &id, NAME_NAME, NAME_MACHINES, &ids) != NI_OK) {
+ return (0);
+ }
+
+ id.nii_object = ids.niil_val[0];
+ ni_idlist_free(&ids);
+
+ if (ni_list(ni, &id, NAME_SERVES, &entries) != NI_OK) {
+ return (0);
+ }
+
+ for (i = 0; i < entries.niel_len; i++) {
+ if (entries.niel_val[i].names != NULL) {
+ nl = *entries.niel_val[i].names;
+ for (j = 0; j < nl.ninl_len; j++) {
+ if (match(dom, nl.ninl_val[j], &tag)) {
+ if (addaddr(ni,
+ entries.niel_val[i].id,
+ tag,
+ target_ni)) {
+ ni_name_free(&tag);
+ break;
+ }
+ ni_name_free(&tag);
+ }
+ }
+ }
+
+ }
+ ni_entrylist_free(&entries);
+ return (target_ni->naddrs > 0);
+}
+
+
+#ifdef notdef
+static int
+get_haddr(
+ ni_private *ni,
+ ni_name hname,
+ ni_name tag,
+ ni_private *target_ni
+ )
+{
+ ni_id id;
+ ni_idlist ids;
+
+ if (ni_root(ni, &id) != NI_OK) {
+ return(0);
+ }
+ if (ni_lookup(ni, &id, NAME_NAME, NAME_MACHINES, &ids) != NI_OK) {
+ return (0);
+ }
+ id.nii_object = ids.niil_val[0];
+ ni_idlist_free(&ids);
+
+ if (ni_lookup(ni, &id, NAME_NAME, hname, &ids) != NI_OK) {
+ return (0);
+ }
+ id.nii_object = ids.niil_val[0];
+ ni_idlist_free(&ids);
+ if (!addaddr(ni, id.nii_object, tag, target_ni)) {
+ return (0);
+ }
+ return (1);
+}
+#endif
+
+
+static ni_status
+getparent(ni_private *oldni, ni_private **newni)
+{
+ ni_rparent_res *resp;
+ ni_private *ni;
+ ni_private *dupni;
+ int found;
+ ni_index i;
+ struct in_addr raddr;
+ int printed = 0;
+ int inlist = 0;
+
+ found = 0;
+ while (!found) {
+ /*
+ * First, find our parent, any parent
+ */
+ for (;;) {
+ resp = RCALLIT(oldni, _ni_rparent_2, NULL);
+ if (resp == NULL) {
+ return (NI_FAILED);
+ }
+ if (resp->status != NI_NORESPONSE) {
+ break;
+ }
+ if (!printed) {
+ syslog(LOG_ERR, "NetInfo timeout finding server for parent of %s/%s, sleeping",
+ inet_ntoa(oldni->addrs[0]), oldni->tags[0]);
+ printed++;
+ }
+ sleep(NI_SLEEPTIME);
+ }
+ if (printed) {
+ raddr.s_addr = htonl(resp->ni_rparent_res_u.binding.addr);
+
+ syslog(LOG_ERR, "NetInfo %s/%s found parent %s/%s",
+ inet_ntoa(oldni->addrs[0]), oldni->tags[0],
+ inet_ntoa(raddr), resp->ni_rparent_res_u.binding.tag);
+ }
+ if (resp->status != NI_OK) {
+ return (resp->status);
+ }
+ ni = ni_alloc();
+ *ni = *oldni;
+ ni_clear(ni);
+ ni->naddrs = 1;
+ ni->addrs = (struct in_addr *)malloc(sizeof(struct in_addr));
+ ni->addrs[0].s_addr=htonl(resp->ni_rparent_res_u.binding.addr);
+ ni->tags = (ni_name *)malloc(sizeof(ni_name));
+ ni->tags[0] = ni_name_dup(resp->ni_rparent_res_u.binding.tag);
+
+ xdr_free(xdr_ni_rparent_res, resp);
+
+ dupni = ni;
+ ni = ni_alloc();
+ *ni = *dupni;
+ ni_clear(ni);
+ if (get_daddr(dupni, ".", ni)) {
+
+ /*
+ * Now make sure returned parent is head of
+ * list
+ */
+ for (i = 0; i < ni->naddrs; i++) {
+ if (ni->addrs[i].s_addr ==
+ dupni->addrs[0].s_addr) {
+ ni_switch(ni, i);
+ inlist++;
+ break;
+ }
+ }
+
+ /*
+ * Reuse dupni client info
+ */
+ ni->tsock = dupni->tsock;
+ ni->tport = dupni->tport;
+ ni->tc = dupni->tc;
+ dupni->tsock = -1;
+ dupni->tport = -1;
+ dupni->tc = NULL;
+ found++;
+
+ /*
+ * If returned parent wasn't in list, it's a rogue.
+ * Log an error and drop the connection.
+ */
+ if (inlist == 0) {
+ syslog(LOG_ERR, "Rogue NetInfo server detected: %s/%s",
+ inet_ntoa(dupni->addrs[0]), dupni->tags[0]);
+ reinit(ni);
+ }
+
+ }
+ ni_free(dupni);
+ }
+ if (found) {
+ *newni = ni;
+ return (NI_OK);
+ } else {
+ ni_free(ni);
+ return (NI_FAILED);
+ }
+}
+
+
+void *
+ni_connect(
+ struct sockaddr_in *sin,
+ const char *tag
+ )
+{
+ void *ni;
+
+ ni = ni_alloc();
+ NIP(ni)->naddrs = 1;
+ NIP(ni)->addrs = (struct in_addr *
+ )malloc(sizeof(struct in_addr));
+ NIP(ni)->addrs[0] = sin->sin_addr;
+ NIP(ni)->tags = (ni_name *)malloc(sizeof(ni_name));
+ NIP(ni)->tags[0] = ni_name_dup(tag);
+ return (ni);
+}
+
+
+ni_status
+ni_addrtag(
+ void *ni,
+ struct sockaddr_in *addr,
+ ni_name *tag
+ )
+{
+
+ if (!confirm_tcp(ni, 0)) {
+ return (NI_FAILED);
+ }
+ *tag = ni_name_dup(NIP(ni)->tags[0]);
+ addr->sin_addr = NIP(ni)->addrs[0];
+ addr->sin_port = htons(NIP(ni)->tport);
+ addr->sin_family = AF_INET;
+ bzero(addr->sin_zero, sizeof(addr->sin_zero));
+ return (NI_OK);
+}
+
+
+void *
+ni_new(
+ void *oldni,
+ const char *domain
+ )
+{
+ ni_private *ni;
+ ni_status status;
+ ni_name sep, addr, tag;
+ struct sockaddr_in sin;
+ struct hostent *he;
+
+ if (oldni == NULL) {
+ ni = ni_alloc();
+ if (!connectlocal(ni)) {
+ free(ni);
+ return (NULL);
+ }
+ if (strcmp(domain, "..") == 0) {
+ oldni = ni;
+ status = getparent((ni_private *)oldni, &ni);
+ ni_free(oldni);
+ if (status != NI_OK) {
+ return (NULL);
+ }
+ } else if ((sep = index(domain, '@')) != NULL) {
+ free(ni);
+ tag = strncpy((char *)malloc(sep-domain+1), domain, sep-domain);
+ tag[sep-domain] = '\0';
+ addr = strcpy ((char *)malloc(strlen(sep+1)), sep+1);
+ sin.sin_addr.s_addr = inet_addr(addr);
+ if (sin.sin_addr.s_addr == INADDR_NONE) {
+ he = gethostbyname(addr);
+ if (he == NULL) {
+ free(addr);
+ free(tag);
+ return (NULL);
+ }
+ bcopy(he->h_addr_list[0], &sin.sin_addr.s_addr, he->h_length);
+ }
+ ni = ni_connect(&sin, tag);
+ free(addr);
+ free(tag);
+ } else if (strcmp(domain, ".") != 0) {
+ /*
+ * nothing else makes sense
+ */
+ free(ni);
+ return (NULL);
+ }
+ } else {
+ if (strcmp(domain, "..") == 0) {
+ status = getparent((ni_private *)oldni, &ni);
+ if (status != NI_OK) {
+ return (NULL);
+ }
+ } else if ((sep = index(domain, '@')) != NULL) {
+ tag = strncpy((char *)malloc(sep-domain+1), domain, sep-domain);
+ tag[sep-domain] = '\0';
+ addr = strcpy ((char *)malloc(strlen(sep+1)), sep+1);
+ sin.sin_addr.s_addr = inet_addr(addr);
+ if (sin.sin_addr.s_addr == INADDR_NONE) {
+ he = gethostbyname(addr);
+ if (he == NULL) {
+ free(addr);
+ free(tag);
+ return (NULL);
+ }
+ bcopy(he->h_addr_list[0], &sin.sin_addr.s_addr, he->h_length);
+ }
+ ni = ni_connect(&sin, tag);
+ free(addr);
+ free(tag);
+ } else {
+ ni = ni_alloc();
+ *ni = *NIP(oldni);
+ ni_clear(ni);
+ if (!get_daddr(oldni, (ni_name)domain, ni)) {
+ ni_free(ni);
+ ni = NULL;
+ }
+ }
+ }
+ return ((void *)ni);
+}
+
+
+void
+ni_free(
+ void *ni
+ )
+{
+ ni_index i;
+
+ if (NIP(ni)->tc != NULL) {
+ clnt_kill(NIP(ni)->tc, NIP(ni)->tsock, NIP(ni)->tport);
+ }
+ if (NIP(ni)->naddrs > 0) {
+ free(NIP(ni)->addrs);
+ for (i = 0; i < NIP(ni)->naddrs; i++) {
+ ni_name_free(&NIP(ni)->tags[i]);
+ }
+ free(NIP(ni)->tags);
+ }
+ if (NIP(ni)->passwd != NULL) {
+ ni_name_free(&NIP(ni)->passwd);
+ }
+ free(ni);
+}
+
+
+/*
+ * The rest of these are just wrappers that end up doing
+ * RPC calls to the local NetInfo server.
+ */
+ni_status
+ni_statistics(
+ void *ni,
+ ni_proplist *pl
+ )
+{
+ ni_proplist *resp;
+
+ if ((resp = (ni_proplist *)RCALLIT(ni, _ni_statistics_2, NULL))
+ == NULL) {
+ return (NI_FAILED);
+ }
+ *pl = *resp;
+ return (NI_OK);
+}
+
+
+ni_status
+ni_root(
+ void *ni,
+ ni_id *id
+ )
+{
+ ni_id_res *resp;
+
+ if ((resp = RCALLIT(ni, _ni_root_2, id)) == NULL) {
+ clnt_debug(ni, "_ni_root");
+ return (NI_FAILED);
+ }
+ if (resp->status == NI_OK) {
+ *id = resp->ni_id_res_u.id;
+ }
+ return (resp->status);
+}
+
+
+ni_status
+ni_self(
+ void *ni,
+ ni_id *id
+ )
+{
+ ni_id_res *resp;
+
+ if ((resp = RCALLIT(ni, _ni_self_2, id)) == NULL) {
+ clnt_debug(ni, "_ni_self");
+ return (NI_FAILED);
+ }
+ if (resp->status == NI_OK) {
+ *id = resp->ni_id_res_u.id;
+ }
+ return (resp->status);
+}
+
+
+ni_status
+ni_parent(
+ void *ni,
+ ni_id *id,
+ ni_index *parent_id_p
+ )
+{
+ ni_parent_res *resp;
+
+ if ((resp = RCALLIT(ni, _ni_parent_2, id)) == NULL) {
+ clnt_debug(ni, "_ni_parent");
+ return (NI_FAILED);
+ }
+ if (resp->status == NI_OK) {
+ *parent_id_p = resp->ni_parent_res_u.stuff.object_id;
+ *id = resp->ni_parent_res_u.stuff.self_id;
+ }
+ return (resp->status);
+}
+
+
+ni_status
+ni_children(
+ void *ni,
+ ni_id *id,
+ ni_idlist *children
+ )
+{
+ ni_children_res *resp;
+
+ if ((resp = RCALLIT(ni, _ni_children_2, id)) == NULL) {
+ clnt_debug(ni, "_ni_children");
+ return (NI_FAILED);
+ }
+ if (resp->status == NI_OK) {
+ *children = resp->ni_children_res_u.stuff.children;
+ *id = resp->ni_children_res_u.stuff.self_id;
+ }
+ return (resp->status);
+}
+
+
+ni_status
+ni_create(
+ void *ni,
+ ni_id *parent_id,
+ ni_proplist pl,
+ ni_id *child_id_p,
+ ni_index where
+ )
+{
+ ni_create_args args;
+ ni_create_res *resp;
+
+ args.id = *parent_id;
+ args.props = pl;
+ args.where = where;
+ args.target_id = NULL;
+ if ((resp = WCALLIT(ni, _ni_create_2, &args)) == NULL) {
+ clnt_debug(ni, "_ni_create");
+ return (NI_FAILED);
+ }
+ if (resp->status == NI_OK) {
+ *child_id_p = resp->ni_create_res_u.stuff.id;
+ *parent_id = resp->ni_create_res_u.stuff.self_id;
+ }
+ return (resp->status);
+}
+
+
+ni_status
+ni_destroy(
+ void *ni,
+ ni_id *parent_id,
+ ni_id self_id
+ )
+{
+ ni_id_res *resp;
+ ni_destroy_args args;
+
+ args.parent_id = *parent_id;
+ args.self_id = self_id;
+ if ((resp = WCALLIT(ni, _ni_destroy_2, &args)) == NULL) {
+ clnt_debug(ni, "_ni_destroy");
+ return (NI_FAILED);
+ }
+ if (resp->status == NI_OK) {
+ *parent_id = resp->ni_id_res_u.id;
+ }
+ return (resp->status);
+}
+
+
+ni_status
+ni_write(
+ void *ni,
+ ni_id *self_id,
+ ni_proplist pl
+ )
+{
+ ni_proplist_stuff args;
+ ni_id_res *resp;
+
+ args.id = *self_id;
+ args.props = pl;
+ if ((resp = WCALLIT(ni, _ni_write_2, &args)) == NULL) {
+ clnt_debug(ni, "_ni_write");
+ return (NI_FAILED);
+ }
+ if (resp->status == NI_OK) {
+ *self_id = resp->ni_id_res_u.id;
+ }
+ return (resp->status);
+}
+
+
+ni_status
+ni_read(
+ void *ni,
+ ni_id *self_id,
+ ni_proplist *pl
+ )
+{
+ ni_proplist_res *resp;
+
+ if ((resp = RCALLIT(ni, _ni_read_2, self_id)) == NULL) {
+ clnt_debug(ni, "_ni_read");
+ return (NI_FAILED);
+ }
+ if (resp->status == NI_OK) {
+ *self_id = resp->ni_proplist_res_u.stuff.id;
+ *pl = resp->ni_proplist_res_u.stuff.props;
+ }
+ return (resp->status);
+}
+
+
+ni_status
+ni_lookup(
+ void *ni,
+ ni_id *id,
+ ni_name_const pname,
+ ni_name_const pval,
+ ni_idlist *hits
+ )
+{
+ ni_lookup_res *resp;
+ ni_lookup_args args;
+
+ args.id = *id;
+ args.key = (ni_name)pname;
+ args.value = (ni_name)pval;
+ if ((resp = RCALLIT(ni, _ni_lookup_2, &args)) == NULL) {
+ clnt_debug(ni, "_ni_lookup");
+ return (NI_FAILED);
+ }
+ if (resp->status == NI_OK) {
+ *hits = resp->ni_lookup_res_u.stuff.idlist;
+ *id = resp->ni_lookup_res_u.stuff.self_id;
+ }
+ return (resp->status);
+}
+
+
+ni_status
+ni_lookupread(
+ void *ni,
+ ni_id *id,
+ ni_name_const pname,
+ ni_name_const pval,
+ ni_proplist *props
+ )
+{
+ ni_proplist_res *resp;
+ ni_lookup_args args;
+
+ args.id = *id;
+ args.key = (ni_name)pname;
+ args.value = (ni_name)pval;
+ if ((resp = RCALLIT(ni, _ni_lookupread_2, &args)) == NULL) {
+ clnt_debug(ni, "_ni_lookupread");
+ return (NI_FAILED);
+ }
+ if (resp->status == NI_OK) {
+ *props = resp->ni_proplist_res_u.stuff.props;
+ *id = resp->ni_proplist_res_u.stuff.id;
+ }
+ return (resp->status);
+}
+
+
+ni_status
+ni_list(
+ void *ni,
+ ni_id *id,
+ ni_name_const pname,
+ ni_entrylist *entries
+ )
+{
+ ni_list_res *resp;
+ ni_name_args args;
+
+ args.id = *id;
+ args.name = (ni_name)pname;
+ if ((resp = RCALLIT(ni, _ni_list_2, &args)) == NULL) {
+ clnt_debug(ni, "_ni_list");
+ return (NI_FAILED);
+ }
+ if (resp->status == NI_OK) {
+ *entries = resp->ni_list_res_u.stuff.entries;
+ *id = resp->ni_list_res_u.stuff.self_id;
+ }
+ return (resp->status);
+}
+
+
+ni_status
+ni_listall(
+ void *ni,
+ ni_id *id,
+ ni_proplist_list *entries
+ )
+{
+ ni_listall_res *resp;
+
+ if ((resp = RCALLIT(ni, _ni_listall_2, id)) == NULL) {
+ clnt_debug(ni, "_ni_listall");
+ return (NI_FAILED);
+ }
+ if (resp->status == NI_OK) {
+ *entries = resp->ni_listall_res_u.stuff.entries;
+ *id = resp->ni_listall_res_u.stuff.self_id;
+ }
+ return (resp->status);
+}
+
+
+ni_status
+ni_readprop(
+ void *ni,
+ ni_id *id,
+ ni_index which,
+ ni_namelist *propval_p
+ )
+{
+ ni_namelist_res *resp;
+ ni_prop_args args;
+
+ args.id = *id;
+ args.prop_index = which;
+ if ((resp = RCALLIT(ni, _ni_readprop_2, &args)) == NULL) {
+ clnt_debug(ni, "_ni_readprop");
+ return (NI_FAILED);
+ }
+ if (resp->status == NI_OK) {
+ *propval_p = resp->ni_namelist_res_u.stuff.values;
+ *id = resp->ni_namelist_res_u.stuff.self_id;
+ }
+ return (resp->status);
+}
+
+
+ni_status
+ni_writeprop(
+ void *ni,
+ ni_id *id,
+ ni_index which,
+ ni_namelist propval
+ )
+{
+ ni_id_res *resp;
+ ni_writeprop_args args;
+
+ args.id = *id;
+ args.prop_index = which;
+ args.values = propval;
+ if ((resp = WCALLIT(ni, _ni_writeprop_2, &args)) == NULL) {
+ clnt_debug(ni, "_ni_writeprop");
+ return (NI_FAILED);
+ }
+ if (resp->status == NI_OK) {
+ *id = resp->ni_id_res_u.id;
+ }
+ return (resp->status);
+}
+
+
+ni_status
+ni_listprops(
+ void *ni,
+ ni_id *id,
+ ni_namelist *propnames
+ )
+{
+ ni_namelist_res *resp;
+
+ if ((resp = RCALLIT(ni, _ni_listprops_2, id)) == NULL) {
+ clnt_debug(ni, "_ni_listprops");
+ return (NI_FAILED);
+ }
+ if (resp->status == NI_OK) {
+ *propnames = resp->ni_namelist_res_u.stuff.values;
+ *id = resp->ni_namelist_res_u.stuff.self_id;
+ }
+ return (resp->status);
+}
+
+
+ni_status
+ni_createprop(
+ void *ni,
+ ni_id *id,
+ ni_property prop,
+ ni_index where
+ )
+{
+ ni_id_res *resp;
+ ni_createprop_args args;
+
+ args.id = *id;
+ args.prop = prop;
+ args.where = where;
+ if ((resp = WCALLIT(ni, _ni_createprop_2, &args)) == NULL) {
+ clnt_debug(ni, "_ni_createprop");
+ return (NI_FAILED);
+ }
+ if (resp->status == NI_OK) {
+ *id = resp->ni_id_res_u.id;
+ }
+ return (resp->status);
+}
+
+
+ni_status
+ni_destroyprop(
+ void *ni,
+ ni_id *id,
+ ni_index which
+ )
+{
+ ni_id_res *resp;
+ ni_prop_args args;
+
+ args.id = *id;
+ args.prop_index = which;
+ if ((resp = WCALLIT(ni, _ni_destroyprop_2, &args)) == NULL) {
+ clnt_debug(ni, "_ni_destroyprop");
+ return (NI_FAILED);
+ }
+ if (resp->status == NI_OK) {
+ *id = resp->ni_id_res_u.id;
+ }
+ return (resp->status);
+}
+
+
+ni_status
+ni_renameprop(
+ void *ni,
+ ni_id *id,
+ ni_index prop_index,
+ ni_name_const name
+ )
+{
+ ni_id_res *resp;
+ ni_propname_args args;
+
+ args.id = *id;
+ args.prop_index = prop_index;
+ args.name = (ni_name)name;
+ if ((resp = WCALLIT(ni, _ni_renameprop_2, &args)) == NULL) {
+ clnt_debug(ni, "_ni_renameprop");
+ return (NI_FAILED);
+ }
+ if (resp->status == NI_OK) {
+ *id = resp->ni_id_res_u.id;
+ }
+ return (resp->status);
+}
+
+
+ni_status
+ni_createname(
+ void *ni,
+ ni_id *id,
+ ni_index prop_index,
+ ni_name_const name,
+ ni_index where
+ )
+{
+ ni_id_res *resp;
+ ni_createname_args args;
+
+ args.id = *id;
+ args.prop_index = prop_index;
+ args.name = (ni_name)name;
+ args.where = where;
+ if ((resp = WCALLIT(ni, _ni_createname_2, &args)) == NULL) {
+ clnt_debug(ni, "_ni_createname");
+ return (NI_FAILED);
+ }
+ if (resp->status == NI_OK) {
+ *id = resp->ni_id_res_u.id;
+ }
+ return (resp->status);
+}
+
+
+ni_status
+ni_destroyname(
+ void *ni,
+ ni_id *id,
+ ni_index prop_index,
+ ni_index name_index
+ )
+{
+ ni_id_res *resp;
+ ni_nameindex_args args;
+
+ args.id = *id;
+ args.prop_index = prop_index;
+ args.name_index = name_index;
+ if ((resp = WCALLIT(ni, _ni_destroyname_2, &args)) == NULL) {
+ clnt_debug(ni, "_ni_destroyname");
+ return (NI_FAILED);
+ }
+ if (resp->status == NI_OK) {
+ *id = resp->ni_id_res_u.id;
+ }
+ return (resp->status);
+}
+
+
+ni_status
+ni_writename(
+ void *ni,
+ ni_id *id,
+ ni_index prop_index,
+ ni_index name_index,
+ ni_name_const name
+ )
+{
+ ni_id_res *resp;
+ ni_writename_args args;
+
+ args.id = *id;
+ args.prop_index = prop_index;
+ args.name_index = name_index;
+ args.name = (ni_name)name;
+ if ((resp = WCALLIT(ni, _ni_writename_2, &args)) == NULL) {
+ clnt_debug(ni, "_ni_writename");
+ return (NI_FAILED);
+ }
+ if (resp->status == NI_OK) {
+ *id = resp->ni_id_res_u.id;
+ }
+ return (resp->status);
+}
+
+
+ni_status
+ni_readname(
+ void *ni,
+ ni_id *id,
+ ni_index prop_index,
+ ni_index name_index,
+ ni_name *name
+ )
+{
+ ni_readname_res *resp;
+ ni_nameindex_args args;
+
+ args.id = *id;
+ args.prop_index = prop_index;
+ args.name_index = name_index;
+ if ((resp = RCALLIT(ni, _ni_readname_2, &args)) == NULL) {
+ clnt_debug(ni, "_ni_readname");
+ return (NI_FAILED);
+ }
+ if (resp->status == NI_OK) {
+ *id = resp->ni_readname_res_u.stuff.id;
+ *name = resp->ni_readname_res_u.stuff.name;
+ }
+ return (resp->status);
+}
+
+
+ni_status
+ni_resync(
+ void *ni
+ )
+{
+ ni_status *resp;
+
+ if ((resp = (ni_status *)RCALLIT(ni, _ni_resync_2, NULL)) == NULL) {
+ return (NI_FAILED);
+ }
+ return (*resp);
+}
+
+
+ni_status
+ni_setuser(
+ void *ni,
+ ni_name_const user
+ )
+{
+ ni_id id;
+ ni_idlist ids;
+ ni_namelist nl;
+ char *p;
+
+ if (user == NULL) {
+ NIP(ni)->uid = getuid();
+ return (ni_setpassword(ni, NULL));
+ }
+
+ if (ni_root(ni, &id) != NI_OK) {
+ return(NI_NOUSER);
+ }
+ if (ni_lookup(ni, &id, NAME_NAME, NAME_USERS, &ids) != NI_OK) {
+ return (NI_NOUSER);
+ }
+ id.nii_object = ids.niil_val[0];
+ ni_idlist_free(&ids);
+
+ if (ni_lookup(ni, &id, NAME_NAME, user, &ids) != NI_OK) {
+ return (NI_NOUSER);
+ }
+ id.nii_object = ids.niil_val[0];
+ ni_idlist_free(&ids);
+ if (ni_lookupprop(ni, &id, NAME_UID, &nl) != NI_OK) {
+ return (NI_NOUSER);
+ }
+ if (nl.ninl_len == 0) {
+ return (NI_NOUSER);
+ }
+ for (p = nl.ninl_val[0]; *p; p++) {
+ if (!isdigit(*p)) {
+ ni_namelist_free(&nl);
+ return (NI_NOUSER);
+ }
+ }
+ NIP(ni)->uid = atoi(nl.ninl_val[0]);
+ if (NIP(ni)->passwd == NULL) {
+ NIP(ni)->passwd = ni_name_dup("");
+ }
+ createauth(NIP(ni));
+ return (NI_OK);
+}
+
+
+ni_status
+ni_setpassword(
+ void *ni,
+ ni_name_const passwd
+ )
+{
+ char *p;
+
+ if (NIP(ni)->passwd != NULL) {
+ ni_name_free(&NIP(ni)->passwd);
+ }
+ if (passwd == NULL) {
+ NIP(ni)->passwd = NULL;
+ if (NIP(ni)->tc != NULL) {
+ auth_destroy(NIP(ni)->tc->cl_auth);
+ NIP(ni)->tc->cl_auth = authnone_create();
+ }
+ return (NI_OK);
+ }
+ NIP(ni)->passwd = ni_name_dup(passwd);
+ /*
+ * Our trivial encryption scheme
+ */
+ for (p = NIP(ni)->passwd; *p; p++) {
+ *p = ~(*p);
+ }
+ createauth(NIP(ni));
+ return (NI_OK);
+}
+
+
+extern int bindresvport(int, struct sockaddr_in *);
+
+
+/*
+ * NeXT note:
+ * The procedure pmap_getport_to below is derived
+ * from Sun Microsystems RPC source code. As such the following
+ * statement applies to it.:
+ *
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/*
+ * Client interface to pmap rpc service.
+ *
+ * Find the mapped port for program,version.
+ * Calls the pmap service remotely to do the lookup.
+ * Returns 0 if no map exists.
+ */
+static u_short
+pmap_getport_to(address, program, version, protocol, timeout_secs, ntries)
+ struct sockaddr_in *address;
+ u_long program;
+ u_long version;
+ u_int protocol;
+ int timeout_secs;
+ int ntries;
+{
+ u_short port = 0;
+ int sock = -1;
+ register CLIENT *client;
+ struct pmap parms;
+ struct timeval timeout;
+
+ sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock < 0) {
+ return (0);
+ }
+ address->sin_port = htons(PMAPPORT);
+ timeout.tv_usec = ((timeout_secs % ntries) * 1000000) / ntries;
+ timeout.tv_sec = (timeout_secs / ntries);
+ client = clntudp_bufcreate(address, PMAPPROG,
+ PMAPVERS, timeout, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
+ if (client != (CLIENT *)NULL) {
+ parms.pm_prog = program;
+ parms.pm_vers = version;
+ parms.pm_prot = protocol;
+ parms.pm_port = 0; /* not needed or used */
+ timeout.tv_usec = 0;
+ timeout.tv_sec = timeout_secs;
+ if (CLNT_CALL(client, PMAPPROC_GETPORT, xdr_pmap, &parms,
+ xdr_u_short, &port, timeout) != RPC_SUCCESS){
+ rpc_createerr.cf_stat = RPC_PMAPFAILURE;
+ clnt_geterr(client, &rpc_createerr.cf_error);
+ port = 0;
+ } else if (port == 0) {
+ rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
+ }
+ }
+ if (client != NULL) {
+ clnt_destroy(client);
+ }
+ (void)close(sock);
+ address->sin_port = 0;
+ return (port);
+}
+
+
+/*
+ * Open a socket, but do not use the default portmap timeout
+ */
+static int
+socket_open(
+ struct sockaddr_in *raddr,
+ int prog,
+ int vers,
+ int timeout,
+ int ntries,
+ int proto
+ )
+{
+ int sock;
+
+ /*
+ * If no port number given ask the pmap for one
+ */
+ if (raddr->sin_port == 0) {
+ u_short port;
+ if ((port = pmap_getport_to(raddr, prog, vers,
+ IPPROTO_UDP, timeout,
+ ntries)) == 0) {
+ return (-1);
+ }
+ raddr->sin_port = htons(port);
+ }
+
+ sock = socket(AF_INET, proto == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM,
+ proto);
+ if (sock < 0) {
+ return (-1);
+ }
+ (void)bindresvport(sock, (struct sockaddr_in *)0);
+ if (proto == IPPROTO_TCP) {
+ if (connect(sock, (struct sockaddr *)raddr,
+ sizeof(*raddr)) < 0) {
+ (void)close(sock);
+ return (-1);
+ }
+ }
+ return (sock);
+}