Implemented a UDPConnection class as a wrapper around a UDP implementation

This commit is contained in:
Ben Davis
2017-07-04 20:34:49 +02:00
parent 90ce0ebf64
commit b677eae167
12 changed files with 649 additions and 10 deletions
+28
View File
@@ -10,6 +10,8 @@
0434EAB818A3C318E433338A /* Pods_BitTorrentTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A8680B0EA21B5A20E03DEE6D /* Pods_BitTorrentTests.framework */; }; 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 */; }; 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 */; }; 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 */; }; B537CF061F03148B0084089B /* HTTPConnectionStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = B537CF051F03148B0084089B /* HTTPConnectionStub.swift */; };
B537CF461F031AD20084089B /* TestText.torrent in Resources */ = {isa = PBXBuildFile; fileRef = B5E9B0E31F02F9E700EF58E3 /* TestText.torrent */; }; B537CF461F031AD20084089B /* TestText.torrent in Resources */ = {isa = PBXBuildFile; fileRef = B5E9B0E31F02F9E700EF58E3 /* TestText.torrent */; };
B537CF491F031C3D0084089B /* BEncode.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B54D0C1A1CA53983004343BD /* BEncode.framework */; }; 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 */; }; B5530DB51F03063E00F71CCD /* HTTPConnectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5530DB41F03063E00F71CCD /* HTTPConnectionTests.swift */; };
B55317DC1F02FC4D00909ADF /* TorrentHTTPTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55317DB1F02FC4D00909ADF /* TorrentHTTPTracker.swift */; }; B55317DC1F02FC4D00909ADF /* TorrentHTTPTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55317DB1F02FC4D00909ADF /* TorrentHTTPTracker.swift */; };
B55317E01F02FE1500909ADF /* URLEncodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55317DF1F02FE1500909ADF /* URLEncodeTests.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 */; }; 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, ); }; }; 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 */; }; 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; }; 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>"; }; 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; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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; }; 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>"; }; B585AB771C3833450093FA41 /* BitTorrent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BitTorrent.h; sourceTree = "<group>"; };
@@ -204,6 +214,18 @@
name = Pods; name = Pods;
sourceTree = "<group>"; 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 */ = { B54D0C131CA53979004343BD /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -306,6 +328,7 @@
B54D0C251CA56A25004343BD /* Models */, B54D0C251CA56A25004343BD /* Models */,
B54D0C291CA5785F004343BD /* Utilities */, B54D0C291CA5785F004343BD /* Utilities */,
B5E9778F1CAFB2090038EBE7 /* HTTP Networking */, B5E9778F1CAFB2090038EBE7 /* HTTP Networking */,
B50B24F01F0A54C100C23E7C /* UDP Networking */,
B585AB791C3833450093FA41 /* Info.plist */, B585AB791C3833450093FA41 /* Info.plist */,
); );
path = BitTorrent; path = BitTorrent;
@@ -712,10 +735,12 @@
B5F81E491F0436D600B25C70 /* TorrentPeer.swift in Sources */, B5F81E491F0436D600B25C70 /* TorrentPeer.swift in Sources */,
B55317DC1F02FC4D00909ADF /* TorrentHTTPTracker.swift in Sources */, B55317DC1F02FC4D00909ADF /* TorrentHTTPTracker.swift in Sources */,
B5E977961CAFB46B0038EBE7 /* String+URLEncode.swift in Sources */, B5E977961CAFB46B0038EBE7 /* String+URLEncode.swift in Sources */,
B558F4831F0A647D00438BB4 /* InternetProtocol.swift in Sources */,
B54D0C7A1CA69FAD004343BD /* TorrentMetaInfo.swift in Sources */, B54D0C7A1CA69FAD004343BD /* TorrentMetaInfo.swift in Sources */,
B5F81E4B1F04399800B25C70 /* TorrentTrackerResponse.swift in Sources */, B5F81E4B1F04399800B25C70 /* TorrentTrackerResponse.swift in Sources */,
B54D0C7B1CA69FD8004343BD /* Data+sha1.swift in Sources */, B54D0C7B1CA69FD8004343BD /* Data+sha1.swift in Sources */,
B5530DB21F03063300F71CCD /* HTTPConnection.swift in Sources */, B5530DB21F03063300F71CCD /* HTTPConnection.swift in Sources */,
B50B24F91F0A554A00C23E7C /* UDPConnection.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -725,11 +750,14 @@
files = ( files = (
B537CF061F03148B0084089B /* HTTPConnectionStub.swift in Sources */, B537CF061F03148B0084089B /* HTTPConnectionStub.swift in Sources */,
B5BD7FD61F03032400621BC2 /* TorrentHTTPTrackerTests.swift in Sources */, B5BD7FD61F03032400621BC2 /* TorrentHTTPTrackerTests.swift in Sources */,
B556D5AE1F0BA1FD00277B8D /* GCDAsyncUdpSocketStub.swift in Sources */,
B5530DB51F03063E00F71CCD /* HTTPConnectionTests.swift in Sources */, B5530DB51F03063E00F71CCD /* HTTPConnectionTests.swift in Sources */,
B56A8A071C83539300426AC8 /* TestHelpers.swift in Sources */, B56A8A071C83539300426AC8 /* TestHelpers.swift in Sources */,
B585AB841C3833450093FA41 /* BitTorrentTests.swift in Sources */, B585AB841C3833450093FA41 /* BitTorrentTests.swift in Sources */,
B54D0C271CA56ADB004343BD /* TorrentMetaInfoTests.swift in Sources */, B54D0C271CA56ADB004343BD /* TorrentMetaInfoTests.swift in Sources */,
B55317E01F02FE1500909ADF /* URLEncodeTests.swift in Sources */, B55317E01F02FE1500909ADF /* URLEncodeTests.swift in Sources */,
B558F4851F0A73D000438BB4 /* InternetProtocolTests.swift in Sources */,
B50B24F71F0A553F00C23E7C /* UDPConnectionTests.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
+12
View File
@@ -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)
}
}
+20 -4
View File
@@ -8,14 +8,14 @@
import UIKit import UIKit
@testable import BitTorrent @testable import BitTorrent
import BlueSocket
@UIApplicationMain @UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate { class AppDelegate: UIResponder, UIApplicationDelegate, UDPConnectionDelegate {
var window: UIWindow? var window: UIWindow?
var tracker: TorrentHTTPTracker! var tracker: TorrentHTTPTracker!
var udpConnection: UDPConnection!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds) window = UIWindow(frame: UIScreen.main.bounds)
@@ -39,10 +39,26 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// numberOfBytesDownloaded: 0, // numberOfBytesDownloaded: 0,
// numberOfPeersToFetch: 50) // numberOfPeersToFetch: 50)
let mySocket = try! Socket.create() // let mySocket = try! Socket.create()
print(mySocket) // 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 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")
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ target 'BitTorrent' do
use_frameworks! use_frameworks!
pod 'Alamofire', '4.5.0' pod 'Alamofire', '4.5.0'
pod 'BlueSocket' pod 'CocoaAsyncSocket'
target 'BitTorrentTests' do target 'BitTorrentTests' do
inherit! :search_paths inherit! :search_paths
+4 -4
View File
@@ -1,6 +1,6 @@
PODS: PODS:
- Alamofire (4.5.0) - Alamofire (4.5.0)
- BlueSocket (0.12.16) - CocoaAsyncSocket (7.6.1)
- OHHTTPStubs (6.0.0): - OHHTTPStubs (6.0.0):
- OHHTTPStubs/Default (= 6.0.0) - OHHTTPStubs/Default (= 6.0.0)
- OHHTTPStubs/Core (6.0.0) - OHHTTPStubs/Core (6.0.0)
@@ -17,14 +17,14 @@ PODS:
DEPENDENCIES: DEPENDENCIES:
- Alamofire (= 4.5.0) - Alamofire (= 4.5.0)
- BlueSocket - CocoaAsyncSocket
- OHHTTPStubs - OHHTTPStubs
SPEC CHECKSUMS: SPEC CHECKSUMS:
Alamofire: f28cdffd29de33a7bfa022cbd63ae95a27fae140 Alamofire: f28cdffd29de33a7bfa022cbd63ae95a27fae140
BlueSocket: 13bf1daf94a316e9efe93aa125cc33b8c3dd6c35 CocoaAsyncSocket: 7eadd3f59e1a6c84e2aefc93e9ff7b55156fe174
OHHTTPStubs: 752f9b11fd810a15162d50f11c06ff94f8e012eb OHHTTPStubs: 752f9b11fd810a15162d50f11c06ff94f8e012eb
PODFILE CHECKSUM: 0d38beb67a05d0be9e85d2555a9fbc44ecf5e80c PODFILE CHECKSUM: 0357d6a70a8533160f506a62590771b1f36ca2cc
COCOAPODS: 1.2.0 COCOAPODS: 1.2.0