1 /* $NetBSD: yplib.c,v 1.44 2012/03/02 17:27:49 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1992, 1993 Theo de Raadt <deraadt@fsa.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 17 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 #if defined(LIBC_SCCS) && !defined(lint) 31 __RCSID("$NetBSD: yplib.c,v 1.44 2012/03/02 17:27:49 christos Exp $"); 32 #endif 33 34 #include "namespace.h" 35 #include "reentrant.h" 36 37 #include <sys/param.h> 38 #include <sys/socket.h> 39 #include <sys/file.h> 40 #include <sys/uio.h> 41 42 #include <arpa/nameser.h> 43 44 #include <assert.h> 45 #include <errno.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <unistd.h> 50 51 #include <rpc/rpc.h> 52 #include <rpc/xdr.h> 53 #include <rpcsvc/yp_prot.h> 54 #include <rpcsvc/ypclnt.h> 55 #include "local.h" 56 57 #define BINDINGDIR "/var/yp/binding" 58 #define YPBINDLOCK "/var/run/ypbind.lock" 59 60 struct dom_binding *_ypbindlist; 61 char _yp_domain[MAXHOSTNAMELEN]; 62 63 #define YPLIB_TIMEOUT 10 64 #define YPLIB_RPC_RETRIES 4 65 66 struct timeval _yplib_timeout = { YPLIB_TIMEOUT, 0 }; 67 struct timeval _yplib_rpc_timeout = { YPLIB_TIMEOUT / YPLIB_RPC_RETRIES, 68 1000000 * (YPLIB_TIMEOUT % YPLIB_RPC_RETRIES) / YPLIB_RPC_RETRIES }; 69 int _yplib_nerrs = 5; 70 int _yplib_bindtries = 0; 71 72 #ifdef __weak_alias 73 __weak_alias(yp_bind, _yp_bind) 74 __weak_alias(yp_unbind, _yp_unbind) 75 __weak_alias(yp_get_default_domain, _yp_get_default_domain) 76 __weak_alias(yp_setbindtries, _yp_setbindtries) 77 #endif 78 79 #ifdef _REENTRANT 80 static mutex_t _ypmutex = MUTEX_INITIALIZER; 81 #define YPLOCK() mutex_lock(&_ypmutex) 82 #define YPUNLOCK() mutex_unlock(&_ypmutex) 83 #else 84 #define YPLOCK() 85 #define YPUNLOCK() 86 #endif 87 88 int 89 yp_setbindtries(int ntries) { 90 int old_val = _yplib_bindtries; 91 92 if (ntries >= 0) 93 _yplib_bindtries = ntries; 94 return old_val; 95 } 96 97 int 98 _yp_dobind(dom, ypdb) 99 const char *dom; 100 struct dom_binding **ypdb; 101 { 102 static int pid = -1; 103 char path[MAXPATHLEN]; 104 struct dom_binding *ysd, *ysd2; 105 struct ypbind_resp ypbr; 106 struct sockaddr_in clnt_sin; 107 int clnt_sock, fd, gpid; 108 CLIENT *client; 109 int new = 0; 110 int nerrs = 0; 111 ssize_t r; 112 113 if (dom == NULL || *dom == '\0') 114 return YPERR_BADARGS; 115 116 /* 117 * test if YP is running or not 118 */ 119 if ((fd = open(YPBINDLOCK, O_RDONLY)) == -1) 120 return YPERR_YPBIND; 121 if (!(flock(fd, LOCK_EX | LOCK_NB) == -1 && errno == EWOULDBLOCK)) { 122 (void)close(fd); 123 return YPERR_YPBIND; 124 } 125 (void)close(fd); 126 127 gpid = getpid(); 128 if (!(pid == -1 || pid == gpid)) { 129 ysd = _ypbindlist; 130 while (ysd) { 131 if (ysd->dom_client) 132 clnt_destroy(ysd->dom_client); 133 ysd2 = ysd->dom_pnext; 134 free(ysd); 135 ysd = ysd2; 136 } 137 _ypbindlist = NULL; 138 } 139 pid = gpid; 140 141 if (ypdb != NULL) 142 *ypdb = NULL; 143 144 for (ysd = _ypbindlist; ysd; ysd = ysd->dom_pnext) 145 if (strcmp(dom, ysd->dom_domain) == 0) 146 break; 147 if (ysd == NULL) { 148 if ((ysd = malloc(sizeof *ysd)) == NULL) 149 return YPERR_YPERR; 150 (void)memset(ysd, 0, sizeof *ysd); 151 ysd->dom_socket = -1; 152 ysd->dom_vers = 0; 153 new = 1; 154 } 155 again: 156 if (ysd->dom_vers == 0) { 157 (void) snprintf(path, sizeof(path), "%s/%s.%d", 158 BINDINGDIR, dom, 2); 159 if ((fd = open(path, O_RDONLY)) == -1) { 160 /* 161 * no binding file, YP is dead, or not yet fully 162 * alive. 163 */ 164 goto trynet; 165 } 166 if (flock(fd, LOCK_EX | LOCK_NB) == -1 && 167 errno == EWOULDBLOCK) { 168 struct iovec iov[2]; 169 struct ypbind_resp ybr; 170 u_short ypb_port; 171 struct ypbind_binding *bn; 172 173 iov[0].iov_base = &ypb_port; 174 iov[0].iov_len = sizeof ypb_port; 175 iov[1].iov_base = &ybr; 176 iov[1].iov_len = sizeof ybr; 177 178 r = readv(fd, iov, 2); 179 if (r != (ssize_t)(iov[0].iov_len + iov[1].iov_len)) { 180 (void)close(fd); 181 ysd->dom_vers = -1; 182 goto again; 183 } 184 (void)memset(&ysd->dom_server_addr, 0, 185 sizeof ysd->dom_server_addr); 186 ysd->dom_server_addr.sin_len = 187 sizeof(struct sockaddr_in); 188 ysd->dom_server_addr.sin_family = AF_INET; 189 bn = &ybr.ypbind_respbody.ypbind_bindinfo; 190 ysd->dom_server_addr.sin_port = 191 bn->ypbind_binding_port; 192 193 ysd->dom_server_addr.sin_addr = 194 bn->ypbind_binding_addr; 195 196 ysd->dom_server_port = ysd->dom_server_addr.sin_port; 197 (void)close(fd); 198 goto gotit; 199 } else { 200 /* no lock on binding file, YP is dead. */ 201 (void)close(fd); 202 if (new) 203 free(ysd); 204 return YPERR_YPBIND; 205 } 206 } 207 trynet: 208 if (ysd->dom_vers == -1 || ysd->dom_vers == 0) { 209 struct ypbind_binding *bn; 210 (void)memset(&clnt_sin, 0, sizeof clnt_sin); 211 clnt_sin.sin_len = sizeof(struct sockaddr_in); 212 clnt_sin.sin_family = AF_INET; 213 clnt_sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 214 215 clnt_sock = RPC_ANYSOCK; 216 client = clnttcp_create(&clnt_sin, YPBINDPROG, YPBINDVERS, 217 &clnt_sock, 0, 0); 218 if (client == NULL) { 219 clnt_pcreateerror("clnttcp_create"); 220 if (new) 221 free(ysd); 222 return YPERR_YPBIND; 223 } 224 r = clnt_call(client, (rpcproc_t)YPBINDPROC_DOMAIN, 225 (xdrproc_t)xdr_ypdomain_wrap_string, &dom, 226 (xdrproc_t)xdr_ypbind_resp, &ypbr, _yplib_timeout); 227 if (r != RPC_SUCCESS) { 228 if (_yplib_bindtries <= 0 && new == 0 && 229 ++nerrs == _yplib_nerrs) { 230 nerrs = 0; 231 fprintf(stderr, 232 "YP server for domain %s not responding, still trying\n", 233 dom); 234 } 235 else if (_yplib_bindtries > 0 && 236 ++nerrs == _yplib_bindtries) { 237 free(ysd); 238 return YPERR_YPBIND; 239 } 240 clnt_destroy(client); 241 ysd->dom_vers = -1; 242 goto again; 243 } 244 clnt_destroy(client); 245 246 (void)memset(&ysd->dom_server_addr, 0, 247 sizeof ysd->dom_server_addr); 248 ysd->dom_server_addr.sin_len = sizeof(struct sockaddr_in); 249 ysd->dom_server_addr.sin_family = AF_INET; 250 bn = &ypbr.ypbind_respbody.ypbind_bindinfo; 251 ysd->dom_server_addr.sin_port = 252 bn->ypbind_binding_port; 253 ysd->dom_server_addr.sin_addr.s_addr = 254 bn->ypbind_binding_addr.s_addr; 255 ysd->dom_server_port = 256 bn->ypbind_binding_port; 257 gotit: 258 ysd->dom_vers = YPVERS; 259 (void)strlcpy(ysd->dom_domain, dom, sizeof(ysd->dom_domain)); 260 } 261 if (ysd->dom_client) 262 clnt_destroy(ysd->dom_client); 263 ysd->dom_socket = RPC_ANYSOCK; 264 ysd->dom_client = clntudp_create(&ysd->dom_server_addr, 265 YPPROG, YPVERS, _yplib_rpc_timeout, &ysd->dom_socket); 266 if (ysd->dom_client == NULL) { 267 clnt_pcreateerror("clntudp_create"); 268 ysd->dom_vers = -1; 269 goto again; 270 } 271 if (fcntl(ysd->dom_socket, F_SETFD, FD_CLOEXEC) == -1) 272 perror("fcntl: F_SETFD"); 273 274 if (new) { 275 ysd->dom_pnext = _ypbindlist; 276 _ypbindlist = ysd; 277 } 278 if (ypdb != NULL) 279 *ypdb = ysd; 280 return 0; 281 } 282 283 void 284 __yp_unbind(ypb) 285 struct dom_binding *ypb; 286 { 287 288 _DIAGASSERT(ypb != NULL); 289 290 clnt_destroy(ypb->dom_client); 291 ypb->dom_client = NULL; 292 ypb->dom_socket = -1; 293 } 294 295 int 296 yp_bind(dom) 297 const char *dom; 298 { 299 if (_yp_invalid_domain(dom)) 300 return YPERR_BADARGS; 301 302 return _yp_dobind(dom, NULL); 303 } 304 305 void 306 yp_unbind(dom) 307 const char *dom; 308 { 309 struct dom_binding *ypb, *ypbp; 310 311 if (_yp_invalid_domain(dom)) 312 return; 313 314 ypbp = NULL; 315 for (ypb = _ypbindlist; ypb; ypb = ypb->dom_pnext) { 316 if (strcmp(dom, ypb->dom_domain) == 0) { 317 clnt_destroy(ypb->dom_client); 318 if (ypbp) 319 ypbp->dom_pnext = ypb->dom_pnext; 320 else 321 _ypbindlist = ypb->dom_pnext; 322 free(ypb); 323 return; 324 } 325 ypbp = ypb; 326 } 327 return; 328 } 329 330 int 331 yp_get_default_domain(domp) 332 char **domp; 333 { 334 if (domp == NULL) 335 return YPERR_BADARGS; 336 *domp = NULL; 337 if (_yp_domain[0] == '\0') 338 if (getdomainname(_yp_domain, sizeof _yp_domain)) 339 return YPERR_NODOM; 340 *domp = _yp_domain; 341 return 0; 342 } 343 344 int 345 _yp_check(dom) 346 char **dom; 347 { 348 char *unused; 349 int good; 350 351 YPLOCK(); 352 353 if (_yp_domain[0] == '\0') 354 if (yp_get_default_domain(&unused)) { 355 good = 0; 356 goto done; 357 } 358 if (dom) 359 *dom = _yp_domain; 360 361 good = yp_bind(_yp_domain) == 0; 362 done: 363 YPUNLOCK(); 364 return good; 365 } 366 367 /* 368 * _yp_invalid_domain: check if given domainname isn't legal. 369 * returns non-zero if invalid 370 */ 371 int 372 _yp_invalid_domain(dom) 373 const char *dom; 374 { 375 if (dom == NULL || *dom == '\0') 376 return 1; 377 378 if (strlen(dom) > YPMAXDOMAIN) 379 return 1; 380 381 if (strchr(dom, '/') != NULL) 382 return 1; 383 384 return 0; 385 } 386