]>
Commit | Line | Data |
---|---|---|
28f540f4 RM |
1 | /* @(#)svc_udp.c 2.2 88/07/29 4.0 RPCSRC */ |
2 | /* | |
3 | * Sun RPC is a product of Sun Microsystems, Inc. and is provided for | |
4 | * unrestricted use provided that this legend is included on all tape | |
5 | * media and as a part of the software program in whole or part. Users | |
6 | * may copy or modify Sun RPC without charge, but are not authorized | |
7 | * to license or distribute it to anyone else except as part of a product or | |
8 | * program developed by the user. | |
cbd3dceb | 9 | * |
28f540f4 RM |
10 | * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE |
11 | * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR | |
12 | * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. | |
cbd3dceb | 13 | * |
28f540f4 RM |
14 | * Sun RPC is provided with no support and without any obligation on the |
15 | * part of Sun Microsystems, Inc. to assist in its use, correction, | |
16 | * modification or enhancement. | |
cbd3dceb | 17 | * |
28f540f4 RM |
18 | * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE |
19 | * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC | |
20 | * OR ANY PART THEREOF. | |
cbd3dceb | 21 | * |
28f540f4 RM |
22 | * In no event will Sun Microsystems, Inc. be liable for any lost revenue |
23 | * or profits or other special, indirect and consequential damages, even if | |
24 | * Sun has been advised of the possibility of such damages. | |
cbd3dceb | 25 | * |
28f540f4 RM |
26 | * Sun Microsystems, Inc. |
27 | * 2550 Garcia Avenue | |
28 | * Mountain View, California 94043 | |
29 | */ | |
30 | #if !defined(lint) && defined(SCCSIDS) | |
31 | static char sccsid[] = "@(#)svc_udp.c 1.24 87/08/11 Copyr 1984 Sun Micro"; | |
32 | #endif | |
33 | ||
34 | /* | |
35 | * svc_udp.c, | |
36 | * Server side for UDP/IP based RPC. (Does some caching in the hopes of | |
37 | * achieving execute-at-most-once semantics.) | |
38 | * | |
39 | * Copyright (C) 1984, Sun Microsystems, Inc. | |
40 | */ | |
41 | ||
42 | #include <stdio.h> | |
e7fd8a39 UD |
43 | #include <unistd.h> |
44 | #include <string.h> | |
28f540f4 RM |
45 | #include <rpc/rpc.h> |
46 | #include <sys/socket.h> | |
47 | #include <errno.h> | |
4360eafd | 48 | #include <libintl.h> |
28f540f4 | 49 | |
50304ef0 UD |
50 | #ifdef USE_IN_LIBIO |
51 | # include <libio/iolibio.h> | |
52 | # define fputs(s, f) _IO_fputs (s, f) | |
53 | #endif | |
28f540f4 RM |
54 | |
55 | #define rpc_buffer(xprt) ((xprt)->xp_p1) | |
1f64ac13 | 56 | #ifndef MAX |
e7fd8a39 | 57 | #define MAX(a, b) ((a > b) ? a : b) |
1f64ac13 | 58 | #endif |
28f540f4 | 59 | |
e7fd8a39 UD |
60 | static bool_t svcudp_recv (SVCXPRT *, struct rpc_msg *); |
61 | static bool_t svcudp_reply (SVCXPRT *, struct rpc_msg *); | |
62 | static enum xprt_stat svcudp_stat (SVCXPRT *); | |
63 | static bool_t svcudp_getargs (SVCXPRT *, xdrproc_t, caddr_t); | |
64 | static bool_t svcudp_freeargs (SVCXPRT *, xdrproc_t, caddr_t); | |
65 | static void svcudp_destroy (SVCXPRT *); | |
66 | ||
67 | static const struct xp_ops svcudp_op = | |
68 | { | |
69 | svcudp_recv, | |
70 | svcudp_stat, | |
71 | svcudp_getargs, | |
72 | svcudp_reply, | |
73 | svcudp_freeargs, | |
74 | svcudp_destroy | |
28f540f4 RM |
75 | }; |
76 | ||
e7fd8a39 UD |
77 | static int cache_get (SVCXPRT *, struct rpc_msg *, char **replyp, |
78 | u_long *replylenp); | |
79 | static void cache_set (SVCXPRT *xprt, u_long replylen); | |
28f540f4 RM |
80 | |
81 | /* | |
82 | * kept in xprt->xp_p2 | |
83 | */ | |
e7fd8a39 UD |
84 | struct svcudp_data |
85 | { | |
86 | u_int su_iosz; /* byte size of send.recv buffer */ | |
87 | u_long su_xid; /* transaction id */ | |
88 | XDR su_xdrs; /* XDR handle */ | |
89 | char su_verfbody[MAX_AUTH_BYTES]; /* verifier body */ | |
90 | char *su_cache; /* cached data, NULL if no cache */ | |
91 | }; | |
28f540f4 RM |
92 | #define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2)) |
93 | ||
94 | /* | |
95 | * Usage: | |
e7fd8a39 | 96 | * xprt = svcudp_create(sock); |
28f540f4 RM |
97 | * |
98 | * If sock<0 then a socket is created, else sock is used. | |
99 | * If the socket, sock is not bound to a port then svcudp_create | |
100 | * binds it to an arbitrary port. In any (successful) case, | |
101 | * xprt->xp_sock is the registered socket number and xprt->xp_port is the | |
102 | * associated port number. | |
103 | * Once *xprt is initialized, it is registered as a transporter; | |
104 | * see (svc.h, xprt_register). | |
105 | * The routines returns NULL if a problem occurred. | |
106 | */ | |
107 | SVCXPRT * | |
e7fd8a39 UD |
108 | svcudp_bufcreate (sock, sendsz, recvsz) |
109 | int sock; | |
110 | u_int sendsz, recvsz; | |
28f540f4 | 111 | { |
e7fd8a39 UD |
112 | bool_t madesock = FALSE; |
113 | SVCXPRT *xprt; | |
114 | struct svcudp_data *su; | |
115 | struct sockaddr_in addr; | |
70b0abba | 116 | socklen_t len = sizeof (struct sockaddr_in); |
e7fd8a39 UD |
117 | |
118 | if (sock == RPC_ANYSOCK) | |
119 | { | |
50304ef0 | 120 | if ((sock = __socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) |
e7fd8a39 UD |
121 | { |
122 | perror (_("svcudp_create: socket creation problem")); | |
123 | return (SVCXPRT *) NULL; | |
28f540f4 | 124 | } |
e7fd8a39 UD |
125 | madesock = TRUE; |
126 | } | |
50304ef0 | 127 | __bzero ((char *) &addr, sizeof (addr)); |
e7fd8a39 UD |
128 | addr.sin_family = AF_INET; |
129 | if (bindresvport (sock, &addr)) | |
130 | { | |
131 | addr.sin_port = 0; | |
132 | (void) bind (sock, (struct sockaddr *) &addr, len); | |
133 | } | |
134 | if (getsockname (sock, (struct sockaddr *) &addr, &len) != 0) | |
135 | { | |
136 | perror (_("svcudp_create - cannot getsockname")); | |
137 | if (madesock) | |
50304ef0 | 138 | (void) __close (sock); |
e7fd8a39 UD |
139 | return (SVCXPRT *) NULL; |
140 | } | |
141 | xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT)); | |
142 | if (xprt == NULL) | |
143 | { | |
144 | (void) fputs (_("svcudp_create: out of memory\n"), stderr); | |
145 | return NULL; | |
146 | } | |
147 | su = (struct svcudp_data *) mem_alloc (sizeof (*su)); | |
148 | if (su == NULL) | |
149 | { | |
150 | (void) fputs (_("svcudp_create: out of memory\n"), stderr); | |
151 | return NULL; | |
152 | } | |
153 | su->su_iosz = ((MAX (sendsz, recvsz) + 3) / 4) * 4; | |
154 | if ((rpc_buffer (xprt) = mem_alloc (su->su_iosz)) == NULL) | |
155 | { | |
156 | (void) fputs (_("svcudp_create: out of memory\n"), stderr); | |
157 | return NULL; | |
158 | } | |
159 | xdrmem_create (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz, XDR_DECODE); | |
160 | su->su_cache = NULL; | |
161 | xprt->xp_p2 = (caddr_t) su; | |
162 | xprt->xp_verf.oa_base = su->su_verfbody; | |
163 | xprt->xp_ops = &svcudp_op; | |
164 | xprt->xp_port = ntohs (addr.sin_port); | |
165 | xprt->xp_sock = sock; | |
166 | xprt_register (xprt); | |
167 | return xprt; | |
28f540f4 RM |
168 | } |
169 | ||
170 | SVCXPRT * | |
e7fd8a39 UD |
171 | svcudp_create (sock) |
172 | int sock; | |
28f540f4 RM |
173 | { |
174 | ||
e7fd8a39 | 175 | return svcudp_bufcreate (sock, UDPMSGSIZE, UDPMSGSIZE); |
28f540f4 RM |
176 | } |
177 | ||
178 | static enum xprt_stat | |
e7fd8a39 UD |
179 | svcudp_stat (xprt) |
180 | SVCXPRT *xprt; | |
28f540f4 RM |
181 | { |
182 | ||
e7fd8a39 | 183 | return XPRT_IDLE; |
28f540f4 RM |
184 | } |
185 | ||
186 | static bool_t | |
e7fd8a39 UD |
187 | svcudp_recv (xprt, msg) |
188 | SVCXPRT *xprt; | |
189 | struct rpc_msg *msg; | |
28f540f4 | 190 | { |
e7fd8a39 UD |
191 | struct svcudp_data *su = su_data (xprt); |
192 | XDR *xdrs = &(su->su_xdrs); | |
193 | int rlen; | |
194 | char *reply; | |
195 | u_long replylen; | |
70b0abba | 196 | socklen_t len; |
e7fd8a39 UD |
197 | |
198 | again: | |
f671aeab | 199 | /* FIXME -- should xp_addrlen be a size_t? */ |
70b0abba | 200 | len = (socklen_t) sizeof(struct sockaddr_in); |
e7fd8a39 | 201 | rlen = recvfrom (xprt->xp_sock, rpc_buffer (xprt), (int) su->su_iosz, 0, |
f671aeab UD |
202 | (struct sockaddr *) &(xprt->xp_raddr), &len); |
203 | xprt->xp_addrlen = len; | |
e7fd8a39 UD |
204 | if (rlen == -1 && errno == EINTR) |
205 | goto again; | |
206 | if (rlen < 16) /* < 4 32-bit ints? */ | |
207 | return FALSE; | |
208 | xdrs->x_op = XDR_DECODE; | |
209 | XDR_SETPOS (xdrs, 0); | |
210 | if (!xdr_callmsg (xdrs, msg)) | |
211 | return FALSE; | |
212 | su->su_xid = msg->rm_xid; | |
213 | if (su->su_cache != NULL) | |
214 | { | |
215 | if (cache_get (xprt, msg, &reply, &replylen)) | |
216 | { | |
217 | (void) sendto (xprt->xp_sock, reply, (int) replylen, 0, | |
f671aeab | 218 | (struct sockaddr *) &xprt->xp_raddr, len); |
e7fd8a39 | 219 | return TRUE; |
28f540f4 | 220 | } |
e7fd8a39 UD |
221 | } |
222 | return TRUE; | |
28f540f4 RM |
223 | } |
224 | ||
225 | static bool_t | |
e7fd8a39 UD |
226 | svcudp_reply (xprt, msg) |
227 | SVCXPRT *xprt; | |
228 | struct rpc_msg *msg; | |
28f540f4 | 229 | { |
e7fd8a39 UD |
230 | struct svcudp_data *su = su_data (xprt); |
231 | XDR *xdrs = &(su->su_xdrs); | |
232 | int slen; | |
233 | bool_t stat = FALSE; | |
234 | ||
235 | xdrs->x_op = XDR_ENCODE; | |
236 | XDR_SETPOS (xdrs, 0); | |
237 | msg->rm_xid = su->su_xid; | |
238 | if (xdr_replymsg (xdrs, msg)) | |
239 | { | |
240 | slen = (int) XDR_GETPOS (xdrs); | |
241 | if (sendto (xprt->xp_sock, rpc_buffer (xprt), slen, 0, | |
242 | (struct sockaddr *) &(xprt->xp_raddr), xprt->xp_addrlen) | |
243 | == slen) | |
244 | { | |
245 | stat = TRUE; | |
246 | if (su->su_cache && slen >= 0) | |
247 | { | |
248 | cache_set (xprt, (u_long) slen); | |
249 | } | |
28f540f4 | 250 | } |
e7fd8a39 UD |
251 | } |
252 | return stat; | |
28f540f4 RM |
253 | } |
254 | ||
255 | static bool_t | |
e7fd8a39 UD |
256 | svcudp_getargs (xprt, xdr_args, args_ptr) |
257 | SVCXPRT *xprt; | |
258 | xdrproc_t xdr_args; | |
259 | caddr_t args_ptr; | |
28f540f4 RM |
260 | { |
261 | ||
e7fd8a39 | 262 | return (*xdr_args) (&(su_data (xprt)->su_xdrs), args_ptr); |
28f540f4 RM |
263 | } |
264 | ||
265 | static bool_t | |
e7fd8a39 UD |
266 | svcudp_freeargs (xprt, xdr_args, args_ptr) |
267 | SVCXPRT *xprt; | |
268 | xdrproc_t xdr_args; | |
269 | caddr_t args_ptr; | |
28f540f4 | 270 | { |
e7fd8a39 | 271 | XDR *xdrs = &(su_data (xprt)->su_xdrs); |
28f540f4 | 272 | |
e7fd8a39 UD |
273 | xdrs->x_op = XDR_FREE; |
274 | return (*xdr_args) (xdrs, args_ptr); | |
28f540f4 RM |
275 | } |
276 | ||
277 | static void | |
e7fd8a39 UD |
278 | svcudp_destroy (xprt) |
279 | SVCXPRT *xprt; | |
28f540f4 | 280 | { |
e7fd8a39 UD |
281 | struct svcudp_data *su = su_data (xprt); |
282 | ||
283 | xprt_unregister (xprt); | |
50304ef0 | 284 | (void) __close (xprt->xp_sock); |
e7fd8a39 UD |
285 | XDR_DESTROY (&(su->su_xdrs)); |
286 | mem_free (rpc_buffer (xprt), su->su_iosz); | |
287 | mem_free ((caddr_t) su, sizeof (struct svcudp_data)); | |
288 | mem_free ((caddr_t) xprt, sizeof (SVCXPRT)); | |
28f540f4 RM |
289 | } |
290 | ||
291 | ||
292 | /***********this could be a separate file*********************/ | |
293 | ||
294 | /* | |
295 | * Fifo cache for udp server | |
296 | * Copies pointers to reply buffers into fifo cache | |
297 | * Buffers are sent again if retransmissions are detected. | |
298 | */ | |
299 | ||
e7fd8a39 | 300 | #define SPARSENESS 4 /* 75% sparse */ |
28f540f4 RM |
301 | |
302 | #define CACHE_PERROR(msg) \ | |
303 | (void) fprintf(stderr,"%s\n", msg) | |
304 | ||
305 | #define ALLOC(type, size) \ | |
306 | (type *) mem_alloc((unsigned) (sizeof(type) * (size))) | |
307 | ||
308 | #define BZERO(addr, type, size) \ | |
50304ef0 | 309 | __bzero((char *) addr, sizeof(type) * (int) (size)) |
28f540f4 RM |
310 | |
311 | /* | |
312 | * An entry in the cache | |
313 | */ | |
314 | typedef struct cache_node *cache_ptr; | |
e7fd8a39 UD |
315 | struct cache_node |
316 | { | |
317 | /* | |
318 | * Index into cache is xid, proc, vers, prog and address | |
319 | */ | |
320 | u_long cache_xid; | |
321 | u_long cache_proc; | |
322 | u_long cache_vers; | |
323 | u_long cache_prog; | |
324 | struct sockaddr_in cache_addr; | |
325 | /* | |
326 | * The cached reply and length | |
327 | */ | |
328 | char *cache_reply; | |
329 | u_long cache_replylen; | |
330 | /* | |
331 | * Next node on the list, if there is a collision | |
332 | */ | |
333 | cache_ptr cache_next; | |
334 | }; | |
28f540f4 RM |
335 | |
336 | ||
337 | ||
338 | /* | |
339 | * The entire cache | |
340 | */ | |
e7fd8a39 UD |
341 | struct udp_cache |
342 | { | |
343 | u_long uc_size; /* size of cache */ | |
344 | cache_ptr *uc_entries; /* hash table of entries in cache */ | |
345 | cache_ptr *uc_fifo; /* fifo list of entries in cache */ | |
346 | u_long uc_nextvictim; /* points to next victim in fifo list */ | |
347 | u_long uc_prog; /* saved program number */ | |
348 | u_long uc_vers; /* saved version number */ | |
349 | u_long uc_proc; /* saved procedure number */ | |
350 | struct sockaddr_in uc_addr; /* saved caller's address */ | |
351 | }; | |
28f540f4 RM |
352 | |
353 | ||
354 | /* | |
355 | * the hashing function | |
356 | */ | |
357 | #define CACHE_LOC(transp, xid) \ | |
cbd3dceb | 358 | (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size)) |
28f540f4 RM |
359 | |
360 | ||
361 | /* | |
cbd3dceb | 362 | * Enable use of the cache. |
28f540f4 RM |
363 | * Note: there is no disable. |
364 | */ | |
e7fd8a39 UD |
365 | int |
366 | svcudp_enablecache (SVCXPRT *transp, u_long size) | |
28f540f4 | 367 | { |
e7fd8a39 UD |
368 | struct svcudp_data *su = su_data (transp); |
369 | struct udp_cache *uc; | |
370 | ||
371 | if (su->su_cache != NULL) | |
372 | { | |
373 | CACHE_PERROR (_("enablecache: cache already enabled")); | |
374 | return 0; | |
375 | } | |
376 | uc = ALLOC (struct udp_cache, 1); | |
377 | if (uc == NULL) | |
378 | { | |
379 | CACHE_PERROR (_("enablecache: could not allocate cache")); | |
380 | return 0; | |
381 | } | |
382 | uc->uc_size = size; | |
383 | uc->uc_nextvictim = 0; | |
384 | uc->uc_entries = ALLOC (cache_ptr, size * SPARSENESS); | |
385 | if (uc->uc_entries == NULL) | |
386 | { | |
387 | CACHE_PERROR (_("enablecache: could not allocate cache data")); | |
388 | return 0; | |
389 | } | |
390 | BZERO (uc->uc_entries, cache_ptr, size * SPARSENESS); | |
391 | uc->uc_fifo = ALLOC (cache_ptr, size); | |
392 | if (uc->uc_fifo == NULL) | |
393 | { | |
394 | CACHE_PERROR (_("enablecache: could not allocate cache fifo")); | |
395 | return 0; | |
396 | } | |
397 | BZERO (uc->uc_fifo, cache_ptr, size); | |
398 | su->su_cache = (char *) uc; | |
399 | return 1; | |
28f540f4 RM |
400 | } |
401 | ||
402 | ||
403 | /* | |
404 | * Set an entry in the cache | |
405 | */ | |
e7fd8a39 UD |
406 | static void |
407 | cache_set (SVCXPRT *xprt, u_long replylen) | |
28f540f4 | 408 | { |
e7fd8a39 UD |
409 | cache_ptr victim; |
410 | cache_ptr *vicp; | |
411 | struct svcudp_data *su = su_data (xprt); | |
412 | struct udp_cache *uc = (struct udp_cache *) su->su_cache; | |
413 | u_int loc; | |
414 | char *newbuf; | |
415 | ||
416 | /* | |
417 | * Find space for the new entry, either by | |
418 | * reusing an old entry, or by mallocing a new one | |
419 | */ | |
420 | victim = uc->uc_fifo[uc->uc_nextvictim]; | |
421 | if (victim != NULL) | |
422 | { | |
423 | loc = CACHE_LOC (xprt, victim->cache_xid); | |
424 | for (vicp = &uc->uc_entries[loc]; | |
425 | *vicp != NULL && *vicp != victim; | |
426 | vicp = &(*vicp)->cache_next) | |
427 | ; | |
428 | if (*vicp == NULL) | |
429 | { | |
430 | CACHE_PERROR (_("cache_set: victim not found")); | |
431 | return; | |
28f540f4 | 432 | } |
e7fd8a39 UD |
433 | *vicp = victim->cache_next; /* remote from cache */ |
434 | newbuf = victim->cache_reply; | |
435 | } | |
436 | else | |
437 | { | |
438 | victim = ALLOC (struct cache_node, 1); | |
439 | if (victim == NULL) | |
440 | { | |
441 | CACHE_PERROR (_("cache_set: victim alloc failed")); | |
442 | return; | |
443 | } | |
444 | newbuf = mem_alloc (su->su_iosz); | |
445 | if (newbuf == NULL) | |
446 | { | |
447 | CACHE_PERROR (_("cache_set: could not allocate new rpc_buffer")); | |
448 | return; | |
449 | } | |
450 | } | |
451 | ||
452 | /* | |
453 | * Store it away | |
454 | */ | |
455 | victim->cache_replylen = replylen; | |
456 | victim->cache_reply = rpc_buffer (xprt); | |
457 | rpc_buffer (xprt) = newbuf; | |
458 | xdrmem_create (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz, XDR_ENCODE); | |
459 | victim->cache_xid = su->su_xid; | |
460 | victim->cache_proc = uc->uc_proc; | |
461 | victim->cache_vers = uc->uc_vers; | |
462 | victim->cache_prog = uc->uc_prog; | |
463 | victim->cache_addr = uc->uc_addr; | |
464 | loc = CACHE_LOC (xprt, victim->cache_xid); | |
465 | victim->cache_next = uc->uc_entries[loc]; | |
466 | uc->uc_entries[loc] = victim; | |
467 | uc->uc_fifo[uc->uc_nextvictim++] = victim; | |
468 | uc->uc_nextvictim %= uc->uc_size; | |
28f540f4 RM |
469 | } |
470 | ||
471 | /* | |
472 | * Try to get an entry from the cache | |
473 | * return 1 if found, 0 if not found | |
474 | */ | |
e7fd8a39 UD |
475 | static int |
476 | cache_get (xprt, msg, replyp, replylenp) | |
477 | SVCXPRT *xprt; | |
478 | struct rpc_msg *msg; | |
479 | char **replyp; | |
480 | u_long *replylenp; | |
28f540f4 | 481 | { |
e7fd8a39 UD |
482 | u_int loc; |
483 | cache_ptr ent; | |
484 | struct svcudp_data *su = su_data (xprt); | |
485 | struct udp_cache *uc = (struct udp_cache *) su->su_cache; | |
486 | ||
50304ef0 | 487 | #define EQADDR(a1, a2) (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0) |
e7fd8a39 UD |
488 | |
489 | loc = CACHE_LOC (xprt, su->su_xid); | |
490 | for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) | |
491 | { | |
492 | if (ent->cache_xid == su->su_xid && | |
493 | ent->cache_proc == uc->uc_proc && | |
494 | ent->cache_vers == uc->uc_vers && | |
495 | ent->cache_prog == uc->uc_prog && | |
496 | EQADDR (ent->cache_addr, uc->uc_addr)) | |
497 | { | |
498 | *replyp = ent->cache_reply; | |
499 | *replylenp = ent->cache_replylen; | |
500 | return 1; | |
28f540f4 | 501 | } |
e7fd8a39 UD |
502 | } |
503 | /* | |
504 | * Failed to find entry | |
505 | * Remember a few things so we can do a set later | |
506 | */ | |
507 | uc->uc_proc = msg->rm_call.cb_proc; | |
508 | uc->uc_vers = msg->rm_call.cb_vers; | |
509 | uc->uc_prog = msg->rm_call.cb_prog; | |
c1301d9a | 510 | memcpy (&uc->uc_addr, &xprt->xp_raddr, sizeof (uc->uc_addr)); |
e7fd8a39 | 511 | return 0; |
28f540f4 | 512 | } |