Skip to content

Commit

Permalink
Fixed IPv6 incompatibility in PSWebSocketServer
Browse files Browse the repository at this point in the history
  • Loading branch information
snej committed Aug 18, 2016
1 parent 14d04c0 commit 77e50c6
Showing 1 changed file with 56 additions and 15 deletions.
71 changes: 56 additions & 15 deletions PocketSocket/PSWebSocketServer.m
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ @interface PSWebSocketServer() <NSStreamDelegate, PSWebSocketDelegate> {

NSArray *_SSLCertificates;
BOOL _secure;


bool _isV6Address;
NSData *_addrData;
CFSocketContext _socketContext;

Expand Down Expand Up @@ -100,6 +101,8 @@ + (instancetype)serverWithHost:(NSString *)host port:(NSUInteger)port {
+ (instancetype)serverWithHost:(NSString *)host port:(NSUInteger)port SSLCertificates:(NSArray *)SSLCertificates {
return [[self alloc] initWithHost:host port:port SSLCertificates:SSLCertificates];
}

// if "host" is equal to nil or "0.0.0.0" or "::" or any format which means ANY ADDRESS, it will bind to ANY for both IPv4 and IPv6.
- (instancetype)initWithHost:(NSString *)host port:(NSUInteger)port SSLCertificates:(NSArray *)SSLCertificates {
NSParameterAssert(port);
if((self = [super init])) {
Expand All @@ -109,23 +112,61 @@ - (instancetype)initWithHost:(NSString *)host port:(NSUInteger)port SSLCertifica
_SSLCertificates = [SSLCertificates copy];
_secure = (_SSLCertificates != nil);

// create addr data
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
if(host && ![host isEqualToString:@"0.0.0.0"]) {
addr.sin_addr.s_addr = inet_addr(host.UTF8String);
if(!addr.sin_addr.s_addr) {
[NSException raise:@"Invalid host" format:@"Could not formulate internet address from host: %@", host];
return nil;
bool isAnyAddress = false;
bool isV6Address = false;
if (host) {
if ([host rangeOfString:@":"].location != NSNotFound) {
isV6Address = true;
if ([host isEqualToString:@"::"]) {
isAnyAddress = true;
}
} else {
if ([host isEqualToString:@"0.0.0.0"]) {
isAnyAddress = true;
}
}
} else {
addr.sin_addr.s_addr = htonl(INADDR_ANY);
isAnyAddress = true;
}
addr.sin_port = htons(port);
_addrData = [NSData dataWithBytes:&addr length:sizeof(addr)];

// create address data
if (isAnyAddress || isV6Address ) { // IPv6 address or address ANY
struct sockaddr_in6 addr;
memset(&addr, 0, sizeof(addr));
addr.sin6_len = sizeof(addr);
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(port);

if (isAnyAddress) {
// A socket at in6addr_any accepts connections from IPv4 addresses too.
addr.sin6_addr = in6addr_any;
} else {
// Note: "0::0:0" or "0:0:0:0:0:0:0:0" will also be bound to ANY(*) even isAnyAddress is false
int ret = inet_pton(AF_INET6, host.UTF8String, &(addr.sin6_addr));
if(ret <= 0) { // only 1 means OK, either 0 or -1 means error
[NSException raise:@"Invalid host" format:@"Could not formulate internet address from host: %@", host];
return nil;
}
}

_addrData = [NSData dataWithBytes:&addr length:sizeof(addr)];
} else { // IPv4 address
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);

addr.sin_addr.s_addr = inet_addr(host.UTF8String); // "host" is non-null here
if(addr.sin_addr.s_addr == INADDR_NONE) {
[NSException raise:@"Invalid host" format:@"Could not formulate internet address from host: %@", host];
return nil;
}

_addrData = [NSData dataWithBytes:&addr length:sizeof(addr)];
}
_isV6Address = isV6Address;

// create socket context
_socketContext = (CFSocketContext){0, (__bridge void *)self, NULL, NULL, NULL};

Expand Down Expand Up @@ -160,7 +201,7 @@ - (void)connect:(BOOL)silent {

// create socket
_socket = CFSocketCreate(kCFAllocatorDefault,
PF_INET,
_isV6Address ? PF_INET6 : PF_INET,
SOCK_STREAM,
IPPROTO_TCP,
kCFSocketAcceptCallBack,
Expand Down

0 comments on commit 77e50c6

Please sign in to comment.