rpc_fwd.c revision 310490
1/*
2 * Copyright (c) 1997-2014 Erez Zadok
3 * Copyright (c) 1989 Jan-Simon Pendry
4 * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
5 * Copyright (c) 1989 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry at Imperial College, London.
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 * File: am-utils/amd/rpc_fwd.c
37 *
38 */
39
40/*
41 * RPC packet forwarding
42 */
43
44#ifdef HAVE_CONFIG_H
45# include <config.h>
46#endif /* HAVE_CONFIG_H */
47#include <am_defs.h>
48#include <amd.h>
49
50/*
51 * Note that the ID field in the external packet is only
52 * ever treated as a 32 bit opaque data object, so there
53 * is no need to convert to and from network byte ordering.
54 */
55
56#define	XID_ALLOC()		(xid++)
57#define	MAX_PACKET_SIZE	8192	/* Maximum UDP packet size */
58
59/*
60 * Each pending reply has an rpc_forward structure
61 * associated with it.  These have a 15 second lifespan.
62 * If a new structure is required, then an expired
63 * one will be re-allocated if available, otherwise a fresh
64 * one is allocated.  Whenever a reply is received the
65 * structure is discarded.
66 */
67typedef struct rpc_forward rpc_forward;
68struct rpc_forward {
69  qelem rf_q;			/* Linked list */
70  time_t rf_ttl;		/* Time to live */
71  u_int rf_xid;			/* Packet id */
72  u_int rf_oldid;		/* Original packet id */
73  fwd_fun *rf_fwd;		/* Forwarding function */
74  voidp rf_ptr;
75  struct sockaddr_in rf_sin;
76};
77
78/*
79 * Head of list of pending replies
80 */
81qelem rpc_head = {&rpc_head, &rpc_head};
82int fwd_sock;
83static u_int xid;
84
85
86/*
87 * Allocate a rely structure
88 */
89static rpc_forward *
90fwd_alloc(void)
91{
92  time_t now = clocktime(NULL);
93  rpc_forward *p = NULL, *p2;
94
95  /*
96   * First search for an existing expired one.
97   */
98  ITER(p2, rpc_forward, &rpc_head) {
99    if (p2->rf_ttl <= now) {
100      p = p2;
101      break;
102    }
103  }
104
105  /*
106   * If one couldn't be found then allocate
107   * a new structure and link it at the
108   * head of the list.
109   */
110  if (p) {
111    /*
112     * Call forwarding function to say that
113     * this message was junked.
114     */
115    dlog("Re-using packet forwarding slot - id %#x", p->rf_xid);
116    if (p->rf_fwd)
117      (*p->rf_fwd) (0, 0, 0, &p->rf_sin, p->rf_ptr, FALSE);
118    rem_que(&p->rf_q);
119  } else {
120    p = ALLOC(struct rpc_forward);
121  }
122  ins_que(&p->rf_q, &rpc_head);
123
124  /*
125   * Set the time to live field
126   * Timeout in 43 seconds
127   */
128  p->rf_ttl = now + 43;
129
130  return p;
131}
132
133
134/*
135 * Free an allocated reply structure.
136 * First unlink it from the list, then
137 * discard it.
138 */
139static void
140fwd_free(rpc_forward *p)
141{
142  rem_que(&p->rf_q);
143  XFREE(p);
144}
145
146
147/*
148 * Initialize the RPC forwarder
149 */
150int
151fwd_init(void)
152{
153#ifdef FIONBIO
154  int on = 1;
155#endif /* FIONBIO */
156
157#ifdef HAVE_TRANSPORT_TYPE_TLI
158  /*
159   * Create ping TLI socket (/dev/tcp and /dev/ticlts did not work)
160   * (HPUX-11 does not like using O_NDELAY in flags)
161   */
162  fwd_sock = t_open("/dev/udp", O_RDWR|O_NONBLOCK, 0);
163  if (fwd_sock < 0) {
164    plog(XLOG_ERROR, "unable to create RPC forwarding TLI socket: %s",
165	 t_errlist[t_errno]);
166    return errno;
167  }
168#else /* not HAVE_TRANSPORT_TYPE_TLI */
169  /*
170   * Create ping socket
171   */
172  fwd_sock = socket(AF_INET, SOCK_DGRAM, 0);
173  if (fwd_sock < 0) {
174    plog(XLOG_ERROR, "unable to create RPC forwarding socket: %m");
175    return errno;
176  }
177#endif /* not HAVE_TRANSPORT_TYPE_TLI */
178
179  /*
180   * Some things we talk to require a priv port - so make one here
181   */
182  if (bind_resv_port(fwd_sock, (u_short *) NULL) < 0)
183    plog(XLOG_ERROR, "can't bind privileged port (rpc_fwd)");
184
185  if (fcntl(fwd_sock, F_SETFL, FNDELAY) < 0
186#ifdef FIONBIO
187      && ioctl(fwd_sock, FIONBIO, &on) < 0
188#endif /* FIONBIO */
189    ) {
190    plog(XLOG_ERROR, "Can't set non-block on forwarding socket: %m");
191    return errno;
192  }
193
194  return 0;
195}
196
197
198/*
199 * Locate a packet in the forwarding list
200 */
201static rpc_forward *
202fwd_locate(u_int id)
203{
204  rpc_forward *p;
205
206  ITER(p, rpc_forward, &rpc_head) {
207    if (p->rf_xid == id)
208      return p;
209  }
210
211  return 0;
212}
213
214
215/*
216 * This is called to forward a packet to another
217 * RPC server.  The message id is changed and noted
218 * so that when a reply appears we can tie it up
219 * correctly.  Just matching the reply's source address
220 * would not work because it might come from a
221 * different address.
222 */
223int
224fwd_packet(int type_id, char *pkt, int len, struct sockaddr_in *fwdto, struct sockaddr_in *replyto, opaque_t cb_arg, fwd_fun cb)
225{
226  rpc_forward *p;
227  u_int *pkt_int;
228  int error;
229#ifdef HAVE_TRANSPORT_TYPE_TLI
230  struct t_unitdata ud;
231#endif /* HAVE_TRANSPORT_TYPE_TLI */
232
233  if ((int) amd_state >= (int) Finishing)
234    return ENOENT;
235
236  /*
237   * See if the type_id is fully specified.
238   * If so, then discard any old entries
239   * for this id.
240   * Otherwise make sure the type_id is
241   * fully qualified by allocating an id here.
242   */
243  switch (type_id & RPC_XID_MASK) {
244  case RPC_XID_PORTMAP:
245    dlog("Sending PORTMAP request %#x", type_id);
246    break;
247  case RPC_XID_MOUNTD:
248    dlog("Sending MOUNTD request %#x", type_id);
249    break;
250  case RPC_XID_NFSPING:
251    dlog("Sending NFS ping %#x", type_id);
252    break;
253  case RPC_XID_WEBNFS:
254    dlog("Sending WebNFS lookup %#x", type_id);
255    break;
256  default:
257    dlog("UNKNOWN RPC XID %#x", type_id);
258    break;
259  }
260
261  if (type_id & ~RPC_XID_MASK) {
262    p = fwd_locate(type_id);
263    if (p) {
264      dlog("Discarding earlier rpc fwd handle");
265      fwd_free(p);
266    }
267  } else {
268    dlog("Allocating a new xid...");
269    type_id = MK_RPC_XID(type_id, XID_ALLOC());
270  }
271
272  p = fwd_alloc();
273  if (!p)
274    return ENOBUFS;
275
276  error = 0;
277
278  pkt_int = (u_int *) pkt;
279
280  /*
281   * Get the original packet id
282   */
283  p->rf_oldid = ntohl(*pkt_int);
284
285  /*
286   * Replace with newly allocated id
287   */
288  p->rf_xid = type_id;
289  *pkt_int = htonl(type_id);
290
291  /*
292   * The sendto may fail if, for example, the route
293   * to a remote host is lost because an intermediate
294   * gateway has gone down.  Important to fill in the
295   * rest of "p" otherwise nasty things happen later...
296   */
297#ifdef DEBUG
298  {
299    char dq[20];
300    if (p && fwdto)
301      dlog("Sending packet id %#x to %s:%d",
302	   p->rf_xid,
303	   inet_dquad(dq, sizeof(dq), fwdto->sin_addr.s_addr),
304	   ntohs(fwdto->sin_port));
305  }
306#endif /* DEBUG */
307
308  /* if NULL, remote server probably down */
309  if (!fwdto) {
310    error = AM_ERRNO_HOST_DOWN;
311    goto out;
312  }
313
314#ifdef HAVE_TRANSPORT_TYPE_TLI
315  ud.addr.buf = (char *) fwdto;
316  if (fwdto)			/* if NULL, set sizes to zero */
317    ud.addr.maxlen = ud.addr.len = sizeof(struct sockaddr_in);
318  else
319    ud.addr.maxlen = ud.addr.len = 0;
320  ud.opt.buf = (char *) NULL;
321  ud.opt.maxlen = ud.opt.len = 0;
322  ud.udata.buf = pkt;
323  ud.udata.maxlen = ud.udata.len = len;
324  if (t_sndudata(fwd_sock, &ud) < 0) {
325    plog(XLOG_ERROR,"fwd_packet failed: t_errno=%d, errno=%d",t_errno,errno);
326    error = errno;
327  }
328#else /* not HAVE_TRANSPORT_TYPE_TLI */
329  if (sendto(fwd_sock, (char *) pkt, len, 0,
330	     (struct sockaddr *) fwdto, sizeof(*fwdto)) < 0)
331    error = errno;
332#endif /* not HAVE_TRANSPORT_TYPE_TLI */
333
334  /*
335   * Save callback function and return address
336   */
337out:
338  p->rf_fwd = cb;
339  if (replyto)
340    p->rf_sin = *replyto;
341  else
342    memset((voidp) &p->rf_sin, 0, sizeof(p->rf_sin));
343  p->rf_ptr = cb_arg;
344
345  return error;
346}
347
348
349/*
350 * Called when some data arrives on the forwarding socket
351 */
352void
353fwd_reply(void)
354{
355  int len;
356  u_int pkt[MAX_PACKET_SIZE / sizeof(u_int) + 1];
357  u_int *pkt_int;
358  u_int pkt_xid;
359  int rc;
360  rpc_forward *p;
361  struct sockaddr_in src_addr;
362  RECVFROM_FROMLEN_TYPE src_addr_len;
363#ifdef HAVE_TRANSPORT_TYPE_TLI
364  struct t_unitdata ud;
365  int flags = 0;
366#endif /* HAVE_TRANSPORT_TYPE_TLI */
367
368  /*
369   * Determine the length of the packet
370   */
371  len = MAX_PACKET_SIZE;
372
373  /*
374   * Read the packet and check for validity
375   */
376again:
377  src_addr_len = sizeof(src_addr);
378#ifdef HAVE_TRANSPORT_TYPE_TLI
379  ud.addr.buf = (char *) &src_addr;
380  ud.addr.maxlen = ud.addr.len = src_addr_len;
381  ud.opt.buf = (char *) NULL;
382  ud.opt.maxlen = ud.opt.len = 0;
383  ud.udata.buf = (char *) pkt;
384  ud.udata.maxlen = ud.udata.len = len;
385  /* XXX: use flags accordingly such as if T_MORE set */
386  rc = t_rcvudata(fwd_sock, &ud, &flags);
387  if (rc == 0)			/* success, reset rc to length */
388    rc = ud.udata.len;
389  else {
390    plog(XLOG_ERROR,"fwd_reply failed: t_errno=%d, errno=%d, flags=%d",t_errno,errno, flags);
391    /*
392     * Clear error indication, otherwise the error condition persists and
393     * amd gets into an infinite loop.
394     */
395    if (t_errno == TLOOK)
396      t_rcvuderr(fwd_sock, NULL);
397  }
398#else /* not HAVE_TRANSPORT_TYPE_TLI */
399  rc = recvfrom(fwd_sock,
400		(char *) pkt,
401		len,
402		0,
403		(struct sockaddr *) &src_addr,
404		&src_addr_len);
405#endif /* not HAVE_TRANSPORT_TYPE_TLI */
406
407  /*
408   * XXX: in svr4, if the T_MORE bit of flags is set, what do
409   * we then do?  -Erez
410   */
411  if (rc < 0 || src_addr_len != sizeof(src_addr) ||
412      src_addr.sin_family != AF_INET) {
413    if (rc < 0 && errno == EINTR)
414      goto again;
415    plog(XLOG_ERROR, "Error reading RPC reply: %m");
416    goto out;
417  }
418
419  /*
420   * Do no more work if finishing soon
421   */
422  if ((int) amd_state >= (int) Finishing)
423    goto out;
424
425  /*
426   * Find packet reference
427   */
428  pkt_int = (u_int *) pkt;
429  pkt_xid = ntohl(*pkt_int);
430
431  switch (pkt_xid & RPC_XID_MASK) {
432  case RPC_XID_PORTMAP:
433    dlog("Receiving PORTMAP reply %#x", pkt_xid);
434    break;
435  case RPC_XID_MOUNTD:
436    dlog("Receiving MOUNTD reply %#x", pkt_xid);
437    break;
438  case RPC_XID_NFSPING:
439    dlog("Receiving NFS ping %#x", pkt_xid);
440    break;
441  case RPC_XID_WEBNFS:
442    dlog("Receiving WebNFS lookup %#x", pkt_xid);
443    break;
444  default:
445    dlog("UNKNOWN RPC XID %#x", pkt_xid);
446    break;
447  }
448
449  p = fwd_locate(pkt_xid);
450  if (!p) {
451    dlog("Can't forward reply id %#x", pkt_xid);
452    goto out;
453  }
454
455  if (p->rf_fwd) {
456    /*
457     * Put the original message id back
458     * into the packet.
459     */
460    *pkt_int = htonl(p->rf_oldid);
461
462    /*
463     * Call forwarding function
464     */
465    (*p->rf_fwd) ((voidp) pkt, rc, &src_addr, &p->rf_sin, p->rf_ptr, TRUE);
466  }
467
468  /*
469   * Free forwarding info
470   */
471  fwd_free(p);
472
473out:;
474}
475