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