Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/lib/apputils/udppktinfo.c
39563 views
1
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
/*
3
* Copyright 2016 by the Massachusetts Institute of Technology.
4
* All Rights Reserved.
5
*
6
* Export of this software from the United States of America may
7
* require a specific license from the United States Government.
8
* It is the responsibility of any person or organization contemplating
9
* export to obtain such a license before exporting.
10
*
11
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
12
* distribute this software and its documentation for any purpose and
13
* without fee is hereby granted, provided that the above copyright
14
* notice appear in all copies and that both that copyright notice and
15
* this permission notice appear in supporting documentation, and that
16
* the name of M.I.T. not be used in advertising or publicity pertaining
17
* to distribution of the software without specific, written prior
18
* permission. Furthermore if you modify this software you must label
19
* your software as modified software and not distribute it in such a
20
* fashion that it might be confused with the original M.I.T. software.
21
* M.I.T. makes no representations about the suitability of
22
* this software for any purpose. It is provided "as is" without express
23
* or implied warranty.
24
*/
25
26
/* macOS requires this define for IPV6_PKTINFO. */
27
#define __APPLE_USE_RFC_3542
28
29
#include "udppktinfo.h"
30
31
#include <netinet/in.h>
32
#include <sys/socket.h>
33
34
#if defined(IP_PKTINFO) && defined(HAVE_STRUCT_IN_PKTINFO)
35
#define HAVE_IP_PKTINFO
36
#endif
37
38
#if defined(IPV6_PKTINFO) && defined(HAVE_STRUCT_IN6_PKTINFO)
39
#define HAVE_IPV6_PKTINFO
40
#endif
41
42
#if defined(HAVE_IP_PKTINFO) || defined(IP_SENDSRCADDR) || \
43
defined(HAVE_IPV6_PKTINFO)
44
#define HAVE_PKTINFO_SUPPORT
45
#endif
46
47
/* Use RFC 3542 API below, but fall back from IPV6_RECVPKTINFO to IPV6_PKTINFO
48
* for RFC 2292 implementations. */
49
#if !defined(IPV6_RECVPKTINFO) && defined(IPV6_PKTINFO)
50
#define IPV6_RECVPKTINFO IPV6_PKTINFO
51
#endif
52
53
/* Parallel, though not standardized. */
54
#if !defined(IP_RECVPKTINFO) && defined(IP_PKTINFO)
55
#define IP_RECVPKTINFO IP_PKTINFO
56
#endif /* IP_RECVPKTINFO */
57
58
#if defined(CMSG_SPACE) && defined(HAVE_STRUCT_CMSGHDR) && \
59
defined(HAVE_PKTINFO_SUPPORT)
60
union pktinfo {
61
#ifdef HAVE_STRUCT_IN6_PKTINFO
62
struct in6_pktinfo pi6;
63
#endif
64
#ifdef HAVE_STRUCT_IN_PKTINFO
65
struct in_pktinfo pi4;
66
#endif
67
#ifdef IP_RECVDSTADDR
68
struct in_addr iaddr;
69
#endif
70
char c;
71
};
72
#endif /* HAVE_IPV6_PKTINFO && HAVE_STRUCT_CMSGHDR && HAVE_PKTINFO_SUPPORT */
73
74
#ifdef HAVE_IP_PKTINFO
75
76
#define set_ipv4_pktinfo set_ipv4_recvpktinfo
77
static inline krb5_error_code
78
set_ipv4_recvpktinfo(int sock)
79
{
80
int sockopt = 1;
81
return setsockopt(sock, IPPROTO_IP, IP_RECVPKTINFO, &sockopt,
82
sizeof(sockopt));
83
}
84
85
#elif defined(IP_RECVDSTADDR) /* HAVE_IP_PKTINFO */
86
87
#define set_ipv4_pktinfo set_ipv4_recvdstaddr
88
static inline krb5_error_code
89
set_ipv4_recvdstaddr(int sock)
90
{
91
int sockopt = 1;
92
return setsockopt(sock, IPPROTO_IP, IP_RECVDSTADDR, &sockopt,
93
sizeof(sockopt));
94
}
95
96
#else /* HAVE_IP_PKTINFO || IP_RECVDSTADDR */
97
#define set_ipv4_pktinfo(s) EINVAL
98
#endif /* HAVE_IP_PKTINFO || IP_RECVDSTADDR */
99
100
#ifdef HAVE_IPV6_PKTINFO
101
102
#define set_ipv6_pktinfo set_ipv6_recvpktinfo
103
static inline krb5_error_code
104
set_ipv6_recvpktinfo(int sock)
105
{
106
int sockopt = 1;
107
return setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &sockopt,
108
sizeof(sockopt));
109
}
110
111
#else /* HAVE_IPV6_PKTINFO */
112
#define set_ipv6_pktinfo(s) EINVAL
113
#endif /* HAVE_IPV6_PKTINFO */
114
115
/*
116
* Set pktinfo option on a socket. Takes a socket and the socket address family
117
* as arguments.
118
*
119
* Returns 0 on success, EINVAL if pktinfo is not supported for the address
120
* family.
121
*/
122
krb5_error_code
123
set_pktinfo(int sock, int family)
124
{
125
switch (family) {
126
case AF_INET:
127
return set_ipv4_pktinfo(sock);
128
case AF_INET6:
129
return set_ipv6_pktinfo(sock);
130
default:
131
return EINVAL;
132
}
133
}
134
135
#if defined(HAVE_PKTINFO_SUPPORT) && defined(CMSG_SPACE)
136
137
/*
138
* Check if a socket is bound to a wildcard address.
139
* Returns 1 if it is, 0 if it's bound to a specific address, or -1 on error
140
* with errno set to the error.
141
*/
142
static int
143
is_socket_bound_to_wildcard(int sock)
144
{
145
struct sockaddr_storage bound_addr;
146
socklen_t bound_addr_len = sizeof(bound_addr);
147
struct sockaddr *sa = ss2sa(&bound_addr);
148
149
if (getsockname(sock, sa, &bound_addr_len) < 0)
150
return -1;
151
152
if (!sa_is_inet(sa)) {
153
errno = EINVAL;
154
return -1;
155
}
156
157
return sa_is_wildcard(sa);
158
}
159
160
#ifdef HAVE_IP_PKTINFO
161
162
static inline struct in_pktinfo *
163
cmsg2pktinfo(struct cmsghdr *cmsgptr)
164
{
165
return (struct in_pktinfo *)(void *)CMSG_DATA(cmsgptr);
166
}
167
168
#define check_cmsg_v4_pktinfo check_cmsg_ip_pktinfo
169
static int
170
check_cmsg_ip_pktinfo(struct cmsghdr *cmsgptr, struct sockaddr_in *to,
171
aux_addressing_info *auxaddr)
172
{
173
struct in_pktinfo *pktinfo;
174
175
if (cmsgptr->cmsg_level == IPPROTO_IP &&
176
cmsgptr->cmsg_type == IP_PKTINFO) {
177
memset(to, 0, sizeof(*to));
178
pktinfo = cmsg2pktinfo(cmsgptr);
179
to->sin_addr = pktinfo->ipi_addr;
180
to->sin_family = AF_INET;
181
return 1;
182
}
183
return 0;
184
}
185
186
#elif defined(IP_RECVDSTADDR) /* HAVE_IP_PKTINFO */
187
188
static inline struct in_addr *
189
cmsg2sin(struct cmsghdr *cmsgptr)
190
{
191
return (struct in_addr *)(void *)CMSG_DATA(cmsgptr);
192
}
193
194
#define check_cmsg_v4_pktinfo check_cmsg_ip_recvdstaddr
195
static int
196
check_cmsg_ip_recvdstaddr(struct cmsghdr *cmsgptr, struct sockaddr_in *to,
197
aux_addressing_info *auxaddr)
198
{
199
struct in_addr *sin_addr;
200
201
if (cmsgptr->cmsg_level == IPPROTO_IP &&
202
cmsgptr->cmsg_type == IP_RECVDSTADDR) {
203
memset(to, 0, sizeof(*to));
204
sin_addr = cmsg2sin(cmsgptr);
205
to->sin_addr = *sin_addr;
206
to->sin_family = AF_INET;
207
return 1;
208
}
209
return 0;
210
}
211
212
#else /* HAVE_IP_PKTINFO || IP_RECVDSTADDR */
213
#define check_cmsg_v4_pktinfo(c, t, l, a) 0
214
#endif /* HAVE_IP_PKTINFO || IP_RECVDSTADDR */
215
216
#ifdef HAVE_IPV6_PKTINFO
217
218
static inline struct in6_pktinfo *
219
cmsg2pktinfo6(struct cmsghdr *cmsgptr)
220
{
221
return (struct in6_pktinfo *)(void *)CMSG_DATA(cmsgptr);
222
}
223
224
#define check_cmsg_v6_pktinfo check_cmsg_ipv6_pktinfo
225
static int
226
check_cmsg_ipv6_pktinfo(struct cmsghdr *cmsgptr, struct sockaddr_in6 *to,
227
aux_addressing_info *auxaddr)
228
{
229
struct in6_pktinfo *pktinfo;
230
231
if (cmsgptr->cmsg_level == IPPROTO_IPV6 &&
232
cmsgptr->cmsg_type == IPV6_PKTINFO) {
233
memset(to, 0, sizeof(*to));
234
pktinfo = cmsg2pktinfo6(cmsgptr);
235
to->sin6_addr = pktinfo->ipi6_addr;
236
to->sin6_family = AF_INET6;
237
auxaddr->ipv6_ifindex = pktinfo->ipi6_ifindex;
238
return 1;
239
}
240
return 0;
241
}
242
#else /* HAVE_IPV6_PKTINFO */
243
#define check_cmsg_v6_pktinfo(c, t, l, a) 0
244
#endif /* HAVE_IPV6_PKTINFO */
245
246
static int
247
check_cmsg_pktinfo(struct cmsghdr *cmsgptr, struct sockaddr_storage *to,
248
aux_addressing_info *auxaddr)
249
{
250
return check_cmsg_v4_pktinfo(cmsgptr, ss2sin(to), auxaddr) ||
251
check_cmsg_v6_pktinfo(cmsgptr, ss2sin6(to), auxaddr);
252
}
253
254
/*
255
* Receive a message from a socket.
256
*
257
* Arguments:
258
* sock
259
* buf - The buffer to store the message in.
260
* len - buf length
261
* flags
262
* from - Set to the address that sent the message
263
* to - Set to the address that the message was sent to if possible.
264
* May not be set in certain cases such as if pktinfo support is
265
* missing. May be NULL.
266
* auxaddr - Miscellaneous address information.
267
*
268
* Returns 0 on success, otherwise an error code.
269
*/
270
krb5_error_code
271
recv_from_to(int sock, void *buf, size_t len, int flags,
272
struct sockaddr_storage *from, struct sockaddr_storage *to,
273
aux_addressing_info *auxaddr)
274
275
{
276
int r;
277
struct iovec iov;
278
char cmsg[CMSG_SPACE(sizeof(union pktinfo))];
279
struct cmsghdr *cmsgptr;
280
struct msghdr msg;
281
socklen_t fromlen = sizeof(*from);
282
283
/* Don't use pktinfo if the socket isn't bound to a wildcard address. */
284
r = is_socket_bound_to_wildcard(sock);
285
if (r < 0)
286
return errno;
287
288
if (to == NULL || !r)
289
return recvfrom(sock, buf, len, flags, ss2sa(from), &fromlen);
290
291
/* Clobber with something recognizable in case we can't extract the address
292
* but try to use it anyways. */
293
memset(to, 0x40, sizeof(*to));
294
to->ss_family = AF_UNSPEC;
295
296
iov.iov_base = buf;
297
iov.iov_len = len;
298
memset(&msg, 0, sizeof(msg));
299
msg.msg_name = ss2sa(from);
300
msg.msg_namelen = sizeof(*from);
301
msg.msg_iov = &iov;
302
msg.msg_iovlen = 1;
303
msg.msg_control = cmsg;
304
msg.msg_controllen = sizeof(cmsg);
305
306
r = recvmsg(sock, &msg, flags);
307
if (r < 0)
308
return r;
309
310
/*
311
* On Darwin (and presumably all *BSD with KAME stacks), CMSG_FIRSTHDR
312
* doesn't check for a non-zero controllen. RFC 3542 recommends making
313
* this check, even though the (new) spec for CMSG_FIRSTHDR says it's
314
* supposed to do the check.
315
*/
316
if (msg.msg_controllen) {
317
cmsgptr = CMSG_FIRSTHDR(&msg);
318
while (cmsgptr) {
319
if (check_cmsg_pktinfo(cmsgptr, to, auxaddr))
320
return r;
321
cmsgptr = CMSG_NXTHDR(&msg, cmsgptr);
322
}
323
}
324
/* No info about destination addr was available. */
325
return r;
326
}
327
328
#ifdef HAVE_IP_PKTINFO
329
330
#define set_msg_from_ipv4 set_msg_from_ip_pktinfo
331
static krb5_error_code
332
set_msg_from_ip_pktinfo(struct msghdr *msg, struct cmsghdr *cmsgptr,
333
const struct sockaddr_in *from,
334
aux_addressing_info *auxaddr)
335
{
336
struct in_pktinfo *p = cmsg2pktinfo(cmsgptr);
337
338
cmsgptr->cmsg_level = IPPROTO_IP;
339
cmsgptr->cmsg_type = IP_PKTINFO;
340
cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
341
p->ipi_spec_dst = from->sin_addr;
342
343
msg->msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
344
return 0;
345
}
346
347
#elif defined(IP_SENDSRCADDR) /* HAVE_IP_PKTINFO */
348
349
#define set_msg_from_ipv4 set_msg_from_ip_sendsrcaddr
350
static krb5_error_code
351
set_msg_from_ip_sendsrcaddr(struct msghdr *msg, struct cmsghdr *cmsgptr,
352
const struct sockaddr_in *from,
353
aux_addressing_info *auxaddr)
354
{
355
struct in_addr *sin_addr = cmsg2sin(cmsgptr);
356
357
cmsgptr->cmsg_level = IPPROTO_IP;
358
cmsgptr->cmsg_type = IP_SENDSRCADDR;
359
cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
360
msg->msg_controllen = CMSG_SPACE(sizeof(struct in_addr));
361
*sin_addr = from->sin_addr;
362
return 0;
363
}
364
365
#else /* HAVE_IP_PKTINFO || IP_SENDSRCADDR */
366
#define set_msg_from_ipv4(m, c, f, l, a) EINVAL
367
#endif /* HAVE_IP_PKTINFO || IP_SENDSRCADDR */
368
369
#ifdef HAVE_IPV6_PKTINFO
370
371
#define set_msg_from_ipv6 set_msg_from_ipv6_pktinfo
372
static krb5_error_code
373
set_msg_from_ipv6_pktinfo(struct msghdr *msg, struct cmsghdr *cmsgptr,
374
const struct sockaddr_in6 *from,
375
aux_addressing_info *auxaddr)
376
{
377
struct in6_pktinfo *p = cmsg2pktinfo6(cmsgptr);
378
379
cmsgptr->cmsg_level = IPPROTO_IPV6;
380
cmsgptr->cmsg_type = IPV6_PKTINFO;
381
cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
382
383
p->ipi6_addr = from->sin6_addr;
384
/*
385
* Because of the possibility of asymmetric routing, we
386
* normally don't want to specify an interface. However,
387
* macOS doesn't like sending from a link-local address
388
* (which can come up in testing at least, if you wind up
389
* with a "foo.local" name) unless we do specify the
390
* interface.
391
*/
392
if (IN6_IS_ADDR_LINKLOCAL(&from->sin6_addr))
393
p->ipi6_ifindex = auxaddr->ipv6_ifindex;
394
/* otherwise, already zero */
395
396
msg->msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
397
return 0;
398
}
399
400
#else /* HAVE_IPV6_PKTINFO */
401
#define set_msg_from_ipv6(m, c, f, l, a) EINVAL
402
#endif /* HAVE_IPV6_PKTINFO */
403
404
static krb5_error_code
405
set_msg_from(struct msghdr *msg, struct cmsghdr *cmsgptr,
406
const struct sockaddr *from, aux_addressing_info *auxaddr)
407
{
408
switch (from->sa_family) {
409
case AF_INET:
410
return set_msg_from_ipv4(msg, cmsgptr, sa2sin(from), auxaddr);
411
case AF_INET6:
412
return set_msg_from_ipv6(msg, cmsgptr, sa2sin6(from), auxaddr);
413
}
414
415
return EINVAL;
416
}
417
418
/*
419
* Send a message to an address.
420
*
421
* Arguments:
422
* sock
423
* buf - The message to send.
424
* len - buf length
425
* flags
426
* to - The address to send the message to.
427
* from - The address to attempt to send the message from. May be NULL.
428
* auxaddr - Miscellaneous address information.
429
*
430
* Returns 0 on success, otherwise an error code.
431
*/
432
krb5_error_code
433
send_to_from(int sock, void *buf, size_t len, int flags,
434
const struct sockaddr *to, const struct sockaddr *from,
435
aux_addressing_info *auxaddr)
436
{
437
int r;
438
struct iovec iov;
439
struct msghdr msg;
440
struct cmsghdr *cmsgptr;
441
char cbuf[CMSG_SPACE(sizeof(union pktinfo))];
442
443
/* Don't use pktinfo if the socket isn't bound to a wildcard address. */
444
r = is_socket_bound_to_wildcard(sock);
445
if (r < 0)
446
return errno;
447
448
if (from == NULL || from->sa_family != to->sa_family || !r)
449
goto use_sendto;
450
451
iov.iov_base = buf;
452
iov.iov_len = len;
453
/* Truncation? */
454
if (iov.iov_len != len)
455
return EINVAL;
456
memset(cbuf, 0, sizeof(cbuf));
457
memset(&msg, 0, sizeof(msg));
458
msg.msg_name = (void *)to;
459
msg.msg_namelen = sa_socklen(to);
460
msg.msg_iov = &iov;
461
msg.msg_iovlen = 1;
462
msg.msg_control = cbuf;
463
/* CMSG_FIRSTHDR needs a non-zero controllen, or it'll return NULL on
464
* Linux. */
465
msg.msg_controllen = sizeof(cbuf);
466
cmsgptr = CMSG_FIRSTHDR(&msg);
467
msg.msg_controllen = 0;
468
469
if (set_msg_from(&msg, cmsgptr, from, auxaddr))
470
goto use_sendto;
471
return sendmsg(sock, &msg, flags);
472
473
use_sendto:
474
return sendto(sock, buf, len, flags, to, sa_socklen(to));
475
}
476
477
#else /* HAVE_PKTINFO_SUPPORT && CMSG_SPACE */
478
479
krb5_error_code
480
recv_from_to(int sock, void *buf, size_t len, int flags,
481
struct sockaddr_storage *from, struct sockaddr_storage *to,
482
aux_addressing_info *auxaddr)
483
{
484
socklen_t fromlen = sizeof(*from);
485
486
if (to != NULL) {
487
/* Clobber with something recognizable in case we try to use the
488
* address. */
489
memset(to, 0x40, sizeof(*to));
490
to->ss_family = AF_UNSPEC;
491
}
492
493
return recvfrom(sock, buf, len, flags, ss2sa(from), &fromlen);
494
}
495
496
krb5_error_code
497
send_to_from(int sock, void *buf, size_t len, int flags,
498
const struct sockaddr *to, const struct sockaddr *from,
499
aux_addressing_info *auxaddr)
500
{
501
return sendto(sock, buf, len, flags, to, sa_socklen(to));
502
}
503
504
#endif /* HAVE_PKTINFO_SUPPORT && CMSG_SPACE */
505
506