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