1 /* $NetBSD: yplib.c,v 1.42 2004/10/29 06:32:09 lukem 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.42 2004/10/29 06:32:09 lukem 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 71 #ifdef __weak_alias 72 __weak_alias(yp_bind, _yp_bind) 73 __weak_alias(yp_unbind, _yp_unbind) 74 __weak_alias(yp_get_default_domain, _yp_get_default_domain) 75 #endif 76 77 #ifdef _REENTRANT 78 static mutex_t _ypmutex = MUTEX_INITIALIZER; 79 #define YPLOCK() mutex_lock(&_ypmutex) 80 #define YPUNLOCK() mutex_unlock(&_ypmutex) 81 #else 82 #define YPLOCK() 83 #define YPUNLOCK() 84 #endif 85 86 int 87 _yp_dobind(dom, ypdb) 88 const char *dom; 89 struct dom_binding **ypdb; 90 { 91 static int pid = -1; 92 char path[MAXPATHLEN]; 93 struct dom_binding *ysd, *ysd2; 94 struct ypbind_resp ypbr; 95 struct sockaddr_in clnt_sin; 96 int clnt_sock, fd, gpid; 97 CLIENT *client; 98 int new = 0; 99 int nerrs = 0; 100 ssize_t r; 101 102 if (dom == NULL || *dom == '\0') 103 return YPERR_BADARGS; 104 105 /* 106 * test if YP is running or not 107 */ 108 if ((fd = open(YPBINDLOCK, O_RDONLY)) == -1) 109 return YPERR_YPBIND; 110 if (!(flock(fd, LOCK_EX | LOCK_NB) == -1 && errno == EWOULDBLOCK)) { 111 (void)close(fd); 112 return YPERR_YPBIND; 113 } 114 (void)close(fd); 115 116 gpid = getpid(); 117 if (!(pid == -1 || pid == gpid)) { 118 ysd = _ypbindlist; 119 while (ysd) { 120 if (ysd->dom_client) 121 clnt_destroy(ysd->dom_client); 122 ysd2 = ysd->dom_pnext; 123 free(ysd); 124 ysd = ysd2; 125 } 126 _ypbindlist = NULL; 127 } 128 pid = gpid; 129 130 if (ypdb != NULL) 131 *ypdb = NULL; 132 133 for (ysd = _ypbindlist; ysd; ysd = ysd->dom_pnext) 134 if (strcmp(dom, ysd->dom_domain) == 0) 135 break; 136 if (ysd == NULL) { 137 if ((ysd = malloc(sizeof *ysd)) == NULL) 138 return YPERR_YPERR; 139 (void)memset(ysd, 0, sizeof *ysd); 140 ysd->dom_socket = -1; 141 ysd->dom_vers = 0; 142 new = 1; 143 } 144 again: 145 if (ysd->dom_vers == 0) { 146 (void) snprintf(path, sizeof(path), "%s/%s.%d", 147 BINDINGDIR, dom, 2); 148 if ((fd = open(path, O_RDONLY)) == -1) { 149 /* 150 * no binding file, YP is dead, or not yet fully 151 * alive. 152 */ 153 goto trynet; 154 } 155 if (flock(fd, LOCK_EX | LOCK_NB) == -1 && 156 errno == EWOULDBLOCK) { 157 struct iovec iov[2]; 158 struct ypbind_resp ybr; 159 u_short ypb_port; 160 struct ypbind_binding *bn; 161 162 iov[0].iov_base = &ypb_port; 163 iov[0].iov_len = sizeof ypb_port; 164 iov[1].iov_base = &ybr; 165 iov[1].iov_len = sizeof ybr; 166 167 r = readv(fd, iov, 2); 168 if (r != (ssize_t)(iov[0].iov_len + iov[1].iov_len)) { 169 (void)close(fd); 170 ysd->dom_vers = -1; 171 goto again; 172 } 173 (void)memset(&ysd->dom_server_addr, 0, 174 sizeof ysd->dom_server_addr); 175 ysd->dom_server_addr.sin_len = 176 sizeof(struct sockaddr_in); 177 ysd->dom_server_addr.sin_family = AF_INET; 178 bn = &ybr.ypbind_respbody.ypbind_bindinfo; 179 ysd->dom_server_addr.sin_port = 180 bn->ypbind_binding_port; 181 182 ysd->dom_server_addr.sin_addr = 183 bn->ypbind_binding_addr; 184 185 ysd->dom_server_port = ysd->dom_server_addr.sin_port; 186 (void)close(fd); 187 goto gotit; 188 } else { 189 /* no lock on binding file, YP is dead. */ 190 (void)close(fd); 191 if (new) 192 free(ysd); 193 return YPERR_YPBIND; 194 } 195 } 196 trynet: 197 if (ysd->dom_vers == -1 || ysd->dom_vers == 0) { 198 struct ypbind_binding *bn; 199 (void)memset(&clnt_sin, 0, sizeof clnt_sin); 200 clnt_sin.sin_len = sizeof(struct sockaddr_in); 201 clnt_sin.sin_family = AF_INET; 202 clnt_sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 203 204 clnt_sock = RPC_ANYSOCK; 205 client = clnttcp_create(&clnt_sin, YPBINDPROG, YPBINDVERS, 206 &clnt_sock, 0, 0); 207 if (client == NULL) { 208 clnt_pcreateerror("clnttcp_create"); 209 if (new) 210 free(ysd); 211 return YPERR_YPBIND; 212 } 213 r = clnt_call(client, (rpcproc_t)YPBINDPROC_DOMAIN, 214 (xdrproc_t)xdr_ypdomain_wrap_string, &dom, 215 (xdrproc_t)xdr_ypbind_resp, &ypbr, _yplib_timeout); 216 if (r != RPC_SUCCESS) { 217 if (new == 0 && ++nerrs == _yplib_nerrs) { 218 nerrs = 0; 219 fprintf(stderr, 220 "YP server for domain %s not responding, still trying\n", 221 dom); 222 } 223 clnt_destroy(client); 224 ysd->dom_vers = -1; 225 goto again; 226 } 227 clnt_destroy(client); 228 229 (void)memset(&ysd->dom_server_addr, 0, 230 sizeof ysd->dom_server_addr); 231 ysd->dom_server_addr.sin_len = sizeof(struct sockaddr_in); 232 ysd->dom_server_addr.sin_family = AF_INET; 233 bn = &ypbr.ypbind_respbody.ypbind_bindinfo; 234 ysd->dom_server_addr.sin_port = 235 bn->ypbind_binding_port; 236 ysd->dom_server_addr.sin_addr.s_addr = 237 bn->ypbind_binding_addr.s_addr; 238 ysd->dom_server_port = 239 bn->ypbind_binding_port; 240 gotit: 241 ysd->dom_vers = YPVERS; 242 (void)strlcpy(ysd->dom_domain, dom, sizeof(ysd->dom_domain)); 243 } 244 if (ysd->dom_client) 245 clnt_destroy(ysd->dom_client); 246 ysd->dom_socket = RPC_ANYSOCK; 247 ysd->dom_client = clntudp_create(&ysd->dom_server_addr, 248 YPPROG, YPVERS, _yplib_rpc_timeout, &ysd->dom_socket); 249 if (ysd->dom_client == NULL) { 250 clnt_pcreateerror("clntudp_create"); 251 ysd->dom_vers = -1; 252 goto again; 253 } 254 if (fcntl(ysd->dom_socket, F_SETFD, 1) == -1) 255 perror("fcntl: F_SETFD"); 256 257 if (new) { 258 ysd->dom_pnext = _ypbindlist; 259 _ypbindlist = ysd; 260 } 261 if (ypdb != NULL) 262 *ypdb = ysd; 263 return 0; 264 } 265 266 void 267 __yp_unbind(ypb) 268 struct dom_binding *ypb; 269 { 270 271 _DIAGASSERT(ypb != NULL); 272 273 clnt_destroy(ypb->dom_client); 274 ypb->dom_client = NULL; 275 ypb->dom_socket = -1; 276 } 277 278 int 279 yp_bind(dom) 280 const char *dom; 281 { 282 if (_yp_invalid_domain(dom)) 283 return YPERR_BADARGS; 284 285 return _yp_dobind(dom, NULL); 286 } 287 288 void 289 yp_unbind(dom) 290 const char *dom; 291 { 292 struct dom_binding *ypb, *ypbp; 293 294 if (_yp_invalid_domain(dom)) 295 return; 296 297 ypbp = NULL; 298 for (ypb = _ypbindlist; ypb; ypb = ypb->dom_pnext) { 299 if (strcmp(dom, ypb->dom_domain) == 0) { 300 clnt_destroy(ypb->dom_client); 301 if (ypbp) 302 ypbp->dom_pnext = ypb->dom_pnext; 303 else 304 _ypbindlist = ypb->dom_pnext; 305 free(ypb); 306 return; 307 } 308 ypbp = ypb; 309 } 310 return; 311 } 312 313 int 314 yp_get_default_domain(domp) 315 char **domp; 316 { 317 if (domp == NULL) 318 return YPERR_BADARGS; 319 *domp = NULL; 320 if (_yp_domain[0] == '\0') 321 if (getdomainname(_yp_domain, sizeof _yp_domain)) 322 return YPERR_NODOM; 323 *domp = _yp_domain; 324 return 0; 325 } 326 327 int 328 _yp_check(dom) 329 char **dom; 330 { 331 char *unused; 332 int good; 333 334 YPLOCK(); 335 336 if (_yp_domain[0] == '\0') 337 if (yp_get_default_domain(&unused)) { 338 good = 0; 339 goto done; 340 } 341 if (dom) 342 *dom = _yp_domain; 343 344 good = yp_bind(_yp_domain) == 0; 345 done: 346 YPUNLOCK(); 347 return good; 348 } 349 350 /* 351 * _yp_invalid_domain: check if given domainname isn't legal. 352 * returns non-zero if invalid 353 */ 354 int 355 _yp_invalid_domain(dom) 356 const char *dom; 357 { 358 if (dom == NULL || *dom == '\0') 359 return 1; 360 361 if (strlen(dom) > YPMAXDOMAIN) 362 return 1; 363 364 if (strchr(dom, '/') != NULL) 365 return 1; 366 367 return 0; 368 } 369