]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/statd/rmtcall.c
b70e5bb701e829a391ff6fda47abd10239fab4cb
[nfs-utils.git] / utils / statd / rmtcall.c
1 /*
2  * Copyright (C) 1996, 1999 Olaf Kirch
3  * Modified by Jeffrey A. Uphoff, 1997-1999.
4  * Modified by H.J. Lu, 1998.
5  * Modified by Lon Hohberger, Oct. 2000
6  *   - Bugfix handling client responses.
7  *   - Paranoia on NOTIFY_CALLBACK case
8  *
9  * NSM for Linux.
10  */
11
12 /*
13  * After reboot, notify all hosts on our notify list. In order not to
14  * hang statd with delivery to dead hosts, we perform all RPC calls in
15  * parallel.
16  *
17  * It would have been nice to use the portmapper's rmtcall feature,
18  * but that's not possible for security reasons (the portmapper would
19  * have to forward the call with root privs for most statd's, which
20  * it won't if it's worth its money).
21  */
22
23 #include "config.h"
24
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <sys/time.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 #include <rpc/rpc.h>
31 #include <rpc/pmap_prot.h>
32 #include <rpc/pmap_rmt.h>
33 #include <time.h>
34 #include <netdb.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include "sm_inter.h"
38 #include "statd.h"
39 #include "notlist.h"
40 #include "log.h"
41
42 #define MAXMSGSIZE      (2048 / sizeof(unsigned int))
43
44 static unsigned long    xid = 0;        /* RPC XID counter */
45 static int              sockfd = -1;    /* notify socket */
46
47 /*
48  * Initialize callback socket
49  */
50 static int
51 get_socket(void)
52 {
53         struct sockaddr_in      sin;
54
55         if (sockfd >= 0)
56                 return sockfd;
57
58         if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
59                 log(L_CRIT, "Can't create socket: %m");
60                 return -1;
61         }
62
63         FD_SET(sockfd, &SVC_FDSET);
64
65         memset(&sin, 0, sizeof(sin));
66         sin.sin_family = AF_INET;
67         if (bindresvport(sockfd, &sin) < 0) {
68                 dprintf(L_WARNING,
69                         "process_hosts: can't bind to reserved port\n");
70         }
71
72         return sockfd;
73 }
74
75 /*
76  * Try to resolve host name for notify/callback request
77  *
78  * When compiled with RESTRICTED_STATD defined, we expect all
79  * host names to be dotted quads. See monitor.c for details. --okir
80  */
81 #ifdef RESTRICTED_STATD
82 static int
83 try_to_resolve(notify_list *lp)
84 {
85         char            *hname;
86
87         if (NL_TYPE(lp) == NOTIFY_REBOOT)
88                 hname = NL_MON_NAME(lp);
89         else
90                 hname = NL_MY_NAME(lp);
91         if (!inet_aton(hname, &(NL_ADDR(lp)))) {
92                 log(L_ERROR, "%s is not an dotted-quad address", hname);
93                 NL_TIMES(lp) = 0;
94                 return 0;
95         }
96
97         /* XXX: In order to handle multi-homed hosts, we could do
98          * a reverse lookup, a forward lookup, and cycle through
99          * all the addresses.
100          */
101         return 1;
102 }
103 #else
104 static int
105 try_to_resolve(notify_list *lp)
106 {
107         struct hostent  *hp;
108         char            *hname;
109
110         if (NL_TYPE(lp) == NOTIFY_REBOOT)
111                 hname = NL_MON_NAME(lp);
112         else
113                 hname = NL_MY_NAME(lp);
114
115         dprintf(L_DEBUG, "Trying to resolve %s.", hname);
116         if (!(hp = gethostbyname(hname))) {
117                 herror("gethostbyname");
118                 NL_TIMES(lp) -= 1;
119                 return 0;
120         }
121
122         if (hp->h_addrtype != AF_INET) {
123                 log(L_ERROR, "%s is not an AF_INET address", hname);
124                 NL_TIMES(lp) = 0;
125                 return 0;
126         }
127
128         /* FIXME: should try all addresses for multi-homed hosts in
129          * alternation because one interface might be down/unreachable. */
130         NL_ADDR(lp) = *(struct in_addr *) hp->h_addr;
131
132         dprintf(L_DEBUG, "address of %s is %s", hname, inet_ntoa(NL_ADDR(lp)));
133         return 1;
134 }
135 #endif
136
137 static unsigned long
138 xmit_call(int sockfd, struct sockaddr_in *sin,
139           u_int32_t prog, u_int32_t vers, u_int32_t proc,
140           xdrproc_t func, void *obj)
141 /*              __u32 prog, __u32 vers, __u32 proc, xdrproc_t func, void *obj) */
142 {
143         unsigned int            msgbuf[MAXMSGSIZE], msglen;
144         struct rpc_msg          mesg;
145         struct pmap             pmap;
146         XDR                     xdr, *xdrs = &xdr;
147         int                     err;
148
149         if (!xid)
150                 xid = getpid() + time(NULL);
151
152         mesg.rm_xid = ++xid;
153         mesg.rm_direction = CALL;
154         mesg.rm_call.cb_rpcvers = 2;
155         if (sin->sin_port == 0) {
156                 sin->sin_port = htons(PMAPPORT);
157                 mesg.rm_call.cb_prog = PMAPPROG;
158                 mesg.rm_call.cb_vers = PMAPVERS;
159                 mesg.rm_call.cb_proc = PMAPPROC_GETPORT;
160                 pmap.pm_prog = prog;
161                 pmap.pm_vers = vers;
162                 pmap.pm_prot = IPPROTO_UDP;
163                 pmap.pm_port = 0;
164                 func = (xdrproc_t) xdr_pmap;
165                 obj  = &pmap;
166         } else {
167                 mesg.rm_call.cb_prog = prog;
168                 mesg.rm_call.cb_vers = vers;
169                 mesg.rm_call.cb_proc = proc;
170         }
171         mesg.rm_call.cb_cred.oa_flavor = AUTH_NULL;
172         mesg.rm_call.cb_cred.oa_base = (caddr_t) NULL;
173         mesg.rm_call.cb_cred.oa_length = 0;
174         mesg.rm_call.cb_verf.oa_flavor = AUTH_NULL;
175         mesg.rm_call.cb_verf.oa_base = (caddr_t) NULL;
176         mesg.rm_call.cb_verf.oa_length = 0;
177
178         /* Create XDR memory object for encoding */
179         xdrmem_create(xdrs, (caddr_t) msgbuf, sizeof(msgbuf), XDR_ENCODE);
180
181         /* Encode the RPC header part and payload */
182         if (!xdr_callmsg(xdrs, &mesg) || !func(xdrs, obj)) {
183                 dprintf(L_WARNING, "xmit_mesg: can't encode RPC message!\n");
184                 xdr_destroy(xdrs);
185                 return 0;
186         }
187
188         /* Get overall length of datagram */
189         msglen = xdr_getpos(xdrs);
190
191         if ((err = sendto(sockfd, msgbuf, msglen, 0,
192                         (struct sockaddr *) sin, sizeof(*sin))) < 0) {
193                 dprintf(L_WARNING, "xmit_mesg: sendto failed: %m");
194         } else if (err != msglen) {
195                 dprintf(L_WARNING, "xmit_mesg: short write: %m\n");
196         }
197
198         xdr_destroy(xdrs);
199
200         return err == msglen? xid : 0;
201 }
202
203 static notify_list *
204 recv_rply(int sockfd, struct sockaddr_in *sin, u_long *portp)
205 {
206         unsigned int            msgbuf[MAXMSGSIZE], msglen;
207         struct rpc_msg          mesg;
208         notify_list             *lp = NULL;
209         XDR                     xdr, *xdrs = &xdr;
210         int                     alen = sizeof(*sin);
211
212         /* Receive message */
213         if ((msglen = recvfrom(sockfd, msgbuf, sizeof(msgbuf), 0,
214                         (struct sockaddr *) sin, &alen)) < 0) {
215                 dprintf(L_WARNING, "recv_rply: recvfrom failed: %m");
216                 return NULL;
217         }
218
219         /* Create XDR object for decoding buffer */
220         xdrmem_create(xdrs, (caddr_t) msgbuf, msglen, XDR_DECODE);
221
222         memset(&mesg, 0, sizeof(mesg));
223         mesg.rm_reply.rp_acpt.ar_results.where = NULL;
224         mesg.rm_reply.rp_acpt.ar_results.proc = (xdrproc_t) xdr_void;
225
226         if (!xdr_replymsg(xdrs, &mesg)) {
227                 log(L_WARNING, "recv_rply: can't decode RPC message!\n");
228                 goto done;
229         }
230
231         if (mesg.rm_reply.rp_stat != 0) {
232                 log(L_WARNING, "recv_rply: [%s] RPC status %d\n", 
233                                 inet_ntoa(sin->sin_addr),
234                                 mesg.rm_reply.rp_stat);
235                 goto done;
236         }
237         if (mesg.rm_reply.rp_acpt.ar_stat != 0) {
238                 log(L_WARNING, "recv_rply: [%s] RPC status %d\n",
239                                 inet_ntoa(sin->sin_addr),
240                                 mesg.rm_reply.rp_acpt.ar_stat);
241                 goto done;
242         }
243
244         for (lp = notify; lp != NULL; lp = lp->next) {
245                 /* LH - this was a bug... it should have been checking
246                  * the xid from the response message from the client,
247                  * not the static, internal xid */
248                 if (lp->xid != mesg.rm_xid)
249                         continue;
250                 if (lp->addr.s_addr != sin->sin_addr.s_addr) {
251                         char addr [18];
252                         strncpy (addr, inet_ntoa(lp->addr),
253                                  sizeof (addr) - 1);
254                         addr [sizeof (addr) - 1] = '\0';
255                         dprintf(L_WARNING, "address mismatch: "
256                                 "expected %s, got %s\n",
257                                 addr, inet_ntoa(sin->sin_addr));
258                 }
259                 if (lp->port == 0) {
260                         if (!xdr_u_long(xdrs, portp)) {
261                                 log(L_WARNING, "recv_rply: [%s] "
262                                         "can't decode reply body!\n",
263                                         inet_ntoa(sin->sin_addr));
264                                 lp = NULL;
265                                 goto done;
266                         }
267                 }
268                 break;
269         }
270
271 done:
272         xdr_destroy(xdrs);
273         return lp;
274 }
275
276 /*
277  * Notify operation for a single list entry
278  */
279 static int
280 process_entry(int sockfd, notify_list *lp)
281 {
282         struct sockaddr_in      sin;
283         struct status           new_status;
284         xdrproc_t               func;
285         void                    *objp;
286         u_int32_t               proc, vers, prog;
287 /*      __u32                   proc, vers, prog; */
288
289         if (lp->addr.s_addr == INADDR_ANY && !try_to_resolve(lp))
290                 return NL_TIMES(lp);
291         if (NL_TIMES(lp) == 0) {
292                 log(L_DEBUG, "Cannot notify %s, giving up.\n",
293                                         inet_ntoa(NL_ADDR(lp)));
294                 return 0;
295         }
296
297         memset(&sin, 0, sizeof(sin));
298         sin.sin_family = AF_INET;
299         sin.sin_port   = lp->port;
300         /* LH - moved address into switch */
301
302         switch (NL_TYPE(lp)) {
303         case NOTIFY_REBOOT:
304                 prog = SM_PROG;
305                 vers = SM_VERS;
306                 proc = SM_NOTIFY;
307
308                 /* Use source address for notify replies */
309                 sin.sin_addr   = lp->addr;
310
311                 func = (xdrproc_t) xdr_stat_chge;
312                 objp = &SM_stat_chge;
313                 break;
314         case NOTIFY_CALLBACK:
315                 prog = NL_MY_PROG(lp);
316                 vers = NL_MY_VERS(lp);
317                 proc = NL_MY_PROC(lp);
318
319                 /* __FORCE__ loopback for callbacks to lockd ... */
320                 /* Just in case we somehow ignored it thus far */
321                 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
322
323                 func = (xdrproc_t) xdr_status;
324                 objp = &new_status;
325                 new_status.mon_name = NL_MON_NAME(lp);
326                 new_status.state    = NL_STATE(lp);
327                 memcpy(new_status.priv, NL_PRIV(lp), SM_PRIV_SIZE);
328                 break;
329         default:
330                 log(L_ERROR, "notify_host: unknown notify type %d",
331                                 NL_TYPE(lp));
332                 return 0;
333         }
334
335         lp->xid = xmit_call(sockfd, &sin, prog, vers, proc, func, objp);
336         if (!lp->xid) {
337                 log(L_WARNING, "notify_host: failed to notify %s\n",
338                                 inet_ntoa(lp->addr));
339         }
340         NL_TIMES(lp) -= 1;
341
342         return 1;
343 }
344
345 /*
346  * Process a datagram received on the notify socket
347  */
348 int
349 process_reply(FD_SET_TYPE *rfds)
350 {
351         struct sockaddr_in      sin;
352         notify_list             *lp;
353         u_long                  port;
354
355         if (sockfd == -1 || !FD_ISSET(sockfd, rfds))
356                 return 0;
357
358         if (!(lp = recv_rply(sockfd, &sin, &port)))
359                 return 1;
360
361         if (lp->port == 0) {
362                 if (port != 0) {
363                         lp->port = htons((unsigned short) port);
364                         process_entry(sockfd, lp);
365                         NL_WHEN(lp) = time(NULL) + NOTIFY_TIMEOUT;
366                         nlist_remove(&notify, lp);
367                         nlist_insert_timer(&notify, lp);
368                         return 1;
369                 }
370                 log(L_WARNING, "recv_rply: [%s] service %d not registered",
371                         inet_ntoa(lp->addr),
372                         NL_TYPE(lp) == NOTIFY_REBOOT? SM_PROG : NL_MY_PROG(lp));
373         } else if (NL_TYPE(lp) == NOTIFY_REBOOT) {
374                 dprintf(L_DEBUG, "Notification of %s succeeded.",
375                         NL_MON_NAME(lp));
376                 xunlink(SM_BAK_DIR, NL_MON_NAME(lp), 0);
377         } else {
378                 dprintf(L_DEBUG, "Callback to %s (for %d) succeeded.",
379                         NL_MY_NAME(lp), NL_MON_NAME(lp));
380         }
381         nlist_free(&notify, lp);
382         return 1;
383 }
384
385 /*
386  * Process a notify list, either for notifying remote hosts after reboot
387  * or for calling back (local) statd clients when the remote has notified
388  * us of a crash. 
389  */
390 int
391 process_notify_list(void)
392 {
393         notify_list     *entry;
394         time_t          now;
395         int             fd;
396
397         if ((fd = get_socket()) < 0)
398                 return 0;
399
400         while ((entry = notify) != NULL && NL_WHEN(entry) < time(&now)) {
401                 if (process_entry(fd, entry)) {
402                         NL_WHEN(entry) = time(NULL) + NOTIFY_TIMEOUT;
403                         nlist_remove(&notify, entry);
404                         nlist_insert_timer(&notify, entry);
405                 } else if (NL_TYPE(entry) == NOTIFY_CALLBACK) {
406                         log(L_ERROR,
407                                 "Can't callback %s (%d,%d), giving up.",
408                                         NL_MY_NAME(entry),
409                                         NL_MY_PROG(entry),
410                                         NL_MY_VERS(entry));
411                         nlist_free(&notify, entry);
412                 } else {
413                         log(L_ERROR,
414                                 "Can't notify %s, giving up.",
415                                         NL_MON_NAME(entry));
416                         xunlink(SM_BAK_DIR, NL_MON_NAME(entry), 0);
417                         nlist_free(&notify, entry);
418                 }
419         }
420
421         return 1;
422 }