xref: /openbsd-src/usr.sbin/amd/amd/rpc_fwd.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
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  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  *	from: @(#)rpc_fwd.c	8.1 (Berkeley) 6/6/93
35  *	$Id: rpc_fwd.c,v 1.7 2003/06/02 23:36:51 millert Exp $
36  */
37 
38 /*
39  * RPC packet forwarding
40  */
41 
42 #include "am.h"
43 #include <sys/ioctl.h>
44 #ifndef F_SETFL
45 #include <fcntl.h>
46 #endif /* F_SETFL */
47 #ifndef FNDELAY
48 #include <sys/file.h>
49 #endif /* FNDELAY */
50 
51 /*
52  * Note that the ID field in the external packet is only
53  * ever treated as a 32 bit opaque data object, so there
54  * is no need to convert to and from network byte ordering.
55  */
56 
57 /*
58  * Each pending reply has an rpc_forward structure
59  * associated with it.  These have a 15 second lifespan.
60  * If a new structure is required, then an expired
61  * one will be re-allocated if available, otherwise a fresh
62  * one is allocated.  Whenever a reply is received the
63  * structure is discarded.
64  */
65 typedef struct rpc_forward rpc_forward;
66 struct rpc_forward {
67 	qelem	rf_q;		/* Linked list */
68 	time_t	rf_ttl;		/* Time to live */
69 	u_int	rf_xid;		/* Packet id */
70 	u_int	rf_oldid;	/* Original packet id */
71 	fwd_fun	rf_fwd;		/* Forwarding function */
72 	void *	rf_ptr;
73 	struct sockaddr_in rf_sin;
74 };
75 
76 /*
77  * Head of list of pending replies
78  */
79 extern qelem rpc_head;
80 qelem rpc_head = { &rpc_head, &rpc_head };
81 
82 static u_int xid;
83 #define	XID_ALLOC()	(xid++)
84 
85 #define	MAX_PACKET_SIZE	8192	/* Maximum UDP packet size */
86 
87 int fwd_sock;
88 
89 /*
90  * Allocate a rely structure
91  */
92 static rpc_forward *
93 fwd_alloc()
94 {
95 	time_t now = clocktime();
96 	rpc_forward *p = 0, *p2;
97 
98 #ifdef DEBUG
99 	/*dlog("fwd_alloca: rpc_head = %#x", rpc_head.q_forw);*/
100 #endif /* DEBUG */
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 #ifdef DEBUG
122 		dlog("Re-using packet forwarding slot - id %#x", p->rf_xid);
123 #endif /* DEBUG */
124 		if (p->rf_fwd)
125 			(*p->rf_fwd)(0, 0, 0, &p->rf_sin, p->rf_ptr, FALSE);
126 		rem_que(&p->rf_q);
127 	} else {
128 		p = ALLOC(rpc_forward);
129 	}
130 	ins_que(&p->rf_q, &rpc_head);
131 
132 	/*
133 	 * Set the time to live field
134 	 * Timeout in 43 seconds
135 	 */
136 	p->rf_ttl = now + 43;
137 
138 #ifdef DEBUG
139 	/*dlog("fwd_alloca: rpc_head = %#x", rpc_head.q_forw);*/
140 #endif /* DEBUG */
141 	return p;
142 }
143 
144 /*
145  * Free an allocated reply structure.
146  * First unlink it from the list, then
147  * discard it.
148  */
149 static void
150 fwd_free(rpc_forward *p)
151 {
152 #ifdef DEBUG
153 	/*dlog("fwd_free: rpc_head = %#x", rpc_head.q_forw);*/
154 #endif /* DEBUG */
155 	rem_que(&p->rf_q);
156 #ifdef DEBUG
157 	/*dlog("fwd_free: rpc_head = %#x", rpc_head.q_forw);*/
158 #endif /* DEBUG */
159 	free((void *)p);
160 }
161 
162 /*
163  * Initialise the RPC forwarder
164  */
165 int fwd_init()
166 {
167 	int on = 1;
168 
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 
178 	/*
179 	 * Some things we talk to require a priv port - so make one here
180 	 */
181 	if (bind_resv_port(fwd_sock, (unsigned short *) 0) < 0)
182 		plog(XLOG_ERROR, "can't bind privileged port");
183 
184 	if (fcntl(fwd_sock, F_SETFL, FNDELAY) < 0 &&
185 			ioctl(fwd_sock, FIONBIO, &on) < 0) {
186 		plog(XLOG_ERROR, "Can't set non-block on forwarding socket: %m");
187 		return errno;
188 	}
189 
190 	return 0;
191 }
192 
193 /*
194  * Locate a packet in the forwarding list
195  */
196 static rpc_forward *
197 fwd_locate(u_int id)
198 {
199 	rpc_forward *p;
200 
201 	ITER(p, rpc_forward, &rpc_head) {
202 		if (p->rf_xid == id)
203 			return p;
204 	}
205 
206 	return 0;
207 }
208 
209 /*
210  * This is called to forward a packet to another
211  * RPC server.  The message id is changed and noted
212  * so that when a reply appears we can tie it up
213  * correctly.  Just matching the reply's source address
214  * would not work because it might come from a
215  * different address.
216  */
217 int
218 fwd_packet(int type_id, void *pkt, int len, struct sockaddr_in *fwdto,
219     struct sockaddr_in *replyto, void *i, fwd_fun cb)
220 {
221 	rpc_forward *p;
222 	u_int *pkt_int;
223 	int error;
224 
225 	if ((int)amd_state >= (int)Finishing)
226 		return ENOENT;
227 
228 	/*
229 	 * See if the type_id is fully specified.
230 	 * If so, then discard any old entries
231 	 * for this id.
232 	 * Otherwise make sure the type_id is
233 	 * fully qualified by allocating an id here.
234 	 */
235 #ifdef DEBUG
236 	switch (type_id & RPC_XID_MASK) {
237 	case RPC_XID_PORTMAP: dlog("Sending PORTMAP request"); break;
238 	case RPC_XID_MOUNTD: dlog("Sending MOUNTD request %#x", type_id); break;
239 	case RPC_XID_NFSPING: dlog("Sending NFS ping"); break;
240 	default: dlog("UNKNOWN RPC XID"); break;
241 	}
242 #endif /* DEBUG */
243 
244 	if (type_id & ~RPC_XID_MASK) {
245 #ifdef DEBUG
246 		/*dlog("Fully qualified rpc type provided");*/
247 #endif /* DEBUG */
248 		p = fwd_locate(type_id);
249 		if (p) {
250 #ifdef DEBUG
251 			dlog("Discarding earlier rpc fwd handle");
252 #endif /* DEBUG */
253 			fwd_free(p);
254 		}
255 	} else {
256 #ifdef DEBUG
257 		dlog("Allocating a new xid...");
258 #endif /* DEBUG */
259 		type_id = MK_RPC_XID(type_id, XID_ALLOC());
260 	}
261 
262 	p = fwd_alloc();
263 	if (!p)
264 		return ENOBUFS;
265 
266 	error = 0;
267 
268 	pkt_int = (u_int *) pkt;
269 
270 	/*
271 	 * Get the original packet id
272 	 */
273 	p->rf_oldid = *pkt_int;
274 
275 	/*
276 	 * Replace with newly allocated id
277 	 */
278 	p->rf_xid = *pkt_int = type_id;
279 
280 	/*
281 	 * The sendto may fail if, for example, the route
282 	 * to a remote host is lost because an intermediate
283 	 * gateway has gone down.  Important to fill in the
284 	 * rest of "p" otherwise nasty things happen later...
285 	 */
286 #ifdef DEBUG
287 	{ char dq[20];
288 
289 	dlog("Sending packet id %#x to %s.%d", p->rf_xid,
290 	    inet_dquad(dq, sizeof(dq), fwdto->sin_addr.s_addr),
291 	    ntohs(fwdto->sin_port));
292 	}
293 #endif /* DEBUG */
294 	if (sendto(fwd_sock, (char *) pkt, len, 0,
295 			(struct sockaddr *) fwdto, sizeof(*fwdto)) < 0)
296 		error = errno;
297 
298 	/*
299 	 * Save callback function and return address
300 	 */
301 	p->rf_fwd = cb;
302 	if (replyto)
303 		p->rf_sin = *replyto;
304 	else
305 		bzero((void *)&p->rf_sin, sizeof(p->rf_sin));
306 	p->rf_ptr = i;
307 
308 	return error;
309 }
310 
311 /*
312  * Called when some data arrives on the forwarding socket
313  */
314 void
315 fwd_reply()
316 {
317 	int len;
318 #ifdef DYNAMIC_BUFFERS
319 	void *pkt;
320 #else
321 	u_int pkt[MAX_PACKET_SIZE/sizeof(u_int)+1];
322 #endif /* DYNAMIC_BUFFERS */
323 	u_int *pkt_int;
324 	int rc;
325 	rpc_forward *p;
326 	struct sockaddr_in src_addr;
327 	socklen_t src_addr_len;
328 
329 	/*
330 	 * Determine the length of the packet
331 	 */
332 #ifdef DYNAMIC_BUFFERS
333 	if (ioctl(fwd_sock, FIONREAD, &len) < 0) {
334 		plog(XLOG_ERROR, "Error reading packet size: %m");
335 		return;
336 	}
337 
338 	/*
339 	 * Allocate a buffer
340 	 */
341 	pkt = (void *)malloc((unsigned) len);
342 	if (!pkt) {
343 		plog(XLOG_ERROR, "Out of buffers in fwd_reply");
344 		return;
345 	}
346 #else
347 	len = MAX_PACKET_SIZE;
348 #endif /* DYNAMIC_BUFFERS */
349 
350 	/*
351 	 * Read the packet and check for validity
352 	 */
353 again:
354 	src_addr_len = sizeof(src_addr);
355 	rc = recvfrom(fwd_sock, (char *) pkt, len, 0,
356 	    (struct sockaddr *) &src_addr, &src_addr_len);
357 	if (rc < 0 || src_addr_len != sizeof(src_addr) ||
358 			src_addr.sin_family != AF_INET) {
359 		if (rc < 0 && errno == EINTR)
360 			goto again;
361 		plog(XLOG_ERROR, "Error reading RPC reply: %m");
362 		goto out;
363 	}
364 
365 #ifdef DYNAMIC_BUFFERS
366 	if (rc != len) {
367 		plog(XLOG_ERROR, "Short read in fwd_reply");
368 		goto out;
369 	}
370 #endif /* DYNAMIC_BUFFERS */
371 
372 	/*
373 	 * Do no more work if finishing soon
374 	 */
375 	if ((int)amd_state >= (int)Finishing)
376 		goto out;
377 
378 	/*
379 	 * Find packet reference
380 	 */
381 	pkt_int = (u_int *) pkt;
382 
383 #ifdef DEBUG
384 	switch (*pkt_int & RPC_XID_MASK) {
385 	case RPC_XID_PORTMAP: dlog("Receiving PORTMAP reply"); break;
386 	case RPC_XID_MOUNTD: dlog("Receiving MOUNTD reply %#x", *pkt_int); break;
387 	case RPC_XID_NFSPING: dlog("Receiving NFS ping %#x", *pkt_int); break;
388 	default: dlog("UNKNOWN RPC XID"); break;
389 	}
390 #endif /* DEBUG */
391 
392 	p = fwd_locate(*pkt_int);
393 	if (!p) {
394 #ifdef DEBUG
395 		dlog("Can't forward reply id %#x", *pkt_int);
396 #endif /* DEBUG */
397 		goto out;
398 	}
399 
400 	if (p->rf_fwd) {
401 		/*
402 		 * Put the original message id back
403 		 * into the packet.
404 		 */
405 		*pkt_int = p->rf_oldid;
406 
407 		/*
408 		 * Call forwarding function
409 		 */
410 		(*p->rf_fwd)((void *)pkt, rc, &src_addr, &p->rf_sin, p->rf_ptr, TRUE);
411 	}
412 
413 	/*
414 	 * Free forwarding info
415 	 */
416 	fwd_free(p);
417 
418 out:;
419 #ifdef DYNAMIC_BUFFERS
420 	/*
421 	 * Free the packet
422 	 */
423 	free((void *)pkt);
424 #endif /* DYNAMIC_BUFFERS */
425 }
426