rpc.c revision 84221
1/* $NetBSD: rpc.c,v 1.18 1998/01/23 19:27:45 thorpej Exp $ */ 2 3/* 4 * Copyright (c) 1992 Regents of the University of California. 5 * All rights reserved. 6 * 7 * This software was developed by the Computer Systems Engineering group 8 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 9 * contributed to Berkeley. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the University of 22 * California, Lawrence Berkeley Laboratory and its contributors. 23 * 4. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 * 39 * @(#) Header: rpc.c,v 1.12 93/09/28 08:31:56 leres Exp (LBL) 40 */ 41 42#include <sys/cdefs.h> 43__FBSDID("$FreeBSD: head/lib/libstand/rpc.c 84221 2001-09-30 22:28:01Z dillon $"); 44 45/* 46 * RPC functions used by NFS and bootparams. 47 * Note that bootparams requires the ability to find out the 48 * address of the server from which its response has come. 49 * This is supported by keeping the IP/UDP headers in the 50 * buffer space provided by the caller. (See rpc_fromaddr) 51 */ 52 53#include <sys/param.h> 54#include <sys/socket.h> 55 56#include <netinet/in.h> 57#include <netinet/in_systm.h> 58 59#include <string.h> 60 61#include "rpcv2.h" 62 63#include "stand.h" 64#include "net.h" 65#include "netif.h" 66#include "rpc.h" 67 68struct auth_info { 69 int32_t authtype; /* auth type */ 70 u_int32_t authlen; /* auth length */ 71}; 72 73struct auth_unix { 74 int32_t ua_time; 75 int32_t ua_hostname; /* null */ 76 int32_t ua_uid; 77 int32_t ua_gid; 78 int32_t ua_gidlist; /* null */ 79}; 80 81struct rpc_call { 82 u_int32_t rp_xid; /* request transaction id */ 83 int32_t rp_direction; /* call direction (0) */ 84 u_int32_t rp_rpcvers; /* rpc version (2) */ 85 u_int32_t rp_prog; /* program */ 86 u_int32_t rp_vers; /* version */ 87 u_int32_t rp_proc; /* procedure */ 88}; 89 90struct rpc_reply { 91 u_int32_t rp_xid; /* request transaction id */ 92 int32_t rp_direction; /* call direction (1) */ 93 int32_t rp_astatus; /* accept status (0: accepted) */ 94 union { 95 u_int32_t rpu_errno; 96 struct { 97 struct auth_info rok_auth; 98 u_int32_t rok_status; 99 } rpu_rok; 100 } rp_u; 101}; 102 103/* Local forwards */ 104static ssize_t recvrpc(struct iodesc *, void *, size_t, time_t); 105static int rpc_getport(struct iodesc *, n_long, n_long); 106 107int rpc_xid; 108int rpc_port = 0x400; /* predecrement */ 109 110/* 111 * Make a rpc call; return length of answer 112 * Note: Caller must leave room for headers. 113 */ 114ssize_t 115rpc_call(d, prog, vers, proc, sdata, slen, rdata, rlen) 116 register struct iodesc *d; 117 register n_long prog, vers, proc; 118 register void *sdata; 119 register size_t slen; 120 register void *rdata; 121 register size_t rlen; 122{ 123 register ssize_t cc; 124 struct auth_info *auth; 125 struct rpc_call *call; 126 struct rpc_reply *reply; 127 char *send_head, *send_tail; 128 char *recv_head, *recv_tail; 129 n_long x; 130 int port; /* host order */ 131 132#ifdef RPC_DEBUG 133 if (debug) 134 printf("rpc_call: prog=0x%x vers=%d proc=%d\n", 135 prog, vers, proc); 136#endif 137 138 port = rpc_getport(d, prog, vers); 139 if (port == -1) 140 return (-1); 141 142 d->destport = htons(port); 143 144 /* 145 * Prepend authorization stuff and headers. 146 * Note, must prepend things in reverse order. 147 */ 148 send_head = sdata; 149 send_tail = (char *)sdata + slen; 150 151 /* Auth verifier is always auth_null */ 152 send_head -= sizeof(*auth); 153 auth = (struct auth_info *)send_head; 154 auth->authtype = htonl(RPCAUTH_NULL); 155 auth->authlen = 0; 156 157#if 1 158 /* Auth credentials: always auth unix (as root) */ 159 send_head -= sizeof(struct auth_unix); 160 bzero(send_head, sizeof(struct auth_unix)); 161 send_head -= sizeof(*auth); 162 auth = (struct auth_info *)send_head; 163 auth->authtype = htonl(RPCAUTH_UNIX); 164 auth->authlen = htonl(sizeof(struct auth_unix)); 165#else 166 /* Auth credentials: always auth_null (XXX OK?) */ 167 send_head -= sizeof(*auth); 168 auth = send_head; 169 auth->authtype = htonl(RPCAUTH_NULL); 170 auth->authlen = 0; 171#endif 172 173 /* RPC call structure. */ 174 send_head -= sizeof(*call); 175 call = (struct rpc_call *)send_head; 176 rpc_xid++; 177 call->rp_xid = htonl(rpc_xid); 178 call->rp_direction = htonl(RPC_CALL); 179 call->rp_rpcvers = htonl(RPC_VER2); 180 call->rp_prog = htonl(prog); 181 call->rp_vers = htonl(vers); 182 call->rp_proc = htonl(proc); 183 184 /* Make room for the rpc_reply header. */ 185 recv_head = rdata; 186 recv_tail = (char *)rdata + rlen; 187 recv_head -= sizeof(*reply); 188 189 cc = sendrecv(d, 190 sendudp, send_head, send_tail - send_head, 191 recvrpc, recv_head, recv_tail - recv_head); 192 193#ifdef RPC_DEBUG 194 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