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