]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/statd/rmtcall.c
Imported upstream 1.2.8
[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
41 #include "sm_inter.h"
42 #include "statd.h"
43 #include "notlist.h"
44 #include "ha-callout.h"
45
46 #include "nsm.h"
47 #include "nfsrpc.h"
48
49 #if SIZEOF_SOCKLEN_T - 0 == 0
50 #define socklen_t int
51 #endif
52
53 static int              sockfd = -1;    /* notify socket */
54
55 /*
56  * Initialize socket used to notify lockd of peer reboots.
57  *
58  * Returns the file descriptor of the new socket if successful;
59  * otherwise returns -1 and logs an error.
60  *
61  * Lockd rejects such requests if the source port is not privileged.
62  * statd_get_socket() must be invoked while statd still holds root
63  * privileges in order for the socket to acquire a privileged source
64  * port.
65  */
66 int
67 statd_get_socket(void)
68 {
69         struct sockaddr_in      sin;
70         struct servent *se;
71         const int loopcnt = 100;
72         int i, tmp_sockets[loopcnt];
73
74         if (sockfd >= 0)
75                 return sockfd;
76
77         for (i = 0; i < loopcnt; ++i) {
78
79                 if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
80                         xlog(L_ERROR, "%s: Can't create socket: %m", __func__);
81                         break;
82                 }
83
84                 memset(&sin, 0, sizeof(sin));
85                 sin.sin_family = AF_INET;
86                 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
87
88                 if (bindresvport(sockfd, &sin) < 0) {
89                         xlog(D_GENERAL, "%s: can't bind to reserved port",
90                                         __func__);
91                         break;
92                 }
93                 se = getservbyport(sin.sin_port, "udp");
94                 if (se == NULL)
95                         break;
96                 /* rather not use that port, try again */
97
98                 tmp_sockets[i] = sockfd;
99         }
100
101         while (--i >= 0)
102                 close(tmp_sockets[i]);
103
104         if (sockfd < 0)
105                 return -1;
106
107         FD_SET(sockfd, &SVC_FDSET);
108         return sockfd;
109 }
110
111 static notify_list *
112 recv_rply(u_long *portp)
113 {
114         char                    msgbuf[NSM_MAXMSGSIZE];
115         ssize_t                 msglen;
116         notify_list             *lp = NULL;
117         XDR                     xdr;
118         struct sockaddr_in      sin;
119         socklen_t               alen = (socklen_t)sizeof(sin);
120         uint32_t                xid;
121
122         memset(msgbuf, 0, sizeof(msgbuf));
123         msglen = recvfrom(sockfd, msgbuf, sizeof(msgbuf), 0,
124                                 (struct sockaddr *)(char *)&sin, &alen);
125         if (msglen == (ssize_t)-1) {
126                 xlog_warn("%s: recvfrom failed: %m", __func__);
127                 return NULL;
128         }
129
130         memset(&xdr, 0, sizeof(xdr));
131         xdrmem_create(&xdr, msgbuf, (unsigned int)msglen, XDR_DECODE);
132         xid = nsm_parse_reply(&xdr);
133         if (xid == 0)
134                 goto done;
135         if (sin.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
136                 struct in_addr addr = sin.sin_addr;
137                 char buf[INET_ADDRSTRLEN];
138
139                 xlog_warn("%s: Unrecognized reply from %s", __func__,
140                                 inet_ntop(AF_INET, &addr, buf,
141                                                 (socklen_t)sizeof(buf)));
142                 goto done;
143         }
144
145         for (lp = notify; lp != NULL; lp = lp->next) {
146                 /* LH - this was a bug... it should have been checking
147                  * the xid from the response message from the client,
148                  * not the static, internal xid */
149                 if (lp->xid != xid)
150                         continue;
151                 if (lp->port == 0)
152                         *portp = nsm_recv_getport(&xdr);
153                 break;
154         }
155
156 done:
157         xdr_destroy(&xdr);
158         return lp;
159 }
160
161 /*
162  * Notify operation for a single list entry
163  */
164 static int
165 process_entry(notify_list *lp)
166 {
167         struct sockaddr_in      sin;
168
169         if (NL_TIMES(lp) == 0) {
170                 xlog(D_GENERAL, "%s: Cannot notify localhost, giving up",
171                                 __func__);
172                 return 0;
173         }
174
175         memset(&sin, 0, sizeof(sin));
176         sin.sin_family = AF_INET;
177         sin.sin_port   = lp->port;
178         /* LH - moved address into switch */
179
180         /* __FORCE__ loopback for callbacks to lockd ... */
181         /* Just in case we somehow ignored it thus far */
182         sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
183
184         if (sin.sin_port == 0)
185                 lp->xid = nsm_xmit_getport(sockfd, &sin,
186                                         (rpcprog_t)NL_MY_PROG(lp),
187                                         (rpcvers_t)NL_MY_VERS(lp));
188         else {
189                 struct mon m;
190
191                 memcpy(m.priv, NL_PRIV(lp), SM_PRIV_SIZE);
192
193                 m.mon_id.mon_name = NL_MON_NAME(lp);
194                 m.mon_id.my_id.my_name = NULL;
195                 m.mon_id.my_id.my_prog = NL_MY_PROG(lp);
196                 m.mon_id.my_id.my_vers = NL_MY_VERS(lp);
197                 m.mon_id.my_id.my_proc = NL_MY_PROC(lp);
198
199                 lp->xid = nsm_xmit_nlmcall(sockfd,
200                                 (struct sockaddr *)(char *)&sin,
201                                 (socklen_t)sizeof(sin), &m, NL_STATE(lp));
202         }
203         if (lp->xid == 0) {
204                 xlog_warn("%s: failed to notify port %d",
205                                 __func__, ntohs(lp->port));
206         }
207         NL_TIMES(lp) -= 1;
208
209         return 1;
210 }
211
212 /*
213  * Process a datagram received on the notify socket
214  */
215 int
216 process_reply(FD_SET_TYPE *rfds)
217 {
218         notify_list             *lp;
219         u_long                  port;
220
221         if (sockfd == -1 || !FD_ISSET(sockfd, rfds))
222                 return 0;
223
224         if (!(lp = recv_rply(&port)))
225                 return 1;
226
227         if (lp->port == 0) {
228                 if (port != 0) {
229                         lp->port = htons((unsigned short) port);
230                         process_entry(lp);
231                         NL_WHEN(lp) = time(NULL) + NOTIFY_TIMEOUT;
232                         nlist_remove(&notify, lp);
233                         nlist_insert_timer(&notify, lp);
234                         return 1;
235                 }
236                 xlog_warn("%s: service %d not registered on localhost",
237                         __func__, NL_MY_PROG(lp));
238         } else {
239                 xlog(D_GENERAL, "%s: Callback to %s (for %d) succeeded",
240                         __func__, NL_MY_NAME(lp), NL_MON_NAME(lp));
241         }
242         nlist_free(&notify, lp);
243         return 1;
244 }
245
246 /*
247  * Process a notify list, either for notifying remote hosts after reboot
248  * or for calling back (local) statd clients when the remote has notified
249  * us of a crash. 
250  */
251 int
252 process_notify_list(void)
253 {
254         notify_list     *entry;
255         time_t          now;
256
257         while ((entry = notify) != NULL && NL_WHEN(entry) < time(&now)) {
258                 if (process_entry(entry)) {
259                         NL_WHEN(entry) = time(NULL) + NOTIFY_TIMEOUT;
260                         nlist_remove(&notify, entry);
261                         nlist_insert_timer(&notify, entry);
262                 } else {
263                         xlog(L_ERROR,
264                                 "%s: Can't callback %s (%d,%d), giving up",
265                                         __func__,
266                                         NL_MY_NAME(entry),
267                                         NL_MY_PROG(entry),
268                                         NL_MY_VERS(entry));
269                         nlist_free(&notify, entry);
270                 }
271         }
272
273         return 1;
274 }