Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for sending UDP multicast on IPv6 #746

Merged
merged 1 commit into from
Oct 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Source/GCD/GCDAsyncUdpSocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,18 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address,
- (BOOL)leaveMulticastGroup:(NSString *)group error:(NSError **)errPtr;
- (BOOL)leaveMulticastGroup:(NSString *)group onInterface:(nullable NSString *)interface error:(NSError **)errPtr;

/**
* Send multicast on a specified interface.
* For IPv4, interface should be the the IP address of the interface (eg @"192.168.10.1").
* For IPv6, interface should be the a network interface name (eg @"en0").
*
* On success, returns YES.
* Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr.
**/

- (BOOL)sendIPv4MulticastOnInterface:(NSString*)interface error:(NSError **)errPtr;
- (BOOL)sendIPv6MulticastOnInterface:(NSString*)interface error:(NSError **)errPtr;

#pragma mark Reuse Port

/**
Expand Down
115 changes: 115 additions & 0 deletions Source/GCD/GCDAsyncUdpSocket.m
Original file line number Diff line number Diff line change
Expand Up @@ -3531,6 +3531,121 @@ - (BOOL)performMulticastRequest:(int)requestType
return result;
}

- (BOOL)sendIPv4MulticastOnInterface:(NSString*)interface error:(NSError **)errPtr
{
__block BOOL result = NO;
__block NSError *err = nil;

dispatch_block_t block = ^{ @autoreleasepool {

if (![self preOp:&err])
{
return_from_block;
}

if ((self->flags & kDidCreateSockets) == 0)
{
if (![self createSockets:&err])
{
return_from_block;
}
}

// Convert interface to address

NSData *interfaceAddr4 = nil;
NSData *interfaceAddr6 = nil;

[self convertIntefaceDescription:interface port:0 intoAddress4:&interfaceAddr4 address6:&interfaceAddr6];

if (interfaceAddr4 == nil)
{
NSString *msg = @"Unknown interface. Specify valid interface by IP address.";
err = [self badParamError:msg];
return_from_block;
}

if (self->socket4FD != SOCKET_NULL) {
const struct sockaddr_in *nativeIface = (struct sockaddr_in *)[interfaceAddr4 bytes];
struct in_addr interface_addr = nativeIface->sin_addr;
int status = setsockopt(self->socket4FD, IPPROTO_IP, IP_MULTICAST_IF, &interface_addr, sizeof(interface_addr));
if (status != 0) {
err = [self errnoErrorWithReason:@"Error in setsockopt() function"];
return_from_block;
result = YES;
}
}

}};

if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey))
block();
else
dispatch_sync(socketQueue, block);

if (errPtr)
*errPtr = err;

return result;
}

- (BOOL)sendIPv6MulticastOnInterface:(NSString*)interface error:(NSError **)errPtr
{
__block BOOL result = NO;
__block NSError *err = nil;

dispatch_block_t block = ^{ @autoreleasepool {

if (![self preOp:&err])
{
return_from_block;
}

if ((self->flags & kDidCreateSockets) == 0)
{
if (![self createSockets:&err])
{
return_from_block;
}
}

// Convert interface to address

NSData *interfaceAddr4 = nil;
NSData *interfaceAddr6 = nil;

[self convertIntefaceDescription:interface port:0 intoAddress4:&interfaceAddr4 address6:&interfaceAddr6];

if (interfaceAddr6 == nil)
{
NSString *msg = @"Unknown interface. Specify valid interface by name (e.g. \"en1\").";
err = [self badParamError:msg];
return_from_block;
}

if ((self->socket6FD != SOCKET_NULL)) {
uint32_t scope_id = [self indexOfInterfaceAddr6:interfaceAddr6];
int status = setsockopt(self->socket6FD, IPPROTO_IPV6, IPV6_MULTICAST_IF, &scope_id, sizeof(scope_id));
if (status != 0) {
err = [self errnoErrorWithReason:@"Error in setsockopt() function"];
return_from_block;
}
result = YES;
}

}};

if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey))
block();
else
dispatch_sync(socketQueue, block);

if (errPtr)
*errPtr = err;

return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Reuse port
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down