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