1 /* 2 * Copyright (c) 1989 Jan-Simon Pendry 3 * Copyright (c) 1989 Imperial College of Science, Technology & Medicine 4 * Copyright (c) 1989, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Jan-Simon Pendry at Imperial College, London. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the University of 21 * California, Berkeley and its contributors. 22 * 4. Neither the name of the University nor the names of its contributors 23 * may be used to endorse or promote products derived from this software 24 * without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 * 38 * from: @(#)rpc_fwd.c 8.1 (Berkeley) 6/6/93 39 * $Id: rpc_fwd.c,v 1.1.1.1 1995/10/18 08:47:12 deraadt Exp $ 40 */ 41 42 /* 43 * RPC packet forwarding 44 */ 45 46 #include "am.h" 47 #include <sys/ioctl.h> 48 #ifndef F_SETFL 49 #include <fcntl.h> 50 #endif /* F_SETFL */ 51 #ifndef FNDELAY 52 #include <sys/file.h> 53 #endif /* FNDELAY */ 54 55 /* 56 * Note that the ID field in the external packet is only 57 * ever treated as a 32 bit opaque data object, so there 58 * is no need to convert to and from network byte ordering. 59 */ 60 61 /* 62 * Each pending reply has an rpc_forward structure 63 * associated with it. These have a 15 second lifespan. 64 * If a new structure is required, then an expired 65 * one will be re-allocated if available, otherwise a fresh 66 * one is allocated. Whenever a reply is received the 67 * structure is discarded. 68 */ 69 typedef struct rpc_forward rpc_forward; 70 struct rpc_forward { 71 qelem rf_q; /* Linked list */ 72 time_t rf_ttl; /* Time to live */ 73 u_int rf_xid; /* Packet id */ 74 u_int rf_oldid; /* Original packet id */ 75 fwd_fun rf_fwd; /* Forwarding function */ 76 voidp rf_ptr; 77 struct sockaddr_in rf_sin; 78 }; 79 80 /* 81 * Head of list of pending replies 82 */ 83 extern qelem rpc_head; 84 qelem rpc_head = { &rpc_head, &rpc_head }; 85 86 static u_int xid; 87 #define XID_ALLOC() (xid++) 88 89 #define MAX_PACKET_SIZE 8192 /* Maximum UDP packet size */ 90 91 int fwd_sock; 92 93 /* 94 * Allocate a rely structure 95 */ 96 static rpc_forward *fwd_alloc() 97 { 98 time_t now = clocktime(); 99 rpc_forward *p = 0, *p2; 100 101 #ifdef DEBUG 102 /*dlog("fwd_alloca: rpc_head = %#x", rpc_head.q_forw);*/ 103 #endif /* DEBUG */ 104 /* 105 * First search for an existing expired one. 106 */ 107 ITER(p2, rpc_forward, &rpc_head) { 108 if (p2->rf_ttl <= now) { 109 p = p2; 110 break; 111 } 112 } 113 114 /* 115 * If one couldn't be found then allocate 116 * a new structure and link it at the 117 * head of the list. 118 */ 119 if (p) { 120 /* 121 * Call forwarding function to say that 122 * this message was junked. 123 */ 124 #ifdef DEBUG 125 dlog("Re-using packet forwarding slot - id %#x", p->rf_xid); 126 #endif /* DEBUG */ 127 if (p->rf_fwd) 128 (*p->rf_fwd)(0, 0, 0, &p->rf_sin, p->rf_ptr, FALSE); 129 rem_que(&p->rf_q); 130 } else { 131 p = ALLOC(rpc_forward); 132 } 133 ins_que(&p->rf_q, &rpc_head); 134 135 /* 136 * Set the time to live field 137 * Timeout in 43 seconds 138 */ 139 p->rf_ttl = now + 43; 140 141 #ifdef DEBUG 142 /*dlog("fwd_alloca: rpc_head = %#x", rpc_head.q_forw);*/ 143 #endif /* DEBUG */ 144 return p; 145 } 146 147 /* 148 * Free an allocated reply structure. 149 * First unlink it from the list, then 150 * discard it. 151 */ 152 static void fwd_free(p) 153 rpc_forward *p; 154 { 155 #ifdef DEBUG 156 /*dlog("fwd_free: rpc_head = %#x", rpc_head.q_forw);*/ 157 #endif /* DEBUG */ 158 rem_que(&p->rf_q); 159 #ifdef DEBUG 160 /*dlog("fwd_free: rpc_head = %#x", rpc_head.q_forw);*/ 161 #endif /* DEBUG */ 162 free((voidp) p); 163 } 164 165 /* 166 * Initialise the RPC forwarder 167 */ 168 int fwd_init() 169 { 170 int on = 1; 171 172 /* 173 * Create ping socket 174 */ 175 fwd_sock = socket(AF_INET, SOCK_DGRAM, 0); 176 if (fwd_sock < 0) { 177 plog(XLOG_ERROR, "Unable to create RPC forwarding socket: %m"); 178 return errno; 179 } 180 181 /* 182 * Some things we talk to require a priv port - so make one here 183 */ 184 if (bind_resv_port(fwd_sock, (unsigned short *) 0) < 0) 185 plog(XLOG_ERROR, "can't bind privileged port"); 186 187 if (fcntl(fwd_sock, F_SETFL, FNDELAY) < 0 && 188 ioctl(fwd_sock, FIONBIO, &on) < 0) { 189 plog(XLOG_ERROR, "Can't set non-block on forwarding socket: %m"); 190 return errno; 191 } 192 193 return 0; 194 } 195 196 /* 197 * Locate a packet in the forwarding list 198 */ 199 static rpc_forward *fwd_locate(id) 200 u_int id; 201 { 202 rpc_forward *p; 203 204 ITER(p, rpc_forward, &rpc_head) { 205 if (p->rf_xid == id) 206 return p; 207 } 208 209 return 0; 210 } 211 212 /* 213 * This is called to forward a packet to another 214 * RPC server. The message id is changed and noted 215 * so that when a reply appears we can tie it up 216 * correctly. Just matching the reply's source address 217 * would not work because it might come from a 218 * different address. 219 */ 220 int fwd_packet(type_id, pkt, len, fwdto, replyto, i, cb) 221 int type_id; 222 voidp pkt; 223 int len; 224 struct sockaddr_in *fwdto, *replyto; 225 voidp i; 226 fwd_fun cb; 227 { 228 rpc_forward *p; 229 u_int *pkt_int; 230 int error; 231 232 if ((int)amd_state >= (int)Finishing) 233 return ENOENT; 234 235 /* 236 * See if the type_id is fully specified. 237 * If so, then discard any old entries 238 * for this id. 239 * Otherwise make sure the type_id is 240 * fully qualified by allocating an id here. 241 */ 242 #ifdef DEBUG 243 switch (type_id & RPC_XID_MASK) { 244 case RPC_XID_PORTMAP: dlog("Sending PORTMAP request"); break; 245 case RPC_XID_MOUNTD: dlog("Sending MOUNTD request %#x", type_id); break; 246 case RPC_XID_NFSPING: dlog("Sending NFS ping"); break; 247 default: dlog("UNKNOWN RPC XID"); break; 248 } 249 #endif /* DEBUG */ 250 251 if (type_id & ~RPC_XID_MASK) { 252 #ifdef DEBUG 253 /*dlog("Fully qualified rpc type provided");*/ 254 #endif /* DEBUG */ 255 p = fwd_locate(type_id); 256 if (p) { 257 #ifdef DEBUG 258 dlog("Discarding earlier rpc fwd handle"); 259 #endif /* DEBUG */ 260 fwd_free(p); 261 } 262 } else { 263 #ifdef DEBUG 264 dlog("Allocating a new xid..."); 265 #endif /* DEBUG */ 266 type_id = MK_RPC_XID(type_id, XID_ALLOC()); 267 } 268 269 p = fwd_alloc(); 270 if (!p) 271 return ENOBUFS; 272 273 error = 0; 274 275 pkt_int = (u_int *) pkt; 276 277 /* 278 * Get the original packet id 279 */ 280 p->rf_oldid = *pkt_int; 281 282 /* 283 * Replace with newly allocated id 284 */ 285 p->rf_xid = *pkt_int = type_id; 286 287 /* 288 * The sendto may fail if, for example, the route 289 * to a remote host is lost because an intermediate 290 * gateway has gone down. Important to fill in the 291 * rest of "p" otherwise nasty things happen later... 292 */ 293 #ifdef DEBUG 294 { char dq[20]; 295 dlog("Sending packet id %#x to %s.%d", p->rf_xid, inet_dquad(dq, fwdto->sin_addr.s_addr), ntohs(fwdto->sin_port)); 296 } 297 #endif /* DEBUG */ 298 if (sendto(fwd_sock, (char *) pkt, len, 0, 299 (struct sockaddr *) fwdto, sizeof(*fwdto)) < 0) 300 error = errno; 301 302 /* 303 * Save callback function and return address 304 */ 305 p->rf_fwd = cb; 306 if (replyto) 307 p->rf_sin = *replyto; 308 else 309 bzero((voidp) &p->rf_sin, sizeof(p->rf_sin)); 310 p->rf_ptr = i; 311 312 return error; 313 } 314 315 /* 316 * Called when some data arrives on the forwarding socket 317 */ 318 void fwd_reply() 319 { 320 int len; 321 #ifdef DYNAMIC_BUFFERS 322 voidp pkt; 323 #else 324 u_int pkt[MAX_PACKET_SIZE/sizeof(u_int)+1]; 325 #endif /* DYNAMIC_BUFFERS */ 326 u_int *pkt_int; 327 int rc; 328 rpc_forward *p; 329 struct sockaddr_in src_addr; 330 int src_addr_len; 331 332 /* 333 * Determine the length of the packet 334 */ 335 #ifdef DYNAMIC_BUFFERS 336 if (ioctl(fwd_sock, FIONREAD, &len) < 0) { 337 plog(XLOG_ERROR, "Error reading packet size: %m"); 338 return; 339 } 340 341 /* 342 * Allocate a buffer 343 */ 344 pkt = (voidp) malloc((unsigned) len); 345 if (!pkt) { 346 plog(XLOG_ERROR, "Out of buffers in fwd_reply"); 347 return; 348 } 349 #else 350 len = MAX_PACKET_SIZE; 351 #endif /* DYNAMIC_BUFFERS */ 352 353 /* 354 * Read the packet and check for validity 355 */ 356 again: 357 src_addr_len = sizeof(src_addr); 358 rc = recvfrom(fwd_sock, (char *) pkt, len, 0, 359 (struct sockaddr *) &src_addr, &src_addr_len); 360 if (rc < 0 || src_addr_len != sizeof(src_addr) || 361 src_addr.sin_family != AF_INET) { 362 if (rc < 0 && errno == EINTR) 363 goto again; 364 plog(XLOG_ERROR, "Error reading RPC reply: %m"); 365 goto out; 366 } 367 368 #ifdef DYNAMIC_BUFFERS 369 if (rc != len) { 370 plog(XLOG_ERROR, "Short read in fwd_reply"); 371 goto out; 372 } 373 #endif /* DYNAMIC_BUFFERS */ 374 375 /* 376 * Do no more work if finishing soon 377 */ 378 if ((int)amd_state >= (int)Finishing) 379 goto out; 380 381 /* 382 * Find packet reference 383 */ 384 pkt_int = (u_int *) pkt; 385 386 #ifdef DEBUG 387 switch (*pkt_int & RPC_XID_MASK) { 388 case RPC_XID_PORTMAP: dlog("Receiving PORTMAP reply"); break; 389 case RPC_XID_MOUNTD: dlog("Receiving MOUNTD reply %#x", *pkt_int); break; 390 case RPC_XID_NFSPING: dlog("Receiving NFS ping %#x", *pkt_int); break; 391 default: dlog("UNKNOWN RPC XID"); break; 392 } 393 #endif /* DEBUG */ 394 395 p = fwd_locate(*pkt_int); 396 if (!p) { 397 #ifdef DEBUG 398 dlog("Can't forward reply id %#x", *pkt_int); 399 #endif /* DEBUG */ 400 goto out; 401 } 402 403 if (p->rf_fwd) { 404 /* 405 * Put the original message id back 406 * into the packet. 407 */ 408 *pkt_int = p->rf_oldid; 409 410 /* 411 * Call forwarding function 412 */ 413 (*p->rf_fwd)((voidp) pkt, rc, &src_addr, &p->rf_sin, p->rf_ptr, TRUE); 414 } 415 416 /* 417 * Free forwarding info 418 */ 419 fwd_free(p); 420 421 out:; 422 #ifdef DYNAMIC_BUFFERS 423 /* 424 * Free the packet 425 */ 426 free((voidp) pkt); 427 #endif /* DYNAMIC_BUFFERS */ 428 } 429