diff options
Diffstat (limited to 'usr.sbin/amd/amd/rpc_fwd.c')
-rw-r--r-- | usr.sbin/amd/amd/rpc_fwd.c | 430 |
1 files changed, 430 insertions, 0 deletions
diff --git a/usr.sbin/amd/amd/rpc_fwd.c b/usr.sbin/amd/amd/rpc_fwd.c new file mode 100644 index 0000000..aaf1e0a --- /dev/null +++ b/usr.sbin/amd/amd/rpc_fwd.c @@ -0,0 +1,430 @@ +/* + * Copyright (c) 1989 Jan-Simon Pendry + * Copyright (c) 1989 Imperial College of Science, Technology & Medicine + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)rpc_fwd.c 8.1 (Berkeley) 6/6/93 + * + * $Id: rpc_fwd.c,v 5.2.2.1 1992/02/09 15:09:01 jsp beta $ + * + */ + +/* + * RPC packet forwarding + */ + +#include "am.h" +#include <sys/ioctl.h> +#ifndef F_SETFL +#include <fcntl.h> +#endif /* F_SETFL */ +#ifndef FNDELAY +#include <sys/file.h> +#endif /* FNDELAY */ + +/* + * Note that the ID field in the external packet is only + * ever treated as a 32 bit opaque data object, so there + * is no need to convert to and from network byte ordering. + */ + +/* + * Each pending reply has an rpc_forward structure + * associated with it. These have a 15 second lifespan. + * If a new structure is required, then an expired + * one will be re-allocated if available, otherwise a fresh + * one is allocated. Whenever a reply is received the + * structure is discarded. + */ +typedef struct rpc_forward rpc_forward; +struct rpc_forward { + qelem rf_q; /* Linked list */ + time_t rf_ttl; /* Time to live */ + u_int rf_xid; /* Packet id */ + u_int rf_oldid; /* Original packet id */ + fwd_fun rf_fwd; /* Forwarding function */ + voidp rf_ptr; + struct sockaddr_in rf_sin; +}; + +/* + * Head of list of pending replies + */ +extern qelem rpc_head; +qelem rpc_head = { &rpc_head, &rpc_head }; + +static u_int xid; +#define XID_ALLOC() (xid++) + +#define MAX_PACKET_SIZE 8192 /* Maximum UDP packet size */ + +int fwd_sock; + +/* + * Allocate a rely structure + */ +static rpc_forward *fwd_alloc() +{ + time_t now = clocktime(); + rpc_forward *p = 0, *p2; + +#ifdef DEBUG + /*dlog("fwd_alloca: rpc_head = %#x", rpc_head.q_forw);*/ +#endif /* DEBUG */ + /* + * First search for an existing expired one. + */ + ITER(p2, rpc_forward, &rpc_head) { + if (p2->rf_ttl <= now) { + p = p2; + break; + } + } + + /* + * If one couldn't be found then allocate + * a new structure and link it at the + * head of the list. + */ + if (p) { + /* + * Call forwarding function to say that + * this message was junked. + */ +#ifdef DEBUG + dlog("Re-using packet forwarding slot - id %#x", p->rf_xid); +#endif /* DEBUG */ + if (p->rf_fwd) + (*p->rf_fwd)(0, 0, 0, &p->rf_sin, p->rf_ptr, FALSE); + rem_que(&p->rf_q); + } else { + p = ALLOC(rpc_forward); + } + ins_que(&p->rf_q, &rpc_head); + + /* + * Set the time to live field + * Timeout in 43 seconds + */ + p->rf_ttl = now + 43; + +#ifdef DEBUG + /*dlog("fwd_alloca: rpc_head = %#x", rpc_head.q_forw);*/ +#endif /* DEBUG */ + return p; +} + +/* + * Free an allocated reply structure. + * First unlink it from the list, then + * discard it. + */ +static void fwd_free(p) +rpc_forward *p; +{ +#ifdef DEBUG + /*dlog("fwd_free: rpc_head = %#x", rpc_head.q_forw);*/ +#endif /* DEBUG */ + rem_que(&p->rf_q); +#ifdef DEBUG + /*dlog("fwd_free: rpc_head = %#x", rpc_head.q_forw);*/ +#endif /* DEBUG */ + free((voidp) p); +} + +/* + * Initialise the RPC forwarder + */ +int fwd_init() +{ + int on = 1; + + /* + * Create ping socket + */ + fwd_sock = socket(AF_INET, SOCK_DGRAM, 0); + if (fwd_sock < 0) { + plog(XLOG_ERROR, "Unable to create RPC forwarding socket: %m"); + return errno; + } + + /* + * Some things we talk to require a priv port - so make one here + */ + if (bind_resv_port(fwd_sock, (unsigned short *) 0) < 0) + plog(XLOG_ERROR, "can't bind privileged port"); + + if (fcntl(fwd_sock, F_SETFL, FNDELAY) < 0 && + ioctl(fwd_sock, FIONBIO, &on) < 0) { + plog(XLOG_ERROR, "Can't set non-block on forwarding socket: %m"); + return errno; + } + + return 0; +} + +/* + * Locate a packet in the forwarding list + */ +static rpc_forward *fwd_locate(id) +u_int id; +{ + rpc_forward *p; + + ITER(p, rpc_forward, &rpc_head) { + if (p->rf_xid == id) + return p; + } + + return 0; +} + +/* + * This is called to forward a packet to another + * RPC server. The message id is changed and noted + * so that when a reply appears we can tie it up + * correctly. Just matching the reply's source address + * would not work because it might come from a + * different address. + */ +int fwd_packet(type_id, pkt, len, fwdto, replyto, i, cb) +int type_id; +voidp pkt; +int len; +struct sockaddr_in *fwdto, *replyto; +voidp i; +fwd_fun cb; +{ + rpc_forward *p; + u_int *pkt_int; + int error; + + if ((int)amd_state >= (int)Finishing) + return ENOENT; + + /* + * See if the type_id is fully specified. + * If so, then discard any old entries + * for this id. + * Otherwise make sure the type_id is + * fully qualified by allocating an id here. + */ +#ifdef DEBUG + switch (type_id & RPC_XID_MASK) { + case RPC_XID_PORTMAP: dlog("Sending PORTMAP request"); break; + case RPC_XID_MOUNTD: dlog("Sending MOUNTD request %#x", type_id); break; + case RPC_XID_NFSPING: dlog("Sending NFS ping"); break; + default: dlog("UNKNOWN RPC XID"); break; + } +#endif /* DEBUG */ + + if (type_id & ~RPC_XID_MASK) { +#ifdef DEBUG + /*dlog("Fully qualified rpc type provided");*/ +#endif /* DEBUG */ + p = fwd_locate(type_id); + if (p) { +#ifdef DEBUG + dlog("Discarding earlier rpc fwd handle"); +#endif /* DEBUG */ + fwd_free(p); + } + } else { +#ifdef DEBUG + dlog("Allocating a new xid..."); +#endif /* DEBUG */ + type_id = MK_RPC_XID(type_id, XID_ALLOC()); + } + + p = fwd_alloc(); + if (!p) + return ENOBUFS; + + error = 0; + + pkt_int = (u_int *) pkt; + + /* + * Get the original packet id + */ + p->rf_oldid = *pkt_int; + + /* + * Replace with newly allocated id + */ + p->rf_xid = *pkt_int = type_id; + + /* + * The sendto may fail if, for example, the route + * to a remote host is lost because an intermediate + * gateway has gone down. Important to fill in the + * rest of "p" otherwise nasty things happen later... + */ +#ifdef DEBUG + { char dq[20]; + dlog("Sending packet id %#x to %s.%d", p->rf_xid, inet_dquad(dq, fwdto->sin_addr.s_addr), ntohs(fwdto->sin_port)); + } +#endif /* DEBUG */ + if (sendto(fwd_sock, (char *) pkt, len, 0, + (struct sockaddr *) fwdto, sizeof(*fwdto)) < 0) + error = errno; + + /* + * Save callback function and return address + */ + p->rf_fwd = cb; + if (replyto) + p->rf_sin = *replyto; + else + bzero((voidp) &p->rf_sin, sizeof(p->rf_sin)); + p->rf_ptr = i; + + return error; +} + +/* + * Called when some data arrives on the forwarding socket + */ +void fwd_reply() +{ + int len; +#ifdef DYNAMIC_BUFFERS + voidp pkt; +#else + u_int pkt[MAX_PACKET_SIZE/sizeof(u_int)+1]; +#endif /* DYNAMIC_BUFFERS */ + u_int *pkt_int; + int rc; + rpc_forward *p; + struct sockaddr_in src_addr; + int src_addr_len; + + /* + * Determine the length of the packet + */ +#ifdef DYNAMIC_BUFFERS + if (ioctl(fwd_sock, FIONREAD, &len) < 0) { + plog(XLOG_ERROR, "Error reading packet size: %m"); + return; + } + + /* + * Allocate a buffer + */ + pkt = (voidp) malloc((unsigned) len); + if (!pkt) { + plog(XLOG_ERROR, "Out of buffers in fwd_reply"); + return; + } +#else + len = MAX_PACKET_SIZE; +#endif /* DYNAMIC_BUFFERS */ + + /* + * Read the packet and check for validity + */ +again: + src_addr_len = sizeof(src_addr); + rc = recvfrom(fwd_sock, (char *) pkt, len, 0, + (struct sockaddr *) &src_addr, &src_addr_len); + if (rc < 0 || src_addr_len != sizeof(src_addr) || + src_addr.sin_family != AF_INET) { + if (rc < 0 && errno == EINTR) + goto again; + plog(XLOG_ERROR, "Error reading RPC reply: %m"); + goto out; + } + +#ifdef DYNAMIC_BUFFERS + if (rc != len) { + plog(XLOG_ERROR, "Short read in fwd_reply"); + goto out; + } +#endif /* DYNAMIC_BUFFERS */ + + /* + * Do no more work if finishing soon + */ + if ((int)amd_state >= (int)Finishing) + goto out; + + /* + * Find packet reference + */ + pkt_int = (u_int *) pkt; + +#ifdef DEBUG + switch (*pkt_int & RPC_XID_MASK) { + case RPC_XID_PORTMAP: dlog("Receiving PORTMAP reply"); break; + case RPC_XID_MOUNTD: dlog("Receiving MOUNTD reply %#x", *pkt_int); break; + case RPC_XID_NFSPING: dlog("Receiving NFS ping %#x", *pkt_int); break; + default: dlog("UNKNOWN RPC XID"); break; + } +#endif /* DEBUG */ + + p = fwd_locate(*pkt_int); + if (!p) { +#ifdef DEBUG + dlog("Can't forward reply id %#x", *pkt_int); +#endif /* DEBUG */ + goto out; + } + + if (p->rf_fwd) { + /* + * Put the original message id back + * into the packet. + */ + *pkt_int = p->rf_oldid; + + /* + * Call forwarding function + */ + (*p->rf_fwd)((voidp) pkt, rc, &src_addr, &p->rf_sin, p->rf_ptr, TRUE); + } + + /* + * Free forwarding info + */ + fwd_free(p); + +out:; +#ifdef DYNAMIC_BUFFERS + /* + * Free the packet + */ + free((voidp) pkt); +#endif /* DYNAMIC_BUFFERS */ +} |