1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 * 22 * Copyright 2002 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 /* 26 * Copyright 2014 Nexenta Systems, Inc. All rights reserved. 27 */ 28 29 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 30 /* All Rights Reserved */ 31 /* 32 * University Copyright- Copyright (c) 1982, 1986, 1988 33 * The Regents of the University of California 34 * All Rights Reserved 35 * 36 * University Acknowledgment- Portions of this document are derived from 37 * software developed by the University of California, Berkeley, and its 38 * contributors. 39 */ 40 41 /* 42 * check_bound.c 43 * Checks to see whether the program is still bound to the 44 * claimed address and returns the univeral merged address 45 * 46 */ 47 48 #include <stdio.h> 49 #include <rpc/rpc.h> 50 #include <netconfig.h> 51 #include <netdir.h> 52 #include <sys/syslog.h> 53 #include <stdlib.h> 54 #include "rpcbind.h" 55 #include <string.h> 56 /* the following just to get my address */ 57 #include <errno.h> 58 #include <sys/socket.h> 59 #include <netinet/in.h> 60 #include <thread.h> 61 #include <synch.h> 62 #include <syslog.h> 63 64 struct fdlist { 65 int fd; 66 mutex_t fd_lock; /* protects fd */ 67 struct netconfig *nconf; 68 struct fdlist *next; 69 int check_binding; 70 }; 71 72 static struct fdlist *fdhead; /* Link list of the check fd's */ 73 static struct fdlist *fdtail; 74 static char *nullstring = ""; 75 76 /* 77 * Returns 1 if the given address is bound for the given addr & transport 78 * For all error cases, we assume that the address is bound 79 * Returns 0 for success. 80 * 81 * fdl: My FD list 82 * uaddr: the universal address 83 */ 84 static bool_t 85 check_bound(struct fdlist *fdl, char *uaddr) 86 { 87 int fd; 88 struct netbuf *na; 89 struct t_bind taddr, *baddr; 90 int ans; 91 92 if (fdl->check_binding == FALSE) 93 return (TRUE); 94 95 na = uaddr2taddr(fdl->nconf, uaddr); 96 if (!na) 97 return (TRUE); /* punt, should never happen */ 98 99 taddr.addr = *na; 100 taddr.qlen = 1; 101 (void) mutex_lock(&fdl->fd_lock); 102 fd = fdl->fd; 103 baddr = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR); 104 if (baddr == NULL) { 105 (void) mutex_unlock(&fdl->fd_lock); 106 netdir_free((char *)na, ND_ADDR); 107 return (TRUE); 108 } 109 if (t_bind(fd, &taddr, baddr) != 0) { 110 (void) mutex_unlock(&fdl->fd_lock); 111 netdir_free((char *)na, ND_ADDR); 112 (void) t_free((char *)baddr, T_BIND); 113 return (TRUE); 114 } 115 if (t_unbind(fd) != 0) { 116 /* Bad fd. Purge this fd */ 117 (void) t_close(fd); 118 fdl->fd = t_open(fdl->nconf->nc_device, O_RDWR, NULL); 119 if (fdl->fd == -1) 120 fdl->check_binding = FALSE; 121 } 122 (void) mutex_unlock(&fdl->fd_lock); 123 ans = memcmp(taddr.addr.buf, baddr->addr.buf, baddr->addr.len); 124 netdir_free((char *)na, ND_ADDR); 125 (void) t_free((char *)baddr, T_BIND); 126 return (ans == 0 ? FALSE : TRUE); 127 } 128 129 /* 130 * Keep open one more file descriptor for this transport, which 131 * will be used to determine whether the given service is up 132 * or not by trying to bind to the registered address. 133 * We are ignoring errors here. It trashes taddr and baddr; 134 * but that perhaps should not matter. 135 * 136 * We check for the following conditions: 137 * 1. Is it possible for t_bind to fail in the case where 138 * we bind to an already bound address and have any 139 * other error number besides TNOADDR. 140 * 2. If an address is specified in bind addr, can I bind to 141 * the same address. 142 * 3. If NULL is specified in bind addr, can I bind to the 143 * address to which the fd finally got bound. 144 */ 145 int 146 add_bndlist(struct netconfig *nconf, struct t_bind *taddr, struct t_bind *baddr) 147 { 148 int fd; 149 struct fdlist *fdl; 150 struct netconfig *newnconf; 151 struct t_info tinfo; 152 struct t_bind tmpaddr; 153 154 newnconf = getnetconfigent(nconf->nc_netid); 155 if (newnconf == NULL) 156 return (-1); 157 fdl = (struct fdlist *)malloc((uint_t)sizeof (struct fdlist)); 158 if (fdl == NULL) { 159 freenetconfigent(newnconf); 160 syslog(LOG_ERR, "no memory!"); 161 return (-1); 162 } 163 (void) mutex_init(&fdl->fd_lock, USYNC_THREAD, NULL); 164 fdl->nconf = newnconf; 165 fdl->next = NULL; 166 if (fdhead == NULL) { 167 fdhead = fdl; 168 fdtail = fdl; 169 } else { 170 fdtail->next = fdl; 171 fdtail = fdl; 172 } 173 fdl->check_binding = FALSE; 174 if ((fdl->fd = t_open(nconf->nc_device, O_RDWR, &tinfo)) < 0) { 175 /* 176 * Note that we haven't dequeued this entry nor have we freed 177 * the netconfig structure. 178 */ 179 if (debugging) { 180 fprintf(stderr, 181 "%s: add_bndlist cannot open connection: %s", 182 nconf->nc_netid, t_errlist[t_errno]); 183 } 184 return (-1); 185 } 186 187 /* Set the qlen only for cots transports */ 188 switch (tinfo.servtype) { 189 case T_COTS: 190 case T_COTS_ORD: 191 taddr->qlen = 1; 192 break; 193 case T_CLTS: 194 taddr->qlen = 0; 195 break; 196 default: 197 goto error; 198 } 199 200 if (t_bind(fdl->fd, taddr, baddr) != 0) { 201 if (t_errno == TNOADDR) { 202 fdl->check_binding = TRUE; 203 return (0); /* All is fine */ 204 } 205 /* Perhaps condition #1 */ 206 if (debugging) { 207 fprintf(stderr, "%s: add_bndlist cannot bind (1): %s", 208 nconf->nc_netid, t_errlist[t_errno]); 209 } 210 goto not_bound; 211 } 212 213 /* Condition #2 */ 214 if (!memcmp(taddr->addr.buf, baddr->addr.buf, 215 (int)baddr->addr.len)) { 216 goto not_bound; 217 } 218 219 /* Condition #3 */ 220 t_unbind(fdl->fd); 221 /* Set the qlen only for cots transports */ 222 switch (tinfo.servtype) { 223 case T_COTS: 224 case T_COTS_ORD: 225 tmpaddr.qlen = 1; 226 break; 227 case T_CLTS: 228 tmpaddr.qlen = 0; 229 break; 230 default: 231 goto error; 232 } 233 tmpaddr.addr.len = tmpaddr.addr.maxlen = 0; 234 tmpaddr.addr.buf = NULL; 235 if (t_bind(fdl->fd, &tmpaddr, taddr) != 0) { 236 if (debugging) { 237 fprintf(stderr, "%s: add_bndlist cannot bind (2): %s", 238 nconf->nc_netid, t_errlist[t_errno]); 239 } 240 goto error; 241 } 242 /* Now fdl->fd is bound to a transport chosen address */ 243 if ((fd = t_open(nconf->nc_device, O_RDWR, &tinfo)) < 0) { 244 if (debugging) { 245 fprintf(stderr, 246 "%s: add_bndlist cannot open connection: %s", 247 nconf->nc_netid, t_errlist[t_errno]); 248 } 249 goto error; 250 } 251 if (t_bind(fd, taddr, baddr) != 0) { 252 if (t_errno == TNOADDR) { 253 /* 254 * This transport is schizo. Previously it handled a 255 * request to bind to an already bound transport by 256 * returning a different bind address, and now it's 257 * returning a TNOADDR for essentially the same 258 * request. The spec may allow this behavior, so 259 * we'll just assume we can't do bind checking with 260 * this transport. 261 */ 262 t_close(fd); 263 goto not_bound; 264 } 265 if (debugging) { 266 fprintf(stderr, "%s: add_bndlist cannot bind (3): %s", 267 nconf->nc_netid, t_errlist[t_errno]); 268 } 269 t_close(fd); 270 goto error; 271 } 272 t_close(fd); 273 if (!memcmp(taddr->addr.buf, baddr->addr.buf, 274 (int)baddr->addr.len)) { 275 switch (tinfo.servtype) { 276 case T_COTS: 277 case T_COTS_ORD: 278 if (baddr->qlen == 1) { 279 goto not_bound; 280 } 281 break; 282 case T_CLTS: 283 goto not_bound; 284 default: 285 goto error; 286 } 287 } 288 289 t_unbind(fdl->fd); 290 fdl->check_binding = TRUE; 291 return (0); 292 293 not_bound: 294 t_close(fdl->fd); 295 fdl->fd = -1; 296 return (1); 297 298 error: 299 t_close(fdl->fd); 300 fdl->fd = -1; 301 return (-1); 302 } 303 304 bool_t 305 is_bound(char *netid, char *uaddr) 306 { 307 struct fdlist *fdl; 308 309 for (fdl = fdhead; fdl; fdl = fdl->next) 310 if (strcmp(fdl->nconf->nc_netid, netid) == 0) 311 break; 312 if (fdl == NULL) 313 return (TRUE); 314 return (check_bound(fdl, uaddr)); 315 } 316 317 /* Return pointer to port string in the universal address */ 318 #define UADDR_PRT_INDX(UADDR, PORT) { \ 319 PORT = strrchr(UADDR, '.'); \ 320 while (*--PORT != '.'); } 321 /* 322 * Returns NULL if there was some system error. 323 * Returns "" if the address was not bound, i.e the server crashed. 324 * Returns the merged address otherwise. 325 */ 326 char * 327 mergeaddr(SVCXPRT *xprt, char *netid, char *uaddr, char *saddr) 328 { 329 struct fdlist *fdl; 330 struct nd_mergearg ma; 331 int stat; 332 333 for (fdl = fdhead; fdl; fdl = fdl->next) 334 if (strcmp(fdl->nconf->nc_netid, netid) == 0) 335 break; 336 if (fdl == NULL) 337 return (NULL); 338 if (check_bound(fdl, uaddr) == FALSE) 339 /* that server died */ 340 return (nullstring); 341 /* 342 * If saddr is not NULL, the remote client may have included the 343 * address by which it contacted us. Use that for the "client" uaddr, 344 * otherwise use the info from the SVCXPRT. 345 */ 346 if (saddr != NULL) { 347 ma.c_uaddr = saddr; 348 } else { 349 350 /* retrieve the client's address */ 351 ma.c_uaddr = taddr2uaddr(fdl->nconf, svc_getrpccaller(xprt)); 352 if (ma.c_uaddr == NULL) { 353 syslog(LOG_ERR, "taddr2uaddr failed for %s: %s", 354 fdl->nconf->nc_netid, netdir_sperror()); 355 return (NULL); 356 } 357 358 } 359 360 /* Not an INET address? */ 361 if ((strcmp(fdl->nconf->nc_protofmly, NC_INET) != 0) && 362 (strcmp(fdl->nconf->nc_protofmly, NC_INET6) != 0)) { 363 ma.s_uaddr = uaddr; 364 stat = netdir_options(fdl->nconf, ND_MERGEADDR, 0, (char *)&ma); 365 } 366 /* Inet address, but no xp_ltaddr */ 367 else if ((ma.s_uaddr = taddr2uaddr(fdl->nconf, 368 &(xprt)->xp_ltaddr)) == NULL) { 369 ma.s_uaddr = uaddr; 370 stat = netdir_options(fdl->nconf, ND_MERGEADDR, 0, (char *)&ma); 371 } else { 372 /* 373 * (xprt)->xp_ltaddr contains portmap's port address. 374 * Overwrite this with actual application's port address 375 * before returning to the caller. 376 */ 377 char *s_uport, *uport; 378 379 /* Get the INET/INET6 address part from ma.s_uaddr */ 380 UADDR_PRT_INDX(ma.s_uaddr, s_uport); 381 *s_uport = '\0'; 382 383 /* Get the port info from uaddr */ 384 UADDR_PRT_INDX(uaddr, uport); 385 386 ma.m_uaddr = malloc(strlen(ma.s_uaddr) + strlen(uport) + 1); 387 if (ma.m_uaddr == NULL) { 388 syslog(LOG_ERR, "mergeaddr: no memory!"); 389 free(ma.s_uaddr); 390 if (saddr == NULL) 391 free(ma.c_uaddr); 392 return (NULL); 393 } 394 395 /* Copy IP address into the Universal address holder */ 396 strcpy(ma.m_uaddr, ma.s_uaddr); 397 /* Append port info to the Universal address holder */ 398 strcat(ma.m_uaddr, uport); 399 free(ma.s_uaddr); 400 stat = 0; 401 } 402 if (saddr == NULL) { 403 free(ma.c_uaddr); 404 } 405 if (stat) { 406 syslog(LOG_ERR, "netdir_merge failed for %s: %s", 407 fdl->nconf->nc_netid, netdir_sperror()); 408 return (NULL); 409 } 410 411 return (ma.m_uaddr); 412 } 413 414 /* 415 * Returns a netconf structure from its internal list. This 416 * structure should not be freed. 417 */ 418 struct netconfig * 419 rpcbind_get_conf(char *netid) 420 { 421 struct fdlist *fdl; 422 423 for (fdl = fdhead; fdl; fdl = fdl->next) 424 if (strcmp(fdl->nconf->nc_netid, netid) == 0) 425 break; 426 if (fdl == NULL) 427 return (NULL); 428 return (fdl->nconf); 429 } 430