Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

iOS Ping functionality #602

Merged
merged 13 commits into from
Feb 27, 2019
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions External Code/SCPinions/SCPacketUtility/SCIcmpPacketUtility.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@

@protocol SCIcmpPacketUtilityDelegate;

@class SCPacketRecord;

@interface SCIcmpPacketUtility : NSObject
{
NSData* _targetAddress;
Expand All @@ -57,7 +59,7 @@
@property (nonatomic, copy, readonly) NSData* targetAddress;
@property (nonatomic, copy, readwrite) NSString* targetAddressString;
@property (nonatomic, assign, readonly) uint16_t nextSequenceNumber;
@property (strong, nonatomic, readonly) NSMutableArray* packetRecords;
@property (strong, nonatomic, readonly) NSMutableArray<SCPacketRecord *>* packetRecords;

+ (SCIcmpPacketUtility*)utilityWithHostAddress:(NSString*)hostAddress; // contains (struct sockaddr) - should have this take IP string, then convert to struct sockaddr

Expand All @@ -67,7 +69,7 @@
- (void)start;
// Starts the packet utility object doing it's thing. You should call this after you've setup the delegate.

- (void)sendPacketWithData:(NSData *)data andTTL:(int)ttl;
- (void)sendPacketWithData:(NSData *)data andTTL:(NSInteger)ttl;
// Sends an ICMP packet. Do not try to send a packet before you receive the -SCIcmpPacketUtility:didStartWithAddress: delegate callback.

- (void)stop;
Expand All @@ -80,27 +82,27 @@

@optional

/// Called after the SCIcmpPacketUtility has successfully started up. After this callback, you can start sending packets via -sendPacketWithData:
- (void)SCIcmpPacketUtility:(SCIcmpPacketUtility*)packetUtility didStartWithAddress:(NSData *)address;
// Called after the SCIcmpPacketUtility has successfully started up. After this callback, you can start sending packets via -sendPacketWithData:

/// If this is called, the SCIcmpPacketUtility object has failed. By the time this callback is called, the object has stopped (that is, you don't need to call -stop yourself).
- (void)SCIcmpPacketUtility:(SCIcmpPacketUtility*)packetUtility didFailWithError:(NSError *)error;
// If this is called, the SCIcmpPacketUtility object has failed. By the time this callback is called, the object has stopped (that is, you don't need to call -stop yourself).

// IMPORTANT: On the send side the packet does not include an IP header.
// On the receive side, it does. In that case, use +[SCIcmpPacketUtility icmpInPacket:]
// to find the ICMP header within the packet.

/// Called whenever the SCIcmpPacketUtility object has successfully sent a packet.
- (void)SCIcmpPacketUtility:(SCIcmpPacketUtility*)packetUtility didSendPacket:(NSData *)packet;
// Called whenever the SCIcmpPacketUtility object has successfully sent a packet.

/// Called whenever the SCIcmpPacketUtility object tries and fails to send a packet.
- (void)SCIcmpPacketUtility:(SCIcmpPacketUtility*)packetUtility didFailToSendPacket:(NSData *)packet error:(NSError *)error;
// Called whenever the SCIcmpPacketUtility object tries and fails to send a packet.

/// Called whenever the SCIcmpPacketUtility object receives an ICMP packet that looks like a response to one of our packets
- (void)SCIcmpPacketUtility:(SCIcmpPacketUtility*)packetUtility didReceiveResponsePacket:(NSData *)packet arrivedAt:(NSDate*)dateTime;
// Called whenever the SCIcmpPacketUtility object receives an ICMP packet that looks like a response to one of our packets

/// Called whenever the SCIcmpPacketUtility object receives an ICMP packet that does not look like a response to one of our packets.
- (void)SCIcmpPacketUtility:(SCIcmpPacketUtility*)packetUtility didReceiveUnexpectedPacket:(NSData *)packet;
// Called whenever the SCIcmpPacketUtility object receives an ICMP packet that does not look like a response to one of our packets.

@end

Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ - (void)stop
#pragma mark - Packet sending


-(void)sendPacketWithData:(NSData *)data andTTL:(int)ttl{
-(void)sendPacketWithData:(NSData *)data andTTL:(NSInteger)ttl {

int err;
NSData * payload;
Expand Down
2 changes: 1 addition & 1 deletion External Code/SCPinions/SCPacketUtility/SCPacketRecord.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
@interface SCPacketRecord : NSObject

@property uint16_t sequenceNumber; //A sequence number for tracking of packet relationships as we perform network operations
@property int sentWithTTL; // The TTL that our original packet has assigned to it
@property NSInteger sentWithTTL; // The TTL that our original packet has assigned to it
@property NSString* responseAddress; //The address of the machine that sent us a respponse packet for this sequence number
@property NSDate* departure; // Departure time of packet sent for this sequence number
@property NSDate* arrival; // Arrive of response packet for this sequence number
Expand Down
56 changes: 38 additions & 18 deletions InternetMap.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,11 @@
A6F3F56B1640F1FB0070E6AE /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A6F3F56A1640F1FB0070E6AE /* OpenGLES.framework */; };
D403BBDE16C1DF2F008AA000 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = D403BBD816C1DF2F008AA000 /* LICENSE */; };
D403BBE016C1DF2F008AA000 /* README in Resources */ = {isa = PBXBuildFile; fileRef = D403BBDA16C1DF2F008AA000 /* README */; };
D429C707221F16FB0031555C /* SCPingUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = D429C706221F16FB0031555C /* SCPingUtility.swift */; };
D468090416795D4600A2C1CC /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D468090216795CC200A2C1CC /* SystemConfiguration.framework */; };
D468090516795D4D00A2C1CC /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D468090016795CB700A2C1CC /* CFNetwork.framework */; };
D490FEA8221CC7FF00F00C3C /* PingLocationsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D490FEA7221CC7FF00F00C3C /* PingLocationsViewController.swift */; };
D490FEAA221CCAA300F00C3C /* DoneButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D490FEA9221CCAA300F00C3C /* DoneButton.swift */; };
D4C1083116B2043D00CA4A0E /* SCIcmpPacketUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = D4C1082B16B2043D00CA4A0E /* SCIcmpPacketUtility.m */; };
D4C1083216B2043D00CA4A0E /* SCPacketRecord.m in Sources */ = {isa = PBXBuildFile; fileRef = D4C1082E16B2043D00CA4A0E /* SCPacketRecord.m */; };
D4C1083316B2043D00CA4A0E /* SCTracerouteUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = D4C1083016B2043D00CA4A0E /* SCTracerouteUtility.m */; };
Expand Down Expand Up @@ -656,8 +659,11 @@
D403BBDC16C1DF2F008AA000 /* traceroute.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = traceroute.h; sourceTree = "<group>"; };
D403BBDD16C1DF2F008AA000 /* traceroute_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = traceroute_private.h; sourceTree = "<group>"; };
D403BBE216C1E272008AA000 /* main-traceroute.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "main-traceroute.h"; sourceTree = "<group>"; };
D429C706221F16FB0031555C /* SCPingUtility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SCPingUtility.swift; sourceTree = "<group>"; };
D468090016795CB700A2C1CC /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; };
D468090216795CC200A2C1CC /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
D490FEA7221CC7FF00F00C3C /* PingLocationsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PingLocationsViewController.swift; path = iOS/PingLocationsViewController.swift; sourceTree = SOURCE_ROOT; };
D490FEA9221CCAA300F00C3C /* DoneButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DoneButton.swift; path = iOS/DoneButton.swift; sourceTree = SOURCE_ROOT; };
D4C1082A16B2043D00CA4A0E /* SCIcmpPacketUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SCIcmpPacketUtility.h; path = "External Code/SCPinions/SCPacketUtility/SCIcmpPacketUtility.h"; sourceTree = SOURCE_ROOT; };
D4C1082B16B2043D00CA4A0E /* SCIcmpPacketUtility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SCIcmpPacketUtility.m; path = "External Code/SCPinions/SCPacketUtility/SCIcmpPacketUtility.m"; sourceTree = SOURCE_ROOT; };
D4C1082C16B2043D00CA4A0E /* SCPacketDeclarations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SCPacketDeclarations.h; path = "External Code/SCPinions/SCPacketUtility/SCPacketDeclarations.h"; sourceTree = SOURCE_ROOT; };
Expand Down Expand Up @@ -1126,10 +1132,11 @@
children = (
84DDB7F61F06E4E00059BAE7 /* Images.xcassets */,
A600AADA16A664E9003B1028 /* Data */,
114DD459167AA62D0061CB36 /* ExternalCode */,
D429C705221F16C50031555C /* Library */,
A6D54A8616544B3000028B51 /* Map */,
A6F3F56D1640F1FB0070E6AE /* Supporting Files */,
A6F3F56C1640F1FB0070E6AE /* UI */,
A6D54A8616544B3000028B51 /* Map */,
114DD459167AA62D0061CB36 /* ExternalCode */,
A6F3F5611640F1FB0070E6AE /* Frameworks */,
A6F3F55F1640F1FB0070E6AE /* Products */,
);
Expand Down Expand Up @@ -1175,16 +1182,22 @@
A6F3F56C1640F1FB0070E6AE /* UI */ = {
isa = PBXGroup;
children = (
1180CF54169B80E300F1FB73 /* Fonts */,
D4CEA32016694E53004B8E8A /* Images */,
A604A0322036137900543D2E /* AppDelegate.swift */,
A60F45821FBE0309001E9D25 /* RootVC.swift */,
A686EA6116B62B61009B070E /* ASNRequest.m */,
A686EA5116B62B52009B070E /* ASNRequest.h */,
A686EA6116B62B61009B070E /* ASNRequest.m */,
A686EA5316B62B52009B070E /* CPPHelpers.h */,
A623BEAF204F3E60001C218D /* TimedMessageLabel.swift */,
A60616361FB3CB9600F1AEB4 /* CreditsViewController.swift */,
D490FEA9221CCAA300F00C3C /* DoneButton.swift */,
A6551EC016CEB88700340590 /* ExpandedScrollView.h */,
A6551EC116CEB88700340590 /* ExpandedScrollView.m */,
A6E1D5C316B9EC7100D8B26F /* ExpandedSlider.h */,
A6E1D5C416B9EC7100D8B26F /* ExpandedSlider.m */,
A6551E6516CAC1AE00340590 /* FirstUseViewController.h */,
A6551E6616CAC1AF00340590 /* FirstUseViewController.m */,
1180CF54169B80E300F1FB73 /* Fonts */,
A686EA5516B62B52009B070E /* HelperMethods.h */,
A686EA6316B62B61009B070E /* HelperMethods.m */,
D4CEA32016694E53004B8E8A /* Images */,
A686EA5616B62B52009B070E /* LabelNumberBoxView.h */,
A686EA6416B62B61009B070E /* LabelNumberBoxView.m */,
A686EA5816B62B52009B070E /* NodeInformationViewController.h */,
Expand All @@ -1193,23 +1206,19 @@
A686EA6716B62B61009B070E /* NodeSearchViewController.m */,
A686EA5A16B62B52009B070E /* NodeTooltipViewController.h */,
A686EA6816B62B61009B070E /* NodeTooltipViewController.m */,
1127C9CF16A0BBFB00413FE9 /* Objective-C++ Wrappers */,
D490FEA7221CC7FF00F00C3C /* PingLocationsViewController.swift */,
A60F45821FBE0309001E9D25 /* RootVC.swift */,
A686EA6016B62B52009B070E /* StringListViewController.h */,
A686EA6C16B62B61009B070E /* StringListViewController.m */,
A60F457B1FB3CFE7001E9D25 /* Theme.swift */,
A623BEAF204F3E60001C218D /* TimedMessageLabel.swift */,
A686EA5D16B62B52009B070E /* TimelineInfoViewController.h */,
A686EA6916B62B61009B070E /* TimelineInfoViewController.m */,
A686EA5E16B62B52009B070E /* UIView+Convenience.h */,
A686EA6A16B62B61009B070E /* UIView+Convenience.m */,
A686EA5F16B62B52009B070E /* ViewController.h */,
A686EA6B16B62B61009B070E /* ViewController.m */,
A686EA6016B62B52009B070E /* StringListViewController.h */,
A686EA6C16B62B61009B070E /* StringListViewController.m */,
A6E1D5C316B9EC7100D8B26F /* ExpandedSlider.h */,
A6E1D5C416B9EC7100D8B26F /* ExpandedSlider.m */,
A6551E6516CAC1AE00340590 /* FirstUseViewController.h */,
A6551E6616CAC1AF00340590 /* FirstUseViewController.m */,
A6551EC016CEB88700340590 /* ExpandedScrollView.h */,
A6551EC116CEB88700340590 /* ExpandedScrollView.m */,
A60F457B1FB3CFE7001E9D25 /* Theme.swift */,
A60616361FB3CB9600F1AEB4 /* CreditsViewController.swift */,
1127C9CF16A0BBFB00413FE9 /* Objective-C++ Wrappers */,
D4D17D20166EA621003A489C /* XIBs */,
);
name = UI;
Expand Down Expand Up @@ -1258,6 +1267,14 @@
path = ..;
sourceTree = "<group>";
};
D429C705221F16C50031555C /* Library */ = {
isa = PBXGroup;
children = (
D429C706221F16FB0031555C /* SCPingUtility.swift */,
);
path = Library;
sourceTree = "<group>";
};
D4CEA32016694E53004B8E8A /* Images */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -1647,6 +1664,7 @@
A686EAFF16B62DBD009B070E /* VertexBuffer.cpp in Sources */,
D4C1083116B2043D00CA4A0E /* SCIcmpPacketUtility.m in Sources */,
D4C1083216B2043D00CA4A0E /* SCPacketRecord.m in Sources */,
D490FEAA221CCAA300F00C3C /* DoneButton.swift in Sources */,
A60616371FB3CB9600F1AEB4 /* CreditsViewController.swift in Sources */,
D4C1083316B2043D00CA4A0E /* SCTracerouteUtility.m in Sources */,
A686EA7116B62B61009B070E /* ASNRequest.m in Sources */,
Expand All @@ -1661,6 +1679,8 @@
A686EA7C16B62B61009B070E /* StringListViewController.m in Sources */,
A60F457C1FB3CFE7001E9D25 /* Theme.swift in Sources */,
A686EA7E16B62B61009B070E /* ConnectionWrapper.mm in Sources */,
D429C707221F16FB0031555C /* SCPingUtility.swift in Sources */,
D490FEA8221CC7FF00F00C3C /* PingLocationsViewController.swift in Sources */,
A686EA7F16B62B61009B070E /* MapControllerWrapper.mm in Sources */,
A686EA8016B62B61009B070E /* NodeWrapper.mm in Sources */,
A686EAE816B62D05009B070E /* SCDispatchQueue.m in Sources */,
Expand Down
126 changes: 126 additions & 0 deletions Library/SCPingUtility.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
//
// SCPingUtility.swift
// Internet Map
//
// Created by Robert MacEachern on 2019-02-21.
// Copyright © 2019 Peer1. All rights reserved.
//

import Foundation

@objc protocol SCPingUtilityDelegate {

/// Invoked immediately before a ping packet is sent.
func pingUtilityWillSendPing(_ pingUtility: SCPingUtility)

/// Invoked each time a response packet is received. Includes all packet records seen so far.
func pingUtility(_ pingUtility: SCPingUtility, didReceiveResponse records: [SCPacketRecord])

/// Invoked when an error occurs with sending a packet or a more general ICMP failure.
func pingUtility(_ pingUtility: SCPingUtility, didFailWithError error: Error)

/// Invoked when the ping utility has finished. Includes all packet records.
func pingUtility(_ pingUtility: SCPingUtility, didFinishWithRecords records: [SCPacketRecord])
}

@objc class SCPingUtility: NSObject {

let ipAddress: String
let count: Int
let ttl: Int

/// The interval between sending packets.
let wait: TimeInterval

@objc var delegate: SCPingUtilityDelegate?

@objc public var packetRecords: [SCPacketRecord] {
let records = icmpUtility.packetRecords as! [SCPacketRecord]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Force cast here seems like it shouldn't be necessary, as the ObjC code is annotated with the right type. Was it maybe from before the annotation as added and never got removed?

Alternatively, is it maybe complaining because it things it might be nullable, and there needs to be a nullable annotation on that as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I was confused by this too. For some reason the generic type isn't bridging to Swift. The interface swift sees is:

var packetRecords: NSMutableArray! { get }

A bit of google searching led me to this Swift bug: Swift does not honor NSMutableArray generics from objective-c classes. I could try the workaround of changing the public interface to NSArray<SCPacketRecord *> as long as nothing else depends on modifying that array.

records.forEach { record in
// The ICMPHeader sequence numbers are big endian. Need to convert them before looking them up.
let responseHeader = responsePacketHeaders.first(where: { CFSwapInt16BigToHost($0.0.sequenceNumber) == record.sequenceNumber})
record.arrival = responseHeader?.1
record.rtt = responseHeader == nil ? 0 : Float(record.arrival.timeIntervalSince1970 - record.departure.timeIntervalSince1970) * 1000.0
record.responseAddress = responseHeader == nil ? nil : ipAddress
record.timedOut = responseHeader == nil && (Date().timeIntervalSince1970 - record.departure.timeIntervalSince1970) > wait
}
return records
}

private var icmpUtility: SCIcmpPacketUtility
private var hasStarted: Bool = false
private var responsePacketHeaders: [(ICMPHeader, arrival: Date)] = []

@objc init(ipAddress: String, count: Int, ttl: Int, wait: TimeInterval) {
self.ipAddress = ipAddress
self.count = count
self.ttl = ttl
self.wait = wait
self.icmpUtility = SCIcmpPacketUtility(hostAddress: ipAddress)
super.init()
self.icmpUtility.delegate = self
}

@objc func start() {
guard !hasStarted else {
NSLog("WARNING: Attempting to restart a SCPingUtility that has alread been started. Create a new instance instead.")
return
}
icmpUtility.start()
hasStarted = true
}

@objc func finish() {
icmpUtility.stop()
self.delegate?.pingUtility(self, didFinishWithRecords: packetRecords)
}

private func sendPingPacketIfNecessary() {
if icmpUtility.nextSequenceNumber < count {
self.delegate?.pingUtilityWillSendPing(self)
icmpUtility.sendPacket(with: nil, andTTL: ttl)
DispatchQueue.main.asyncAfter(deadline: .now() + wait) {
self.sendPingPacketIfNecessary()
}
} else {
NSLog("SCPingUtility reached number of ping packets to send")
finish()
}
}
}

extension SCPingUtility: SCIcmpPacketUtilityDelegate {

func scIcmpPacketUtility(_ packetUtility: SCIcmpPacketUtility!, didSendPacket packet: Data!) {
NSLog("SCPingUtility SCIcmpPacketUtility.didSendPacket")
}

func scIcmpPacketUtility(_ packetUtility: SCIcmpPacketUtility!, didFailWithError error: Error!) {
NSLog("SCPingUtility SCIcmpPacketUtility.didFailWithError \(error.localizedDescription)")
self.delegate?.pingUtility(self, didFailWithError: error)
}

func scIcmpPacketUtility(_ packetUtility: SCIcmpPacketUtility!, didStartWithAddress address: Data!) {
NSLog("SCPingUtility SCIcmpPacketUtility.didStartWithAddress")
sendPingPacketIfNecessary()
}

func scIcmpPacketUtility(_ packetUtility: SCIcmpPacketUtility!, didReceiveUnexpectedPacket packet: Data!) {
NSLog("SCPingUtility SCIcmpPacketUtility.didReceiveUnexpectedPacket")
}

func scIcmpPacketUtility(_ packetUtility: SCIcmpPacketUtility!, didFailToSendPacket packet: Data!, error: Error!) {
NSLog("didFailToSendPacket \(error.localizedDescription)")
self.delegate?.pingUtility(self, didFailWithError: error)
}

func scIcmpPacketUtility(_ packetUtility: SCIcmpPacketUtility!, didReceiveResponsePacket packet: Data!, arrivedAt dateTime: Date!) {
NSLog("SCPingUtility SCIcmpPacketUtility.didReceiveResponsePacket at time \(dateTime!)")
guard let icmpPacket = SCIcmpPacketUtility.icmp(inPacket: packet)?.pointee else {
fatalError("Unable to access icmp packet")
}

responsePacketHeaders.append((icmpPacket, dateTime))
self.delegate?.pingUtility(self, didReceiveResponse: self.packetRecords)
}
}
4 changes: 4 additions & 0 deletions Theme.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,9 @@ enum Theme {
static let primary = Theme.blue

static let fontNameLight = "NexaLight"

static func settingsItemBackgroundImage() -> UIImage {
return UIDevice.current.userInterfaceIdiom == .phone ? UIImage(named: "iphone-bg.png")! : UIImage(named: "ipad-bg.png")!
}
}

Loading