lock_proc.c revision 1.2 1 /* $NetBSD: lock_proc.c,v 1.2 1997/10/18 04:01:15 lukem Exp $ */
2
3 /*
4 * Copyright (c) 1995
5 * A.R. Gordon (andrew.gordon (at) net-tel.co.uk). All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed for the FreeBSD project
18 * 4. Neither the name of the author nor the names of any co-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 ANDREW GORDON 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 AUTHOR 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 */
35
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __RCSID("$NetBSD: lock_proc.c,v 1.2 1997/10/18 04:01:15 lukem Exp $");
39 #endif
40
41 #include <sys/param.h>
42 #include <sys/socket.h>
43
44 #include <netinet/in.h>
45 #include <arpa/inet.h>
46
47 #include <netdb.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <syslog.h>
51
52 #include <rpc/rpc.h>
53 #include <rpcsvc/sm_inter.h>
54
55 #include "lockd.h"
56 #include "nlm_prot.h"
57
58
59 #define CLIENT_CACHE_SIZE 64 /* No. of client sockets cached */
60 #define CLIENT_CACHE_LIFETIME 120 /* In seconds */
61
62 static CLIENT *get_client __P((struct sockaddr_in *));
63 static void log_from_addr __P((char *, struct svc_req *));
64 static void transmit_result __P((int, nlm_res *, struct svc_req *));
65
66 /* log_from_addr ----------------------------------------------------------- */
67 /*
68 * Purpose: Log name of function called and source address
69 * Returns: Nothing
70 * Notes: Extracts the source address from the transport handle
71 * passed in as part of the called procedure specification
72 */
73 static void
74 log_from_addr(fun_name, req)
75 char *fun_name;
76 struct svc_req *req;
77 {
78 struct sockaddr_in *addr;
79 struct hostent *host;
80 char hostname_buf[40];
81
82 addr = svc_getcaller(req->rq_xprt);
83 host = gethostbyaddr((char *)&(addr->sin_addr), addr->sin_len, AF_INET);
84 if (host) {
85 strncpy(hostname_buf, host->h_name, sizeof(hostname_buf));
86 hostname_buf[sizeof(hostname_buf) - 1] = '\0';
87 } else /* No hostname available - print raw address */
88 strcpy(hostname_buf, inet_ntoa(addr->sin_addr));
89
90 syslog(LOG_DEBUG, "%s from %s", fun_name, hostname_buf);
91 }
92
93 /* get_client -------------------------------------------------------------- */
94 /*
95 * Purpose: Get a CLIENT* for making RPC calls to lockd on given host
96 * Returns: CLIENT* pointer, from clnt_udp_create, or NULL if error
97 * Notes: Creating a CLIENT* is quite expensive, involving a
98 * conversation with the remote portmapper to get the
99 * port number. Since a given client is quite likely
100 * to make several locking requests in succession, it is
101 * desirable to cache the created CLIENT*.
102 *
103 * Since we are using UDP rather than TCP, there is no cost
104 * to the remote system in keeping these cached indefinitely.
105 * Unfortunately there is a snag: if the remote system
106 * reboots, the cached portmapper results will be invalid,
107 * and we will never detect this since all of the xxx_msg()
108 * calls return no result - we just fire off a udp packet
109 * and hope for the best.
110 *
111 * We solve this by discarding cached values after two
112 * minutes, regardless of whether they have been used
113 * in the meanwhile (since a bad one might have been used
114 * plenty of times, as the host keeps retrying the request
115 * and we keep sending the reply back to the wrong port).
116 *
117 * Given that the entries will always expire in the order
118 * that they were created, there is no point in a LRU
119 * algorithm for when the cache gets full - entries are
120 * always re-used in sequence.
121 */
122 static CLIENT *clnt_cache_ptr[CLIENT_CACHE_SIZE];
123 static long clnt_cache_time[CLIENT_CACHE_SIZE]; /* time entry created */
124 static struct in_addr clnt_cache_addr[CLIENT_CACHE_SIZE];
125 static int clnt_cache_next_to_use = 0;
126
127 static CLIENT *
128 get_client(host_addr)
129 struct sockaddr_in *host_addr;
130 {
131 CLIENT *client;
132 struct timeval retry_time, time_now;
133 int i, sock_no;
134
135 gettimeofday(&time_now, NULL);
136
137 /*
138 * Search for the given client in the cache, zapping any expired
139 * entries that we happen to notice in passing.
140 */
141 for (i = 0; i < CLIENT_CACHE_SIZE; i++) {
142 client = clnt_cache_ptr[i];
143 if (client && ((clnt_cache_time[i] + CLIENT_CACHE_LIFETIME)
144 < time_now.tv_sec)) {
145 /* Cache entry has expired. */
146 if (debug_level > 3)
147 syslog(LOG_DEBUG, "Expired CLIENT* in cache");
148 clnt_cache_time[i] = 0L;
149 clnt_destroy(client);
150 clnt_cache_ptr[i] = NULL;
151 client = NULL;
152 }
153 if (client && !memcmp(&clnt_cache_addr[i],
154 &host_addr->sin_addr, sizeof(struct in_addr))) {
155 /* Found it! */
156 if (debug_level > 3)
157 syslog(LOG_DEBUG, "Found CLIENT* in cache");
158 return (client);
159 }
160 }
161
162 /* Not found in cache. Free the next entry if it is in use. */
163 if (clnt_cache_ptr[clnt_cache_next_to_use]) {
164 clnt_destroy(clnt_cache_ptr[clnt_cache_next_to_use]);
165 clnt_cache_ptr[clnt_cache_next_to_use] = NULL;
166 }
167
168 /* Create the new client handle */
169 sock_no = RPC_ANYSOCK;
170 retry_time.tv_sec = 5;
171 retry_time.tv_usec = 0;
172 host_addr->sin_port = 0; /* Force consultation with portmapper */
173 client = clntudp_create(host_addr, NLM_PROG, NLM_VERS,
174 retry_time, &sock_no);
175 if (!client) {
176 syslog(LOG_ERR, clnt_spcreateerror("clntudp_create"));
177 syslog(LOG_ERR, "Unable to return result to %s",
178 inet_ntoa(host_addr->sin_addr));
179 return NULL;
180 }
181
182 /* Success - update the cache entry */
183 clnt_cache_ptr[clnt_cache_next_to_use] = client;
184 clnt_cache_addr[clnt_cache_next_to_use] = host_addr->sin_addr;
185 clnt_cache_time[clnt_cache_next_to_use] = time_now.tv_sec;
186 if (++clnt_cache_next_to_use > CLIENT_CACHE_SIZE)
187 clnt_cache_next_to_use = 0;
188
189 /*
190 * Disable the default timeout, so we can specify our own in calls
191 * to clnt_call(). (Note that the timeout is a different concept
192 * from the retry period set in clnt_udp_create() above.)
193 */
194 retry_time.tv_sec = -1;
195 retry_time.tv_usec = -1;
196 clnt_control(client, CLSET_TIMEOUT, (char *)&retry_time);
197
198 if (debug_level > 3)
199 syslog(LOG_DEBUG, "Created CLIENT* for %s",
200 inet_ntoa(host_addr->sin_addr));
201 return client;
202 }
203
204
205 /* transmit_result --------------------------------------------------------- */
206 /*
207 * Purpose: Transmit result for nlm_xxx_msg pseudo-RPCs
208 * Returns: Nothing - we have no idea if the datagram got there
209 * Notes: clnt_call() will always fail (with timeout) as we are
210 * calling it with timeout 0 as a hack to just issue a datagram
211 * without expecting a result
212 */
213 static void
214 transmit_result(opcode, result, req)
215 int opcode;
216 nlm_res *result;
217 struct svc_req *req;
218 {
219 static char dummy;
220 struct sockaddr_in *addr;
221 CLIENT *cli;
222 struct timeval timeo;
223 int success;
224
225 addr = svc_getcaller(req->rq_xprt);
226 if ((cli = get_client(addr)) != NULL) {
227 timeo.tv_sec = 0; /* No timeout - not expecting response */
228 timeo.tv_usec = 0;
229
230 success = clnt_call(cli, opcode, xdr_nlm_res, result, xdr_void,
231 &dummy, timeo);
232
233 if (debug_level > 2)
234 syslog(LOG_DEBUG, "clnt_call returns %d\n", success);
235 }
236 }
237 /* ------------------------------------------------------------------------- */
238 /*
239 * Functions for Unix<->Unix locking (ie. monitored locking, with rpc.statd
240 * involved to ensure reclaim of locks after a crash of the "stateless"
241 * server.
242 *
243 * These all come in two flavours - nlm_xxx() and nlm_xxx_msg().
244 * The first are standard RPCs with argument and result.
245 * The nlm_xxx_msg() calls implement exactly the same functions, but
246 * use two pseudo-RPCs (one in each direction). These calls are NOT
247 * standard use of the RPC protocol in that they do not return a result
248 * at all (NB. this is quite different from returning a void result).
249 * The effect of this is to make the nlm_xxx_msg() calls simple unacknowledged
250 * datagrams, requiring higher-level code to perform retries.
251 *
252 * Despite the disadvantages of the nlm_xxx_msg() approach (some of which
253 * are documented in the comments to get_client() above), this is the
254 * interface used by all current commercial NFS implementations
255 * [Solaris, SCO, AIX etc.]. This is presumed to be because these allow
256 * implementations to continue using the standard RPC libraries, while
257 * avoiding the block-until-result nature of the library interface.
258 *
259 * No client implementations have been identified so far that make use
260 * of the true RPC version (early SunOS releases would be a likely candidate
261 * for testing).
262 */
263
264 /* nlm_test ---------------------------------------------------------------- */
265 /*
266 * Purpose: Test whether a specified lock would be granted if requested
267 * Returns: nlm_granted (or error code)
268 * Notes:
269 */
270 nlm_testres *
271 nlm_test_1_svc(arg, rqstp)
272 nlm_testargs *arg;
273 struct svc_req *rqstp;
274 {
275 static nlm_testres res;
276
277 if (debug_level)
278 log_from_addr("nlm_test", rqstp);
279
280 /*
281 * Copy the cookie from the argument into the result. Note that this
282 * is slightly hazardous, as the structure contains a pointer to a
283 * malloc()ed buffer that will get freed by the caller. However, the
284 * main function transmits the result before freeing the argument
285 * so it is in fact safe.
286 */
287 res.cookie = arg->cookie;
288 res.stat.stat = nlm_granted;
289 return (&res);
290 }
291
292 void *
293 nlm_test_msg_1_svc(arg, rqstp)
294 nlm_testargs *arg;
295 struct svc_req *rqstp;
296 {
297 nlm_testres res;
298 static char dummy;
299 struct sockaddr_in *addr;
300 CLIENT *cli;
301 int success;
302 struct timeval timeo;
303
304 if (debug_level)
305 log_from_addr("nlm_test_msg", rqstp);
306
307 res.cookie = arg->cookie;
308 res.stat.stat = nlm_granted;
309
310 /*
311 * nlm_test has different result type to the other operations, so
312 * can't use transmit_result() in this case
313 */
314 addr = svc_getcaller(rqstp->rq_xprt);
315 if ((cli = get_client(addr)) != NULL) {
316 timeo.tv_sec = 0; /* No timeout - not expecting response */
317 timeo.tv_usec = 0;
318
319 success = clnt_call(cli, NLM_TEST_RES, xdr_nlm_testres,
320 &res, xdr_void, &dummy, timeo);
321
322 if (debug_level > 2)
323 syslog(LOG_DEBUG, "clnt_call returns %d\n", success);
324 }
325 return (NULL);
326 }
327
328 /* nlm_lock ---------------------------------------------------------------- */
329 /*
330 * Purposes: Establish a lock
331 * Returns: granted, denied or blocked
332 * Notes: *** grace period support missing
333 */
334 nlm_res *
335 nlm_lock_1_svc(arg, rqstp)
336 nlm_lockargs *arg;
337 struct svc_req *rqstp;
338 {
339 static nlm_res res;
340
341 if (debug_level)
342 log_from_addr("nlm_lock", rqstp);
343
344 /* copy cookie from arg to result. See comment in nlm_test_1() */
345 res.cookie = arg->cookie;
346
347 res.stat.stat = nlm_granted;
348 return (&res);
349 }
350
351 void *
352 nlm_lock_msg_1_svc(arg, rqstp)
353 nlm_lockargs *arg;
354 struct svc_req *rqstp;
355 {
356 static nlm_res res;
357
358 if (debug_level)
359 log_from_addr("nlm_lock_msg", rqstp);
360
361 res.cookie = arg->cookie;
362 res.stat.stat = nlm_granted;
363 transmit_result(NLM_LOCK_RES, &res, rqstp);
364
365 return (NULL);
366 }
367
368 /* nlm_cancel -------------------------------------------------------------- */
369 /*
370 * Purpose: Cancel a blocked lock request
371 * Returns: granted or denied
372 * Notes:
373 */
374 nlm_res *
375 nlm_cancel_1_svc(arg, rqstp)
376 nlm_cancargs *arg;
377 struct svc_req *rqstp;
378 {
379 static nlm_res res;
380
381 if (debug_level)
382 log_from_addr("nlm_cancel", rqstp);
383
384 /* copy cookie from arg to result. See comment in nlm_test_1() */
385 res.cookie = arg->cookie;
386
387 /*
388 * Since at present we never return 'nlm_blocked', there can never be
389 * a lock to cancel, so this call always fails.
390 */
391 res.stat.stat = nlm_denied;
392 return (&res);
393 }
394
395 void *
396 nlm_cancel_msg_1_svc(arg, rqstp)
397 nlm_cancargs *arg;
398 struct svc_req *rqstp;
399 {
400 static nlm_res res;
401
402 if (debug_level)
403 log_from_addr("nlm_cancel_msg", rqstp);
404
405 res.cookie = arg->cookie;
406 /*
407 * Since at present we never return 'nlm_blocked', there can never be
408 * a lock to cancel, so this call always fails.
409 */
410 res.stat.stat = nlm_denied;
411 transmit_result(NLM_CANCEL_RES, &res, rqstp);
412 return (NULL);
413 }
414
415 /* nlm_unlock -------------------------------------------------------------- */
416 /*
417 * Purpose: Release an existing lock
418 * Returns: Always granted, unless during grace period
419 * Notes: "no such lock" error condition is ignored, as the
420 * protocol uses unreliable UDP datagrams, and may well
421 * re-try an unlock that has already succeeded.
422 */
423 nlm_res *
424 nlm_unlock_1_svc(arg, rqstp)
425 nlm_unlockargs *arg;
426 struct svc_req *rqstp;
427 {
428 static nlm_res res;
429
430 if (debug_level)
431 log_from_addr("nlm_unlock", rqstp);
432
433 res.stat.stat = nlm_granted;
434 res.cookie = arg->cookie;
435
436 return (&res);
437 }
438
439 void *
440 nlm_unlock_msg_1_svc(arg, rqstp)
441 nlm_unlockargs *arg;
442 struct svc_req *rqstp;
443 {
444 static nlm_res res;
445
446 if (debug_level)
447 log_from_addr("nlm_unlock_msg", rqstp);
448
449 res.stat.stat = nlm_granted;
450 res.cookie = arg->cookie;
451
452 transmit_result(NLM_UNLOCK_RES, &res, rqstp);
453 return (NULL);
454 }
455
456 /* ------------------------------------------------------------------------- */
457 /*
458 * Client-side pseudo-RPCs for results. Note that for the client there
459 * are only nlm_xxx_msg() versions of each call, since the 'real RPC'
460 * version returns the results in the RPC result, and so the client
461 * does not normally receive incoming RPCs.
462 *
463 * The exception to this is nlm_granted(), which is genuinely an RPC
464 * call from the server to the client - a 'call-back' in normal procedure
465 * call terms.
466 */
467
468 /* nlm_granted ------------------------------------------------------------- */
469 /*
470 * Purpose: Receive notification that formerly blocked lock now granted
471 * Returns: always success ('granted')
472 * Notes:
473 */
474 nlm_res *
475 nlm_granted_1_svc(arg, rqstp)
476 nlm_testargs *arg;
477 struct svc_req *rqstp;
478 {
479 static nlm_res res;
480
481 if (debug_level)
482 log_from_addr("nlm_granted", rqstp);
483
484 /* copy cookie from arg to result. See comment in nlm_test_1() */
485 res.cookie = arg->cookie;
486
487 res.stat.stat = nlm_granted;
488 return (&res);
489 }
490
491 void *
492 nlm_granted_msg_1_svc(arg, rqstp)
493 nlm_testargs *arg;
494 struct svc_req *rqstp;
495 {
496 static nlm_res res;
497
498 if (debug_level)
499 log_from_addr("nlm_granted_msg", rqstp);
500
501 res.cookie = arg->cookie;
502 res.stat.stat = nlm_granted;
503 transmit_result(NLM_GRANTED_RES, &res, rqstp);
504 return (NULL);
505 }
506
507 /* nlm_test_res ------------------------------------------------------------ */
508 /*
509 * Purpose: Accept result from earlier nlm_test_msg() call
510 * Returns: Nothing
511 */
512 void *
513 nlm_test_res_1_svc(arg, rqstp)
514 nlm_testres *arg;
515 struct svc_req *rqstp;
516 {
517 if (debug_level)
518 log_from_addr("nlm_test_res", rqstp);
519 return (NULL);
520 }
521
522 /* nlm_lock_res ------------------------------------------------------------ */
523 /*
524 * Purpose: Accept result from earlier nlm_lock_msg() call
525 * Returns: Nothing
526 */
527 void *
528 nlm_lock_res_1_svc(arg, rqstp)
529 nlm_res *arg;
530 struct svc_req *rqstp;
531 {
532 if (debug_level)
533 log_from_addr("nlm_lock_res", rqstp);
534
535 return (NULL);
536 }
537
538 /* nlm_cancel_res ---------------------------------------------------------- */
539 /*
540 * Purpose: Accept result from earlier nlm_cancel_msg() call
541 * Returns: Nothing
542 */
543 void *
544 nlm_cancel_res_1_svc(arg, rqstp)
545 nlm_res *arg;
546 struct svc_req *rqstp;
547 {
548 if (debug_level)
549 log_from_addr("nlm_cancel_res", rqstp);
550 return (NULL);
551 }
552
553 /* nlm_unlock_res ---------------------------------------------------------- */
554 /*
555 * Purpose: Accept result from earlier nlm_unlock_msg() call
556 * Returns: Nothing
557 */
558 void *
559 nlm_unlock_res_1_svc(arg, rqstp)
560 nlm_res *arg;
561 struct svc_req *rqstp;
562 {
563 if (debug_level)
564 log_from_addr("nlm_unlock_res", rqstp);
565 return (NULL);
566 }
567
568 /* nlm_granted_res --------------------------------------------------------- */
569 /*
570 * Purpose: Accept result from earlier nlm_granted_msg() call
571 * Returns: Nothing
572 */
573 void *
574 nlm_granted_res_1_svc(arg, rqstp)
575 nlm_res *arg;
576 struct svc_req *rqstp;
577 {
578 if (debug_level)
579 log_from_addr("nlm_granted_res", rqstp);
580 return (NULL);
581 }
582
583 /* ------------------------------------------------------------------------- */
584 /*
585 * Calls for PCNFS locking (aka non-monitored locking, no involvement
586 * of rpc.statd).
587 *
588 * These are all genuine RPCs - no nlm_xxx_msg() nonsense here.
589 */
590
591 /* nlm_share --------------------------------------------------------------- */
592 /*
593 * Purpose: Establish a DOS-style lock
594 * Returns: success or failure
595 * Notes: Blocking locks are not supported - client is expected
596 * to retry if required.
597 */
598 nlm_shareres *
599 nlm_share_3_svc(arg, rqstp)
600 nlm_shareargs *arg;
601 struct svc_req *rqstp;
602 {
603 static nlm_shareres res;
604
605 if (debug_level)
606 log_from_addr("nlm_share", rqstp);
607
608 res.cookie = arg->cookie;
609 res.stat = nlm_granted;
610 res.sequence = 1234356; /* X/Open says this field is ignored? */
611 return (&res);
612 }
613
614 /* nlm_unshare ------------------------------------------------------------ */
615 /*
616 * Purpose: Release a DOS-style lock
617 * Returns: nlm_granted, unless in grace period
618 * Notes:
619 */
620 nlm_shareres *
621 nlm_unshare_3_svc(arg, rqstp)
622 nlm_shareargs *arg;
623 struct svc_req *rqstp;
624 {
625 static nlm_shareres res;
626
627 if (debug_level)
628 log_from_addr("nlm_unshare", rqstp);
629
630 res.cookie = arg->cookie;
631 res.stat = nlm_granted;
632 res.sequence = 1234356; /* X/Open says this field is ignored? */
633 return (&res);
634 }
635
636 /* nlm_nm_lock ------------------------------------------------------------ */
637 /*
638 * Purpose: non-monitored version of nlm_lock()
639 * Returns: as for nlm_lock()
640 * Notes: These locks are in the same style as the standard nlm_lock,
641 * but the rpc.statd should not be called to establish a
642 * monitor for the client machine, since that machine is
643 * declared not to be running a rpc.statd, and so would not
644 * respond to the statd protocol.
645 */
646 nlm_res *
647 nlm_nm_lock_3_svc(arg, rqstp)
648 nlm_lockargs *arg;
649 struct svc_req *rqstp;
650 {
651 static nlm_res res;
652
653 if (debug_level)
654 log_from_addr("nlm_nm_lock", rqstp);
655
656 /* copy cookie from arg to result. See comment in nlm_test_1() */
657 res.cookie = arg->cookie;
658 res.stat.stat = nlm_granted;
659 return (&res);
660 }
661
662 /* nlm_free_all ------------------------------------------------------------ */
663 /*
664 * Purpose: Release all locks held by a named client
665 * Returns: Nothing
666 * Notes: Potential denial of service security problem here - the
667 * locks to be released are specified by a host name, independent
668 * of the address from which the request has arrived.
669 * Should probably be rejected if the named host has been
670 * using monitored locks.
671 */
672 void *
673 nlm_free_all_3_svc(arg, rqstp)
674 nlm_notify *arg;
675 struct svc_req *rqstp;
676 {
677 static char dummy;
678
679 if (debug_level)
680 log_from_addr("nlm_free_all", rqstp);
681 return (&dummy);
682 }
683