重庆迈尖科技有限公司
2018-08-14 3e67eb3d9b87c159ccd1be9c1c55a562341dfe8d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
/*
    Copyright (C) 2016 Apple Inc. All Rights Reserved.
    See LICENSE.txt for this sample’s licensing information
    
    Abstract:
    An object wrapper around the low-level BSD Sockets ping function.
 */
 
@import Foundation;
 
#include <AssertMacros.h>           // for __Check_Compile_Time
 
NS_ASSUME_NONNULL_BEGIN
 
@protocol SimplePingDelegate;
 
/*! Controls the IP address version used by SimplePing instances.
 */
 
typedef NS_ENUM(NSInteger, SimplePingAddressStyle) {
    SimplePingAddressStyleAny,          ///< Use the first IPv4 or IPv6 address found; the default.
    SimplePingAddressStyleICMPv4,       ///< Use the first IPv4 address found.
    SimplePingAddressStyleICMPv6        ///< Use the first IPv6 address found.
};
 
/*! An object wrapper around the low-level BSD Sockets ping function.
 *  \details To use the class create an instance, set the delegate and call `-start` 
 *      to start the instance on the current run loop.  If things go well you'll soon get the 
 *      `-simplePing:didStartWithAddress:` delegate callback.  From there you can can call 
 *      `-sendPingWithData:` to send a ping and you'll receive the 
 *      `-simplePing:didReceivePingResponsePacket:sequenceNumber:` and 
 *      `-simplePing:didReceiveUnexpectedPacket:` delegate callbacks as ICMP packets arrive.
 *
 *      The class can be used from any thread but the use of any single instance must be 
 *      confined to a specific thread and that thread must run its run loop.
 */
 
@interface SimplePing : NSObject
 
- (instancetype)init NS_UNAVAILABLE;
 
/*! Initialise the object to ping the specified host.
 *  \param hostName The DNS name of the host to ping; an IPv4 or IPv6 address in string form will 
 *      work here.
 *  \returns The initialised object.
 */
 
- (instancetype)initWithHostName:(NSString *)hostName NS_DESIGNATED_INITIALIZER;
 
/*! A copy of the value passed to `-initWithHostName:`.
 */
 
@property (nonatomic, copy, readonly) NSString * hostName;
 
/*! The delegate for this object.
 *  \details Delegate callbacks are schedule in the default run loop mode of the run loop of the 
 *      thread that calls `-start`.
 */
 
@property (nonatomic, weak, readwrite, nullable) id<SimplePingDelegate> delegate;
 
/*! Controls the IP address version used by the object.
 *  \details You should set this value before starting the object.
 */
 
@property (nonatomic, assign, readwrite) SimplePingAddressStyle addressStyle;
 
/*! The address being pinged.
 *  \details The contents of the NSData is a (struct sockaddr) of some form.  The 
 *      value is nil while the object is stopped and remains nil on start until 
 *      `-simplePing:didStartWithAddress:` is called.
 */
 
@property (nonatomic, copy, readonly, nullable) NSData * hostAddress;
 
/*! The address family for `hostAddress`, or `AF_UNSPEC` if that's nil.
 */
 
@property (nonatomic, assign, readonly) sa_family_t hostAddressFamily;
 
/*! The identifier used by pings by this object.
 *  \details When you create an instance of this object it generates a random identifier 
 *      that it uses to identify its own pings.
 */
 
@property (nonatomic, assign, readonly) uint16_t identifier;
 
/*! The next sequence number to be used by this object.
 *  \details This value starts at zero and increments each time you send a ping (safely 
 *      wrapping back to zero if necessary).  The sequence number is included in the ping, 
 *      allowing you to match up requests and responses, and thus calculate ping times and 
 *      so on.
 */
 
@property (nonatomic, assign, readonly) uint16_t nextSequenceNumber;
 
/*! Starts the object.
 *  \details You should set up the delegate and any ping parameters before calling this.
 *      
 *      If things go well you'll soon get the `-simplePing:didStartWithAddress:` delegate 
 *      callback, at which point you can start sending pings (via `-sendPingWithData:`) and 
 *      will start receiving ICMP packets (either ping responses, via the 
 *      `-simplePing:didReceivePingResponsePacket:sequenceNumber:` delegate callback, or 
 *      unsolicited ICMP packets, via the `-simplePing:didReceiveUnexpectedPacket:` delegate 
 *      callback).
 *
 *      If the object fails to start, typically because `hostName` doesn't resolve, you'll get 
 *      the `-simplePing:didFailWithError:` delegate callback.
 *
 *      It is not correct to start an already started object.
 */
 
- (void)start;
 
/*! Sends a ping packet containing the specified data.
 *  \details Sends an actual ping.
 *
 *      The object must be started when you call this method and, on starting the object, you must 
 *      wait for the `-simplePing:didStartWithAddress:` delegate callback before calling it.
 *  \param data Some data to include in the ping packet, after the ICMP header, or nil if you 
 *      want the packet to include a standard 56 byte payload (resulting in a standard 64 byte 
 *      ping).
 */
 
- (void)sendPingWithData:(nullable NSData *)data;
 
/*! Stops the object.
 *  \details You should call this when you're done pinging.
 *      
 *      It's safe to call this on an object that's stopped.
 */
 
- (void)stop;
 
@end
 
/*! A delegate protocol for the SimplePing class.
 */
 
@protocol SimplePingDelegate <NSObject>
 
@optional
 
/*! A SimplePing delegate callback, called once the object has started up.
 *  \details This is called shortly after you start the object to tell you that the 
 *      object has successfully started.  On receiving this callback, you can call 
 *      `-sendPingWithData:` to send pings.
 *
 *      If the object didn't start, `-simplePing:didFailWithError:` is called instead.
 *  \param pinger The object issuing the callback.
 *  \param address The address that's being pinged; at the time this delegate callback 
 *      is made, this will have the same value as the `hostAddress` property.
 */
 
- (void)simplePing:(SimplePing *)pinger didStartWithAddress:(NSData *)address;
 
/*! A SimplePing delegate callback, called if the object fails to start up.
 *  \details This is called shortly after you start the object to tell you that the 
 *      object has failed to start.  The most likely cause of failure is a problem 
 *      resolving `hostName`.
 *
 *      By the time this callback is called, the object has stopped (that is, you don't 
 *      need to call `-stop` yourself).
 *  \param pinger The object issuing the callback.
 *  \param error Describes the failure.
 */
    
- (void)simplePing:(SimplePing *)pinger didFailWithError:(NSError *)error;
 
/*! A SimplePing delegate callback, called when the object has successfully sent a ping packet. 
 *  \details Each call to `-sendPingWithData:` will result in either a 
 *      `-simplePing:didSendPacket:sequenceNumber:` delegate callback or a 
 *      `-simplePing:didFailToSendPacket:sequenceNumber:error:` delegate callback (unless you 
 *      stop the object before you get the callback).  These callbacks are currently delivered 
 *      synchronously from within `-sendPingWithData:`, but this synchronous behaviour is not 
 *      considered API.
 *  \param pinger The object issuing the callback.
 *  \param packet The packet that was sent; this includes the ICMP header (`ICMPHeader`) and the 
 *      data you passed to `-sendPingWithData:` but does not include any IP-level headers.
 *  \param sequenceNumber The ICMP sequence number of that packet.
 */
 
- (void)simplePing:(SimplePing *)pinger didSendPacket:(NSData *)packet sequenceNumber:(uint16_t)sequenceNumber;
 
/*! A SimplePing delegate callback, called when the object fails to send a ping packet. 
 *  \details Each call to `-sendPingWithData:` will result in either a 
 *      `-simplePing:didSendPacket:sequenceNumber:` delegate callback or a 
 *      `-simplePing:didFailToSendPacket:sequenceNumber:error:` delegate callback (unless you 
 *      stop the object before you get the callback).  These callbacks are currently delivered 
 *      synchronously from within `-sendPingWithData:`, but this synchronous behaviour is not 
 *      considered API.
 *  \param pinger The object issuing the callback.
 *  \param packet The packet that was not sent; see `-simplePing:didSendPacket:sequenceNumber:` 
 *      for details.
 *  \param sequenceNumber The ICMP sequence number of that packet.
 *  \param error Describes the failure.
 */
 
- (void)simplePing:(SimplePing *)pinger didFailToSendPacket:(NSData *)packet sequenceNumber:(uint16_t)sequenceNumber error:(NSError *)error;
 
/*! A SimplePing delegate callback, called when the object receives a ping response.
 *  \details If the object receives an ping response that matches a ping request that it 
 *      sent, it informs the delegate via this callback.  Matching is primarily done based on 
 *      the ICMP identifier, although other criteria are used as well.
 *  \param pinger The object issuing the callback.
 *  \param packet The packet received; this includes the ICMP header (`ICMPHeader`) and any data that 
 *      follows that in the ICMP message but does not include any IP-level headers.
 *  \param sequenceNumber The ICMP sequence number of that packet.
 */
 
- (void)simplePing:(SimplePing *)pinger didReceivePingResponsePacket:(NSData *)packet sequenceNumber:(uint16_t)sequenceNumber;
 
/*! A SimplePing delegate callback, called when the object receives an unmatched ICMP message.
 *  \details If the object receives an ICMP message that does not match a ping request that it 
 *      sent, it informs the delegate via this callback.  The nature of ICMP handling in a 
 *      BSD kernel makes this a common event because, when an ICMP message arrives, it is 
 *      delivered to all ICMP sockets.
 *
 *      IMPORTANT: This callback is especially common when using IPv6 because IPv6 uses ICMP 
 *      for important network management functions.  For example, IPv6 routers periodically 
 *      send out Router Advertisement (RA) packets via Neighbor Discovery Protocol (NDP), which 
 *      is implemented on top of ICMP.
 *
 *      For more on matching, see the discussion associated with 
 *      `-simplePing:didReceivePingResponsePacket:sequenceNumber:`.
 *  \param pinger The object issuing the callback.
 *  \param packet The packet received; this includes the ICMP header (`ICMPHeader`) and any data that 
 *      follows that in the ICMP message but does not include any IP-level headers.
 */
 
- (void)simplePing:(SimplePing *)pinger didReceiveUnexpectedPacket:(NSData *)packet;
 
@end
 
#pragma mark * ICMP On-The-Wire Format
 
/*! Describes the on-the-wire header format for an ICMP ping.
 *  \details This defines the header structure of ping packets on the wire.  Both IPv4 and 
 *      IPv6 use the same basic structure.  
 *  
 *      This is declared in the header because clients of SimplePing might want to use 
 *      it parse received ping packets.
 */
 
struct ICMPHeader {
    uint8_t     type;
    uint8_t     code;
    uint16_t    checksum;
    uint16_t    identifier;
    uint16_t    sequenceNumber;
    // data...
};
typedef struct ICMPHeader ICMPHeader;
 
__Check_Compile_Time(sizeof(ICMPHeader) == 8);
__Check_Compile_Time(offsetof(ICMPHeader, type) == 0);
__Check_Compile_Time(offsetof(ICMPHeader, code) == 1);
__Check_Compile_Time(offsetof(ICMPHeader, checksum) == 2);
__Check_Compile_Time(offsetof(ICMPHeader, identifier) == 4);
__Check_Compile_Time(offsetof(ICMPHeader, sequenceNumber) == 6);
 
enum {
    ICMPv4TypeEchoRequest = 8,          ///< The ICMP `type` for a ping request; in this case `code` is always 0.
    ICMPv4TypeEchoReply   = 0           ///< The ICMP `type` for a ping response; in this case `code` is always 0.
};
 
enum {
    ICMPv6TypeEchoRequest = 128,        ///< The ICMP `type` for a ping request; in this case `code` is always 0.
    ICMPv6TypeEchoReply   = 129         ///< The ICMP `type` for a ping response; in this case `code` is always 0.
};
 
NS_ASSUME_NONNULL_END