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