138494Sobrien/* 2310490Scy * Copyright (c) 1997-2014 Erez Zadok 338494Sobrien * Copyright (c) 1989 Jan-Simon Pendry 438494Sobrien * Copyright (c) 1989 Imperial College of Science, Technology & Medicine 538494Sobrien * Copyright (c) 1989 The Regents of the University of California. 638494Sobrien * All rights reserved. 738494Sobrien * 838494Sobrien * This code is derived from software contributed to Berkeley by 938494Sobrien * Jan-Simon Pendry at Imperial College, London. 1038494Sobrien * 1138494Sobrien * Redistribution and use in source and binary forms, with or without 1238494Sobrien * modification, are permitted provided that the following conditions 1338494Sobrien * are met: 1438494Sobrien * 1. Redistributions of source code must retain the above copyright 1538494Sobrien * notice, this list of conditions and the following disclaimer. 1638494Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1738494Sobrien * notice, this list of conditions and the following disclaimer in the 1838494Sobrien * documentation and/or other materials provided with the distribution. 19310490Scy * 3. Neither the name of the University nor the names of its contributors 2038494Sobrien * may be used to endorse or promote products derived from this software 2138494Sobrien * without specific prior written permission. 2238494Sobrien * 2338494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2438494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2538494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2638494Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2738494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2838494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2938494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3038494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3138494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3238494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3338494Sobrien * SUCH DAMAGE. 3438494Sobrien * 3538494Sobrien * 36174294Sobrien * File: am-utils/amd/rpc_fwd.c 3738494Sobrien * 3838494Sobrien */ 3938494Sobrien 4038494Sobrien/* 4138494Sobrien * RPC packet forwarding 4238494Sobrien */ 4338494Sobrien 4438494Sobrien#ifdef HAVE_CONFIG_H 4538494Sobrien# include <config.h> 4638494Sobrien#endif /* HAVE_CONFIG_H */ 4738494Sobrien#include <am_defs.h> 4838494Sobrien#include <amd.h> 4938494Sobrien 5038494Sobrien/* 5138494Sobrien * Note that the ID field in the external packet is only 5238494Sobrien * ever treated as a 32 bit opaque data object, so there 5338494Sobrien * is no need to convert to and from network byte ordering. 5438494Sobrien */ 5538494Sobrien 56174294Sobrien#define XID_ALLOC() (xid++) 5738494Sobrien#define MAX_PACKET_SIZE 8192 /* Maximum UDP packet size */ 5838494Sobrien 5938494Sobrien/* 6038494Sobrien * Each pending reply has an rpc_forward structure 6138494Sobrien * associated with it. These have a 15 second lifespan. 6238494Sobrien * If a new structure is required, then an expired 6338494Sobrien * one will be re-allocated if available, otherwise a fresh 6438494Sobrien * one is allocated. Whenever a reply is received the 6538494Sobrien * structure is discarded. 6638494Sobrien */ 6738494Sobrientypedef struct rpc_forward rpc_forward; 6838494Sobrienstruct rpc_forward { 6938494Sobrien qelem rf_q; /* Linked list */ 7038494Sobrien time_t rf_ttl; /* Time to live */ 7138494Sobrien u_int rf_xid; /* Packet id */ 7238494Sobrien u_int rf_oldid; /* Original packet id */ 73174294Sobrien fwd_fun *rf_fwd; /* Forwarding function */ 7438494Sobrien voidp rf_ptr; 7538494Sobrien struct sockaddr_in rf_sin; 7638494Sobrien}; 7738494Sobrien 7838494Sobrien/* 7938494Sobrien * Head of list of pending replies 8038494Sobrien */ 8138494Sobrienqelem rpc_head = {&rpc_head, &rpc_head}; 8238494Sobrienint fwd_sock; 8338494Sobrienstatic u_int xid; 8438494Sobrien 8538494Sobrien 8638494Sobrien/* 8738494Sobrien * Allocate a rely structure 8838494Sobrien */ 8938494Sobrienstatic rpc_forward * 9038494Sobrienfwd_alloc(void) 9138494Sobrien{ 92174294Sobrien time_t now = clocktime(NULL); 93310490Scy rpc_forward *p = NULL, *p2; 9438494Sobrien 9538494Sobrien /* 9638494Sobrien * First search for an existing expired one. 9738494Sobrien */ 9838494Sobrien ITER(p2, rpc_forward, &rpc_head) { 9938494Sobrien if (p2->rf_ttl <= now) { 10038494Sobrien p = p2; 10138494Sobrien break; 10238494Sobrien } 10338494Sobrien } 10438494Sobrien 10538494Sobrien /* 10638494Sobrien * If one couldn't be found then allocate 10738494Sobrien * a new structure and link it at the 10838494Sobrien * head of the list. 10938494Sobrien */ 11038494Sobrien if (p) { 11138494Sobrien /* 11238494Sobrien * Call forwarding function to say that 11338494Sobrien * this message was junked. 11438494Sobrien */ 11538494Sobrien dlog("Re-using packet forwarding slot - id %#x", p->rf_xid); 11638494Sobrien if (p->rf_fwd) 11738494Sobrien (*p->rf_fwd) (0, 0, 0, &p->rf_sin, p->rf_ptr, FALSE); 11838494Sobrien rem_que(&p->rf_q); 11938494Sobrien } else { 12038494Sobrien p = ALLOC(struct rpc_forward); 12138494Sobrien } 12238494Sobrien ins_que(&p->rf_q, &rpc_head); 12338494Sobrien 12438494Sobrien /* 12538494Sobrien * Set the time to live field 12638494Sobrien * Timeout in 43 seconds 12738494Sobrien */ 12838494Sobrien p->rf_ttl = now + 43; 12938494Sobrien 13038494Sobrien return p; 13138494Sobrien} 13238494Sobrien 13338494Sobrien 13438494Sobrien/* 13538494Sobrien * Free an allocated reply structure. 13638494Sobrien * First unlink it from the list, then 13738494Sobrien * discard it. 13838494Sobrien */ 13938494Sobrienstatic void 14038494Sobrienfwd_free(rpc_forward *p) 14138494Sobrien{ 14238494Sobrien rem_que(&p->rf_q); 14338494Sobrien XFREE(p); 14438494Sobrien} 14538494Sobrien 14638494Sobrien 14738494Sobrien/* 14838494Sobrien * Initialize the RPC forwarder 14938494Sobrien */ 15038494Sobrienint 15138494Sobrienfwd_init(void) 15238494Sobrien{ 15338494Sobrien#ifdef FIONBIO 15438494Sobrien int on = 1; 15538494Sobrien#endif /* FIONBIO */ 15638494Sobrien 15738494Sobrien#ifdef HAVE_TRANSPORT_TYPE_TLI 15838494Sobrien /* 15938494Sobrien * Create ping TLI socket (/dev/tcp and /dev/ticlts did not work) 16038494Sobrien * (HPUX-11 does not like using O_NDELAY in flags) 16138494Sobrien */ 16238494Sobrien fwd_sock = t_open("/dev/udp", O_RDWR|O_NONBLOCK, 0); 16338494Sobrien if (fwd_sock < 0) { 16438494Sobrien plog(XLOG_ERROR, "unable to create RPC forwarding TLI socket: %s", 16538494Sobrien t_errlist[t_errno]); 16638494Sobrien return errno; 16738494Sobrien } 16838494Sobrien#else /* not HAVE_TRANSPORT_TYPE_TLI */ 16938494Sobrien /* 17038494Sobrien * Create ping socket 17138494Sobrien */ 17238494Sobrien fwd_sock = socket(AF_INET, SOCK_DGRAM, 0); 17338494Sobrien if (fwd_sock < 0) { 17438494Sobrien plog(XLOG_ERROR, "unable to create RPC forwarding socket: %m"); 17538494Sobrien return errno; 17638494Sobrien } 17738494Sobrien#endif /* not HAVE_TRANSPORT_TYPE_TLI */ 17838494Sobrien 17938494Sobrien /* 18038494Sobrien * Some things we talk to require a priv port - so make one here 18138494Sobrien */ 182310490Scy if (bind_resv_port(fwd_sock, (u_short *) NULL) < 0) 18382794Sobrien plog(XLOG_ERROR, "can't bind privileged port (rpc_fwd)"); 18438494Sobrien 18538494Sobrien if (fcntl(fwd_sock, F_SETFL, FNDELAY) < 0 18638494Sobrien#ifdef FIONBIO 18738494Sobrien && ioctl(fwd_sock, FIONBIO, &on) < 0 18838494Sobrien#endif /* FIONBIO */ 18938494Sobrien ) { 19038494Sobrien plog(XLOG_ERROR, "Can't set non-block on forwarding socket: %m"); 19138494Sobrien return errno; 19238494Sobrien } 19338494Sobrien 19438494Sobrien return 0; 19538494Sobrien} 19638494Sobrien 19738494Sobrien 19838494Sobrien/* 19938494Sobrien * Locate a packet in the forwarding list 20038494Sobrien */ 20138494Sobrienstatic rpc_forward * 20238494Sobrienfwd_locate(u_int id) 20338494Sobrien{ 20438494Sobrien rpc_forward *p; 20538494Sobrien 20638494Sobrien ITER(p, rpc_forward, &rpc_head) { 20738494Sobrien if (p->rf_xid == id) 20838494Sobrien return p; 20938494Sobrien } 21038494Sobrien 21138494Sobrien return 0; 21238494Sobrien} 21338494Sobrien 21438494Sobrien 21538494Sobrien/* 21638494Sobrien * This is called to forward a packet to another 21738494Sobrien * RPC server. The message id is changed and noted 21838494Sobrien * so that when a reply appears we can tie it up 21938494Sobrien * correctly. Just matching the reply's source address 22038494Sobrien * would not work because it might come from a 22138494Sobrien * different address. 22238494Sobrien */ 22338494Sobrienint 224174294Sobrienfwd_packet(int type_id, char *pkt, int len, struct sockaddr_in *fwdto, struct sockaddr_in *replyto, opaque_t cb_arg, fwd_fun cb) 22538494Sobrien{ 22638494Sobrien rpc_forward *p; 22738494Sobrien u_int *pkt_int; 22838494Sobrien int error; 22938494Sobrien#ifdef HAVE_TRANSPORT_TYPE_TLI 23038494Sobrien struct t_unitdata ud; 23138494Sobrien#endif /* HAVE_TRANSPORT_TYPE_TLI */ 23238494Sobrien 23338494Sobrien if ((int) amd_state >= (int) Finishing) 23438494Sobrien return ENOENT; 23538494Sobrien 23638494Sobrien /* 23738494Sobrien * See if the type_id is fully specified. 23838494Sobrien * If so, then discard any old entries 23938494Sobrien * for this id. 24038494Sobrien * Otherwise make sure the type_id is 24138494Sobrien * fully qualified by allocating an id here. 24238494Sobrien */ 24338494Sobrien switch (type_id & RPC_XID_MASK) { 24438494Sobrien case RPC_XID_PORTMAP: 245174294Sobrien dlog("Sending PORTMAP request %#x", type_id); 24638494Sobrien break; 24738494Sobrien case RPC_XID_MOUNTD: 24838494Sobrien dlog("Sending MOUNTD request %#x", type_id); 24938494Sobrien break; 25038494Sobrien case RPC_XID_NFSPING: 251174294Sobrien dlog("Sending NFS ping %#x", type_id); 25238494Sobrien break; 253174294Sobrien case RPC_XID_WEBNFS: 254174294Sobrien dlog("Sending WebNFS lookup %#x", type_id); 255174294Sobrien break; 25638494Sobrien default: 257174294Sobrien dlog("UNKNOWN RPC XID %#x", type_id); 25838494Sobrien break; 25938494Sobrien } 26038494Sobrien 26138494Sobrien if (type_id & ~RPC_XID_MASK) { 26238494Sobrien p = fwd_locate(type_id); 26338494Sobrien if (p) { 26438494Sobrien dlog("Discarding earlier rpc fwd handle"); 26538494Sobrien fwd_free(p); 26638494Sobrien } 26738494Sobrien } else { 26838494Sobrien dlog("Allocating a new xid..."); 269174294Sobrien type_id = MK_RPC_XID(type_id, XID_ALLOC()); 27038494Sobrien } 27138494Sobrien 27238494Sobrien p = fwd_alloc(); 27338494Sobrien if (!p) 27438494Sobrien return ENOBUFS; 27538494Sobrien 27638494Sobrien error = 0; 27738494Sobrien 27838494Sobrien pkt_int = (u_int *) pkt; 27938494Sobrien 28038494Sobrien /* 28138494Sobrien * Get the original packet id 28238494Sobrien */ 283174294Sobrien p->rf_oldid = ntohl(*pkt_int); 28438494Sobrien 28538494Sobrien /* 28638494Sobrien * Replace with newly allocated id 28738494Sobrien */ 288174294Sobrien p->rf_xid = type_id; 289174294Sobrien *pkt_int = htonl(type_id); 29038494Sobrien 29138494Sobrien /* 29238494Sobrien * The sendto may fail if, for example, the route 29338494Sobrien * to a remote host is lost because an intermediate 29438494Sobrien * gateway has gone down. Important to fill in the 29538494Sobrien * rest of "p" otherwise nasty things happen later... 29638494Sobrien */ 29738494Sobrien#ifdef DEBUG 29838494Sobrien { 29938494Sobrien char dq[20]; 30038494Sobrien if (p && fwdto) 301174294Sobrien dlog("Sending packet id %#x to %s:%d", 30238494Sobrien p->rf_xid, 303174294Sobrien inet_dquad(dq, sizeof(dq), fwdto->sin_addr.s_addr), 30438494Sobrien ntohs(fwdto->sin_port)); 30538494Sobrien } 30638494Sobrien#endif /* DEBUG */ 30738494Sobrien 30838494Sobrien /* if NULL, remote server probably down */ 30938494Sobrien if (!fwdto) { 31038494Sobrien error = AM_ERRNO_HOST_DOWN; 31138494Sobrien goto out; 31238494Sobrien } 31338494Sobrien 31438494Sobrien#ifdef HAVE_TRANSPORT_TYPE_TLI 31538494Sobrien ud.addr.buf = (char *) fwdto; 31638494Sobrien if (fwdto) /* if NULL, set sizes to zero */ 31738494Sobrien ud.addr.maxlen = ud.addr.len = sizeof(struct sockaddr_in); 31838494Sobrien else 31938494Sobrien ud.addr.maxlen = ud.addr.len = 0; 32038494Sobrien ud.opt.buf = (char *) NULL; 32138494Sobrien ud.opt.maxlen = ud.opt.len = 0; 32238494Sobrien ud.udata.buf = pkt; 32338494Sobrien ud.udata.maxlen = ud.udata.len = len; 32438494Sobrien if (t_sndudata(fwd_sock, &ud) < 0) { 32538494Sobrien plog(XLOG_ERROR,"fwd_packet failed: t_errno=%d, errno=%d",t_errno,errno); 32638494Sobrien error = errno; 32738494Sobrien } 32838494Sobrien#else /* not HAVE_TRANSPORT_TYPE_TLI */ 32938494Sobrien if (sendto(fwd_sock, (char *) pkt, len, 0, 33038494Sobrien (struct sockaddr *) fwdto, sizeof(*fwdto)) < 0) 33138494Sobrien error = errno; 33238494Sobrien#endif /* not HAVE_TRANSPORT_TYPE_TLI */ 33338494Sobrien 33438494Sobrien /* 33538494Sobrien * Save callback function and return address 33638494Sobrien */ 33738494Sobrienout: 33838494Sobrien p->rf_fwd = cb; 33938494Sobrien if (replyto) 34038494Sobrien p->rf_sin = *replyto; 34138494Sobrien else 34238494Sobrien memset((voidp) &p->rf_sin, 0, sizeof(p->rf_sin)); 343174294Sobrien p->rf_ptr = cb_arg; 34438494Sobrien 34538494Sobrien return error; 34638494Sobrien} 34738494Sobrien 34838494Sobrien 34938494Sobrien/* 35038494Sobrien * Called when some data arrives on the forwarding socket 35138494Sobrien */ 35238494Sobrienvoid 35338494Sobrienfwd_reply(void) 35438494Sobrien{ 35538494Sobrien int len; 35638494Sobrien u_int pkt[MAX_PACKET_SIZE / sizeof(u_int) + 1]; 35738494Sobrien u_int *pkt_int; 358174294Sobrien u_int pkt_xid; 35938494Sobrien int rc; 36038494Sobrien rpc_forward *p; 36138494Sobrien struct sockaddr_in src_addr; 36238494Sobrien RECVFROM_FROMLEN_TYPE src_addr_len; 36338494Sobrien#ifdef HAVE_TRANSPORT_TYPE_TLI 36438494Sobrien struct t_unitdata ud; 36538494Sobrien int flags = 0; 36638494Sobrien#endif /* HAVE_TRANSPORT_TYPE_TLI */ 36738494Sobrien 36838494Sobrien /* 36938494Sobrien * Determine the length of the packet 37038494Sobrien */ 37138494Sobrien len = MAX_PACKET_SIZE; 37238494Sobrien 37338494Sobrien /* 37438494Sobrien * Read the packet and check for validity 37538494Sobrien */ 37638494Sobrienagain: 37738494Sobrien src_addr_len = sizeof(src_addr); 37838494Sobrien#ifdef HAVE_TRANSPORT_TYPE_TLI 37938494Sobrien ud.addr.buf = (char *) &src_addr; 38038494Sobrien ud.addr.maxlen = ud.addr.len = src_addr_len; 38138494Sobrien ud.opt.buf = (char *) NULL; 38238494Sobrien ud.opt.maxlen = ud.opt.len = 0; 38338494Sobrien ud.udata.buf = (char *) pkt; 38438494Sobrien ud.udata.maxlen = ud.udata.len = len; 38538494Sobrien /* XXX: use flags accordingly such as if T_MORE set */ 38638494Sobrien rc = t_rcvudata(fwd_sock, &ud, &flags); 38738494Sobrien if (rc == 0) /* success, reset rc to length */ 38838494Sobrien rc = ud.udata.len; 38938494Sobrien else { 39038494Sobrien plog(XLOG_ERROR,"fwd_reply failed: t_errno=%d, errno=%d, flags=%d",t_errno,errno, flags); 391174294Sobrien /* 392174294Sobrien * Clear error indication, otherwise the error condition persists and 393174294Sobrien * amd gets into an infinite loop. 394174294Sobrien */ 395174294Sobrien if (t_errno == TLOOK) 396174294Sobrien t_rcvuderr(fwd_sock, NULL); 39738494Sobrien } 39838494Sobrien#else /* not HAVE_TRANSPORT_TYPE_TLI */ 39938494Sobrien rc = recvfrom(fwd_sock, 40038494Sobrien (char *) pkt, 40138494Sobrien len, 40238494Sobrien 0, 40338494Sobrien (struct sockaddr *) &src_addr, 40438494Sobrien &src_addr_len); 40538494Sobrien#endif /* not HAVE_TRANSPORT_TYPE_TLI */ 40638494Sobrien 40738494Sobrien /* 40838494Sobrien * XXX: in svr4, if the T_MORE bit of flags is set, what do 40938494Sobrien * we then do? -Erez 41038494Sobrien */ 41138494Sobrien if (rc < 0 || src_addr_len != sizeof(src_addr) || 41238494Sobrien src_addr.sin_family != AF_INET) { 41338494Sobrien if (rc < 0 && errno == EINTR) 41438494Sobrien goto again; 41538494Sobrien plog(XLOG_ERROR, "Error reading RPC reply: %m"); 41638494Sobrien goto out; 41738494Sobrien } 41838494Sobrien 41938494Sobrien /* 42038494Sobrien * Do no more work if finishing soon 42138494Sobrien */ 42238494Sobrien if ((int) amd_state >= (int) Finishing) 42338494Sobrien goto out; 42438494Sobrien 42538494Sobrien /* 42638494Sobrien * Find packet reference 42738494Sobrien */ 42838494Sobrien pkt_int = (u_int *) pkt; 429174294Sobrien pkt_xid = ntohl(*pkt_int); 43038494Sobrien 431174294Sobrien switch (pkt_xid & RPC_XID_MASK) { 43238494Sobrien case RPC_XID_PORTMAP: 433174294Sobrien dlog("Receiving PORTMAP reply %#x", pkt_xid); 43438494Sobrien break; 43538494Sobrien case RPC_XID_MOUNTD: 436174294Sobrien dlog("Receiving MOUNTD reply %#x", pkt_xid); 43738494Sobrien break; 43838494Sobrien case RPC_XID_NFSPING: 439174294Sobrien dlog("Receiving NFS ping %#x", pkt_xid); 44038494Sobrien break; 441174294Sobrien case RPC_XID_WEBNFS: 442174294Sobrien dlog("Receiving WebNFS lookup %#x", pkt_xid); 443174294Sobrien break; 44438494Sobrien default: 445174294Sobrien dlog("UNKNOWN RPC XID %#x", pkt_xid); 44638494Sobrien break; 44738494Sobrien } 44838494Sobrien 449174294Sobrien p = fwd_locate(pkt_xid); 45038494Sobrien if (!p) { 451174294Sobrien dlog("Can't forward reply id %#x", pkt_xid); 45238494Sobrien goto out; 45338494Sobrien } 45438494Sobrien 45538494Sobrien if (p->rf_fwd) { 45638494Sobrien /* 45738494Sobrien * Put the original message id back 45838494Sobrien * into the packet. 45938494Sobrien */ 460174294Sobrien *pkt_int = htonl(p->rf_oldid); 46138494Sobrien 46238494Sobrien /* 46338494Sobrien * Call forwarding function 46438494Sobrien */ 46538494Sobrien (*p->rf_fwd) ((voidp) pkt, rc, &src_addr, &p->rf_sin, p->rf_ptr, TRUE); 46638494Sobrien } 46738494Sobrien 46838494Sobrien /* 46938494Sobrien * Free forwarding info 47038494Sobrien */ 47138494Sobrien fwd_free(p); 47238494Sobrien 47338494Sobrienout:; 47438494Sobrien} 475