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