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