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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 /* 29 * Portions of this source code were derived from Berkeley 30 * 4.3 BSD under license from the Regents of the University of 31 * California. 32 */ 33 34 #pragma ident "%Z%%M% %I% %E% SMI" 35 36 #ifdef PORTMAP 37 /* 38 * rpc_soc.c 39 * 40 * The backward compatibility routines for the earlier implementation 41 * of RPC, where the only transports supported were tcp/ip and udp/ip. 42 * Based on berkeley socket abstraction, now implemented on the top 43 * of TLI/Streams 44 */ 45 46 #include "mt.h" 47 #include "rpc_mt.h" 48 #include <stdio.h> 49 #include <sys/types.h> 50 #include <rpc/rpc.h> 51 #include <netinet/in.h> 52 #include <sys/socket.h> 53 #include <netdb.h> 54 #include <netdir.h> 55 #include <errno.h> 56 #include <sys/syslog.h> 57 #include <rpc/pmap_clnt.h> 58 #include <rpc/pmap_prot.h> 59 #include <rpc/nettype.h> 60 #include <syslog.h> 61 #include <string.h> 62 #include <stdlib.h> 63 #include <unistd.h> 64 65 int __rpc_bindresvport(int, struct sockaddr_in *, int *, int); 66 int __rpc_bindresvport_ipv6(int, struct sockaddr *, int *, int, char *); 67 void get_myaddress_ipv6(char *, struct sockaddr *); 68 69 extern mutex_t rpcsoc_lock; 70 71 /* 72 * A common clnt create routine 73 */ 74 static CLIENT * 75 clnt_com_create(struct sockaddr_in *raddr, rpcprog_t prog, rpcvers_t vers, 76 int *sockp, uint_t sendsz, uint_t recvsz, char *tp) 77 { 78 CLIENT *cl; 79 int madefd = FALSE; 80 int fd = *sockp; 81 struct t_info tinfo; 82 struct netconfig *nconf; 83 int port; 84 struct netbuf bindaddr; 85 bool_t locked = TRUE; 86 87 (void) mutex_lock(&rpcsoc_lock); 88 if ((nconf = __rpc_getconfip(tp)) == NULL) { 89 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; 90 (void) mutex_unlock(&rpcsoc_lock); 91 return (NULL); 92 } 93 if (fd == RPC_ANYSOCK) { 94 fd = t_open(nconf->nc_device, O_RDWR, &tinfo); 95 if (fd == -1) 96 goto syserror; 97 RPC_RAISEFD(fd); 98 madefd = TRUE; 99 } else { 100 if (t_getinfo(fd, &tinfo) == -1) 101 goto syserror; 102 } 103 104 if (raddr->sin_port == 0) { 105 uint_t proto; 106 ushort_t sport; 107 108 /* pmap_getport is recursive */ 109 (void) mutex_unlock(&rpcsoc_lock); 110 proto = strcmp(tp, "udp") == 0 ? IPPROTO_UDP : IPPROTO_TCP; 111 sport = pmap_getport(raddr, prog, vers, proto); 112 if (sport == 0) { 113 locked = FALSE; 114 goto err; 115 } 116 raddr->sin_port = htons(sport); 117 /* pmap_getport is recursive */ 118 (void) mutex_lock(&rpcsoc_lock); 119 } 120 121 /* Transform sockaddr_in to netbuf */ 122 bindaddr.maxlen = bindaddr.len = __rpc_get_a_size(tinfo.addr); 123 bindaddr.buf = (char *)raddr; 124 125 (void) __rpc_bindresvport(fd, NULL, &port, 0); 126 cl = clnt_tli_create(fd, nconf, &bindaddr, prog, vers, 127 sendsz, recvsz); 128 if (cl) { 129 if (madefd == TRUE) { 130 /* 131 * The fd should be closed while destroying the handle. 132 */ 133 (void) CLNT_CONTROL(cl, CLSET_FD_CLOSE, NULL); 134 *sockp = fd; 135 } 136 (void) freenetconfigent(nconf); 137 (void) mutex_unlock(&rpcsoc_lock); 138 return (cl); 139 } 140 goto err; 141 142 syserror: 143 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 144 rpc_createerr.cf_error.re_errno = errno; 145 rpc_createerr.cf_error.re_terrno = t_errno; 146 147 err: if (madefd == TRUE) 148 (void) t_close(fd); 149 (void) freenetconfigent(nconf); 150 if (locked == TRUE) 151 (void) mutex_unlock(&rpcsoc_lock); 152 return (NULL); 153 } 154 155 CLIENT * 156 clntudp_bufcreate(struct sockaddr_in *raddr, rpcprog_t prog, rpcvers_t vers, 157 struct timeval wait, int *sockp, uint_t sendsz, uint_t recvsz) 158 { 159 CLIENT *cl; 160 161 cl = clnt_com_create(raddr, prog, vers, sockp, sendsz, recvsz, "udp"); 162 if (cl == NULL) 163 return (NULL); 164 (void) CLNT_CONTROL(cl, CLSET_RETRY_TIMEOUT, (char *)&wait); 165 return (cl); 166 } 167 168 CLIENT * 169 clntudp_create(struct sockaddr_in *raddr, rpcprog_t program, rpcvers_t version, 170 struct timeval wait, int *sockp) 171 { 172 return (clntudp_bufcreate(raddr, program, version, wait, sockp, 173 UDPMSGSIZE, UDPMSGSIZE)); 174 } 175 176 CLIENT * 177 clnttcp_create(struct sockaddr_in *raddr, rpcprog_t prog, rpcvers_t vers, 178 int *sockp, uint_t sendsz, uint_t recvsz) 179 { 180 return (clnt_com_create(raddr, prog, vers, sockp, sendsz, 181 recvsz, "tcp")); 182 } 183 184 CLIENT * 185 clntraw_create(rpcprog_t prog, rpcvers_t vers) 186 { 187 return (clnt_raw_create(prog, vers)); 188 } 189 190 /* 191 * A common server create routine 192 */ 193 static SVCXPRT * 194 svc_com_create(int fd, uint_t sendsize, uint_t recvsize, char *netid) 195 { 196 struct netconfig *nconf; 197 SVCXPRT *svc; 198 int madefd = FALSE; 199 int port; 200 int res; 201 202 if ((nconf = __rpc_getconfip(netid)) == NULL) { 203 (void) syslog(LOG_ERR, "Could not get %s transport", netid); 204 return (NULL); 205 } 206 if (fd == RPC_ANYSOCK) { 207 fd = t_open(nconf->nc_device, O_RDWR, NULL); 208 if (fd == -1) { 209 char errorstr[100]; 210 211 __tli_sys_strerror(errorstr, sizeof (errorstr), 212 t_errno, errno); 213 (void) syslog(LOG_ERR, 214 "svc%s_create: could not open connection : %s", netid, 215 errorstr); 216 (void) freenetconfigent(nconf); 217 return (NULL); 218 } 219 madefd = TRUE; 220 } 221 222 res = __rpc_bindresvport(fd, NULL, &port, 8); 223 svc = svc_tli_create(fd, nconf, NULL, 224 sendsize, recvsize); 225 (void) freenetconfigent(nconf); 226 if (svc == NULL) { 227 if (madefd) 228 (void) t_close(fd); 229 return (NULL); 230 } 231 if (res == -1) 232 /* LINTED pointer cast */ 233 port = (((struct sockaddr_in *)svc->xp_ltaddr.buf)->sin_port); 234 svc->xp_port = ntohs(port); 235 return (svc); 236 } 237 238 SVCXPRT * 239 svctcp_create(int fd, uint_t sendsize, uint_t recvsize) 240 { 241 return (svc_com_create(fd, sendsize, recvsize, "tcp")); 242 } 243 244 SVCXPRT * 245 svcudp_bufcreate(int fd, uint_t sendsz, uint_t recvsz) 246 { 247 return (svc_com_create(fd, sendsz, recvsz, "udp")); 248 } 249 250 SVCXPRT * 251 svcfd_create(int fd, uint_t sendsize, uint_t recvsize) 252 { 253 return (svc_fd_create(fd, sendsize, recvsize)); 254 } 255 256 257 SVCXPRT * 258 svcudp_create(int fd) 259 { 260 return (svc_com_create(fd, UDPMSGSIZE, UDPMSGSIZE, "udp")); 261 } 262 263 SVCXPRT * 264 svcraw_create(void) 265 { 266 return (svc_raw_create()); 267 } 268 269 /* 270 * Bind a fd to a privileged IP port. 271 * This is slightly different from the code in netdir_options 272 * because it has a different interface - main thing is that it 273 * needs to know its own address. We also wanted to set the qlen. 274 * t_getname() can be used for those purposes and perhaps job can be done. 275 */ 276 int 277 __rpc_bindresvport_ipv6(int fd, struct sockaddr *sin, int *portp, int qlen, 278 char *fmly) 279 { 280 int res; 281 static in_port_t port, *sinport; 282 struct sockaddr_in6 myaddr; 283 int i; 284 struct t_bind tbindstr, *tres; 285 struct t_info tinfo; 286 extern mutex_t portnum_lock; 287 288 /* VARIABLES PROTECTED BY portnum_lock: port */ 289 290 #define STARTPORT 600 291 #define ENDPORT (IPPORT_RESERVED - 1) 292 #define NPORTS (ENDPORT - STARTPORT + 1) 293 294 if (sin == 0 && fmly == 0) { 295 errno = EINVAL; 296 return (-1); 297 } 298 if (geteuid()) { 299 errno = EACCES; 300 return (-1); 301 } 302 if ((i = t_getstate(fd)) != T_UNBND) { 303 if (t_errno == TBADF) 304 errno = EBADF; 305 if (i != -1) 306 errno = EISCONN; 307 return (-1); 308 } 309 if (sin == 0) { 310 sin = (struct sockaddr *)&myaddr; 311 get_myaddress_ipv6(fmly, sin); 312 } 313 if (sin->sa_family == AF_INET) { 314 /* LINTED pointer cast */ 315 sinport = &((struct sockaddr_in *)sin)->sin_port; 316 } else if (sin->sa_family == AF_INET6) { 317 /* LINTED pointer cast */ 318 sinport = &((struct sockaddr_in6 *)sin)->sin6_port; 319 } else { 320 errno = EPFNOSUPPORT; 321 return (-1); 322 } 323 324 /* Transform sockaddr to netbuf */ 325 if (t_getinfo(fd, &tinfo) == -1) { 326 return (-1); 327 } 328 /* LINTED pointer cast */ 329 tres = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR); 330 if (tres == NULL) 331 return (-1); 332 333 tbindstr.qlen = qlen; 334 tbindstr.addr.buf = (char *)sin; 335 tbindstr.addr.len = tbindstr.addr.maxlen = __rpc_get_a_size(tinfo.addr); 336 /* LINTED pointer cast */ 337 sin = (struct sockaddr *)tbindstr.addr.buf; 338 339 res = -1; 340 (void) mutex_lock(&portnum_lock); 341 if (port == 0) 342 port = (getpid() % NPORTS) + STARTPORT; 343 for (i = 0; i < NPORTS; i++) { 344 *sinport = htons(port++); 345 if (port > ENDPORT) 346 port = STARTPORT; 347 res = t_bind(fd, &tbindstr, tres); 348 if (res == 0) { 349 if ((tbindstr.addr.len == tres->addr.len) && 350 (memcmp(tbindstr.addr.buf, tres->addr.buf, 351 (int)tres->addr.len) == 0)) 352 break; 353 (void) t_unbind(fd); 354 res = -1; 355 } else if (t_errno != TSYSERR || errno != EADDRINUSE) 356 break; 357 } 358 (void) mutex_unlock(&portnum_lock); 359 360 if ((portp != NULL) && (res == 0)) 361 *portp = *sinport; 362 (void) t_free((char *)tres, T_BIND); 363 return (res); 364 } 365 366 int 367 __rpc_bindresvport(int fd, struct sockaddr_in *sin, int *portp, int qlen) 368 { 369 return (__rpc_bindresvport_ipv6(fd, (struct sockaddr *)sin, portp, 370 qlen, NC_INET)); 371 } 372 373 /* 374 * Get clients IP address. 375 * don't use gethostbyname, which would invoke yellow pages 376 * Remains only for backward compatibility reasons. 377 * Used mainly by the portmapper so that it can register 378 * with itself. Also used by pmap*() routines 379 */ 380 void 381 get_myaddress_ipv6(char *fmly, struct sockaddr *addr) 382 { 383 if (fmly != 0 && strcmp(fmly, NC_INET6) == 0) { 384 /* LINTED pointer cast */ 385 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr; 386 (void) memset(sin6, 0, sizeof (*sin6)); 387 sin6->sin6_family = AF_INET6; 388 sin6->sin6_port = htons(PMAPPORT); 389 if (__can_use_af(AF_INET6)) { 390 /* Local copy of in6addr_any to avoid -lsocket */ 391 struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; 392 sin6->sin6_addr = in6addr_any; 393 } else { 394 struct in_addr in4; 395 in4.s_addr = INADDR_ANY; 396 IN6_INADDR_TO_V4MAPPED(&in4, &sin6->sin6_addr); 397 } 398 } else { 399 /* LINTED pointer cast */ 400 struct sockaddr_in *sin = (struct sockaddr_in *)addr; 401 (void) memset(sin, 0, sizeof (*sin)); 402 sin->sin_family = AF_INET; 403 sin->sin_port = htons(PMAPPORT); 404 sin->sin_addr.s_addr = INADDR_ANY; 405 } 406 } 407 408 void 409 get_myaddress(struct sockaddr_in *addr) 410 { 411 get_myaddress_ipv6(0, (struct sockaddr *)addr); 412 } 413 414 /* 415 * Get port used by specified service on specified host. 416 * Exists for source compatibility only. 417 * Obsoleted by rpcb_getaddr(). 418 */ 419 ushort_t 420 getrpcport(char *host, rpcprog_t prognum, rpcvers_t versnum, 421 rpcprot_t proto) 422 { 423 struct sockaddr_in addr; 424 struct hostent *hp; 425 426 if ((hp = gethostbyname(host)) == NULL) 427 return (0); 428 (void) memcpy(&addr.sin_addr, hp->h_addr, hp->h_length); 429 addr.sin_family = AF_INET; 430 addr.sin_port = 0; 431 return (pmap_getport(&addr, prognum, versnum, proto)); 432 } 433 434 /* 435 * For connectionless "udp" transport. Obsoleted by rpc_call(). 436 */ 437 int 438 callrpc(char *host, rpcprog_t prognum, rpcvers_t versnum, rpcproc_t procnum, 439 xdrproc_t inproc, char *in, xdrproc_t outproc, char *out) 440 { 441 return ((int)rpc_call(host, prognum, versnum, procnum, inproc, 442 in, outproc, out, "udp")); 443 } 444 445 /* 446 * For connectionless kind of transport. Obsoleted by rpc_reg() 447 */ 448 int 449 registerrpc(rpcprog_t prognum, rpcvers_t versnum, rpcproc_t procnum, 450 char *(*progname)(), xdrproc_t inproc, xdrproc_t outproc) 451 { 452 return (rpc_reg(prognum, versnum, procnum, progname, inproc, 453 outproc, "udp")); 454 } 455 456 /* 457 * All the following clnt_broadcast stuff is convulated; it supports 458 * the earlier calling style of the callback function 459 */ 460 static pthread_key_t clnt_broadcast_key; 461 static resultproc_t clnt_broadcast_result_main; 462 463 /* 464 * Need to translate the netbuf address into sockaddr_in address. 465 * Dont care about netid here. 466 */ 467 /* ARGSUSED2 */ 468 static bool_t 469 rpc_wrap_bcast(char *resultp, struct netbuf *addr, struct netconfig *nconf) 470 { 471 resultproc_t clnt_broadcast_result; 472 473 clnt_broadcast_result = thr_main()? clnt_broadcast_result_main : 474 (resultproc_t)pthread_getspecific(clnt_broadcast_key); 475 return ((*clnt_broadcast_result)(resultp, 476 /* LINTED pointer cast */ 477 (struct sockaddr_in *)addr->buf)); 478 } 479 480 /* 481 * Broadcasts on UDP transport. Obsoleted by rpc_broadcast(). 482 */ 483 enum clnt_stat 484 clnt_broadcast(rpcprog_t prog, rpcvers_t vers, rpcproc_t proc, xdrproc_t xargs, 485 caddr_t argsp, xdrproc_t xresults, 486 caddr_t resultsp, resultproc_t eachresult) 487 { 488 extern mutex_t tsd_lock; 489 490 if (thr_main()) { 491 clnt_broadcast_result_main = eachresult; 492 } else { 493 if (clnt_broadcast_key == 0) { 494 (void) mutex_lock(&tsd_lock); 495 if (clnt_broadcast_key == 0) 496 (void) pthread_key_create(&clnt_broadcast_key, 497 NULL); 498 (void) mutex_unlock(&tsd_lock); 499 } 500 (void) pthread_setspecific(clnt_broadcast_key, 501 (void *)eachresult); 502 } 503 return (rpc_broadcast(prog, vers, proc, xargs, argsp, xresults, 504 resultsp, (resultproc_t)rpc_wrap_bcast, "udp")); 505 } 506 507 /* 508 * Create the client des authentication object. Obsoleted by 509 * authdes_seccreate(). 510 */ 511 AUTH * 512 authdes_create(char *servername, uint_t window, struct sockaddr_in *syncaddr, 513 des_block *ckey) 514 { 515 char *hostname = NULL; 516 517 if (syncaddr) { 518 /* 519 * Change addr to hostname, because that is the way 520 * new interface takes it. 521 */ 522 struct netconfig *nconf; 523 struct netbuf nb_syncaddr; 524 struct nd_hostservlist *hlist; 525 AUTH *nauth; 526 int fd; 527 struct t_info tinfo; 528 529 if ((nconf = __rpc_getconfip("udp")) == NULL && 530 (nconf = __rpc_getconfip("tcp")) == NULL) 531 goto fallback; 532 533 /* Transform sockaddr_in to netbuf */ 534 if ((fd = t_open(nconf->nc_device, O_RDWR, &tinfo)) == -1) { 535 (void) freenetconfigent(nconf); 536 goto fallback; 537 } 538 (void) t_close(fd); 539 nb_syncaddr.maxlen = nb_syncaddr.len = 540 __rpc_get_a_size(tinfo.addr); 541 nb_syncaddr.buf = (char *)syncaddr; 542 if (netdir_getbyaddr(nconf, &hlist, &nb_syncaddr)) { 543 (void) freenetconfigent(nconf); 544 goto fallback; 545 } 546 if (hlist && hlist->h_cnt > 0 && hlist->h_hostservs) 547 hostname = hlist->h_hostservs->h_host; 548 nauth = authdes_seccreate(servername, window, hostname, ckey); 549 (void) netdir_free((char *)hlist, ND_HOSTSERVLIST); 550 (void) freenetconfigent(nconf); 551 return (nauth); 552 } 553 fallback: 554 return (authdes_seccreate(servername, window, hostname, ckey)); 555 } 556 #endif /* PORTMAP */ 557