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