xref: /original-bsd/usr.sbin/amd/amd/rpc_fwd.c (revision c3e32dec)
1 /*
2  * Copyright (c) 1989 Jan-Simon Pendry
3  * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
4  * Copyright (c) 1989, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Jan-Simon Pendry at Imperial College, London.
9  *
10  * %sccs.include.redist.c%
11  *
12  *	@(#)rpc_fwd.c	8.1 (Berkeley) 06/06/93
13  *
14  * $Id: rpc_fwd.c,v 5.2.2.1 1992/02/09 15:09:01 jsp beta $
15  *
16  */
17 
18 /*
19  * RPC packet forwarding
20  */
21 
22 #include "am.h"
23 #include <sys/ioctl.h>
24 #ifndef F_SETFL
25 #include <fcntl.h>
26 #endif /* F_SETFL */
27 #ifndef FNDELAY
28 #include <sys/file.h>
29 #endif /* FNDELAY */
30 
31 /*
32  * Note that the ID field in the external packet is only
33  * ever treated as a 32 bit opaque data object, so there
34  * is no need to convert to and from network byte ordering.
35  */
36 
37 /*
38  * Each pending reply has an rpc_forward structure
39  * associated with it.  These have a 15 second lifespan.
40  * If a new structure is required, then an expired
41  * one will be re-allocated if available, otherwise a fresh
42  * one is allocated.  Whenever a reply is received the
43  * structure is discarded.
44  */
45 typedef struct rpc_forward rpc_forward;
46 struct rpc_forward {
47 	qelem	rf_q;		/* Linked list */
48 	time_t	rf_ttl;		/* Time to live */
49 	u_int	rf_xid;		/* Packet id */
50 	u_int	rf_oldid;	/* Original packet id */
51 	fwd_fun	rf_fwd;		/* Forwarding function */
52 	voidp	rf_ptr;
53 	struct sockaddr_in rf_sin;
54 };
55 
56 /*
57  * Head of list of pending replies
58  */
59 extern qelem rpc_head;
60 qelem rpc_head = { &rpc_head, &rpc_head };
61 
62 static u_int xid;
63 #define	XID_ALLOC()	(xid++)
64 
65 #define	MAX_PACKET_SIZE	8192	/* Maximum UDP packet size */
66 
67 int fwd_sock;
68 
69 /*
70  * Allocate a rely structure
71  */
72 static rpc_forward *fwd_alloc()
73 {
74 	time_t now = clocktime();
75 	rpc_forward *p = 0, *p2;
76 
77 #ifdef DEBUG
78 	/*dlog("fwd_alloca: rpc_head = %#x", rpc_head.q_forw);*/
79 #endif /* DEBUG */
80 	/*
81 	 * First search for an existing expired one.
82 	 */
83 	ITER(p2, rpc_forward, &rpc_head) {
84 		if (p2->rf_ttl <= now) {
85 			p = p2;
86 			break;
87 		}
88 	}
89 
90 	/*
91 	 * If one couldn't be found then allocate
92 	 * a new structure and link it at the
93 	 * head of the list.
94 	 */
95 	if (p) {
96 		/*
97 		 * Call forwarding function to say that
98 		 * this message was junked.
99 		 */
100 #ifdef DEBUG
101 		dlog("Re-using packet forwarding slot - id %#x", p->rf_xid);
102 #endif /* DEBUG */
103 		if (p->rf_fwd)
104 			(*p->rf_fwd)(0, 0, 0, &p->rf_sin, p->rf_ptr, FALSE);
105 		rem_que(&p->rf_q);
106 	} else {
107 		p = ALLOC(rpc_forward);
108 	}
109 	ins_que(&p->rf_q, &rpc_head);
110 
111 	/*
112 	 * Set the time to live field
113 	 * Timeout in 43 seconds
114 	 */
115 	p->rf_ttl = now + 43;
116 
117 #ifdef DEBUG
118 	/*dlog("fwd_alloca: rpc_head = %#x", rpc_head.q_forw);*/
119 #endif /* DEBUG */
120 	return p;
121 }
122 
123 /*
124  * Free an allocated reply structure.
125  * First unlink it from the list, then
126  * discard it.
127  */
128 static void fwd_free(p)
129 rpc_forward *p;
130 {
131 #ifdef DEBUG
132 	/*dlog("fwd_free: rpc_head = %#x", rpc_head.q_forw);*/
133 #endif /* DEBUG */
134 	rem_que(&p->rf_q);
135 #ifdef DEBUG
136 	/*dlog("fwd_free: rpc_head = %#x", rpc_head.q_forw);*/
137 #endif /* DEBUG */
138 	free((voidp) p);
139 }
140 
141 /*
142  * Initialise the RPC forwarder
143  */
144 int fwd_init()
145 {
146 	int on = 1;
147 
148 	/*
149 	 * Create ping socket
150 	 */
151 	fwd_sock = socket(AF_INET, SOCK_DGRAM, 0);
152 	if (fwd_sock < 0) {
153 		plog(XLOG_ERROR, "Unable to create RPC forwarding socket: %m");
154 		return errno;
155 	}
156 
157 	/*
158 	 * Some things we talk to require a priv port - so make one here
159 	 */
160 	if (bind_resv_port(fwd_sock, (unsigned short *) 0) < 0)
161 		plog(XLOG_ERROR, "can't bind privileged port");
162 
163 	if (fcntl(fwd_sock, F_SETFL, FNDELAY) < 0 &&
164 			ioctl(fwd_sock, FIONBIO, &on) < 0) {
165 		plog(XLOG_ERROR, "Can't set non-block on forwarding socket: %m");
166 		return errno;
167 	}
168 
169 	return 0;
170 }
171 
172 /*
173  * Locate a packet in the forwarding list
174  */
175 static rpc_forward *fwd_locate(id)
176 u_int id;
177 {
178 	rpc_forward *p;
179 
180 	ITER(p, rpc_forward, &rpc_head) {
181 		if (p->rf_xid == id)
182 			return p;
183 	}
184 
185 	return 0;
186 }
187 
188 /*
189  * This is called to forward a packet to another
190  * RPC server.  The message id is changed and noted
191  * so that when a reply appears we can tie it up
192  * correctly.  Just matching the reply's source address
193  * would not work because it might come from a
194  * different address.
195  */
196 int fwd_packet(type_id, pkt, len, fwdto, replyto, i, cb)
197 int type_id;
198 voidp pkt;
199 int len;
200 struct sockaddr_in *fwdto, *replyto;
201 voidp i;
202 fwd_fun cb;
203 {
204 	rpc_forward *p;
205 	u_int *pkt_int;
206 	int error;
207 
208 	if ((int)amd_state >= (int)Finishing)
209 		return ENOENT;
210 
211 	/*
212 	 * See if the type_id is fully specified.
213 	 * If so, then discard any old entries
214 	 * for this id.
215 	 * Otherwise make sure the type_id is
216 	 * fully qualified by allocating an id here.
217 	 */
218 #ifdef DEBUG
219 	switch (type_id & RPC_XID_MASK) {
220 	case RPC_XID_PORTMAP: dlog("Sending PORTMAP request"); break;
221 	case RPC_XID_MOUNTD: dlog("Sending MOUNTD request %#x", type_id); break;
222 	case RPC_XID_NFSPING: dlog("Sending NFS ping"); break;
223 	default: dlog("UNKNOWN RPC XID"); break;
224 	}
225 #endif /* DEBUG */
226 
227 	if (type_id & ~RPC_XID_MASK) {
228 #ifdef DEBUG
229 		/*dlog("Fully qualified rpc type provided");*/
230 #endif /* DEBUG */
231 		p = fwd_locate(type_id);
232 		if (p) {
233 #ifdef DEBUG
234 			dlog("Discarding earlier rpc fwd handle");
235 #endif /* DEBUG */
236 			fwd_free(p);
237 		}
238 	} else {
239 #ifdef DEBUG
240 		dlog("Allocating a new xid...");
241 #endif /* DEBUG */
242 		type_id = MK_RPC_XID(type_id, XID_ALLOC());
243 	}
244 
245 	p = fwd_alloc();
246 	if (!p)
247 		return ENOBUFS;
248 
249 	error = 0;
250 
251 	pkt_int = (u_int *) pkt;
252 
253 	/*
254 	 * Get the original packet id
255 	 */
256 	p->rf_oldid = *pkt_int;
257 
258 	/*
259 	 * Replace with newly allocated id
260 	 */
261 	p->rf_xid = *pkt_int = type_id;
262 
263 	/*
264 	 * The sendto may fail if, for example, the route
265 	 * to a remote host is lost because an intermediate
266 	 * gateway has gone down.  Important to fill in the
267 	 * rest of "p" otherwise nasty things happen later...
268 	 */
269 #ifdef DEBUG
270 	{ char dq[20];
271 	dlog("Sending packet id %#x to %s.%d", p->rf_xid, inet_dquad(dq, fwdto->sin_addr.s_addr), ntohs(fwdto->sin_port));
272 	}
273 #endif /* DEBUG */
274 	if (sendto(fwd_sock, (char *) pkt, len, 0,
275 			(struct sockaddr *) fwdto, sizeof(*fwdto)) < 0)
276 		error = errno;
277 
278 	/*
279 	 * Save callback function and return address
280 	 */
281 	p->rf_fwd = cb;
282 	if (replyto)
283 		p->rf_sin = *replyto;
284 	else
285 		bzero((voidp) &p->rf_sin, sizeof(p->rf_sin));
286 	p->rf_ptr = i;
287 
288 	return error;
289 }
290 
291 /*
292  * Called when some data arrives on the forwarding socket
293  */
294 void fwd_reply()
295 {
296 	int len;
297 #ifdef DYNAMIC_BUFFERS
298 	voidp pkt;
299 #else
300 	u_int pkt[MAX_PACKET_SIZE/sizeof(u_int)+1];
301 #endif /* DYNAMIC_BUFFERS */
302 	u_int *pkt_int;
303 	int rc;
304 	rpc_forward *p;
305 	struct sockaddr_in src_addr;
306 	int src_addr_len;
307 
308 	/*
309 	 * Determine the length of the packet
310 	 */
311 #ifdef DYNAMIC_BUFFERS
312 	if (ioctl(fwd_sock, FIONREAD, &len) < 0) {
313 		plog(XLOG_ERROR, "Error reading packet size: %m");
314 		return;
315 	}
316 
317 	/*
318 	 * Allocate a buffer
319 	 */
320 	pkt = (voidp) malloc((unsigned) len);
321 	if (!pkt) {
322 		plog(XLOG_ERROR, "Out of buffers in fwd_reply");
323 		return;
324 	}
325 #else
326 	len = MAX_PACKET_SIZE;
327 #endif /* DYNAMIC_BUFFERS */
328 
329 	/*
330 	 * Read the packet and check for validity
331 	 */
332 again:
333 	src_addr_len = sizeof(src_addr);
334 	rc = recvfrom(fwd_sock, (char *) pkt, len, 0,
335 			(struct sockaddr *) &src_addr, &src_addr_len);
336 	if (rc < 0 || src_addr_len != sizeof(src_addr) ||
337 			src_addr.sin_family != AF_INET) {
338 		if (rc < 0 && errno == EINTR)
339 			goto again;
340 		plog(XLOG_ERROR, "Error reading RPC reply: %m");
341 		goto out;
342 	}
343 
344 #ifdef DYNAMIC_BUFFERS
345 	if (rc != len) {
346 		plog(XLOG_ERROR, "Short read in fwd_reply");
347 		goto out;
348 	}
349 #endif /* DYNAMIC_BUFFERS */
350 
351 	/*
352 	 * Do no more work if finishing soon
353 	 */
354 	if ((int)amd_state >= (int)Finishing)
355 		goto out;
356 
357 	/*
358 	 * Find packet reference
359 	 */
360 	pkt_int = (u_int *) pkt;
361 
362 #ifdef DEBUG
363 	switch (*pkt_int & RPC_XID_MASK) {
364 	case RPC_XID_PORTMAP: dlog("Receiving PORTMAP reply"); break;
365 	case RPC_XID_MOUNTD: dlog("Receiving MOUNTD reply %#x", *pkt_int); break;
366 	case RPC_XID_NFSPING: dlog("Receiving NFS ping %#x", *pkt_int); break;
367 	default: dlog("UNKNOWN RPC XID"); break;
368 	}
369 #endif /* DEBUG */
370 
371 	p = fwd_locate(*pkt_int);
372 	if (!p) {
373 #ifdef DEBUG
374 		dlog("Can't forward reply id %#x", *pkt_int);
375 #endif /* DEBUG */
376 		goto out;
377 	}
378 
379 	if (p->rf_fwd) {
380 		/*
381 		 * Put the original message id back
382 		 * into the packet.
383 		 */
384 		*pkt_int = p->rf_oldid;
385 
386 		/*
387 		 * Call forwarding function
388 		 */
389 		(*p->rf_fwd)((voidp) pkt, rc, &src_addr, &p->rf_sin, p->rf_ptr, TRUE);
390 	}
391 
392 	/*
393 	 * Free forwarding info
394 	 */
395 	fwd_free(p);
396 
397 out:;
398 #ifdef DYNAMIC_BUFFERS
399 	/*
400 	 * Free the packet
401 	 */
402 	free((voidp) pkt);
403 #endif /* DYNAMIC_BUFFERS */
404 }
405