~ubuntu-branches/ubuntu/precise/libpgm/precise

« back to all changes in this revision

Viewing changes to openpgm/pgm/sockaddr.c

  • Committer: Bazaar Package Importer
  • Author(s): Gabriel de Perthuis
  • Date: 2011-04-07 16:48:52 UTC
  • Revision ID: james.westby@ubuntu.com-20110407164852-8uamem42ojeptj6l
Tags: upstream-5.1.116~dfsg
ImportĀ upstreamĀ versionĀ 5.1.116~dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* vim:ts=8:sts=4:sw=4:noai:noexpandtab
 
2
 * 
 
3
 * struct sockaddr functions independent of in or in6.
 
4
 *
 
5
 * Copyright (c) 2006-2011 Miru Limited.
 
6
 *
 
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.
 
11
 * 
 
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.
 
16
 * 
 
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
 
20
 */
 
21
 
 
22
#include <errno.h>
 
23
#ifndef _WIN32
 
24
#       include <sys/socket.h>
 
25
#       include <netdb.h>
 
26
#endif
 
27
#include <impl/framework.h>
 
28
 
 
29
 
 
30
/* FreeBSD */
 
31
#ifndef IPV6_ADD_MEMBERSHIP
 
32
#       define IPV6_ADD_MEMBERSHIP      IPV6_JOIN_GROUP
 
33
#       define IPV6_DROP_MEMBERSHIP     IPV6_LEAVE_GROUP
 
34
#endif
 
35
/* OpenSolaris differences */
 
36
#if !defined(_WIN32) && !defined(MCAST_MSFILTER)
 
37
#       include <sys/ioctl.h>
 
38
#endif
 
39
#ifndef SOL_IP
 
40
#       define SOL_IP                   IPPROTO_IP
 
41
#endif
 
42
#ifndef SOL_IPV6
 
43
#       define SOL_IPV6                 IPPROTO_IPV6
 
44
#endif
 
45
#ifndef IP_MAX_MEMBERSHIPS
 
46
#       define IP_MAX_MEMBERSHIPS       20
 
47
#endif
 
48
 
 
49
PGM_GNUC_INTERNAL
 
50
sa_family_t
 
51
pgm_sockaddr_family (
 
52
        const struct sockaddr*  sa
 
53
        )
 
54
{
 
55
        return sa->sa_family;
 
56
}
 
57
 
 
58
PGM_GNUC_INTERNAL
 
59
in_port_t
 
60
pgm_sockaddr_port (
 
61
        const struct sockaddr*  sa
 
62
        )
 
63
{
 
64
        in_port_t sa_port;
 
65
        switch (sa->sa_family) {
 
66
        case AF_INET: {
 
67
                struct sockaddr_in s4;
 
68
                memcpy (&s4, sa, sizeof(s4));
 
69
                sa_port = s4.sin_port;
 
70
                break;
 
71
        }
 
72
 
 
73
        case AF_INET6: {
 
74
                struct sockaddr_in6 s6;
 
75
                memcpy (&s6, sa, sizeof(s6));
 
76
                sa_port = s6.sin6_port;
 
77
                break;
 
78
        }
 
79
 
 
80
        default:
 
81
                sa_port = 0;
 
82
                break;
 
83
        }
 
84
        return sa_port;
 
85
}
 
86
 
 
87
PGM_GNUC_INTERNAL
 
88
socklen_t
 
89
pgm_sockaddr_len (
 
90
        const struct sockaddr*  sa
 
91
        )
 
92
{
 
93
        socklen_t sa_len;
 
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;
 
98
        }
 
99
        return sa_len;
 
100
}
 
101
 
 
102
PGM_GNUC_INTERNAL
 
103
socklen_t
 
104
pgm_sockaddr_storage_len (
 
105
        const struct sockaddr_storage*  ss
 
106
        )
 
107
{
 
108
        socklen_t ss_len;
 
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;
 
113
        }
 
114
        return ss_len;
 
115
}
 
116
 
 
117
PGM_GNUC_INTERNAL
 
118
uint32_t
 
119
pgm_sockaddr_scope_id (
 
120
        const struct sockaddr*  sa
 
121
        )
 
122
{
 
123
        uint32_t scope_id;
 
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;
 
128
        } else
 
129
                scope_id = 0;
 
130
        return scope_id;
 
131
}
 
132
 
 
133
PGM_GNUC_INTERNAL
 
134
int
 
135
pgm_sockaddr_ntop (
 
136
        const struct sockaddr* restrict sa,
 
137
        char*                  restrict host,
 
138
        size_t                          hostlen
 
139
        )
 
140
{
 
141
        return getnameinfo (sa, pgm_sockaddr_len (sa),
 
142
                            host, hostlen,
 
143
                            NULL, 0,
 
144
                            NI_NUMERICHOST);
 
145
}
 
146
 
 
147
PGM_GNUC_INTERNAL
 
148
int
 
149
pgm_sockaddr_pton (
 
150
        const char*      restrict src,
 
151
        struct sockaddr* restrict dst           /* will error on wrong size */
 
152
        )
 
153
{
 
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
 
159
        }, *result = NULL;
 
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);
 
164
                return 1;
 
165
        }
 
166
        return 0;
 
167
}
 
168
 
 
169
/* returns tri-state value: 1 if sa is multicast, 0 if sa is not multicast, -1 on error
 
170
 */
 
171
 
 
172
PGM_GNUC_INTERNAL
 
173
int
 
174
pgm_sockaddr_is_addr_multicast (
 
175
        const struct sockaddr*  sa
 
176
        )
 
177
{
 
178
        int retval;
 
179
 
 
180
        switch (sa->sa_family) {
 
181
        case AF_INET: {
 
182
                struct sockaddr_in s4;
 
183
                memcpy (&s4, sa, sizeof(s4));
 
184
                retval = IN_MULTICAST(ntohl( s4.sin_addr.s_addr ));
 
185
                break;
 
186
        }
 
187
 
 
188
        case AF_INET6: {
 
189
                struct sockaddr_in6 s6;
 
190
                memcpy (&s6, sa, sizeof(s6));
 
191
                retval = IN6_IS_ADDR_MULTICAST( &s6.sin6_addr );
 
192
                break;
 
193
        }
 
194
 
 
195
        default:
 
196
                retval = -1;
 
197
                break;
 
198
        }
 
199
        return retval;
 
200
}
 
201
 
 
202
/* returns 1 if sa is unspecified, 0 if specified.
 
203
 */
 
204
 
 
205
PGM_GNUC_INTERNAL
 
206
int
 
207
pgm_sockaddr_is_addr_unspecified (
 
208
        const struct sockaddr*  sa
 
209
        )
 
210
{
 
211
        int retval;
 
212
 
 
213
        switch (sa->sa_family) {
 
214
        case AF_INET: {
 
215
                struct sockaddr_in s4;
 
216
                memcpy (&s4, sa, sizeof(s4));
 
217
                retval = (INADDR_ANY == s4.sin_addr.s_addr);
 
218
                break;
 
219
        }
 
220
 
 
221
        case AF_INET6: {
 
222
                struct sockaddr_in6 s6;
 
223
                memcpy (&s6, sa, sizeof(s6));
 
224
                retval = IN6_IS_ADDR_UNSPECIFIED( &s6.sin6_addr );
 
225
                break;
 
226
        }
 
227
 
 
228
        default:
 
229
                retval = -1;
 
230
                break;
 
231
        }
 
232
        return retval;
 
233
}
 
234
 
 
235
PGM_GNUC_INTERNAL
 
236
int
 
237
pgm_sockaddr_cmp (
 
238
        const struct sockaddr* restrict sa1,
 
239
        const struct sockaddr* restrict sa2
 
240
        )
 
241
{
 
242
        int retval = 0;
 
243
 
 
244
        if (sa1->sa_family != sa2->sa_family)
 
245
                retval = sa1->sa_family < sa2->sa_family ? -1 : 1;
 
246
        else {
 
247
                switch (sa1->sa_family) {
 
248
                case AF_INET: {
 
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;
 
254
                        break;
 
255
                }
 
256
 
 
257
/* IN6_ARE_ADDR_EQUAL(a,b) only returns true or false */
 
258
                case AF_INET6: {
 
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;
 
265
                        break;
 
266
                }
 
267
 
 
268
                default:
 
269
                        break;
 
270
                }
 
271
        }
 
272
        return retval;
 
273
}
 
274
 
 
275
/* IP header included with data.
 
276
 *
 
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().
 
280
 */
 
281
 
 
282
PGM_GNUC_INTERNAL
 
283
int
 
284
pgm_sockaddr_hdrincl (
 
285
        const SOCKET            s,
 
286
        const sa_family_t       sa_family,
 
287
        const bool              v
 
288
        )
 
289
{
 
290
        int retval = SOCKET_ERROR;
 
291
 
 
292
        switch (sa_family) {
 
293
        case AF_INET: {
 
294
#ifndef _WIN32
 
295
/* Solaris:ip(7P)  Mentioned but not detailed.
 
296
 *
 
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."
 
301
 *
 
302
 * FreeBSD,OS X:IP(4) provided by example "int hincl = 1;"
 
303
 *
 
304
 * Stevens: "IP_HDRINCL has datatype int."
 
305
 */
 
306
                const int optval = v ? 1 : 0;
 
307
#else
 
308
                const DWORD optval = v ? 1 : 0;
 
309
#endif
 
310
                retval = setsockopt (s, IPPROTO_IP, IP_HDRINCL, (const char*)&optval, sizeof(optval));
 
311
                break;
 
312
        }
 
313
 
 
314
        case AF_INET6:  /* method only exists on Win32, just ignore */
 
315
                retval = 0;
 
316
                break;
 
317
 
 
318
        default: break;
 
319
        }
 
320
        return retval;
 
321
}
 
322
 
 
323
/* Return destination IP address.
 
324
 *
 
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().
 
328
 */
 
329
 
 
330
PGM_GNUC_INTERNAL
 
331
int
 
332
pgm_sockaddr_pktinfo (
 
333
        const SOCKET            s,
 
334
        const sa_family_t       sa_family,
 
335
        const bool              v
 
336
        )
 
337
{
 
338
        int retval = SOCKET_ERROR;
 
339
#ifndef _WIN32
 
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"
 
343
 *
 
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."
 
349
 *
 
350
 * Absent from FreeBSD & OS X, suggested replacement IP_RECVDSTADDR.
 
351
 * OS X:IP6(4) "IPV6_PKTINFO int *"
 
352
 *
 
353
 * Stevens: "IP_RECVDSTADDR has datatype int."
 
354
 */
 
355
        const int optval = v ? 1 : 0;
 
356
#else
 
357
        const DWORD optval = v ? 1 : 0;
 
358
#endif
 
359
 
 
360
        switch (sa_family) {
 
361
        case AF_INET:
 
362
#ifdef IP_RECVDSTADDR
 
363
                retval = setsockopt (s, IPPROTO_IP, IP_RECVDSTADDR, (const char*)&optval, sizeof(optval));
 
364
#else
 
365
                retval = setsockopt (s, IPPROTO_IP, IP_PKTINFO, (const char*)&optval, sizeof(optval));
 
366
#endif
 
367
                break;
 
368
 
 
369
        case AF_INET6:
 
370
#ifdef IPV6_RECVPKTINFO
 
371
                retval = setsockopt (s, IPPROTO_IPV6, IPV6_RECVPKTINFO, (const char*)&optval, sizeof(optval));
 
372
#else
 
373
                retval = setsockopt (s, IPPROTO_IPV6, IPV6_PKTINFO, (const char*)&optval, sizeof(optval));
 
374
#endif
 
375
                break;
 
376
 
 
377
        default: break;
 
378
        }
 
379
        return retval;
 
380
}
 
381
 
 
382
/* Set IP Router Alert option for all outgoing packets.
 
383
 *
 
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().
 
387
 */
 
388
 
 
389
PGM_GNUC_INTERNAL
 
390
int
 
391
pgm_sockaddr_router_alert (
 
392
        const SOCKET            s,
 
393
        const sa_family_t       sa_family,
 
394
        const bool              v
 
395
        )
 
396
{
 
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."
 
402
 *
 
403
 * Sent on special queue to rsvpd on Linux and so best avoided.
 
404
 */
 
405
        const int optval = v ? 1 : 0;
 
406
 
 
407
        switch (sa_family) {
 
408
        case AF_INET:
 
409
                retval = setsockopt (s, IPPROTO_IP, IP_ROUTER_ALERT, (const char*)&optval, sizeof(optval));
 
410
                break;
 
411
 
 
412
        case AF_INET6:
 
413
                retval = setsockopt (s, IPPROTO_IPV6, IPV6_ROUTER_ALERT, (const char*)&optval, sizeof(optval));
 
414
                break;
 
415
 
 
416
        default: break;
 
417
        }
 
418
#else
 
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 = {
 
422
                .ipopt_dst  = 0,
 
423
                .ipopt_list = { PGM_IPOPT_RA, 0x04, 0x00, 0x00 }
 
424
        };
 
425
        const int optlen = v ? sizeof(router_alert) : 0;
 
426
#       else
 
427
/* manually set the IP option */
 
428
#               ifndef _WIN32
 
429
        const int ipopt_ra = (PGM_IPOPT_RA << 24) | (0x04 << 16);
 
430
        const int router_alert = htonl (ipopt_ra);
 
431
#               else
 
432
        const DWORD ipopt_ra = (PGM_IPOPT_RA << 24) | (0x04 << 16);
 
433
        const DWORD router_alert = htonl (ipopt_ra);
 
434
#               endif
 
435
        const int optlen = v ? sizeof(router_alert) : 0;
 
436
#       endif
 
437
 
 
438
        switch (sa_family) {
 
439
        case AF_INET:
 
440
/* Linux:ip(7) "The maximum option size for IPv4 is 40 bytes."
 
441
 */
 
442
                retval = setsockopt (s, IPPROTO_IP, IP_OPTIONS, (const char*)&router_alert, optlen);
 
443
                break;
 
444
 
 
445
        default: break;
 
446
        }
 
447
#endif
 
448
        return retval;
 
449
}
 
450
 
 
451
/* Type-of-service and precedence.
 
452
 *
 
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().
 
456
 */
 
457
 
 
458
PGM_GNUC_INTERNAL
 
459
int
 
460
pgm_sockaddr_tos (
 
461
        const SOCKET            s,
 
462
        const sa_family_t       sa_family,
 
463
        const int               tos
 
464
        )
 
465
{
 
466
        int retval = SOCKET_ERROR;
 
467
 
 
468
        switch (sa_family) {
 
469
        case AF_INET: {
 
470
#ifndef _WIN32
 
471
/* Solaris:ip(7P) "This option takes an integer argument as its input value."
 
472
 *
 
473
 * Linux:ip(7) "TOS is a byte."
 
474
 *
 
475
 * FreeBSD,OS X:IP(4) provided by example "int tos = IPTOS_LOWDELAY;"
 
476
 *
 
477
 * Stevens: "IP_TOS has datatype int."
 
478
 */
 
479
                const int optval = tos;
 
480
#else
 
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
 
484
 */
 
485
                const DWORD optval = tos;
 
486
#endif
 
487
                retval = setsockopt (s, IPPROTO_IP, IP_TOS, (const char*)&optval, sizeof(optval));
 
488
                break;
 
489
        }
 
490
 
 
491
        case AF_INET6:  /* TRAFFIC_CLASS not implemented */
 
492
                break;
 
493
 
 
494
        default: break;
 
495
        }
 
496
        return retval;
 
497
}
 
498
 
 
499
/* Join multicast group.
 
500
 * NB: IPV6_JOIN_GROUP == IPV6_ADD_MEMBERSHIP
 
501
 *
 
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().
 
505
 */
 
506
 
 
507
PGM_GNUC_INTERNAL
 
508
int
 
509
pgm_sockaddr_join_group (
 
510
        const SOCKET            s,
 
511
        const sa_family_t       sa_family,
 
512
        const struct group_req* gr
 
513
        )
 
514
{
 
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.
 
521
 *
 
522
 * Stevens: "MCAST_JOIN_GROUP has datatype group_req{}."
 
523
 *
 
524
 * RFC3678: Argument type struct group_req
 
525
 */
 
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));
 
528
#else
 
529
        switch (sa_family) {
 
530
        case AF_INET: {
 
531
/* Solaris:ip(7P) Just mentions "Join a multicast group."
 
532
 * No further details provided.
 
533
 *
 
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."
 
536
 *
 
537
 * FreeBSD,OS X:IP(4) provided by example "struct ip_mreq mreq;"
 
538
 *
 
539
 * Windows can optionally abuse imt_interface to be 0.0.0.<imr_ifindex>
 
540
 *
 
541
 * Stevens: "IP_ADD_MEMBERSHIP has datatype ip_mreq{}."
 
542
 *
 
543
 * RFC3678: Argument type struct ip_mreq
 
544
 */
 
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))
 
551
                        return -1;
 
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));
 
555
#else
 
556
                struct ip_mreq mreq;
 
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))
 
561
                        return -1;
 
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 */
 
565
                break;
 
566
        }
 
567
 
 
568
        case AF_INET6: {
 
569
/* Solaris:ip6(7P) "Takes a struct ipv6_mreq as the parameter;"
 
570
 *
 
571
 * Linux:ipv6(7) "Argument is a pointer to a struct ipv6_mreq structure."
 
572
 *
 
573
 * OS X:IP6(4) "IPV6_JOIN_GROUP struct ipv6_mreq *"
 
574
 *
 
575
 * Stevens: "IPV6_JOIN_GROUP has datatype ipv6_mreq{}."
 
576
 */
 
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));
 
582
                break;
 
583
        }
 
584
 
 
585
        default: break;
 
586
        }
 
587
#endif /* CONFIG_HAVE_MCAST_JOIN */
 
588
        return retval;
 
589
}
 
590
 
 
591
/* leave a joined group
 
592
 */
 
593
 
 
594
PGM_GNUC_INTERNAL
 
595
int
 
596
pgm_sockaddr_leave_group (
 
597
        const SOCKET            s,
 
598
        const sa_family_t       sa_family,
 
599
        const struct group_req* gr
 
600
        )
 
601
{
 
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));
 
606
#else
 
607
        switch (sa_family) {
 
608
        case AF_INET: {
 
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))
 
615
                        return -1;
 
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));
 
619
#else
 
620
                struct ip_mreq mreq;
 
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))
 
625
                        return -1;
 
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 */
 
629
                break;
 
630
        }
 
631
 
 
632
        case AF_INET6: {
 
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));
 
638
                break;
 
639
        }
 
640
 
 
641
        default: break;
 
642
        }
 
643
#endif /* CONFIG_HAVE_MCAST_JOIN */
 
644
        return retval;
 
645
}
 
646
 
 
647
/* block either at the NIC or kernel, packets from a particular source
 
648
 */
 
649
 
 
650
PGM_GNUC_INTERNAL
 
651
int
 
652
pgm_sockaddr_block_source (
 
653
        const SOCKET                    s,
 
654
        const sa_family_t               sa_family,
 
655
        const struct group_source_req*  gsr
 
656
        )
 
657
{
 
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)
 
663
        switch (sa_family) {
 
664
        case AF_INET: {
 
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));
 
673
                break;
 
674
        }
 
675
 
 
676
        case AF_INET6:
 
677
/* No IPv6 API implemented, MCAST_BLOCK_SOURCE should be available instead.
 
678
 */
 
679
                break;
 
680
 
 
681
        default: break;
 
682
        }
 
683
#else
 
684
/* unused parameters */
 
685
        (void)s;
 
686
        (void)sa_family;
 
687
        (void)gsr;
 
688
#endif /* CONFIG_HAVE_MCAST_JOIN */
 
689
        return retval;
 
690
}
 
691
 
 
692
/* unblock a blocked multicast source.
 
693
 */
 
694
 
 
695
PGM_GNUC_INTERNAL
 
696
int
 
697
pgm_sockaddr_unblock_source (
 
698
        const SOCKET                    s,
 
699
        const sa_family_t               sa_family,
 
700
        const struct group_source_req*  gsr
 
701
        )
 
702
{
 
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)
 
708
        switch (sa_family) {
 
709
        case AF_INET: {
 
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));
 
718
                break;
 
719
        }
 
720
 
 
721
        case AF_INET6:
 
722
/* No IPv6 API implemented, MCAST_UNBLOCK_SOURCE should be available instead.
 
723
 */
 
724
                break;
 
725
 
 
726
        default: break;
 
727
        }
 
728
#else
 
729
/* unused parameters */
 
730
        (void)s;
 
731
        (void)sa_family;
 
732
        (void)gsr;
 
733
#endif /* CONFIG_HAVE_MCAST_JOIN */
 
734
        return retval;
 
735
}
 
736
 
 
737
/* Join source-specific multicast.
 
738
 * NB: Silently reverts to ASM if SSM not supported.
 
739
 *
 
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().
 
743
 */
 
744
 
 
745
PGM_GNUC_INTERNAL
 
746
int
 
747
pgm_sockaddr_join_source_group (
 
748
        const SOCKET                    s,
 
749
        const sa_family_t               sa_family,
 
750
        const struct group_source_req*  gsr
 
751
        )
 
752
{
 
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
 
756
 * parameter."
 
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-
 
759
 * independent.
 
760
 *
 
761
 * Stevens: "MCAST_JOIN_SOURCE_GROUP has datatype group_source_req{}."
 
762
 *
 
763
 * RFC3678: Argument type struct group_source_req
 
764
 */
 
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)
 
768
        switch (sa_family) {
 
769
        case AF_INET: {
 
770
/* Solaris:ip(7P) "The following options take a struct ip_mreq as the
 
771
 * parameter."  Incorrect literature wrt RFC.
 
772
 *
 
773
 * Linux:ip(7) absent.
 
774
 *
 
775
 * OS X:IP(4) absent.
 
776
 *
 
777
 * Stevens: "IP_ADD_SOURCE_MEMBERSHIP has datatype ip_mreq_source{}."
 
778
 *
 
779
 * RFC3678: Argument type struct ip_mreq_source
 
780
 */
 
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));
 
789
                break;
 
790
        }
 
791
 
 
792
        case AF_INET6:
 
793
/* No IPv6 API implemented, MCAST_JOIN_SOURCE_GROUP should be available instead.
 
794
 */
 
795
                retval = pgm_sockaddr_join_group (s, sa_family, (const struct group_req*)gsr);
 
796
                break;
 
797
 
 
798
        default: break;
 
799
        }
 
800
#else
 
801
        retval = pgm_sockaddr_join_group (s, sa_family, (const struct group_req*)gsr);  
 
802
#endif /* CONFIG_HAVE_MCAST_JOIN */
 
803
        return retval;
 
804
}
 
805
 
 
806
/* drop a SSM source
 
807
 */
 
808
 
 
809
PGM_GNUC_INTERNAL
 
810
int
 
811
pgm_sockaddr_leave_source_group (
 
812
        const SOCKET                    s,
 
813
        const sa_family_t               sa_family,
 
814
        const struct group_source_req*  gsr
 
815
        )
 
816
{
 
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)
 
822
        switch (sa_family) {
 
823
        case AF_INET: {
 
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));
 
832
                break;
 
833
        }
 
834
 
 
835
        case AF_INET6:
 
836
/* No IPv6 API implemented, MCAST_LEAVE_SOURCE_GROUP should be available instead.
 
837
 */
 
838
                retval = pgm_sockaddr_leave_group (s, sa_family, (const struct group_req*)gsr);
 
839
                break;
 
840
 
 
841
        default: break;
 
842
        }
 
843
#else
 
844
        retval = pgm_sockaddr_leave_group (s, sa_family, (const struct group_req*)gsr); 
 
845
#endif /* CONFIG_HAVE_MCAST_JOIN */
 
846
        return retval;
 
847
}
 
848
 
 
849
#if defined(MCAST_MSFILTER) || defined(SIOCSMSFILTER)
 
850
/* Batch block and unblock sources.
 
851
 */
 
852
 
 
853
PGM_GNUC_INTERNAL
 
854
int
 
855
pgm_sockaddr_msfilter (
 
856
        const SOCKET                    s,
 
857
        const sa_family_t               sa_family,
 
858
        const struct group_filter*      gf_list
 
859
        )
 
860
{
 
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;
 
878
                unsigned i;
 
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;
 
888
                }
 
889
#               ifdef IP_MSFILTER
 
890
                retval = ioctlsocket (s, IP_MSFILTER, (char*)filter);
 
891
#               else
 
892
                retval = ioctlsocket (s, SIO_SET_MULTICAST_FILTER, (u_long*)filter);
 
893
#               endif
 
894
        }
 
895
#       endif
 
896
        return retval;
 
897
}
 
898
#endif /* MCAST_MSFILTER || SIOCSMSFILTER */
 
899
 
 
900
/* Specify outgoing interface.
 
901
 *
 
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().
 
905
 */
 
906
 
 
907
PGM_GNUC_INTERNAL
 
908
int
 
909
pgm_sockaddr_multicast_if (
 
910
        const SOCKET            s,
 
911
        const struct sockaddr*  address,
 
912
        const unsigned          ifindex
 
913
        )
 
914
{
 
915
        int retval = SOCKET_ERROR;
 
916
 
 
917
        switch (address->sa_family) {
 
918
        case AF_INET: {
 
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."
 
921
 *
 
922
 * Linux:ip(7) "Argument is an ip_mreqn or ip_mreq structure similar to
 
923
 * IP_ADD_MEMBERSHIP."
 
924
 *
 
925
 * OS X:IP(4) provided by example "struct in_addr addr;"
 
926
 *
 
927
 * Stevens: "IP_MULTICAST_IF has datatype struct in_addr{}."
 
928
 */
 
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));
 
932
                break;
 
933
        }
 
934
 
 
935
        case AF_INET6: {
 
936
#ifndef _WIN32
 
937
/* Solaris:ip6(7P) "This option takes an integer as an argument; the integer
 
938
 * is the interface index of the selected interface."
 
939
 *
 
940
 * Linux:ipv6(7) "The argument is a pointer to an interface index (see 
 
941
 * netdevice(7)) in an integer."
 
942
 *
 
943
 * OS X:IP6(4) "IPV6_MULTICAST_IF u_int *"
 
944
 *
 
945
 * Stevens: "IPV6_MULTICAST_IF has datatype u_int."
 
946
 */
 
947
                const unsigned int optval = ifindex;
 
948
#else
 
949
                const DWORD optval = ifindex;
 
950
#endif
 
951
                retval = setsockopt (s, IPPROTO_IPV6, IPV6_MULTICAST_IF, (const char*)&optval, sizeof(optval));
 
952
                break;
 
953
        }
 
954
 
 
955
        default: break;
 
956
        }
 
957
        return retval;
 
958
}
 
959
 
 
960
/* Specify multicast loop, other applications on the same host may receive
 
961
 * outgoing packets.  This does not affect unicast packets such as NAKs.
 
962
 *
 
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().
 
966
 */
 
967
 
 
968
PGM_GNUC_INTERNAL
 
969
int
 
970
pgm_sockaddr_multicast_loop (
 
971
        const SOCKET            s,
 
972
        const sa_family_t       sa_family,
 
973
        const bool              v
 
974
        )
 
975
{
 
976
        int retval = SOCKET_ERROR;
 
977
 
 
978
        switch (sa_family) {
 
979
        case AF_INET: {
 
980
#ifndef _WIN32
 
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."
 
984
 *
 
985
 * Linux:ip(7) "Sets or reads a boolean integer argument"
 
986
 *
 
987
 * OS X:IP(4) provided by example "u_char loop;"
 
988
 *
 
989
 * Stevens: "IP_MULTICAST_LOOP has datatype u_char."
 
990
 */
 
991
                const unsigned char optval = v ? 1 : 0;
 
992
#else
 
993
                const DWORD optval = v ? 1 : 0;
 
994
#endif
 
995
                retval = setsockopt (s, IPPROTO_IP, IP_MULTICAST_LOOP, (const char*)&optval, sizeof(optval));
 
996
                break;
 
997
        }
 
998
 
 
999
        case AF_INET6: {
 
1000
#ifndef _WIN32
 
1001
/* Solaris:ip(7P) "Setting the unsigned character argument to 0 will cause the opposite behavior."
 
1002
 *
 
1003
 * Linux:ipv6(7) "Argument is a pointer to boolean."
 
1004
 *
 
1005
 * OS X:IP6(7) "IPV6_MULTICAST_LOOP u_int *"
 
1006
 *
 
1007
 * Stevens: "IPV6_MULTICAST_LOOP has datatype u_int."
 
1008
 */
 
1009
                const unsigned int optval = v ? 1 : 0;
 
1010
#else
 
1011
                const DWORD optval = v ? 1 : 0;
 
1012
#endif
 
1013
                retval = setsockopt (s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (const char*)&optval, sizeof(optval));
 
1014
                break;
 
1015
        }
 
1016
 
 
1017
        default: break;
 
1018
        }
 
1019
        return retval;
 
1020
}
 
1021
 
 
1022
/* Specify TTL or outgoing hop limit.
 
1023
 * NB: Only affects multicast hops, unicast hop-limit is not changed.
 
1024
 *
 
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().
 
1028
 */
 
1029
 
 
1030
PGM_GNUC_INTERNAL
 
1031
int
 
1032
pgm_sockaddr_multicast_hops (
 
1033
        const SOCKET            s,
 
1034
        const sa_family_t       sa_family,
 
1035
        const unsigned          hops
 
1036
        )
 
1037
{
 
1038
        int retval = SOCKET_ERROR;
 
1039
 
 
1040
        switch (sa_family) {
 
1041
        case AF_INET: {
 
1042
#ifndef _WIN32
 
1043
/* Solaris:ip(7P) "This option takes an unsigned character as an argument."
 
1044
 *
 
1045
 * Linux:ip(7) "Argument is an integer."
 
1046
 *
 
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;"
 
1049
 *
 
1050
 * Stevens: "IP_MULTICAST_TTL has datatype u_char."
 
1051
 */
 
1052
                const unsigned char optval = hops;
 
1053
#else
 
1054
                const DWORD optval = hops;
 
1055
#endif
 
1056
                retval = setsockopt (s, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&optval, sizeof(optval));
 
1057
                break;
 
1058
        }
 
1059
 
 
1060
        case AF_INET6: {
 
1061
#ifndef _WIN32
 
1062
/* Solaris:ip6(7P) "This option takes an integer as an argument."
 
1063
 *
 
1064
 * Linux:ipv6(7) "Argument is a pointer to an integer."
 
1065
 *
 
1066
 * OS X:IP6(7) "IPV6_MULTICAST_HOPS int *"
 
1067
 *
 
1068
 * Stevens: "IPV6_MULTICAST_HOPS has datatype int."
 
1069
 */
 
1070
                const int optval = hops;
 
1071
#else
 
1072
                const DWORD optval = hops;
 
1073
#endif
 
1074
                retval = setsockopt (s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char*)&optval, sizeof(optval));
 
1075
                break;
 
1076
        }
 
1077
 
 
1078
        default: break;
 
1079
        }
 
1080
        return retval;
 
1081
}
 
1082
 
 
1083
PGM_GNUC_INTERNAL
 
1084
void
 
1085
pgm_sockaddr_nonblocking (
 
1086
        const SOCKET    s,
 
1087
        const bool      v
 
1088
        )
 
1089
{
 
1090
#ifndef _WIN32
 
1091
        int flags = fcntl (s, F_GETFL);
 
1092
        if (!v) flags &= ~O_NONBLOCK;
 
1093
        else flags |= O_NONBLOCK;
 
1094
        fcntl (s, F_SETFL, flags);
 
1095
#else
 
1096
        u_long mode = v;
 
1097
        ioctlsocket (s, FIONBIO, &mode);
 
1098
#endif
 
1099
}
 
1100
 
 
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
 
1103
 * link-local scope.
 
1104
 */
 
1105
 
 
1106
PGM_GNUC_INTERNAL
 
1107
const char*
 
1108
pgm_inet_ntop (
 
1109
        int                  af,
 
1110
        const void* restrict src,
 
1111
        char*       restrict dst,
 
1112
        socklen_t            size
 
1113
        )
 
1114
{
 
1115
        pgm_assert (AF_INET == af || AF_INET6 == af);
 
1116
        pgm_assert (NULL != src);
 
1117
        pgm_assert (NULL != dst);
 
1118
        pgm_assert (size > 0);
 
1119
 
 
1120
        switch (af) {
 
1121
        case AF_INET:
 
1122
        {
 
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),
 
1128
                             dst, size,
 
1129
                             NULL, 0,
 
1130
                             NI_NUMERICHOST);
 
1131
                return dst;
 
1132
        }
 
1133
        case AF_INET6:
 
1134
        {
 
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),
 
1140
                             dst, size,
 
1141
                             NULL, 0,
 
1142
                             NI_NUMERICHOST);
 
1143
                return dst;
 
1144
        }
 
1145
        }
 
1146
 
 
1147
#ifndef _WIN32
 
1148
        errno = EAFNOSUPPORT;
 
1149
#else
 
1150
        WSASetLastError (WSAEAFNOSUPPORT);
 
1151
#endif
 
1152
        return NULL;
 
1153
}
 
1154
 
 
1155
PGM_GNUC_INTERNAL
 
1156
int
 
1157
pgm_inet_pton (
 
1158
        int                  af,
 
1159
        const char* restrict src,
 
1160
        void*       restrict dst
 
1161
        )
 
1162
{
 
1163
        pgm_assert (AF_INET == af || AF_INET6 == af);
 
1164
        pgm_assert (NULL != src);
 
1165
        pgm_assert (NULL != dst);
 
1166
 
 
1167
        struct addrinfo hints = {
 
1168
                .ai_family      = af,
 
1169
                .ai_socktype    = SOCK_STREAM,          /* not really */
 
1170
                .ai_protocol    = IPPROTO_TCP,          /* not really */
 
1171
                .ai_flags       = AI_NUMERICHOST
 
1172
        }, *result = NULL;
 
1173
 
 
1174
        const int e = getaddrinfo (src, NULL, &hints, &result);
 
1175
        if (0 != e) {
 
1176
                return 0;       /* error */
 
1177
        }
 
1178
 
 
1179
        pgm_assert (NULL != result->ai_addr);
 
1180
        pgm_assert (0 != result->ai_addrlen);
 
1181
 
 
1182
        switch (result->ai_addr->sa_family) {
 
1183
        case AF_INET: {
 
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));
 
1187
                break;
 
1188
        }
 
1189
 
 
1190
        case AF_INET6: {
 
1191
                struct sockaddr_in6 s6;
 
1192
                memcpy (&s6, result->ai_addr, sizeof(s6));
 
1193
                memcpy (dst, &s6.sin6_addr, sizeof(struct in6_addr));
 
1194
                break;
 
1195
        }
 
1196
 
 
1197
        default:
 
1198
                pgm_assert_not_reached();
 
1199
                break;
 
1200
        }
 
1201
 
 
1202
        freeaddrinfo (result);
 
1203
        return 1;       /* success */
 
1204
}
 
1205
 
 
1206
PGM_GNUC_INTERNAL
 
1207
int
 
1208
pgm_nla_to_sockaddr (
 
1209
        const void*      restrict nla,
 
1210
        struct sockaddr* restrict sa
 
1211
        )
 
1212
{
 
1213
        uint16_t nla_family;
 
1214
        int retval = 0;
 
1215
 
 
1216
        memcpy (&nla_family, nla, sizeof(nla_family));
 
1217
        sa->sa_family = ntohs (nla_family);
 
1218
        switch (sa->sa_family) {
 
1219
        case AFI_IP:
 
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;
 
1222
                break;
 
1223
 
 
1224
        case AFI_IP6:
 
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));
 
1227
                break;
 
1228
 
 
1229
        default:
 
1230
                retval = -EINVAL;
 
1231
                break;
 
1232
        }
 
1233
 
 
1234
        return retval;
 
1235
}
 
1236
 
 
1237
PGM_GNUC_INTERNAL
 
1238
int
 
1239
pgm_sockaddr_to_nla (
 
1240
        const struct sockaddr* restrict sa,
 
1241
        void*                  restrict nla
 
1242
        )
 
1243
{
 
1244
        int retval = 0;
 
1245
 
 
1246
        *(uint16_t*)nla = sa->sa_family;
 
1247
        *(uint16_t*)((char*)nla + sizeof(uint16_t)) = 0;        /* reserved 16bit space */
 
1248
        switch (sa->sa_family) {
 
1249
        case AF_INET:
 
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;
 
1252
                break;
 
1253
 
 
1254
        case AF_INET6:
 
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));
 
1257
                break;
 
1258
 
 
1259
        default:
 
1260
                retval = -EINVAL;
 
1261
                break;
 
1262
        }
 
1263
 
 
1264
        return retval;
 
1265
}
 
1266
 
 
1267
/* eof */