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