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