#include "mt.h"
#include <stdlib.h>
#include <unistd.h>
#include "../rpc/rpc_mt.h"
#include <rpc/rpc.h>
#include <sys/types.h>
#include "yp_b.h"
#include <rpcsvc/yp_prot.h>
#include <rpcsvc/ypclnt.h>
#include <malloc.h>
#include <string.h>
#include <sys/time.h>
extern int __yp_dobind_cflookup(char *, struct dom_binding **, int);
extern int __yp_dobind_rsvdport_cflookup(char *, struct dom_binding **, int);
static int domatch(char *, char *, char *, int, struct dom_binding *,
struct timeval *, char **, int *);
int yp_match_rsvdport();
int yp_match_rsvdport_cflookup();
struct cache {
struct cache *next;
unsigned int birth;
char *domain;
char *map;
char *key;
int keylen;
char *val;
int vallen;
};
static mutex_t cache_lock = DEFAULTMUTEX;
static int generation;
static struct cache *head;
#define CACHESZ 16
#define CACHETO 600
static void
freenode(struct cache *n)
{
if (n->val != 0)
free(n->val);
if (n->key != 0)
free(n->key);
if (n->map != 0)
free(n->map);
if (n->domain != 0)
free(n->domain);
free(n);
}
static struct cache *
makenode(char *domain, char *map, int keylen, int vallen)
{
struct cache *n;
if (strncmp(map, "passwd", 6) == 0)
return (0);
if ((n = calloc(1, sizeof (*n))) == 0)
return (0);
if (((n->domain = strdup(domain)) == 0) ||
((n->map = strdup(map)) == 0) ||
((n->key = malloc(keylen)) == 0) ||
((n->val = malloc(vallen)) == 0)) {
freenode(n);
return (0);
}
return (n);
}
static int
in_cache(char *domain, char *map, char *key, int keylen, char **val,
int *vallen)
{
struct cache *c, **pp;
int cnt;
struct timeval now;
struct timezone tz;
if (strncmp(map, "passwd", 6) == 0)
return (0);
for (pp = &head, cnt = 0; (c = *pp) != 0; pp = &c->next, cnt++) {
if ((c->keylen == keylen) &&
(memcmp(key, c->key, (size_t)keylen) == 0) &&
(strcmp(map, c->map) == 0) &&
(strcmp(domain, c->domain) == 0)) {
(void) gettimeofday(&now, &tz);
if ((now.tv_sec - c->birth) > CACHETO) {
*pp = c->next;
freenode(c);
break;
} else {
*val = c->val;
*vallen = c->vallen;
*pp = c->next;
c->next = head;
head = c;
return (1);
}
}
if (cnt >= CACHESZ) {
*pp = c->next;
freenode(c);
break;
}
}
return (0);
}
int
__yp_match_cflookup(char *domain, char *map, char *key, int keylen, char **val,
int *vallen, int hardlookup)
{
size_t domlen;
size_t maplen;
int reason;
struct dom_binding *pdomb;
int savesize;
struct timeval now;
struct timezone tz;
char *my_val;
int my_vallen;
int found_it;
int cachegen;
if ((map == NULL) || (domain == NULL))
return (YPERR_BADARGS);
domlen = strlen(domain);
maplen = strlen(map);
if ((domlen == 0) || (domlen > YPMAXDOMAIN) ||
(maplen == 0) || (maplen > YPMAXMAP) ||
(key == NULL) || (keylen == 0))
return (YPERR_BADARGS);
(void) mutex_lock(&cache_lock);
found_it = in_cache(domain, map, key, keylen, &my_val, &my_vallen);
cachegen = generation;
if (found_it) {
savesize = my_vallen + 2;
if ((*val = malloc((size_t)savesize)) == 0) {
(void) mutex_unlock(&cache_lock);
return (YPERR_RESRC);
}
(void) memcpy(*val, my_val, (size_t)savesize);
*vallen = my_vallen;
(void) mutex_unlock(&cache_lock);
return (0);
}
(void) mutex_unlock(&cache_lock);
for (;;) {
if (reason = __yp_dobind_cflookup(domain, &pdomb, hardlookup))
return (reason);
if (pdomb->dom_binding->ypbind_hi_vers >= YPVERS) {
reason = domatch(domain, map, key, keylen, pdomb,
&_ypserv_timeout, val, vallen);
__yp_rel_binding(pdomb);
if (reason == YPERR_RPC || reason == YPERR_YPSERV ||
reason == YPERR_BUSY ) {
yp_unbind(domain);
if (hardlookup)
(void) sleep(_ypsleeptime);
else
return (reason);
} else
break;
} else {
__yp_rel_binding(pdomb);
return (YPERR_VERS);
}
}
if (reason == 0) {
(void) mutex_lock(&cache_lock);
if (generation != cachegen &&
in_cache(domain, map, key, keylen, &my_val, &my_vallen)) {
} else {
struct cache *c;
savesize = *vallen + 2;
c = makenode(domain, map, keylen, savesize);
if (c != 0) {
(void) gettimeofday(&now, &tz);
c->birth = now.tv_sec;
c->keylen = keylen;
c->vallen = *vallen;
(void) memcpy(c->key, key, (size_t)keylen);
(void) memcpy(c->val, *val, (size_t)savesize);
c->next = head;
head = c;
++generation;
}
}
(void) mutex_unlock(&cache_lock);
} else if (reason == YPERR_MAP && geteuid() == 0) {
int rsvdreason;
rsvdreason = yp_match_rsvdport(domain, map, key, keylen, val,
vallen);
if (rsvdreason == 0)
reason = rsvdreason;
}
return (reason);
}
int
yp_match(
char *domain,
char *map,
char *key,
int keylen,
char **val,
int *vallen)
{
return (__yp_match_cflookup(domain, map, key, keylen, val, vallen, 1));
}
extern void
__empty_yp_cache(void)
{
struct cache *p, *n;
(void) mutex_lock(&cache_lock);
p = head;
head = 0;
(void) mutex_unlock(&cache_lock);
if (p == 0)
return;
n = p->next;
while (p) {
freenode(p);
p = n;
if (p)
n = p->next;
}
}
int
__yp_match_rsvdport_cflookup(
char *domain,
char *map,
char *key,
int keylen,
char **val,
int *vallen,
int hardlookup)
{
size_t domlen;
size_t maplen;
int reason;
struct dom_binding *pdomb;
int savesize;
struct timeval now;
struct timezone tz;
char *my_val;
int my_vallen;
int found_it;
int cachegen;
if ((map == NULL) || (domain == NULL))
return (YPERR_BADARGS);
domlen = strlen(domain);
maplen = strlen(map);
if ((domlen == 0) || (domlen > YPMAXDOMAIN) ||
(maplen == 0) || (maplen > YPMAXMAP) ||
(key == NULL) || (keylen == 0))
return (YPERR_BADARGS);
(void) mutex_lock(&cache_lock);
found_it = in_cache(domain, map, key, keylen, &my_val, &my_vallen);
cachegen = generation;
if (found_it) {
savesize = my_vallen + 2;
if ((*val = malloc((size_t)savesize)) == 0) {
(void) mutex_unlock(&cache_lock);
return (YPERR_RESRC);
}
(void) memcpy(*val, my_val, (size_t)savesize);
*vallen = my_vallen;
(void) mutex_unlock(&cache_lock);
return (0);
}
(void) mutex_unlock(&cache_lock);
for (;;) {
if (reason = __yp_dobind_rsvdport_cflookup(domain, &pdomb,
hardlookup))
return (reason);
if (pdomb->dom_binding->ypbind_hi_vers >= YPVERS) {
reason = domatch(domain, map, key, keylen,
pdomb, &_ypserv_timeout, val, vallen);
__yp_rel_binding(pdomb);
free_dom_binding(pdomb);
if (reason == YPERR_RPC || reason == YPERR_YPSERV ||
reason == YPERR_BUSY ) {
yp_unbind(domain);
if (hardlookup)
(void) sleep(_ypsleeptime);
else
return (reason);
} else
break;
} else {
__yp_rel_binding(pdomb);
free_dom_binding(pdomb);
return (YPERR_VERS);
}
}
if (reason == 0) {
(void) mutex_lock(&cache_lock);
if (generation != cachegen &&
in_cache(domain, map, key, keylen, &my_val, &my_vallen)) {
} else {
struct cache *c;
savesize = *vallen + 2;
c = makenode(domain, map, keylen, savesize);
if (c != 0) {
(void) gettimeofday(&now, &tz);
c->birth = now.tv_sec;
c->keylen = keylen;
c->vallen = *vallen;
(void) memcpy(c->key, key, (size_t)keylen);
(void) memcpy(c->val, *val, (size_t)savesize);
c->next = head;
head = c;
++generation;
}
}
(void) mutex_unlock(&cache_lock);
}
return (reason);
}
int
yp_match_rsvdport(
char *domain,
char *map,
char *key,
int keylen,
char **val,
int *vallen)
{
return (__yp_match_rsvdport_cflookup(domain, map, key, keylen, val,
vallen, 1));
}
static int
domatch(char *domain, char *map, char *key, int keylen,
struct dom_binding *pdomb, struct timeval *timeoutp, char **val,
int *vallen)
{
struct ypreq_key req;
struct ypresp_val resp;
unsigned int retval = 0;
req.domain = domain;
req.map = map;
req.keydat.dptr = key;
req.keydat.dsize = keylen;
resp.valdat.dptr = NULL;
resp.valdat.dsize = 0;
(void) memset((char *)&resp, 0, sizeof (struct ypresp_val));
switch (clnt_call(pdomb->dom_client, YPPROC_MATCH,
(xdrproc_t)xdr_ypreq_key, (char *)&req,
(xdrproc_t)xdr_ypresp_val, (char *)&resp,
*timeoutp)) {
case RPC_SUCCESS:
break;
case RPC_TIMEDOUT:
return (YPERR_YPSERV);
default:
return (YPERR_RPC);
}
if (resp.status != YP_TRUE) {
retval = ypprot_err(resp.status);
}
if (!retval && ((*val = malloc((size_t)
resp.valdat.dsize + 2)) == NULL)) {
retval = YPERR_RESRC;
}
if (!retval) {
*vallen = (int)resp.valdat.dsize;
(void) memcpy(*val, resp.valdat.dptr,
(size_t)resp.valdat.dsize);
(*val)[resp.valdat.dsize] = '\n';
(*val)[resp.valdat.dsize + 1] = '\0';
}
CLNT_FREERES(pdomb->dom_client,
(xdrproc_t)xdr_ypresp_val, (char *)&resp);
return (retval);
}