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. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 /* 38 * RPC functions used by NFS and bootparams. 39 * Note that bootparams requires the ability to find out the 40 * address of the server from which its response has come. 41 * This is supported by keeping the IP/UDP headers in the 42 * buffer space provided by the caller. (See rpc_fromaddr) 43 */ 44 45 #include <sys/param.h> 46 #include <sys/socket.h> 47 48 #include <netinet/in.h> 49 #include <netinet/in_systm.h> 50 51 #include <string.h> 52 53 #include "rpcv2.h" 54 55 #include "stand.h" 56 #include "net.h" 57 #include "netif.h" 58 #include "rpc.h" 59 60 struct auth_info { 61 int32_t authtype; /* auth type */ 62 uint32_t authlen; /* auth length */ 63 }; 64 65 struct auth_unix { 66 int32_t ua_time; 67 int32_t ua_hostname; /* null */ 68 int32_t ua_uid; 69 int32_t ua_gid; 70 int32_t ua_gidlist; /* null */ 71 }; 72 73 struct rpc_call { 74 uint32_t rp_xid; /* request transaction id */ 75 int32_t rp_direction; /* call direction (0) */ 76 uint32_t rp_rpcvers; /* rpc version (2) */ 77 uint32_t rp_prog; /* program */ 78 uint32_t rp_vers; /* version */ 79 uint32_t rp_proc; /* procedure */ 80 }; 81 82 struct rpc_reply { 83 uint32_t rp_xid; /* request transaction id */ 84 int32_t rp_direction; /* call direction (1) */ 85 int32_t rp_astatus; /* accept status (0: accepted) */ 86 union { 87 uint32_t rpu_errno; 88 struct { 89 struct auth_info rok_auth; 90 uint32_t rok_status; 91 } rpu_rok; 92 } rp_u; 93 }; 94 95 /* Local forwards */ 96 static ssize_t recvrpc(struct iodesc *, void **, void **, time_t, void *); 97 static int rpc_getport(struct iodesc *, n_long, n_long); 98 99 int rpc_xid; 100 int rpc_port = 0x400; /* predecrement */ 101 102 /* 103 * Make a rpc call; return length of answer 104 * Note: Caller must leave room for headers. 105 */ 106 ssize_t 107 rpc_call(struct iodesc *d, n_long prog, n_long vers, n_long proc, 108 void *sdata, size_t slen, void **rdata, void **pkt) 109 { 110 ssize_t cc, rsize; 111 struct auth_info *auth; 112 struct rpc_call *call; 113 struct rpc_reply *reply; 114 char *send_head, *send_tail; 115 void *ptr; 116 n_long x; 117 int port; /* host order */ 118 119 #ifdef RPC_DEBUG 120 if (debug) 121 printf("rpc_call: prog=0x%x vers=%d proc=%d\n", 122 prog, vers, proc); 123 #endif 124 125 port = rpc_getport(d, prog, vers); 126 if (port == -1) 127 return (-1); 128 129 d->destport = htons(port); 130 131 /* 132 * Prepend authorization stuff and headers. 133 * Note, must prepend things in reverse order. 134 */ 135 send_head = sdata; 136 send_tail = (char *)sdata + slen; 137 138 /* Auth verifier is always auth_null */ 139 send_head -= sizeof(*auth); 140 auth = (struct auth_info *)send_head; 141 auth->authtype = htonl(RPCAUTH_NULL); 142 auth->authlen = 0; 143 144 /* Auth credentials: always auth unix (as root) */ 145 send_head -= sizeof(struct auth_unix); 146 bzero(send_head, sizeof(struct auth_unix)); 147 send_head -= sizeof(*auth); 148 auth = (struct auth_info *)send_head; 149 auth->authtype = htonl(RPCAUTH_UNIX); 150 auth->authlen = htonl(sizeof(struct auth_unix)); 151 152 /* RPC call structure. */ 153 send_head -= sizeof(*call); 154 call = (struct rpc_call *)send_head; 155 rpc_xid++; 156 call->rp_xid = htonl(rpc_xid); 157 call->rp_direction = htonl(RPC_CALL); 158 call->rp_rpcvers = htonl(RPC_VER2); 159 call->rp_prog = htonl(prog); 160 call->rp_vers = htonl(vers); 161 call->rp_proc = htonl(proc); 162 163 ptr = NULL; 164 cc = sendrecv(d, 165 sendudp, send_head, send_tail - send_head, 166 recvrpc, &ptr, (void **)&reply, NULL); 167 168 #ifdef RPC_DEBUG 169 if (debug) 170 printf("callrpc: cc=%zd\n", cc); 171 #endif 172 if (cc == -1) 173 return (-1); 174 175 if (cc <= sizeof(*reply)) { 176 errno = EBADRPC; 177 free(ptr); 178 return (-1); 179 } 180 181 /* 182 * Check the RPC reply status. 183 * The xid, dir, astatus were already checked. 184 */ 185 auth = &reply->rp_u.rpu_rok.rok_auth; 186 x = ntohl(auth->authlen); 187 if (x != 0) { 188 #ifdef RPC_DEBUG 189 if (debug) 190 printf("callrpc: reply auth != NULL\n"); 191 #endif 192 errno = EBADRPC; 193 free(ptr); 194 return (-1); 195 } 196 x = ntohl(reply->rp_u.rpu_rok.rok_status); 197 if (x != 0) { 198 printf("callrpc: error = %ld\n", (long)x); 199 errno = EBADRPC; 200 free(ptr); 201 return (-1); 202 } 203 204 rsize = cc - sizeof(*reply); 205 *rdata = (void *)((uintptr_t)reply + sizeof(*reply)); 206 *pkt = ptr; 207 return (rsize); 208 } 209 210 /* 211 * Returns true if packet is the one we're waiting for. 212 * This just checks the XID, direction, acceptance. 213 * Remaining checks are done by callrpc 214 */ 215 static ssize_t 216 recvrpc(struct iodesc *d, void **pkt, void **payload, time_t tleft, void *extra) 217 { 218 void *ptr; 219 struct rpc_reply *reply; 220 ssize_t n; 221 int x; 222 223 errno = 0; 224 #ifdef RPC_DEBUG 225 if (debug) 226 printf("recvrpc: called\n"); 227 #endif 228 229 ptr = NULL; 230 n = readudp(d, &ptr, (void **)&reply, tleft); 231 if (n <= (4 * 4)) { 232 free(ptr); 233 return (-1); 234 } 235 236 x = ntohl(reply->rp_xid); 237 if (x != rpc_xid) { 238 #ifdef RPC_DEBUG 239 if (debug) 240 printf("recvrpc: rp_xid %d != xid %d\n", x, rpc_xid); 241 #endif 242 free(ptr); 243 return (-1); 244 } 245 246 x = ntohl(reply->rp_direction); 247 if (x != RPC_REPLY) { 248 #ifdef RPC_DEBUG 249 if (debug) 250 printf("recvrpc: rp_direction %d != REPLY\n", x); 251 #endif 252 free(ptr); 253 return (-1); 254 } 255 256 x = ntohl(reply->rp_astatus); 257 if (x != RPC_MSGACCEPTED) { 258 errno = ntohl(reply->rp_u.rpu_errno); 259 printf("recvrpc: reject, astat=%d, errno=%d\n", x, errno); 260 free(ptr); 261 return (-1); 262 } 263 264 *pkt = ptr; 265 *payload = reply; 266 /* Return data count (thus indicating success) */ 267 return (n); 268 } 269 270 /* 271 * Given a pointer to a reply just received, 272 * dig out the IP address/port from the headers. 273 */ 274 void 275 rpc_fromaddr(void *pkt, struct in_addr *addr, u_short *port) 276 { 277 struct hackhdr { 278 /* Tail of IP header: just IP addresses */ 279 n_long ip_src; 280 n_long ip_dst; 281 /* UDP header: */ 282 uint16_t uh_sport; /* source port */ 283 uint16_t uh_dport; /* destination port */ 284 int16_t uh_ulen; /* udp length */ 285 uint16_t uh_sum; /* udp checksum */ 286 /* RPC reply header: */ 287 struct rpc_reply rpc; 288 } *hhdr; 289 290 hhdr = ((struct hackhdr *)pkt) - 1; 291 addr->s_addr = hhdr->ip_src; 292 *port = hhdr->uh_sport; 293 } 294 295 /* 296 * RPC Portmapper cache 297 */ 298 #define PMAP_NUM 8 /* need at most 5 pmap entries */ 299 300 int rpc_pmap_num; 301 struct pmap_list { 302 struct in_addr addr; /* server, net order */ 303 u_int prog; /* host order */ 304 u_int vers; /* host order */ 305 int port; /* host order */ 306 } rpc_pmap_list[PMAP_NUM]; 307 308 /* 309 * return port number in host order, or -1. 310 * arguments are: 311 * addr .. server, net order. 312 * prog .. host order. 313 * vers .. host order. 314 */ 315 int 316 rpc_pmap_getcache(struct in_addr addr, u_int prog, u_int vers) 317 { 318 struct pmap_list *pl; 319 320 for (pl = rpc_pmap_list; pl < &rpc_pmap_list[rpc_pmap_num]; pl++) { 321 if (pl->addr.s_addr == addr.s_addr && 322 pl->prog == prog && pl->vers == vers ) 323 { 324 return (pl->port); 325 } 326 } 327 return (-1); 328 } 329 330 /* 331 * arguments are: 332 * addr .. server, net order. 333 * prog .. host order. 334 * vers .. host order. 335 * port .. host order. 336 */ 337 void 338 rpc_pmap_putcache(struct in_addr addr, u_int prog, u_int vers, int port) 339 { 340 struct pmap_list *pl; 341 342 /* Don't overflow cache... */ 343 if (rpc_pmap_num >= PMAP_NUM) { 344 /* ... just re-use the last entry. */ 345 rpc_pmap_num = PMAP_NUM - 1; 346 #ifdef RPC_DEBUG 347 printf("rpc_pmap_putcache: cache overflow\n"); 348 #endif 349 } 350 351 pl = &rpc_pmap_list[rpc_pmap_num]; 352 rpc_pmap_num++; 353 354 /* Cache answer */ 355 pl->addr = addr; 356 pl->prog = prog; 357 pl->vers = vers; 358 pl->port = port; 359 } 360 361 362 /* 363 * Request a port number from the port mapper. 364 * Returns the port in host order. 365 * prog and vers are host order. 366 */ 367 int 368 rpc_getport(struct iodesc *d, n_long prog, n_long vers) 369 { 370 struct args { 371 n_long prog; /* call program */ 372 n_long vers; /* call version */ 373 n_long proto; /* call protocol */ 374 n_long port; /* call port (unused) */ 375 } *args; 376 struct res { 377 n_long port; 378 } *res; 379 struct { 380 n_long h[RPC_HEADER_WORDS]; 381 struct args d; 382 } sdata; 383 void *pkt; 384 ssize_t cc; 385 int port; 386 387 #ifdef RPC_DEBUG 388 if (debug) 389 printf("%s: prog=0x%x vers=%d\n", __func__, prog, vers); 390 #endif 391 392 /* This one is fixed forever. */ 393 if (prog == PMAPPROG) { 394 port = PMAPPORT; 395 goto out; 396 } 397 398 /* Try for cached answer first */ 399 port = rpc_pmap_getcache(d->destip, prog, vers); 400 if (port != -1) 401 goto out; 402 403 args = &sdata.d; 404 args->prog = htonl(prog); 405 args->vers = htonl(vers); 406 args->proto = htonl(IPPROTO_UDP); 407 args->port = 0; 408 pkt = NULL; 409 410 cc = rpc_call(d, PMAPPROG, PMAPVERS, PMAPPROC_GETPORT, 411 args, sizeof(*args), (void **)&res, &pkt); 412 if (cc < sizeof(*res)) { 413 printf("getport: %s", strerror(errno)); 414 errno = EBADRPC; 415 free(pkt); 416 return (-1); 417 } 418 port = (int)ntohl(res->port); 419 free(pkt); 420 421 rpc_pmap_putcache(d->destip, prog, vers, port); 422 423 out: 424 #ifdef RPC_DEBUG 425 if (debug) 426 printf("%s: port=%u\n", __func__, port); 427 #endif 428 return (port); 429 } 430