Implemented a UDPConnection class as a wrapper around a UDP implementation
This commit is contained in:
@@ -10,6 +10,8 @@
|
||||
0434EAB818A3C318E433338A /* Pods_BitTorrentTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A8680B0EA21B5A20E03DEE6D /* Pods_BitTorrentTests.framework */; };
|
||||
34D4082D0E0A2C899857DC89 /* Pods_BitTorrent.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B47AE52A67ECE61259FCDAD /* Pods_BitTorrent.framework */; };
|
||||
867491B4C409A91D4D72CCE7 /* Pods_BitTorrent_BitTorrentExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D97F9FB9F74204574FF6840B /* Pods_BitTorrent_BitTorrentExample.framework */; };
|
||||
B50B24F71F0A553F00C23E7C /* UDPConnectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50B24F61F0A553B00C23E7C /* UDPConnectionTests.swift */; };
|
||||
B50B24F91F0A554A00C23E7C /* UDPConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50B24F81F0A554A00C23E7C /* UDPConnection.swift */; };
|
||||
B537CF061F03148B0084089B /* HTTPConnectionStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = B537CF051F03148B0084089B /* HTTPConnectionStub.swift */; };
|
||||
B537CF461F031AD20084089B /* TestText.torrent in Resources */ = {isa = PBXBuildFile; fileRef = B5E9B0E31F02F9E700EF58E3 /* TestText.torrent */; };
|
||||
B537CF491F031C3D0084089B /* BEncode.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B54D0C1A1CA53983004343BD /* BEncode.framework */; };
|
||||
@@ -25,6 +27,9 @@
|
||||
B5530DB51F03063E00F71CCD /* HTTPConnectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5530DB41F03063E00F71CCD /* HTTPConnectionTests.swift */; };
|
||||
B55317DC1F02FC4D00909ADF /* TorrentHTTPTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55317DB1F02FC4D00909ADF /* TorrentHTTPTracker.swift */; };
|
||||
B55317E01F02FE1500909ADF /* URLEncodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55317DF1F02FE1500909ADF /* URLEncodeTests.swift */; };
|
||||
B556D5AE1F0BA1FD00277B8D /* GCDAsyncUdpSocketStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = B551C9541F0B9D3D004115CB /* GCDAsyncUdpSocketStub.swift */; };
|
||||
B558F4831F0A647D00438BB4 /* InternetProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B558F4821F0A647D00438BB4 /* InternetProtocol.swift */; };
|
||||
B558F4851F0A73D000438BB4 /* InternetProtocolTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B558F4841F0A73D000438BB4 /* InternetProtocolTests.swift */; };
|
||||
B56A8A071C83539300426AC8 /* TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56A8A061C83539300426AC8 /* TestHelpers.swift */; };
|
||||
B585AB781C3833450093FA41 /* BitTorrent.h in Headers */ = {isa = PBXBuildFile; fileRef = B585AB771C3833450093FA41 /* BitTorrent.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
B585AB7F1C3833450093FA41 /* BitTorrent.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B585AB741C3833450093FA41 /* BitTorrent.framework */; };
|
||||
@@ -112,6 +117,8 @@
|
||||
8B47AE52A67ECE61259FCDAD /* Pods_BitTorrent.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BitTorrent.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
99A91AD4708288B297A8B700 /* Pods-BitTorrentTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BitTorrentTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-BitTorrentTests/Pods-BitTorrentTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
A8680B0EA21B5A20E03DEE6D /* Pods_BitTorrentTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BitTorrentTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B50B24F61F0A553B00C23E7C /* UDPConnectionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UDPConnectionTests.swift; sourceTree = "<group>"; };
|
||||
B50B24F81F0A554A00C23E7C /* UDPConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UDPConnection.swift; sourceTree = "<group>"; };
|
||||
B537CF051F03148B0084089B /* HTTPConnectionStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = HTTPConnectionStub.swift; path = "HTTP Networking/HTTPConnectionStub.swift"; sourceTree = "<group>"; };
|
||||
B54D0C141CA53983004343BD /* BEncode.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = BEncode.xcodeproj; path = Submodules/BEncodeSwift/BEncode.xcodeproj; sourceTree = "<group>"; };
|
||||
B54D0C231CA56A22004343BD /* TorrentMetaInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TorrentMetaInfo.swift; path = Models/TorrentMetaInfo.swift; sourceTree = "<group>"; };
|
||||
@@ -123,10 +130,13 @@
|
||||
B54D0C721CA5879E004343BD /* iphoneos.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = iphoneos.modulemap; sourceTree = "<group>"; };
|
||||
B54D0C731CA587A9004343BD /* iphonesimulator.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = iphonesimulator.modulemap; sourceTree = "<group>"; };
|
||||
B54D0C741CA587B5004343BD /* macosx.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = macosx.modulemap; sourceTree = "<group>"; };
|
||||
B551C9541F0B9D3D004115CB /* GCDAsyncUdpSocketStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GCDAsyncUdpSocketStub.swift; sourceTree = "<group>"; };
|
||||
B5530DB11F03063300F71CCD /* HTTPConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = HTTPConnection.swift; path = "HTTP Networking/HTTPConnection.swift"; sourceTree = "<group>"; };
|
||||
B5530DB41F03063E00F71CCD /* HTTPConnectionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = HTTPConnectionTests.swift; path = "HTTP Networking/HTTPConnectionTests.swift"; sourceTree = "<group>"; };
|
||||
B55317DB1F02FC4D00909ADF /* TorrentHTTPTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TorrentHTTPTracker.swift; path = Tracker/TorrentHTTPTracker.swift; sourceTree = "<group>"; };
|
||||
B55317DF1F02FE1500909ADF /* URLEncodeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = URLEncodeTests.swift; path = "HTTP Networking/URLEncodeTests.swift"; sourceTree = "<group>"; };
|
||||
B558F4821F0A647D00438BB4 /* InternetProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternetProtocol.swift; sourceTree = "<group>"; };
|
||||
B558F4841F0A73D000438BB4 /* InternetProtocolTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternetProtocolTests.swift; sourceTree = "<group>"; };
|
||||
B56A8A061C83539300426AC8 /* TestHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TestHelpers.swift; path = Utilities/TestHelpers.swift; sourceTree = "<group>"; };
|
||||
B585AB741C3833450093FA41 /* BitTorrent.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BitTorrent.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B585AB771C3833450093FA41 /* BitTorrent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BitTorrent.h; sourceTree = "<group>"; };
|
||||
@@ -204,6 +214,18 @@
|
||||
name = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B50B24F01F0A54C100C23E7C /* UDP Networking */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B558F4821F0A647D00438BB4 /* InternetProtocol.swift */,
|
||||
B558F4841F0A73D000438BB4 /* InternetProtocolTests.swift */,
|
||||
B50B24F81F0A554A00C23E7C /* UDPConnection.swift */,
|
||||
B551C9541F0B9D3D004115CB /* GCDAsyncUdpSocketStub.swift */,
|
||||
B50B24F61F0A553B00C23E7C /* UDPConnectionTests.swift */,
|
||||
);
|
||||
path = "UDP Networking";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B54D0C131CA53979004343BD /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -306,6 +328,7 @@
|
||||
B54D0C251CA56A25004343BD /* Models */,
|
||||
B54D0C291CA5785F004343BD /* Utilities */,
|
||||
B5E9778F1CAFB2090038EBE7 /* HTTP Networking */,
|
||||
B50B24F01F0A54C100C23E7C /* UDP Networking */,
|
||||
B585AB791C3833450093FA41 /* Info.plist */,
|
||||
);
|
||||
path = BitTorrent;
|
||||
@@ -712,10 +735,12 @@
|
||||
B5F81E491F0436D600B25C70 /* TorrentPeer.swift in Sources */,
|
||||
B55317DC1F02FC4D00909ADF /* TorrentHTTPTracker.swift in Sources */,
|
||||
B5E977961CAFB46B0038EBE7 /* String+URLEncode.swift in Sources */,
|
||||
B558F4831F0A647D00438BB4 /* InternetProtocol.swift in Sources */,
|
||||
B54D0C7A1CA69FAD004343BD /* TorrentMetaInfo.swift in Sources */,
|
||||
B5F81E4B1F04399800B25C70 /* TorrentTrackerResponse.swift in Sources */,
|
||||
B54D0C7B1CA69FD8004343BD /* Data+sha1.swift in Sources */,
|
||||
B5530DB21F03063300F71CCD /* HTTPConnection.swift in Sources */,
|
||||
B50B24F91F0A554A00C23E7C /* UDPConnection.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -725,11 +750,14 @@
|
||||
files = (
|
||||
B537CF061F03148B0084089B /* HTTPConnectionStub.swift in Sources */,
|
||||
B5BD7FD61F03032400621BC2 /* TorrentHTTPTrackerTests.swift in Sources */,
|
||||
B556D5AE1F0BA1FD00277B8D /* GCDAsyncUdpSocketStub.swift in Sources */,
|
||||
B5530DB51F03063E00F71CCD /* HTTPConnectionTests.swift in Sources */,
|
||||
B56A8A071C83539300426AC8 /* TestHelpers.swift in Sources */,
|
||||
B585AB841C3833450093FA41 /* BitTorrentTests.swift in Sources */,
|
||||
B54D0C271CA56ADB004343BD /* TorrentMetaInfoTests.swift in Sources */,
|
||||
B55317E01F02FE1500909ADF /* URLEncodeTests.swift in Sources */,
|
||||
B558F4851F0A73D000438BB4 /* InternetProtocolTests.swift in Sources */,
|
||||
B50B24F71F0A553F00C23E7C /* UDPConnectionTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
//
|
||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||
//
|
||||
|
||||
#import <CommonCrypto/CommonCrypto.h>
|
||||
#import "GCDAsyncSocket.h"
|
||||
#import "GCDAsyncUdpSocket.h"
|
||||
#import "InternetProtocol.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// GCDAsyncUdpSocketProtocol.swift
|
||||
// BitTorrent
|
||||
//
|
||||
// Created by Ben Davis on 04/07/2017.
|
||||
// Copyright © 2017 Ben Davis. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CocoaAsyncSocket
|
||||
|
||||
class GCDAsyncUdpSocketStub: GCDAsyncUdpSocket {
|
||||
|
||||
weak var _delegate: GCDAsyncUdpSocketDelegate?
|
||||
|
||||
override func delegate() -> GCDAsyncUdpSocketDelegate? {
|
||||
return _delegate
|
||||
}
|
||||
|
||||
override func setDelegate(_ delegate: GCDAsyncUdpSocketDelegate?) {
|
||||
_delegate = delegate
|
||||
}
|
||||
|
||||
var bindToPortCalled = false
|
||||
var bindToPortParameter: UInt16?
|
||||
override func bind(toPort port: UInt16) throws {
|
||||
bindToPortCalled = true
|
||||
bindToPortParameter = port
|
||||
}
|
||||
|
||||
var beginReceivingCalled = false
|
||||
override func beginReceiving() throws {
|
||||
beginReceivingCalled = true
|
||||
}
|
||||
|
||||
var _delegateQueue: DispatchQueue?
|
||||
|
||||
override func delegateQueue() -> DispatchQueue? {
|
||||
return _delegateQueue
|
||||
}
|
||||
|
||||
override func synchronouslySetDelegateQueue(_ delegateQueue: DispatchQueue?) {
|
||||
_delegateQueue = delegateQueue
|
||||
}
|
||||
|
||||
var closeCalled = false
|
||||
override func close() {
|
||||
closeCalled = true
|
||||
}
|
||||
|
||||
var sendCalled = false
|
||||
var sendParameters: (data: Data, host: String, port: UInt16, timeout: TimeInterval, tag: Int)?
|
||||
override func send(_ data: Data, toHost host: String, port: UInt16, withTimeout timeout: TimeInterval, tag: Int) {
|
||||
sendCalled = true
|
||||
sendParameters = (data, host, port, timeout, tag)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// InternetProtocol.h
|
||||
// BitTorrent
|
||||
//
|
||||
// Created by Ben Davis on 16/11/2014.
|
||||
// Copyright (c) 2014 Ben Davis. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface InternetProtocol : NSObject
|
||||
|
||||
+ (NSString *)getIPAddress:(BOOL)preferIPv4;
|
||||
+ (NSDictionary *)getIPAddresses;
|
||||
|
||||
+ (NSString*)ipAddressOfHostname:(NSString*)hostName;
|
||||
|
||||
+ (NSString*)ipAddressFromSockAddrData:(NSData*)data;
|
||||
+ (uint16_t)portFromSockAddrData:(NSData*)data;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,105 @@
|
||||
//
|
||||
// InternetProtocol.m
|
||||
// BitTorrent
|
||||
//
|
||||
// Created by Ben Davis on 16/11/2014.
|
||||
// Copyright (c) 2014 Ben Davis. All rights reserved.
|
||||
//
|
||||
|
||||
#import "InternetProtocol.h"
|
||||
#include <ifaddrs.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#define IOS_CELLULAR @"pdp_ip0"
|
||||
#define IOS_WIFI @"en0"
|
||||
#define IP_ADDR_IPv4 @"ipv4"
|
||||
#define IP_ADDR_IPv6 @"ipv6"
|
||||
|
||||
@implementation InternetProtocol
|
||||
|
||||
+ (NSString *)getIPAddress:(BOOL)preferIPv4 {
|
||||
NSArray *searchArray = preferIPv4 ?
|
||||
@[ IOS_WIFI @"/" IP_ADDR_IPv4, IOS_WIFI @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6 ] :
|
||||
@[ IOS_WIFI @"/" IP_ADDR_IPv6, IOS_WIFI @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4 ] ;
|
||||
|
||||
NSDictionary *addresses = [self getIPAddresses];
|
||||
NSLog(@"addresses: %@", addresses);
|
||||
|
||||
__block NSString *address;
|
||||
[searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop)
|
||||
{
|
||||
address = addresses[key];
|
||||
if(address) *stop = YES;
|
||||
} ];
|
||||
return address ? address : @"0.0.0.0";
|
||||
}
|
||||
|
||||
+ (NSDictionary *)getIPAddresses {
|
||||
NSMutableDictionary *addresses = [NSMutableDictionary dictionaryWithCapacity:8];
|
||||
|
||||
// retrieve the current interfaces - returns 0 on success
|
||||
struct ifaddrs *interfaces;
|
||||
if(!getifaddrs(&interfaces)) {
|
||||
// Loop through linked list of interfaces
|
||||
struct ifaddrs *interface;
|
||||
for(interface=interfaces; interface; interface=interface->ifa_next) {
|
||||
if(!(interface->ifa_flags & IFF_UP) /* || (interface->ifa_flags & IFF_LOOPBACK) */ ) {
|
||||
continue; // deeply nested code harder to read
|
||||
}
|
||||
const struct sockaddr_in *addr = (const struct sockaddr_in*)interface->ifa_addr;
|
||||
char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];
|
||||
if(addr && (addr->sin_family==AF_INET || addr->sin_family==AF_INET6)) {
|
||||
NSString *name = [NSString stringWithUTF8String:interface->ifa_name];
|
||||
NSString *type;
|
||||
if(addr->sin_family == AF_INET) {
|
||||
if(inet_ntop(AF_INET, &addr->sin_addr, addrBuf, INET_ADDRSTRLEN)) {
|
||||
type = IP_ADDR_IPv4;
|
||||
}
|
||||
} else {
|
||||
const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)interface->ifa_addr;
|
||||
if(inet_ntop(AF_INET6, &addr6->sin6_addr, addrBuf, INET6_ADDRSTRLEN)) {
|
||||
type = IP_ADDR_IPv6;
|
||||
}
|
||||
}
|
||||
if(type) {
|
||||
NSString *key = [NSString stringWithFormat:@"%@/%@", name, type];
|
||||
addresses[key] = [NSString stringWithUTF8String:addrBuf];
|
||||
}
|
||||
}
|
||||
}
|
||||
// Free memory
|
||||
freeifaddrs(interfaces);
|
||||
}
|
||||
return [addresses count] ? addresses : nil;
|
||||
}
|
||||
|
||||
+(NSString *)ipAddressOfHostname:(NSString *)hostName {
|
||||
struct hostent *host_entry = gethostbyname([hostName cStringUsingEncoding:NSASCIIStringEncoding]);
|
||||
char *buff;
|
||||
if (host_entry != NULL) {
|
||||
buff = inet_ntoa(*((struct in_addr *)host_entry->h_addr_list[0]));
|
||||
NSString *ip = [NSString stringWithUTF8String:buff];
|
||||
return ip;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSString*)ipAddressFromSockAddrData:(NSData*)data {
|
||||
struct sockaddr_in * socketAddress = (struct sockaddr_in *)data.bytes;
|
||||
struct in_addr ipAsStruct = ((struct sockaddr_in)*(socketAddress)).sin_addr;
|
||||
char *buff;
|
||||
buff = inet_ntoa(ipAsStruct);
|
||||
NSString *ip = [NSString stringWithUTF8String:buff];
|
||||
return ip;
|
||||
}
|
||||
|
||||
+ (uint16_t)portFromSockAddrData:(NSData*)data {
|
||||
struct sockaddr_in * socketAddressPtr = (struct sockaddr_in *)data.bytes;
|
||||
struct sockaddr_in socketAddress = ((struct sockaddr_in)*(socketAddressPtr));
|
||||
return socketAddress.sin_port;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,197 @@
|
||||
//
|
||||
// InternetProtocol.swift
|
||||
// BitTorrent
|
||||
//
|
||||
// Created by Ben Davis on 03/07/2017.
|
||||
// Copyright © 2017 Ben Davis. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let IOS_CELLULAR_INTERFACE_NAME = "pdp_ip0"
|
||||
let IOS_WIFI_INTERFACE_NAME = "en0"
|
||||
let IP_ADDR_IPv4_INTERFACE_NAME = "ipv4"
|
||||
let IP_ADDR_IPv6_INTERFACE_NAME = "ipv6"
|
||||
|
||||
func ipAddress(fromSockAddrData data: Data) -> String? {
|
||||
let socketAddress = data.withUnsafeBytes() { (pointer: UnsafePointer<sockaddr_in>) in
|
||||
return pointer.pointee
|
||||
}
|
||||
guard let resultCString = inet_ntoa(socketAddress.sin_addr) else {
|
||||
return nil
|
||||
}
|
||||
return String(cString: resultCString)
|
||||
}
|
||||
|
||||
func port(fromSockAddrData data: Data) -> UInt16 {
|
||||
let socketAddress = data.withUnsafeBytes() { (pointer: UnsafePointer<sockaddr_in>) in
|
||||
return pointer.pointee
|
||||
}
|
||||
return socketAddress.sin_port
|
||||
}
|
||||
|
||||
// + (NSString*)ipAddressFromSockAddrData:(NSData*)data {
|
||||
// struct sockaddr_in * socketAddress = (struct sockaddr_in *)data.bytes;
|
||||
// struct in_addr ipAsStruct = ((struct sockaddr_in)*(socketAddress)).sin_addr;
|
||||
// char *buff;
|
||||
// buff = inet_ntoa(ipAsStruct);
|
||||
// NSString *ip = [NSString stringWithUTF8String:buff];
|
||||
// return ip;
|
||||
// }
|
||||
//
|
||||
// + (uint16_t)portFromSockAddrData:(NSData*)data {
|
||||
// struct sockaddr_in * socketAddressPtr = (struct sockaddr_in *)data.bytes;
|
||||
// struct sockaddr_in socketAddress = ((struct sockaddr_in)*(socketAddressPtr));
|
||||
// return socketAddress.sin_port;
|
||||
//}
|
||||
|
||||
func getIPAddress(of hostname: String) -> String? {
|
||||
|
||||
guard let hostnameCString = hostname.cString(using: .ascii),
|
||||
let hostEntry = gethostbyname(hostnameCString)?.pointee,
|
||||
let hostAddressList = hostEntry.h_addr_list?.pointee else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let firstHostAddress = hostAddressList.withMemoryRebound(to: in_addr.self, capacity: 1) { $0.pointee }
|
||||
let firstHostAddressCString = inet_ntoa(firstHostAddress)!
|
||||
return String(cString: firstHostAddressCString)
|
||||
}
|
||||
|
||||
func getLocalIPAddress(preferIPv4: Bool = true) -> String? {
|
||||
|
||||
// Prefer wifi over cellular
|
||||
let searchArray = preferIPv4 ?
|
||||
[
|
||||
IOS_WIFI_INTERFACE_NAME + "/" + IP_ADDR_IPv4_INTERFACE_NAME,
|
||||
IOS_WIFI_INTERFACE_NAME + "/" + IP_ADDR_IPv6_INTERFACE_NAME,
|
||||
IOS_CELLULAR_INTERFACE_NAME + "/" + IP_ADDR_IPv4_INTERFACE_NAME,
|
||||
IOS_CELLULAR_INTERFACE_NAME + "/" + IP_ADDR_IPv6_INTERFACE_NAME,
|
||||
] :
|
||||
[
|
||||
IOS_WIFI_INTERFACE_NAME + "/" + IP_ADDR_IPv6_INTERFACE_NAME,
|
||||
IOS_WIFI_INTERFACE_NAME + "/" + IP_ADDR_IPv4_INTERFACE_NAME,
|
||||
IOS_CELLULAR_INTERFACE_NAME + "/" + IP_ADDR_IPv6_INTERFACE_NAME,
|
||||
IOS_CELLULAR_INTERFACE_NAME + "/" + IP_ADDR_IPv4_INTERFACE_NAME,
|
||||
]
|
||||
|
||||
let addresses = getLocalIPAddresses()
|
||||
|
||||
for searchItem in searchArray {
|
||||
if let result = addresses[searchItem] {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLocalIPAddresses() -> [String: String] {
|
||||
|
||||
var addresses: [String: String] = [:]
|
||||
|
||||
// Get list of all network interfaces on the local machine:
|
||||
var ifaddrsPointer : UnsafeMutablePointer<ifaddrs>?
|
||||
guard getifaddrs(&ifaddrsPointer) == 0, let ifaddrsList = ifaddrsPointer?.pointee else {
|
||||
return [:]
|
||||
}
|
||||
|
||||
// For each network interface ...
|
||||
for ifaddrs in ifaddrsList {
|
||||
if !ifaddrs.isUpAndRunning || ifaddrs.isLoopbackNet {
|
||||
continue
|
||||
}
|
||||
|
||||
if ifaddrs.isIpv4 || ifaddrs.isIpv6 {
|
||||
if let addressString = ifaddrs.convertToIPString(), let name = ifaddrs.nameAndTypeString() {
|
||||
addresses[name] = addressString
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs(ifaddrsPointer)
|
||||
|
||||
return addresses
|
||||
}
|
||||
|
||||
public class ifaddrsIterator: IteratorProtocol {
|
||||
public typealias Element = ifaddrs
|
||||
|
||||
var nextElement: ifaddrs?
|
||||
|
||||
init(first: ifaddrs) {
|
||||
nextElement = first
|
||||
}
|
||||
|
||||
public func next() -> ifaddrs? {
|
||||
let result = nextElement
|
||||
nextElement = result?.ifa_next?.pointee
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
extension ifaddrs: Sequence {
|
||||
public func makeIterator() -> ifaddrsIterator {
|
||||
return ifaddrsIterator(first: self)
|
||||
}
|
||||
}
|
||||
|
||||
extension ifaddrs {
|
||||
|
||||
var name: String {
|
||||
return String(cString: ifa_name)
|
||||
}
|
||||
|
||||
var isIpv4: Bool {
|
||||
let addr = ifa_addr.pointee
|
||||
return addr.sa_family == UInt8(AF_INET)
|
||||
}
|
||||
|
||||
var isIpv6: Bool {
|
||||
let addr = ifa_addr.pointee
|
||||
return addr.sa_family == UInt8(AF_INET6)
|
||||
}
|
||||
|
||||
var isUpAndRunning: Bool {
|
||||
let flags = Int32(ifa_flags)
|
||||
let upAndRunningFlags = (IFF_UP|IFF_RUNNING)
|
||||
return (flags & upAndRunningFlags) == upAndRunningFlags
|
||||
}
|
||||
|
||||
var isLoopbackNet: Bool {
|
||||
let flags = Int32(ifa_flags)
|
||||
return (flags & IFF_LOOPBACK) == IFF_LOOPBACK
|
||||
}
|
||||
|
||||
func convertToIPString() -> String? {
|
||||
return ifa_addr.pointee.toString()
|
||||
}
|
||||
|
||||
func nameAndTypeString() -> String? {
|
||||
|
||||
guard isIpv4 || isIpv6 else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return "\(name) - " + (isIpv4 ? IP_ADDR_IPv4_INTERFACE_NAME : IP_ADDR_IPv6_INTERFACE_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
extension sockaddr {
|
||||
|
||||
func toString() -> String? {
|
||||
|
||||
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
|
||||
var addr = self
|
||||
|
||||
guard getnameinfo(&addr,
|
||||
socklen_t(addr.sa_len),
|
||||
&hostname,
|
||||
socklen_t(hostname.count),
|
||||
nil,
|
||||
socklen_t(0),
|
||||
NI_NUMERICHOST) == 0 else { return nil }
|
||||
|
||||
return String(validatingUTF8: hostname)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// InternetProtocolTests.swift
|
||||
// BitTorrentTests
|
||||
//
|
||||
// Created by Ben Davis on 03/07/2017.
|
||||
// Copyright © 2017 Ben Davis. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import BitTorrent
|
||||
|
||||
class InternetProtocolTests: XCTestCase {
|
||||
|
||||
func test_canDecodeLocalhost() {
|
||||
let result = getIPAddress(of: "localhost")
|
||||
XCTAssertNotNil(result)
|
||||
XCTAssertEqual(result!, "127.0.0.1")
|
||||
}
|
||||
|
||||
func test_invalidHostnameReturnsNil() {
|
||||
let result = getIPAddress(of: "asldfjhablskhdbj")
|
||||
XCTAssertNil(result)
|
||||
}
|
||||
|
||||
func test_nonAsciiHostnameReturnsNil() {
|
||||
let result = getIPAddress(of: "🙁")
|
||||
XCTAssertNil(result)
|
||||
}
|
||||
|
||||
func test_canDecodeGoogle() {
|
||||
let result = getIPAddress(of: "google.com")
|
||||
XCTAssertNotNil(result)
|
||||
}
|
||||
|
||||
func test_canDecodeIPv4AddressFromData() {
|
||||
let data = Data(bytes: [16,2,122,105,127,0,0,1,0,0,0,0,0,0,0,0])
|
||||
let result = ipAddress(fromSockAddrData: data)
|
||||
XCTAssertNotNil(result)
|
||||
XCTAssertEqual(result, "127.0.0.1")
|
||||
}
|
||||
|
||||
func test_canDecodeSocketPortFromData() {
|
||||
let data = Data(bytes: [16,2,122,105,127,0,0,1,0,0,0,0,0,0,0,0])
|
||||
let result = port(fromSockAddrData: data)
|
||||
XCTAssertNotNil(result)
|
||||
XCTAssertEqual(result, 27002)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// UDPConnection.swift
|
||||
// BitTorrent
|
||||
//
|
||||
// Created by Ben Davis on 03/07/2017.
|
||||
// Copyright © 2017 Ben Davis. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CocoaAsyncSocket
|
||||
|
||||
protocol UDPConnectionDelegate: class {
|
||||
func udpConnection(_ sender: UDPConnection, receivedData data: Data, fromHost host: String)
|
||||
}
|
||||
|
||||
/// This class is a thin wrapper around the socket library to protect against changes
|
||||
/// in its interface, and to allow me to replace CocoaAsyncSocket with a swift framework
|
||||
/// one day.
|
||||
class UDPConnection: NSObject {
|
||||
|
||||
weak var delegate: UDPConnectionDelegate?
|
||||
|
||||
private let socket: GCDAsyncUdpSocket
|
||||
|
||||
// Designated init for testing
|
||||
init(socket: GCDAsyncUdpSocket) {
|
||||
self.socket = socket
|
||||
super.init()
|
||||
socket.setDelegate(self)
|
||||
socket.synchronouslySetDelegateQueue(.main)
|
||||
}
|
||||
|
||||
// Useful init which should be used
|
||||
override convenience init() {
|
||||
self.init(socket: GCDAsyncUdpSocket())
|
||||
}
|
||||
|
||||
deinit {
|
||||
socket.close()
|
||||
}
|
||||
|
||||
func startListening(on port: UInt16) {
|
||||
try? socket.bind(toPort: port)
|
||||
try? socket.beginReceiving()
|
||||
}
|
||||
|
||||
func send(_ data: Data, toHost host: String, port: UInt16, timeout: TimeInterval) {
|
||||
socket.send(data, toHost: host, port: port, withTimeout: timeout, tag: 0)
|
||||
}
|
||||
}
|
||||
|
||||
extension UDPConnection: GCDAsyncUdpSocketDelegate {
|
||||
|
||||
func udpSocket(_ sock: GCDAsyncUdpSocket,
|
||||
didReceive data: Data,
|
||||
fromAddress address: Data,
|
||||
withFilterContext filterContext: Any?) {
|
||||
|
||||
let hostString = ipAddress(fromSockAddrData: address)!
|
||||
delegate?.udpConnection(self, receivedData: data, fromHost: hostString)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// UDPConnectionTests.swift
|
||||
// BitTorrentTests
|
||||
//
|
||||
// Created by Ben Davis on 03/07/2017.
|
||||
// Copyright © 2017 Ben Davis. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import CocoaAsyncSocket
|
||||
@testable import BitTorrent
|
||||
|
||||
class UDPConnectionDelegateTestingStub: UDPConnectionDelegate {
|
||||
|
||||
var receivedDataCalled = false
|
||||
var receivedDataParameters: (sender: UDPConnection, data: Data, host: String)?
|
||||
func udpConnection(_ sender: UDPConnection, receivedData data: Data, fromHost host: String) {
|
||||
receivedDataCalled = true
|
||||
receivedDataParameters = (sender, data, host)
|
||||
}
|
||||
}
|
||||
|
||||
class UDPConnectionTests: XCTestCase {
|
||||
|
||||
var socket: GCDAsyncUdpSocketStub!
|
||||
var delegate: UDPConnectionDelegateTestingStub!
|
||||
var sut: UDPConnection!
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
|
||||
socket = GCDAsyncUdpSocketStub()
|
||||
delegate = UDPConnectionDelegateTestingStub()
|
||||
sut = UDPConnection(socket: socket)
|
||||
sut.delegate = delegate
|
||||
}
|
||||
|
||||
func test_isSocketDelegate() {
|
||||
XCTAssert(socket._delegate === sut)
|
||||
XCTAssert(socket._delegateQueue === DispatchQueue.main)
|
||||
}
|
||||
|
||||
func test_canStartListeningOnPort() {
|
||||
|
||||
let port: UInt16 = 123
|
||||
sut.startListening(on: port)
|
||||
|
||||
XCTAssert(socket.bindToPortCalled)
|
||||
XCTAssertEqual(socket.bindToPortParameter, port)
|
||||
XCTAssert(socket.beginReceivingCalled)
|
||||
}
|
||||
|
||||
func test_socketClosedOnDeinit() {
|
||||
sut = nil
|
||||
XCTAssert(socket.closeCalled)
|
||||
}
|
||||
|
||||
// MARK: - Receiving data
|
||||
|
||||
func test_canRecieveData() {
|
||||
|
||||
// 127.0.0.1:27002
|
||||
let addressData = Data(bytes: [16,2,122,105,127,0,0,1,0,0,0,0,0,0,0,0])
|
||||
let packetData = Data(bytes: [1,2,3])
|
||||
|
||||
sut.udpSocket(socket, didReceive: packetData, fromAddress: addressData, withFilterContext: nil)
|
||||
|
||||
XCTAssert(delegate.receivedDataCalled)
|
||||
XCTAssertEqual(delegate.receivedDataParameters?.sender, sut)
|
||||
XCTAssertEqual(delegate.receivedDataParameters?.data, packetData)
|
||||
XCTAssertEqual(delegate.receivedDataParameters?.host, "127.0.0.1")
|
||||
}
|
||||
|
||||
// MARK: - Sending data
|
||||
|
||||
func test_canSendData() {
|
||||
|
||||
let data = Data(bytes: [1,2,3])
|
||||
let host = "127.0.0.1"
|
||||
let port: UInt16 = 3475
|
||||
let timeout: TimeInterval = 10
|
||||
|
||||
sut.send(data, toHost: host, port: port, timeout: timeout)
|
||||
|
||||
XCTAssert(socket.sendCalled)
|
||||
XCTAssertEqual(socket.sendParameters!.data, data)
|
||||
XCTAssertEqual(socket.sendParameters!.host, host)
|
||||
XCTAssertEqual(socket.sendParameters!.port, port)
|
||||
XCTAssertEqual(socket.sendParameters!.timeout, timeout)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
|
||||
import UIKit
|
||||
@testable import BitTorrent
|
||||
import BlueSocket
|
||||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate, UDPConnectionDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
|
||||
var tracker: TorrentHTTPTracker!
|
||||
var udpConnection: UDPConnection!
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
|
||||
window = UIWindow(frame: UIScreen.main.bounds)
|
||||
@@ -39,10 +39,26 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
// numberOfBytesDownloaded: 0,
|
||||
// numberOfPeersToFetch: 50)
|
||||
|
||||
let mySocket = try! Socket.create()
|
||||
print(mySocket)
|
||||
// let mySocket = try! Socket.create()
|
||||
// print(mySocket)
|
||||
|
||||
udpConnection = UDPConnection()
|
||||
udpConnection.delegate = self
|
||||
|
||||
udpConnection.startListening(on: 59740)
|
||||
|
||||
let data = "Hello, world!".data(using: .utf8)!
|
||||
udpConnection.send(data, toHost: "127.0.0.1", port: 31337, timeout: 10000)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension UDPConnectionDelegate {
|
||||
|
||||
func udpConnection(_ sender: UDPConnection, receivedData data: Data, fromHost host: String) {
|
||||
print(sender, data, host)
|
||||
print(data == "Hello, world!".data(using: .utf8)! ? "Success" : "Fail")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ target 'BitTorrent' do
|
||||
use_frameworks!
|
||||
|
||||
pod 'Alamofire', '4.5.0'
|
||||
pod 'BlueSocket'
|
||||
pod 'CocoaAsyncSocket'
|
||||
|
||||
target 'BitTorrentTests' do
|
||||
inherit! :search_paths
|
||||
@@ -15,7 +15,7 @@ target 'BitTorrent' do
|
||||
|
||||
target 'BitTorrentExample' do
|
||||
use_frameworks!
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
PODS:
|
||||
- Alamofire (4.5.0)
|
||||
- BlueSocket (0.12.16)
|
||||
- CocoaAsyncSocket (7.6.1)
|
||||
- OHHTTPStubs (6.0.0):
|
||||
- OHHTTPStubs/Default (= 6.0.0)
|
||||
- OHHTTPStubs/Core (6.0.0)
|
||||
@@ -17,14 +17,14 @@ PODS:
|
||||
|
||||
DEPENDENCIES:
|
||||
- Alamofire (= 4.5.0)
|
||||
- BlueSocket
|
||||
- CocoaAsyncSocket
|
||||
- OHHTTPStubs
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Alamofire: f28cdffd29de33a7bfa022cbd63ae95a27fae140
|
||||
BlueSocket: 13bf1daf94a316e9efe93aa125cc33b8c3dd6c35
|
||||
CocoaAsyncSocket: 7eadd3f59e1a6c84e2aefc93e9ff7b55156fe174
|
||||
OHHTTPStubs: 752f9b11fd810a15162d50f11c06ff94f8e012eb
|
||||
|
||||
PODFILE CHECKSUM: 0d38beb67a05d0be9e85d2555a9fbc44ecf5e80c
|
||||
PODFILE CHECKSUM: 0357d6a70a8533160f506a62590771b1f36ca2cc
|
||||
|
||||
COCOAPODS: 1.2.0
|
||||
|
||||
Reference in New Issue
Block a user