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