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 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 /* 40 * interface to pmap rpc service. 41 */ 42 #include "mt.h" 43 #include "rpc_mt.h" 44 #include <rpc/rpc.h> 45 #include <rpc/nettype.h> 46 #include <netdir.h> 47 #include <rpc/pmap_prot.h> 48 #include <rpc/pmap_clnt.h> 49 #include <rpc/pmap_rmt.h> 50 #include <string.h> 51 #include <syslog.h> 52 #include <netinet/in.h> 53 #include <sys/socket.h> 54 #include <unistd.h> 55 56 int use_portmapper = 0; 57 static const struct timeval timeout = { 5, 0 }; 58 static const struct timeval tottimeout = { 60, 0 }; 59 static const struct timeval rmttimeout = { 3, 0 }; 60 61 /* 62 * Solaris hasn't trully supported local portmappers since Solaris 2.4. 63 * 64 * In Solaris 2.0 the portmapper was replaced with rpcbind. Essentially 65 * rpcbind implements version 3 of the portmapper protocol. (The last 66 * version of the portmapper protocol while it was still called 67 * portmap was version 2.) The rpcbind protocol provides a lot 68 * of improvements over the portmap protocol. (Like the ability 69 * to bind to non AF_INET transports like TLI and to unregister 70 * individual transport providers instead of entire serivices.) 71 * 72 * So in Solaris 2.0 the portmapper was replace with rpcbind, but it 73 * wasn't until Solaris 2.5 that all the local portmapper code was 74 * modified to assume that the local processes managing rpc services 75 * always supported the rpcbind protocol. When this happened all the 76 * local portmap registration code was enhanced to translated any 77 * portmapper requests into rpcbind requests. This is a fine assumption 78 * for Solaris where we always have control over the local 79 * portmapper/rpcbind service and we can make sure that it always 80 * understands the rpcbind protocol. 81 * 82 * But this is a problem for BrandZ. In BrandZ we don't have contol over 83 * what local portmapper is running. (Unless we want to replace it.) 84 * In the Linux case, current Linux distributions don't support the 85 * rpcbind protocol, instead they support the old portmapper protocol 86 * (verison 2.) So to allow Solaris services to register with the 87 * Linux portmapper (which we need to support to allow us to run the 88 * native NFS daemons) there are two things that need to be done. 89 * 90 * - The classic interfaces for registering services with the version 2 91 * portmapper is via pmap_set() and pmap_unset(). In Solaris 2.5 these 92 * functions were changed to translate portmap requests into rpcbind 93 * requests. These interfaces need to be enhanced so that if we're 94 * trying to register with a portmapper instead of rpcbind, we don't 95 * translate the requests to rpcbind requests. 96 * 97 * - Libnsl provides lots of interfaces to simplify the creation of rpc 98 * services (see rpc_svc_*). Internally, the interfaces all assume 99 * that the local process that manages rpc services support the rpcbind 100 * protocol. To avoid having to update all rpc services that use these 101 * functions to be portmapper aware, we need to enhance these functions 102 * to support the portmapper protocol in addition to rpcbind. 103 * 104 * To address both these requirements we've introduced three key functions. 105 * 106 * __pmap_set() - Registers services using the portmapper version 2 107 * protocol. (Behaves like the Pre-Solaris 2.5 pmap_set()) 108 * 109 * __pmap_unset() - Unregisters services using the portmapper version 2 110 * protocol. (Behaves like the Pre-Solaris 2.5 pmap_unset()) 111 * 112 * __use_portmapper() - Tells libnsl if the local system expects 113 * the portmapper protocol versus the rpcbind protocol. 114 * 115 * If an rpc program uses this interface to tell libnsl 116 * that it want's to use portmap based services instead of 117 * rpcbind based services, then libnsl will internally 118 * replace attempts to register services via rpcbind 119 * with portmap. 120 */ 121 122 static CLIENT * 123 pmap_common(const struct netconfig *nconf, int *socket) 124 { 125 struct sockaddr_in sa_local; 126 CLIENT *client; 127 128 /* we only support tcp and udp */ 129 if ((nconf != NULL) && 130 (strcmp(nconf->nc_netid, "udp") != 0) && 131 (strcmp(nconf->nc_netid, "tcp") != 0)) 132 return (NULL); 133 134 /* try connecting to the portmapper via udp */ 135 get_myaddress(&sa_local); 136 client = clntudp_bufcreate(&sa_local, PMAPPROG, PMAPVERS, 137 timeout, socket, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); 138 if (client == NULL) { 139 /* try connecting to the portmapper via tcp */ 140 client = clnttcp_create(&sa_local, PMAPPROG, PMAPVERS, 141 socket, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); 142 if (client == NULL) 143 return (NULL); 144 } 145 146 return (client); 147 } 148 149 void 150 __use_portmapper(int p) 151 { 152 use_portmapper = p; 153 } 154 155 /* 156 * Set a mapping between program, version and address. 157 * Calls the portmapper service to do the mapping. 158 */ 159 bool_t 160 __pmap_set(const rpcprog_t program, const rpcvers_t version, 161 const struct netconfig *nconf, const struct netbuf *address) 162 { 163 struct sockaddr_in *sa; 164 struct pmap parms; 165 CLIENT *client; 166 bool_t rslt; 167 int socket = RPC_ANYSOCK; 168 169 /* address better be a sockaddr_in struct */ 170 if (address == NULL) 171 return (FALSE); 172 if (address->len != sizeof (struct sockaddr_in)) 173 return (FALSE); 174 175 /* get a connection to the portmapper */ 176 if (nconf == NULL) 177 return (FALSE); 178 if ((client = pmap_common(nconf, &socket)) == NULL) 179 return (FALSE); 180 181 /* LINTED pointer cast */ 182 sa = (struct sockaddr_in *)(address->buf); 183 184 /* initialize the portmapper request */ 185 parms.pm_prog = program; 186 parms.pm_vers = version; 187 parms.pm_port = ntohs(sa->sin_port); 188 parms.pm_prot = 189 (strcmp(nconf->nc_netid, "udp") == 0) ? IPPROTO_UDP : IPPROTO_TCP; 190 191 /* make the call */ 192 if (CLNT_CALL(client, PMAPPROC_SET, xdr_pmap, (caddr_t)&parms, 193 xdr_bool, (char *)&rslt, tottimeout) != RPC_SUCCESS) 194 rslt = FALSE; 195 196 CLNT_DESTROY(client); 197 (void) close(socket); 198 return (rslt); 199 } 200 201 /* 202 * Remove the mapping between program, version and port. 203 * Calls the portmapper service remotely to do the un-mapping. 204 */ 205 bool_t 206 __pmap_unset(const rpcprog_t program, const rpcvers_t version) 207 { 208 struct pmap parms; 209 CLIENT *client; 210 bool_t rslt; 211 int socket = RPC_ANYSOCK; 212 213 /* get a connection to the portmapper */ 214 if ((client = pmap_common(NULL, &socket)) == NULL) 215 return (FALSE); 216 217 /* initialize the portmapper request */ 218 parms.pm_prog = program; 219 parms.pm_vers = version; 220 parms.pm_port = 0; 221 parms.pm_prot = 0; 222 223 /* make the call */ 224 CLNT_CALL(client, PMAPPROC_UNSET, xdr_pmap, (caddr_t)&parms, 225 xdr_bool, (char *)&rslt, tottimeout); 226 CLNT_DESTROY(client); 227 (void) close(socket); 228 return (rslt); 229 } 230 231 /* 232 * Set a mapping between program, version and port. 233 * Calls the pmap service remotely to do the mapping. 234 */ 235 bool_t 236 pmap_set(rpcprog_t program, rpcvers_t version, rpcprot_t protocol, 237 ushort_t port) 238 { 239 bool_t rslt; 240 struct netbuf *na; 241 struct netconfig *nconf; 242 char buf[32]; 243 244 if ((protocol != IPPROTO_UDP) && (protocol != IPPROTO_TCP)) 245 return (FALSE); 246 nconf = __rpc_getconfip(protocol == IPPROTO_UDP ? "udp" : "tcp"); 247 if (!nconf) 248 return (FALSE); 249 (void) sprintf(buf, "0.0.0.0.%d.%d", port >> 8 & 0xff, port & 0xff); 250 na = uaddr2taddr(nconf, buf); 251 if (!na) { 252 freenetconfigent(nconf); 253 return (FALSE); 254 } 255 if (!use_portmapper) 256 rslt = rpcb_set(program, version, nconf, na); 257 else 258 rslt = __pmap_set(program, version, nconf, na); 259 netdir_free((char *)na, ND_ADDR); 260 freenetconfigent(nconf); 261 return (rslt); 262 } 263 264 /* 265 * Remove the mapping between program, version and port. 266 * Calls the pmap service remotely to do the un-mapping. 267 */ 268 bool_t 269 pmap_unset(rpcprog_t program, rpcvers_t version) 270 { 271 struct netconfig *nconf; 272 bool_t udp_rslt = FALSE; 273 bool_t tcp_rslt = FALSE; 274 275 if (use_portmapper) 276 return (__pmap_unset(program, version)); 277 278 nconf = __rpc_getconfip("udp"); 279 if (nconf) { 280 udp_rslt = rpcb_unset(program, version, nconf); 281 freenetconfigent(nconf); 282 } 283 nconf = __rpc_getconfip("tcp"); 284 if (nconf) { 285 tcp_rslt = rpcb_unset(program, version, nconf); 286 freenetconfigent(nconf); 287 } 288 /* 289 * XXX: The call may still succeed even if only one of the 290 * calls succeeded. This was the best that could be 291 * done for backward compatibility. 292 */ 293 return (tcp_rslt || udp_rslt); 294 } 295 296 /* 297 * Find the mapped port for program, version. 298 * Calls the pmap service remotely to do the lookup. 299 * Returns 0 if no map exists. 300 * 301 * XXX: It talks only to the portmapper and not to the rpcbind 302 * service. There may be implementations out there which do not 303 * run portmapper as a part of rpcbind. 304 */ 305 ushort_t 306 pmap_getport(struct sockaddr_in *address, rpcprog_t program, 307 rpcvers_t version, rpcprot_t protocol) 308 { 309 ushort_t port = 0; 310 int fd = RPC_ANYFD; 311 CLIENT *client; 312 struct pmap parms; 313 314 address->sin_port = htons(PMAPPORT); 315 client = clntudp_bufcreate(address, PMAPPROG, PMAPVERS, timeout, 316 &fd, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); 317 if (client != NULL) { 318 parms.pm_prog = program; 319 parms.pm_vers = version; 320 parms.pm_prot = protocol; 321 parms.pm_port = 0; /* not needed or used */ 322 if (CLNT_CALL(client, PMAPPROC_GETPORT, (xdrproc_t)xdr_pmap, 323 (caddr_t)&parms, (xdrproc_t)xdr_u_short, 324 (caddr_t)&port, tottimeout) != RPC_SUCCESS) { 325 rpc_createerr.cf_stat = RPC_PMAPFAILURE; 326 clnt_geterr(client, &rpc_createerr.cf_error); 327 } else if (port == 0) { 328 rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; 329 } 330 CLNT_DESTROY(client); 331 } 332 address->sin_port = 0; 333 return (port); 334 } 335 336 /* 337 * Get a copy of the current port maps. 338 * Calls the pmap service remotely to do get the maps. 339 */ 340 struct pmaplist * 341 pmap_getmaps(struct sockaddr_in *address) 342 { 343 pmaplist_ptr head = NULL; 344 int fd = RPC_ANYFD; 345 struct timeval minutetimeout; 346 CLIENT *client; 347 348 minutetimeout.tv_sec = 60; 349 minutetimeout.tv_usec = 0; 350 address->sin_port = htons(PMAPPORT); 351 client = clnttcp_create(address, PMAPPROG, PMAPVERS, &fd, 50, 500); 352 if (client != NULL) { 353 if (CLNT_CALL(client, PMAPPROC_DUMP, (xdrproc_t)xdr_void, 354 NULL, (xdrproc_t)xdr_pmaplist_ptr, 355 (caddr_t)&head, minutetimeout) != RPC_SUCCESS) { 356 (void) syslog(LOG_ERR, "%s", 357 clnt_sperror(client, "pmap_getmaps rpc problem")); 358 } 359 CLNT_DESTROY(client); 360 } 361 address->sin_port = 0; 362 return ((struct pmaplist *)head); 363 } 364 365 /* 366 * pmapper remote-call-service interface. 367 * This routine is used to call the pmapper remote call service 368 * which will look up a service program in the port maps, and then 369 * remotely call that routine with the given parameters. This allows 370 * programs to do a lookup and call in one step. 371 */ 372 enum clnt_stat 373 pmap_rmtcall(struct sockaddr_in *addr, rpcprog_t prog, rpcvers_t vers, 374 rpcproc_t proc, xdrproc_t xdrargs, caddr_t argsp, xdrproc_t xdrres, 375 caddr_t resp, struct timeval tout, rpcport_t *port_ptr) 376 { 377 int fd = RPC_ANYFD; 378 CLIENT *client; 379 struct p_rmtcallargs a; 380 struct p_rmtcallres r; 381 enum clnt_stat stat; 382 short tmp = addr->sin_port; 383 384 addr->sin_port = htons(PMAPPORT); 385 client = clntudp_create(addr, PMAPPROG, PMAPVERS, rmttimeout, &fd); 386 if (client != NULL) { 387 a.prog = prog; 388 a.vers = vers; 389 a.proc = proc; 390 a.args.args_val = argsp; 391 a.xdr_args = xdrargs; 392 r.res.res_val = resp; 393 r.xdr_res = xdrres; 394 stat = CLNT_CALL(client, PMAPPROC_CALLIT, 395 (xdrproc_t)xdr_rmtcallargs, 396 (caddr_t)&a, (xdrproc_t)xdr_rmtcallres, 397 (caddr_t)&r, tout); 398 CLNT_DESTROY(client); 399 } else { 400 stat = RPC_FAILED; 401 } 402 addr->sin_port = tmp; 403 *port_ptr = r.port; 404 return (stat); 405 } 406 407 #endif /* PORTMAP */ 408