]>
Commit | Line | Data |
---|---|---|
03fb6eb0 A |
1 | /* |
2 | * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
ad21edcc A |
6 | * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights |
7 | * Reserved. This file contains Original Code and/or Modifications of | |
8 | * Original Code as defined in and that are subject to the Apple Public | |
9 | * Source License Version 1.1 (the "License"). You may not use this file | |
10 | * except in compliance with the License. Please obtain a copy of the | |
11 | * License at http://www.apple.com/publicsource and read it before using | |
12 | * this file. | |
03fb6eb0 A |
13 | * |
14 | * The Original Code and all software distributed under the License are | |
ad21edcc | 15 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
03fb6eb0 A |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
ad21edcc A |
18 | * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the |
19 | * License for the specific language governing rights and limitations | |
20 | * under the License. | |
03fb6eb0 A |
21 | * |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | /* | |
25 | * multi_call: send out multiple call messages, wait for first reply | |
26 | * Copyright (C) 1991 by NeXT, Inc. | |
27 | */ | |
28 | #include <rpc/rpc.h> | |
29 | #include <rpc/pmap_prot.h> | |
30 | #include <sys/socket.h> | |
31 | #include <syslog.h> | |
32 | #include <sys/param.h> | |
33 | #include <arpa/inet.h> | |
34 | #include <string.h> | |
35 | #include <unistd.h> | |
36 | ||
37 | #ifdef NETINFOD | |
38 | # include "socket_lock.h" | |
39 | # include "clib.h" | |
40 | # define multi_call _multi_call | |
41 | #else | |
42 | # define socket_lock() | |
43 | # define socket_unlock() | |
44 | # include "clib.h" | |
45 | #endif | |
46 | ||
47 | ||
48 | #define NRETRIES 5 | |
49 | #define USECS_PER_SEC 1000000 | |
50 | ||
51 | ||
52 | /* | |
53 | * Wrapper for gethostname() syscall | |
54 | */ | |
55 | static char * | |
56 | get_hostname(void) | |
57 | { | |
58 | int len; | |
59 | static char hostname[MAXHOSTNAMELEN + 1]; | |
60 | ||
61 | len = gethostname(hostname, sizeof(hostname)); | |
62 | if (len < 0) { | |
63 | hostname[0] = 0; | |
64 | } else { | |
65 | hostname[len] = 0; | |
66 | } | |
67 | return (hostname); | |
68 | } | |
69 | ||
70 | ||
71 | /* | |
72 | * Encode a call message | |
73 | */ | |
74 | static int | |
75 | encodemsg( | |
76 | char *buf, | |
77 | int buflen, | |
78 | struct rpc_msg *call, | |
79 | unsigned prognum, | |
80 | unsigned versnum, | |
81 | unsigned procnum, | |
82 | xdrproc_t xdr_args, | |
83 | void *arg | |
84 | ) | |
85 | { | |
86 | XDR xdr; | |
87 | unsigned size; | |
88 | unsigned pos; | |
89 | ||
90 | xdrmem_create(&xdr, buf, buflen, XDR_ENCODE); | |
91 | if (!xdr_callmsg(&xdr, call) || | |
92 | !xdr_u_int(&xdr, &prognum) || | |
93 | !xdr_u_int(&xdr, &versnum) || | |
94 | !xdr_u_int(&xdr, &procnum)) { | |
95 | return (0); | |
96 | } | |
97 | pos = xdr_getpos(&xdr); | |
98 | xdr_setpos(&xdr, pos + BYTES_PER_XDR_UNIT); | |
99 | if (!(*xdr_args)(&xdr, arg)) { | |
100 | return (0); | |
101 | } | |
102 | size = xdr_getpos(&xdr) - pos; | |
103 | xdr_setpos(&xdr, pos); | |
104 | if (!xdr_u_int(&xdr, &size)) { | |
105 | return (0); | |
106 | } | |
107 | return (pos + BYTES_PER_XDR_UNIT + size); | |
108 | } | |
109 | ||
110 | /* | |
111 | * Decode a reply message | |
112 | */ | |
113 | static int | |
114 | decodemsg( | |
115 | XDR *xdr, | |
116 | xdrproc_t xdr_res, | |
117 | void *res | |
118 | ) | |
119 | { | |
120 | unsigned port; | |
121 | unsigned len; | |
122 | long *buf; | |
123 | XDR bufxdr; | |
124 | ||
125 | if (!xdr_u_int(xdr, &port) || | |
126 | !xdr_u_int(xdr, &len) || | |
ccd4a120 | 127 | !(buf = (long *)xdr_inline(xdr, len))) { |
03fb6eb0 A |
128 | return (0); |
129 | } | |
130 | xdrmem_create(&bufxdr, (char *)buf, len * BYTES_PER_XDR_UNIT, | |
131 | XDR_DECODE); | |
132 | if (!(*xdr_res)(&bufxdr, res)) { | |
133 | return (0); | |
134 | } | |
135 | return (1); | |
136 | ||
137 | } | |
138 | ||
139 | /* | |
140 | * Do the real work | |
141 | */ | |
142 | enum clnt_stat | |
143 | multi_call( | |
144 | unsigned naddrs, | |
145 | struct in_addr *addrs, | |
146 | u_long prognum, | |
147 | u_long versnum, | |
148 | u_long procnum, | |
149 | xdrproc_t xdr_args, | |
150 | void *argsvec, | |
151 | unsigned argsize, | |
152 | xdrproc_t xdr_res, | |
153 | void *res, | |
154 | int (*eachresult)(void *, struct sockaddr_in *, int), | |
155 | int timeout | |
156 | ) | |
157 | { | |
158 | struct authunix_parms aup; | |
159 | char credbuf[MAX_AUTH_BYTES]; | |
160 | struct opaque_auth cred; | |
161 | struct opaque_auth verf; | |
162 | int gids[NGROUPS]; | |
163 | int s; | |
164 | struct timeval tv; | |
165 | struct timeval subtimeout; | |
166 | unsigned long long utimeout; | |
167 | int callno; | |
168 | int serverno; | |
169 | struct rpc_msg call; | |
170 | struct rpc_msg reply; | |
171 | struct sockaddr_in sin; | |
172 | struct sockaddr_in from; | |
173 | int fromsize; | |
174 | char buf[UDPMSGSIZE]; | |
175 | int buflen; | |
176 | unsigned trans_id; | |
177 | int dtablesize = getdtablesize(); | |
178 | XDR xdr; | |
179 | int sendlen; | |
180 | fd_set fds; | |
181 | ||
182 | /* | |
183 | * Fill in Unix auth stuff | |
184 | */ | |
185 | aup.aup_time = time(0); | |
186 | aup.aup_machname = get_hostname(); | |
187 | aup.aup_uid = getuid(); | |
188 | aup.aup_gid = getgid(); | |
189 | aup.aup_gids = gids; | |
190 | aup.aup_len = getgroups(NGROUPS, aup.aup_gids); | |
191 | ||
192 | /* | |
193 | * Encode unix auth | |
194 | */ | |
195 | xdrmem_create(&xdr, credbuf, sizeof(credbuf), XDR_ENCODE); | |
196 | if (!xdr_authunix_parms(&xdr, &aup)) { | |
197 | return (RPC_CANTENCODEARGS); | |
198 | } | |
199 | cred.oa_flavor = AUTH_UNIX; | |
200 | cred.oa_base = credbuf; | |
201 | cred.oa_length = xdr_getpos(&xdr); | |
202 | ||
203 | verf.oa_flavor = AUTH_NULL; | |
204 | verf.oa_length = 0; | |
205 | ||
206 | /* | |
207 | * Set up call header information | |
208 | */ | |
209 | trans_id = time(0) ^ getpid(); | |
210 | call.rm_xid = trans_id; | |
211 | call.rm_direction = CALL; | |
212 | call.rm_call.cb_rpcvers = 2; | |
213 | call.rm_call.cb_prog = PMAPPROG; | |
214 | call.rm_call.cb_vers = PMAPVERS; | |
215 | call.rm_call.cb_proc = PMAPPROC_CALLIT; | |
216 | call.rm_call.cb_cred = cred; | |
217 | call.rm_call.cb_verf = verf; | |
218 | ||
219 | ||
220 | /* | |
221 | * Open socket | |
222 | */ | |
223 | socket_lock(); | |
224 | s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); | |
225 | socket_unlock(); | |
226 | if (s < 0) { | |
227 | syslog(LOG_ERR, "multi_call: socket: %m"); | |
228 | return (RPC_FAILED); | |
229 | } | |
230 | ||
231 | /* | |
232 | * Init timeouts | |
233 | */ | |
234 | utimeout = ((unsigned long long) timeout) * USECS_PER_SEC; | |
235 | subtimeout.tv_sec = (utimeout >> NRETRIES) / USECS_PER_SEC; | |
236 | subtimeout.tv_usec = (utimeout >> NRETRIES) % USECS_PER_SEC; | |
237 | tv = subtimeout; | |
238 | ||
239 | /* | |
240 | * Init address info | |
241 | */ | |
242 | sin.sin_family = AF_INET; | |
243 | sin.sin_port = htons(PMAPPORT); | |
244 | bzero(sin.sin_zero, sizeof(sin.sin_zero)); | |
245 | ||
246 | for (callno = 0; callno <= NRETRIES; callno++) { | |
247 | /* | |
248 | * Send a call message to each host with the appropriate args | |
249 | */ | |
250 | for (serverno = 0; serverno < naddrs; serverno++) { | |
251 | call.rm_xid = trans_id + serverno; | |
252 | buflen = encodemsg(buf, sizeof(buf), &call, | |
253 | prognum, versnum, procnum, | |
254 | xdr_args, (argsvec + | |
255 | (serverno * argsize))); | |
256 | if (buflen == 0) { | |
257 | /* | |
258 | * Encode failed | |
259 | */ | |
260 | continue; | |
261 | } | |
262 | sin.sin_addr = addrs[serverno]; | |
263 | sendlen = sendto(s, buf, buflen, 0, | |
264 | (struct sockaddr *)&sin, sizeof(sin)); | |
265 | if (sendlen != buflen) { | |
266 | syslog(LOG_ERR, | |
267 | "Cannot send multicall packet to %s: %m", | |
268 | inet_ntoa(addrs[serverno])); | |
269 | } | |
270 | } | |
271 | ||
272 | /* | |
273 | * Double the timeout from previous timeout, if necessary | |
274 | */ | |
275 | if (callno > 1) { | |
276 | tv.tv_sec *= 2; | |
277 | tv.tv_usec *= 2; | |
278 | if (tv.tv_usec >= USECS_PER_SEC) { | |
279 | tv.tv_usec -= USECS_PER_SEC; | |
280 | tv.tv_sec++; | |
281 | } | |
282 | } | |
283 | ||
284 | ||
285 | #ifdef NETINFOD | |
286 | /* | |
287 | * Check for cancel by user | |
288 | */ | |
289 | if (alert_aborted()) { | |
290 | socket_lock(); | |
291 | close(s); | |
292 | socket_unlock(); | |
293 | return (RPC_FAILED); | |
294 | } | |
295 | #endif | |
296 | /* | |
297 | * Wait for reply | |
298 | */ | |
299 | FD_ZERO(&fds); | |
300 | FD_SET(s, &fds); | |
301 | switch (select(dtablesize, &fds, NULL, NULL, &tv)) { | |
302 | case -1: | |
303 | syslog(LOG_ERR, "select failure: %m"); | |
304 | continue; | |
305 | case 0: | |
306 | continue; | |
307 | default: | |
308 | break; | |
309 | } | |
310 | ||
311 | /* | |
312 | * Receive packet | |
313 | */ | |
314 | fromsize = sizeof(from); | |
315 | buflen = recvfrom(s, buf, sizeof(buf), 0, | |
316 | (struct sockaddr *)&from, &fromsize); | |
317 | if (buflen < 0) { | |
318 | continue; | |
319 | } | |
320 | ||
321 | /* | |
322 | * Decode packet and if no errors, call eachresult | |
323 | */ | |
324 | xdrmem_create(&xdr, buf, buflen, XDR_DECODE); | |
325 | reply.rm_reply.rp_acpt.ar_results.proc = xdr_void; | |
326 | reply.rm_reply.rp_acpt.ar_results.where = NULL; | |
327 | if (xdr_replymsg(&xdr, &reply) && | |
328 | (reply.rm_xid >= trans_id) && | |
329 | (reply.rm_xid < trans_id + naddrs) && | |
330 | (reply.rm_reply.rp_stat == MSG_ACCEPTED) && | |
331 | (reply.acpted_rply.ar_stat == SUCCESS) && | |
332 | decodemsg(&xdr, xdr_res, res)) { | |
333 | if ((*eachresult)(res, &from, | |
334 | reply.rm_xid - trans_id)) { | |
335 | xdr_free(xdr_res, res); | |
336 | socket_lock(); | |
337 | close(s); | |
338 | socket_unlock(); | |
339 | return (RPC_SUCCESS); | |
340 | } | |
341 | } | |
342 | xdr_free(xdr_res, res); | |
343 | } | |
344 | socket_lock(); | |
345 | close(s); | |
346 | socket_unlock(); | |
347 | return (RPC_TIMEDOUT); | |
348 | } | |
349 | ||
350 | ||
351 | ||
352 |