rpc.c revision 84221
1162271Srwatson/* $NetBSD: rpc.c,v 1.18 1998/01/23 19:27:45 thorpej Exp $ */ 2162271Srwatson 3172106Srwatson/* 4162271Srwatson * Copyright (c) 1992 Regents of the University of California. 5162271Srwatson * All rights reserved. 6162271Srwatson * 7162271Srwatson * This software was developed by the Computer Systems Engineering group 8162271Srwatson * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 9162271Srwatson * contributed to Berkeley. 10162271Srwatson * 11162271Srwatson * Redistribution and use in source and binary forms, with or without 12162271Srwatson * modification, are permitted provided that the following conditions 13162271Srwatson * are met: 14162271Srwatson * 1. Redistributions of source code must retain the above copyright 15162271Srwatson * notice, this list of conditions and the following disclaimer. 16162271Srwatson * 2. Redistributions in binary form must reproduce the above copyright 17162271Srwatson * notice, this list of conditions and the following disclaimer in the 18162271Srwatson * documentation and/or other materials provided with the distribution. 19162271Srwatson * 3. All advertising materials mentioning features or use of this software 20162271Srwatson * must display the following acknowledgement: 21162271Srwatson * This product includes software developed by the University of 22162271Srwatson * California, Lawrence Berkeley Laboratory and its contributors. 23162271Srwatson * 4. Neither the name of the University nor the names of its contributors 24162271Srwatson * may be used to endorse or promote products derived from this software 25162271Srwatson * without specific prior written permission. 26162271Srwatson * 27162271Srwatson * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28162271Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29162271Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30162271Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31162271Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32162271Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33162271Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34172106Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35172106Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36162271Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37162271Srwatson * SUCH DAMAGE. 38162271Srwatson * 39162271Srwatson * @(#) Header: rpc.c,v 1.12 93/09/28 08:31:56 leres Exp (LBL) 40162271Srwatson */ 41162271Srwatson 42162271Srwatson#include <sys/cdefs.h> 43162271Srwatson__FBSDID("$FreeBSD: head/lib/libstand/rpc.c 84221 2001-09-30 22:28:01Z dillon $"); 44162271Srwatson 45162271Srwatson/* 46172106Srwatson * RPC functions used by NFS and bootparams. 47172106Srwatson * Note that bootparams requires the ability to find out the 48172106Srwatson * address of the server from which its response has come. 49172106Srwatson * This is supported by keeping the IP/UDP headers in the 50172106Srwatson * buffer space provided by the caller. (See rpc_fromaddr) 51172106Srwatson */ 52172106Srwatson 53172106Srwatson#include <sys/param.h> 54172106Srwatson#include <sys/socket.h> 55162271Srwatson 56162271Srwatson#include <netinet/in.h> 57172106Srwatson#include <netinet/in_systm.h> 58172106Srwatson 59172106Srwatson#include <string.h> 60172106Srwatson 61162271Srwatson#include "rpcv2.h" 62162271Srwatson 63172106Srwatson#include "stand.h" 64172106Srwatson#include "net.h" 65172106Srwatson#include "netif.h" 66172106Srwatson#include "rpc.h" 67172106Srwatson 68172106Srwatsonstruct auth_info { 69172106Srwatson int32_t authtype; /* auth type */ 70172106Srwatson u_int32_t authlen; /* auth length */ 71172106Srwatson}; 72172106Srwatson 73172106Srwatsonstruct auth_unix { 74172106Srwatson int32_t ua_time; 75172106Srwatson int32_t ua_hostname; /* null */ 76172106Srwatson int32_t ua_uid; 77172106Srwatson int32_t ua_gid; 78172106Srwatson int32_t ua_gidlist; /* null */ 79172106Srwatson}; 80172106Srwatson 81172106Srwatsonstruct rpc_call { 82172106Srwatson u_int32_t rp_xid; /* request transaction id */ 83172106Srwatson int32_t rp_direction; /* call direction (0) */ 84172106Srwatson u_int32_t rp_rpcvers; /* rpc version (2) */ 85172106Srwatson u_int32_t rp_prog; /* program */ 86172106Srwatson u_int32_t rp_vers; /* version */ 87172106Srwatson u_int32_t rp_proc; /* procedure */ 88172106Srwatson}; 89172106Srwatson 90172106Srwatsonstruct rpc_reply { 91172106Srwatson u_int32_t rp_xid; /* request transaction id */ 92172106Srwatson int32_t rp_direction; /* call direction (1) */ 93172106Srwatson int32_t rp_astatus; /* accept status (0: accepted) */ 94172106Srwatson union { 95172106Srwatson u_int32_t rpu_errno; 96162271Srwatson struct { 97172106Srwatson struct auth_info rok_auth; 98162271Srwatson u_int32_t rok_status; 99172106Srwatson } rpu_rok; 100162271Srwatson } rp_u; 101172106Srwatson}; 102172106Srwatson 103172106Srwatson/* Local forwards */ 104172106Srwatsonstatic ssize_t recvrpc(struct iodesc *, void *, size_t, time_t); 105172106Srwatsonstatic int rpc_getport(struct iodesc *, n_long, n_long); 106172106Srwatson 107172106Srwatsonint rpc_xid; 108172106Srwatsonint rpc_port = 0x400; /* predecrement */ 109172106Srwatson 110172106Srwatson/* 111172106Srwatson * Make a rpc call; return length of answer 112172106Srwatson * Note: Caller must leave room for headers. 113172106Srwatson */ 114172106Srwatsonssize_t 115162271Srwatsonrpc_call(d, prog, vers, proc, sdata, slen, rdata, rlen) 116172106Srwatson register struct iodesc *d; 117172106Srwatson register n_long prog, vers, proc; 118172106Srwatson register void *sdata; 119172106Srwatson register size_t slen; 120162271Srwatson register void *rdata; 121172106Srwatson register size_t rlen; 122172106Srwatson{ 123172106Srwatson register ssize_t cc; 124172106Srwatson struct auth_info *auth; 125172106Srwatson struct rpc_call *call; 126172106Srwatson struct rpc_reply *reply; 127172106Srwatson char *send_head, *send_tail; 128172106Srwatson char *recv_head, *recv_tail; 129172106Srwatson n_long x; 130172106Srwatson int port; /* host order */ 131172106Srwatson 132172106Srwatson#ifdef RPC_DEBUG 133172106Srwatson if (debug) 134172106Srwatson printf("rpc_call: prog=0x%x vers=%d proc=%d\n", 135162271Srwatson prog, vers, proc); 136172106Srwatson#endif 137172106Srwatson 138172106Srwatson port = rpc_getport(d, prog, vers); 139172106Srwatson if (port == -1) 140162271Srwatson return (-1); 141172106Srwatson 142172106Srwatson d->destport = htons(port); 143172106Srwatson 144172106Srwatson /* 145172106Srwatson * Prepend authorization stuff and headers. 146172106Srwatson * Note, must prepend things in reverse order. 147172106Srwatson */ 148172106Srwatson send_head = sdata; 149172106Srwatson send_tail = (char *)sdata + slen; 150172106Srwatson 151172106Srwatson /* Auth verifier is always auth_null */ 152172106Srwatson send_head -= sizeof(*auth); 153172106Srwatson auth = (struct auth_info *)send_head; 154172106Srwatson auth->authtype = htonl(RPCAUTH_NULL); 155162271Srwatson auth->authlen = 0; 156172106Srwatson 157172106Srwatson#if 1 158172106Srwatson /* Auth credentials: always auth unix (as root) */ 159172106Srwatson send_head -= sizeof(struct auth_unix); 160162271Srwatson bzero(send_head, sizeof(struct auth_unix)); 161172106Srwatson send_head -= sizeof(*auth); 162172106Srwatson auth = (struct auth_info *)send_head; 163172106Srwatson auth->authtype = htonl(RPCAUTH_UNIX); 164172106Srwatson auth->authlen = htonl(sizeof(struct auth_unix)); 165172106Srwatson#else 166172106Srwatson /* Auth credentials: always auth_null (XXX OK?) */ 167172106Srwatson send_head -= sizeof(*auth); 168172106Srwatson auth = send_head; 169172106Srwatson auth->authtype = htonl(RPCAUTH_NULL); 170172106Srwatson auth->authlen = 0; 171172106Srwatson#endif 172172106Srwatson 173172106Srwatson /* RPC call structure. */ 174172106Srwatson send_head -= sizeof(*call); 175162271Srwatson call = (struct rpc_call *)send_head; 176172106Srwatson rpc_xid++; 177172106Srwatson call->rp_xid = htonl(rpc_xid); 178172106Srwatson call->rp_direction = htonl(RPC_CALL); 179162271Srwatson call->rp_rpcvers = htonl(RPC_VER2); 180172106Srwatson call->rp_prog = htonl(prog); 181172106Srwatson call->rp_vers = htonl(vers); 182172106Srwatson call->rp_proc = htonl(proc); 183162271Srwatson 184172106Srwatson /* Make room for the rpc_reply header. */ 185162271Srwatson recv_head = rdata; 186172106Srwatson recv_tail = (char *)rdata + rlen; 187172106Srwatson recv_head -= sizeof(*reply); 188172106Srwatson 189162271Srwatson cc = sendrecv(d, 190172106Srwatson sendudp, send_head, send_tail - send_head, 191172106Srwatson recvrpc, recv_head, recv_tail - recv_head); 192172106Srwatson 193162271Srwatson#ifdef RPC_DEBUG 194162271Srwatson if (debug) 195 printf("callrpc: cc=%ld rlen=%lu\n", (long)cc, (u_long)rlen); 196#endif 197 if (cc == -1) 198 return (-1); 199 200 if (cc <= sizeof(*reply)) { 201 errno = EBADRPC; 202 return (-1); 203 } 204 205 recv_tail = recv_head + cc; 206 207 /* 208 * Check the RPC reply status. 209 * The xid, dir, astatus were already checked. 210 */ 211 reply = (struct rpc_reply *)recv_head; 212 auth = &reply->rp_u.rpu_rok.rok_auth; 213 x = ntohl(auth->authlen); 214 if (x != 0) { 215#ifdef RPC_DEBUG 216 if (debug) 217 printf("callrpc: reply auth != NULL\n"); 218#endif 219 errno = EBADRPC; 220 return(-1); 221 } 222 x = ntohl(reply->rp_u.rpu_rok.rok_status); 223 if (x != 0) { 224 printf("callrpc: error = %ld\n", (long)x); 225 errno = EBADRPC; 226 return(-1); 227 } 228 recv_head += sizeof(*reply); 229 230 return (ssize_t)(recv_tail - recv_head); 231} 232 233/* 234 * Returns true if packet is the one we're waiting for. 235 * This just checks the XID, direction, acceptance. 236 * Remaining checks are done by callrpc 237 */ 238static ssize_t 239recvrpc(d, pkt, len, tleft) 240 register struct iodesc *d; 241 register void *pkt; 242 register size_t len; 243 time_t tleft; 244{ 245 register struct rpc_reply *reply; 246 ssize_t n; 247 int x; 248 249 errno = 0; 250#ifdef RPC_DEBUG 251 if (debug) 252 printf("recvrpc: called len=%lu\n", (u_long)len); 253#endif 254 255 n = readudp(d, pkt, len, tleft); 256 if (n <= (4 * 4)) 257 return -1; 258 259 reply = (struct rpc_reply *)pkt; 260 261 x = ntohl(reply->rp_xid); 262 if (x != rpc_xid) { 263#ifdef RPC_DEBUG 264 if (debug) 265 printf("recvrpc: rp_xid %d != xid %d\n", x, rpc_xid); 266#endif 267 return -1; 268 } 269 270 x = ntohl(reply->rp_direction); 271 if (x != RPC_REPLY) { 272#ifdef RPC_DEBUG 273 if (debug) 274 printf("recvrpc: rp_direction %d != REPLY\n", x); 275#endif 276 return -1; 277 } 278 279 x = ntohl(reply->rp_astatus); 280 if (x != RPC_MSGACCEPTED) { 281 errno = ntohl(reply->rp_u.rpu_errno); 282 printf("recvrpc: reject, astat=%d, errno=%d\n", x, errno); 283 return -1; 284 } 285 286 /* Return data count (thus indicating success) */ 287 return (n); 288} 289 290/* 291 * Given a pointer to a reply just received, 292 * dig out the IP address/port from the headers. 293 */ 294void 295rpc_fromaddr(pkt, addr, port) 296 void *pkt; 297 struct in_addr *addr; 298 u_short *port; 299{ 300 struct hackhdr { 301 /* Tail of IP header: just IP addresses */ 302 n_long ip_src; 303 n_long ip_dst; 304 /* UDP header: */ 305 u_int16_t uh_sport; /* source port */ 306 u_int16_t uh_dport; /* destination port */ 307 int16_t uh_ulen; /* udp length */ 308 u_int16_t uh_sum; /* udp checksum */ 309 /* RPC reply header: */ 310 struct rpc_reply rpc; 311 } *hhdr; 312 313 hhdr = ((struct hackhdr *)pkt) - 1; 314 addr->s_addr = hhdr->ip_src; 315 *port = hhdr->uh_sport; 316} 317 318/* 319 * RPC Portmapper cache 320 */ 321#define PMAP_NUM 8 /* need at most 5 pmap entries */ 322 323int rpc_pmap_num; 324struct pmap_list { 325 struct in_addr addr; /* server, net order */ 326 u_int prog; /* host order */ 327 u_int vers; /* host order */ 328 int port; /* host order */ 329} rpc_pmap_list[PMAP_NUM]; 330 331/* return port number in host order, or -1 */ 332int 333rpc_pmap_getcache(addr, prog, vers) 334 struct in_addr addr; /* server, net order */ 335 u_int prog; /* host order */ 336 u_int vers; /* host order */ 337{ 338 struct pmap_list *pl; 339 340 for (pl = rpc_pmap_list; pl < &rpc_pmap_list[rpc_pmap_num]; pl++) { 341 if (pl->addr.s_addr == addr.s_addr && 342 pl->prog == prog && pl->vers == vers ) 343 { 344 return (pl->port); 345 } 346 } 347 return (-1); 348} 349 350void 351rpc_pmap_putcache(addr, prog, vers, port) 352 struct in_addr addr; /* server, net order */ 353 u_int prog; /* host order */ 354 u_int vers; /* host order */ 355 int port; /* host order */ 356{ 357 struct pmap_list *pl; 358 359 /* Don't overflow cache... */ 360 if (rpc_pmap_num >= PMAP_NUM) { 361 /* ... just re-use the last entry. */ 362 rpc_pmap_num = PMAP_NUM - 1; 363#ifdef RPC_DEBUG 364 printf("rpc_pmap_putcache: cache overflow\n"); 365#endif 366 } 367 368 pl = &rpc_pmap_list[rpc_pmap_num]; 369 rpc_pmap_num++; 370 371 /* Cache answer */ 372 pl->addr = addr; 373 pl->prog = prog; 374 pl->vers = vers; 375 pl->port = port; 376} 377 378 379/* 380 * Request a port number from the port mapper. 381 * Returns the port in host order. 382 */ 383int 384rpc_getport(d, prog, vers) 385 register struct iodesc *d; 386 n_long prog; /* host order */ 387 n_long vers; /* host order */ 388{ 389 struct args { 390 n_long prog; /* call program */ 391 n_long vers; /* call version */ 392 n_long proto; /* call protocol */ 393 n_long port; /* call port (unused) */ 394 } *args; 395 struct res { 396 n_long port; 397 } *res; 398 struct { 399 n_long h[RPC_HEADER_WORDS]; 400 struct args d; 401 } sdata; 402 struct { 403 n_long h[RPC_HEADER_WORDS]; 404 struct res d; 405 n_long pad; 406 } rdata; 407 ssize_t cc; 408 int port; 409 410#ifdef RPC_DEBUG 411 if (debug) 412 printf("getport: prog=0x%x vers=%d\n", prog, vers); 413#endif 414 415 /* This one is fixed forever. */ 416 if (prog == PMAPPROG) 417 return (PMAPPORT); 418 419 /* Try for cached answer first */ 420 port = rpc_pmap_getcache(d->destip, prog, vers); 421 if (port != -1) 422 return (port); 423 424 args = &sdata.d; 425 args->prog = htonl(prog); 426 args->vers = htonl(vers); 427 args->proto = htonl(IPPROTO_UDP); 428 args->port = 0; 429 res = &rdata.d; 430 431 cc = rpc_call(d, PMAPPROG, PMAPVERS, PMAPPROC_GETPORT, 432 args, sizeof(*args), res, sizeof(*res)); 433 if (cc < sizeof(*res)) { 434 printf("getport: %s", strerror(errno)); 435 errno = EBADRPC; 436 return (-1); 437 } 438 port = (int)ntohl(res->port); 439 440 rpc_pmap_putcache(d->destip, prog, vers, port); 441 442 return (port); 443} 444