1
0
Files
2022-09-29 17:59:04 +03:00

2629 lines
61 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <alloca.h>
#include <sys/param.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/uio.h>
#include <mdbm.h>
#include <ns_api.h>
#include <ns_daemon.h>
#include <t6net.h>
#include "ns_nisserv.h"
static nisserv_t *_nsdata_list;
static uint32_t _nsdata_xid;
nisstring_t _nisserv_host;
uint32_t _nisserv_euid, _nisserv_egid;
char _nisserv_buf[8192];
nsd_file_t *__nsd_mounts;
int _nisserv_udp=-1, _nisserv_tcp=-1;
extern int _ltoa(long, char *);
extern int bindresvport(int, struct sockaddr_in *);
extern int sendfromto(int, void *, int, int, struct in_addr *, void *, int,
mac_t);
extern int recvfromto(int, void *, int, int, struct sockaddr *, int,
struct in_addr *, mac_t *);
static int dispatch_tcp(nsd_file_t **, int);
securenets_t *_nisserv_securenets = 0;
/*
** Looks up the NIS specific structure by transaction ID.
*/
nisserv_t *
nisserv_data_byxid(uint32_t xid)
{
nisserv_t *nd;
for (nd = _nsdata_list; nd && (nd->xid != xid); nd = nd->next);
return nd;
}
/*
** Creates a data structure to hold NIS specific protocol information for
** a request and adds it to a list.
*/
nisserv_t *
nisserv_data_new(void)
{
nisserv_t *nd;
nd = (nisserv_t *)nsd_calloc(1, sizeof(nisserv_t));
if (! nd) {
nsd_logprintf(NSD_LOG_RESOURCE,
"nisserv_data_new: malloc failed\n");
return (nisserv_t *)0;
}
nd->xid = ++_nsdata_xid;
nd->next = _nsdata_list;
_nsdata_list = nd;
return nd;
}
/*
** Looks up the NIS specific structure by file descriptor.
*/
nisserv_t *
nisserv_data_byfd(int fd)
{
nisserv_t *nd;
for (nd = _nsdata_list; nd && (nd->fd != fd); nd = nd->next);
return nd;
}
/*
** Removes the NIS specific data structure by transaction ID.
*/
void
nisserv_data_clear(nisserv_t **data)
{
nisserv_t **last, *nd;
for (last = &_nsdata_list, nd = _nsdata_list;
nd && (nd != *data);
last = &nd->next, nd = nd->next);
if (nd) {
*last = nd->next;
}
if (data && *data) {
if ((*data)->data) {
free((*data)->data);
}
if ((*data)->lbl) {
mac_free((*data)->lbl);
}
if ((*data)->cred) {
nsd_cred_clear(&(*data)->cred);
}
free(*data);
*data = 0;
}
}
/*
** This routine is called when we are waiting for more data, or the queue
** to drain in dispatch_tcp.
*/
static int
nisserv_timeout(nsd_file_t **rqp, nsd_times_t *to) {
nisserv_t *nd;
nsd_logprintf(NSD_LOG_LOW, "entering nisserv_timeout:\n");
nd = (nisserv_t *)to->t_clientdata;
nsd_timeout_remove(to->t_file);
if (nd) {
nsd_callback_remove(nd->fd);
close(nd->fd);
nisserv_data_clear(&nd);
}
*rqp = 0;
return NSD_CONTINUE;
}
/*
** This is a commn routine to return the results to the client. For UDP
** we use recvfromto to send the packet to the client from the same address
** it was originally sent to. For TCP we prepend a length and use writev.
** This routine assumes that this is the last packet sent so it closes any
** remaining file descriptors and destorys the data.
*/
static void
reply(nisserv_t *nd, char *buf, int len)
{
if (nd->proto == IPPROTO_UDP) {
if (sendfromto(nd->fd, buf, len, 0, &nd->local, &nd->sin,
sizeof(nd->sin), nd->lbl) < 0) {
nsd_logprintf(NSD_LOG_LOW,
"nisserv reply: sendfromto failed: %s\n",
strerror(errno));
}
} else {
struct iovec iov[2];
uint32_t p;
p = htonl(0x80000000 + len);
iov[0].iov_base = &p;
iov[0].iov_len = sizeof(p);
iov[1].iov_base = buf;
iov[1].iov_len = len;
if (writev(nd->fd, iov, 2) < 0) {
nsd_logprintf(NSD_LOG_LOW,
"nisserv reply: writev failed: %s\n",
strerror(errno));
}
nsd_callback_remove(nd->fd);
close(nd->fd);
}
nisserv_data_clear(&nd);
}
/*
** This is a handy routine to return an error to a requestor.
*/
static int
reject_err(nisserv_t *nd, uint32_t error)
{
uint32_t *p;
if (! nd) {
return NSD_CONTINUE;
}
p = (uint32_t *)_nisserv_buf;
*p++ = htonl(nd->xid);
*p++ = htonl(REPLY);
*p++ = htonl(MSG_DENIED);
*p++ = htonl(error);
reply(nd, _nisserv_buf, (char *)p - _nisserv_buf);
return NSD_CONTINUE;
}
/*
** This is a handy routine to return an error to a requestor.
*/
static int
accept_err(nisserv_t *nd, uint32_t error)
{
uint32_t *p;
if (! nd) {
return NSD_CONTINUE;
}
p = (uint32_t *)_nisserv_buf;
*p++ = htonl(nd->xid);
*p++ = htonl(REPLY);
*p++ = htonl(MSG_ACCEPTED);
*p++ = 0, *p++ = 0;
*p++ = htonl(error);
reply(nd, _nisserv_buf, (char *)p - _nisserv_buf);
return NSD_CONTINUE;
}
/*
** This is a convenience routine to return an error from the NIS level.
*/
static int
nisserv_err(nisserv_t *nd, uint32_t error)
{
uint32_t *p;
if (! nd) {
return NSD_CONTINUE;
}
p = (uint32_t *)_nisserv_buf;
*p++ = htonl(nd->xid);
*p++ = htonl(REPLY);
*p++ = htonl(MSG_ACCEPTED);
*p++ = 0, *p++ = 0;
*p++ = htonl(SUCCESS);
if (nd->cmd == YPPROC_ALL) {
*p++ = 1;
}
*p++ = htonl(error);
switch (nd->cmd) {
case YPPROC_ALL:
*p++=0;
/* ypresp_key_val */
case YPPROC_FIRST:
case YPPROC_NEXT:
/* ypresp_xfr -- should never go here */
case YPPROC_XFR:
*p++=0;
/* ypresp_val */
case YPPROC_MATCH:
/* ypresp_master */
case YPPROC_MASTER:
/* ypresp_order */
case YPPROC_ORDER:
/* ypresp_ypmaplist */
case YPPROC_MAPLIST:
*p++=0;
/* void */
case YPPROC_NULL:
case YPPROC_CLEAR:
/* bool (done by the status)*/
case YPPROC_DOMAIN:
case YPPROC_DOMAIN_NONACK:
/* ypresp_all (FALSE)*/
default:
;
}
reply(nd, _nisserv_buf, (char *)p - _nisserv_buf);
return NSD_CONTINUE;
}
/*
** This is a convenience routine to return an error from the NIS level.
*/
static int
nispush_err(nisserv_t *nd, uint32_t transid, uint32_t error)
{
uint32_t *p;
if (! nd) {
return NSD_CONTINUE;
}
p = (uint32_t *)_nisserv_buf;
*p++ = htonl(nd->xid);
*p++ = htonl(REPLY);
*p++ = htonl(MSG_ACCEPTED);
*p++ = 0, *p++ = 0;
*p++ = htonl(TRUE);
*p++ = htonl(transid);
*p++ = htonl(error);
reply(nd, _nisserv_buf, (char *)p - _nisserv_buf);
return NSD_CONTINUE;
}
static int
nis_check(nsd_cred_t *cp, nsd_file_t *fp)
{
int i;
nsd_logprintf(NSD_LOG_LOW, "entering nis_check:\n");
nsd_logprintf(NSD_LOG_LOW, "\tcred uid: %d, gid: %d\n", cp->c_uid,
cp->c_gid[0]);
nsd_logprintf(NSD_LOG_LOW, "\tfile uid: %d, gid: %d\n", fp->f_uid,
fp->f_gid);
if (((cp->c_uid == 0) || (cp->c_uid == fp->f_uid)) &&
(fp->f_mode & S_IRUSR)) {
nsd_logprintf(NSD_LOG_LOW, "\tsucceed case 1\n");
return 1;
} else if (fp->f_mode & S_IROTH) {
nsd_logprintf(NSD_LOG_LOW, "\tsucceed case 2\n");
return 1;
} else {
if (fp->f_mode & S_IRGRP) {
for (i = 0; i < cp->c_gids; i++) {
if (cp->c_gid[i] == fp->f_gid) {
nsd_logprintf(NSD_LOG_LOW,
"\tsucceed case 3\n");
return 1;
}
}
}
}
nsd_logprintf(NSD_LOG_LOW, "\tfail: case 4\n");
return 0;
}
/*
** This is a convenience routine used when building a response packet.
*/
static int
rpc_hdr(nisserv_t *nd, uint32_t *buf)
{
uint32_t *p;
if (! nd || ! buf) {
return 0;
}
p = buf;
*p++ = htonl(nd->xid);
*p++ = htonl(REPLY);
*p++ = htonl(MSG_ACCEPTED);
*p++ = 0; *p++ = 0;
*p++ = SUCCESS;
return (p - buf);
}
/*
** This routine post-parses hosts entries. NSD passes host data with multiple
** addresses, but pre-6.5 Irix and other operating systems cannot parse this
** so here we strip them off.
*/
static int
host_rewrite(char *src, int slen, char *dst, int dlen)
{
int len;
char *d;
struct in_addr addr;
nsd_logprintf(NSD_LOG_HIGH, "entering host_rewrite: %*.*s\n", slen,
slen, src);
for (; *src && slen && isspace(*src); src++, slen--);
/* copy first address */
for (len = 0, d = dst; *src && slen && (len < dlen) && !isspace(*src);
src++, slen--, d++, len++) {
*d = *src;
}
/* skip subsequent addresses */
for (;;) {
/* skip whitespace */
for (; *src && slen && isspace(*src); src++, slen--);
/* skip if address */
if (! inet_aton(src, &addr) || ! (*src && slen)) {
break;
}
for (; *src && slen && !isspace(*src); src++, slen--);
}
/* copy rest of line */
if (len < dlen) {
*d++ = '\t', len++;
for (; *src && (*src != '\n') && slen && (len < dlen);
src++, slen--, d++, len++) {
*d = *src;
}
}
/* null extend to next word boundary */
for (dlen = len; dlen % 4; d++, dlen++) {
*d = (char)0;
}
nsd_logprintf(NSD_LOG_HIGH, "exiting host_rewrite: %*.*s\n", len,
len, dst);
return len;
}
/*
** Sends results from a forwarded request to the requestor. This is only
** called from YPPROC_MATCH and the portmap register.
*/
static int
nisserv_send(nsd_file_t *rq)
{
nisserv_t *nd;
uint32_t *p;
int len;
nsd_logprintf(NSD_LOG_MIN, "entering nisserv_send:\n");
nd = rq->f_sender;
if (! nd) {
nsd_logprintf(NSD_LOG_LOW, "\treturning NSD_CONTINUE\n");
return NSD_CONTINUE;
}
rq->f_sender = 0;
if (! nd->sin.sin_port) {
nisserv_data_clear(&nd);
nsd_file_clear(&rq);
nsd_logprintf(NSD_LOG_LOW, "\treturning NSD_CONTINUE\n");
return NSD_CONTINUE;
}
/*
** If we determine that this came from a "secure" map then we require
** that the request be sent from port<1024.
*/
if (nsd_attr_fetch_bool(rq->f_attrs, "nis_secure", 0) &&
ntohs(nd->sin.sin_port) >= 1024) {
nsd_file_clear(&rq);
nsd_logprintf(NSD_LOG_LOW, "\tReturning YP_NOMAP\n");
return nisserv_err(nd, YP_NOMAP);
}
if (! nis_check(nd->cred, rq)) {
nsd_logprintf(NSD_LOG_LOW, "\tPermission denied\n");
nsd_file_clear(&rq);
return nisserv_err(nd, YP_NOMAP);
}
/*
** Build up match reply.
*/
p = (uint32_t *)_nisserv_buf;
p += rpc_hdr(nd, p);
if (rq->f_status == NS_SUCCESS) {
*p++ = htonl(YP_TRUE);
if (rq->f_used > 0) {
if (strncasecmp(nd->table.string, "hosts.", 6) == 0) {
len = host_rewrite(rq->f_data, rq->f_used,
(char *)(p + 1), (sizeof(_nisserv_buf) -
((char *)p - _nisserv_buf)) - sizeof(*p));
*p++ = htonl(len);
p += WORDS(len);
} else {
*p++ = htonl(rq->f_used);
p[rq->f_used/sizeof(*p)] = 0;
memcpy(p, rq->f_data, rq->f_used);
p += WORDS(rq->f_used);
nsd_logprintf(NSD_LOG_HIGH,
"\tlen = %d, result = %*s\n",
rq->f_used, rq->f_used, rq->f_data);
}
} else {
*p++ = htonl(0);
}
} else if (rq->f_status == NS_NOTFOUND) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_NOKEY\n");
*p++ = YP_NOKEY;
*p++ = 0;
} else {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_NOMAP [%d]\n",
rq->f_status);
*p++ = YP_NOMAP;
*p++ = 0;
}
reply(nd, _nisserv_buf, (char *)p - _nisserv_buf);
nsd_file_clear(&rq);
return NSD_CONTINUE;
}
/*
** Just sends back an empty data field.
*/
/*ARGSUSED*/
static int
nisserv_null(nsd_file_t **rqp, nisserv_t *nd, uint32_t *p)
{
nsd_logprintf(NSD_LOG_MIN, "entering nisserv_null:\n");
p = (uint32_t *)_nisserv_buf;
p += rpc_hdr(nd, p);
reply(nd, _nisserv_buf, (char *)p - _nisserv_buf);
return NSD_CONTINUE;
}
/*
** This checks to see if we are handling a given domain and return true
** if we do, and false if we don't and the requestor wanted a nack.
*/
/* ARGSUSED */
static int
nisserv_domain(nsd_file_t **rqp, nisserv_t *nd, uint32_t *p, int nack)
{
nsd_file_t *dp;
int len;
nsd_logprintf(NSD_LOG_MIN, "entering nisserv_domain:\n");
dp = __nsd_mounts;
len = ntohl(*p++);
if (len > MAXNAMELEN) {
dp = (nsd_file_t *)0;
}
nsd_logprintf(NSD_LOG_LOW, "\tlen = %d, domain = %s\n", len, p);
/*
** Search through top level directory for domain directory.
*/
if (dp &&
nsd_file_byname(__nsd_mounts->f_data, (char *)p, len)) {
p = (uint32_t *)_nisserv_buf;
p += rpc_hdr(nd, p);
*p++ = htonl(TRUE);
nsd_logprintf(NSD_LOG_LOW, "\treturning TRUE to %s.%d\n",
inet_ntoa(nd->sin.sin_addr), nd->sin.sin_port);
reply(nd, _nisserv_buf, (char *)p - _nisserv_buf);
} else if (nack) {
p = (uint32_t *)_nisserv_buf;
p += rpc_hdr(nd, p);
*p++ = htonl(FALSE);
nsd_logprintf(NSD_LOG_LOW, "\treturning FALSE\n");
reply(nd, _nisserv_buf, (char *)p - _nisserv_buf);
} else {
/* While we have been told NOT to send a NACK, We know that
** this routine is only ever intended to get called by
** portmap as a callit. In that case we dont want it to
** wait around for an answer that we will never give it,
** so lets send it a GARBAGE_ARGS which will tell portmap
** that we will not be sending a responce. Portmap will
** not pass this on.
*/
p = (uint32_t *)_nisserv_buf;
p += rpc_hdr(nd,p);
*(p-1) = GARBAGE_ARGS;
*p++ = htonl(FALSE);
nsd_logprintf(NSD_LOG_MAX, "\treturning RPC GARBAGE_ARGS\n");
reply(nd, _nisserv_buf, (char *)p - _nisserv_buf);
}
return NSD_CONTINUE;
}
/*
** Looks up a specific key in a specific map.
*/
static int
nisserv_match(nsd_file_t **rqp, nisserv_t *nd, uint32_t *p)
{
nsd_file_t *dp, *cp, *rq;
int len;
nsd_logprintf(NSD_LOG_MIN, "entering nisserv_match:\n");
/*
** Domain.
*/
len = ntohl(*p++);
if (len > MAXNAMELEN) {
nsd_logprintf(NSD_LOG_LOW, "\tReturning YP_BADARGS\n");
return nisserv_err(nd, YP_BADARGS);
}
memcpy(nd->domain.string, p, len);
nd->domain.string[len] = (char)0;
nd->domain.len = len;
nd->domain.words = WORDS(len);
p += nd->domain.words;
nsd_logprintf(NSD_LOG_LOW, "\tdomain = %s\n", nd->domain.string);
/*
** We do not serve data from the .local domain.
*/
if (strcasecmp(nd->domain.string, NS_DOMAIN_LOCAL) == 0) {
nsd_logprintf(NSD_LOG_LOW, "\tReturning YP_NODOM\n");
return nisserv_err(nd, YP_NODOM);
}
/*
** Table.
*/
len = ntohl(*p++);
if (len > MAXNAMELEN) {
nsd_logprintf(NSD_LOG_LOW, "\tReturning YP_BADARGS 2\n");
return nisserv_err(nd, YP_BADARGS);
}
memcpy(nd->table.string, p, len);
nd->table.string[len] = (char)0;
nd->table.len = len;
nd->table.words = WORDS(nd->table.len);
nsd_logprintf(NSD_LOG_LOW, "\ttable = %s\n", nd->table.string);
p += nd->table.words;
/*
** Key.
*/
len = ntohl(*p++);
if (len <= 0 || len > MAXNAMELEN ) {
nsd_logprintf(NSD_LOG_LOW, "\tReturning YP_BADARGS 3\n");
return nisserv_err(nd, YP_BADARGS);
}
memcpy(nd->key.string, p, len);
nd->key.string[len] = (char)0;
/*
** the mail.aliases map requires a null to be
** accounted for in len. This is not in the protocol,
** but in the client programs.
if (!strcmp(nd->table.string,"mail.aliases")) {
len=strlen(nd->key.string);
}
*/
nd->key.len = len;
nd->key.words = WORDS(len);
p += nd->key.words;
nsd_logprintf(NSD_LOG_LOW, "\tkey = %s (%d)\n", nd->key.string, len);
dp = __nsd_mounts;
if (! dp || !(dp = nsd_file_byname(dp->f_data, nd->domain.string,
nd->domain.len))) {
nsd_logprintf(NSD_LOG_LOW, "\tReturning YP_NODOM\n");
return nisserv_err(nd, YP_NODOM);
}
if (! (dp = nsd_file_byname(dp->f_data, nd->table.string,
nd->table.len))) {
nsd_logprintf(NSD_LOG_LOW, "\tReturning YP_NOMAP 2\n");
return nisserv_err(nd, YP_NOMAP);
}
if ((rq = nsd_file_byname(dp->f_data, nd->key.string, nd->key.len)) &&
rq->f_status == NS_SUCCESS && rq->f_type == NFREG) {
/*
** In the daemon cache so we just return the data.
** If the file is marked with the nis_secure attribute then
** we require that the client request from port<1024.
*/
if (nsd_attr_fetch_bool(rq->f_attrs, "nis_secure", 0) &&
ntohs(nd->sin.sin_port) >= 1024) {
nsd_logprintf(NSD_LOG_LOW, "\tReturning YP_NOMAP 3\n");
return nisserv_err(nd, YP_NOMAP);
}
if (! nis_check(nd->cred, rq)) {
nsd_logprintf(NSD_LOG_LOW, "\tPermission denied\n");
return nisserv_err(nd, YP_NOMAP);
}
p = (uint32_t *)_nisserv_buf;
p += rpc_hdr(nd, p);
*p++ = htonl(YP_TRUE);
if (rq->f_used > 0) {
if (strncasecmp(nd->table.string, "hosts.", 6) == 0) {
len = host_rewrite(rq->f_data, rq->f_used,
(char *)(p + 1),
(sizeof(_nisserv_buf) -
((char *)p - _nisserv_buf)) - sizeof(*p));
*p++ = htonl(len);
p += WORDS(len);
} else {
*p++ = ntohl(rq->f_used);
p[rq->f_used/sizeof(*p)] = 0;
memcpy(p, rq->f_data, rq->f_used);
p += WORDS(rq->f_used);
}
} else {
*p++ = htonl(0);
}
reply(nd, _nisserv_buf, (char *)p - _nisserv_buf);
return NSD_CONTINUE;
}
/*
** If nis_secure attribute is set on table then we require the
** request to be coming from port<1024. If this isn't the case
** then we do not bother to setup a request.
*/
if (nsd_attr_fetch_bool(dp->f_attrs, "nis_secure", 0) &&
ntohs(nd->sin.sin_port) >= 1024) {
nsd_logprintf(NSD_LOG_LOW, "\tReturning YP_NOMAP 4\n");
return nisserv_err(nd, YP_NOMAP);
}
/*
** Setup a request.
*/
if (nsd_file_init(&rq, nd->key.string, nd->key.len, dp, NFREG,
nd->cred) != NSD_OK) {
return nisserv_err(nd, YP_YPERR);
}
rq->f_index = LOOKUP;
nsd_file_copycallouts(dp, rq, 0);
nsd_attr_store(&rq->f_attrs, "key", nd->key.string);
for (cp = nsd_file_getcallout(rq); cp; cp = nsd_file_nextcallout(rq)) {
if (cp->f_lib && cp->f_lib->l_funcs[rq->f_index]) {
nsd_attr_continue(&rq->f_attrs, cp->f_attrs);
*rqp = rq;
rq->f_send = nisserv_send;
rq->f_sender = nd;
return (*cp->f_lib->l_funcs[rq->f_index])(rq);
}
}
/*
** No callouts match.
*/
nsd_logprintf(NSD_LOG_LOW, "\tReturning YP_NOMAP\n");
nisserv_data_clear((nisserv_t **)&rq->f_sender);
nsd_file_clear(&rq);
return nisserv_err(nd, YP_NOMAP);
}
/*
** This returns the first key in the data. This only works out of
** the hash file. We do not bother to implement this with all the
** other protocols as it would be very inefficient.
*/
/*ARGSUSED*/
static int
nisserv_first(nsd_file_t **rqp, nisserv_t *nd, uint32_t *p)
{
int len,pagesize;
hash_file_t *hf;
kvpair kv;
char *key, *val;
nsd_file_t *dp,*dp2;
struct stat sbuf;
nsd_logprintf(NSD_LOG_MIN, "entering nisserv_first:\n");
/*
** Arguments are a domain and a map. We walk from the top of
** the tree looking for these.
*/
len = ntohl(*p++);
if (len > MAXNAMELEN) {
nsd_logprintf(NSD_LOG_HIGH, "\treturning YP_BADARGS\n");
return nisserv_err(nd, YP_BADARGS);
}
dp = __nsd_mounts;
if (! dp || !(dp = nsd_file_byname(dp->f_data, (char *)p, len))) {
nsd_logprintf(NSD_LOG_HIGH, "\treturning YP_NODOM\n");
return nisserv_err(nd, YP_NODOM);
}
p += WORDS(len);
len = ntohl(*p++);
if (len > MAXNAMELEN) {
nsd_logprintf(NSD_LOG_HIGH, "\treturning YP_BADARGS 2\n");
return nisserv_err(nd, YP_BADARGS);
}
if (! (dp = nsd_file_byname(dp->f_data, (char *)p, len))) {
nsd_logprintf(NSD_LOG_HIGH, "\treturning YP_NOMAP\n");
return nisserv_err(nd, YP_NOMAP);
}
/*
** The library name is ".nisserv" or ".ypserv". XXX - We should really
** walk through all the callouts testing the library as the name
** could change.
*/
if (! (dp2 = nsd_file_byname(dp->f_callouts, ".nisserv", 8))) {
if (! (dp2 = nsd_file_byname(dp->f_callouts, ".ypserv", 7))) {
nsd_logprintf(NSD_LOG_HIGH, "\treturning YP_NODOM 2\n");
return nisserv_err(nd, YP_NODOM);
}
}
dp = dp2;
/*
** Now look for the database file.
*/
hf = hash_get_file(dp);
if (! hf) {
nsd_logprintf(NSD_LOG_HIGH, "\treturning YP_NOMAP\n");
return nisserv_err(nd, YP_NOMAP);
}
if (stat(hf->file, &sbuf)) {
nsd_logprintf(NSD_LOG_HIGH, "\treturning YP_NOMAP 2\n");
return nisserv_err(nd, YP_NOMAP);
}
if ((! hf->map) || (hf->version < sbuf.st_mtime)
|| _Mdbm_invalid(hf->map)) {
if (! hash_reopen(hf)) {
nsd_logprintf(NSD_LOG_HIGH, "\treturning YP_BADDB 3\n");
return nisserv_err(nd, YP_BADDB);
}
hf->version = sbuf.st_mtime;
}
pagesize = MDBM_PAGE_SIZE(hf->map);
key = alloca(pagesize);
val = alloca(pagesize);
for (kv.key.dptr = key , kv.key.dsize = pagesize,
kv.val.dptr = val , kv.val.dsize = pagesize,
kv = mdbm_first(hf->map,kv);
kv.key.dptr;
kv.key.dptr = key , kv.key.dsize = pagesize,
kv.val.dptr = val , kv.val.dsize = pagesize,
kv = mdbm_next(hf->map,kv)) {
/* Skip private records. */
if ((kv.key.dsize < 3) ||
(strncmp(kv.key.dptr, "YP_", 3) != 0)) {
break;
}
}
if (! kv.key.dptr) {
nsd_logprintf(NSD_LOG_HIGH, "\treturning YP_NOMORE\n");
return nisserv_err(nd, YP_NOMORE);
}
/*
** Build the response.
*/
p = (uint32_t *)_nisserv_buf;
p += rpc_hdr(nd, p);
*p++ = htonl(TRUE);
*p++ = htonl(kv.val.dsize);
memcpy(p, kv.val.dptr, kv.val.dsize);
p += WORDS(kv.val.dsize);
*p++ = htonl(kv.key.dsize);
memcpy(p, kv.key.dptr, kv.key.dsize);
p += WORDS(kv.key.dsize);
reply(nd, _nisserv_buf, (char *)p - _nisserv_buf);
return NSD_CONTINUE;
}
/*
** This looks up a key in the hash file, then returns the next one.
** This is only implemented for the hash files.
*/
/*ARGSUSED*/
static int
nisserv_next(nsd_file_t **rqp, nisserv_t *nd, uint32_t *p)
{
int len;
hash_file_t *hf;
kvpair kv;
int pagesize;
char *val, *key;
nsd_file_t *dp,*dp2;
struct stat sbuf;
nsd_logprintf(NSD_LOG_MIN, "entering nisserv_next:\n");
/*
** Arguments are a domain and a map. We walk from the top of
** the tree looking for these.
*/
len = ntohl(*p++);
if (len > MAXNAMELEN) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_BADARGS\n");
return nisserv_err(nd, YP_BADARGS);
}
nsd_logprintf(NSD_LOG_HIGH, "\tdomain: %*.*s\n", len, len, p);
dp = __nsd_mounts;
if (! dp || !(dp = nsd_file_byname(dp->f_data, (char *)p, len))) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_NODOM\n");
return nisserv_err(nd, YP_NODOM);
}
p += WORDS(len);
len = ntohl(*p++);
if (len > MAXNAMELEN) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_BADARGS 2\n");
return nisserv_err(nd, YP_BADARGS);
}
nsd_logprintf(NSD_LOG_HIGH, "\ttable: %*.*s\n", len, len, p);
if (! (dp = nsd_file_byname(dp->f_data, (char *)p, len))) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_NOMAP\n");
return nisserv_err(nd, YP_NOMAP);
}
p += WORDS(len);
/*
** The library name is ".nisserv" or ".ypserv". XXX - We should really
** walk through all the callouts testing the library as the name
** could change.
*/
if (! (dp2 = nsd_file_byname(dp->f_callouts, ".nisserv", 8))) {
if(! (dp2 = nsd_file_byname(dp->f_callouts, ".ypserv", 7))) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_NODOM\n");
return nisserv_err(nd, YP_NODOM);
}
}
dp = dp2;
/*
** Now look for the database file.
*/
hf = hash_get_file(dp);
if (! hf) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_NOMAP\n");
return nisserv_err(nd, YP_NOMAP);
}
if (stat(hf->file, &sbuf)) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_NOMAP\n");
return nisserv_err(nd, YP_NOMAP);
}
if ((! hf->map) || (hf->version < sbuf.st_mtime)
|| _Mdbm_invalid(hf->map)) {
if (! hash_reopen(hf)) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_BADDB\n");
return nisserv_err(nd, YP_BADDB);
}
hf->version = sbuf.st_mtime;
}
/*
** Look for the key.
*/
len = ntohl(*p++);
if (len > MAXNAMELEN) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_BADARGS 3\n");
return nisserv_err(nd, YP_BADARGS);
}
nsd_logprintf(NSD_LOG_HIGH, "\tkey: %*.*s\n", len, len, p);
pagesize=MDBM_PAGE_SIZE(hf->map);
key = alloca(pagesize);
val = alloca(pagesize);
kv.key.dptr = (char *)p;
kv.key.dsize = len;
kv.val.dptr = key;
kv.val.dsize = pagesize;
kv.val = mdbm_fetch(hf->map, kv);
if (! kv.val.dptr) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_NOMORE\n");
return nisserv_err(nd, YP_NOMORE);
}
for (kv.key.dptr = key , kv.key.dsize = pagesize,
kv.val.dptr = val , kv.val.dsize = pagesize,
kv = mdbm_next(hf->map,kv);
kv.key.dptr;
kv.key.dptr = key , kv.key.dsize = pagesize,
kv.val.dptr = val , kv.val.dsize = pagesize,
kv = mdbm_next(hf->map,kv)) {
/* Skip private records. */
if ((kv.key.dsize < 3) ||
(strncmp(kv.key.dptr, "YP_", 3) != 0)) {
break;
}
}
if (! kv.key.dptr) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_NOMORE\n");
return nisserv_err(nd, YP_NOMORE);
}
/*
** Build the response.
*/
p = (uint32_t *)_nisserv_buf;
p += rpc_hdr(nd, p);
*p++ = htonl(TRUE);
*p++ = htonl(kv.val.dsize);
memcpy(p, kv.val.dptr, kv.val.dsize);
p += WORDS(kv.val.dsize);
*p++ = htonl(kv.key.dsize);
memcpy(p, kv.key.dptr, kv.key.dsize);
p += WORDS(kv.key.dsize);
nsd_logprintf(NSD_LOG_HIGH, "\tkey: %*.*s, val: %*.*s\n", kv.key.dsize,
kv.key.dsize, kv.key.dptr, kv.val.dsize, kv.val.dsize, kv.val.dptr);
reply(nd, _nisserv_buf, (char *)p - _nisserv_buf);
return NSD_CONTINUE;
}
/*
** This exec's the ypxfr function. Ypxfr will copy over a new hash
** file from the master server then ping us to reopen the hash files.
*/
/*ARGSUSED*/
static int
nisserv_xfr(nsd_file_t **rqp, nisserv_t *nd, uint32_t *p)
{
pid_t pid;
uint32_t transid, proto, port;
char tidstr[16], protostr[16], portstr[16], *ipaddr;
int len, i;
nsd_logprintf(NSD_LOG_MIN, "entering nisserv_xfr:\n");
/*
** Ypmap_parms. domain, map, ordernum, owner (owner saved in nd->key).
*/
len = ntohl(*p++);
nsd_logprintf(NSD_LOG_LOW, "\tlen: %d, domain: %s\n", len, p);
if (len > MAXNAMELEN) {
return nispush_err(nd, 0, YPPUSH_BADARGS);
}
memcpy(nd->domain.string, p, len);
nd->domain.string[len] = (char)0;
nd->domain.len = len;
nd->domain.words = WORDS(len);
p += nd->domain.words;
len = ntohl(*p++);
nsd_logprintf(NSD_LOG_LOW, "\tlen: %d, table: %s\n", len, p);
if (len > MAXNAMELEN) {
return nispush_err(nd, 0, YPPUSH_BADARGS);
}
memcpy(nd->table.string, p, len);
nd->table.string[len] = (char)0;
nd->table.len = len;
nd->table.words = WORDS(nd->table.len);
p += nd->table.words;
/* we ignore ordernum */
p++;
len = ntohl(*p++);
nsd_logprintf(NSD_LOG_LOW, "\tlen: %d, key: %s\n", len, p);
if (len > MAXNAMELEN) {
return nispush_err(nd, 0, YPPUSH_BADARGS);
}
memcpy(nd->key.string, p, len);
nd->key.string[len] = (char)0;
nd->key.len = len;
nd->key.words = WORDS(len);
p += nd->key.words;
transid = ntohl(*p++);
_ltoa((long)transid, tidstr);
proto = ntohl(*p++);
_ltoa((long)proto, protostr);
port = ntohl(*p++);
_ltoa((long)port, portstr);
ipaddr = inet_ntoa(nd->sin.sin_addr);
pid = fork();
switch (pid) {
case -1:
nsd_logprintf(NSD_LOG_RESOURCE, "nisserv_xfr: failed fork\n");
return nispush_err(nd, transid, YPPUSH_XFRERR);
case 0:
/*
** Child.
*/
for (i = 3; i < getdtablehi(); i++) {
close(i);
}
if (execl("/usr/sbin/ypxfr", "ypxfr", "-d", nd->domain.string,
"-C", tidstr, protostr, ipaddr, portstr, nd->table.string,
(char *)0) < 0) {
nsd_logprintf(NSD_LOG_RESOURCE,
"nisserv_xfr: failed exec\n");
}
nispush_err(nd, transid, YPPUSH_XFRERR);
exit(1);
}
/*
** Parent.
*/
return nisserv_null(rqp, nd, p);
}
/*
** This exec's the ypxfr function. Ypxfr will copy over a new hash
** file from the master server then ping us to reopen the hash files.
*/
/*ARGSUSED*/
static int
nisserv_newxfr(nsd_file_t **rqp, nisserv_t *nd, uint32_t *p)
{
pid_t pid;
uint32_t transid, proto;
char tidstr[16], protostr[16], name[64];
int len, i;
nsd_logprintf(NSD_LOG_MIN, "entering nisserv_xfr:\n");
/*
** Ypmap_parms. domain, map, ordernum, owner (owner saved in nd->key).
*/
len = ntohl(*p++);
nsd_logprintf(NSD_LOG_LOW, "\tlen: %d, domain: %s\n", len, p);
if (len > MAXNAMELEN) {
return nispush_err(nd, 0, YPPUSH_BADARGS);
}
memcpy(nd->domain.string, p, len);
nd->domain.string[len] = (char)0;
nd->domain.len = len;
nd->domain.words = WORDS(len);
p += nd->domain.words;
len = ntohl(*p++);
nsd_logprintf(NSD_LOG_LOW, "\tlen: %d, table: %s\n", len, p);
if (len > MAXNAMELEN) {
return nispush_err(nd, 0, YPPUSH_BADARGS);
}
memcpy(nd->table.string, p, len);
nd->table.string[len] = (char)0;
nd->table.len = len;
nd->table.words = WORDS(nd->table.len);
p += nd->table.words;
/* we ignore ordernum */
p++;
len = ntohl(*p++);
nsd_logprintf(NSD_LOG_LOW, "\tlen: %d, key: %s\n", len, p);
if (len > MAXNAMELEN) {
return nispush_err(nd, 0, YPPUSH_BADARGS);
}
memcpy(nd->key.string, p, len);
nd->key.string[len] = (char)0;
nd->key.len = len;
nd->key.words = WORDS(len);
p += nd->key.words;
transid = ntohl(*p++);
_ltoa((long)transid, tidstr);
proto = ntohl(*p++);
_ltoa((long)proto, protostr);
len = ntohl(*p++);
nsd_logprintf(NSD_LOG_LOW, "\tlen:%d, name: %s\n", len, p);
if (len >= sizeof(name)) {
return nispush_err(nd, 0, YPPUSH_BADARGS);
}
memcpy(name, p, len);
name[len] = 0;
pid = fork();
switch (pid) {
case -1:
nsd_logprintf(NSD_LOG_RESOURCE, "nisserv_xfr: failed fork\n");
return nispush_err(nd, transid, YPPUSH_XFRERR);
case 0:
/*
** Child.
*/
for (i = 3; i < getdtablehi(); i++) {
close(i);
}
if (execl("/usr/sbin/ypxfr", "ypxfr", "-d", nd->domain.string,
"-C", tidstr, protostr, name, nd->table.string,
(char *)0) < 0) {
nsd_logprintf(NSD_LOG_RESOURCE,
"nisserv_xfr: failed exec\n");
}
nispush_err(nd, transid, YPPUSH_XFRERR);
exit(1);
}
/*
** Parent.
*/
return nisserv_null(rqp, nd, p);
}
/*
** This is just a notice that we should reopen all the hash files to
** keep from getting bad data. It would be nice if the protocol told
** us which specific file, but . . .
*/
static int
nisserv_clear(nsd_file_t **rqp, nisserv_t *nd, uint32_t *p)
{
nsd_logprintf(NSD_LOG_MIN, "entering nisserv_clear:\n");
hash_clear();
return nisserv_null(rqp, nd, p);
}
/*
** This routine returns the master server for the requested table and
** domain. This is stored as a key in the hash file. This only makes
** sense for the hash files so that is what we return.
*/
/*ARGSUSED*/
static int
nisserv_master(nsd_file_t **rqp, nisserv_t *nd, uint32_t *p)
{
int len;
hash_file_t *hf;
kvpair kv;
nsd_file_t *dp, *dp2;
struct stat sbuf;
nsd_logprintf(NSD_LOG_MIN, "entering nisserv_master:\n");
/*
** Arguments are a domain and a map. We walk from the top of
** the tree looking for these.
*/
len = ntohl(*p++);
if (len > MAXNAMELEN) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_BADARGS\n");
return nisserv_err(nd, YP_BADARGS);
}
nsd_logprintf(NSD_LOG_LOW, "\tlen = %d, domain = %s\n", len, p);
dp = __nsd_mounts;
if (! dp || !(dp = nsd_file_byname(dp->f_data, (char *)p, len))) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_NODOM\n");
return nisserv_err(nd, YP_NODOM);
}
p += WORDS(len);
len = ntohl(*p++);
if (len > MAXNAMELEN) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_BADARGS\n");
return nisserv_err(nd, YP_BADARGS);
}
nsd_logprintf(NSD_LOG_LOW, "\tlen = %d, map = %s\n", len, p);
if (! (dp = nsd_file_byname(dp->f_data, (char *)p, len))) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_NOMAP\n");
return nisserv_err(nd, YP_NOMAP);
}
/*
** The library name is ".nisserv" or ".ypserv". XXX - We should really
** walk through all the callouts testing the library as the name
** could change.
*/
if (! (dp2 = nsd_file_byname(dp->f_callouts, ".nisserv", 8))) {
if (! (dp2 = nsd_file_byname(dp->f_callouts, ".ypserv", 7))) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_NODOM\n");
return nisserv_err(nd, YP_NODOM);
}
}
dp = dp2;
/*
** Now look for the database file.
*/
hf = hash_get_file(dp);
if (! hf) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_NOMAP\n");
return nisserv_err(nd, YP_NOMAP);
}
if (stat(hf->file, &sbuf)) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_NOMAP\n");
return nisserv_err(nd, YP_NOMAP);
}
if ((! hf->map) || (hf->version < sbuf.st_mtime)
|| _Mdbm_invalid(hf->map)) {
if (! hash_reopen(hf)) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_BADDB\n");
return nisserv_err(nd, YP_BADDB);
}
hf->version = sbuf.st_mtime;
}
/*
** Pull out the magic key.
*/
kv.key.dptr = "YP_MASTER_NAME";
kv.key.dsize = sizeof("YP_MASTER_NAME") - 1;
kv.val.dsize = MDBM_PAGE_SIZE(hf->map);
kv.val.dptr = alloca(kv.val.dsize);
kv.val = mdbm_fetch(hf->map, kv);
if (! kv.val.dptr) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_NOKEY\n");
return nisserv_err(nd, YP_NOKEY);
}
/*
** Build the response.
*/
p = (uint32_t *)_nisserv_buf;
p += rpc_hdr(nd, p);
*p++ = htonl(TRUE);
*p++ = htonl(kv.val.dsize);
memcpy(p, kv.val.dptr, kv.val.dsize);
p += WORDS(kv.val.dsize);
reply(nd, _nisserv_buf, (char *)p - _nisserv_buf);
return NSD_CONTINUE;
}
/*ARGSUSED*/
static int
nisserv_order(nsd_file_t **rqp, nisserv_t *nd, uint32_t *p)
{
int len;
hash_file_t *hf;
kvpair kv;
nsd_file_t *dp, *dp2;
char buf[MAXNAMELEN];
struct stat sbuf;
nsd_logprintf(NSD_LOG_MIN, "entering nisserv_order:\n");
/*
** Arguments are a domain and a map. We walk from the top of
** the tree looking for these.
*/
len = ntohl(*p++);
if (len > MAXNAMELEN) {
return nisserv_err(nd, YP_BADARGS);
}
nsd_logprintf(NSD_LOG_LOW, "\tlen: %d, domain: %s\n", len, p);
dp = __nsd_mounts;
if (! dp || !(dp = nsd_file_byname(dp->f_data, (char *)p, len))) {
return nisserv_err(nd, YP_NODOM);
}
p += WORDS(len);
len = ntohl(*p++);
if (len > MAXNAMELEN) {
return nisserv_err(nd, YP_BADARGS);
}
nsd_logprintf(NSD_LOG_LOW, "\tlen: %d, table: %s\n", len, p);
if (! (dp = nsd_file_byname(dp->f_data, (char *)p, len))) {
return nisserv_err(nd, YP_NOMAP);
}
/*
** The library name is ".nisserv" or ".ypserv". XXX - We should really
** walk through all the callouts testing the library as the name
** could change.
*/
if (! (dp2 = nsd_file_byname(dp->f_callouts, ".nisserv", 8))) {
if (! (dp2 = nsd_file_byname(dp->f_callouts, ".ypserv", 7))) {
return nisserv_err(nd, YP_NODOM);
}
}
dp = dp2;
/*
** Now look for the database file.
*/
hf = hash_get_file(dp);
if (! hf) {
return nisserv_err(nd, YP_NOMAP);
}
if (stat(hf->file, &sbuf)) {
return nisserv_err(nd, YP_NOMAP);
}
if ((! hf->map) || (hf->version < sbuf.st_mtime)
|| _Mdbm_invalid(hf->map)) {
if (! hash_reopen(hf)) {
return nisserv_err(nd, YP_BADDB);
}
hf->version = sbuf.st_mtime;
}
/*
** Pull out the magic key.
*/
kv.key.dptr = "YP_LAST_MODIFIED";
kv.key.dsize = sizeof("YP_LAST_MODIFIED") - 1;
kv.val.dsize = MDBM_PAGE_SIZE(hf->map);
kv.val.dptr = alloca(kv.val.dsize);
kv.val = mdbm_fetch(hf->map, kv);
if (! kv.val.dptr) {
return nisserv_err(nd, YP_NOKEY);
}
memcpy(buf, kv.val.dptr, kv.val.dsize);
buf[kv.val.dsize] = (char)0;
/*
** Build the response.
*/
p = (uint32_t *)_nisserv_buf;
p += rpc_hdr(nd, p);
*p++ = htonl(TRUE);
*p++ = htonl(atol(buf));
reply(nd, _nisserv_buf, (char *)p - _nisserv_buf);
return NSD_CONTINUE;
}
/*ARGSUSED*/
static int
nisserv_maplist(nsd_file_t **rqp, nisserv_t *nd, uint32_t *p)
{
int len;
nsd_file_t *dp, *fp;
u_char *c;
uint32_t *u, last;
hash_file_t *hf;
char *table;
nsd_logprintf(NSD_LOG_MIN, "entering nisserv_maplist:\n");
/*
** The only argument is a domain. We walk the directory tree looking
** for the domain directory.
*/
len = (int)ntohl(*p++);
if (len > MAXNAMELEN) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_BADARGS\n");
return nisserv_err(nd, YP_BADARGS);
}
nsd_logprintf(NSD_LOG_LOW, "len = %d, domain = %s\n", len, p);
dp = __nsd_mounts;
if (! dp || !(dp = nsd_file_byname(dp->f_data, (char *)p, len))) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_NODOM\n");
return nisserv_err(nd, YP_NODOM);
}
/*
** Walk the directory appending each map name to the end of the
** response. XXX - We should not encode information about the
** directory format here.
*/
p = (uint32_t *)_nisserv_buf;
p += rpc_hdr(nd, p);
*p++ = htonl(TRUE);
for (c = (u_char *)dp->f_data; c && *c; c = (u_char *)(u + 1)) {
u = (uint32_t *)c;
u += WORDS(*c + 1);
if (*(c+1) == '.') {
continue;
}
if (*u == last) {
continue;
}
last = *u;
fp = nsd_file_byid(last);
if (! fp) {
continue;
}
hf = hash_get_file(fp);
if (! hf) {
continue;
}
if (access(hf->file, R_OK) < 0) {
continue;
}
table = nsd_attr_fetch_string(fp->f_attrs, "table", fp->f_name);
len = strlen(table);
if ((char *)&p[len + 3] >=
&_nisserv_buf[sizeof(_nisserv_buf)]) {
break;
}
*p++ = htonl(TRUE);
*p++ = htonl(len);
p[len/sizeof(*p)] = 0;
memcpy(p, table, len);
p += WORDS(len);
}
*p++ = FALSE;
reply(nd, _nisserv_buf, (char *)p - _nisserv_buf);
return NSD_CONTINUE;
}
/*
** This function simply clears the list of securenets.
*/
static void
securenets_clear(void)
{
securenets_t *sp;
for (sp = _nisserv_securenets; sp; sp = _nisserv_securenets) {
_nisserv_securenets = _nisserv_securenets->next;
free(sp);
}
}
/*
** This routine walks the list of securenets checking that this address
** is on one of them.
*/
int
nisserv_insecurenets(struct in_addr *addr)
{
securenets_t *sp;
if (! _nisserv_securenets || (addr->s_addr == INADDR_LOOPBACK)) {
return 1;
}
if (! addr) {
return 0;
}
for (sp = _nisserv_securenets; sp; sp = sp->next) {
if ((addr->s_addr & sp->netmask.s_addr) ==
(sp->addr.s_addr & sp->netmask.s_addr)) {
return 1;
}
}
return 0;
}
/*
** This routine just parses the securenets file and adds entries to
** the list.
*/
static int
securenets_parse(void)
{
FILE *fp;
securenets_t *sp;
char *p, *q;;
securenets_clear();
fp = fopen("/etc/securenets", "r");
if (! fp) {
nsd_logprintf(NSD_LOG_MAX,
"securenets_parse: failed to open securenets file\n");
return 0;
}
while (fgets(_nisserv_buf, sizeof(_nisserv_buf), fp) != NULL) {
for (p = _nisserv_buf; *p && isspace(*p); p++);
for (q = p; *q && !isspace(*q); q++);
if (! *q) {
nsd_logprintf(NSD_LOG_MAX,
"securenets_parse: no first space\n");
continue;
}
*q++ = 0;
sp = (securenets_t *)nsd_malloc(sizeof(*sp));
if (! sp) {
nsd_logprintf(NSD_LOG_RESOURCE,
"securenets_parse: failed malloc, securenets list will be incomplete\n");
fclose(fp);
return 0;
}
if (! inet_aton(p, &sp->netmask)) {
nsd_logprintf(NSD_LOG_MAX,
"securenets_parse: failed inet_aton for netmask\n");
free(sp);
continue;
}
for (p = q; *p && isspace(*p); p++);
for (q = p; *q && !isspace(*q); q++);
if (*q) {
*q = 0;
}
if (! inet_aton(p, &sp->addr)) {
nsd_logprintf(NSD_LOG_MAX,
"securenets_parse: failed inet_aton for address\n");
free(sp);
continue;
}
sp->next = _nisserv_securenets;
_nisserv_securenets = sp;
}
fclose(fp);
return 1;
}
/*
** This routine is called when we receive a packet of the udp port.
*/
static int
dispatch_udp(nsd_file_t **rqp, int fd)
{
uint32_t *p, xid, cmd;
nisserv_t *nd;
int len, nlen, i;
struct sockaddr_in sin;
struct in_addr dst;
mac_t lbl;
nsd_logprintf(NSD_LOG_MIN, "entering dispatch_udp:\n");
*rqp = 0;
/*
** Read in the packet.
*/
len = sizeof(sin);
memset(_nisserv_buf, 0, sizeof(_nisserv_buf));
nlen = recvfromto(fd, _nisserv_buf, sizeof(_nisserv_buf), 0,
(struct sockaddr *)&sin, len, &dst, &lbl);
if (nlen < 0) {
nsd_logprintf(NSD_LOG_RESOURCE,
"dispatch_udp: failed recvfrom: %s\n", strerror(errno));
return NSD_CONTINUE;
}
/*
** If this is not coming from one of our supported networks then we
** ignore the request.
*/
if (! nisserv_insecurenets(&sin.sin_addr)) {
nsd_logprintf(NSD_LOG_MIN,
"dispatch_udp: request from insecure network %s\n",
inet_ntoa(sin.sin_addr));
return NSD_CONTINUE;
}
p = (uint32_t *)_nisserv_buf;
xid = ntohl(p[0]);
if (ntohl(p[1]) != CALL) {
mac_free(lbl);
return NSD_CONTINUE;
}
nd = nisserv_data_new();
if (! nd) {
mac_free(lbl);
return NSD_CONTINUE;
}
nd->proto = IPPROTO_UDP;
nd->xid = xid;
nd->fd = fd;
nd->lbl = lbl;
nd->sin = sin;
nd->local = dst;
if (ntohl(p[2]) != RPC_MSG_VERSION) {
return reject_err(nd, RPC_MISMATCH);
}
if ((ntohl(p[3]) != YPPROG) || (ntohl(p[4]) != YPVERS)) {
return accept_err(nd, PROG_MISMATCH);
}
cmd = ntohl(p[5]);
nd->cmd = cmd;
/*
** Remember credentials.
*/
switch (ntohl(p[6])) {
case AUTH_NONE:
nd->cred = nsd_cred_new(0, 1, 0);
if (! nd->cred) {
return accept_err(nd, SYSTEM_ERR);
}
p += WORDS(ntohl(p[7]));
p += 8;
break;
case AUTH_UNIX:
p += WORDS(ntohl(p[9]));
p += 10;
nd->cred = nsd_cred_new((uid_t)ntohl(p[0]), 0);
if (! nd->cred) {
return accept_err(nd, SYSTEM_ERR);
}
nd->cred->c_gid[0] = (gid_t)ntohl(p[1]);
nd->cred->c_gids = ntohl(p[2]);
for (i = 0; i < nd->cred->c_gids; i++) {
nd->cred->c_gid[i+1] = (gid_t)ntohl(p[i+3]);
}
p += (i + 3);
break;
case AUTH_DES:
nsd_logprintf(NSD_LOG_OPER,
"dispatch_udp: received AUTH_DES authorization\n");
return reject_err(nd, AUTH_FAILED);
default:
nsd_logprintf(NSD_LOG_OPER,
"dispatch_udp: received unknown authentication type 0x%x\n",
ntohl(p[6]));
return reject_err(nd, AUTH_FAILED);
}
/* skip verf */
p += WORDS(ntohl(p[1]));
p += 2;
switch (cmd) {
case YPPROC_NULL:
return nisserv_null(rqp, nd, p);
case YPPROC_DOMAIN:
return nisserv_domain(rqp, nd, p, 1);
case YPPROC_DOMAIN_NONACK:
return nisserv_domain(rqp, nd, p, 0);
case YPPROC_MATCH:
return nisserv_match(rqp, nd, p);
case YPPROC_FIRST:
return nisserv_first(rqp, nd, p);
case YPPROC_NEXT:
return nisserv_next(rqp, nd, p);
case YPPROC_XFR:
return nisserv_xfr(rqp, nd, p);
case YPPROC_CLEAR:
return nisserv_clear(rqp, nd, p);
case YPPROC_ALL:
/* We do not support yp_all over udp. */
return nisserv_err(nd, YP_BADOP);
case YPPROC_MASTER:
return nisserv_master(rqp, nd, p);
case YPPROC_ORDER:
return nisserv_order(rqp, nd, p);
case YPPROC_MAPLIST:
return nisserv_maplist(rqp, nd, p);
case YPPROC_NEWXFR:
return nisserv_newxfr(rqp, nd, p);
}
return accept_err(nd, PROC_UNAVAIL);
}
/*
** This routine is called when we receive a request packet on the tcp port.
** This routine if horribly complex because it is reintrant. If we do
** not have enough data to complete the next part of the process then we
** save state and fallback to the select loop until it arrives. When we
** get back here we start back where we left off.
*/
/* ARGSUSED */
static int
nisserv_all(nsd_file_t **rqp, nisserv_t *nd, uint32_t *p)
{
int len, qlen;
nsd_file_t *dp, *dp2;
uint32_t *end = (uint32_t *)(_nisserv_buf + 1600);
hash_file_t *hf;
struct stat sbuf;
kvpair kv;
int pagesize=0;
char *key, *val, *q;
switch(nd->status) {
case 0:
case 1:
case 2:
/* Huh? */
case 3:
len = ntohl(*p++);
if (len > MAXNAMELEN) {
nsd_logprintf(NSD_LOG_LOW,
"nisserv_all: domain too long\n");
return nisserv_err(nd, YP_BADARGS);
}
memcpy(nd->domain.string, p, len);
nd->domain.string[len] = 0;
nd->domain.len = len;
nd->domain.words = WORDS(len);
nsd_logprintf(NSD_LOG_LOW, "\tdomain = %s\n",
nd->domain.string);
/*
** We do not serve from the .local domain.
*/
if (strcasecmp(nd->domain.string, NS_DOMAIN_LOCAL) == 0) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_NODOM\n");
return nisserv_err(nd, YP_NODOM);
}
p += nd->domain.words;
len = ntohl(*p++);
if (len > MAXNAMELEN) {
nsd_logprintf(NSD_LOG_LOW,
"\treturning YP_BADARGS 2\n");
return nisserv_err(nd, YP_BADARGS);
}
memcpy(nd->table.string, p, len);
nd->table.string[len] = 0;
nd->table.len = len;
nd->table.words = WORDS(len);
nsd_logprintf(NSD_LOG_LOW, "\ttable = %s\n", nd->table.string);
p += nd->table.words;
/*
** Done parsing request.
*/
free(nd->data);
nd->data = 0;
nd->size = nd->len = 0;
/*
** Walk the tree looking for the callout directory.
*/
if (! __nsd_mounts ||
! (dp = nsd_file_byname(__nsd_mounts->f_data,
nd->domain.string, nd->domain.len))) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_NODOM\n");
return nisserv_err(nd, YP_NODOM);
}
dp = nsd_file_byname(dp->f_data, nd->table.string,
nd->table.len);
if (! dp) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_NOMAP\n");
return nisserv_err(nd, YP_NOMAP);
}
dp2 = nsd_file_byname(dp->f_callouts, ".nisserv", 8);
if (! dp2) {
dp2 = nsd_file_byname(dp->f_callouts, ".ypserv", 7);
if (! dp2) {
nsd_logprintf(NSD_LOG_LOW,
"\treturning YP_NODOM\n");
return nisserv_err(nd, YP_NODOM);
}
}
dp = dp2;
if (! nis_check(nd->cred, dp)) {
nsd_logprintf(NSD_LOG_LOW, "\tpermission denied\n");
return nisserv_err(nd, YP_NODOM);
}
/*
** Check the nis_secure argument to see if we can accept
** this request.
*/
if (nsd_attr_fetch_bool(dp->f_attrs, "nis_secure", 0) &&
ntohs(nd->sin.sin_port) >= 1024) {
nsd_logprintf(NSD_LOG_LOW, "\tReturning YP_NOMAP\n");
return nisserv_err(nd, YP_NOMAP);
}
/*
** Now look for the database file, and reopen it if needed.
*/
hf = hash_get_file(dp);
if (! hf) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_NOMAP\n");
return nisserv_err(nd, YP_NOMAP);
}
if (stat(hf->file, &sbuf)) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_NOMAP 2\n");
return nisserv_err(nd, YP_NOMAP);
}
if ((! hf->map) || (hf->version < sbuf.st_mtime)
|| _Mdbm_invalid(hf->map)) {
if (! hash_reopen(hf)) {
nsd_logprintf(NSD_LOG_LOW,
"\treturning YP_BADDB 3\n");
return nisserv_err(nd, YP_BADDB);
}
hf->version = sbuf.st_mtime;
}
if (! pagesize) {
pagesize = MDBM_PAGE_SIZE(hf->map);
key = alloca(pagesize);
val = alloca(pagesize);
}
/*
** We may sleep when the socket buffer fills so we need
** a callback to deal with when the buffer empties.
*/
nsd_callback_new(nd->fd, dispatch_tcp, NSD_WRITE);
/*
** Build out-going header.
*/
p = (uint32_t *)_nisserv_buf;
p++;
p += rpc_hdr(nd, p);
break;
case 4:
/*
** We get here if we were in the middle of writing data
** and found we would block. We reopen the hash file if
** needed, then jump back to where we were.
*/
/*
** Walk the tree looking for the callout directory.
*/
if (! __nsd_mounts ||
! (dp = nsd_file_byname(__nsd_mounts->f_data,
nd->domain.string, nd->domain.len))) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_NODOM\n");
goto tcperror;
}
dp = nsd_file_byname(dp->f_data, nd->table.string,
nd->table.len);
if (! dp) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_NOMAP\n");
goto tcperror;
}
dp2 = nsd_file_byname(dp->f_callouts, ".nisserv", 8);
if (! dp2) {
dp2 = nsd_file_byname(dp->f_callouts, ".ypserv", 7);
if (! dp2) {
nsd_logprintf(NSD_LOG_LOW,
"\treturning YP_NODOM\n");
goto tcperror;
}
}
dp = dp2;
if (! nis_check(nd->cred, dp)) {
nsd_logprintf(NSD_LOG_LOW, "\tpermission denied\n");
goto tcperror;
}
/*
** Now look for the database file, and reopen it if needed.
*/
hf = hash_get_file(dp);
if (! hf) {
nsd_logprintf(NSD_LOG_MIN,
"dispatch_tcp: got NULL hash pointer\n");
goto tcperror;
}
if (stat(hf->file, &sbuf)) {
nsd_logprintf(NSD_LOG_LOW, "\treturning YP_NOMAP 2\n");
return nisserv_err(nd, YP_NOMAP);
}
if ((! hf->map) || (hf->version < sbuf.st_mtime)
|| _Mdbm_invalid(hf->map)) {
if (! hash_reopen(hf)) {
nsd_logprintf(NSD_LOG_RESOURCE,
"\tfailed to reopen hash file\n");
goto tcperror;
}
hf->version = sbuf.st_mtime;
}
if (! pagesize) {
pagesize = MDBM_PAGE_SIZE(hf->map);
key = alloca(pagesize);
val = alloca(pagesize);
}
/*
** Lookup saved key in database to reset hints.
*/
kv.key.dptr = nd->key.string;
kv.key.dsize = nd->key.len;
kv.val.dsize = 0;
kv.val.dptr = 0;
mdbm_fetch(hf->map, kv);
if (nd->data && (nd->size > 0)) {
memcpy(_nisserv_buf, nd->data, nd->size);
free(nd->data);
nd->data = (char *)0;
}
len = nd->size;
nd->size = 0;
nsd_logprintf(NSD_LOG_LOW, "len = %d, nd->len = %d\n",
len, nd->len);
p = (uint32_t *)(_nisserv_buf + len);
q = _nisserv_buf + nd->len;
goto writeagain;
case 5:
goto writeend;
}
/*
** Walk through the hash file printing each entry. We write
** out 4KB at a time.
*/
for (kv.key.dptr = key , kv.key.dsize = pagesize,
kv.val.dptr = val , kv.val.dsize = pagesize,
kv = mdbm_first(hf->map,kv);
kv.key.dptr;
kv.key.dptr = key , kv.key.dsize = pagesize,
kv.val.dptr = val , kv.val.dsize = pagesize,
kv = mdbm_next(hf->map,kv)) {
/* Skip private records. */
if ((kv.key.dsize == 0) || ((kv.key.dsize > 3) &&
(strncmp(kv.key.dptr, "YP_", 3) == 0))) {
continue;
}
*p++ = htonl(TRUE);
*p++ = htonl(TRUE);
*p++ = htonl(kv.val.dsize);
if (kv.val.dsize > 0) {
p[kv.val.dsize/sizeof(*p)] = 0;
memcpy(p, kv.val.dptr, kv.val.dsize);
p += WORDS(kv.val.dsize);
}
*p++ = htonl(kv.key.dsize);
if (kv.key.dsize > 0) {
p[kv.key.dsize/sizeof(*p)] = 0;
memcpy(p, kv.key.dptr, kv.key.dsize);
p += WORDS(kv.key.dsize);
}
if (p >= end) {
q = _nisserv_buf;
*(uint32_t *)_nisserv_buf =
htonl((char *)end - q - sizeof(*p));
writeagain:
len = (char *)end - q;
qlen = write(nd->fd, q, len);
if (qlen < len) {
if (qlen < 0) {
if (errno == EWOULDBLOCK) {
nd->len = q - _nisserv_buf;
len = (char *)p - _nisserv_buf;
p = (uint32_t *)_nisserv_buf;
nd->status = 4;
memcpy(nd->key.string,
kv.key.dptr, kv.key.dsize);
nd->key.len = kv.key.dsize;
goto needmore;
} else {
nsd_logprintf(NSD_LOG_LOW,
"\terror in write: %s\n",
strerror(errno));
goto tcperror;
}
}
q += qlen;
goto writeagain;
}
len = (p - end) * sizeof(*p);
if (len > 0) {
memcpy(_nisserv_buf + sizeof(*p), end, len);
p = (uint32_t *)(_nisserv_buf + len);
} else {
p = (uint32_t *)_nisserv_buf;
}
p++;
}
}
*p++ = htonl(FALSE);
len = (char *)p - _nisserv_buf;
p = (uint32_t *)_nisserv_buf;
*p = htonl(0x80000000 + len - sizeof(*p));
writeend:
qlen = write(nd->fd, p, len);
if (qlen < len) {
if (qlen < 0) {
if (errno == EWOULDBLOCK) {
nd->status = 5;
goto needmore;
} else {
nsd_logprintf(NSD_LOG_LOW,
"\terror on final write: %s\n",
strerror(errno));
goto tcperror;
}
}
len -= qlen;
p += qlen/sizeof(*p);
goto writeend;
}
nsd_callback_remove(nd->fd);
close(nd->fd);
nisserv_data_clear(&nd);
return NSD_CONTINUE;
tcperror:
nsd_callback_remove(nd->fd);
close(nd->fd);
if (nd) {
nisserv_data_clear(&nd);
}
nsd_logprintf(NSD_LOG_LOW, "\treturning NSD_CONTINUE\n");
return NSD_CONTINUE;
needmore:
nsd_timeout_new((nsd_file_t *)nd, 30000, nisserv_timeout, (void *)nd);
if (len > 0) {
nd->data = (char *)nsd_malloc(len);
if (! nd->data) {
nsd_callback_remove(nd->fd);
close(nd->fd);
nsd_timeout_remove((nsd_file_t *)nd);
nisserv_data_clear(&nd);
return NSD_CONTINUE;
}
memcpy(nd->data, p, len);
nd->size = len;
nsd_logprintf(NSD_LOG_LOW,
"\treturning NSD_CONTINUE: needmore, status = %d\n",
nd->status);
return NSD_CONTINUE;
}
nsd_logprintf(NSD_LOG_LOW,
"\treturning NSD_CONTINUE: needmore 2, status = %d\n", nd->status);
return NSD_CONTINUE;
}
/*
** This routine reads in an entire request then hands it off to be
** processed by another.
*/
static int
dispatch_tcp(nsd_file_t **rqp, int fd)
{
nisserv_t *nd;
int n, i;
u_int32_t *p=0;
struct sockaddr_in sin;
nsd_logprintf(NSD_LOG_MIN, "entering dispatch_tcp:\n");
*rqp = 0;
/*
** Locate NIS data for connection.
*/
nd = nisserv_data_byfd(fd);
if (! nd) {
nsd_logprintf(NSD_LOG_LOW,
"dispatch_tcp: Cannot find request structure for "
"file descriptor: %d\n", fd);
goto disperr;
}
nsd_timeout_remove((nsd_file_t *)nd);
switch (nd->status) {
case 0:
/*
** First time through. We need to determine the size of
** the request and read in as much as we can.
*/
n = read(fd, &nd->size, sizeof(nd->size));
if (n < sizeof(nd->size)) {
nsd_logprintf(NSD_LOG_LOW,
"dispatch_tcp: read failed: %s\n",
strerror(errno));
goto disperr;
}
if (nd->size & 0x80000000) {
nd->size &= ~0x80000000;
} else {
nsd_logprintf(NSD_LOG_LOW,
"dispatch_tcp: continuation packet on request\n");
goto disperr;
}
if (nd->size > 4096) {
nsd_logprintf(NSD_LOG_LOW,
"dispatch_tcp: request too large\n");
return reject_err(nd, GARBAGE_ARGS);
}
nd->data = nsd_calloc(1, nd->size);
if (! nd->data) {
nsd_logprintf(NSD_LOG_LOW,
"dispatch_tcp: failed malloc\n");
return accept_err(nd, SYSTEM_ERR);
}
nd->size -= sizeof(nd->size);
case 1:
/*
** We have started to read in the packet. Here we read
** as much as is available.
*/
n = read(fd, nd->data + nd->len,
nd->size + sizeof(nd->size) - nd->len);
switch (n) {
case -1:
if (errno != EWOULDBLOCK) {
nsd_logprintf(NSD_LOG_LOW,
"dispatch_tcp: read failed: %s\n",
strerror(errno));
goto disperr;
}
n = 0;
break;
case 0:
goto disperr;
}
nd->len += n;
if (nd->len < nd->size) {
nd->status = 1;
nsd_timeout_new((nsd_file_t *)nd, 5000,
nisserv_timeout, (void *)nd);
nsd_logprintf(NSD_LOG_HIGH,
"dispatch_tcp: incomplete read, waiting "
"for more: %d of %d\n", nd->len, nd->size);
return NSD_CONTINUE;
}
case 2:
n = sizeof(sin);
if (getpeername(fd, &sin, &n) < 0) {
nsd_logprintf(NSD_LOG_LOW,
"dispatch_tcp: getpeername failed: %s\n",
strerror(errno));
return reject_err(nd, AUTH_FAILED);
}
/*
** If this is not coming from one of our supported networks
** then we ignore the request.
*/
if (! nisserv_insecurenets(&sin.sin_addr)) {
nsd_logprintf(NSD_LOG_MIN,
"dispatch_udp: request from insecure network %s\n",
inet_ntoa(sin.sin_addr));
return reject_err(nd, AUTH_FAILED);
}
p = (uint32_t *)nd->data;
nd->xid = ntohl(p[0]);
if (ntohl(p[1]) != CALL) {
return NSD_CONTINUE;
}
nd->fd = fd;
nd->sin = sin;
if (ntohl(p[2]) != RPC_MSG_VERSION) {
return reject_err(nd, RPC_MISMATCH);
}
if ((ntohl(p[3]) != YPPROG) || (ntohl(p[4]) != YPVERS)) {
return accept_err(nd, PROG_MISMATCH);
}
nd->cmd = ntohl(p[5]);
/*
** Remember credentials.
*/
switch (ntohl(p[6])) {
case AUTH_NONE:
nd->cred = nsd_cred_new(0, 1, 0);
if (! nd->cred) {
return accept_err(nd, SYSTEM_ERR);
}
p += WORDS(ntohl(p[7]));
p += 8;
break;
case AUTH_UNIX:
p += WORDS(ntohl(p[9]));
p += 10;
nd->cred = nsd_cred_new((uid_t)ntohl(p[0]), 0);
if (! nd->cred) {
return accept_err(nd, SYSTEM_ERR);
}
nd->cred->c_gid[0] = (gid_t)ntohl(p[1]);
nd->cred->c_gids = ntohl(p[2]);
for (i = 0; i < nd->cred->c_gids; i++) {
nd->cred->c_gid[i+1] = (gid_t)ntohl(p[i+3]);
}
p += (i + 3);
break;
case AUTH_DES:
nsd_logprintf(NSD_LOG_OPER,
"dispatch_udp: received AUTH_DES authorization\n");
return reject_err(nd, AUTH_FAILED);
default:
nsd_logprintf(NSD_LOG_OPER,
"dispatch_udp: received unknown authentication "
"type 0x%x\n", ntohl(p[6]));
return reject_err(nd, AUTH_FAILED);
}
/* skip verf */
p += WORDS(ntohl(p[1]));
p += 2;
nd->status = 3;
nsd_callback_remove(fd);
}
switch (nd->cmd) {
case YPPROC_NULL:
return nisserv_null(rqp, nd, p);
case YPPROC_DOMAIN:
return nisserv_domain(rqp, nd, p, 1);
case YPPROC_DOMAIN_NONACK:
return nisserv_domain(rqp, nd, p, 0);
case YPPROC_MATCH:
return nisserv_match(rqp, nd, p);
case YPPROC_FIRST:
return nisserv_first(rqp, nd, p);
case YPPROC_NEXT:
return nisserv_next(rqp, nd, p);
case YPPROC_XFR:
return nisserv_xfr(rqp, nd, p);
case YPPROC_CLEAR:
return nisserv_clear(rqp, nd, p);
case YPPROC_ALL:
return nisserv_all(rqp, nd, p);
case YPPROC_MASTER:
return nisserv_master(rqp, nd, p);
case YPPROC_ORDER:
return nisserv_order(rqp, nd, p);
case YPPROC_MAPLIST:
return nisserv_maplist(rqp, nd, p);
case YPPROC_NEWXFR:
return nisserv_newxfr(rqp, nd, p);
}
return accept_err(nd, PROC_UNAVAIL);
disperr:
nsd_callback_remove(fd);
close(fd);
if (nd) {
nisserv_data_clear(&nd);
}
nsd_logprintf(NSD_LOG_LOW, "\treturning NSD_CONTINUE\n");
return NSD_CONTINUE;
}
/*
** This routine just handles setting up a TCP connection.
*/
/*ARGSUSED*/
static int
accept_tcp(nsd_file_t **rqp, int fd)
{
int rsock, n;
struct sockaddr_in sin;
nisserv_t *nd;
nsd_logprintf(NSD_LOG_MIN, "entering accept_tcp:\n");
/*
** We have fallen out of select so we must have a connection
** ready to be accepted on.
*/
n = sizeof(sin);
rsock = accept(fd, &sin, &n);
if (rsock < 0) {
nsd_logprintf(NSD_LOG_RESOURCE,
"accept_tcp: error in accept: %s\n", strerror(errno));
return NSD_CONTINUE;
}
if (! nisserv_insecurenets(&sin.sin_addr)) {
nsd_logprintf(NSD_LOG_MIN,
"accept_tcp: request from insecure network %s\n",
inet_ntoa(sin.sin_addr));
close(rsock);
return NSD_CONTINUE;
}
nd = nisserv_data_new();
if (! nd) {
nsd_logprintf(NSD_LOG_RESOURCE, "accept_tcp: malloc failed\n");
close(rsock);
return NSD_CONTINUE;
}
nd->fd = rsock;
nd->proto = IPPROTO_TCP;
memcpy(&nd->sin, &sin, sizeof(nd->sin));
if (tsix_get_mac(nd->fd, &nd->lbl) == -1) {
nsd_logprintf(NSD_LOG_RESOURCE,
"accept_tcp: tsix_get_mac failed\n");
nisserv_data_clear(&nd);
close(rsock);
return NSD_CONTINUE;
}
if (tsix_set_mac(nd->fd, nd->lbl) == -1) {
nsd_logprintf(NSD_LOG_RESOURCE,
"accept_tcp: tsix_set_mac failed\n");
nisserv_data_clear(&nd);
close(rsock);
return NSD_CONTINUE;
}
nsd_timeout_new((nsd_file_t *)nd, 5000, nisserv_timeout, (void *)nd);
nsd_callback_new(rsock, dispatch_tcp, NSD_READ);
return NSD_CONTINUE;
}
/*
** Check if the nis server should run.
*/
static int
chkconfig_ypserv(void) {
char buf[16];
int fd;
fd = nsd_open("/etc/config/ypserv", O_RDONLY, 0);
if (fd < 0) {
nsd_logprintf(NSD_LOG_CRIT,
"ypserv is not enabled by chkconfig\n");
return FALSE;
}
if (read(fd, buf, sizeof(buf)) < 0) {
close(fd);
nsd_logprintf(NSD_LOG_RESOURCE,"failed to chkconfig ypserv\n");
return FALSE;
}
close(fd);
if (strncasecmp(buf, "on", 2) == 0) {
return TRUE;
} else {
nsd_logprintf(NSD_LOG_CRIT,
"ypserv is not enabled by chkconfig\n");
return FALSE;
}
}
/*
** The init function is called when the library is first opened or when
** the daemon receives a signal. We just set the state back to startup
** by freeing all the cached files, open up sockets for the NIS service
** and register the service with the portmapper.
*/
/* ARGSUSED */
int
init(char *map)
{
struct sockaddr_in sin;
int len = sizeof(struct sockaddr_in);
struct timeval t;
int n;
nsd_logprintf(NSD_LOG_MIN, "entering init (nisserv):\n");
/*
** If the old ypserv chkconfig esists then we believe it.
*/
if (! chkconfig_ypserv()) {
return NSD_ERROR;
}
hash_clear();
gettimeofday(&t);
_nsdata_xid = getpid() ^ t.tv_sec ^ t.tv_usec;
if (gethostname(_nisserv_host.string,
sizeof(_nisserv_host.string)) < 0) {
strcpy(_nisserv_host.string, "IRIS");
_nisserv_host.len = 4;
_nisserv_host.words = 1;
} else {
_nisserv_host.len = strlen(_nisserv_host.string);
_nisserv_host.words = WORDS(_nisserv_host.len);
}
_nisserv_euid = geteuid();
_nisserv_egid = getegid();
/*
** Setup service sockets, bind them to reserved port, and register
** the services with the portmapper.
*/
if (_nisserv_udp < 0) {
_nisserv_udp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (_nisserv_udp < 0) {
nsd_logprintf(NSD_LOG_RESOURCE,
"init (nisserv): failed to create UDP socket: %s\n",
strerror(errno));
return NSD_ERROR;
}
if (tsix_on(_nisserv_udp) == -1) {
close(_nisserv_udp);
_nisserv_udp = -1;
return NSD_ERROR;
}
}
memset(&sin, 0, len);
sin.sin_family = AF_INET;
if (bindresvport(_nisserv_udp, &sin) < 0) {
sin.sin_port = 0;
bind(_nisserv_udp, (struct sockaddr *)&sin, len);
}
if (getsockname(_nisserv_udp, (struct sockaddr *)&sin, &len) < 0) {
nsd_logprintf(NSD_LOG_RESOURCE,
"init (nisserv): failed to bind UDP socket: %s\n",
strerror(errno));
close(_nisserv_udp);
_nisserv_udp = -1;
return NSD_ERROR;
}
n = 1;
setsockopt(_nisserv_udp, IPPROTO_IP, IP_RECVDSTADDR, &n, sizeof(n));
if (! __nsd_mounts) {
nsd_portmap_unregister(YPPROG, YPVERS);
}
nsd_portmap_register(YPPROG, YPVERS, IPPROTO_UDP, sin.sin_port);
nsd_callback_new(_nisserv_udp, dispatch_udp, NSD_READ);
nsd_logprintf(NSD_LOG_LOW, "Added nisserv UDP on port %d\n",
sin.sin_port);
if (_nisserv_tcp < 0) {
_nisserv_tcp = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (_nisserv_tcp < 0) {
nsd_logprintf(NSD_LOG_RESOURCE,
"init (nisserv): failed to create TCP socket: %s\n",
strerror(errno));
return NSD_ERROR;
}
if (tsix_on(_nisserv_tcp) == -1) {
close(_nisserv_tcp);
_nisserv_tcp = -1;
return NSD_ERROR;
}
}
len = sizeof(struct sockaddr_in);
memset(&sin, 0, len);
sin.sin_family = AF_INET;
if (bindresvport(_nisserv_tcp, &sin) < 0) {
sin.sin_port = 0;
bind(_nisserv_tcp, (struct sockaddr *)&sin, len);
}
if (getsockname(_nisserv_tcp, (struct sockaddr *)&sin, &len) < 0) {
nsd_logprintf(NSD_LOG_RESOURCE,
"init (nisserv): failed to bind TCP socket: %s\n",
strerror(errno));
close(_nisserv_tcp);
_nisserv_tcp = -1;
return NSD_ERROR;
}
/*
** Set non-blocking I/O for the stream socket. We continue if this
** fails, but we may block later.
*/
if (fcntl(_nisserv_tcp, F_SETFL, FNDELAY) < 0) {
nsd_logprintf(NSD_LOG_RESOURCE,
"init (nisserv): failed to set tcp socket for non-blocking I/O\n");
}
n = 4096;
if (setsockopt(_nisserv_tcp, SOL_SOCKET, SO_SNDBUF, &n,
sizeof(n)) < 0) {
nsd_logprintf(NSD_LOG_LOW,
"accept_tcp: setsockopt failed: %s\n", strerror(errno));
}
nsd_portmap_register(YPPROG, YPVERS, IPPROTO_TCP, sin.sin_port);
nsd_callback_new(_nisserv_tcp, accept_tcp, NSD_READ);
if (listen(_nisserv_tcp, 100) < 0) {
nsd_logprintf(NSD_LOG_RESOURCE,
"init (nisserv): listen failed: %s\n", strerror(errno));
return NSD_ERROR;
}
nsd_logprintf(NSD_LOG_LOW, "Added nisserv TCP on port %d\n",
sin.sin_port);
securenets_parse();
return NSD_OK;
}