1
/* vim:ts=8:sts=4:sw=4:noai:noexpandtab
3
* struct sockaddr functions independent of in or in6.
5
* Copyright (c) 2006-2011 Miru Limited.
7
* This library is free software; you can redistribute it and/or
8
* modify it under the terms of the GNU Lesser General Public
9
* License as published by the Free Software Foundation; either
10
* version 2.1 of the License, or (at your option) any later version.
12
* This library is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
* Lesser General Public License for more details.
17
* You should have received a copy of the GNU Lesser General Public
18
* License along with this library; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24
# include <sys/socket.h>
27
#include <impl/framework.h>
31
#ifndef IPV6_ADD_MEMBERSHIP
32
# define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
33
# define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
35
/* OpenSolaris differences */
36
#if !defined(_WIN32) && !defined(MCAST_MSFILTER)
37
# include <sys/ioctl.h>
40
# define SOL_IP IPPROTO_IP
43
# define SOL_IPV6 IPPROTO_IPV6
45
#ifndef IP_MAX_MEMBERSHIPS
46
# define IP_MAX_MEMBERSHIPS 20
52
const struct sockaddr* sa
61
const struct sockaddr* sa
65
switch (sa->sa_family) {
67
struct sockaddr_in s4;
68
memcpy (&s4, sa, sizeof(s4));
69
sa_port = s4.sin_port;
74
struct sockaddr_in6 s6;
75
memcpy (&s6, sa, sizeof(s6));
76
sa_port = s6.sin6_port;
90
const struct sockaddr* sa
94
switch (sa->sa_family) {
95
case AF_INET: sa_len = sizeof(struct sockaddr_in); break;
96
case AF_INET6: sa_len = sizeof(struct sockaddr_in6); break;
97
default: sa_len = 0; break;
104
pgm_sockaddr_storage_len (
105
const struct sockaddr_storage* ss
109
switch (ss->ss_family) {
110
case AF_INET: ss_len = sizeof(struct sockaddr_in); break;
111
case AF_INET6: ss_len = sizeof(struct sockaddr_in6); break;
112
default: ss_len = 0; break;
119
pgm_sockaddr_scope_id (
120
const struct sockaddr* sa
124
if (AF_INET6 == sa->sa_family) {
125
struct sockaddr_in6 s6;
126
memcpy (&s6, sa, sizeof(s6));
127
scope_id = s6.sin6_scope_id;
136
const struct sockaddr* restrict sa,
141
return getnameinfo (sa, pgm_sockaddr_len (sa),
150
const char* restrict src,
151
struct sockaddr* restrict dst /* will error on wrong size */
154
struct addrinfo hints = {
155
.ai_family = AF_UNSPEC,
156
.ai_socktype = SOCK_STREAM, /* not really */
157
.ai_protocol = IPPROTO_TCP, /* not really */
158
.ai_flags = AI_NUMERICHOST
160
const int status = getaddrinfo (src, NULL, &hints, &result);
161
if (PGM_LIKELY(0 == status)) {
162
memcpy (dst, result->ai_addr, result->ai_addrlen);
163
freeaddrinfo (result);
169
/* returns tri-state value: 1 if sa is multicast, 0 if sa is not multicast, -1 on error
174
pgm_sockaddr_is_addr_multicast (
175
const struct sockaddr* sa
180
switch (sa->sa_family) {
182
struct sockaddr_in s4;
183
memcpy (&s4, sa, sizeof(s4));
184
retval = IN_MULTICAST(ntohl( s4.sin_addr.s_addr ));
189
struct sockaddr_in6 s6;
190
memcpy (&s6, sa, sizeof(s6));
191
retval = IN6_IS_ADDR_MULTICAST( &s6.sin6_addr );
202
/* returns 1 if sa is unspecified, 0 if specified.
207
pgm_sockaddr_is_addr_unspecified (
208
const struct sockaddr* sa
213
switch (sa->sa_family) {
215
struct sockaddr_in s4;
216
memcpy (&s4, sa, sizeof(s4));
217
retval = (INADDR_ANY == s4.sin_addr.s_addr);
222
struct sockaddr_in6 s6;
223
memcpy (&s6, sa, sizeof(s6));
224
retval = IN6_IS_ADDR_UNSPECIFIED( &s6.sin6_addr );
238
const struct sockaddr* restrict sa1,
239
const struct sockaddr* restrict sa2
244
if (sa1->sa_family != sa2->sa_family)
245
retval = sa1->sa_family < sa2->sa_family ? -1 : 1;
247
switch (sa1->sa_family) {
249
struct sockaddr_in sa1_in, sa2_in;
250
memcpy (&sa1_in, sa1, sizeof(sa1_in));
251
memcpy (&sa2_in, sa2, sizeof(sa2_in));
252
if (sa1_in.sin_addr.s_addr != sa2_in.sin_addr.s_addr)
253
retval = sa1_in.sin_addr.s_addr < sa2_in.sin_addr.s_addr ? -1 : 1;
257
/* IN6_ARE_ADDR_EQUAL(a,b) only returns true or false */
259
struct sockaddr_in6 sa1_in6, sa2_in6;
260
memcpy (&sa1_in6, sa1, sizeof(sa1_in6));
261
memcpy (&sa2_in6, sa2, sizeof(sa2_in6));
262
retval = memcmp (&sa1_in6.sin6_addr, &sa2_in6.sin6_addr, sizeof(struct in6_addr));
263
if (0 == retval && sa1_in6.sin6_scope_id != sa2_in6.sin6_scope_id)
264
retval = sa1_in6.sin6_scope_id < sa2_in6.sin6_scope_id ? -1 : 1;
275
/* IP header included with data.
277
* If no error occurs, pgm_sockaddr_hdrincl returns zero. Otherwise, a value
278
* of SOCKET_ERROR is returned, and a specific error code can be retrieved
279
* by calling pgm_get_last_sock_error().
284
pgm_sockaddr_hdrincl (
286
const sa_family_t sa_family,
290
int retval = SOCKET_ERROR;
295
/* Solaris:ip(7P) Mentioned but not detailed.
297
* Linux:ip(7) "A boolean integer flag is zero when it is false, otherwise
298
* true. If enabled, the user supplies an IP header in front of the user
299
* data." Mentions only send-side, nothing about receive-side.
300
* Linux:raw(7) "For receiving the IP header is always included in the packet."
302
* FreeBSD,OS X:IP(4) provided by example "int hincl = 1;"
304
* Stevens: "IP_HDRINCL has datatype int."
306
const int optval = v ? 1 : 0;
308
const DWORD optval = v ? 1 : 0;
310
retval = setsockopt (s, IPPROTO_IP, IP_HDRINCL, (const char*)&optval, sizeof(optval));
314
case AF_INET6: /* method only exists on Win32, just ignore */
323
/* Return destination IP address.
325
* If no error occurs, pgm_sockaddr_pktinfo returns zero. Otherwise, a value
326
* of SOCKET_ERROR is returned, and a specific error code can be retrieved
327
* by calling pgm_get_last_sock_error().
332
pgm_sockaddr_pktinfo (
334
const sa_family_t sa_family,
338
int retval = SOCKET_ERROR;
340
/* Solaris:ip(7P) "The following options take in_pktinfo_t as the parameter"
341
* Completely different, although ip6(7P) is a little better, "The following
342
* options are boolean switches controlling the reception of ancillary data"
344
* Linux:ip(7) "A boolean integer flag is zero when it is false, otherwise
345
* true. The argument is a flag that tells the socket whether the IP_PKTINFO
346
* message should be passed or not."
347
* Linux:ipv6(7) Not listed, however IPV6_PKTINFO is with "Argument is a pointer
348
* to a boolean value in an integer."
350
* Absent from FreeBSD & OS X, suggested replacement IP_RECVDSTADDR.
351
* OS X:IP6(4) "IPV6_PKTINFO int *"
353
* Stevens: "IP_RECVDSTADDR has datatype int."
355
const int optval = v ? 1 : 0;
357
const DWORD optval = v ? 1 : 0;
362
#ifdef IP_RECVDSTADDR
363
retval = setsockopt (s, IPPROTO_IP, IP_RECVDSTADDR, (const char*)&optval, sizeof(optval));
365
retval = setsockopt (s, IPPROTO_IP, IP_PKTINFO, (const char*)&optval, sizeof(optval));
370
#ifdef IPV6_RECVPKTINFO
371
retval = setsockopt (s, IPPROTO_IPV6, IPV6_RECVPKTINFO, (const char*)&optval, sizeof(optval));
373
retval = setsockopt (s, IPPROTO_IPV6, IPV6_PKTINFO, (const char*)&optval, sizeof(optval));
382
/* Set IP Router Alert option for all outgoing packets.
384
* If no error occurs, pgm_sockaddr_router_alert returns zero. Otherwise, a
385
* value of SOCKET_ERROR is returned, and a specific error code can be
386
* retrieved by calling pgm_get_last_sock_error().
391
pgm_sockaddr_router_alert (
393
const sa_family_t sa_family,
397
int retval = SOCKET_ERROR;
398
#ifdef CONFIG_IP_ROUTER_ALERT
399
/* Linux:ip(7) "A boolean integer flag is zero when it is false, otherwise
400
* true. Expects an integer flag."
401
* Linux:ipv6(7) "Argument is a pointer to an integer."
403
* Sent on special queue to rsvpd on Linux and so best avoided.
405
const int optval = v ? 1 : 0;
409
retval = setsockopt (s, IPPROTO_IP, IP_ROUTER_ALERT, (const char*)&optval, sizeof(optval));
413
retval = setsockopt (s, IPPROTO_IPV6, IPV6_ROUTER_ALERT, (const char*)&optval, sizeof(optval));
419
# if defined(CONFIG_HAVE_IPOPTION)
420
/* NB: struct ipoption is not very portable and requires a lot of additional headers */
421
const struct ipoption router_alert = {
423
.ipopt_list = { PGM_IPOPT_RA, 0x04, 0x00, 0x00 }
425
const int optlen = v ? sizeof(router_alert) : 0;
427
/* manually set the IP option */
429
const int ipopt_ra = (PGM_IPOPT_RA << 24) | (0x04 << 16);
430
const int router_alert = htonl (ipopt_ra);
432
const DWORD ipopt_ra = (PGM_IPOPT_RA << 24) | (0x04 << 16);
433
const DWORD router_alert = htonl (ipopt_ra);
435
const int optlen = v ? sizeof(router_alert) : 0;
440
/* Linux:ip(7) "The maximum option size for IPv4 is 40 bytes."
442
retval = setsockopt (s, IPPROTO_IP, IP_OPTIONS, (const char*)&router_alert, optlen);
451
/* Type-of-service and precedence.
453
* If no error occurs, pgm_sockaddr_tos returns zero. Otherwise, a value of
454
* SOCKET_ERROR is returned, and a specific error code can be retrieved by
455
* calling pgm_get_last_sock_error().
462
const sa_family_t sa_family,
466
int retval = SOCKET_ERROR;
471
/* Solaris:ip(7P) "This option takes an integer argument as its input value."
473
* Linux:ip(7) "TOS is a byte."
475
* FreeBSD,OS X:IP(4) provided by example "int tos = IPTOS_LOWDELAY;"
477
* Stevens: "IP_TOS has datatype int."
479
const int optval = tos;
481
/* IP_TOS only works on Win32 with system override:
482
* http://support.microsoft.com/kb/248611
483
* TODO: Implement GQoS (IPv4 only), qWAVE QOS is Vista+ only
485
const DWORD optval = tos;
487
retval = setsockopt (s, IPPROTO_IP, IP_TOS, (const char*)&optval, sizeof(optval));
491
case AF_INET6: /* TRAFFIC_CLASS not implemented */
499
/* Join multicast group.
500
* NB: IPV6_JOIN_GROUP == IPV6_ADD_MEMBERSHIP
502
* If no error occurs, pgm_sockaddr_join_group returns zero. Otherwise, a
503
* value of SOCKET_ERROR is returned, and a specific error code can be
504
* retrieved by calling pgm_get_last_sock_error().
509
pgm_sockaddr_join_group (
511
const sa_family_t sa_family,
512
const struct group_req* gr
515
int retval = SOCKET_ERROR;
516
#ifdef CONFIG_HAVE_MCAST_JOIN
517
/* Solaris:ip(7P) "The following options take a struct ip_mreq_source as the
518
* parameter." Presumably with source field zeroed out.
519
* Solaris:ip6(7P) "Takes a struct group_req as the parameter."
520
* Different type for each family, however group_req is protocol-independent.
522
* Stevens: "MCAST_JOIN_GROUP has datatype group_req{}."
524
* RFC3678: Argument type struct group_req
526
const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6;
527
retval = setsockopt (s, recv_level, MCAST_JOIN_GROUP, gr, sizeof(struct group_req));
531
/* Solaris:ip(7P) Just mentions "Join a multicast group."
532
* No further details provided.
534
* Linux:ip(7) "Argument is an ip_mreqn structure. For compatibility, the old
535
* ip_mreq structure (present since Linux 1.2) is still supported."
537
* FreeBSD,OS X:IP(4) provided by example "struct ip_mreq mreq;"
539
* Windows can optionally abuse imt_interface to be 0.0.0.<imr_ifindex>
541
* Stevens: "IP_ADD_MEMBERSHIP has datatype ip_mreq{}."
543
* RFC3678: Argument type struct ip_mreq
545
#ifdef CONFIG_HAVE_IP_MREQN
546
struct ip_mreqn mreqn;
547
struct sockaddr_in ifaddr;
548
memset (&mreqn, 0, sizeof(mreqn));
549
mreqn.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gr->gr_group)->sin_addr.s_addr;
550
if (!pgm_if_indextoaddr (gr->gr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL))
552
mreqn.imr_address.s_addr = ifaddr.sin_addr.s_addr;
553
mreqn.imr_ifindex = gr->gr_interface;
554
retval = setsockopt (s, SOL_IP, IP_ADD_MEMBERSHIP, (const char*)&mreqn, sizeof(mreqn));
557
struct sockaddr_in ifaddr;
558
memset (&mreq, 0, sizeof(mreq));
559
mreq.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gr->gr_group)->sin_addr.s_addr;
560
if (!pgm_if_indextoaddr (gr->gr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL))
562
mreq.imr_interface.s_addr = ifaddr.sin_addr.s_addr;
563
retval = setsockopt (s, SOL_IP, IP_ADD_MEMBERSHIP, (const char*)&mreq, sizeof(mreq));
564
#endif /* !CONFIG_HAVE_IP_MREQN */
569
/* Solaris:ip6(7P) "Takes a struct ipv6_mreq as the parameter;"
571
* Linux:ipv6(7) "Argument is a pointer to a struct ipv6_mreq structure."
573
* OS X:IP6(4) "IPV6_JOIN_GROUP struct ipv6_mreq *"
575
* Stevens: "IPV6_JOIN_GROUP has datatype ipv6_mreq{}."
577
struct ipv6_mreq mreq6;
578
memset (&mreq6, 0, sizeof(mreq6));
579
mreq6.ipv6mr_multiaddr = ((const struct sockaddr_in6*)&gr->gr_group)->sin6_addr;
580
mreq6.ipv6mr_interface = gr->gr_interface;
581
retval = setsockopt (s, SOL_IPV6, IPV6_ADD_MEMBERSHIP, (const char*)&mreq6, sizeof(mreq6));
587
#endif /* CONFIG_HAVE_MCAST_JOIN */
591
/* leave a joined group
596
pgm_sockaddr_leave_group (
598
const sa_family_t sa_family,
599
const struct group_req* gr
602
int retval = SOCKET_ERROR;
603
#ifdef CONFIG_HAVE_MCAST_JOIN
604
const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6;
605
retval = setsockopt (s, recv_level, MCAST_LEAVE_GROUP, gr, sizeof(struct group_req));
609
#ifdef CONFIG_HAVE_IP_MREQN
610
struct ip_mreqn mreqn;
611
struct sockaddr_in ifaddr;
612
memset (&mreqn, 0, sizeof(mreqn));
613
mreqn.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gr->gr_group)->sin_addr.s_addr;
614
if (!pgm_if_indextoaddr (gr->gr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL))
616
mreqn.imr_address.s_addr = ifaddr.sin_addr.s_addr;
617
mreqn.imr_ifindex = gr->gr_interface;
618
retval = setsockopt (s, SOL_IP, IP_DROP_MEMBERSHIP, (const char*)&mreqn, sizeof(mreqn));
621
struct sockaddr_in ifaddr;
622
memset (&mreq, 0, sizeof(mreq));
623
mreq.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gr->gr_group)->sin_addr.s_addr;
624
if (!pgm_if_indextoaddr (gr->gr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL))
626
mreq.imr_interface.s_addr = ifaddr.sin_addr.s_addr;
627
retval = setsockopt (s, SOL_IP, IP_DROP_MEMBERSHIP, (const char*)&mreq, sizeof(mreq));
628
#endif /* !CONFIG_HAVE_IP_MREQN */
633
struct ipv6_mreq mreq6;
634
memset (&mreq6, 0, sizeof(mreq6));
635
mreq6.ipv6mr_multiaddr = ((const struct sockaddr_in6*)&gr->gr_group)->sin6_addr;
636
mreq6.ipv6mr_interface = gr->gr_interface;
637
retval = setsockopt (s, SOL_IPV6, IPV6_DROP_MEMBERSHIP, (const char*)&mreq6, sizeof(mreq6));
643
#endif /* CONFIG_HAVE_MCAST_JOIN */
647
/* block either at the NIC or kernel, packets from a particular source
652
pgm_sockaddr_block_source (
654
const sa_family_t sa_family,
655
const struct group_source_req* gsr
658
int retval = SOCKET_ERROR;
659
#ifdef CONFIG_HAVE_MCAST_JOIN
660
const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6;
661
retval = setsockopt (s, recv_level, MCAST_BLOCK_SOURCE, gsr, sizeof(struct group_source_req));
662
#elif defined(IP_BLOCK_SOURCE)
665
struct ip_mreq_source mreqs;
666
struct sockaddr_in ifaddr;
667
memset (&mreqs, 0, sizeof(mreqs));
668
mreqs.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_group)->sin_addr.s_addr;
669
mreqs.imr_sourceaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_source)->sin_addr.s_addr;
670
pgm_if_indextoaddr (gsr->gsr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL);
671
mreqs.imr_interface.s_addr = ifaddr.sin_addr.s_addr;
672
retval = setsockopt (s, SOL_IP, IP_BLOCK_SOURCE, (const char*)&mreqs, sizeof(mreqs));
677
/* No IPv6 API implemented, MCAST_BLOCK_SOURCE should be available instead.
684
/* unused parameters */
688
#endif /* CONFIG_HAVE_MCAST_JOIN */
692
/* unblock a blocked multicast source.
697
pgm_sockaddr_unblock_source (
699
const sa_family_t sa_family,
700
const struct group_source_req* gsr
703
int retval = SOCKET_ERROR;
704
#ifdef CONFIG_HAVE_MCAST_JOIN
705
const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6;
706
retval = setsockopt (s, recv_level, MCAST_UNBLOCK_SOURCE, gsr, sizeof(struct group_source_req));
707
#elif defined(IP_UNBLOCK_SOURCE)
710
struct ip_mreq_source mreqs;
711
struct sockaddr_in ifaddr;
712
memset (&mreqs, 0, sizeof(mreqs));
713
mreqs.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_group)->sin_addr.s_addr;
714
mreqs.imr_sourceaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_source)->sin_addr.s_addr;
715
pgm_if_indextoaddr (gsr->gsr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL);
716
mreqs.imr_interface.s_addr = ifaddr.sin_addr.s_addr;
717
retval = setsockopt (s, SOL_IP, IP_UNBLOCK_SOURCE, (const char*)&mreqs, sizeof(mreqs));
722
/* No IPv6 API implemented, MCAST_UNBLOCK_SOURCE should be available instead.
729
/* unused parameters */
733
#endif /* CONFIG_HAVE_MCAST_JOIN */
737
/* Join source-specific multicast.
738
* NB: Silently reverts to ASM if SSM not supported.
740
* If no error occurs, pgm_sockaddr_join_source_group returns zero.
741
* Otherwise, a value of SOCKET_ERROR is returned, and a specific error
742
* code can be retrieved by calling pgm_get_last_sock_error().
747
pgm_sockaddr_join_source_group (
749
const sa_family_t sa_family,
750
const struct group_source_req* gsr
753
int retval = SOCKET_ERROR;
754
#ifdef CONFIG_HAVE_MCAST_JOIN
755
/* Solaris:ip(7P) "The following options take a struct ip_mreq_source as the
757
* Solaris:ip6(7P) "Takes a struct group_source_req as the parameter."
758
* Different type for each family, however group_source_req is protocol-
761
* Stevens: "MCAST_JOIN_SOURCE_GROUP has datatype group_source_req{}."
763
* RFC3678: Argument type struct group_source_req
765
const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6;
766
retval = setsockopt (s, recv_level, MCAST_JOIN_SOURCE_GROUP, gsr, sizeof(struct group_source_req));
767
#elif defined(IP_ADD_SOURCE_MEMBERSHIP)
770
/* Solaris:ip(7P) "The following options take a struct ip_mreq as the
771
* parameter." Incorrect literature wrt RFC.
773
* Linux:ip(7) absent.
777
* Stevens: "IP_ADD_SOURCE_MEMBERSHIP has datatype ip_mreq_source{}."
779
* RFC3678: Argument type struct ip_mreq_source
781
struct ip_mreq_source mreqs;
782
struct sockaddr_in ifaddr;
783
memset (&mreqs, 0, sizeof(mreqs));
784
mreqs.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_group)->sin_addr.s_addr;
785
mreqs.imr_sourceaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_source)->sin_addr.s_addr;
786
pgm_if_indextoaddr (gsr->gsr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL);
787
mreqs.imr_interface.s_addr = ifaddr.sin_addr.s_addr;
788
retval = setsockopt (s, SOL_IP, IP_ADD_SOURCE_MEMBERSHIP, (const char*)&mreqs, sizeof(mreqs));
793
/* No IPv6 API implemented, MCAST_JOIN_SOURCE_GROUP should be available instead.
795
retval = pgm_sockaddr_join_group (s, sa_family, (const struct group_req*)gsr);
801
retval = pgm_sockaddr_join_group (s, sa_family, (const struct group_req*)gsr);
802
#endif /* CONFIG_HAVE_MCAST_JOIN */
811
pgm_sockaddr_leave_source_group (
813
const sa_family_t sa_family,
814
const struct group_source_req* gsr
817
int retval = SOCKET_ERROR;
818
#ifdef CONFIG_HAVE_MCAST_JOIN
819
const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6;
820
retval = setsockopt (s, recv_level, MCAST_LEAVE_SOURCE_GROUP, gsr, sizeof(struct group_source_req));
821
#elif defined(IP_ADD_SOURCE_MEMBERSHIP)
824
struct ip_mreq_source mreqs;
825
struct sockaddr_in ifaddr;
826
memset (&mreqs, 0, sizeof(mreqs));
827
mreqs.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_group)->sin_addr.s_addr;
828
mreqs.imr_sourceaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_source)->sin_addr.s_addr;
829
pgm_if_indextoaddr (gsr->gsr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL);
830
mreqs.imr_interface.s_addr = ifaddr.sin_addr.s_addr;
831
retval = setsockopt (s, SOL_IP, IP_DROP_SOURCE_MEMBERSHIP, (const char*)&mreqs, sizeof(mreqs));
836
/* No IPv6 API implemented, MCAST_LEAVE_SOURCE_GROUP should be available instead.
838
retval = pgm_sockaddr_leave_group (s, sa_family, (const struct group_req*)gsr);
844
retval = pgm_sockaddr_leave_group (s, sa_family, (const struct group_req*)gsr);
845
#endif /* CONFIG_HAVE_MCAST_JOIN */
849
#if defined(MCAST_MSFILTER) || defined(SIOCSMSFILTER)
850
/* Batch block and unblock sources.
855
pgm_sockaddr_msfilter (
857
const sa_family_t sa_family,
858
const struct group_filter* gf_list
861
int retval = SOCKET_ERROR;
862
# ifdef MCAST_MSFILTER
863
const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6;
864
const socklen_t len = GROUP_FILTER_SIZE(gf_list->gf_numsrc);
865
retval = setsockopt (s, recv_level, MCAST_MSFILTER, (const char*)gf_list, len);
866
# elif defined(SIOCSMSFILTER)
867
/* Windows Vista and later */
868
const socklen_t len = GROUP_FILTER_SIZE(gf_list->gf_numsrc);
869
u_long* filter = pgm_alloca (len);
870
memcpy (filter, gf_list, len);
871
retval = ioctlsocket (s, SIOCSMSFILTER, filter);
872
# elif defined(IP_MSFILTER) || defined(SIO_SET_MULTICAST_FILTER)
873
/* IPv4-only filter API */
874
if (AF_INET == sa_family) {
875
const socklen_t len = IP_MSFILTER_SIZE(gf_list->gr_numsrc);
876
struct ip_msfilter* filter = pgm_alloca (len);
877
struct sockaddr_in sa4;
879
memcpy (&sa4, &gf_list->gf_group, sizeof (sa4));
880
filter->imsf_multiaddr.s_addr = sa4.sin_addr.s_addr;
881
pgm_if_indextoaddr (gf_list->gf_interface, AF_INET, 0, (struct sockaddr*)&sa4, NULL);
882
filter->imsf_interface.s_addr = sa4.sin_addr.s_addr;
883
filter->imsf_fmode = gf_list->gf_fmode;
884
filter->imsf_numsrc = gf_list->gf_numsrc;
885
for (i = 0; i < gf_list->gf_numsrc; i++) {
886
memcpy (&sa4, &gf_list->gf_slist[i], sizeof (sa4));
887
filter->imsf_slist[i].s_addr = sa4.sin_addr.s_addr;
890
retval = ioctlsocket (s, IP_MSFILTER, (char*)filter);
892
retval = ioctlsocket (s, SIO_SET_MULTICAST_FILTER, (u_long*)filter);
898
#endif /* MCAST_MSFILTER || SIOCSMSFILTER */
900
/* Specify outgoing interface.
902
* If no error occurs, pgm_sockaddr_multicast_if returns zero. Otherwise, a
903
* value of SOCKET_ERROR is returned, and a specific error code can be
904
* retrieved by calling pgm_get_last_sock_error().
909
pgm_sockaddr_multicast_if (
911
const struct sockaddr* address,
912
const unsigned ifindex
915
int retval = SOCKET_ERROR;
917
switch (address->sa_family) {
919
/* Solaris:ip(7P) "This option takes a struct in_addr as an argument, and it
920
* selects that interface for outgoing IP multicast packets."
922
* Linux:ip(7) "Argument is an ip_mreqn or ip_mreq structure similar to
923
* IP_ADD_MEMBERSHIP."
925
* OS X:IP(4) provided by example "struct in_addr addr;"
927
* Stevens: "IP_MULTICAST_IF has datatype struct in_addr{}."
929
struct sockaddr_in s4;
930
memcpy (&s4, address, sizeof(struct sockaddr_in));
931
retval = setsockopt (s, IPPROTO_IP, IP_MULTICAST_IF, (const char*)&s4.sin_addr, sizeof(s4.sin_addr));
937
/* Solaris:ip6(7P) "This option takes an integer as an argument; the integer
938
* is the interface index of the selected interface."
940
* Linux:ipv6(7) "The argument is a pointer to an interface index (see
941
* netdevice(7)) in an integer."
943
* OS X:IP6(4) "IPV6_MULTICAST_IF u_int *"
945
* Stevens: "IPV6_MULTICAST_IF has datatype u_int."
947
const unsigned int optval = ifindex;
949
const DWORD optval = ifindex;
951
retval = setsockopt (s, IPPROTO_IPV6, IPV6_MULTICAST_IF, (const char*)&optval, sizeof(optval));
960
/* Specify multicast loop, other applications on the same host may receive
961
* outgoing packets. This does not affect unicast packets such as NAKs.
963
* If no error occurs, pgm_sockaddr_multicast_loop returns zero. Otherwise, a
964
* value of SOCKET_ERROR is returned, and a specific error code can be
965
* retrieved by calling pgm_get_last_sock_error().
970
pgm_sockaddr_multicast_loop (
972
const sa_family_t sa_family,
976
int retval = SOCKET_ERROR;
981
/* Solaris:ip(7P) "Setting the unsigned character argument to 0 causes the
982
* opposite behavior, meaning that when multiple zones are present, the
983
* datagrams are delivered to all zones except the sending zone."
985
* Linux:ip(7) "Sets or reads a boolean integer argument"
987
* OS X:IP(4) provided by example "u_char loop;"
989
* Stevens: "IP_MULTICAST_LOOP has datatype u_char."
991
const unsigned char optval = v ? 1 : 0;
993
const DWORD optval = v ? 1 : 0;
995
retval = setsockopt (s, IPPROTO_IP, IP_MULTICAST_LOOP, (const char*)&optval, sizeof(optval));
1001
/* Solaris:ip(7P) "Setting the unsigned character argument to 0 will cause the opposite behavior."
1003
* Linux:ipv6(7) "Argument is a pointer to boolean."
1005
* OS X:IP6(7) "IPV6_MULTICAST_LOOP u_int *"
1007
* Stevens: "IPV6_MULTICAST_LOOP has datatype u_int."
1009
const unsigned int optval = v ? 1 : 0;
1011
const DWORD optval = v ? 1 : 0;
1013
retval = setsockopt (s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (const char*)&optval, sizeof(optval));
1022
/* Specify TTL or outgoing hop limit.
1023
* NB: Only affects multicast hops, unicast hop-limit is not changed.
1025
* If no error occurs, pgm_sockaddr_multicast_hops returns zero. Otherwise, a
1026
* value of SOCKET_ERROR is returned, and a specific error code can be
1027
* retrieved by calling pgm_get_last_sock_error().
1032
pgm_sockaddr_multicast_hops (
1034
const sa_family_t sa_family,
1038
int retval = SOCKET_ERROR;
1040
switch (sa_family) {
1043
/* Solaris:ip(7P) "This option takes an unsigned character as an argument."
1045
* Linux:ip(7) "Argument is an integer."
1047
* OS X:IP(4) provided by example for SOCK_DGRAM with IP_TTL: "int ttl = 60;",
1048
* or for SOCK_RAW & SOCK_DGRAM with IP_MULTICAST_TTL: "u_char ttl;"
1050
* Stevens: "IP_MULTICAST_TTL has datatype u_char."
1052
const unsigned char optval = hops;
1054
const DWORD optval = hops;
1056
retval = setsockopt (s, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&optval, sizeof(optval));
1062
/* Solaris:ip6(7P) "This option takes an integer as an argument."
1064
* Linux:ipv6(7) "Argument is a pointer to an integer."
1066
* OS X:IP6(7) "IPV6_MULTICAST_HOPS int *"
1068
* Stevens: "IPV6_MULTICAST_HOPS has datatype int."
1070
const int optval = hops;
1072
const DWORD optval = hops;
1074
retval = setsockopt (s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char*)&optval, sizeof(optval));
1085
pgm_sockaddr_nonblocking (
1091
int flags = fcntl (s, F_GETFL);
1092
if (!v) flags &= ~O_NONBLOCK;
1093
else flags |= O_NONBLOCK;
1094
fcntl (s, F_SETFL, flags);
1097
ioctlsocket (s, FIONBIO, &mode);
1101
/* Note that are sockaddr structure is not passed these functions inherently
1102
* cannot support IPv6 Zone Indices and hence are rather limited for the
1110
const void* restrict src,
1115
pgm_assert (AF_INET == af || AF_INET6 == af);
1116
pgm_assert (NULL != src);
1117
pgm_assert (NULL != dst);
1118
pgm_assert (size > 0);
1123
struct sockaddr_in sin;
1124
memset (&sin, 0, sizeof(sin));
1125
sin.sin_family = AF_INET;
1126
sin.sin_addr = *(const struct in_addr*)src;
1127
getnameinfo ((struct sockaddr*)&sin, sizeof(sin),
1135
struct sockaddr_in6 sin6;
1136
memset (&sin6, 0, sizeof(sin6));
1137
sin6.sin6_family = AF_INET6;
1138
sin6.sin6_addr = *(const struct in6_addr*)src;
1139
getnameinfo ((struct sockaddr*)&sin6, sizeof(sin6),
1148
errno = EAFNOSUPPORT;
1150
WSASetLastError (WSAEAFNOSUPPORT);
1159
const char* restrict src,
1163
pgm_assert (AF_INET == af || AF_INET6 == af);
1164
pgm_assert (NULL != src);
1165
pgm_assert (NULL != dst);
1167
struct addrinfo hints = {
1169
.ai_socktype = SOCK_STREAM, /* not really */
1170
.ai_protocol = IPPROTO_TCP, /* not really */
1171
.ai_flags = AI_NUMERICHOST
1174
const int e = getaddrinfo (src, NULL, &hints, &result);
1176
return 0; /* error */
1179
pgm_assert (NULL != result->ai_addr);
1180
pgm_assert (0 != result->ai_addrlen);
1182
switch (result->ai_addr->sa_family) {
1184
struct sockaddr_in s4;
1185
memcpy (&s4, result->ai_addr, sizeof(s4));
1186
memcpy (dst, &s4.sin_addr.s_addr, sizeof(struct in_addr));
1191
struct sockaddr_in6 s6;
1192
memcpy (&s6, result->ai_addr, sizeof(s6));
1193
memcpy (dst, &s6.sin6_addr, sizeof(struct in6_addr));
1198
pgm_assert_not_reached();
1202
freeaddrinfo (result);
1203
return 1; /* success */
1208
pgm_nla_to_sockaddr (
1209
const void* restrict nla,
1210
struct sockaddr* restrict sa
1213
uint16_t nla_family;
1216
memcpy (&nla_family, nla, sizeof(nla_family));
1217
sa->sa_family = ntohs (nla_family);
1218
switch (sa->sa_family) {
1220
sa->sa_family = AF_INET;
1221
((struct sockaddr_in*)sa)->sin_addr.s_addr = ((const struct in_addr*)((const char*)nla + sizeof(uint32_t)))->s_addr;
1225
sa->sa_family = AF_INET6;
1226
memcpy (&((struct sockaddr_in6*)sa)->sin6_addr, (const struct in6_addr*)((const char*)nla + sizeof(uint32_t)), sizeof(struct in6_addr));
1239
pgm_sockaddr_to_nla (
1240
const struct sockaddr* restrict sa,
1246
*(uint16_t*)nla = sa->sa_family;
1247
*(uint16_t*)((char*)nla + sizeof(uint16_t)) = 0; /* reserved 16bit space */
1248
switch (sa->sa_family) {
1250
*(uint16_t*)nla = htons (AFI_IP);
1251
((struct in_addr*)((char*)nla + sizeof(uint32_t)))->s_addr = ((const struct sockaddr_in*)sa)->sin_addr.s_addr;
1255
*(uint16_t*)nla = htons (AFI_IP6);
1256
memcpy ((struct in6_addr*)((char*)nla + sizeof(uint32_t)), &((const struct sockaddr_in6*)sa)->sin6_addr, sizeof(struct in6_addr));