Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/lib/rpc/svc_udp.c
39537 views
1
/* @(#)svc_udp.c 2.2 88/07/29 4.0 RPCSRC */
2
/*
3
* Copyright (c) 2010, Oracle America, Inc.
4
*
5
* All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions are met:
9
*
10
* * Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
*
13
* * Redistributions in binary form must reproduce the above copyright
14
* notice, this list of conditions and the following disclaimer in
15
* the documentation and/or other materials provided with the
16
* distribution.
17
*
18
* * Neither the name of the "Oracle America, Inc." nor the names of
19
* its contributors may be used to endorse or promote products
20
* derived from this software without specific prior written permission.
21
*
22
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
23
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
28
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
*/
34
#if !defined(lint) && defined(SCCSIDS)
35
static char sccsid[] = "@(#)svc_udp.c 1.24 87/08/11 Copyr 1984 Sun Micro";
36
#endif
37
38
/*
39
* svc_udp.c,
40
* Server side for UDP/IP based RPC. (Does some caching in the hopes of
41
* achieving execute-at-most-once semantics.)
42
*/
43
44
#include "k5-platform.h"
45
#include <unistd.h>
46
#include <gssrpc/rpc.h>
47
#include <sys/socket.h>
48
#ifdef HAVE_SYS_UIO_H
49
#include <sys/uio.h>
50
#endif
51
#include <port-sockets.h>
52
#include <socket-utils.h>
53
54
55
#ifndef GETSOCKNAME_ARG3_TYPE
56
#define GETSOCKNAME_ARG3_TYPE int
57
#endif
58
59
#define rpc_buffer(xprt) ((xprt)->xp_p1)
60
#ifndef MAX
61
#define MAX(a, b) ((a > b) ? a : b)
62
#endif
63
64
static bool_t svcudp_recv(SVCXPRT *, struct rpc_msg *);
65
static bool_t svcudp_reply(SVCXPRT *, struct rpc_msg *);
66
static enum xprt_stat svcudp_stat(SVCXPRT *);
67
static bool_t svcudp_getargs(SVCXPRT *, xdrproc_t, void *);
68
static bool_t svcudp_freeargs(SVCXPRT *, xdrproc_t, void *);
69
static void svcudp_destroy(SVCXPRT *);
70
71
static void cache_set(SVCXPRT *, uint32_t);
72
static int cache_get(SVCXPRT *, struct rpc_msg *, char **, uint32_t *);
73
74
static struct xp_ops svcudp_op = {
75
svcudp_recv,
76
svcudp_stat,
77
svcudp_getargs,
78
svcudp_reply,
79
svcudp_freeargs,
80
svcudp_destroy
81
};
82
83
84
/*
85
* kept in xprt->xp_p2
86
*/
87
struct svcudp_data {
88
u_int su_iosz; /* byte size of send.recv buffer */
89
uint32_t su_xid; /* transaction id */
90
XDR su_xdrs; /* XDR handle */
91
char su_verfbody[MAX_AUTH_BYTES]; /* verifier body */
92
void * su_cache; /* cached data, NULL if no cache */
93
};
94
#define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2))
95
96
/*
97
* Usage:
98
* xprt = svcudp_create(sock);
99
*
100
* If sock<0 then a socket is created, else sock is used.
101
* If the socket, sock is not bound to a port then svcudp_create
102
* binds it to an arbitrary port. In any (successful) case,
103
* xprt->xp_sock is the registered socket number and xprt->xp_port is the
104
* associated port number.
105
* Once *xprt is initialized, it is registered as a transporter;
106
* see (svc.h, xprt_register).
107
* The routines returns NULL if a problem occurred.
108
*/
109
SVCXPRT *
110
svcudp_bufcreate(
111
int sock,
112
u_int sendsz,
113
u_int recvsz)
114
{
115
bool_t madesock = FALSE;
116
SVCXPRT *xprt;
117
struct svcudp_data *su;
118
struct sockaddr_storage ss;
119
struct sockaddr *sa = (struct sockaddr *)&ss;
120
socklen_t len;
121
122
if (sock == RPC_ANYSOCK) {
123
if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
124
perror("svcudp_create: socket creation problem");
125
return ((SVCXPRT *)NULL);
126
}
127
set_cloexec_fd(sock);
128
madesock = TRUE;
129
memset(&ss, 0, sizeof(ss));
130
sa->sa_family = AF_INET;
131
} else {
132
len = sizeof(struct sockaddr_storage);
133
if (getsockname(sock, sa, &len) < 0) {
134
perror("svcudp_create - cannot getsockname");
135
return ((SVCXPRT *)NULL);
136
}
137
}
138
139
if (bindresvport_sa(sock, sa)) {
140
sa_setport(sa, 0);
141
(void)bind(sock, sa, sa_socklen(sa));
142
}
143
len = sizeof(struct sockaddr_storage);
144
if (getsockname(sock, sa, &len) != 0) {
145
perror("svcudp_create - cannot getsockname");
146
if (madesock)
147
(void)close(sock);
148
return ((SVCXPRT *)NULL);
149
}
150
xprt = (SVCXPRT *)mem_alloc(sizeof(SVCXPRT));
151
if (xprt == NULL) {
152
(void)fprintf(stderr, "svcudp_create: out of memory\n");
153
return (NULL);
154
}
155
su = (struct svcudp_data *)mem_alloc(sizeof(*su));
156
if (su == NULL) {
157
(void)fprintf(stderr, "svcudp_create: out of memory\n");
158
return (NULL);
159
}
160
su->su_iosz = ((MAX(sendsz, recvsz) + 3) / 4) * 4;
161
if ((rpc_buffer(xprt) = mem_alloc(su->su_iosz)) == NULL) {
162
(void)fprintf(stderr, "svcudp_create: out of memory\n");
163
return (NULL);
164
}
165
xdrmem_create(
166
&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_DECODE);
167
su->su_cache = NULL;
168
xprt->xp_p2 = (caddr_t)su;
169
xprt->xp_auth = NULL;
170
xprt->xp_verf.oa_base = su->su_verfbody;
171
xprt->xp_ops = &svcudp_op;
172
xprt->xp_port = sa_getport(sa);
173
xprt->xp_sock = sock;
174
xprt_register(xprt);
175
return (xprt);
176
}
177
178
SVCXPRT *
179
svcudp_create(int sock)
180
{
181
182
return(svcudp_bufcreate(sock, UDPMSGSIZE, UDPMSGSIZE));
183
}
184
185
static enum xprt_stat
186
svcudp_stat(SVCXPRT *xprt)
187
{
188
189
return (XPRT_IDLE);
190
}
191
192
static bool_t
193
svcudp_recv(
194
SVCXPRT *xprt,
195
struct rpc_msg *msg)
196
{
197
struct msghdr dummy;
198
struct iovec dummy_iov[1];
199
struct svcudp_data *su = su_data(xprt);
200
XDR *xdrs = &su->su_xdrs;
201
int rlen;
202
char *reply;
203
uint32_t replylen;
204
socklen_t addrlen;
205
206
again:
207
memset(&dummy, 0, sizeof(dummy));
208
dummy_iov[0].iov_base = rpc_buffer(xprt);
209
dummy_iov[0].iov_len = (int) su->su_iosz;
210
dummy.msg_iov = dummy_iov;
211
dummy.msg_iovlen = 1;
212
dummy.msg_namelen = xprt->xp_laddrlen = sizeof(struct sockaddr_in);
213
dummy.msg_name = (char *) &xprt->xp_laddr;
214
rlen = recvmsg(xprt->xp_sock, &dummy, MSG_PEEK);
215
if (rlen == -1) {
216
if (errno == EINTR)
217
goto again;
218
else
219
return (FALSE);
220
}
221
222
addrlen = sizeof(struct sockaddr_in);
223
rlen = recvfrom(xprt->xp_sock, rpc_buffer(xprt), (int) su->su_iosz,
224
0, (struct sockaddr *)&(xprt->xp_raddr), &addrlen);
225
if (rlen == -1 && errno == EINTR)
226
goto again;
227
if (rlen < (int) (4*sizeof(uint32_t)))
228
return (FALSE);
229
xprt->xp_addrlen = addrlen;
230
xdrs->x_op = XDR_DECODE;
231
XDR_SETPOS(xdrs, 0);
232
if (! xdr_callmsg(xdrs, msg))
233
return (FALSE);
234
su->su_xid = msg->rm_xid;
235
if (su->su_cache != NULL) {
236
if (cache_get(xprt, msg, &reply, &replylen)) {
237
(void) sendto(xprt->xp_sock, reply, (int) replylen, 0,
238
(struct sockaddr *) &xprt->xp_raddr, xprt->xp_addrlen);
239
return (TRUE);
240
}
241
}
242
return (TRUE);
243
}
244
245
static bool_t svcudp_reply(
246
SVCXPRT *xprt,
247
struct rpc_msg *msg)
248
{
249
struct svcudp_data *su = su_data(xprt);
250
XDR *xdrs = &su->su_xdrs;
251
u_int slen;
252
bool_t stat = FALSE;
253
ssize_t r;
254
255
xdrproc_t xdr_results = NULL;
256
caddr_t xdr_location = 0;
257
bool_t has_args;
258
259
if (msg->rm_reply.rp_stat == MSG_ACCEPTED &&
260
msg->rm_reply.rp_acpt.ar_stat == SUCCESS) {
261
has_args = TRUE;
262
xdr_results = msg->acpted_rply.ar_results.proc;
263
xdr_location = msg->acpted_rply.ar_results.where;
264
265
msg->acpted_rply.ar_results.proc = xdr_void;
266
msg->acpted_rply.ar_results.where = NULL;
267
} else
268
has_args = FALSE;
269
270
xdrs->x_op = XDR_ENCODE;
271
XDR_SETPOS(xdrs, 0);
272
msg->rm_xid = su->su_xid;
273
if (xdr_replymsg(xdrs, msg) &&
274
(!has_args ||
275
(SVCAUTH_WRAP(xprt->xp_auth, xdrs, xdr_results, xdr_location)))) {
276
slen = XDR_GETPOS(xdrs);
277
r = sendto(xprt->xp_sock, rpc_buffer(xprt), slen, 0,
278
(struct sockaddr *)&(xprt->xp_raddr), xprt->xp_addrlen);
279
if (r >= 0 && (u_int)r == slen) {
280
stat = TRUE;
281
if (su->su_cache) {
282
cache_set(xprt, (uint32_t) slen);
283
}
284
}
285
}
286
return (stat);
287
}
288
289
static bool_t
290
svcudp_getargs(
291
SVCXPRT *xprt,
292
xdrproc_t xdr_args,
293
void * args_ptr)
294
{
295
if (! SVCAUTH_UNWRAP(xprt->xp_auth, &(su_data(xprt)->su_xdrs),
296
xdr_args, args_ptr)) {
297
(void)svcudp_freeargs(xprt, xdr_args, args_ptr);
298
return FALSE;
299
}
300
return TRUE;
301
}
302
303
static bool_t
304
svcudp_freeargs(
305
SVCXPRT *xprt,
306
xdrproc_t xdr_args,
307
void * args_ptr)
308
{
309
XDR *xdrs = &su_data(xprt)->su_xdrs;
310
311
xdrs->x_op = XDR_FREE;
312
return ((*xdr_args)(xdrs, args_ptr));
313
}
314
315
static void
316
svcudp_destroy(SVCXPRT *xprt)
317
{
318
struct svcudp_data *su = su_data(xprt);
319
320
xprt_unregister(xprt);
321
if (xprt->xp_sock != INVALID_SOCKET)
322
(void)closesocket(xprt->xp_sock);
323
xprt->xp_sock = INVALID_SOCKET;
324
if (xprt->xp_auth != NULL) {
325
SVCAUTH_DESTROY(xprt->xp_auth);
326
xprt->xp_auth = NULL;
327
}
328
XDR_DESTROY(&(su->su_xdrs));
329
mem_free(rpc_buffer(xprt), su->su_iosz);
330
mem_free((caddr_t)su, sizeof(struct svcudp_data));
331
mem_free((caddr_t)xprt, sizeof(SVCXPRT));
332
}
333
334
335
/***********this could be a separate file*********************/
336
337
/*
338
* Fifo cache for udp server
339
* Copies pointers to reply buffers into fifo cache
340
* Buffers are sent again if retransmissions are detected.
341
*/
342
343
#define SPARSENESS 4 /* 75% sparse */
344
345
#define CACHE_PERROR(msg) \
346
(void) fprintf(stderr,"%s\n", msg)
347
348
#define ALLOC(type, size) \
349
(type *) mem_alloc((unsigned) (sizeof(type) * (size)))
350
351
#define BZERO(addr, type, size) \
352
memset(addr, 0, sizeof(type) * (int) (size))
353
354
/*
355
* An entry in the cache
356
*/
357
typedef struct cache_node *cache_ptr;
358
struct cache_node {
359
/*
360
* Index into cache is xid, proc, vers, prog and address
361
*/
362
uint32_t cache_xid;
363
rpcproc_t cache_proc;
364
rpcvers_t cache_vers;
365
rpcprog_t cache_prog;
366
struct sockaddr_in cache_addr;
367
/*
368
* The cached reply and length
369
*/
370
char * cache_reply;
371
uint32_t cache_replylen;
372
/*
373
* Next node on the list, if there is a collision
374
*/
375
cache_ptr cache_next;
376
};
377
378
379
380
/*
381
* The entire cache
382
*/
383
struct udp_cache {
384
uint32_t uc_size; /* size of cache */
385
cache_ptr *uc_entries; /* hash table of entries in cache */
386
cache_ptr *uc_fifo; /* fifo list of entries in cache */
387
uint32_t uc_nextvictim; /* points to next victim in fifo list */
388
rpcprog_t uc_prog; /* saved program number */
389
rpcvers_t uc_vers; /* saved version number */
390
rpcproc_t uc_proc; /* saved procedure number */
391
struct sockaddr_in uc_addr; /* saved caller's address */
392
};
393
394
395
/*
396
* the hashing function
397
*/
398
#define CACHE_LOC(transp, xid) \
399
(xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
400
401
402
/*
403
* Enable use of the cache.
404
* Note: there is no disable.
405
*/
406
int
407
svcudp_enablecache(
408
SVCXPRT *transp,
409
uint32_t size)
410
{
411
struct svcudp_data *su = su_data(transp);
412
struct udp_cache *uc;
413
414
if (su->su_cache != NULL) {
415
CACHE_PERROR("enablecache: cache already enabled");
416
return(0);
417
}
418
uc = ALLOC(struct udp_cache, 1);
419
if (uc == NULL) {
420
CACHE_PERROR("enablecache: could not allocate cache");
421
return(0);
422
}
423
uc->uc_size = size;
424
uc->uc_nextvictim = 0;
425
uc->uc_entries = ALLOC(cache_ptr, size * SPARSENESS);
426
if (uc->uc_entries == NULL) {
427
CACHE_PERROR("enablecache: could not allocate cache data");
428
return(0);
429
}
430
BZERO(uc->uc_entries, cache_ptr, size * SPARSENESS);
431
uc->uc_fifo = ALLOC(cache_ptr, size);
432
if (uc->uc_fifo == NULL) {
433
CACHE_PERROR("enablecache: could not allocate cache fifo");
434
return(0);
435
}
436
BZERO(uc->uc_fifo, cache_ptr, size);
437
su->su_cache = (char *) uc;
438
return(1);
439
}
440
441
442
/*
443
* Set an entry in the cache
444
*/
445
static void
446
cache_set(
447
SVCXPRT *xprt,
448
uint32_t replylen)
449
{
450
cache_ptr victim;
451
cache_ptr *vicp;
452
struct svcudp_data *su = su_data(xprt);
453
struct udp_cache *uc = (struct udp_cache *) su->su_cache;
454
u_int loc;
455
char *newbuf;
456
457
/*
458
* Find space for the new entry, either by
459
* reusing an old entry, or by mallocing a new one
460
*/
461
victim = uc->uc_fifo[uc->uc_nextvictim];
462
if (victim != NULL) {
463
loc = CACHE_LOC(xprt, victim->cache_xid);
464
for (vicp = &uc->uc_entries[loc];
465
*vicp != NULL && *vicp != victim;
466
vicp = &(*vicp)->cache_next)
467
;
468
if (*vicp == NULL) {
469
CACHE_PERROR("cache_set: victim not found");
470
return;
471
}
472
*vicp = victim->cache_next; /* remote from cache */
473
newbuf = victim->cache_reply;
474
} else {
475
victim = ALLOC(struct cache_node, 1);
476
if (victim == NULL) {
477
CACHE_PERROR("cache_set: victim alloc failed");
478
return;
479
}
480
newbuf = mem_alloc(su->su_iosz);
481
if (newbuf == NULL) {
482
CACHE_PERROR("cache_set: could not allocate new rpc_buffer");
483
free(victim);
484
return;
485
}
486
}
487
488
/*
489
* Store it away
490
*/
491
victim->cache_replylen = replylen;
492
victim->cache_reply = rpc_buffer(xprt);
493
rpc_buffer(xprt) = newbuf;
494
xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_ENCODE);
495
victim->cache_xid = su->su_xid;
496
victim->cache_proc = uc->uc_proc;
497
victim->cache_vers = uc->uc_vers;
498
victim->cache_prog = uc->uc_prog;
499
victim->cache_addr = uc->uc_addr;
500
loc = CACHE_LOC(xprt, victim->cache_xid);
501
victim->cache_next = uc->uc_entries[loc];
502
uc->uc_entries[loc] = victim;
503
uc->uc_fifo[uc->uc_nextvictim++] = victim;
504
uc->uc_nextvictim %= uc->uc_size;
505
}
506
507
/*
508
* Try to get an entry from the cache
509
* return 1 if found, 0 if not found
510
*/
511
static int
512
cache_get(
513
SVCXPRT *xprt,
514
struct rpc_msg *msg,
515
char **replyp,
516
uint32_t *replylenp)
517
{
518
u_int loc;
519
cache_ptr ent;
520
struct svcudp_data *su = su_data(xprt);
521
struct udp_cache *uc = su->su_cache;
522
523
# define EQADDR(a1, a2) (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
524
525
loc = CACHE_LOC(xprt, su->su_xid);
526
for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) {
527
if (ent->cache_xid == su->su_xid &&
528
ent->cache_proc == uc->uc_proc &&
529
ent->cache_vers == uc->uc_vers &&
530
ent->cache_prog == uc->uc_prog &&
531
EQADDR(ent->cache_addr, uc->uc_addr)) {
532
*replyp = ent->cache_reply;
533
*replylenp = ent->cache_replylen;
534
return(1);
535
}
536
}
537
/*
538
* Failed to find entry
539
* Remember a few things so we can do a set later
540
*/
541
uc->uc_proc = msg->rm_call.cb_proc;
542
uc->uc_vers = msg->rm_call.cb_vers;
543
uc->uc_prog = msg->rm_call.cb_prog;
544
uc->uc_addr = xprt->xp_raddr;
545
return(0);
546
}
547
548