Add PSK support in Wifi common implementation

PiperOrigin-RevId: 758744875
This commit is contained in:
Guogang Li
2025-05-14 10:41:07 -07:00
committed by Copybara-Service
parent 3c718941b1
commit cc1239789f
5 changed files with 290 additions and 117 deletions
@@ -14,6 +14,8 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class GNCIPv4Address;
@class GNCNWFrameworkServerSocket;
@class GNCNWFrameworkSocket;
@@ -55,6 +57,31 @@ typedef void (^ServiceUpdateHandler)(NSString *_Nonnull serviceName,
includePeerToPeer:(BOOL)includePeerToPeer
error:(NSError **_Nullable)error;
/**
* Listens for incoming connections with PSK based TLS on a given port.
*
* This function will always be called before a call to
* startAdvertisingServiceName:serviceType:error:, but may also be called on its own without a call
* to start advertising the service. This function will also be called during upgrade attempts on
* the advertising side.
*
* This function should block execution until ready for incoming connections.
*
* @param port The port on which the listener can accept connections. Should be a number between 1
* and 65536 to open a server socket on that exact port. Zero can be used to listen on
* a random port.
* @param PSKIdentity The PSK identity of the service.
* @param PSKSharedSecret The PSK shared secret of the service.
* @param includePeerToPeer Whether to include peer-to-peer services.
* @param[out] error Error that will be populated on failure.
* @return Returns a server socket or nil if an error has occured.
*/
- (nullable GNCNWFrameworkServerSocket *)listenForServiceWithPSKIdentity:(NSData *)PSKIdentity
PSKSharedSecret:(NSData *)PSKSharedSecret
port:(NSInteger)port
includePeerToPeer:(BOOL)includePeerToPeer
error:(NSError **_Nullable)error;
/**
* Creates a Bonjour service that advertises the listener on the local network.
*
@@ -64,9 +91,9 @@ typedef void (^ServiceUpdateHandler)(NSString *_Nonnull serviceName,
* @param txtRecords The TXT record to advertise with the service.
*/
- (void)startAdvertisingPort:(NSInteger)port
serviceName:(nonnull NSString *)serviceName
serviceType:(nonnull NSString *)serviceType
txtRecords:(nonnull NSDictionary<NSString *, NSString *> *)txtRecords;
serviceName:(NSString *)serviceName
serviceType:(NSString *)serviceType
txtRecords:(NSDictionary<NSString *, NSString *> *)txtRecords;
/**
* Removes the Bonjour service advertisement.
@@ -85,7 +112,7 @@ typedef void (^ServiceUpdateHandler)(NSString *_Nonnull serviceName,
* @param[out] error Error that will be populated on failure.
* @return Returns YES when discovery has successfully started.
*/
- (BOOL)startDiscoveryForServiceType:(nonnull NSString *)serviceType
- (BOOL)startDiscoveryForServiceType:(NSString *)serviceType
serviceFoundHandler:(ServiceUpdateHandler)serviceFoundHandler
serviceLostHandler:(ServiceUpdateHandler)serviceLostHandler
includePeerToPeer:(BOOL)includePeerToPeer
@@ -96,7 +123,7 @@ typedef void (^ServiceUpdateHandler)(NSString *_Nonnull serviceName,
*
* @param serviceType The Bonjour type of the service.
*/
- (void)stopDiscoveryForServiceType:(nonnull NSString *)serviceType;
- (void)stopDiscoveryForServiceType:(NSString *)serviceType;
/**
* Connects to a Bonjour service.
@@ -106,8 +133,24 @@ typedef void (^ServiceUpdateHandler)(NSString *_Nonnull serviceName,
* @param[out] error Error that will be populated on failure.
* @return Returns a connected socket or nil if an error has occured.
*/
- (nullable GNCNWFrameworkSocket *)connectToServiceName:(nonnull NSString *)serviceName
serviceType:(nonnull NSString *)serviceType
- (nullable GNCNWFrameworkSocket *)connectToServiceName:(NSString *)serviceName
serviceType:(NSString *)serviceType
error:(NSError **_Nullable)error;
/**
* Connects to a Bonjour service with PSK based TLS.
*
* @param serviceName The Bonjour name of the service.
* @param serviceType The Bonjour type of the service.
* @param PSKIdentity The PSK identity of the service.
* @param PSKSharedSecret The PSK shared secret of the service.
* @param[out] error Error that will be populated on failure.
* @return Returns a connected socket or nil if an error has occured.
*/
- (nullable GNCNWFrameworkSocket *)connectToServiceName:(NSString *)serviceName
serviceType:(NSString *)serviceType
PSKIdentity:(NSData *)PSKIdentity
PSKSharedSecret:(NSData *)PSKSharedSecret
error:(NSError **_Nullable)error;
/**
@@ -119,7 +162,7 @@ typedef void (^ServiceUpdateHandler)(NSString *_Nonnull serviceName,
* @param[out] error Error that will be populated on failure.
* @return Returns a connected socket or nil if an error has occured.
*/
- (nullable GNCNWFrameworkSocket *)connectToHost:(nonnull GNCIPv4Address *)host
- (nullable GNCNWFrameworkSocket *)connectToHost:(GNCIPv4Address *)host
port:(NSInteger)port
includePeerToPeer:(BOOL)includePeerToPeer
error:(NSError **_Nullable)error;
@@ -134,3 +177,5 @@ typedef void (^ServiceUpdateHandler)(NSString *_Nonnull serviceName,
*/
- (BOOL)isDiscoveringAnyService;
@end
NS_ASSUME_NONNULL_END
@@ -22,8 +22,11 @@
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkServerSocket+Internal.h"
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkServerSocket.h"
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkSocket.h"
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWParameters.h"
#import "GoogleToolboxForMac/GTMLogger.h"
NS_ASSUME_NONNULL_BEGIN
// An arbitrary timeout that should be pretty lenient.
NSTimeInterval const GNCConnectionTimeoutInSeconds = 2;
@@ -60,6 +63,14 @@ NSDictionary<NSString *, NSString *> *GNCTXTRecordForBrowseResult(nw_browse_resu
return txtRecords;
}
@interface GNCNWFramework ()
- (nullable GNCNWFrameworkSocket *)internalConnectToEndpoint:(nw_endpoint_t)endpoint
PSKIdentity:(nullable NSData *)PSKIdentity
PSKSharedSecret:(nullable NSData *)PSKSharedSecret
includePeerToPeer:(BOOL)includePeerToPeer
error:(NSError **)error;
@end
@implementation GNCNWFramework {
// Holds a weak reference to a server socket that is retrievable by port. This allows us to stop
// advertisements for a given port without taking a strong reference. This keeps the ownership
@@ -111,9 +122,9 @@ static GNCNWFramework *gInstance = nil;
return _serviceBrowsers.count > 0;
}
- (GNCNWFrameworkServerSocket *)listenForServiceOnPort:(NSInteger)port
includePeerToPeer:(BOOL)includePeerToPeer
error:(NSError **)error {
- (nullable GNCNWFrameworkServerSocket *)listenForServiceOnPort:(NSInteger)port
includePeerToPeer:(BOOL)includePeerToPeer
error:(NSError **)error {
GTMLoggerInfo(@"[GNCNWFramework] Listen on port: %ld with includePeerToPeer: %@.", (long)port,
(includePeerToPeer ? @"true" : @"false"));
GNCNWFrameworkServerSocket *serverSocket = [[GNCNWFrameworkServerSocket alloc] initWithPort:port];
@@ -126,6 +137,28 @@ static GNCNWFramework *gInstance = nil;
return nil;
}
- (nullable GNCNWFrameworkServerSocket *)listenForServiceWithPSKIdentity:(NSData *)PSKIdentity
PSKSharedSecret:(NSData *)PSKSharedSecret
port:(NSInteger)port
includePeerToPeer:(BOOL)includePeerToPeer
error:
(NSError **_Nullable)error {
GTMLoggerInfo(
@"[GNCNWFramework] Listen on port: %ld with includePeerToPeer: %@, and PSKIdentity: %@.",
(long)port, (includePeerToPeer ? @"true" : @"false"), PSKIdentity);
GNCNWFrameworkServerSocket *serverSocket = [[GNCNWFrameworkServerSocket alloc] initWithPort:port];
_includePeerToPeer = includePeerToPeer;
BOOL success = [serverSocket startListeningWithPSKIdentity:PSKIdentity
PSKSharedSecret:PSKSharedSecret
includePeerToPeer:_includePeerToPeer
error:error];
if (success) {
[_serverSockets setObject:serverSocket forKey:@(serverSocket.port)];
return serverSocket;
}
return nil;
}
- (void)startAdvertisingPort:(NSInteger)port
serviceName:(NSString *)serviceName
serviceType:(NSString *)serviceType
@@ -158,11 +191,12 @@ static GNCNWFramework *gInstance = nil;
// Create a parameters object configured to support TCP. TLS MUST be disabled for Nearby to
// function properly.
nw_parameters_t parameters =
nw_parameters_create_secure_tcp(/*tls*/ NW_PARAMETERS_DISABLE_PROTOCOL,
/*tcp*/ NW_PARAMETERS_DEFAULT_CONFIGURATION);
_includePeerToPeer = includePeerToPeer;
nw_parameters_set_include_peer_to_peer(parameters, _includePeerToPeer);
nw_parameters_t parameters = GNCBuildNonTLSParameters(/*includePeerToPeer=*/_includePeerToPeer);
if (!parameters) {
GTMLoggerError(@"[GNCNWFramework] Failed to create NW parameters.");
return NO;
}
nw_browse_descriptor_t descriptor =
nw_browse_descriptor_create_bonjour_service([serviceType UTF8String], /*domain=*/nil);
nw_browse_descriptor_set_include_txt_record(descriptor, YES);
@@ -287,42 +321,76 @@ static GNCNWFramework *gInstance = nil;
nw_browser_cancel(browser);
}
- (GNCNWFrameworkSocket *)connectToServiceName:(NSString *)serviceName
serviceType:(NSString *)serviceType
error:(NSError **)error {
- (nullable GNCNWFrameworkSocket *)connectToServiceName:(NSString *)serviceName
serviceType:(NSString *)serviceType
error:(NSError **)error {
GTMLoggerInfo(@"[GNCNWFramework] Connect to service {serviceName:%@, serviceType:%@, "
@"includePeerToPeer:%@}.",
serviceName, serviceType, (_includePeerToPeer ? @"true" : @"false"));
nw_endpoint_t endpoint = nw_endpoint_create_bonjour_service([serviceName UTF8String],
[serviceType UTF8String], "local");
return [self connectToEndpoint:endpoint includePeerToPeer:_includePeerToPeer error:error];
return [self internalConnectToEndpoint:endpoint
PSKIdentity:nil
PSKSharedSecret:nil
includePeerToPeer:_includePeerToPeer
error:error];
}
- (GNCNWFrameworkSocket *)connectToHost:(GNCIPv4Address *)host
port:(NSInteger)port
includePeerToPeer:(BOOL)includePeerToPeer
error:(NSError **)error {
- (nullable GNCNWFrameworkSocket *)connectToServiceName:(NSString *)serviceName
serviceType:(NSString *)serviceType
PSKIdentity:(NSData *)PSKIdentity
PSKSharedSecret:(NSData *)PSKSharedSecret
error:(NSError **_Nullable)error {
GTMLoggerInfo(@"[GNCNWFramework] Connect to service {serviceName:%@, serviceType:%@, "
@"includePeerToPeer:%@}.",
serviceName, serviceType, (_includePeerToPeer ? @"true" : @"false"));
nw_endpoint_t endpoint = nw_endpoint_create_bonjour_service([serviceName UTF8String],
[serviceType UTF8String], "local");
return [self internalConnectToEndpoint:endpoint
PSKIdentity:PSKIdentity
PSKSharedSecret:PSKSharedSecret
includePeerToPeer:_includePeerToPeer
error:error];
}
- (nullable GNCNWFrameworkSocket *)connectToHost:(GNCIPv4Address *)host
port:(NSInteger)port
includePeerToPeer:(BOOL)includePeerToPeer
error:(NSError **)error {
GTMLoggerInfo(@"[GNCNWFramework] Connect to host {host:%s, port:%ld}.",
host.dottedRepresentation.UTF8String, (long)port);
nw_endpoint_t endpoint =
nw_endpoint_create_host(host.dottedRepresentation.UTF8String, @(port).stringValue.UTF8String);
return [self connectToEndpoint:endpoint includePeerToPeer:(BOOL)includePeerToPeer error:error];
return [self internalConnectToEndpoint:endpoint
PSKIdentity:nil
PSKSharedSecret:nil
includePeerToPeer:(BOOL)includePeerToPeer
error:error];
}
- (GNCNWFrameworkSocket *)connectToEndpoint:(nw_endpoint_t)endpoint
includePeerToPeer:(BOOL)includePeerToPeer
error:(NSError **)error {
// MARK: - Private Methods (Implementation)
- (nullable GNCNWFrameworkSocket *)internalConnectToEndpoint:(nw_endpoint_t)endpoint
PSKIdentity:(nullable NSData *)PSKIdentity
PSKSharedSecret:(nullable NSData *)PSKSharedSecret
includePeerToPeer:(BOOL)includePeerToPeer
error:(NSError **)error {
NSCondition *condition = [[NSCondition alloc] init];
[condition lock];
__block nw_connection_state_t blockResult = nw_connection_state_invalid;
__block NSError *blockError = nil;
nw_parameters_t parameters =
nw_parameters_create_secure_tcp(/*tls*/ NW_PARAMETERS_DISABLE_PROTOCOL,
/*tcp*/ NW_PARAMETERS_DEFAULT_CONFIGURATION);
_includePeerToPeer = includePeerToPeer;
nw_parameters_set_include_peer_to_peer(parameters, _includePeerToPeer);
nw_parameters_t parameters =
(PSKIdentity == nil || PSKSharedSecret == nil)
? GNCBuildNonTLSParameters(/*includePeerToPeer=*/_includePeerToPeer)
: GNCBuildTLSParameters(/*PSK=*/PSKSharedSecret, /*identity=*/PSKIdentity,
/*includePeerToPeer=*/_includePeerToPeer);
if (!parameters) {
GTMLoggerError(@"[GNCNWFramework] Failed to create NW parameters.");
return nil;
}
nw_connection_t connection = nw_connection_create(endpoint, parameters);
nw_connection_set_queue(connection, dispatch_get_main_queue());
nw_connection_set_state_changed_handler(
@@ -373,3 +441,5 @@ static GNCNWFramework *gInstance = nil;
}
@end
NS_ASSUME_NONNULL_END
@@ -16,6 +16,8 @@
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkServerSocket.h"
NS_ASSUME_NONNULL_BEGIN
@interface GNCNWFrameworkServerSocket (Internal)
/**
@@ -28,7 +30,25 @@
* @param[out] error Error that will be populated on failure.
* @return Returns YES when listening has successfully started.
*/
- (BOOL)startListeningWithError:(NSError **_Nullable)error includePeerToPeer:(BOOL)peerToPeer;
- (BOOL)startListeningWithError:(NSError **_Nullable)error
includePeerToPeer:(BOOL)includePeerToPeer;
/**
* Starts listening for inbound connections with a PSK identity and shared secret.
*
* Blocks execution until listening has started or failed.
*
* @param PSKIdentity The PSK identity to use for the connection.
* @param PSKSharedSecret The PSK shared secret to use for the connection.
* @param includePeerToPeer Indicates if the server should be configured for peer-to-peer
* connections.
* @param[out] error Error that will be populated on failure.
* @return Returns YES when listening has successfully started.
*/
- (BOOL)startListeningWithPSKIdentity:(NSData *)PSKIdentify
PSKSharedSecret:(NSData *)PSKSharedSecret
includePeerToPeer:(BOOL)includePeerToPeer
error:(NSError **_Nullable)error;
/**
* Creates a Bonjour service that advertises the listener on the local network.
@@ -37,9 +57,9 @@
* @param serviceType The Bonjour type of the service.
* @param txtRecords The TXT record to advertise with the service.
*/
- (void)startAdvertisingServiceName:(nonnull NSString *)serviceName
serviceType:(nonnull NSString *)serviceType
txtRecords:(nonnull NSDictionary<NSString *, NSString *> *)txtRecords;
- (void)startAdvertisingServiceName:(NSString *)serviceName
serviceType:(NSString *)serviceType
txtRecords:(NSDictionary<NSString *, NSString *> *)txtRecords;
/**
* Removes the Bonjour service advertisement.
@@ -47,3 +67,5 @@
- (void)stopAdvertising;
@end
NS_ASSUME_NONNULL_END
@@ -14,6 +14,8 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class GNCIPv4Address;
@class GNCNWFrameworkSocket;
@@ -22,14 +24,14 @@
/**
* @remark init is not an available initializer.
*/
- (nonnull instancetype)init NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
/**
* Creates a server socket for a given port.
*
* @param port The port of the server socket.
*/
- (nonnull instancetype)initWithPort:(NSInteger)port NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithPort:(NSInteger)port NS_DESIGNATED_INITIALIZER;
/**
* The IPv4 address of the physical network interface.
@@ -62,3 +64,5 @@
- (void)close;
@end
NS_ASSUME_NONNULL_END
@@ -26,13 +26,21 @@
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkError.h"
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkServerSocket+Internal.h"
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkSocket.h"
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWParameters.h"
#import "GoogleToolboxForMac/GTMLogger.h"
NS_ASSUME_NONNULL_BEGIN
@interface GNCNWFrameworkServerSocket ()
@property(nonatomic, readonly) NSMutableArray<nw_connection_t> *pendingConnections;
@property(nonatomic, readonly) NSMutableArray<nw_connection_t> *readyConnections;
- (BOOL)internalStartListeningWithPSKIdentity:(nullable NSData *)PSKIdentify
PSKSharedSecret:(nullable NSData *)PSKSharedSecret
includePeerToPeer:(BOOL)includePeerToPeer
error:(NSError **_Nullable)error;
@end
@implementation GNCNWFrameworkServerSocket {
@@ -81,7 +89,7 @@
return _ipAddress;
}
- (GNCNWFrameworkSocket *)acceptWithError:(NSError **)error {
- (nullable GNCNWFrameworkSocket *)acceptWithError:(NSError **)error {
// Wait until we have a ready connection and listener is in a ready/invalid state.
[_condition lock];
nw_connection_t connection = [self.readyConnections lastObject];
@@ -113,16 +121,108 @@
}
- (BOOL)startListeningWithError:(NSError **)error includePeerToPeer:(BOOL)includePeerToPeer {
// Create a parameters object configured to support TCP. TLS MUST be disabled for Nearby to
// function properly.
nw_parameters_t parameters =
nw_parameters_create_secure_tcp(/*tls*/ NW_PARAMETERS_DISABLE_PROTOCOL,
/*tcp*/ NW_PARAMETERS_DEFAULT_CONFIGURATION);
nw_parameters_set_include_peer_to_peer(parameters, includePeerToPeer);
return [self internalStartListeningWithPSKIdentity:nil
PSKSharedSecret:nil
includePeerToPeer:includePeerToPeer
error:error];
}
// If the server socket port is zero, a random port will be selected. The server socket port will
// be updated to reflect the port it's listening on, once the listener transitions to the ready
// state.
- (BOOL)startListeningWithPSKIdentity:(NSData *)PSKIdentify
PSKSharedSecret:(NSData *)PSKSharedSecret
includePeerToPeer:(BOOL)includePeerToPeer
error:(NSError **_Nullable)error {
return [self internalStartListeningWithPSKIdentity:PSKIdentify
PSKSharedSecret:PSKSharedSecret
includePeerToPeer:includePeerToPeer
error:error];
}
- (void)startAdvertisingServiceName:(NSString *)serviceName
serviceType:(NSString *)serviceType
txtRecords:(NSDictionary<NSString *, NSString *> *)txtRecords {
GTMLoggerInfo(@"[GNCNWFrameworkServerSocket] Start advertising {serviceName:%@, "
@"serviceType: %@}",
serviceName, serviceType);
nw_advertise_descriptor_t advertiseDescriptor = nw_advertise_descriptor_create_bonjour_service(
[serviceName UTF8String], [serviceType UTF8String], /*domain=*/nil);
nw_txt_record_t txtRecord = nw_txt_record_create_dictionary();
for (NSString *key in txtRecords) {
NSString *recordValue = [txtRecords objectForKey:key];
NSData *encodedRecord = [recordValue dataUsingEncoding:NSUTF8StringEncoding];
if (!nw_txt_record_set_key(txtRecord, [key UTF8String], encodedRecord.bytes,
encodedRecord.length)) {
GTMLoggerError(@"[GNCNWFrameworkServerSocket] Failed to set text record key: %@, value: %@",
key, recordValue);
continue;
}
GTMLoggerDebug(@"[GNCNWFrameworkServerSocket] Text record {key: "
@"%@, value: %@}",
key, recordValue);
}
nw_advertise_descriptor_set_txt_record_object(advertiseDescriptor, txtRecord);
nw_listener_set_advertise_descriptor(_listener, advertiseDescriptor);
}
- (void)stopAdvertising {
nw_listener_set_advertise_descriptor(_listener, nil);
}
/**
* Attemps to find an IPv4 hostname for a Wi-Fi/Ethernet interface.
*
* Returns the IP address as a 4 byte string. If not available, this returns an empty string to
* align with the Windows implementation.
*/
+ (GNCIPv4Address *)lookupIpAddress {
GNCIPv4Address *result = [GNCIPv4Address addressFromFourByteInt:0];
struct ifaddrs *ifaddr;
if (getifaddrs(&ifaddr) == 0) {
for (struct ifaddrs *cur = ifaddr; cur != NULL; cur = cur->ifa_next) {
struct sockaddr *address = cur->ifa_addr;
if (address == nil) {
continue;
}
// Filter out any interfaces that aren't IPv4.
if (address->sa_family != AF_INET) {
continue;
}
// Filter out any interfaces that aren't en0 (Wi-Fi/Ethernet).
NSString *interfaceName = @(cur->ifa_name);
if (![interfaceName isEqualToString:@"en0"]) {
continue;
}
uint32_t host = ((struct sockaddr_in *)address)->sin_addr.s_addr;
result = [GNCIPv4Address addressFromFourByteInt:host];
break;
}
freeifaddrs(ifaddr);
}
return result;
}
// MARK: - Private Methods (Implementation)
- (BOOL)internalStartListeningWithPSKIdentity:(nullable NSData *)PSKIdentify
PSKSharedSecret:(nullable NSData *)PSKSharedSecret
includePeerToPeer:(BOOL)includePeerToPeer
error:(NSError **_Nullable)error {
nw_parameters_t parameters =
(PSKIdentify == nil || PSKSharedSecret == nil)
? GNCBuildNonTLSParameters(/*includePeerToPeer=*/includePeerToPeer)
: GNCBuildTLSParameters(/*PSK=*/PSKSharedSecret, /*identity=*/PSKIdentify,
/*includePeerToPeer=*/includePeerToPeer);
if (!parameters) {
GTMLoggerError(@"[GNCNWFrameworkServerSocket] Failed to create NW parameters.");
return NO;
}
// If the server socket port is zero, a random port will be selected. The server socket port
// will be updated to reflect the port it's listening on, once the listener transitions to the
// ready state.
if (self.port == 0) {
_listener = nw_listener_create(parameters);
} else {
@@ -215,74 +315,6 @@
}
}
- (void)startAdvertisingServiceName:(NSString *)serviceName
serviceType:(NSString *)serviceType
txtRecords:(NSDictionary<NSString *, NSString *> *)txtRecords {
GTMLoggerInfo(@"[GNCNWFrameworkServerSocket] Start advertising {serviceName:%@, "
@"serviceType: %@}",
serviceName, serviceType);
nw_advertise_descriptor_t advertiseDescriptor = nw_advertise_descriptor_create_bonjour_service(
[serviceName UTF8String], [serviceType UTF8String], /*domain=*/nil);
nw_txt_record_t txtRecord = nw_txt_record_create_dictionary();
for (NSString *key in txtRecords) {
NSString *recordValue = [txtRecords objectForKey:key];
nw_txt_record_set_key(txtRecord, [key UTF8String], [recordValue UTF8String],
[recordValue length]);
GTMLoggerDebug(@"[GNCNWFrameworkServerSocket] Text record {key: "
@"%@, value: %@}",
key, recordValue);
}
nw_advertise_descriptor_set_txt_record_object(advertiseDescriptor, txtRecord);
nw_listener_set_advertise_descriptor(_listener, advertiseDescriptor);
}
- (void)stopAdvertising {
nw_listener_set_advertise_descriptor(_listener, nil);
}
/**
* Attemps to find an IPv4 hostname for a Wi-Fi/Ethernet interface.
*
* Returns the IP address as a 4 byte string. If not available, this returns an empty string to
* align with the Windows implementation.
*/
+ (GNCIPv4Address *)lookupIpAddress {
struct ifaddrs *ifaddr;
// Note: The data returned by `getifaddrs()` is dynamically allocated and should be freed using
// `freeifaddrs()` when no longer needed.
//
// See: https://linux.die.net/man/3/getifaddrs
if (getifaddrs(&ifaddr) == -1) {
return [GNCIPv4Address addressFromFourByteInt:0];
}
// Walk through linked list, maintaining head pointer so we can free list later.
for (struct ifaddrs *cur = ifaddr; cur != nil; cur = cur->ifa_next) {
struct sockaddr *address = cur->ifa_addr;
if (address == nil) {
continue;
}
// Filter out any interfaces that aren't IPv4.
if (address->sa_family != AF_INET) {
continue;
}
// Filter out any interfaces that aren't en0 (Wi-Fi/Ethernet).
NSString *interfaceName = @(cur->ifa_name);
if (![interfaceName isEqualToString:@"en0"]) {
continue;
}
uint32_t host = ((struct sockaddr_in *)address)->sin_addr.s_addr;
freeifaddrs(ifaddr);
return [GNCIPv4Address addressFromFourByteInt:host];
}
freeifaddrs(ifaddr);
return [GNCIPv4Address addressFromFourByteInt:0];
}
@end
NS_ASSUME_NONNULL_END