├── .DS_Store ├── LICENSE ├── Pinger.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── liaoyaxiong.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── liaoyaxiong.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── Pinger ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── LDSRouterInfo.h ├── LDSRouterInfo.m ├── SceneDelegate.h ├── SceneDelegate.m ├── SimplePing.h ├── SimplePing.m ├── ViewController.h ├── ViewController.m ├── main.m └── route.h ├── PingerTests ├── Info.plist └── PingerTests.m ├── PingerUITests ├── Info.plist └── PingerUITests.m └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asomePubcode/Pinger/b9f0b49d07ef6c683e5aed212e3d3a8e4127f4d5/.DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 asome public code 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Pinger.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | BE1C4E612511C41200CBE14B /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = BE1C4E602511C41200CBE14B /* AppDelegate.m */; }; 11 | BE1C4E642511C41200CBE14B /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = BE1C4E632511C41200CBE14B /* SceneDelegate.m */; }; 12 | BE1C4E672511C41200CBE14B /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BE1C4E662511C41200CBE14B /* ViewController.m */; }; 13 | BE1C4E6A2511C41200CBE14B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BE1C4E682511C41200CBE14B /* Main.storyboard */; }; 14 | BE1C4E6C2511C41300CBE14B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BE1C4E6B2511C41300CBE14B /* Assets.xcassets */; }; 15 | BE1C4E6F2511C41300CBE14B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BE1C4E6D2511C41300CBE14B /* LaunchScreen.storyboard */; }; 16 | BE1C4E722511C41300CBE14B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = BE1C4E712511C41300CBE14B /* main.m */; }; 17 | BE1C4E7C2511C41400CBE14B /* PingerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BE1C4E7B2511C41400CBE14B /* PingerTests.m */; }; 18 | BE1C4E872511C41400CBE14B /* PingerUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = BE1C4E862511C41400CBE14B /* PingerUITests.m */; }; 19 | BE1C4E962511C42600CBE14B /* SimplePing.m in Sources */ = {isa = PBXBuildFile; fileRef = BE1C4E952511C42600CBE14B /* SimplePing.m */; }; 20 | BE1C4EAC2511F72A00CBE14B /* LDSRouterInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = BE1C4EAA2511F72A00CBE14B /* LDSRouterInfo.m */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXContainerItemProxy section */ 24 | BE1C4E782511C41400CBE14B /* PBXContainerItemProxy */ = { 25 | isa = PBXContainerItemProxy; 26 | containerPortal = BE1C4E542511C41200CBE14B /* Project object */; 27 | proxyType = 1; 28 | remoteGlobalIDString = BE1C4E5B2511C41200CBE14B; 29 | remoteInfo = Pinger; 30 | }; 31 | BE1C4E832511C41400CBE14B /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = BE1C4E542511C41200CBE14B /* Project object */; 34 | proxyType = 1; 35 | remoteGlobalIDString = BE1C4E5B2511C41200CBE14B; 36 | remoteInfo = Pinger; 37 | }; 38 | /* End PBXContainerItemProxy section */ 39 | 40 | /* Begin PBXFileReference section */ 41 | BE1C4E5C2511C41200CBE14B /* Pinger.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Pinger.app; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | BE1C4E5F2511C41200CBE14B /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 43 | BE1C4E602511C41200CBE14B /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 44 | BE1C4E622511C41200CBE14B /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; }; 45 | BE1C4E632511C41200CBE14B /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = ""; }; 46 | BE1C4E652511C41200CBE14B /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 47 | BE1C4E662511C41200CBE14B /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 48 | BE1C4E692511C41200CBE14B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 49 | BE1C4E6B2511C41300CBE14B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 50 | BE1C4E6E2511C41300CBE14B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 51 | BE1C4E702511C41300CBE14B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 52 | BE1C4E712511C41300CBE14B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 53 | BE1C4E772511C41400CBE14B /* PingerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PingerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | BE1C4E7B2511C41400CBE14B /* PingerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PingerTests.m; sourceTree = ""; }; 55 | BE1C4E7D2511C41400CBE14B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 56 | BE1C4E822511C41400CBE14B /* PingerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PingerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | BE1C4E862511C41400CBE14B /* PingerUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PingerUITests.m; sourceTree = ""; }; 58 | BE1C4E882511C41400CBE14B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 59 | BE1C4E942511C42600CBE14B /* SimplePing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimplePing.h; sourceTree = ""; }; 60 | BE1C4E952511C42600CBE14B /* SimplePing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SimplePing.m; sourceTree = ""; }; 61 | BE1C4EA92511F72A00CBE14B /* LDSRouterInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LDSRouterInfo.h; sourceTree = ""; }; 62 | BE1C4EAA2511F72A00CBE14B /* LDSRouterInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LDSRouterInfo.m; sourceTree = ""; }; 63 | BE1C4EAB2511F72A00CBE14B /* route.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = route.h; sourceTree = ""; }; 64 | /* End PBXFileReference section */ 65 | 66 | /* Begin PBXFrameworksBuildPhase section */ 67 | BE1C4E592511C41200CBE14B /* Frameworks */ = { 68 | isa = PBXFrameworksBuildPhase; 69 | buildActionMask = 2147483647; 70 | files = ( 71 | ); 72 | runOnlyForDeploymentPostprocessing = 0; 73 | }; 74 | BE1C4E742511C41400CBE14B /* Frameworks */ = { 75 | isa = PBXFrameworksBuildPhase; 76 | buildActionMask = 2147483647; 77 | files = ( 78 | ); 79 | runOnlyForDeploymentPostprocessing = 0; 80 | }; 81 | BE1C4E7F2511C41400CBE14B /* Frameworks */ = { 82 | isa = PBXFrameworksBuildPhase; 83 | buildActionMask = 2147483647; 84 | files = ( 85 | ); 86 | runOnlyForDeploymentPostprocessing = 0; 87 | }; 88 | /* End PBXFrameworksBuildPhase section */ 89 | 90 | /* Begin PBXGroup section */ 91 | BE1C4E532511C41200CBE14B = { 92 | isa = PBXGroup; 93 | children = ( 94 | BE1C4E5E2511C41200CBE14B /* Pinger */, 95 | BE1C4E7A2511C41400CBE14B /* PingerTests */, 96 | BE1C4E852511C41400CBE14B /* PingerUITests */, 97 | BE1C4E5D2511C41200CBE14B /* Products */, 98 | ); 99 | sourceTree = ""; 100 | }; 101 | BE1C4E5D2511C41200CBE14B /* Products */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | BE1C4E5C2511C41200CBE14B /* Pinger.app */, 105 | BE1C4E772511C41400CBE14B /* PingerTests.xctest */, 106 | BE1C4E822511C41400CBE14B /* PingerUITests.xctest */, 107 | ); 108 | name = Products; 109 | sourceTree = ""; 110 | }; 111 | BE1C4E5E2511C41200CBE14B /* Pinger */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | BE1C4EA92511F72A00CBE14B /* LDSRouterInfo.h */, 115 | BE1C4EAA2511F72A00CBE14B /* LDSRouterInfo.m */, 116 | BE1C4EAB2511F72A00CBE14B /* route.h */, 117 | BE1C4E942511C42600CBE14B /* SimplePing.h */, 118 | BE1C4E952511C42600CBE14B /* SimplePing.m */, 119 | BE1C4E5F2511C41200CBE14B /* AppDelegate.h */, 120 | BE1C4E602511C41200CBE14B /* AppDelegate.m */, 121 | BE1C4E622511C41200CBE14B /* SceneDelegate.h */, 122 | BE1C4E632511C41200CBE14B /* SceneDelegate.m */, 123 | BE1C4E652511C41200CBE14B /* ViewController.h */, 124 | BE1C4E662511C41200CBE14B /* ViewController.m */, 125 | BE1C4E682511C41200CBE14B /* Main.storyboard */, 126 | BE1C4E6B2511C41300CBE14B /* Assets.xcassets */, 127 | BE1C4E6D2511C41300CBE14B /* LaunchScreen.storyboard */, 128 | BE1C4E702511C41300CBE14B /* Info.plist */, 129 | BE1C4E712511C41300CBE14B /* main.m */, 130 | ); 131 | path = Pinger; 132 | sourceTree = ""; 133 | }; 134 | BE1C4E7A2511C41400CBE14B /* PingerTests */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | BE1C4E7B2511C41400CBE14B /* PingerTests.m */, 138 | BE1C4E7D2511C41400CBE14B /* Info.plist */, 139 | ); 140 | path = PingerTests; 141 | sourceTree = ""; 142 | }; 143 | BE1C4E852511C41400CBE14B /* PingerUITests */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | BE1C4E862511C41400CBE14B /* PingerUITests.m */, 147 | BE1C4E882511C41400CBE14B /* Info.plist */, 148 | ); 149 | path = PingerUITests; 150 | sourceTree = ""; 151 | }; 152 | /* End PBXGroup section */ 153 | 154 | /* Begin PBXNativeTarget section */ 155 | BE1C4E5B2511C41200CBE14B /* Pinger */ = { 156 | isa = PBXNativeTarget; 157 | buildConfigurationList = BE1C4E8B2511C41400CBE14B /* Build configuration list for PBXNativeTarget "Pinger" */; 158 | buildPhases = ( 159 | BE1C4E582511C41200CBE14B /* Sources */, 160 | BE1C4E592511C41200CBE14B /* Frameworks */, 161 | BE1C4E5A2511C41200CBE14B /* Resources */, 162 | ); 163 | buildRules = ( 164 | ); 165 | dependencies = ( 166 | ); 167 | name = Pinger; 168 | productName = Pinger; 169 | productReference = BE1C4E5C2511C41200CBE14B /* Pinger.app */; 170 | productType = "com.apple.product-type.application"; 171 | }; 172 | BE1C4E762511C41400CBE14B /* PingerTests */ = { 173 | isa = PBXNativeTarget; 174 | buildConfigurationList = BE1C4E8E2511C41400CBE14B /* Build configuration list for PBXNativeTarget "PingerTests" */; 175 | buildPhases = ( 176 | BE1C4E732511C41400CBE14B /* Sources */, 177 | BE1C4E742511C41400CBE14B /* Frameworks */, 178 | BE1C4E752511C41400CBE14B /* Resources */, 179 | ); 180 | buildRules = ( 181 | ); 182 | dependencies = ( 183 | BE1C4E792511C41400CBE14B /* PBXTargetDependency */, 184 | ); 185 | name = PingerTests; 186 | productName = PingerTests; 187 | productReference = BE1C4E772511C41400CBE14B /* PingerTests.xctest */; 188 | productType = "com.apple.product-type.bundle.unit-test"; 189 | }; 190 | BE1C4E812511C41400CBE14B /* PingerUITests */ = { 191 | isa = PBXNativeTarget; 192 | buildConfigurationList = BE1C4E912511C41400CBE14B /* Build configuration list for PBXNativeTarget "PingerUITests" */; 193 | buildPhases = ( 194 | BE1C4E7E2511C41400CBE14B /* Sources */, 195 | BE1C4E7F2511C41400CBE14B /* Frameworks */, 196 | BE1C4E802511C41400CBE14B /* Resources */, 197 | ); 198 | buildRules = ( 199 | ); 200 | dependencies = ( 201 | BE1C4E842511C41400CBE14B /* PBXTargetDependency */, 202 | ); 203 | name = PingerUITests; 204 | productName = PingerUITests; 205 | productReference = BE1C4E822511C41400CBE14B /* PingerUITests.xctest */; 206 | productType = "com.apple.product-type.bundle.ui-testing"; 207 | }; 208 | /* End PBXNativeTarget section */ 209 | 210 | /* Begin PBXProject section */ 211 | BE1C4E542511C41200CBE14B /* Project object */ = { 212 | isa = PBXProject; 213 | attributes = { 214 | LastUpgradeCheck = 1200; 215 | TargetAttributes = { 216 | BE1C4E5B2511C41200CBE14B = { 217 | CreatedOnToolsVersion = 12.0; 218 | }; 219 | BE1C4E762511C41400CBE14B = { 220 | CreatedOnToolsVersion = 12.0; 221 | TestTargetID = BE1C4E5B2511C41200CBE14B; 222 | }; 223 | BE1C4E812511C41400CBE14B = { 224 | CreatedOnToolsVersion = 12.0; 225 | TestTargetID = BE1C4E5B2511C41200CBE14B; 226 | }; 227 | }; 228 | }; 229 | buildConfigurationList = BE1C4E572511C41200CBE14B /* Build configuration list for PBXProject "Pinger" */; 230 | compatibilityVersion = "Xcode 9.3"; 231 | developmentRegion = en; 232 | hasScannedForEncodings = 0; 233 | knownRegions = ( 234 | en, 235 | Base, 236 | ); 237 | mainGroup = BE1C4E532511C41200CBE14B; 238 | productRefGroup = BE1C4E5D2511C41200CBE14B /* Products */; 239 | projectDirPath = ""; 240 | projectRoot = ""; 241 | targets = ( 242 | BE1C4E5B2511C41200CBE14B /* Pinger */, 243 | BE1C4E762511C41400CBE14B /* PingerTests */, 244 | BE1C4E812511C41400CBE14B /* PingerUITests */, 245 | ); 246 | }; 247 | /* End PBXProject section */ 248 | 249 | /* Begin PBXResourcesBuildPhase section */ 250 | BE1C4E5A2511C41200CBE14B /* Resources */ = { 251 | isa = PBXResourcesBuildPhase; 252 | buildActionMask = 2147483647; 253 | files = ( 254 | BE1C4E6F2511C41300CBE14B /* LaunchScreen.storyboard in Resources */, 255 | BE1C4E6C2511C41300CBE14B /* Assets.xcassets in Resources */, 256 | BE1C4E6A2511C41200CBE14B /* Main.storyboard in Resources */, 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | }; 260 | BE1C4E752511C41400CBE14B /* Resources */ = { 261 | isa = PBXResourcesBuildPhase; 262 | buildActionMask = 2147483647; 263 | files = ( 264 | ); 265 | runOnlyForDeploymentPostprocessing = 0; 266 | }; 267 | BE1C4E802511C41400CBE14B /* Resources */ = { 268 | isa = PBXResourcesBuildPhase; 269 | buildActionMask = 2147483647; 270 | files = ( 271 | ); 272 | runOnlyForDeploymentPostprocessing = 0; 273 | }; 274 | /* End PBXResourcesBuildPhase section */ 275 | 276 | /* Begin PBXSourcesBuildPhase section */ 277 | BE1C4E582511C41200CBE14B /* Sources */ = { 278 | isa = PBXSourcesBuildPhase; 279 | buildActionMask = 2147483647; 280 | files = ( 281 | BE1C4E672511C41200CBE14B /* ViewController.m in Sources */, 282 | BE1C4E612511C41200CBE14B /* AppDelegate.m in Sources */, 283 | BE1C4EAC2511F72A00CBE14B /* LDSRouterInfo.m in Sources */, 284 | BE1C4E722511C41300CBE14B /* main.m in Sources */, 285 | BE1C4E962511C42600CBE14B /* SimplePing.m in Sources */, 286 | BE1C4E642511C41200CBE14B /* SceneDelegate.m in Sources */, 287 | ); 288 | runOnlyForDeploymentPostprocessing = 0; 289 | }; 290 | BE1C4E732511C41400CBE14B /* Sources */ = { 291 | isa = PBXSourcesBuildPhase; 292 | buildActionMask = 2147483647; 293 | files = ( 294 | BE1C4E7C2511C41400CBE14B /* PingerTests.m in Sources */, 295 | ); 296 | runOnlyForDeploymentPostprocessing = 0; 297 | }; 298 | BE1C4E7E2511C41400CBE14B /* Sources */ = { 299 | isa = PBXSourcesBuildPhase; 300 | buildActionMask = 2147483647; 301 | files = ( 302 | BE1C4E872511C41400CBE14B /* PingerUITests.m in Sources */, 303 | ); 304 | runOnlyForDeploymentPostprocessing = 0; 305 | }; 306 | /* End PBXSourcesBuildPhase section */ 307 | 308 | /* Begin PBXTargetDependency section */ 309 | BE1C4E792511C41400CBE14B /* PBXTargetDependency */ = { 310 | isa = PBXTargetDependency; 311 | target = BE1C4E5B2511C41200CBE14B /* Pinger */; 312 | targetProxy = BE1C4E782511C41400CBE14B /* PBXContainerItemProxy */; 313 | }; 314 | BE1C4E842511C41400CBE14B /* PBXTargetDependency */ = { 315 | isa = PBXTargetDependency; 316 | target = BE1C4E5B2511C41200CBE14B /* Pinger */; 317 | targetProxy = BE1C4E832511C41400CBE14B /* PBXContainerItemProxy */; 318 | }; 319 | /* End PBXTargetDependency section */ 320 | 321 | /* Begin PBXVariantGroup section */ 322 | BE1C4E682511C41200CBE14B /* Main.storyboard */ = { 323 | isa = PBXVariantGroup; 324 | children = ( 325 | BE1C4E692511C41200CBE14B /* Base */, 326 | ); 327 | name = Main.storyboard; 328 | sourceTree = ""; 329 | }; 330 | BE1C4E6D2511C41300CBE14B /* LaunchScreen.storyboard */ = { 331 | isa = PBXVariantGroup; 332 | children = ( 333 | BE1C4E6E2511C41300CBE14B /* Base */, 334 | ); 335 | name = LaunchScreen.storyboard; 336 | sourceTree = ""; 337 | }; 338 | /* End PBXVariantGroup section */ 339 | 340 | /* Begin XCBuildConfiguration section */ 341 | BE1C4E892511C41400CBE14B /* Debug */ = { 342 | isa = XCBuildConfiguration; 343 | buildSettings = { 344 | ALWAYS_SEARCH_USER_PATHS = NO; 345 | CLANG_ANALYZER_NONNULL = YES; 346 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 347 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 348 | CLANG_CXX_LIBRARY = "libc++"; 349 | CLANG_ENABLE_MODULES = YES; 350 | CLANG_ENABLE_OBJC_ARC = YES; 351 | CLANG_ENABLE_OBJC_WEAK = YES; 352 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 353 | CLANG_WARN_BOOL_CONVERSION = YES; 354 | CLANG_WARN_COMMA = YES; 355 | CLANG_WARN_CONSTANT_CONVERSION = YES; 356 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 357 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 358 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 359 | CLANG_WARN_EMPTY_BODY = YES; 360 | CLANG_WARN_ENUM_CONVERSION = YES; 361 | CLANG_WARN_INFINITE_RECURSION = YES; 362 | CLANG_WARN_INT_CONVERSION = YES; 363 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 364 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 365 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 366 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 367 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 368 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 369 | CLANG_WARN_STRICT_PROTOTYPES = YES; 370 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 371 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 372 | CLANG_WARN_UNREACHABLE_CODE = YES; 373 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 374 | COPY_PHASE_STRIP = NO; 375 | DEBUG_INFORMATION_FORMAT = dwarf; 376 | ENABLE_STRICT_OBJC_MSGSEND = YES; 377 | ENABLE_TESTABILITY = YES; 378 | GCC_C_LANGUAGE_STANDARD = gnu11; 379 | GCC_DYNAMIC_NO_PIC = NO; 380 | GCC_NO_COMMON_BLOCKS = YES; 381 | GCC_OPTIMIZATION_LEVEL = 0; 382 | GCC_PREPROCESSOR_DEFINITIONS = ( 383 | "DEBUG=1", 384 | "$(inherited)", 385 | ); 386 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 387 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 388 | GCC_WARN_UNDECLARED_SELECTOR = YES; 389 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 390 | GCC_WARN_UNUSED_FUNCTION = YES; 391 | GCC_WARN_UNUSED_VARIABLE = YES; 392 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 393 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 394 | MTL_FAST_MATH = YES; 395 | ONLY_ACTIVE_ARCH = YES; 396 | SDKROOT = iphoneos; 397 | }; 398 | name = Debug; 399 | }; 400 | BE1C4E8A2511C41400CBE14B /* Release */ = { 401 | isa = XCBuildConfiguration; 402 | buildSettings = { 403 | ALWAYS_SEARCH_USER_PATHS = NO; 404 | CLANG_ANALYZER_NONNULL = YES; 405 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 406 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 407 | CLANG_CXX_LIBRARY = "libc++"; 408 | CLANG_ENABLE_MODULES = YES; 409 | CLANG_ENABLE_OBJC_ARC = YES; 410 | CLANG_ENABLE_OBJC_WEAK = YES; 411 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 412 | CLANG_WARN_BOOL_CONVERSION = YES; 413 | CLANG_WARN_COMMA = YES; 414 | CLANG_WARN_CONSTANT_CONVERSION = YES; 415 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 416 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 417 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 418 | CLANG_WARN_EMPTY_BODY = YES; 419 | CLANG_WARN_ENUM_CONVERSION = YES; 420 | CLANG_WARN_INFINITE_RECURSION = YES; 421 | CLANG_WARN_INT_CONVERSION = YES; 422 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 423 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 424 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 425 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 426 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 427 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 428 | CLANG_WARN_STRICT_PROTOTYPES = YES; 429 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 430 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 431 | CLANG_WARN_UNREACHABLE_CODE = YES; 432 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 433 | COPY_PHASE_STRIP = NO; 434 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 435 | ENABLE_NS_ASSERTIONS = NO; 436 | ENABLE_STRICT_OBJC_MSGSEND = YES; 437 | GCC_C_LANGUAGE_STANDARD = gnu11; 438 | GCC_NO_COMMON_BLOCKS = YES; 439 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 440 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 441 | GCC_WARN_UNDECLARED_SELECTOR = YES; 442 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 443 | GCC_WARN_UNUSED_FUNCTION = YES; 444 | GCC_WARN_UNUSED_VARIABLE = YES; 445 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 446 | MTL_ENABLE_DEBUG_INFO = NO; 447 | MTL_FAST_MATH = YES; 448 | SDKROOT = iphoneos; 449 | VALIDATE_PRODUCT = YES; 450 | }; 451 | name = Release; 452 | }; 453 | BE1C4E8C2511C41400CBE14B /* Debug */ = { 454 | isa = XCBuildConfiguration; 455 | buildSettings = { 456 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 457 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 458 | CODE_SIGN_IDENTITY = "iPhone Developer"; 459 | CODE_SIGN_STYLE = Manual; 460 | DEVELOPMENT_TEAM = 4AQATEQGY4; 461 | INFOPLIST_FILE = Pinger/Info.plist; 462 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 463 | LD_RUNPATH_SEARCH_PATHS = ( 464 | "$(inherited)", 465 | "@executable_path/Frameworks", 466 | ); 467 | PRODUCT_BUNDLE_IDENTIFIER = com.iotsmarthome.lds; 468 | PRODUCT_NAME = "$(TARGET_NAME)"; 469 | PROVISIONING_PROFILE_SPECIFIER = comIotsmarthomeLdsDevelopment; 470 | TARGETED_DEVICE_FAMILY = "1,2"; 471 | }; 472 | name = Debug; 473 | }; 474 | BE1C4E8D2511C41400CBE14B /* Release */ = { 475 | isa = XCBuildConfiguration; 476 | buildSettings = { 477 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 478 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 479 | CODE_SIGN_IDENTITY = "iPhone Developer"; 480 | CODE_SIGN_STYLE = Manual; 481 | DEVELOPMENT_TEAM = 4AQATEQGY4; 482 | INFOPLIST_FILE = Pinger/Info.plist; 483 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 484 | LD_RUNPATH_SEARCH_PATHS = ( 485 | "$(inherited)", 486 | "@executable_path/Frameworks", 487 | ); 488 | PRODUCT_BUNDLE_IDENTIFIER = com.iotsmarthome.lds; 489 | PRODUCT_NAME = "$(TARGET_NAME)"; 490 | PROVISIONING_PROFILE_SPECIFIER = comIotsmarthomeLdsDevelopment; 491 | TARGETED_DEVICE_FAMILY = "1,2"; 492 | }; 493 | name = Release; 494 | }; 495 | BE1C4E8F2511C41400CBE14B /* Debug */ = { 496 | isa = XCBuildConfiguration; 497 | buildSettings = { 498 | BUNDLE_LOADER = "$(TEST_HOST)"; 499 | CODE_SIGN_STYLE = Automatic; 500 | INFOPLIST_FILE = PingerTests/Info.plist; 501 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 502 | LD_RUNPATH_SEARCH_PATHS = ( 503 | "$(inherited)", 504 | "@executable_path/Frameworks", 505 | "@loader_path/Frameworks", 506 | ); 507 | PRODUCT_BUNDLE_IDENTIFIER = asml.PingerTests; 508 | PRODUCT_NAME = "$(TARGET_NAME)"; 509 | TARGETED_DEVICE_FAMILY = "1,2"; 510 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Pinger.app/Pinger"; 511 | }; 512 | name = Debug; 513 | }; 514 | BE1C4E902511C41400CBE14B /* Release */ = { 515 | isa = XCBuildConfiguration; 516 | buildSettings = { 517 | BUNDLE_LOADER = "$(TEST_HOST)"; 518 | CODE_SIGN_STYLE = Automatic; 519 | INFOPLIST_FILE = PingerTests/Info.plist; 520 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 521 | LD_RUNPATH_SEARCH_PATHS = ( 522 | "$(inherited)", 523 | "@executable_path/Frameworks", 524 | "@loader_path/Frameworks", 525 | ); 526 | PRODUCT_BUNDLE_IDENTIFIER = asml.PingerTests; 527 | PRODUCT_NAME = "$(TARGET_NAME)"; 528 | TARGETED_DEVICE_FAMILY = "1,2"; 529 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Pinger.app/Pinger"; 530 | }; 531 | name = Release; 532 | }; 533 | BE1C4E922511C41400CBE14B /* Debug */ = { 534 | isa = XCBuildConfiguration; 535 | buildSettings = { 536 | CODE_SIGN_STYLE = Automatic; 537 | INFOPLIST_FILE = PingerUITests/Info.plist; 538 | LD_RUNPATH_SEARCH_PATHS = ( 539 | "$(inherited)", 540 | "@executable_path/Frameworks", 541 | "@loader_path/Frameworks", 542 | ); 543 | PRODUCT_BUNDLE_IDENTIFIER = asml.PingerUITests; 544 | PRODUCT_NAME = "$(TARGET_NAME)"; 545 | TARGETED_DEVICE_FAMILY = "1,2"; 546 | TEST_TARGET_NAME = Pinger; 547 | }; 548 | name = Debug; 549 | }; 550 | BE1C4E932511C41400CBE14B /* Release */ = { 551 | isa = XCBuildConfiguration; 552 | buildSettings = { 553 | CODE_SIGN_STYLE = Automatic; 554 | INFOPLIST_FILE = PingerUITests/Info.plist; 555 | LD_RUNPATH_SEARCH_PATHS = ( 556 | "$(inherited)", 557 | "@executable_path/Frameworks", 558 | "@loader_path/Frameworks", 559 | ); 560 | PRODUCT_BUNDLE_IDENTIFIER = asml.PingerUITests; 561 | PRODUCT_NAME = "$(TARGET_NAME)"; 562 | TARGETED_DEVICE_FAMILY = "1,2"; 563 | TEST_TARGET_NAME = Pinger; 564 | }; 565 | name = Release; 566 | }; 567 | /* End XCBuildConfiguration section */ 568 | 569 | /* Begin XCConfigurationList section */ 570 | BE1C4E572511C41200CBE14B /* Build configuration list for PBXProject "Pinger" */ = { 571 | isa = XCConfigurationList; 572 | buildConfigurations = ( 573 | BE1C4E892511C41400CBE14B /* Debug */, 574 | BE1C4E8A2511C41400CBE14B /* Release */, 575 | ); 576 | defaultConfigurationIsVisible = 0; 577 | defaultConfigurationName = Release; 578 | }; 579 | BE1C4E8B2511C41400CBE14B /* Build configuration list for PBXNativeTarget "Pinger" */ = { 580 | isa = XCConfigurationList; 581 | buildConfigurations = ( 582 | BE1C4E8C2511C41400CBE14B /* Debug */, 583 | BE1C4E8D2511C41400CBE14B /* Release */, 584 | ); 585 | defaultConfigurationIsVisible = 0; 586 | defaultConfigurationName = Release; 587 | }; 588 | BE1C4E8E2511C41400CBE14B /* Build configuration list for PBXNativeTarget "PingerTests" */ = { 589 | isa = XCConfigurationList; 590 | buildConfigurations = ( 591 | BE1C4E8F2511C41400CBE14B /* Debug */, 592 | BE1C4E902511C41400CBE14B /* Release */, 593 | ); 594 | defaultConfigurationIsVisible = 0; 595 | defaultConfigurationName = Release; 596 | }; 597 | BE1C4E912511C41400CBE14B /* Build configuration list for PBXNativeTarget "PingerUITests" */ = { 598 | isa = XCConfigurationList; 599 | buildConfigurations = ( 600 | BE1C4E922511C41400CBE14B /* Debug */, 601 | BE1C4E932511C41400CBE14B /* Release */, 602 | ); 603 | defaultConfigurationIsVisible = 0; 604 | defaultConfigurationName = Release; 605 | }; 606 | /* End XCConfigurationList section */ 607 | }; 608 | rootObject = BE1C4E542511C41200CBE14B /* Project object */; 609 | } 610 | -------------------------------------------------------------------------------- /Pinger.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Pinger.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Pinger.xcodeproj/project.xcworkspace/xcuserdata/liaoyaxiong.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asomePubcode/Pinger/b9f0b49d07ef6c683e5aed212e3d3a8e4127f4d5/Pinger.xcodeproj/project.xcworkspace/xcuserdata/liaoyaxiong.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Pinger.xcodeproj/xcuserdata/liaoyaxiong.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Pinger.xcodeproj/xcuserdata/liaoyaxiong.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Pinger.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Pinger/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // Pinger 4 | // 5 | // Created by 廖亚雄 on 2020/9/16. 6 | // 7 | 8 | #import 9 | 10 | @interface AppDelegate : UIResponder 11 | 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /Pinger/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // Pinger 4 | // 5 | // Created by 廖亚雄 on 2020/9/16. 6 | // 7 | 8 | #import "AppDelegate.h" 9 | 10 | @interface AppDelegate () 11 | 12 | @end 13 | 14 | @implementation AppDelegate 15 | 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 18 | // Override point for customization after application launch. 19 | return YES; 20 | } 21 | 22 | 23 | #pragma mark - UISceneSession lifecycle 24 | 25 | 26 | - (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options { 27 | // Called when a new scene session is being created. 28 | // Use this method to select a configuration to create the new scene with. 29 | return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role]; 30 | } 31 | 32 | 33 | - (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions { 34 | // Called when the user discards a scene session. 35 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 36 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 37 | } 38 | 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /Pinger/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Pinger/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Pinger/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Pinger/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Pinger/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Pinger/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UIApplicationSupportsIndirectInputEvents 43 | 44 | UILaunchStoryboardName 45 | LaunchScreen 46 | UIMainStoryboardFile 47 | Main 48 | UIRequiredDeviceCapabilities 49 | 50 | armv7 51 | 52 | UISupportedInterfaceOrientations 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationLandscapeLeft 56 | UIInterfaceOrientationLandscapeRight 57 | 58 | UISupportedInterfaceOrientations~ipad 59 | 60 | UIInterfaceOrientationPortrait 61 | UIInterfaceOrientationPortraitUpsideDown 62 | UIInterfaceOrientationLandscapeLeft 63 | UIInterfaceOrientationLandscapeRight 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /Pinger/LDSRouterInfo.h: -------------------------------------------------------------------------------- 1 | // 2 | // LDSRouterInfo.h 3 | // LDSProtocol 4 | // 5 | // Created by 廖亚雄 on 2019/3/15. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface LDSRouterInfo : NSObject 13 | + (NSDictionary *)getRouterInfo; 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /Pinger/LDSRouterInfo.m: -------------------------------------------------------------------------------- 1 | // 2 | // LDSRouterInfo.m 3 | // LDSProtocol 4 | // 5 | // Created by 廖亚雄 on 2019/3/15. 6 | // 7 | 8 | #import "LDSRouterInfo.h" 9 | #import 10 | #import 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | //#include "getgateway.h" 17 | #include "route.h" 18 | #include 19 | #include 20 | 21 | #define CTL_NET 4 /* network, see socket.h */ 22 | int getdefaultgateway(in_addr_t * addr); 23 | #if defined(BSD) || defined(__APPLE__) 24 | 25 | #define ROUNDUP(a) \ 26 | ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 27 | int getdefaultgateway(in_addr_t * addr) 28 | { 29 | int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, 30 | NET_RT_FLAGS, RTF_GATEWAY}; 31 | size_t l; 32 | char * buf, * p; 33 | struct rt_msghdr * rt; 34 | struct sockaddr * sa; 35 | struct sockaddr * sa_tab[RTAX_MAX]; 36 | int i; 37 | int r = -1; 38 | if(sysctl(mib, sizeof(mib)/sizeof(int), 0, &l, 0, 0) < 0) { 39 | return -1; 40 | } 41 | if(l>0) { 42 | buf = malloc(l); 43 | if(sysctl(mib, sizeof(mib)/sizeof(int), buf, &l, 0, 0) < 0) { 44 | return -1; 45 | } 46 | for(p=buf; prtm_msglen) { 47 | rt = (struct rt_msghdr *)p; 48 | sa = (struct sockaddr *)(rt + 1); 49 | for(i=0; irtm_addrs & (1 << i)) { 51 | sa_tab[i] = sa; 52 | sa = (struct sockaddr *)((char *)sa + ROUNDUP(sa->sa_len)); 53 | } else { 54 | sa_tab[i] = NULL; 55 | } 56 | } 57 | 58 | if( ((rt->rtm_addrs & (RTA_DST|RTA_GATEWAY)) == (RTA_DST|RTA_GATEWAY)) 59 | && sa_tab[RTAX_DST]->sa_family == AF_INET 60 | && sa_tab[RTAX_GATEWAY]->sa_family == AF_INET) { 61 | 62 | 63 | if(((struct sockaddr_in *)sa_tab[RTAX_DST])->sin_addr.s_addr == 0) { 64 | char ifName[128]; 65 | if_indextoname(rt->rtm_index,ifName); 66 | 67 | if(strcmp("en0",ifName)==0){ 68 | 69 | *addr = ((struct sockaddr_in *)(sa_tab[RTAX_GATEWAY]))->sin_addr.s_addr; 70 | r = 0; 71 | } 72 | } 73 | } 74 | } 75 | free(buf); 76 | } 77 | return r; 78 | } 79 | #endif 80 | @implementation LDSRouterInfo 81 | + (NSDictionary *)getRouterInfo { 82 | NSMutableDictionary *router = @{}.mutableCopy; 83 | 84 | NSString *address = nil; 85 | struct ifaddrs *interfaces = NULL; 86 | struct ifaddrs *temp_addr = NULL; 87 | int success = 0; 88 | 89 | success = getifaddrs(&interfaces); 90 | 91 | if (success == 0) { 92 | 93 | temp_addr = interfaces; 94 | 95 | while(temp_addr != NULL) { 96 | 97 | if(temp_addr->ifa_addr->sa_family == AF_INET) { 98 | 99 | if([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) { 100 | NSString *netmask = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_netmask)->sin_addr)]; 101 | NSLog(@"子网掩码:%@",netmask); 102 | NSString *ip = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)]; 103 | NSLog(@"本地IP:%@",ip); 104 | NSString *boardCast = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_dstaddr)->sin_addr)]; 105 | NSLog(@"广播地址:%@",boardCast); 106 | 107 | [router setObject:netmask?:@"" forKey:@"subnetMask"]; 108 | [router setObject:ip?:@"" forKey:@"ip"]; 109 | [router setObject:boardCast?:@"" forKey:@"dns"]; 110 | } 111 | } 112 | temp_addr = temp_addr->ifa_next; 113 | } 114 | } 115 | freeifaddrs(interfaces); 116 | struct in_addr addr; 117 | NSString * addrStr = @""; 118 | if (getdefaultgateway(&(addr.s_addr)) == 0) 119 | { 120 | addrStr = [NSString stringWithUTF8String:inet_ntoa(addr)]; 121 | } 122 | [router setObject:addrStr forKey:@"router"]; 123 | 124 | return router.copy; 125 | } 126 | 127 | 128 | @end 129 | -------------------------------------------------------------------------------- /Pinger/SceneDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.h 3 | // Pinger 4 | // 5 | // Created by 廖亚雄 on 2020/9/16. 6 | // 7 | 8 | #import 9 | 10 | @interface SceneDelegate : UIResponder 11 | 12 | @property (strong, nonatomic) UIWindow * window; 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Pinger/SceneDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.m 3 | // Pinger 4 | // 5 | // Created by 廖亚雄 on 2020/9/16. 6 | // 7 | 8 | #import "SceneDelegate.h" 9 | 10 | @interface SceneDelegate () 11 | 12 | @end 13 | 14 | @implementation SceneDelegate 15 | 16 | 17 | - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions { 18 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 19 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 20 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 21 | } 22 | 23 | 24 | - (void)sceneDidDisconnect:(UIScene *)scene { 25 | // Called as the scene is being released by the system. 26 | // This occurs shortly after the scene enters the background, or when its session is discarded. 27 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 28 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 29 | } 30 | 31 | 32 | - (void)sceneDidBecomeActive:(UIScene *)scene { 33 | // Called when the scene has moved from an inactive state to an active state. 34 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 35 | } 36 | 37 | 38 | - (void)sceneWillResignActive:(UIScene *)scene { 39 | // Called when the scene will move from an active state to an inactive state. 40 | // This may occur due to temporary interruptions (ex. an incoming phone call). 41 | } 42 | 43 | 44 | - (void)sceneWillEnterForeground:(UIScene *)scene { 45 | // Called as the scene transitions from the background to the foreground. 46 | // Use this method to undo the changes made on entering the background. 47 | } 48 | 49 | 50 | - (void)sceneDidEnterBackground:(UIScene *)scene { 51 | // Called as the scene transitions from the foreground to the background. 52 | // Use this method to save data, release shared resources, and store enough scene-specific state information 53 | // to restore the scene back to its current state. 54 | } 55 | 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /Pinger/SimplePing.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2016 Apple Inc. All Rights Reserved. 3 | See LICENSE.txt for this sample’s licensing information 4 | 5 | Abstract: 6 | An object wrapper around the low-level BSD Sockets ping function. 7 | */ 8 | 9 | @import Foundation; 10 | 11 | #include // for __Check_Compile_Time 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @protocol SimplePingDelegate; 16 | 17 | /*! Controls the IP address version used by SimplePing instances. 18 | */ 19 | 20 | typedef NS_ENUM(NSInteger, SimplePingAddressStyle) { 21 | SimplePingAddressStyleAny, ///< Use the first IPv4 or IPv6 address found; the default. 22 | SimplePingAddressStyleICMPv4, ///< Use the first IPv4 address found. 23 | SimplePingAddressStyleICMPv6 ///< Use the first IPv6 address found. 24 | }; 25 | 26 | /*! An object wrapper around the low-level BSD Sockets ping function. 27 | * \details To use the class create an instance, set the delegate and call `-start` 28 | * to start the instance on the current run loop. If things go well you'll soon get the 29 | * `-simplePing:didStartWithAddress:` delegate callback. From there you can can call 30 | * `-sendPingWithData:` to send a ping and you'll receive the 31 | * `-simplePing:didReceivePingResponsePacket:sequenceNumber:` and 32 | * `-simplePing:didReceiveUnexpectedPacket:` delegate callbacks as ICMP packets arrive. 33 | * 34 | * The class can be used from any thread but the use of any single instance must be 35 | * confined to a specific thread and that thread must run its run loop. 36 | */ 37 | 38 | @interface SimplePing : NSObject 39 | 40 | - (instancetype)init NS_UNAVAILABLE; 41 | 42 | /*! Initialise the object to ping the specified host. 43 | * \param hostName The DNS name of the host to ping; an IPv4 or IPv6 address in string form will 44 | * work here. 45 | * \returns The initialised object. 46 | */ 47 | 48 | - (instancetype)initWithHostName:(NSString *)hostName NS_DESIGNATED_INITIALIZER; 49 | 50 | /*! A copy of the value passed to `-initWithHostName:`. 51 | */ 52 | 53 | @property (nonatomic, copy, readonly) NSString * hostName; 54 | 55 | /*! The delegate for this object. 56 | * \details Delegate callbacks are schedule in the default run loop mode of the run loop of the 57 | * thread that calls `-start`. 58 | */ 59 | 60 | @property (nonatomic, weak, readwrite, nullable) id delegate; 61 | 62 | /*! Controls the IP address version used by the object. 63 | * \details You should set this value before starting the object. 64 | */ 65 | 66 | @property (nonatomic, assign, readwrite) SimplePingAddressStyle addressStyle; 67 | 68 | /*! The address being pinged. 69 | * \details The contents of the NSData is a (struct sockaddr) of some form. The 70 | * value is nil while the object is stopped and remains nil on start until 71 | * `-simplePing:didStartWithAddress:` is called. 72 | */ 73 | 74 | @property (nonatomic, copy, readonly, nullable) NSData * hostAddress; 75 | 76 | /*! The address family for `hostAddress`, or `AF_UNSPEC` if that's nil. 77 | */ 78 | 79 | @property (nonatomic, assign, readonly) sa_family_t hostAddressFamily; 80 | 81 | /*! The identifier used by pings by this object. 82 | * \details When you create an instance of this object it generates a random identifier 83 | * that it uses to identify its own pings. 84 | */ 85 | 86 | @property (nonatomic, assign, readonly) uint16_t identifier; 87 | 88 | /*! The next sequence number to be used by this object. 89 | * \details This value starts at zero and increments each time you send a ping (safely 90 | * wrapping back to zero if necessary). The sequence number is included in the ping, 91 | * allowing you to match up requests and responses, and thus calculate ping times and 92 | * so on. 93 | */ 94 | 95 | @property (nonatomic, assign, readonly) uint16_t nextSequenceNumber; 96 | 97 | /*! Starts the object. 98 | * \details You should set up the delegate and any ping parameters before calling this. 99 | * 100 | * If things go well you'll soon get the `-simplePing:didStartWithAddress:` delegate 101 | * callback, at which point you can start sending pings (via `-sendPingWithData:`) and 102 | * will start receiving ICMP packets (either ping responses, via the 103 | * `-simplePing:didReceivePingResponsePacket:sequenceNumber:` delegate callback, or 104 | * unsolicited ICMP packets, via the `-simplePing:didReceiveUnexpectedPacket:` delegate 105 | * callback). 106 | * 107 | * If the object fails to start, typically because `hostName` doesn't resolve, you'll get 108 | * the `-simplePing:didFailWithError:` delegate callback. 109 | * 110 | * It is not correct to start an already started object. 111 | */ 112 | 113 | - (void)start; 114 | 115 | /*! Sends a ping packet containing the specified data. 116 | * \details Sends an actual ping. 117 | * 118 | * The object must be started when you call this method and, on starting the object, you must 119 | * wait for the `-simplePing:didStartWithAddress:` delegate callback before calling it. 120 | * \param data Some data to include in the ping packet, after the ICMP header, or nil if you 121 | * want the packet to include a standard 56 byte payload (resulting in a standard 64 byte 122 | * ping). 123 | */ 124 | 125 | - (void)sendPingWithData:(nullable NSData *)data; 126 | 127 | /*! Stops the object. 128 | * \details You should call this when you're done pinging. 129 | * 130 | * It's safe to call this on an object that's stopped. 131 | */ 132 | 133 | - (void)stop; 134 | 135 | @end 136 | 137 | /*! A delegate protocol for the SimplePing class. 138 | */ 139 | 140 | @protocol SimplePingDelegate 141 | 142 | @optional 143 | 144 | /*! A SimplePing delegate callback, called once the object has started up. 145 | * \details This is called shortly after you start the object to tell you that the 146 | * object has successfully started. On receiving this callback, you can call 147 | * `-sendPingWithData:` to send pings. 148 | * 149 | * If the object didn't start, `-simplePing:didFailWithError:` is called instead. 150 | * \param pinger The object issuing the callback. 151 | * \param address The address that's being pinged; at the time this delegate callback 152 | * is made, this will have the same value as the `hostAddress` property. 153 | */ 154 | 155 | - (void)simplePing:(SimplePing *)pinger didStartWithAddress:(NSData *)address; 156 | 157 | /*! A SimplePing delegate callback, called if the object fails to start up. 158 | * \details This is called shortly after you start the object to tell you that the 159 | * object has failed to start. The most likely cause of failure is a problem 160 | * resolving `hostName`. 161 | * 162 | * By the time this callback is called, the object has stopped (that is, you don't 163 | * need to call `-stop` yourself). 164 | * \param pinger The object issuing the callback. 165 | * \param error Describes the failure. 166 | */ 167 | 168 | - (void)simplePing:(SimplePing *)pinger didFailWithError:(NSError *)error; 169 | 170 | /*! A SimplePing delegate callback, called when the object has successfully sent a ping packet. 171 | * \details Each call to `-sendPingWithData:` will result in either a 172 | * `-simplePing:didSendPacket:sequenceNumber:` delegate callback or a 173 | * `-simplePing:didFailToSendPacket:sequenceNumber:error:` delegate callback (unless you 174 | * stop the object before you get the callback). These callbacks are currently delivered 175 | * synchronously from within `-sendPingWithData:`, but this synchronous behaviour is not 176 | * considered API. 177 | * \param pinger The object issuing the callback. 178 | * \param packet The packet that was sent; this includes the ICMP header (`ICMPHeader`) and the 179 | * data you passed to `-sendPingWithData:` but does not include any IP-level headers. 180 | * \param sequenceNumber The ICMP sequence number of that packet. 181 | */ 182 | 183 | - (void)simplePing:(SimplePing *)pinger didSendPacket:(NSData *)packet sequenceNumber:(uint16_t)sequenceNumber; 184 | 185 | /*! A SimplePing delegate callback, called when the object fails to send a ping packet. 186 | * \details Each call to `-sendPingWithData:` will result in either a 187 | * `-simplePing:didSendPacket:sequenceNumber:` delegate callback or a 188 | * `-simplePing:didFailToSendPacket:sequenceNumber:error:` delegate callback (unless you 189 | * stop the object before you get the callback). These callbacks are currently delivered 190 | * synchronously from within `-sendPingWithData:`, but this synchronous behaviour is not 191 | * considered API. 192 | * \param pinger The object issuing the callback. 193 | * \param packet The packet that was not sent; see `-simplePing:didSendPacket:sequenceNumber:` 194 | * for details. 195 | * \param sequenceNumber The ICMP sequence number of that packet. 196 | * \param error Describes the failure. 197 | */ 198 | 199 | - (void)simplePing:(SimplePing *)pinger didFailToSendPacket:(NSData *)packet sequenceNumber:(uint16_t)sequenceNumber error:(NSError *)error; 200 | 201 | /*! A SimplePing delegate callback, called when the object receives a ping response. 202 | * \details If the object receives an ping response that matches a ping request that it 203 | * sent, it informs the delegate via this callback. Matching is primarily done based on 204 | * the ICMP identifier, although other criteria are used as well. 205 | * \param pinger The object issuing the callback. 206 | * \param packet The packet received; this includes the ICMP header (`ICMPHeader`) and any data that 207 | * follows that in the ICMP message but does not include any IP-level headers. 208 | * \param sequenceNumber The ICMP sequence number of that packet. 209 | */ 210 | 211 | - (void)simplePing:(SimplePing *)pinger didReceivePingResponsePacket:(NSData *)packet sequenceNumber:(uint16_t)sequenceNumber; 212 | 213 | /*! A SimplePing delegate callback, called when the object receives an unmatched ICMP message. 214 | * \details If the object receives an ICMP message that does not match a ping request that it 215 | * sent, it informs the delegate via this callback. The nature of ICMP handling in a 216 | * BSD kernel makes this a common event because, when an ICMP message arrives, it is 217 | * delivered to all ICMP sockets. 218 | * 219 | * IMPORTANT: This callback is especially common when using IPv6 because IPv6 uses ICMP 220 | * for important network management functions. For example, IPv6 routers periodically 221 | * send out Router Advertisement (RA) packets via Neighbor Discovery Protocol (NDP), which 222 | * is implemented on top of ICMP. 223 | * 224 | * For more on matching, see the discussion associated with 225 | * `-simplePing:didReceivePingResponsePacket:sequenceNumber:`. 226 | * \param pinger The object issuing the callback. 227 | * \param packet The packet received; this includes the ICMP header (`ICMPHeader`) and any data that 228 | * follows that in the ICMP message but does not include any IP-level headers. 229 | */ 230 | 231 | - (void)simplePing:(SimplePing *)pinger didReceiveUnexpectedPacket:(NSData *)packet; 232 | 233 | @end 234 | 235 | #pragma mark * ICMP On-The-Wire Format 236 | 237 | /*! Describes the on-the-wire header format for an ICMP ping. 238 | * \details This defines the header structure of ping packets on the wire. Both IPv4 and 239 | * IPv6 use the same basic structure. 240 | * 241 | * This is declared in the header because clients of SimplePing might want to use 242 | * it parse received ping packets. 243 | */ 244 | 245 | struct ICMPHeader { 246 | uint8_t type; 247 | uint8_t code; 248 | uint16_t checksum; 249 | uint16_t identifier; 250 | uint16_t sequenceNumber; 251 | // data... 252 | }; 253 | typedef struct ICMPHeader ICMPHeader; 254 | 255 | __Check_Compile_Time(sizeof(ICMPHeader) == 8); 256 | __Check_Compile_Time(offsetof(ICMPHeader, type) == 0); 257 | __Check_Compile_Time(offsetof(ICMPHeader, code) == 1); 258 | __Check_Compile_Time(offsetof(ICMPHeader, checksum) == 2); 259 | __Check_Compile_Time(offsetof(ICMPHeader, identifier) == 4); 260 | __Check_Compile_Time(offsetof(ICMPHeader, sequenceNumber) == 6); 261 | 262 | enum { 263 | ICMPv4TypeEchoRequest = 8, ///< The ICMP `type` for a ping request; in this case `code` is always 0. 264 | ICMPv4TypeEchoReply = 0 ///< The ICMP `type` for a ping response; in this case `code` is always 0. 265 | }; 266 | 267 | enum { 268 | ICMPv6TypeEchoRequest = 128, ///< The ICMP `type` for a ping request; in this case `code` is always 0. 269 | ICMPv6TypeEchoReply = 129 ///< The ICMP `type` for a ping response; in this case `code` is always 0. 270 | }; 271 | 272 | NS_ASSUME_NONNULL_END 273 | -------------------------------------------------------------------------------- /Pinger/SimplePing.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2016 Apple Inc. All Rights Reserved. 3 | See LICENSE.txt for this sample’s licensing information 4 | 5 | Abstract: 6 | An object wrapper around the low-level BSD Sockets ping function. 7 | */ 8 | 9 | #import "SimplePing.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #pragma mark * IPv4 and ICMPv4 On-The-Wire Format 16 | 17 | /*! Describes the on-the-wire header format for an IPv4 packet. 18 | * \details This defines the header structure of IPv4 packets on the wire. We need 19 | * this in order to skip this header in the IPv4 case, where the kernel passes 20 | * it to us for no obvious reason. 21 | */ 22 | 23 | struct IPv4Header { 24 | uint8_t versionAndHeaderLength; 25 | uint8_t differentiatedServices; 26 | uint16_t totalLength; 27 | uint16_t identification; 28 | uint16_t flagsAndFragmentOffset; 29 | uint8_t timeToLive; 30 | uint8_t protocol; 31 | uint16_t headerChecksum; 32 | uint8_t sourceAddress[4]; 33 | uint8_t destinationAddress[4]; 34 | // options... 35 | // data... 36 | }; 37 | typedef struct IPv4Header IPv4Header; 38 | 39 | __Check_Compile_Time(sizeof(IPv4Header) == 20); 40 | __Check_Compile_Time(offsetof(IPv4Header, versionAndHeaderLength) == 0); 41 | __Check_Compile_Time(offsetof(IPv4Header, differentiatedServices) == 1); 42 | __Check_Compile_Time(offsetof(IPv4Header, totalLength) == 2); 43 | __Check_Compile_Time(offsetof(IPv4Header, identification) == 4); 44 | __Check_Compile_Time(offsetof(IPv4Header, flagsAndFragmentOffset) == 6); 45 | __Check_Compile_Time(offsetof(IPv4Header, timeToLive) == 8); 46 | __Check_Compile_Time(offsetof(IPv4Header, protocol) == 9); 47 | __Check_Compile_Time(offsetof(IPv4Header, headerChecksum) == 10); 48 | __Check_Compile_Time(offsetof(IPv4Header, sourceAddress) == 12); 49 | __Check_Compile_Time(offsetof(IPv4Header, destinationAddress) == 16); 50 | 51 | /*! Calculates an IP checksum. 52 | * \details This is the standard BSD checksum code, modified to use modern types. 53 | * \param buffer A pointer to the data to checksum. 54 | * \param bufferLen The length of that data. 55 | * \returns The checksum value, in network byte order. 56 | */ 57 | 58 | static uint16_t in_cksum(const void *buffer, size_t bufferLen) { 59 | // 60 | size_t bytesLeft; 61 | int32_t sum; 62 | const uint16_t * cursor; 63 | union { 64 | uint16_t us; 65 | uint8_t uc[2]; 66 | } last; 67 | uint16_t answer; 68 | 69 | bytesLeft = bufferLen; 70 | sum = 0; 71 | cursor = buffer; 72 | 73 | /* 74 | * Our algorithm is simple, using a 32 bit accumulator (sum), we add 75 | * sequential 16 bit words to it, and at the end, fold back all the 76 | * carry bits from the top 16 bits into the lower 16 bits. 77 | */ 78 | while (bytesLeft > 1) { 79 | sum += *cursor; 80 | cursor += 1; 81 | bytesLeft -= 2; 82 | } 83 | 84 | /* mop up an odd byte, if necessary */ 85 | if (bytesLeft == 1) { 86 | last.uc[0] = * (const uint8_t *) cursor; 87 | last.uc[1] = 0; 88 | sum += last.us; 89 | } 90 | 91 | /* add back carry outs from top 16 bits to low 16 bits */ 92 | sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ 93 | sum += (sum >> 16); /* add carry */ 94 | answer = (uint16_t) ~sum; /* truncate to 16 bits */ 95 | 96 | return answer; 97 | } 98 | 99 | #pragma mark * SimplePing 100 | 101 | @interface SimplePing () 102 | 103 | // read/write versions of public properties 104 | 105 | @property (nonatomic, copy, readwrite, nullable) NSData * hostAddress; 106 | @property (nonatomic, assign, readwrite ) uint16_t nextSequenceNumber; 107 | 108 | // private properties 109 | 110 | /*! True if nextSequenceNumber has wrapped from 65535 to 0. 111 | */ 112 | 113 | @property (nonatomic, assign, readwrite) BOOL nextSequenceNumberHasWrapped; 114 | 115 | /*! A host object for name-to-address resolution. 116 | */ 117 | 118 | @property (nonatomic, strong, readwrite, nullable) CFHostRef host __attribute__ ((NSObject)); 119 | 120 | /*! A socket object for ICMP send and receive. 121 | */ 122 | 123 | @property (nonatomic, strong, readwrite, nullable) CFSocketRef socket __attribute__ ((NSObject)); 124 | 125 | @end 126 | 127 | @implementation SimplePing 128 | 129 | - (instancetype)initWithHostName:(NSString *)hostName { 130 | NSParameterAssert(hostName != nil); 131 | self = [super init]; 132 | if (self != nil) { 133 | self->_hostName = [hostName copy]; 134 | self->_identifier = (uint16_t) arc4random(); 135 | } 136 | return self; 137 | } 138 | 139 | - (void)dealloc { 140 | [self stop]; 141 | // Double check that -stop took care of _host and _socket. 142 | assert(self->_host == NULL); 143 | assert(self->_socket == NULL); 144 | } 145 | 146 | - (sa_family_t)hostAddressFamily { 147 | sa_family_t result; 148 | 149 | result = AF_UNSPEC; 150 | if ( (self.hostAddress != nil) && (self.hostAddress.length >= sizeof(struct sockaddr)) ) { 151 | result = ((const struct sockaddr *) self.hostAddress.bytes)->sa_family; 152 | } 153 | return result; 154 | } 155 | 156 | /*! Shuts down the pinger object and tell the delegate about the error. 157 | * \param error Describes the failure. 158 | */ 159 | 160 | - (void)didFailWithError:(NSError *)error { 161 | id strongDelegate; 162 | 163 | assert(error != nil); 164 | 165 | // We retain ourselves temporarily because it's common for the delegate method 166 | // to release its last reference to us, which causes -dealloc to be called here. 167 | // If we then reference self on the return path, things go badly. I don't think 168 | // that happens currently, but I've got into the habit of doing this as a 169 | // defensive measure. 170 | 171 | CFAutorelease( CFBridgingRetain( self )); 172 | 173 | [self stop]; 174 | strongDelegate = self.delegate; 175 | if ( (strongDelegate != nil) && [strongDelegate respondsToSelector:@selector(simplePing:didFailWithError:)] ) { 176 | [strongDelegate simplePing:self didFailWithError:error]; 177 | } 178 | } 179 | 180 | /*! Shuts down the pinger object and tell the delegate about the error. 181 | * \details This converts the CFStreamError to an NSError and then call through to 182 | * -didFailWithError: to do the real work. 183 | * \param streamError Describes the failure. 184 | */ 185 | 186 | - (void)didFailWithHostStreamError:(CFStreamError)streamError { 187 | NSDictionary * userInfo; 188 | NSError * error; 189 | 190 | if (streamError.domain == kCFStreamErrorDomainNetDB) { 191 | userInfo = @{(id) kCFGetAddrInfoFailureKey: @(streamError.error)}; 192 | } else { 193 | userInfo = nil; 194 | } 195 | error = [NSError errorWithDomain:(NSString *) kCFErrorDomainCFNetwork code:kCFHostErrorUnknown userInfo:userInfo]; 196 | 197 | [self didFailWithError:error]; 198 | } 199 | 200 | /*! Builds a ping packet from the supplied parameters. 201 | * \param type The packet type, which is different for IPv4 and IPv6. 202 | * \param payload Data to place after the ICMP header. 203 | * \param requiresChecksum Determines whether a checksum is calculated (IPv4) or not (IPv6). 204 | * \returns A ping packet suitable to be passed to the kernel. 205 | */ 206 | 207 | - (NSData *)pingPacketWithType:(uint8_t)type payload:(NSData *)payload requiresChecksum:(BOOL)requiresChecksum { 208 | NSMutableData * packet; 209 | ICMPHeader * icmpPtr; 210 | 211 | packet = [NSMutableData dataWithLength:sizeof(*icmpPtr) + payload.length]; 212 | assert(packet != nil); 213 | 214 | icmpPtr = packet.mutableBytes; 215 | icmpPtr->type = type; 216 | icmpPtr->code = 0; 217 | icmpPtr->checksum = 0; 218 | icmpPtr->identifier = OSSwapHostToBigInt16(self.identifier); 219 | icmpPtr->sequenceNumber = OSSwapHostToBigInt16(self.nextSequenceNumber); 220 | memcpy(&icmpPtr[1], [payload bytes], [payload length]); 221 | 222 | if (requiresChecksum) { 223 | // The IP checksum routine returns a 16-bit number that's already in correct byte order 224 | // (due to wacky 1's complement maths), so we just put it into the packet as a 16-bit unit. 225 | 226 | icmpPtr->checksum = in_cksum(packet.bytes, packet.length); 227 | } 228 | 229 | return packet; 230 | } 231 | 232 | - (void)sendPingWithData:(NSData *)data { 233 | int err; 234 | NSData * payload; 235 | NSData * packet; 236 | ssize_t bytesSent; 237 | id strongDelegate; 238 | 239 | // data may be nil 240 | NSParameterAssert(self.hostAddress != nil); // gotta wait for -simplePing:didStartWithAddress: 241 | 242 | // Construct the ping packet. 243 | 244 | payload = data; 245 | if (payload == nil) { 246 | payload = [[NSString stringWithFormat:@"%28zd bottles of beer on the wall", (ssize_t) 99 - (size_t) (self.nextSequenceNumber % 100) ] dataUsingEncoding:NSASCIIStringEncoding]; 247 | assert(payload != nil); 248 | 249 | // Our dummy payload is sized so that the resulting ICMP packet, including the ICMPHeader, is 250 | // 64-bytes, which makes it easier to recognise our packets on the wire. 251 | 252 | assert([payload length] == 56); 253 | } 254 | 255 | switch (self.hostAddressFamily) { 256 | case AF_INET: { 257 | packet = [self pingPacketWithType:ICMPv4TypeEchoRequest payload:payload requiresChecksum:YES]; 258 | } break; 259 | case AF_INET6: { 260 | packet = [self pingPacketWithType:ICMPv6TypeEchoRequest payload:payload requiresChecksum:NO]; 261 | } break; 262 | default: { 263 | assert(NO); 264 | } break; 265 | } 266 | assert(packet != nil); 267 | 268 | // Send the packet. 269 | 270 | if (self.socket == NULL) { 271 | bytesSent = -1; 272 | err = EBADF; 273 | } else { 274 | bytesSent = sendto( 275 | CFSocketGetNative(self.socket), 276 | packet.bytes, 277 | packet.length, 278 | 0, 279 | self.hostAddress.bytes, 280 | (socklen_t) self.hostAddress.length 281 | ); 282 | err = 0; 283 | if (bytesSent < 0) { 284 | err = errno; 285 | } 286 | } 287 | 288 | // Handle the results of the send. 289 | 290 | strongDelegate = self.delegate; 291 | if ( (bytesSent > 0) && (((NSUInteger) bytesSent) == packet.length) ) { 292 | 293 | // Complete success. Tell the client. 294 | 295 | if ( (strongDelegate != nil) && [strongDelegate respondsToSelector:@selector(simplePing:didSendPacket:sequenceNumber:)] ) { 296 | [strongDelegate simplePing:self didSendPacket:packet sequenceNumber:self.nextSequenceNumber]; 297 | } 298 | } else { 299 | NSError * error; 300 | 301 | // Some sort of failure. Tell the client. 302 | 303 | if (err == 0) { 304 | err = ENOBUFS; // This is not a hugely descriptor error, alas. 305 | } 306 | error = [NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:nil]; 307 | if ( (strongDelegate != nil) && [strongDelegate respondsToSelector:@selector(simplePing:didFailToSendPacket:sequenceNumber:error:)] ) { 308 | [strongDelegate simplePing:self didFailToSendPacket:packet sequenceNumber:self.nextSequenceNumber error:error]; 309 | } 310 | } 311 | 312 | self.nextSequenceNumber += 1; 313 | if (self.nextSequenceNumber == 0) { 314 | self.nextSequenceNumberHasWrapped = YES; 315 | } 316 | } 317 | 318 | /*! Calculates the offset of the ICMP header within an IPv4 packet. 319 | * \details In the IPv4 case the kernel returns us a buffer that includes the 320 | * IPv4 header. We're not interested in that, so we have to skip over it. 321 | * This code does a rough check of the IPv4 header and, if it looks OK, 322 | * returns the offset of the ICMP header. 323 | * \param packet The IPv4 packet, as returned to us by the kernel. 324 | * \returns The offset of the ICMP header, or NSNotFound. 325 | */ 326 | 327 | + (NSUInteger)icmpHeaderOffsetInIPv4Packet:(NSData *)packet { 328 | // Returns the offset of the ICMPv4Header within an IP packet. 329 | NSUInteger result; 330 | const struct IPv4Header * ipPtr; 331 | size_t ipHeaderLength; 332 | 333 | result = NSNotFound; 334 | if (packet.length >= (sizeof(IPv4Header) + sizeof(ICMPHeader))) { 335 | ipPtr = (const IPv4Header *) packet.bytes; 336 | if ( ((ipPtr->versionAndHeaderLength & 0xF0) == 0x40) && // IPv4 337 | ( ipPtr->protocol == IPPROTO_ICMP ) ) { 338 | ipHeaderLength = (ipPtr->versionAndHeaderLength & 0x0F) * sizeof(uint32_t); 339 | if (packet.length >= (ipHeaderLength + sizeof(ICMPHeader))) { 340 | result = ipHeaderLength; 341 | } 342 | } 343 | } 344 | return result; 345 | } 346 | 347 | /*! Checks whether the specified sequence number is one we sent. 348 | * \param sequenceNumber The incoming sequence number. 349 | * \returns YES if the sequence number looks like one we sent. 350 | */ 351 | 352 | - (BOOL)validateSequenceNumber:(uint16_t)sequenceNumber { 353 | if (self.nextSequenceNumberHasWrapped) { 354 | // If the sequence numbers have wrapped that we can't reliably check 355 | // whether this is a sequence number we sent. Rather, we check to see 356 | // whether the sequence number is within the last 120 sequence numbers 357 | // we sent. Note that the uint16_t subtraction here does the right 358 | // thing regardless of the wrapping. 359 | // 360 | // Why 120? Well, if we send one ping per second, 120 is 2 minutes, which 361 | // is the standard "max time a packet can bounce around the Internet" value. 362 | return ((uint16_t) (self.nextSequenceNumber - sequenceNumber)) < (uint16_t) 120; 363 | } else { 364 | return sequenceNumber < self.nextSequenceNumber; 365 | } 366 | } 367 | 368 | /*! Checks whether an incoming IPv4 packet looks like a ping response. 369 | * \details This routine modifies this `packet` data! It does this for two reasons: 370 | * 371 | * * It needs to zero out the `checksum` field of the ICMPHeader in order to do 372 | * its checksum calculation. 373 | * 374 | * * It removes the IPv4 header from the front of the packet. 375 | * \param packet The IPv4 packet, as returned to us by the kernel. 376 | * \param sequenceNumberPtr A pointer to a place to start the ICMP sequence number. 377 | * \returns YES if the packet looks like a reasonable IPv4 ping response. 378 | */ 379 | 380 | - (BOOL)validatePing4ResponsePacket:(NSMutableData *)packet sequenceNumber:(uint16_t *)sequenceNumberPtr { 381 | BOOL result; 382 | NSUInteger icmpHeaderOffset; 383 | ICMPHeader * icmpPtr; 384 | uint16_t receivedChecksum; 385 | uint16_t calculatedChecksum; 386 | 387 | result = NO; 388 | 389 | icmpHeaderOffset = [[self class] icmpHeaderOffsetInIPv4Packet:packet]; 390 | if (icmpHeaderOffset != NSNotFound) { 391 | icmpPtr = (struct ICMPHeader *) (((uint8_t *) packet.mutableBytes) + icmpHeaderOffset); 392 | 393 | receivedChecksum = icmpPtr->checksum; 394 | icmpPtr->checksum = 0; 395 | calculatedChecksum = in_cksum(icmpPtr, packet.length - icmpHeaderOffset); 396 | icmpPtr->checksum = receivedChecksum; 397 | 398 | if (receivedChecksum == calculatedChecksum) { 399 | if ( (icmpPtr->type == ICMPv4TypeEchoReply) && (icmpPtr->code == 0) ) { 400 | if ( OSSwapBigToHostInt16(icmpPtr->identifier) == self.identifier ) { 401 | uint16_t sequenceNumber; 402 | 403 | sequenceNumber = OSSwapBigToHostInt16(icmpPtr->sequenceNumber); 404 | if ([self validateSequenceNumber:sequenceNumber]) { 405 | 406 | // Remove the IPv4 header off the front of the data we received, leaving us with 407 | // just the ICMP header and the ping payload. 408 | [packet replaceBytesInRange:NSMakeRange(0, icmpHeaderOffset) withBytes:NULL length:0]; 409 | 410 | *sequenceNumberPtr = sequenceNumber; 411 | result = YES; 412 | } 413 | } 414 | } 415 | } 416 | } 417 | 418 | return result; 419 | } 420 | 421 | /*! Checks whether an incoming IPv6 packet looks like a ping response. 422 | * \param packet The IPv6 packet, as returned to us by the kernel; note that this routine 423 | * could modify this data but does not need to in the IPv6 case. 424 | * \param sequenceNumberPtr A pointer to a place to start the ICMP sequence number. 425 | * \returns YES if the packet looks like a reasonable IPv4 ping response. 426 | */ 427 | 428 | - (BOOL)validatePing6ResponsePacket:(NSMutableData *)packet sequenceNumber:(uint16_t *)sequenceNumberPtr { 429 | BOOL result; 430 | const ICMPHeader * icmpPtr; 431 | 432 | result = NO; 433 | 434 | if (packet.length >= sizeof(*icmpPtr)) { 435 | icmpPtr = packet.bytes; 436 | 437 | // In the IPv6 case we don't check the checksum because that's hard (we need to 438 | // cook up an IPv6 pseudo header and we don't have the ingredients) and unnecessary 439 | // (the kernel has already done this check). 440 | 441 | if ( (icmpPtr->type == ICMPv6TypeEchoReply) && (icmpPtr->code == 0) ) { 442 | if ( OSSwapBigToHostInt16(icmpPtr->identifier) == self.identifier ) { 443 | uint16_t sequenceNumber; 444 | 445 | sequenceNumber = OSSwapBigToHostInt16(icmpPtr->sequenceNumber); 446 | if ([self validateSequenceNumber:sequenceNumber]) { 447 | *sequenceNumberPtr = sequenceNumber; 448 | result = YES; 449 | } 450 | } 451 | } 452 | } 453 | return result; 454 | } 455 | 456 | /*! Checks whether an incoming packet looks like a ping response. 457 | * \param packet The packet, as returned to us by the kernel; note that may end up modifying 458 | * this data. 459 | * \param sequenceNumberPtr A pointer to a place to start the ICMP sequence number. 460 | * \returns YES if the packet looks like a reasonable IPv4 ping response. 461 | */ 462 | 463 | - (BOOL)validatePingResponsePacket:(NSMutableData *)packet sequenceNumber:(uint16_t *)sequenceNumberPtr { 464 | BOOL result; 465 | 466 | switch (self.hostAddressFamily) { 467 | case AF_INET: { 468 | result = [self validatePing4ResponsePacket:packet sequenceNumber:sequenceNumberPtr]; 469 | } break; 470 | case AF_INET6: { 471 | result = [self validatePing6ResponsePacket:packet sequenceNumber:sequenceNumberPtr]; 472 | } break; 473 | default: { 474 | assert(NO); 475 | result = NO; 476 | } break; 477 | } 478 | return result; 479 | } 480 | 481 | /*! Reads data from the ICMP socket. 482 | * \details Called by the socket handling code (SocketReadCallback) to process an ICMP 483 | * message waiting on the socket. 484 | */ 485 | 486 | - (void)readData { 487 | int err; 488 | struct sockaddr_storage addr; 489 | socklen_t addrLen; 490 | ssize_t bytesRead; 491 | void * buffer; 492 | enum { kBufferSize = 65535 }; 493 | 494 | // 65535 is the maximum IP packet size, which seems like a reasonable bound 495 | // here (plus it's what uses). 496 | 497 | buffer = malloc(kBufferSize); 498 | assert(buffer != NULL); 499 | 500 | // Actually read the data. We use recvfrom(), and thus get back the source address, 501 | // but we don't actually do anything with it. It would be trivial to pass it to 502 | // the delegate but we don't need it in this example. 503 | 504 | addrLen = sizeof(addr); 505 | bytesRead = recvfrom(CFSocketGetNative(self.socket), buffer, kBufferSize, 0, (struct sockaddr *) &addr, &addrLen); 506 | err = 0; 507 | if (bytesRead < 0) { 508 | err = errno; 509 | } 510 | 511 | // Process the data we read. 512 | 513 | if (bytesRead > 0) { 514 | NSMutableData * packet; 515 | id strongDelegate; 516 | uint16_t sequenceNumber; 517 | 518 | packet = [NSMutableData dataWithBytes:buffer length:(NSUInteger) bytesRead]; 519 | assert(packet != nil); 520 | 521 | // We got some data, pass it up to our client. 522 | 523 | strongDelegate = self.delegate; 524 | if ( [self validatePingResponsePacket:packet sequenceNumber:&sequenceNumber] ) { 525 | if ( (strongDelegate != nil) && [strongDelegate respondsToSelector:@selector(simplePing:didReceivePingResponsePacket:sequenceNumber:)] ) { 526 | [strongDelegate simplePing:self didReceivePingResponsePacket:packet sequenceNumber:sequenceNumber]; 527 | } 528 | } else { 529 | if ( (strongDelegate != nil) && [strongDelegate respondsToSelector:@selector(simplePing:didReceiveUnexpectedPacket:)] ) { 530 | [strongDelegate simplePing:self didReceiveUnexpectedPacket:packet]; 531 | } 532 | } 533 | } else { 534 | 535 | // We failed to read the data, so shut everything down. 536 | 537 | if (err == 0) { 538 | err = EPIPE; 539 | } 540 | [self didFailWithError:[NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:nil]]; 541 | } 542 | 543 | free(buffer); 544 | 545 | // Note that we don't loop back trying to read more data. Rather, we just 546 | // let CFSocket call us again. 547 | } 548 | 549 | /*! The callback for our CFSocket object. 550 | * \details This simply routes the call to our `-readData` method. 551 | * \param s See the documentation for CFSocketCallBack. 552 | * \param type See the documentation for CFSocketCallBack. 553 | * \param address See the documentation for CFSocketCallBack. 554 | * \param data See the documentation for CFSocketCallBack. 555 | * \param info See the documentation for CFSocketCallBack; this is actually a pointer to the 556 | * 'owning' object. 557 | */ 558 | 559 | static void SocketReadCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) { 560 | // This C routine is called by CFSocket when there's data waiting on our 561 | // ICMP socket. It just redirects the call to Objective-C code. 562 | SimplePing * obj; 563 | 564 | obj = (__bridge SimplePing *) info; 565 | assert([obj isKindOfClass:[SimplePing class]]); 566 | 567 | #pragma unused(s) 568 | assert(s == obj.socket); 569 | #pragma unused(type) 570 | assert(type == kCFSocketReadCallBack); 571 | #pragma unused(address) 572 | assert(address == nil); 573 | #pragma unused(data) 574 | assert(data == nil); 575 | 576 | [obj readData]; 577 | } 578 | 579 | /*! Starts the send and receive infrastructure. 580 | * \details This is called once we've successfully resolved `hostName` in to 581 | * `hostAddress`. It's responsible for setting up the socket for sending and 582 | * receiving pings. 583 | */ 584 | 585 | - (void)startWithHostAddress { 586 | int err; 587 | int fd; 588 | 589 | assert(self.hostAddress != nil); 590 | 591 | // Open the socket. 592 | 593 | fd = -1; 594 | err = 0; 595 | switch (self.hostAddressFamily) { 596 | case AF_INET: { 597 | fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); 598 | if (fd < 0) { 599 | err = errno; 600 | } 601 | } break; 602 | case AF_INET6: { 603 | fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6); 604 | if (fd < 0) { 605 | err = errno; 606 | } 607 | } break; 608 | default: { 609 | err = EPROTONOSUPPORT; 610 | } break; 611 | } 612 | 613 | if (err != 0) { 614 | [self didFailWithError:[NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:nil]]; 615 | } else { 616 | CFSocketContext context = {0, (__bridge void *)(self), NULL, NULL, NULL}; 617 | CFRunLoopSourceRef rls; 618 | id strongDelegate; 619 | 620 | // Wrap it in a CFSocket and schedule it on the runloop. 621 | 622 | self.socket = (CFSocketRef) CFAutorelease( CFSocketCreateWithNative(NULL, fd, kCFSocketReadCallBack, SocketReadCallback, &context) ); 623 | assert(self.socket != NULL); 624 | 625 | // The socket will now take care of cleaning up our file descriptor. 626 | 627 | assert( CFSocketGetSocketFlags(self.socket) & kCFSocketCloseOnInvalidate ); 628 | fd = -1; 629 | 630 | rls = CFSocketCreateRunLoopSource(NULL, self.socket, 0); 631 | assert(rls != NULL); 632 | 633 | CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); 634 | 635 | CFRelease(rls); 636 | 637 | strongDelegate = self.delegate; 638 | if ( (strongDelegate != nil) && [strongDelegate respondsToSelector:@selector(simplePing:didStartWithAddress:)] ) { 639 | [strongDelegate simplePing:self didStartWithAddress:self.hostAddress]; 640 | } 641 | } 642 | assert(fd == -1); 643 | } 644 | 645 | /*! Processes the results of our name-to-address resolution. 646 | * \details Called by our CFHost resolution callback (HostResolveCallback) when host 647 | * resolution is complete. We just latch the first appropriate address and kick 648 | * off the send and receive infrastructure. 649 | */ 650 | 651 | - (void)hostResolutionDone { 652 | Boolean resolved; 653 | NSArray * addresses; 654 | 655 | // Find the first appropriate address. 656 | 657 | addresses = (__bridge NSArray *) CFHostGetAddressing(self.host, &resolved); 658 | if ( resolved && (addresses != nil) ) { 659 | resolved = false; 660 | for (NSData * address in addresses) { 661 | const struct sockaddr * addrPtr; 662 | 663 | addrPtr = (const struct sockaddr *) address.bytes; 664 | if ( address.length >= sizeof(struct sockaddr) ) { 665 | switch (addrPtr->sa_family) { 666 | case AF_INET: { 667 | if (self.addressStyle != SimplePingAddressStyleICMPv6) { 668 | self.hostAddress = address; 669 | resolved = true; 670 | } 671 | } break; 672 | case AF_INET6: { 673 | if (self.addressStyle != SimplePingAddressStyleICMPv4) { 674 | self.hostAddress = address; 675 | resolved = true; 676 | } 677 | } break; 678 | } 679 | } 680 | if (resolved) { 681 | break; 682 | } 683 | } 684 | } 685 | 686 | // We're done resolving, so shut that down. 687 | 688 | [self stopHostResolution]; 689 | 690 | // If all is OK, start the send and receive infrastructure, otherwise stop. 691 | 692 | if (resolved) { 693 | [self startWithHostAddress]; 694 | } else { 695 | [self didFailWithError:[NSError errorWithDomain:(NSString *)kCFErrorDomainCFNetwork code:kCFHostErrorHostNotFound userInfo:nil]]; 696 | } 697 | } 698 | 699 | /*! The callback for our CFHost object. 700 | * \details This simply routes the call to our `-hostResolutionDone` or 701 | * `-didFailWithHostStreamError:` methods. 702 | * \param theHost See the documentation for CFHostClientCallBack. 703 | * \param typeInfo See the documentation for CFHostClientCallBack. 704 | * \param error See the documentation for CFHostClientCallBack. 705 | * \param info See the documentation for CFHostClientCallBack; this is actually a pointer to 706 | * the 'owning' object. 707 | */ 708 | 709 | static void HostResolveCallback(CFHostRef theHost, CFHostInfoType typeInfo, const CFStreamError *error, void *info) { 710 | // This C routine is called by CFHost when the host resolution is complete. 711 | // It just redirects the call to the appropriate Objective-C method. 712 | SimplePing * obj; 713 | 714 | obj = (__bridge SimplePing *) info; 715 | assert([obj isKindOfClass:[SimplePing class]]); 716 | 717 | #pragma unused(theHost) 718 | assert(theHost == obj.host); 719 | #pragma unused(typeInfo) 720 | assert(typeInfo == kCFHostAddresses); 721 | 722 | if ( (error != NULL) && (error->domain != 0) ) { 723 | [obj didFailWithHostStreamError:*error]; 724 | } else { 725 | [obj hostResolutionDone]; 726 | } 727 | } 728 | 729 | - (void)start { 730 | Boolean success; 731 | CFHostClientContext context = {0, (__bridge void *)(self), NULL, NULL, NULL}; 732 | CFStreamError streamError; 733 | // 734 | // assert(self.host == NULL); 735 | // assert(self.hostAddress == nil); 736 | 737 | self.host = (CFHostRef) CFAutorelease( CFHostCreateWithName(NULL, (__bridge CFStringRef) self.hostName) ); 738 | assert(self.host != NULL); 739 | 740 | CFHostSetClient(self.host, HostResolveCallback, &context); 741 | 742 | CFHostScheduleWithRunLoop(self.host, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); 743 | 744 | success = CFHostStartInfoResolution(self.host, kCFHostAddresses, &streamError); 745 | if ( ! success ) { 746 | [self didFailWithHostStreamError:streamError]; 747 | } 748 | } 749 | 750 | /*! Stops the name-to-address resolution infrastructure. 751 | */ 752 | 753 | - (void)stopHostResolution { 754 | // Shut down the CFHost. 755 | if (self.host != NULL) { 756 | CFHostSetClient(self.host, NULL, NULL); 757 | CFHostUnscheduleFromRunLoop(self.host, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); 758 | self.host = NULL; 759 | } 760 | } 761 | 762 | /*! Stops the send and receive infrastructure. 763 | */ 764 | 765 | - (void)stopSocket { 766 | if (self.socket != NULL) { 767 | CFSocketInvalidate(self.socket); 768 | self.socket = NULL; 769 | } 770 | } 771 | 772 | - (void)stop { 773 | [self stopHostResolution]; 774 | [self stopSocket]; 775 | 776 | // Junk the host address on stop. If the client calls -start again, we'll 777 | // re-resolve the host name. 778 | 779 | self.hostAddress = NULL; 780 | } 781 | 782 | @end 783 | -------------------------------------------------------------------------------- /Pinger/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // Pinger 4 | // 5 | // Created by 廖亚雄 on 2020/9/16. 6 | // 7 | 8 | #import 9 | 10 | @interface ViewController : UIViewController 11 | 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /Pinger/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // Pinger 4 | // 5 | // Created by 廖亚雄 on 2020/9/16. 6 | // 7 | 8 | #import "ViewController.h" 9 | #import "SimplePing.h" 10 | #import "LDSRouterInfo.h" 11 | @interface ViewController () 12 | { 13 | SimplePing *pinger;dispatch_source_t timer; 14 | } 15 | @end 16 | 17 | @implementation ViewController 18 | dispatch_source_t timer; 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | // Do any additional setup after loading the view. 22 | NSDictionary *router = [LDSRouterInfo getRouterInfo]; 23 | pinger = [[SimplePing alloc] initWithHostName:router[@"ip"]]; 24 | pinger.delegate = self; 25 | [self->pinger start]; 26 | 27 | } 28 | - (void)simplePing:(SimplePing *)pinger didStartWithAddress:(NSData *)address { 29 | if (timer) { 30 | return; 31 | } 32 | timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); 33 | dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC, 0 * NSEC_PER_SEC); 34 | dispatch_source_set_event_handler(timer, ^{ 35 | [pinger sendPingWithData:nil]; 36 | }); 37 | dispatch_resume(timer); 38 | } 39 | 40 | - (void)simplePing:(SimplePing *)pinger didSendPacket:(NSData *)packet sequenceNumber:(uint16_t)sequenceNumber { 41 | NSLog(@"可以使用局域网"); 42 | } 43 | 44 | - (void)simplePing:(SimplePing *)pinger didFailToSendPacket:(NSData *)packet sequenceNumber:(uint16_t)sequenceNumber error:(NSError *)error { 45 | if (error.code == 65) {//no route to host 46 | NSLog(@"不可以使用局域网"); 47 | } 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /Pinger/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Pinger 4 | // 5 | // Created by 廖亚雄 on 2020/9/16. 6 | // 7 | 8 | #import 9 | #import "AppDelegate.h" 10 | 11 | int main(int argc, char * argv[]) { 12 | NSString * appDelegateClassName; 13 | @autoreleasepool { 14 | // Setup code that might create autoreleased objects goes here. 15 | appDelegateClassName = NSStringFromClass([AppDelegate class]); 16 | } 17 | return UIApplicationMain(argc, argv, nil, appDelegateClassName); 18 | } 19 | -------------------------------------------------------------------------------- /Pinger/route.h: -------------------------------------------------------------------------------- 1 | // 2 | // route.h 3 | // MSTEnterprise 4 | // 5 | // Created by 磊强 on 13-4-24. 6 | // Copyright (c) 2013年 LeiQiang. All rights reserved. 7 | // 8 | 9 | #ifndef _NET_ROUTE_H_ 10 | #define _NET_ROUTE_H_ 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | /* 17 | * Kernel resident routing tables. 18 | * 19 | * The routing tables are initialized when interface addresses 20 | * are set by making entries for all directly connected interfaces. 21 | */ 22 | 23 | /* 24 | * A route consists of a destination address and a reference 25 | * to a routing entry. These are often held by protocols 26 | * in their control blocks, e.g. inpcb. 27 | */ 28 | struct route; 29 | 30 | /* 31 | * These numbers are used by reliable protocols for determining 32 | * retransmission behavior and are included in the routing structure. 33 | */ 34 | struct rt_metrics { 35 | u_int32_t rmx_locks; /* Kernel must leave these values alone */ 36 | u_int32_t rmx_mtu; /* MTU for this path */ 37 | u_int32_t rmx_hopcount; /* max hops expected */ 38 | int32_t rmx_expire; /* lifetime for route, e.g. redirect */ 39 | u_int32_t rmx_recvpipe; /* inbound delay-bandwidth product */ 40 | u_int32_t rmx_sendpipe; /* outbound delay-bandwidth product */ 41 | u_int32_t rmx_ssthresh; /* outbound gateway buffer limit */ 42 | u_int32_t rmx_rtt; /* estimated round trip time */ 43 | u_int32_t rmx_rttvar; /* estimated rtt variance */ 44 | u_int32_t rmx_pksent; /* packets sent using this route */ 45 | u_int32_t rmx_filler[4]; /* will be used for T/TCP later */ 46 | }; 47 | 48 | /* 49 | * rmx_rtt and rmx_rttvar are stored as microseconds; 50 | */ 51 | #define RTM_RTTUNIT 1000000 /* units for rtt, rttvar, as units per sec */ 52 | 53 | /* 54 | * We distinguish between routes to hosts and routes to networks, 55 | * preferring the former if available. For each route we infer 56 | * the interface to use from the gateway address supplied when 57 | * the route was entered. Routes that forward packets through 58 | * gateways are marked so that the output routines know to address the 59 | * gateway rather than the ultimate destination. 60 | */ 61 | 62 | 63 | #define RTF_UP 0x1 /* route usable */ 64 | #define RTF_GATEWAY 0x2 /* destination is a gateway */ 65 | #define RTF_HOST 0x4 /* host entry (net otherwise) */ 66 | #define RTF_REJECT 0x8 /* host or net unreachable */ 67 | #define RTF_DYNAMIC 0x10 /* created dynamically (by redirect) */ 68 | #define RTF_MODIFIED 0x20 /* modified dynamically (by redirect) */ 69 | #define RTF_DONE 0x40 /* message confirmed */ 70 | #define RTF_DELCLONE 0x80 /* delete cloned route */ 71 | #define RTF_CLONING 0x100 /* generate new routes on use */ 72 | #define RTF_XRESOLVE 0x200 /* external daemon resolves name */ 73 | #define RTF_LLINFO 0x400 /* generated by link layer (e.g. ARP) */ 74 | #define RTF_STATIC 0x800 /* manually added */ 75 | #define RTF_BLACKHOLE 0x1000 /* just discard pkts (during updates) */ 76 | #define RTF_PROTO2 0x4000 /* protocol specific routing flag */ 77 | #define RTF_PROTO1 0x8000 /* protocol specific routing flag */ 78 | 79 | #define RTF_PRCLONING 0x10000 /* protocol requires cloning */ 80 | #define RTF_WASCLONED 0x20000 /* route generated through cloning */ 81 | #define RTF_PROTO3 0x40000 /* protocol specific routing flag */ 82 | /* 0x80000 unused */ 83 | #define RTF_PINNED 0x100000 /* future use */ 84 | #define RTF_LOCAL 0x200000 /* route represents a local address */ 85 | #define RTF_BROADCAST 0x400000 /* route represents a bcast address */ 86 | #define RTF_MULTICAST 0x800000 /* route represents a mcast address */ 87 | #define RTF_IFSCOPE 0x1000000 /* has valid interface scope */ 88 | #define RTF_CONDEMNED 0x2000000 /* defunct; no longer modifiable */ 89 | /* 0x4000000 and up unassigned */ 90 | 91 | /* 92 | * Routing statistics. 93 | */ 94 | struct rtstat { 95 | short rts_badredirect; /* bogus redirect calls */ 96 | short rts_dynamic; /* routes created by redirects */ 97 | short rts_newgateway; /* routes modified by redirects */ 98 | short rts_unreach; /* lookups which failed */ 99 | short rts_wildcard; /* lookups satisfied by a wildcard */ 100 | }; 101 | 102 | /* 103 | * Structures for routing messages. 104 | */ 105 | struct rt_msghdr { 106 | u_short rtm_msglen; /* to skip over non-understood messages */ 107 | u_char rtm_version; /* future binary compatibility */ 108 | u_char rtm_type; /* message type */ 109 | u_short rtm_index; /* index for associated ifp */ 110 | int rtm_flags; /* flags, incl. kern & message, e.g. DONE */ 111 | int rtm_addrs; /* bitmask identifying sockaddrs in msg */ 112 | pid_t rtm_pid; /* identify sender */ 113 | int rtm_seq; /* for sender to identify action */ 114 | int rtm_errno; /* why failed */ 115 | int rtm_use; /* from rtentry */ 116 | u_int32_t rtm_inits; /* which metrics we are initializing */ 117 | struct rt_metrics rtm_rmx; /* metrics themselves */ 118 | }; 119 | 120 | struct rt_msghdr2 { 121 | u_short rtm_msglen; /* to skip over non-understood messages */ 122 | u_char rtm_version; /* future binary compatibility */ 123 | u_char rtm_type; /* message type */ 124 | u_short rtm_index; /* index for associated ifp */ 125 | int rtm_flags; /* flags, incl. kern & message, e.g. DONE */ 126 | int rtm_addrs; /* bitmask identifying sockaddrs in msg */ 127 | int32_t rtm_refcnt; /* reference count */ 128 | int rtm_parentflags; /* flags of the parent route */ 129 | int rtm_reserved; /* reserved field set to 0 */ 130 | int rtm_use; /* from rtentry */ 131 | u_int32_t rtm_inits; /* which metrics we are initializing */ 132 | struct rt_metrics rtm_rmx; /* metrics themselves */ 133 | }; 134 | 135 | 136 | #define RTM_VERSION 5 /* Up the ante and ignore older versions */ 137 | 138 | /* 139 | * Message types. 140 | */ 141 | #define RTM_ADD 0x1 /* Add Route */ 142 | #define RTM_DELETE 0x2 /* Delete Route */ 143 | #define RTM_CHANGE 0x3 /* Change Metrics or flags */ 144 | #define RTM_GET 0x4 /* Report Metrics */ 145 | #define RTM_LOSING 0x5 /* Kernel Suspects Partitioning */ 146 | #define RTM_REDIRECT 0x6 /* Told to use different route */ 147 | #define RTM_MISS 0x7 /* Lookup failed on this address */ 148 | #define RTM_LOCK 0x8 /* fix specified metrics */ 149 | #define RTM_OLDADD 0x9 /* caused by SIOCADDRT */ 150 | #define RTM_OLDDEL 0xa /* caused by SIOCDELRT */ 151 | #define RTM_RESOLVE 0xb /* req to resolve dst to LL addr */ 152 | #define RTM_NEWADDR 0xc /* address being added to iface */ 153 | #define RTM_DELADDR 0xd /* address being removed from iface */ 154 | #define RTM_IFINFO 0xe /* iface going up/down etc. */ 155 | #define RTM_NEWMADDR 0xf /* mcast group membership being added to if */ 156 | #define RTM_DELMADDR 0x10 /* mcast group membership being deleted */ 157 | #define RTM_IFINFO2 0x12 /* */ 158 | #define RTM_NEWMADDR2 0x13 /* */ 159 | #define RTM_GET2 0x14 /* */ 160 | 161 | /* 162 | * Bitmask values for rtm_inits and rmx_locks. 163 | */ 164 | #define RTV_MTU 0x1 /* init or lock _mtu */ 165 | #define RTV_HOPCOUNT 0x2 /* init or lock _hopcount */ 166 | #define RTV_EXPIRE 0x4 /* init or lock _expire */ 167 | #define RTV_RPIPE 0x8 /* init or lock _recvpipe */ 168 | #define RTV_SPIPE 0x10 /* init or lock _sendpipe */ 169 | #define RTV_SSTHRESH 0x20 /* init or lock _ssthresh */ 170 | #define RTV_RTT 0x40 /* init or lock _rtt */ 171 | #define RTV_RTTVAR 0x80 /* init or lock _rttvar */ 172 | 173 | /* 174 | * Bitmask values for rtm_addrs. 175 | */ 176 | #define RTA_DST 0x1 /* destination sockaddr present */ 177 | #define RTA_GATEWAY 0x2 /* gateway sockaddr present */ 178 | #define RTA_NETMASK 0x4 /* netmask sockaddr present */ 179 | #define RTA_GENMASK 0x8 /* cloning mask sockaddr present */ 180 | #define RTA_IFP 0x10 /* interface name sockaddr present */ 181 | #define RTA_IFA 0x20 /* interface addr sockaddr present */ 182 | #define RTA_AUTHOR 0x40 /* sockaddr for author of redirect */ 183 | #define RTA_BRD 0x80 /* for NEWADDR, broadcast or p-p dest addr */ 184 | 185 | /* 186 | * Index offsets for sockaddr array for alternate internal encoding. 187 | */ 188 | #define RTAX_DST 0 /* destination sockaddr present */ 189 | #define RTAX_GATEWAY 1 /* gateway sockaddr present */ 190 | #define RTAX_NETMASK 2 /* netmask sockaddr present */ 191 | #define RTAX_GENMASK 3 /* cloning mask sockaddr present */ 192 | #define RTAX_IFP 4 /* interface name sockaddr present */ 193 | #define RTAX_IFA 5 /* interface addr sockaddr present */ 194 | #define RTAX_AUTHOR 6 /* sockaddr for author of redirect */ 195 | #define RTAX_BRD 7 /* for NEWADDR, broadcast or p-p dest addr */ 196 | #define RTAX_MAX 8 /* size of array to allocate */ 197 | 198 | struct rt_addrinfo { 199 | int rti_addrs; 200 | struct sockaddr *rti_info[RTAX_MAX]; 201 | }; 202 | 203 | struct route_cb { 204 | int ip_count; 205 | int ip6_count; 206 | int ipx_count; 207 | int ns_count; 208 | int iso_count; 209 | int any_count; 210 | }; 211 | 212 | 213 | 214 | #endif 215 | -------------------------------------------------------------------------------- /PingerTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /PingerTests/PingerTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PingerTests.m 3 | // PingerTests 4 | // 5 | // Created by 廖亚雄 on 2020/9/16. 6 | // 7 | 8 | #import 9 | 10 | @interface PingerTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation PingerTests 15 | 16 | - (void)setUp { 17 | // Put setup code here. This method is called before the invocation of each test method in the class. 18 | } 19 | 20 | - (void)tearDown { 21 | // Put teardown code here. This method is called after the invocation of each test method in the class. 22 | } 23 | 24 | - (void)testExample { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | - (void)testPerformanceExample { 30 | // This is an example of a performance test case. 31 | [self measureBlock:^{ 32 | // Put the code you want to measure the time of here. 33 | }]; 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /PingerUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /PingerUITests/PingerUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PingerUITests.m 3 | // PingerUITests 4 | // 5 | // Created by 廖亚雄 on 2020/9/16. 6 | // 7 | 8 | #import 9 | 10 | @interface PingerUITests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation PingerUITests 15 | 16 | - (void)setUp { 17 | // Put setup code here. This method is called before the invocation of each test method in the class. 18 | 19 | // In UI tests it is usually best to stop immediately when a failure occurs. 20 | self.continueAfterFailure = NO; 21 | 22 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 23 | } 24 | 25 | - (void)tearDown { 26 | // Put teardown code here. This method is called after the invocation of each test method in the class. 27 | } 28 | 29 | - (void)testExample { 30 | // UI tests must launch the application that they test. 31 | XCUIApplication *app = [[XCUIApplication alloc] init]; 32 | [app launch]; 33 | 34 | // Use recording to get started writing UI tests. 35 | // Use XCTAssert and related functions to verify your tests produce the correct results. 36 | } 37 | 38 | - (void)testLaunchPerformance { 39 | if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) { 40 | // This measures how long it takes to launch your application. 41 | [self measureWithMetrics:@[[[XCTApplicationLaunchMetric alloc] init]] block:^{ 42 | [[[XCUIApplication alloc] init] launch]; 43 | }]; 44 | } 45 | } 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pinger --------------------------------------------------------------------------------