├── .gitignore ├── LICENSE ├── MMDB-Swift.podspec ├── MMDB.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ ├── MMDB-OSX.xcscheme │ ├── MMDB-iOS.xcscheme │ ├── MMDBTests-OSX.xcscheme │ └── MMDBTests-iOS.xcscheme ├── Package.swift ├── README.md ├── Sources ├── Info-OSX.plist ├── Info-iOS.plist ├── MMDB.h ├── MMDB.swift └── libmaxminddb │ ├── LICENSE │ ├── data-pool.c │ ├── data-pool.h │ ├── maxminddb-compat-util.h │ ├── maxminddb.c │ ├── maxminddb.h │ ├── maxminddb_config.h │ ├── maxminddb_config.h.in │ ├── maxminddb_unions.c │ └── maxminddb_unions.h └── Tests └── MMDBTests ├── Info-OSX.plist ├── Info-iOS.plist └── MMDBTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/swift,osx,c 3 | 4 | ### Swift ### 5 | # Xcode 6 | # 7 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 8 | 9 | ## Build generated 10 | build/ 11 | DerivedData 12 | 13 | ## Various settings 14 | *.pbxuser 15 | !default.pbxuser 16 | *.mode1v3 17 | !default.mode1v3 18 | *.mode2v3 19 | !default.mode2v3 20 | *.perspectivev3 21 | !default.perspectivev3 22 | xcuserdata 23 | 24 | ## Other 25 | *.xccheckout 26 | *.moved-aside 27 | *.xcuserstate 28 | *.xcscmblueprint 29 | 30 | ## Obj-C/Swift specific 31 | *.hmap 32 | *.ipa 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | # Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/screenshots 64 | 65 | 66 | ### OSX ### 67 | .DS_Store 68 | .AppleDouble 69 | .LSOverride 70 | 71 | # Icon must end with two \r 72 | Icon 73 | 74 | # Thumbnails 75 | ._* 76 | 77 | # Files that might appear in the root of a volume 78 | .DocumentRevisions-V100 79 | .fseventsd 80 | .Spotlight-V100 81 | .TemporaryItems 82 | .Trashes 83 | .VolumeIcon.icns 84 | 85 | # Directories potentially created on remote AFP share 86 | .AppleDB 87 | .AppleDesktop 88 | Network Trash Folder 89 | Temporary Items 90 | .apdisk 91 | 92 | 93 | ### C ### 94 | # Object files 95 | *.o 96 | *.ko 97 | *.obj 98 | *.elf 99 | 100 | # Precompiled Headers 101 | *.gch 102 | *.pch 103 | 104 | # Libraries 105 | *.lib 106 | *.a 107 | *.la 108 | *.lo 109 | 110 | # Shared objects (inc. Windows DLLs) 111 | *.dll 112 | *.so 113 | *.so.* 114 | *.dylib 115 | 116 | # Executables 117 | *.exe 118 | *.out 119 | *.app 120 | *.i*86 121 | *.x86_64 122 | *.hex 123 | 124 | # Debug files 125 | *.dSYM/ 126 | 127 | 128 | # Ignore database file 129 | MMDB/GeoLite2-Country.mmdb 130 | Tests/MMDBTests/GeoLite2-Country.mmdb 131 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /MMDB-Swift.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "MMDB-Swift" 3 | s.version = "0.5.1" 4 | s.summary = "A wrapper for MaxMind DB" 5 | s.description = <<-DESC 6 | A tiny wrapper for libmaxminddb which allows you to lookup 7 | Geo data by IP address. 8 | DESC 9 | 10 | s.homepage = "https://github.com/lexrus/MMDB-Swift" 11 | 12 | s.license = { :type => "APACHE 2.0", :file => "LICENSE" } 13 | 14 | s.author = { "Lex Tang" => "lexrus@gmail.com" } 15 | s.social_media_url = "https://twitter.com/lexrus" 16 | 17 | s.platform = :ios, :osx 18 | 19 | s.swift_version = "5.0" 20 | s.ios.deployment_target = "8.0" 21 | s.osx.deployment_target = "10.10" 22 | 23 | s.source = { :git => "https://github.com/lexrus/MMDB-Swift.git", 24 | :tag => s.version } 25 | 26 | s.source_files = "Sources/MMDB.swift", "Sources/libmaxminddb/*.{h,c}" 27 | s.ios.public_header_files = "Sources/libmaxminddb/*.h", "Sources/MMDB.h" 28 | s.osx.public_header_files = "Sources/libmaxminddb/*.h", "Sources/MMDB.h" 29 | 30 | s.framework = "Foundation" 31 | s.requires_arc = true 32 | end 33 | -------------------------------------------------------------------------------- /MMDB.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 366F5FE71F052F7F00DE3883 /* MMDBTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366F5FE61F052F7F00DE3883 /* MMDBTests.swift */; }; 11 | 366F5FE91F052F7F00DE3883 /* MMDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D716CB471C23A58700D89B5C /* MMDB.framework */; }; 12 | 366F5FF81F05301400DE3883 /* MMDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D716CB541C23A5DC00D89B5C /* MMDB.framework */; }; 13 | 366F5FFE1F05302300DE3883 /* MMDBTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366F5FE61F052F7F00DE3883 /* MMDBTests.swift */; }; 14 | 36D1827B23C0688C002E42A5 /* GeoLite2-Country.mmdb in Resources */ = {isa = PBXBuildFile; fileRef = D73CAED01C21AA9A000C2132 /* GeoLite2-Country.mmdb */; }; 15 | 36D1827C23C0688D002E42A5 /* GeoLite2-Country.mmdb in Resources */ = {isa = PBXBuildFile; fileRef = D73CAED01C21AA9A000C2132 /* GeoLite2-Country.mmdb */; }; 16 | D716CB5C1C23A65200D89B5C /* maxminddb.c in Sources */ = {isa = PBXBuildFile; fileRef = D73CAECC1C21A7A2000C2132 /* maxminddb.c */; }; 17 | D716CB5D1C23A65200D89B5C /* maxminddb_unions.c in Sources */ = {isa = PBXBuildFile; fileRef = D716CB3F1C2269D200D89B5C /* maxminddb_unions.c */; }; 18 | D716CB5E1C23A65D00D89B5C /* MMDB.swift in Sources */ = {isa = PBXBuildFile; fileRef = D716CB321C21432C00D89B5C /* MMDB.swift */; }; 19 | D716CB5F1C23A66C00D89B5C /* MMDB.swift in Sources */ = {isa = PBXBuildFile; fileRef = D716CB321C21432C00D89B5C /* MMDB.swift */; }; 20 | D716CB601C23A66C00D89B5C /* maxminddb.c in Sources */ = {isa = PBXBuildFile; fileRef = D73CAECC1C21A7A2000C2132 /* maxminddb.c */; }; 21 | D716CB611C23A66C00D89B5C /* maxminddb_unions.c in Sources */ = {isa = PBXBuildFile; fileRef = D716CB3F1C2269D200D89B5C /* maxminddb_unions.c */; }; 22 | D736A9811C23B7D40070EAA3 /* maxminddb_config.h in Headers */ = {isa = PBXBuildFile; fileRef = D73CAECE1C21A7B0000C2132 /* maxminddb_config.h */; settings = {ATTRIBUTES = (Public, ); }; }; 23 | D736A9821C23B7D40070EAA3 /* maxminddb-compat-util.h in Headers */ = {isa = PBXBuildFile; fileRef = D73CAED11C21AAEA000C2132 /* maxminddb-compat-util.h */; settings = {ATTRIBUTES = (Public, ); }; }; 24 | D736A9831C23B7D40070EAA3 /* maxminddb.h in Headers */ = {isa = PBXBuildFile; fileRef = D73CAECF1C21A7B0000C2132 /* maxminddb.h */; settings = {ATTRIBUTES = (Public, ); }; }; 25 | D736A9841C23B7D40070EAA3 /* maxminddb_unions.h in Headers */ = {isa = PBXBuildFile; fileRef = D716CB401C2269D200D89B5C /* maxminddb_unions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 26 | D736A9851C23B7E30070EAA3 /* maxminddb_config.h in Headers */ = {isa = PBXBuildFile; fileRef = D73CAECE1C21A7B0000C2132 /* maxminddb_config.h */; settings = {ATTRIBUTES = (Public, ); }; }; 27 | D736A9861C23B7E30070EAA3 /* maxminddb-compat-util.h in Headers */ = {isa = PBXBuildFile; fileRef = D73CAED11C21AAEA000C2132 /* maxminddb-compat-util.h */; settings = {ATTRIBUTES = (Public, ); }; }; 28 | D736A9871C23B7E30070EAA3 /* maxminddb.h in Headers */ = {isa = PBXBuildFile; fileRef = D73CAECF1C21A7B0000C2132 /* maxminddb.h */; settings = {ATTRIBUTES = (Public, ); }; }; 29 | D736A9881C23B7E30070EAA3 /* maxminddb_unions.h in Headers */ = {isa = PBXBuildFile; fileRef = D716CB401C2269D200D89B5C /* maxminddb_unions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 30 | D736A98A1C23DD190070EAA3 /* MMDB.h in Headers */ = {isa = PBXBuildFile; fileRef = D736A9891C23DD190070EAA3 /* MMDB.h */; settings = {ATTRIBUTES = (Public, ); }; }; 31 | D736A98B1C23DD280070EAA3 /* MMDB.h in Headers */ = {isa = PBXBuildFile; fileRef = D736A9891C23DD190070EAA3 /* MMDB.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32 | D7999FEE2307B8B4003EB8CC /* data-pool.h in Headers */ = {isa = PBXBuildFile; fileRef = D7999FEC2307B8B4003EB8CC /* data-pool.h */; }; 33 | D7999FEF2307B8B4003EB8CC /* data-pool.h in Headers */ = {isa = PBXBuildFile; fileRef = D7999FEC2307B8B4003EB8CC /* data-pool.h */; }; 34 | D7999FF02307B8B4003EB8CC /* data-pool.c in Sources */ = {isa = PBXBuildFile; fileRef = D7999FED2307B8B4003EB8CC /* data-pool.c */; }; 35 | D7999FF12307B8B4003EB8CC /* data-pool.c in Sources */ = {isa = PBXBuildFile; fileRef = D7999FED2307B8B4003EB8CC /* data-pool.c */; }; 36 | /* End PBXBuildFile section */ 37 | 38 | /* Begin PBXContainerItemProxy section */ 39 | 366F5FEA1F052F7F00DE3883 /* PBXContainerItemProxy */ = { 40 | isa = PBXContainerItemProxy; 41 | containerPortal = D716CB271C21432C00D89B5C /* Project object */; 42 | proxyType = 1; 43 | remoteGlobalIDString = D716CB461C23A58700D89B5C; 44 | remoteInfo = "MMDB-iOS"; 45 | }; 46 | 366F5FF91F05301400DE3883 /* PBXContainerItemProxy */ = { 47 | isa = PBXContainerItemProxy; 48 | containerPortal = D716CB271C21432C00D89B5C /* Project object */; 49 | proxyType = 1; 50 | remoteGlobalIDString = D716CB531C23A5DC00D89B5C; 51 | remoteInfo = "MMDB-OSX"; 52 | }; 53 | /* End PBXContainerItemProxy section */ 54 | 55 | /* Begin PBXFileReference section */ 56 | 366F5FE41F052F7F00DE3883 /* MMDBTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "MMDBTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | 366F5FE61F052F7F00DE3883 /* MMDBTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MMDBTests.swift; sourceTree = ""; }; 58 | 366F5FE81F052F7F00DE3883 /* Info-iOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = ""; }; 59 | 366F5FF31F05301400DE3883 /* MMDBTests-OSX.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "MMDBTests-OSX.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 60 | 366F5FF71F05301400DE3883 /* Info-OSX.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-OSX.plist"; sourceTree = ""; }; 61 | D716CB321C21432C00D89B5C /* MMDB.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = MMDB.swift; path = ../Sources/MMDB.swift; sourceTree = ""; }; 62 | D716CB3F1C2269D200D89B5C /* maxminddb_unions.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = maxminddb_unions.c; sourceTree = ""; }; 63 | D716CB401C2269D200D89B5C /* maxminddb_unions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = maxminddb_unions.h; sourceTree = ""; }; 64 | D716CB471C23A58700D89B5C /* MMDB.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MMDB.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 65 | D716CB4B1C23A58700D89B5C /* Info-iOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Info-iOS.plist"; path = "../Sources/Info-iOS.plist"; sourceTree = ""; }; 66 | D716CB541C23A5DC00D89B5C /* MMDB.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MMDB.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 67 | D716CB581C23A5DC00D89B5C /* Info-OSX.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Info-OSX.plist"; path = "../Sources/Info-OSX.plist"; sourceTree = ""; }; 68 | D736A9891C23DD190070EAA3 /* MMDB.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MMDB.h; path = ../Sources/MMDB.h; sourceTree = ""; }; 69 | D73CAECC1C21A7A2000C2132 /* maxminddb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = maxminddb.c; sourceTree = ""; }; 70 | D73CAECE1C21A7B0000C2132 /* maxminddb_config.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = maxminddb_config.h; sourceTree = ""; }; 71 | D73CAECF1C21A7B0000C2132 /* maxminddb.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = maxminddb.h; sourceTree = ""; }; 72 | D73CAED01C21AA9A000C2132 /* GeoLite2-Country.mmdb */ = {isa = PBXFileReference; lastKnownFileType = file; path = "GeoLite2-Country.mmdb"; sourceTree = ""; }; 73 | D73CAED11C21AAEA000C2132 /* maxminddb-compat-util.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "maxminddb-compat-util.h"; sourceTree = ""; }; 74 | D7999FEC2307B8B4003EB8CC /* data-pool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "data-pool.h"; sourceTree = ""; }; 75 | D7999FED2307B8B4003EB8CC /* data-pool.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "data-pool.c"; sourceTree = ""; }; 76 | D7999FF22307B9D7003EB8CC /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 77 | D7999FF32307B9D8003EB8CC /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 78 | D7999FF42307B9D8003EB8CC /* MMDB-Swift.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = "MMDB-Swift.podspec"; sourceTree = ""; }; 79 | /* End PBXFileReference section */ 80 | 81 | /* Begin PBXFrameworksBuildPhase section */ 82 | 366F5FE11F052F7F00DE3883 /* Frameworks */ = { 83 | isa = PBXFrameworksBuildPhase; 84 | buildActionMask = 2147483647; 85 | files = ( 86 | 366F5FE91F052F7F00DE3883 /* MMDB.framework in Frameworks */, 87 | ); 88 | runOnlyForDeploymentPostprocessing = 0; 89 | }; 90 | 366F5FF01F05301400DE3883 /* Frameworks */ = { 91 | isa = PBXFrameworksBuildPhase; 92 | buildActionMask = 2147483647; 93 | files = ( 94 | 366F5FF81F05301400DE3883 /* MMDB.framework in Frameworks */, 95 | ); 96 | runOnlyForDeploymentPostprocessing = 0; 97 | }; 98 | D716CB431C23A58700D89B5C /* Frameworks */ = { 99 | isa = PBXFrameworksBuildPhase; 100 | buildActionMask = 2147483647; 101 | files = ( 102 | ); 103 | runOnlyForDeploymentPostprocessing = 0; 104 | }; 105 | D716CB501C23A5DC00D89B5C /* Frameworks */ = { 106 | isa = PBXFrameworksBuildPhase; 107 | buildActionMask = 2147483647; 108 | files = ( 109 | ); 110 | runOnlyForDeploymentPostprocessing = 0; 111 | }; 112 | /* End PBXFrameworksBuildPhase section */ 113 | 114 | /* Begin PBXGroup section */ 115 | 366F5FE51F052F7F00DE3883 /* MMDBTests */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | D73CAED01C21AA9A000C2132 /* GeoLite2-Country.mmdb */, 119 | 366F5FF71F05301400DE3883 /* Info-OSX.plist */, 120 | 366F5FE81F052F7F00DE3883 /* Info-iOS.plist */, 121 | 366F5FE61F052F7F00DE3883 /* MMDBTests.swift */, 122 | ); 123 | name = MMDBTests; 124 | path = Tests/MMDBTests; 125 | sourceTree = ""; 126 | }; 127 | D716CB261C21432C00D89B5C = { 128 | isa = PBXGroup; 129 | children = ( 130 | D7999FF22307B9D7003EB8CC /* README.md */, 131 | D7999FF42307B9D8003EB8CC /* MMDB-Swift.podspec */, 132 | D7999FF32307B9D8003EB8CC /* Package.swift */, 133 | D7C937F0209A79CA007F77C5 /* MMDB-Swift */, 134 | D716CB311C21432C00D89B5C /* libmaxminddb */, 135 | 366F5FE51F052F7F00DE3883 /* MMDBTests */, 136 | D716CB301C21432C00D89B5C /* Products */, 137 | ); 138 | sourceTree = ""; 139 | }; 140 | D716CB301C21432C00D89B5C /* Products */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | D716CB471C23A58700D89B5C /* MMDB.framework */, 144 | D716CB541C23A5DC00D89B5C /* MMDB.framework */, 145 | 366F5FE41F052F7F00DE3883 /* MMDBTests-iOS.xctest */, 146 | 366F5FF31F05301400DE3883 /* MMDBTests-OSX.xctest */, 147 | ); 148 | name = Products; 149 | sourceTree = ""; 150 | }; 151 | D716CB311C21432C00D89B5C /* libmaxminddb */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | D73CAED11C21AAEA000C2132 /* maxminddb-compat-util.h */, 155 | D73CAECC1C21A7A2000C2132 /* maxminddb.c */, 156 | D73CAECF1C21A7B0000C2132 /* maxminddb.h */, 157 | D73CAECE1C21A7B0000C2132 /* maxminddb_config.h */, 158 | D716CB3F1C2269D200D89B5C /* maxminddb_unions.c */, 159 | D716CB401C2269D200D89B5C /* maxminddb_unions.h */, 160 | D7999FED2307B8B4003EB8CC /* data-pool.c */, 161 | D7999FEC2307B8B4003EB8CC /* data-pool.h */, 162 | ); 163 | name = libmaxminddb; 164 | path = Sources/libmaxminddb; 165 | sourceTree = ""; 166 | }; 167 | D7C937F0209A79CA007F77C5 /* MMDB-Swift */ = { 168 | isa = PBXGroup; 169 | children = ( 170 | D716CB581C23A5DC00D89B5C /* Info-OSX.plist */, 171 | D716CB4B1C23A58700D89B5C /* Info-iOS.plist */, 172 | D736A9891C23DD190070EAA3 /* MMDB.h */, 173 | D716CB321C21432C00D89B5C /* MMDB.swift */, 174 | ); 175 | name = "MMDB-Swift"; 176 | path = Sources; 177 | sourceTree = SOURCE_ROOT; 178 | }; 179 | /* End PBXGroup section */ 180 | 181 | /* Begin PBXHeadersBuildPhase section */ 182 | D716CB441C23A58700D89B5C /* Headers */ = { 183 | isa = PBXHeadersBuildPhase; 184 | buildActionMask = 2147483647; 185 | files = ( 186 | D736A98A1C23DD190070EAA3 /* MMDB.h in Headers */, 187 | D736A9811C23B7D40070EAA3 /* maxminddb_config.h in Headers */, 188 | D736A9821C23B7D40070EAA3 /* maxminddb-compat-util.h in Headers */, 189 | D736A9831C23B7D40070EAA3 /* maxminddb.h in Headers */, 190 | D7999FEE2307B8B4003EB8CC /* data-pool.h in Headers */, 191 | D736A9841C23B7D40070EAA3 /* maxminddb_unions.h in Headers */, 192 | ); 193 | runOnlyForDeploymentPostprocessing = 0; 194 | }; 195 | D716CB511C23A5DC00D89B5C /* Headers */ = { 196 | isa = PBXHeadersBuildPhase; 197 | buildActionMask = 2147483647; 198 | files = ( 199 | D736A98B1C23DD280070EAA3 /* MMDB.h in Headers */, 200 | D736A9851C23B7E30070EAA3 /* maxminddb_config.h in Headers */, 201 | D736A9861C23B7E30070EAA3 /* maxminddb-compat-util.h in Headers */, 202 | D736A9871C23B7E30070EAA3 /* maxminddb.h in Headers */, 203 | D7999FEF2307B8B4003EB8CC /* data-pool.h in Headers */, 204 | D736A9881C23B7E30070EAA3 /* maxminddb_unions.h in Headers */, 205 | ); 206 | runOnlyForDeploymentPostprocessing = 0; 207 | }; 208 | /* End PBXHeadersBuildPhase section */ 209 | 210 | /* Begin PBXNativeTarget section */ 211 | 366F5FE31F052F7F00DE3883 /* MMDBTests-iOS */ = { 212 | isa = PBXNativeTarget; 213 | buildConfigurationList = 366F5FEE1F052F7F00DE3883 /* Build configuration list for PBXNativeTarget "MMDBTests-iOS" */; 214 | buildPhases = ( 215 | 366F5FE01F052F7F00DE3883 /* Sources */, 216 | 366F5FE11F052F7F00DE3883 /* Frameworks */, 217 | 366F5FE21F052F7F00DE3883 /* Resources */, 218 | ); 219 | buildRules = ( 220 | ); 221 | dependencies = ( 222 | 366F5FEB1F052F7F00DE3883 /* PBXTargetDependency */, 223 | ); 224 | name = "MMDBTests-iOS"; 225 | productName = MMDBTests; 226 | productReference = 366F5FE41F052F7F00DE3883 /* MMDBTests-iOS.xctest */; 227 | productType = "com.apple.product-type.bundle.unit-test"; 228 | }; 229 | 366F5FF21F05301400DE3883 /* MMDBTests-OSX */ = { 230 | isa = PBXNativeTarget; 231 | buildConfigurationList = 366F5FFB1F05301400DE3883 /* Build configuration list for PBXNativeTarget "MMDBTests-OSX" */; 232 | buildPhases = ( 233 | 366F5FEF1F05301400DE3883 /* Sources */, 234 | 366F5FF01F05301400DE3883 /* Frameworks */, 235 | 366F5FF11F05301400DE3883 /* Resources */, 236 | ); 237 | buildRules = ( 238 | ); 239 | dependencies = ( 240 | 366F5FFA1F05301400DE3883 /* PBXTargetDependency */, 241 | ); 242 | name = "MMDBTests-OSX"; 243 | productName = "MMDBTests-OSX"; 244 | productReference = 366F5FF31F05301400DE3883 /* MMDBTests-OSX.xctest */; 245 | productType = "com.apple.product-type.bundle.unit-test"; 246 | }; 247 | D716CB461C23A58700D89B5C /* MMDB-iOS */ = { 248 | isa = PBXNativeTarget; 249 | buildConfigurationList = D716CB4C1C23A58700D89B5C /* Build configuration list for PBXNativeTarget "MMDB-iOS" */; 250 | buildPhases = ( 251 | D716CB421C23A58700D89B5C /* Sources */, 252 | D716CB431C23A58700D89B5C /* Frameworks */, 253 | D716CB441C23A58700D89B5C /* Headers */, 254 | D716CB451C23A58700D89B5C /* Resources */, 255 | ); 256 | buildRules = ( 257 | ); 258 | dependencies = ( 259 | ); 260 | name = "MMDB-iOS"; 261 | productName = MMDBFramework; 262 | productReference = D716CB471C23A58700D89B5C /* MMDB.framework */; 263 | productType = "com.apple.product-type.framework"; 264 | }; 265 | D716CB531C23A5DC00D89B5C /* MMDB-OSX */ = { 266 | isa = PBXNativeTarget; 267 | buildConfigurationList = D716CB591C23A5DC00D89B5C /* Build configuration list for PBXNativeTarget "MMDB-OSX" */; 268 | buildPhases = ( 269 | D716CB4F1C23A5DC00D89B5C /* Sources */, 270 | D716CB501C23A5DC00D89B5C /* Frameworks */, 271 | D716CB511C23A5DC00D89B5C /* Headers */, 272 | D716CB521C23A5DC00D89B5C /* Resources */, 273 | ); 274 | buildRules = ( 275 | ); 276 | dependencies = ( 277 | ); 278 | name = "MMDB-OSX"; 279 | productName = "MMDB-OSX"; 280 | productReference = D716CB541C23A5DC00D89B5C /* MMDB.framework */; 281 | productType = "com.apple.product-type.framework"; 282 | }; 283 | /* End PBXNativeTarget section */ 284 | 285 | /* Begin PBXProject section */ 286 | D716CB271C21432C00D89B5C /* Project object */ = { 287 | isa = PBXProject; 288 | attributes = { 289 | LastSwiftUpdateCheck = 0830; 290 | LastUpgradeCheck = 1130; 291 | ORGANIZATIONNAME = lexrus.com; 292 | TargetAttributes = { 293 | 366F5FE31F052F7F00DE3883 = { 294 | CreatedOnToolsVersion = 8.3.2; 295 | LastSwiftMigration = 0930; 296 | ProvisioningStyle = Automatic; 297 | }; 298 | 366F5FF21F05301400DE3883 = { 299 | CreatedOnToolsVersion = 8.3.2; 300 | ProvisioningStyle = Automatic; 301 | }; 302 | D716CB461C23A58700D89B5C = { 303 | CreatedOnToolsVersion = 7.2; 304 | LastSwiftMigration = 0930; 305 | }; 306 | D716CB531C23A5DC00D89B5C = { 307 | CreatedOnToolsVersion = 7.2; 308 | LastSwiftMigration = 0810; 309 | }; 310 | }; 311 | }; 312 | buildConfigurationList = D716CB2A1C21432C00D89B5C /* Build configuration list for PBXProject "MMDB" */; 313 | compatibilityVersion = "Xcode 3.2"; 314 | developmentRegion = en; 315 | hasScannedForEncodings = 0; 316 | knownRegions = ( 317 | en, 318 | Base, 319 | ); 320 | mainGroup = D716CB261C21432C00D89B5C; 321 | productRefGroup = D716CB301C21432C00D89B5C /* Products */; 322 | projectDirPath = ""; 323 | projectRoot = ""; 324 | targets = ( 325 | D716CB461C23A58700D89B5C /* MMDB-iOS */, 326 | D716CB531C23A5DC00D89B5C /* MMDB-OSX */, 327 | 366F5FE31F052F7F00DE3883 /* MMDBTests-iOS */, 328 | 366F5FF21F05301400DE3883 /* MMDBTests-OSX */, 329 | ); 330 | }; 331 | /* End PBXProject section */ 332 | 333 | /* Begin PBXResourcesBuildPhase section */ 334 | 366F5FE21F052F7F00DE3883 /* Resources */ = { 335 | isa = PBXResourcesBuildPhase; 336 | buildActionMask = 2147483647; 337 | files = ( 338 | 36D1827B23C0688C002E42A5 /* GeoLite2-Country.mmdb in Resources */, 339 | ); 340 | runOnlyForDeploymentPostprocessing = 0; 341 | }; 342 | 366F5FF11F05301400DE3883 /* Resources */ = { 343 | isa = PBXResourcesBuildPhase; 344 | buildActionMask = 2147483647; 345 | files = ( 346 | 36D1827C23C0688D002E42A5 /* GeoLite2-Country.mmdb in Resources */, 347 | ); 348 | runOnlyForDeploymentPostprocessing = 0; 349 | }; 350 | D716CB451C23A58700D89B5C /* Resources */ = { 351 | isa = PBXResourcesBuildPhase; 352 | buildActionMask = 2147483647; 353 | files = ( 354 | ); 355 | runOnlyForDeploymentPostprocessing = 0; 356 | }; 357 | D716CB521C23A5DC00D89B5C /* Resources */ = { 358 | isa = PBXResourcesBuildPhase; 359 | buildActionMask = 2147483647; 360 | files = ( 361 | ); 362 | runOnlyForDeploymentPostprocessing = 0; 363 | }; 364 | /* End PBXResourcesBuildPhase section */ 365 | 366 | /* Begin PBXSourcesBuildPhase section */ 367 | 366F5FE01F052F7F00DE3883 /* Sources */ = { 368 | isa = PBXSourcesBuildPhase; 369 | buildActionMask = 2147483647; 370 | files = ( 371 | 366F5FE71F052F7F00DE3883 /* MMDBTests.swift in Sources */, 372 | ); 373 | runOnlyForDeploymentPostprocessing = 0; 374 | }; 375 | 366F5FEF1F05301400DE3883 /* Sources */ = { 376 | isa = PBXSourcesBuildPhase; 377 | buildActionMask = 2147483647; 378 | files = ( 379 | 366F5FFE1F05302300DE3883 /* MMDBTests.swift in Sources */, 380 | ); 381 | runOnlyForDeploymentPostprocessing = 0; 382 | }; 383 | D716CB421C23A58700D89B5C /* Sources */ = { 384 | isa = PBXSourcesBuildPhase; 385 | buildActionMask = 2147483647; 386 | files = ( 387 | D716CB5E1C23A65D00D89B5C /* MMDB.swift in Sources */, 388 | D7999FF02307B8B4003EB8CC /* data-pool.c in Sources */, 389 | D716CB5C1C23A65200D89B5C /* maxminddb.c in Sources */, 390 | D716CB5D1C23A65200D89B5C /* maxminddb_unions.c in Sources */, 391 | ); 392 | runOnlyForDeploymentPostprocessing = 0; 393 | }; 394 | D716CB4F1C23A5DC00D89B5C /* Sources */ = { 395 | isa = PBXSourcesBuildPhase; 396 | buildActionMask = 2147483647; 397 | files = ( 398 | D716CB5F1C23A66C00D89B5C /* MMDB.swift in Sources */, 399 | D7999FF12307B8B4003EB8CC /* data-pool.c in Sources */, 400 | D716CB601C23A66C00D89B5C /* maxminddb.c in Sources */, 401 | D716CB611C23A66C00D89B5C /* maxminddb_unions.c in Sources */, 402 | ); 403 | runOnlyForDeploymentPostprocessing = 0; 404 | }; 405 | /* End PBXSourcesBuildPhase section */ 406 | 407 | /* Begin PBXTargetDependency section */ 408 | 366F5FEB1F052F7F00DE3883 /* PBXTargetDependency */ = { 409 | isa = PBXTargetDependency; 410 | target = D716CB461C23A58700D89B5C /* MMDB-iOS */; 411 | targetProxy = 366F5FEA1F052F7F00DE3883 /* PBXContainerItemProxy */; 412 | }; 413 | 366F5FFA1F05301400DE3883 /* PBXTargetDependency */ = { 414 | isa = PBXTargetDependency; 415 | target = D716CB531C23A5DC00D89B5C /* MMDB-OSX */; 416 | targetProxy = 366F5FF91F05301400DE3883 /* PBXContainerItemProxy */; 417 | }; 418 | /* End PBXTargetDependency section */ 419 | 420 | /* Begin XCBuildConfiguration section */ 421 | 366F5FEC1F052F7F00DE3883 /* Debug */ = { 422 | isa = XCBuildConfiguration; 423 | buildSettings = { 424 | CLANG_ANALYZER_NONNULL = YES; 425 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 426 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 427 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 428 | INFOPLIST_FILE = "Tests/MMDBTests/Info-iOS.plist"; 429 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 430 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 431 | PRODUCT_BUNDLE_IDENTIFIER = "com.lexrus.MMDBTests-iOS"; 432 | PRODUCT_NAME = "$(TARGET_NAME)"; 433 | SDKROOT = iphoneos; 434 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 435 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 436 | }; 437 | name = Debug; 438 | }; 439 | 366F5FED1F052F7F00DE3883 /* Release */ = { 440 | isa = XCBuildConfiguration; 441 | buildSettings = { 442 | CLANG_ANALYZER_NONNULL = YES; 443 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 444 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 445 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 446 | INFOPLIST_FILE = "Tests/MMDBTests/Info-iOS.plist"; 447 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 448 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 449 | PRODUCT_BUNDLE_IDENTIFIER = "com.lexrus.MMDBTests-iOS"; 450 | PRODUCT_NAME = "$(TARGET_NAME)"; 451 | SDKROOT = iphoneos; 452 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 453 | VALIDATE_PRODUCT = YES; 454 | }; 455 | name = Release; 456 | }; 457 | 366F5FFC1F05301400DE3883 /* Debug */ = { 458 | isa = XCBuildConfiguration; 459 | buildSettings = { 460 | CLANG_ANALYZER_NONNULL = YES; 461 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 462 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 463 | COMBINE_HIDPI_IMAGES = YES; 464 | INFOPLIST_FILE = "Tests/MMDBTests/Info-OSX.plist"; 465 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 466 | MACOSX_DEPLOYMENT_TARGET = 10.12; 467 | PRODUCT_BUNDLE_IDENTIFIER = "com.lexrus.MMDBTests-OSX"; 468 | PRODUCT_NAME = "$(TARGET_NAME)"; 469 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 470 | }; 471 | name = Debug; 472 | }; 473 | 366F5FFD1F05301400DE3883 /* Release */ = { 474 | isa = XCBuildConfiguration; 475 | buildSettings = { 476 | CLANG_ANALYZER_NONNULL = YES; 477 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 478 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 479 | COMBINE_HIDPI_IMAGES = YES; 480 | INFOPLIST_FILE = "Tests/MMDBTests/Info-OSX.plist"; 481 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 482 | MACOSX_DEPLOYMENT_TARGET = 10.12; 483 | PRODUCT_BUNDLE_IDENTIFIER = "com.lexrus.MMDBTests-OSX"; 484 | PRODUCT_NAME = "$(TARGET_NAME)"; 485 | }; 486 | name = Release; 487 | }; 488 | D716CB341C21432C00D89B5C /* Debug */ = { 489 | isa = XCBuildConfiguration; 490 | buildSettings = { 491 | ALWAYS_SEARCH_USER_PATHS = NO; 492 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 493 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 494 | CLANG_CXX_LIBRARY = "libc++"; 495 | CLANG_ENABLE_MODULES = YES; 496 | CLANG_ENABLE_OBJC_ARC = YES; 497 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 498 | CLANG_WARN_BOOL_CONVERSION = YES; 499 | CLANG_WARN_COMMA = YES; 500 | CLANG_WARN_CONSTANT_CONVERSION = YES; 501 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 502 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 503 | CLANG_WARN_EMPTY_BODY = YES; 504 | CLANG_WARN_ENUM_CONVERSION = YES; 505 | CLANG_WARN_INFINITE_RECURSION = YES; 506 | CLANG_WARN_INT_CONVERSION = YES; 507 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 508 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 509 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 510 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 511 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 512 | CLANG_WARN_STRICT_PROTOTYPES = YES; 513 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 514 | CLANG_WARN_UNREACHABLE_CODE = YES; 515 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 516 | CODE_SIGN_IDENTITY = "-"; 517 | COPY_PHASE_STRIP = NO; 518 | DEBUG_INFORMATION_FORMAT = dwarf; 519 | ENABLE_STRICT_OBJC_MSGSEND = YES; 520 | ENABLE_TESTABILITY = YES; 521 | GCC_C_LANGUAGE_STANDARD = gnu99; 522 | GCC_DYNAMIC_NO_PIC = NO; 523 | GCC_NO_COMMON_BLOCKS = YES; 524 | GCC_OPTIMIZATION_LEVEL = 0; 525 | GCC_PREPROCESSOR_DEFINITIONS = ( 526 | "DEBUG=1", 527 | "$(inherited)", 528 | ); 529 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 530 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 531 | GCC_WARN_UNDECLARED_SELECTOR = YES; 532 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 533 | GCC_WARN_UNUSED_FUNCTION = YES; 534 | GCC_WARN_UNUSED_VARIABLE = YES; 535 | HEADER_SEARCH_PATHS = "${SRCROOT}/MMDB"; 536 | MACOSX_DEPLOYMENT_TARGET = 10.11; 537 | MTL_ENABLE_DEBUG_INFO = YES; 538 | ONLY_ACTIVE_ARCH = YES; 539 | SDKROOT = macosx; 540 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 541 | SWIFT_VERSION = 5.0; 542 | }; 543 | name = Debug; 544 | }; 545 | D716CB351C21432C00D89B5C /* Release */ = { 546 | isa = XCBuildConfiguration; 547 | buildSettings = { 548 | ALWAYS_SEARCH_USER_PATHS = NO; 549 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 550 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 551 | CLANG_CXX_LIBRARY = "libc++"; 552 | CLANG_ENABLE_MODULES = YES; 553 | CLANG_ENABLE_OBJC_ARC = YES; 554 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 555 | CLANG_WARN_BOOL_CONVERSION = YES; 556 | CLANG_WARN_COMMA = YES; 557 | CLANG_WARN_CONSTANT_CONVERSION = YES; 558 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 559 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 560 | CLANG_WARN_EMPTY_BODY = YES; 561 | CLANG_WARN_ENUM_CONVERSION = YES; 562 | CLANG_WARN_INFINITE_RECURSION = YES; 563 | CLANG_WARN_INT_CONVERSION = YES; 564 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 565 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 566 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 567 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 568 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 569 | CLANG_WARN_STRICT_PROTOTYPES = YES; 570 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 571 | CLANG_WARN_UNREACHABLE_CODE = YES; 572 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 573 | CODE_SIGN_IDENTITY = "-"; 574 | COPY_PHASE_STRIP = NO; 575 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 576 | ENABLE_NS_ASSERTIONS = NO; 577 | ENABLE_STRICT_OBJC_MSGSEND = YES; 578 | GCC_C_LANGUAGE_STANDARD = gnu99; 579 | GCC_NO_COMMON_BLOCKS = YES; 580 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 581 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 582 | GCC_WARN_UNDECLARED_SELECTOR = YES; 583 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 584 | GCC_WARN_UNUSED_FUNCTION = YES; 585 | GCC_WARN_UNUSED_VARIABLE = YES; 586 | HEADER_SEARCH_PATHS = "${SRCROOT}/MMDB"; 587 | MACOSX_DEPLOYMENT_TARGET = 10.11; 588 | MTL_ENABLE_DEBUG_INFO = NO; 589 | SDKROOT = macosx; 590 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 591 | SWIFT_VERSION = 5.0; 592 | }; 593 | name = Release; 594 | }; 595 | D716CB4D1C23A58700D89B5C /* Debug */ = { 596 | isa = XCBuildConfiguration; 597 | buildSettings = { 598 | APPLICATION_EXTENSION_API_ONLY = YES; 599 | CODE_SIGN_IDENTITY = ""; 600 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 601 | CURRENT_PROJECT_VERSION = 1; 602 | DEFINES_MODULE = YES; 603 | DYLIB_COMPATIBILITY_VERSION = 1; 604 | DYLIB_CURRENT_VERSION = 1; 605 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 606 | GCC_WARN_64_TO_32_BIT_CONVERSION = NO; 607 | INFOPLIST_FILE = "$(SRCROOT)/Sources/Info-iOS.plist"; 608 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 609 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 610 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 611 | PRODUCT_BUNDLE_IDENTIFIER = "com.lexrus.MMDB-iOS"; 612 | PRODUCT_NAME = MMDB; 613 | SDKROOT = iphoneos; 614 | SKIP_INSTALL = YES; 615 | TARGETED_DEVICE_FAMILY = "1,2"; 616 | VERSIONING_SYSTEM = "apple-generic"; 617 | VERSION_INFO_PREFIX = ""; 618 | }; 619 | name = Debug; 620 | }; 621 | D716CB4E1C23A58700D89B5C /* Release */ = { 622 | isa = XCBuildConfiguration; 623 | buildSettings = { 624 | APPLICATION_EXTENSION_API_ONLY = YES; 625 | CODE_SIGN_IDENTITY = ""; 626 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 627 | CURRENT_PROJECT_VERSION = 1; 628 | DEFINES_MODULE = YES; 629 | DYLIB_COMPATIBILITY_VERSION = 1; 630 | DYLIB_CURRENT_VERSION = 1; 631 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 632 | GCC_WARN_64_TO_32_BIT_CONVERSION = NO; 633 | INFOPLIST_FILE = "$(SRCROOT)/Sources/Info-iOS.plist"; 634 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 635 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 636 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 637 | PRODUCT_BUNDLE_IDENTIFIER = "com.lexrus.MMDB-iOS"; 638 | PRODUCT_NAME = MMDB; 639 | SDKROOT = iphoneos; 640 | SKIP_INSTALL = YES; 641 | TARGETED_DEVICE_FAMILY = "1,2"; 642 | VALIDATE_PRODUCT = YES; 643 | VERSIONING_SYSTEM = "apple-generic"; 644 | VERSION_INFO_PREFIX = ""; 645 | }; 646 | name = Release; 647 | }; 648 | D716CB5A1C23A5DC00D89B5C /* Debug */ = { 649 | isa = XCBuildConfiguration; 650 | buildSettings = { 651 | APPLICATION_EXTENSION_API_ONLY = YES; 652 | CODE_SIGN_IDENTITY = ""; 653 | COMBINE_HIDPI_IMAGES = YES; 654 | CURRENT_PROJECT_VERSION = 1; 655 | DEFINES_MODULE = YES; 656 | DYLIB_COMPATIBILITY_VERSION = 1; 657 | DYLIB_CURRENT_VERSION = 1; 658 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 659 | FRAMEWORK_VERSION = A; 660 | GCC_WARN_64_TO_32_BIT_CONVERSION = NO; 661 | INFOPLIST_FILE = "$(SRCROOT)/Sources/Info-OSX.plist"; 662 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 663 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 664 | MACOSX_DEPLOYMENT_TARGET = 10.9; 665 | PRODUCT_BUNDLE_IDENTIFIER = "com.lexrus.MMDB-OSX"; 666 | PRODUCT_NAME = MMDB; 667 | SKIP_INSTALL = YES; 668 | VERSIONING_SYSTEM = "apple-generic"; 669 | VERSION_INFO_PREFIX = ""; 670 | }; 671 | name = Debug; 672 | }; 673 | D716CB5B1C23A5DC00D89B5C /* Release */ = { 674 | isa = XCBuildConfiguration; 675 | buildSettings = { 676 | APPLICATION_EXTENSION_API_ONLY = YES; 677 | CODE_SIGN_IDENTITY = ""; 678 | COMBINE_HIDPI_IMAGES = YES; 679 | CURRENT_PROJECT_VERSION = 1; 680 | DEFINES_MODULE = YES; 681 | DYLIB_COMPATIBILITY_VERSION = 1; 682 | DYLIB_CURRENT_VERSION = 1; 683 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 684 | FRAMEWORK_VERSION = A; 685 | GCC_WARN_64_TO_32_BIT_CONVERSION = NO; 686 | INFOPLIST_FILE = "$(SRCROOT)/Sources/Info-OSX.plist"; 687 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 688 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 689 | MACOSX_DEPLOYMENT_TARGET = 10.9; 690 | PRODUCT_BUNDLE_IDENTIFIER = "com.lexrus.MMDB-OSX"; 691 | PRODUCT_NAME = MMDB; 692 | SKIP_INSTALL = YES; 693 | VERSIONING_SYSTEM = "apple-generic"; 694 | VERSION_INFO_PREFIX = ""; 695 | }; 696 | name = Release; 697 | }; 698 | /* End XCBuildConfiguration section */ 699 | 700 | /* Begin XCConfigurationList section */ 701 | 366F5FEE1F052F7F00DE3883 /* Build configuration list for PBXNativeTarget "MMDBTests-iOS" */ = { 702 | isa = XCConfigurationList; 703 | buildConfigurations = ( 704 | 366F5FEC1F052F7F00DE3883 /* Debug */, 705 | 366F5FED1F052F7F00DE3883 /* Release */, 706 | ); 707 | defaultConfigurationIsVisible = 0; 708 | defaultConfigurationName = Release; 709 | }; 710 | 366F5FFB1F05301400DE3883 /* Build configuration list for PBXNativeTarget "MMDBTests-OSX" */ = { 711 | isa = XCConfigurationList; 712 | buildConfigurations = ( 713 | 366F5FFC1F05301400DE3883 /* Debug */, 714 | 366F5FFD1F05301400DE3883 /* Release */, 715 | ); 716 | defaultConfigurationIsVisible = 0; 717 | defaultConfigurationName = Release; 718 | }; 719 | D716CB2A1C21432C00D89B5C /* Build configuration list for PBXProject "MMDB" */ = { 720 | isa = XCConfigurationList; 721 | buildConfigurations = ( 722 | D716CB341C21432C00D89B5C /* Debug */, 723 | D716CB351C21432C00D89B5C /* Release */, 724 | ); 725 | defaultConfigurationIsVisible = 0; 726 | defaultConfigurationName = Release; 727 | }; 728 | D716CB4C1C23A58700D89B5C /* Build configuration list for PBXNativeTarget "MMDB-iOS" */ = { 729 | isa = XCConfigurationList; 730 | buildConfigurations = ( 731 | D716CB4D1C23A58700D89B5C /* Debug */, 732 | D716CB4E1C23A58700D89B5C /* Release */, 733 | ); 734 | defaultConfigurationIsVisible = 0; 735 | defaultConfigurationName = Release; 736 | }; 737 | D716CB591C23A5DC00D89B5C /* Build configuration list for PBXNativeTarget "MMDB-OSX" */ = { 738 | isa = XCConfigurationList; 739 | buildConfigurations = ( 740 | D716CB5A1C23A5DC00D89B5C /* Debug */, 741 | D716CB5B1C23A5DC00D89B5C /* Release */, 742 | ); 743 | defaultConfigurationIsVisible = 0; 744 | defaultConfigurationName = Release; 745 | }; 746 | /* End XCConfigurationList section */ 747 | }; 748 | rootObject = D716CB271C21432C00D89B5C /* Project object */; 749 | } 750 | -------------------------------------------------------------------------------- /MMDB.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MMDB.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /MMDB.xcodeproj/xcshareddata/xcschemes/MMDB-OSX.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 42 | 48 | 49 | 50 | 51 | 52 | 62 | 63 | 69 | 70 | 71 | 72 | 78 | 79 | 85 | 86 | 87 | 88 | 90 | 91 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /MMDB.xcodeproj/xcshareddata/xcschemes/MMDB-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 51 | 52 | 53 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 76 | 77 | 83 | 84 | 85 | 86 | 92 | 93 | 99 | 100 | 101 | 102 | 104 | 105 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /MMDB.xcodeproj/xcshareddata/xcschemes/MMDBTests-OSX.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 37 | 38 | 44 | 45 | 47 | 48 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /MMDB.xcodeproj/xcshareddata/xcschemes/MMDBTests-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 37 | 38 | 44 | 45 | 47 | 48 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "MMDB", 6 | products: [ 7 | .library(name: "MMDB", targets: ["MMDB"]), 8 | ], 9 | dependencies: [], 10 | targets: [ 11 | .target(name: "MMDB", dependencies: ["libmaxminddb"]), 12 | .testTarget(name: "MMDBTests", dependencies: ["MMDB"]), 13 | .target(name: "libmaxminddb") 14 | ] 15 | ) 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MMDB-Swift 2 | 3 | ![Language](https://img.shields.io/badge/language-Swift%205-orange.svg) 4 | [![Version](https://img.shields.io/cocoapods/v/MMDB-Swift.svg?style=flat)](http://cocoapods.org/pods/MMDB-Swift) 5 | [![Carthage Compatible](https://img.shields.io/badge/Carthage-✓-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 6 | [![SPM Compatible](https://img.shields.io/badge/SPM-✓-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 7 | ![Platform](https://img.shields.io/badge/platform-iOS%7COSX%7CLinux-lightgrey.svg) 8 | 9 | 10 | A tiny wrapper for [libmaxminddb](https://github.com/maxmind/libmaxminddb) which allows you to lookup Geo data by IP address. 11 | 12 | --- 13 | 14 | **NOTE** From `v0.5.0`, MMDB-Swift no longer bundles [GeoLite2 database](http://dev.maxmind.com/geoip/geoip2/geolite2/) due to the license change. Developers should download the binary version from the [Maxmind website](http://dev.maxmind.com/geoip/geoip2/geolite2/). 15 | 16 | ## CocoaPods 17 | 18 | MMDB-Swift is available through [CocoaPods](http://cocoapods.org). To install it, simply add the following line to your Podfile: 19 | 20 | ``` ruby 21 | platform :ios, '8.0' 22 | use_frameworks! 23 | pod "MMDB-Swift" 24 | ``` 25 | 26 | Then, run the following command: 27 | 28 | ``` bash 29 | pod install 30 | ``` 31 | 32 | ## Carthage 33 | 34 | To integrate MMDB-Swift into your Xcode project using Carthage, add the following line to your `Cartfile`: 35 | 36 | ``` 37 | github "lexrus/MMDB-Swift" 38 | ``` 39 | 40 | Run `carthage update` to build the frameworks and drag the built `MMDB.framework` into your Xcode project. 41 | 42 | ## Swift Package Manager 43 | 44 | Package.swift 45 | 46 | ``` swift 47 | import PackageDescription 48 | 49 | let package = Package( 50 | name: "YOUR_AWESOME_PROJECT", 51 | targets: [], 52 | dependencies: [ 53 | .Package( 54 | url: "https://github.com/lexrus/MMDB-Swift", 55 | versions: "0.0.1" ..< Version.max 56 | ) 57 | ] 58 | ) 59 | ``` 60 | 61 | 62 | ## Usage 63 | 64 | ``` swift 65 | guard let db = MMDB("PATH_TO_THE_DATABASE") else { 66 | print("Failed to open DB.") 67 | return 68 | } 69 | if let country = db.lookup("8.8.4.4") { 70 | print(country) 71 | } 72 | ``` 73 | 74 | This outputs: 75 | 76 | ``` json 77 | { 78 | "continent": { 79 | "code": "NA", 80 | "names": { 81 | "ja": "北アメリカ", 82 | "en": "North America", 83 | "ru": "Северная Америка", 84 | "es": "Norteamérica", 85 | "de": "Nordamerika", 86 | "zh-CN": "北美洲", 87 | "fr": "Amérique du Nord", 88 | "pt-BR": "América do Norte" 89 | } 90 | }, 91 | "isoCode": "US", 92 | "names": { 93 | "ja": "アメリカ合衆国", 94 | "en": "United States", 95 | "ru": "США", 96 | "es": "Estados Unidos", 97 | "de": "USA", 98 | "zh-CN": "美国", 99 | "fr": "États-Unis", 100 | "pt-BR": "Estados Unidos" 101 | } 102 | } 103 | ``` 104 | 105 | Notice that country is a struct defined as: 106 | 107 | ``` swift 108 | public struct MMDBContinent { 109 | var code: String? 110 | var names: [String: String]? 111 | } 112 | 113 | public struct MMDBCountry: CustomStringConvertible { 114 | var continent = MMDBContinent() 115 | var isoCode = "" 116 | var names = [String: String]() 117 | ... 118 | } 119 | ``` 120 | 121 | ## Author 122 | 123 | [Lex Tang](https://github.com/lexrus) (Twitter: [@lexrus](https://twitter.com/lexrus)) 124 | 125 | ## License 126 | 127 | MMDB-Swift is available under the [Apache License Version 2.0](http://www.apache.org/licenses/LICENSE-2.0). See the [LICENSE](https://github.com/lexrus/MMDB-Swift/blob/master/LICENSE) file for more info. 128 | 129 | The GeoLite2 databases are distributed under the [Creative Commons Attribution-ShareAlike 3.0 Unported License](http://creativecommons.org/licenses/by-sa/3.0/). 130 | -------------------------------------------------------------------------------- /Sources/Info-OSX.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2017 lexrus.com. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Sources/Info-iOS.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Sources/MMDB.h: -------------------------------------------------------------------------------- 1 | // 2 | // MMDB.h 3 | // MMDB 4 | // 5 | // Created by Lex on 12/18/15. 6 | // Copyright © 2017 lexrus.com. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for MMDB. 12 | FOUNDATION_EXPORT double MMDBVersionNumber; 13 | 14 | //! Project version string for MMDB. 15 | FOUNDATION_EXPORT const unsigned char MMDBVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | #import 20 | #import 21 | #import 22 | -------------------------------------------------------------------------------- /Sources/MMDB.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MMDB.swift 3 | // MMDB 4 | // 5 | // Created by Lex on 12/16/15. 6 | // Copyright © 2017 lexrus.com. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct MMDBContinent { 12 | public var code: String? 13 | public var names: [String: String]? 14 | } 15 | 16 | public struct MMDBCountry: CustomStringConvertible { 17 | public var continent = MMDBContinent() 18 | public var isoCode = "" 19 | public var names = [String: String]() 20 | 21 | init(dictionary: NSDictionary) { 22 | if let dict = dictionary["continent"] as? NSDictionary, 23 | let code = dict["code"] as? String, 24 | let continentNames = dict["names"] as? [String: String] 25 | { 26 | continent.code = code 27 | continent.names = continentNames 28 | } 29 | if let dict = dictionary["country"] as? NSDictionary, 30 | let iso = dict["iso_code"] as? String, 31 | let countryNames = dict["names"] as? [String: String] 32 | { 33 | self.isoCode = iso 34 | self.names = countryNames 35 | } 36 | } 37 | 38 | public var description: String { 39 | var s = "{\n" 40 | s += " \"continent\": {\n" 41 | s += " \"code\": \"" + (self.continent.code ?? "") + "\",\n" 42 | s += " \"names\": {\n" 43 | var i = continent.names?.count ?? 0 44 | continent.names?.forEach { 45 | s += " \"" 46 | s += $0.0 + "\": \"" 47 | s += $0.1 + "\"" 48 | s += (i > 1 ? "," : "") 49 | s += "\n" 50 | i -= 1 51 | } 52 | s += " }\n" 53 | s += " },\n" 54 | s += " \"isoCode\": \"" + self.isoCode + "\",\n" 55 | s += " \"names\": {\n" 56 | i = names.count 57 | names.forEach { 58 | s += " \"" 59 | s += $0.0 + "\": \"" 60 | s += $0.1 + "\"" 61 | s += (i > 1 ? "," : "") 62 | s += "\n" 63 | i -= 1 64 | } 65 | s += " }\n}" 66 | return s 67 | } 68 | } 69 | 70 | final public class MMDB { 71 | 72 | fileprivate var db = MMDB_s() 73 | 74 | fileprivate typealias ListPtr = UnsafeMutablePointer 75 | fileprivate typealias StringPtr = UnsafeMutablePointer 76 | 77 | public init?(_ filename: String) { 78 | if openDB(atPath: filename) { return } 79 | 80 | return nil 81 | } 82 | private func openDB(atPath: String) -> Bool { 83 | let cfilename = (atPath as NSString).utf8String 84 | let cfilenamePtr = UnsafePointer(cfilename) 85 | let status = MMDB_open(cfilenamePtr, UInt32(MMDB_MODE_MASK), &db) 86 | if status != MMDB_SUCCESS { 87 | print(String(cString: MMDB_strerror(errno))) 88 | return false 89 | } else { 90 | return true 91 | } 92 | } 93 | 94 | fileprivate func lookupString(_ s: String) -> MMDB_lookup_result_s? { 95 | let string = (s as NSString).utf8String 96 | let stringPtr = UnsafePointer(string) 97 | 98 | var gaiError: Int32 = 0 99 | var error: Int32 = 0 100 | 101 | let result = MMDB_lookup_string(&db, stringPtr, &gaiError, &error) 102 | if gaiError == noErr && error == noErr { 103 | return result 104 | } 105 | return nil 106 | } 107 | 108 | fileprivate func getString(_ list: ListPtr) -> String { 109 | var data = list.pointee.entry_data 110 | let type = (Int32)(data.type) 111 | 112 | // Ignore other useless keys 113 | guard data.has_data && type == MMDB_DATA_TYPE_UTF8_STRING else { 114 | return "" 115 | } 116 | 117 | let str = MMDB_get_entry_data_char(&data) 118 | let size = size_t(data.data_size) 119 | let cKey = mmdb_strndup(str, size) 120 | let key = String(cString: cKey!) 121 | free(cKey) 122 | 123 | return key 124 | } 125 | 126 | fileprivate func getType(_ list: ListPtr) -> Int32 { 127 | let data = list.pointee.entry_data 128 | return (Int32)(data.type) 129 | } 130 | 131 | fileprivate func getSize(_ list: ListPtr) -> UInt32 { 132 | return list.pointee.entry_data.data_size 133 | } 134 | 135 | 136 | public func lookup(_ IPString: String) -> MMDBCountry? { 137 | guard let dict = lookup(ip: IPString) else { 138 | return nil 139 | } 140 | 141 | let country = MMDBCountry(dictionary: dict) 142 | 143 | return country 144 | } 145 | 146 | private func dump(list: ListPtr?) -> (ptr: ListPtr?, out: Any?) { 147 | var list = list 148 | switch getType(list!) { 149 | 150 | case MMDB_DATA_TYPE_MAP: 151 | let dict = NSMutableDictionary() 152 | var size = getSize(list!) 153 | 154 | list = list?.pointee.next 155 | while size > 0 && list != nil { 156 | let key = getString(list!) 157 | list = list?.pointee.next 158 | let sub = dump(list: list) 159 | list = sub.ptr 160 | if let out = sub.out, key.count > 0 { 161 | dict[key] = out 162 | } else { 163 | break 164 | } 165 | size -= 1 166 | } 167 | return (ptr: list, out: dict) 168 | 169 | case MMDB_DATA_TYPE_UTF8_STRING: 170 | let str = getString(list!) 171 | list = list?.pointee.next 172 | return (ptr: list, out: str) 173 | 174 | case MMDB_DATA_TYPE_UINT32: 175 | var res: NSNumber = 0 176 | if let entryData = list?.pointee.entry_data { 177 | var mutableEntryData = entryData 178 | if let uint = MMDB_get_entry_data_uint32(&mutableEntryData) { 179 | let v: UInt32 = uint.pointee 180 | res = NSNumber(value: v) 181 | } 182 | } 183 | list = list?.pointee.next 184 | return (ptr: list, out: res) 185 | 186 | default: () 187 | 188 | } 189 | return (ptr: list, out: nil) 190 | } 191 | 192 | public func lookup(ip: String) -> NSDictionary? { 193 | guard let result = lookupString(ip) else { 194 | return nil 195 | } 196 | 197 | var entry = result.entry 198 | var list: ListPtr? 199 | let status = MMDB_get_entry_data_list(&entry, &list) 200 | if status != MMDB_SUCCESS { 201 | return nil 202 | } 203 | let res = self.dump(list: list) 204 | if let dict = res.out, let d = dict as? NSDictionary { 205 | return d 206 | } 207 | return nil 208 | } 209 | 210 | deinit { 211 | MMDB_close(&db) 212 | } 213 | 214 | } 215 | -------------------------------------------------------------------------------- /Sources/libmaxminddb/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Sources/libmaxminddb/data-pool.c: -------------------------------------------------------------------------------- 1 | #include "data-pool.h" 2 | #include "maxminddb.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | static bool can_multiply(size_t const, size_t const, size_t const); 9 | 10 | // Allocate an MMDB_data_pool_s. It initially has space for size 11 | // MMDB_entry_data_list_s structs. 12 | MMDB_data_pool_s *data_pool_new(size_t const size) 13 | { 14 | MMDB_data_pool_s *const pool = calloc(1, sizeof(MMDB_data_pool_s)); 15 | if (!pool) { 16 | return NULL; 17 | } 18 | 19 | if (size == 0 || 20 | !can_multiply(SIZE_MAX, size, sizeof(MMDB_entry_data_list_s))) { 21 | data_pool_destroy(pool); 22 | return NULL; 23 | } 24 | pool->size = size; 25 | pool->blocks[0] = calloc(pool->size, sizeof(MMDB_entry_data_list_s)); 26 | if (!pool->blocks[0]) { 27 | data_pool_destroy(pool); 28 | return NULL; 29 | } 30 | pool->blocks[0]->pool = pool; 31 | 32 | pool->sizes[0] = size; 33 | 34 | pool->block = pool->blocks[0]; 35 | 36 | return pool; 37 | } 38 | 39 | // Determine if we can multiply m*n. We can do this if the result will be below 40 | // the given max. max will typically be SIZE_MAX. 41 | // 42 | // We want to know if we'll wrap around. 43 | static bool can_multiply(size_t const max, size_t const m, size_t const n) 44 | { 45 | if (m == 0) { 46 | return false; 47 | } 48 | 49 | return n <= max / m; 50 | } 51 | 52 | // Clean up the data pool. 53 | void data_pool_destroy(MMDB_data_pool_s *const pool) 54 | { 55 | if (!pool) { 56 | return; 57 | } 58 | 59 | for (size_t i = 0; i <= pool->index; i++) { 60 | free(pool->blocks[i]); 61 | } 62 | 63 | free(pool); 64 | } 65 | 66 | // Claim a new struct from the pool. Doing this may cause the pool's size to 67 | // grow. 68 | MMDB_entry_data_list_s *data_pool_alloc(MMDB_data_pool_s *const pool) 69 | { 70 | if (!pool) { 71 | return NULL; 72 | } 73 | 74 | if (pool->used < pool->size) { 75 | MMDB_entry_data_list_s *const element = pool->block + pool->used; 76 | pool->used++; 77 | return element; 78 | } 79 | 80 | // Take it from a new block of memory. 81 | 82 | size_t const new_index = pool->index + 1; 83 | if (new_index == DATA_POOL_NUM_BLOCKS) { 84 | // See the comment about not growing this on DATA_POOL_NUM_BLOCKS. 85 | return NULL; 86 | } 87 | 88 | if (!can_multiply(SIZE_MAX, pool->size, 2)) { 89 | return NULL; 90 | } 91 | size_t const new_size = pool->size * 2; 92 | 93 | if (!can_multiply(SIZE_MAX, new_size, sizeof(MMDB_entry_data_list_s))) { 94 | return NULL; 95 | } 96 | pool->blocks[new_index] = calloc(new_size, sizeof(MMDB_entry_data_list_s)); 97 | if (!pool->blocks[new_index]) { 98 | return NULL; 99 | } 100 | 101 | // We don't need to set this, but it's useful for introspection in tests. 102 | pool->blocks[new_index]->pool = pool; 103 | 104 | pool->index = new_index; 105 | pool->block = pool->blocks[pool->index]; 106 | 107 | pool->size = new_size; 108 | pool->sizes[pool->index] = pool->size; 109 | 110 | MMDB_entry_data_list_s *const element = pool->block; 111 | pool->used = 1; 112 | return element; 113 | } 114 | 115 | // Turn the structs in the array-like pool into a linked list. 116 | // 117 | // Before calling this function, the list isn't linked up. 118 | MMDB_entry_data_list_s *data_pool_to_list(MMDB_data_pool_s *const pool) 119 | { 120 | if (!pool) { 121 | return NULL; 122 | } 123 | 124 | if (pool->index == 0 && pool->used == 0) { 125 | return NULL; 126 | } 127 | 128 | for (size_t i = 0; i <= pool->index; i++) { 129 | MMDB_entry_data_list_s *const block = pool->blocks[i]; 130 | 131 | size_t size = pool->sizes[i]; 132 | if (i == pool->index) { 133 | size = pool->used; 134 | } 135 | 136 | for (size_t j = 0; j < size - 1; j++) { 137 | MMDB_entry_data_list_s *const cur = block + j; 138 | cur->next = block + j + 1; 139 | } 140 | 141 | if (i < pool->index) { 142 | MMDB_entry_data_list_s *const last = block + size - 1; 143 | last->next = pool->blocks[i + 1]; 144 | } 145 | } 146 | 147 | return pool->blocks[0]; 148 | } 149 | 150 | #ifdef TEST_DATA_POOL 151 | 152 | #include 153 | #include 154 | 155 | static void test_can_multiply(void); 156 | 157 | int main(void) 158 | { 159 | plan(NO_PLAN); 160 | test_can_multiply(); 161 | done_testing(); 162 | } 163 | 164 | static void test_can_multiply(void) 165 | { 166 | { 167 | ok(can_multiply(SIZE_MAX, 1, SIZE_MAX), "1*SIZE_MAX is ok"); 168 | } 169 | 170 | { 171 | ok(!can_multiply(SIZE_MAX, 2, SIZE_MAX), "2*SIZE_MAX is not ok"); 172 | } 173 | 174 | { 175 | ok(can_multiply(SIZE_MAX, 10240, sizeof(MMDB_entry_data_list_s)), 176 | "1024 entry_data_list_s's are okay"); 177 | } 178 | } 179 | 180 | #endif 181 | -------------------------------------------------------------------------------- /Sources/libmaxminddb/data-pool.h: -------------------------------------------------------------------------------- 1 | #ifndef DATA_POOL_H 2 | #define DATA_POOL_H 3 | 4 | #include "maxminddb.h" 5 | 6 | #include 7 | #include 8 | 9 | // This should be large enough that we never need to grow the array of pointers 10 | // to blocks. 32 is enough. Even starting out of with size 1 (1 struct), the 11 | // 32nd element alone will provide 2**32 structs as we exponentially increase 12 | // the number in each block. Being confident that we do not have to grow the 13 | // array lets us avoid writing code to do that. That code would be risky as it 14 | // would rarely be hit and likely not be well tested. 15 | #define DATA_POOL_NUM_BLOCKS 32 16 | 17 | // A pool of memory for MMDB_entry_data_list_s structs. This is so we can 18 | // allocate multiple up front rather than one at a time for performance 19 | // reasons. 20 | // 21 | // The order you add elements to it (by calling data_pool_alloc()) ends up as 22 | // the order of the list. 23 | // 24 | // The memory only grows. There is no support for releasing an element you take 25 | // back to the pool. 26 | typedef struct MMDB_data_pool_s { 27 | // Index of the current block we're allocating out of. 28 | size_t index; 29 | 30 | // The size of the current block, counting by structs. 31 | size_t size; 32 | 33 | // How many used in the current block, counting by structs. 34 | size_t used; 35 | 36 | // The current block we're allocating out of. 37 | MMDB_entry_data_list_s *block; 38 | 39 | // The size of each block. 40 | size_t sizes[DATA_POOL_NUM_BLOCKS]; 41 | 42 | // An array of pointers to blocks of memory holding space for list 43 | // elements. 44 | MMDB_entry_data_list_s *blocks[DATA_POOL_NUM_BLOCKS]; 45 | } MMDB_data_pool_s; 46 | 47 | MMDB_data_pool_s *data_pool_new(size_t const); 48 | void data_pool_destroy(MMDB_data_pool_s *const); 49 | MMDB_entry_data_list_s *data_pool_alloc(MMDB_data_pool_s *const); 50 | MMDB_entry_data_list_s *data_pool_to_list(MMDB_data_pool_s *const); 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /Sources/libmaxminddb/maxminddb-compat-util.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* *INDENT-OFF* */ 5 | 6 | /* The memmem, strdup, and strndup functions were all copied from the 7 | * FreeBSD source, along with the relevant copyright notice. 8 | * 9 | * It'd be nicer to simply use the functions available on the system if they 10 | * exist, but there doesn't seem to be a good way to detect them without also 11 | * defining things like _GNU_SOURCE, which we want to avoid, because then we 12 | * end up _accidentally_ using GNU features without noticing, which then 13 | * breaks on systems like OSX. 14 | * 15 | * C is fun! */ 16 | 17 | /* Applies to memmem implementation */ 18 | /*- 19 | * Copyright (c) 2005 Pascal Gloor 20 | * 21 | * Redistribution and use in source and binary forms, with or without 22 | * modification, are permitted provided that the following conditions 23 | * are met: 24 | * 1. Redistributions of source code must retain the above copyright 25 | * notice, this list of conditions and the following disclaimer. 26 | * 2. Redistributions in binary form must reproduce the above copyright 27 | * notice, this list of conditions and the following disclaimer in the 28 | * documentation and/or other materials provided with the distribution. 29 | * 3. The name of the author may not be used to endorse or promote 30 | * products derived from this software without specific prior written 31 | * permission. 32 | * 33 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 34 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 35 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 36 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 37 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 38 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 39 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 40 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 41 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 42 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 43 | * SUCH DAMAGE. 44 | */ 45 | static void * 46 | mmdb_memmem(const void *l, size_t l_len, const void *s, size_t s_len) 47 | { 48 | register char *cur, *last; 49 | const char *cl = (const char *)l; 50 | const char *cs = (const char *)s; 51 | 52 | /* we need something to compare */ 53 | if (l_len == 0 || s_len == 0) 54 | return NULL; 55 | 56 | /* "s" must be smaller or equal to "l" */ 57 | if (l_len < s_len) 58 | return NULL; 59 | 60 | /* special case where s_len == 1 */ 61 | if (s_len == 1) 62 | return memchr(l, (int)*cs, l_len); 63 | 64 | /* the last position where its possible to find "s" in "l" */ 65 | last = (char *)cl + l_len - s_len; 66 | 67 | for (cur = (char *)cl; cur <= last; cur++) 68 | if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0) 69 | return cur; 70 | 71 | return NULL; 72 | } 73 | 74 | /* Applies to strnlen implementation */ 75 | /*- 76 | * Copyright (c) 2009 David Schultz 77 | * All rights reserved. 78 | * 79 | * Redistribution and use in source and binary forms, with or without 80 | * modification, are permitted provided that the following conditions 81 | * are met: 82 | * 1. Redistributions of source code must retain the above copyright 83 | * notice, this list of conditions and the following disclaimer. 84 | * 2. Redistributions in binary form must reproduce the above copyright 85 | * notice, this list of conditions and the following disclaimer in the 86 | * documentation and/or other materials provided with the distribution. 87 | * 88 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 89 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 90 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 91 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 92 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 93 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 94 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 95 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 96 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 97 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 98 | * SUCH DAMAGE. 99 | */ 100 | static size_t 101 | mmdb_strnlen(const char *s, size_t maxlen) 102 | { 103 | size_t len; 104 | 105 | for (len = 0; len < maxlen; len++, s++) { 106 | if (!*s) 107 | break; 108 | } 109 | return (len); 110 | } 111 | 112 | /* Applies to strdup and strndup implementation */ 113 | /* 114 | * Copyright (c) 1988, 1993 115 | * The Regents of the University of California. All rights reserved. 116 | * 117 | * Redistribution and use in source and binary forms, with or without 118 | * modification, are permitted provided that the following conditions 119 | * are met: 120 | * 1. Redistributions of source code must retain the above copyright 121 | * notice, this list of conditions and the following disclaimer. 122 | * 2. Redistributions in binary form must reproduce the above copyright 123 | * notice, this list of conditions and the following disclaimer in the 124 | * documentation and/or other materials provided with the distribution. 125 | * 3. Neither the name of the University nor the names of its contributors 126 | * may be used to endorse or promote products derived from this software 127 | * without specific prior written permission. 128 | * 129 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 130 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 131 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 132 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 133 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 134 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 135 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 136 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 137 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 138 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 139 | * SUCH DAMAGE. 140 | */ 141 | static char * 142 | mmdb_strdup(const char *str) 143 | { 144 | size_t len; 145 | char *copy; 146 | 147 | len = strlen(str) + 1; 148 | if ((copy = malloc(len)) == NULL) 149 | return (NULL); 150 | memcpy(copy, str, len); 151 | return (copy); 152 | } 153 | 154 | static char * 155 | mmdb_strndup(const char *str, size_t n) 156 | { 157 | size_t len; 158 | char *copy; 159 | 160 | len = mmdb_strnlen(str, n); 161 | if ((copy = malloc(len + 1)) == NULL) 162 | return (NULL); 163 | memcpy(copy, str, len); 164 | copy[len] = '\0'; 165 | return (copy); 166 | } 167 | /* *INDENT-ON* */ 168 | -------------------------------------------------------------------------------- /Sources/libmaxminddb/maxminddb.c: -------------------------------------------------------------------------------- 1 | #if HAVE_CONFIG_H 2 | #include 3 | #endif 4 | #include "data-pool.h" 5 | #include "maxminddb.h" 6 | #include "maxminddb-compat-util.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #ifdef _WIN32 17 | #ifndef UNICODE 18 | #define UNICODE 19 | #endif 20 | #include 21 | #include 22 | #else 23 | #include 24 | #include 25 | #include 26 | #endif 27 | 28 | #define MMDB_DATA_SECTION_SEPARATOR (16) 29 | #define MAXIMUM_DATA_STRUCTURE_DEPTH (512) 30 | 31 | #ifdef MMDB_DEBUG 32 | #define LOCAL 33 | #define DEBUG_MSG(msg) fprintf(stderr, msg "\n") 34 | #define DEBUG_MSGF(fmt, ...) fprintf(stderr, fmt "\n", __VA_ARGS__) 35 | #define DEBUG_BINARY(fmt, byte) \ 36 | do { \ 37 | char *binary = byte_to_binary(byte); \ 38 | if (NULL == binary) { \ 39 | fprintf(stderr, "Malloc failed in DEBUG_BINARY\n"); \ 40 | abort(); \ 41 | } \ 42 | fprintf(stderr, fmt "\n", binary); \ 43 | free(binary); \ 44 | } while (0) 45 | #define DEBUG_NL fprintf(stderr, "\n") 46 | #else 47 | #define LOCAL static 48 | #define DEBUG_MSG(...) 49 | #define DEBUG_MSGF(...) 50 | #define DEBUG_BINARY(...) 51 | #define DEBUG_NL 52 | #endif 53 | 54 | #ifdef MMDB_DEBUG 55 | char *byte_to_binary(uint8_t byte) 56 | { 57 | char *bits = malloc(sizeof(char) * 9); 58 | if (NULL == bits) { 59 | return bits; 60 | } 61 | 62 | for (uint8_t i = 0; i < 8; i++) { 63 | bits[i] = byte & (128 >> i) ? '1' : '0'; 64 | } 65 | bits[8] = '\0'; 66 | 67 | return bits; 68 | } 69 | 70 | char *type_num_to_name(uint8_t num) 71 | { 72 | switch (num) { 73 | case 0: 74 | return "extended"; 75 | case 1: 76 | return "pointer"; 77 | case 2: 78 | return "utf8_string"; 79 | case 3: 80 | return "double"; 81 | case 4: 82 | return "bytes"; 83 | case 5: 84 | return "uint16"; 85 | case 6: 86 | return "uint32"; 87 | case 7: 88 | return "map"; 89 | case 8: 90 | return "int32"; 91 | case 9: 92 | return "uint64"; 93 | case 10: 94 | return "uint128"; 95 | case 11: 96 | return "array"; 97 | case 12: 98 | return "container"; 99 | case 13: 100 | return "end_marker"; 101 | case 14: 102 | return "boolean"; 103 | case 15: 104 | return "float"; 105 | default: 106 | return "unknown type"; 107 | } 108 | } 109 | #endif 110 | 111 | /* None of the values we check on the lhs are bigger than uint32_t, so on 112 | * platforms where SIZE_MAX is a 64-bit integer, this would be a no-op, and it 113 | * makes the compiler complain if we do the check anyway. */ 114 | #if SIZE_MAX == UINT32_MAX 115 | #define MAYBE_CHECK_SIZE_OVERFLOW(lhs, rhs, error) \ 116 | if ((lhs) > (rhs)) { \ 117 | return error; \ 118 | } 119 | #else 120 | #define MAYBE_CHECK_SIZE_OVERFLOW(...) 121 | #endif 122 | 123 | typedef struct record_info_s { 124 | uint16_t record_length; 125 | uint32_t (*left_record_getter)(const uint8_t *); 126 | uint32_t (*right_record_getter)(const uint8_t *); 127 | uint8_t right_record_offset; 128 | } record_info_s; 129 | 130 | #define METADATA_MARKER "\xab\xcd\xefMaxMind.com" 131 | /* This is 128kb */ 132 | #define METADATA_BLOCK_MAX_SIZE 131072 133 | 134 | // 64 leads us to allocating 4 KiB on a 64bit system. 135 | #define MMDB_POOL_INIT_SIZE 64 136 | 137 | LOCAL int map_file(MMDB_s *const mmdb); 138 | LOCAL const uint8_t *find_metadata(const uint8_t *file_content, 139 | ssize_t file_size, uint32_t *metadata_size); 140 | LOCAL int read_metadata(MMDB_s *mmdb); 141 | LOCAL MMDB_s make_fake_metadata_db(const MMDB_s *const mmdb); 142 | LOCAL int value_for_key_as_uint16(MMDB_entry_s *start, char *key, 143 | uint16_t *value); 144 | LOCAL int value_for_key_as_uint32(MMDB_entry_s *start, char *key, 145 | uint32_t *value); 146 | LOCAL int value_for_key_as_uint64(MMDB_entry_s *start, char *key, 147 | uint64_t *value); 148 | LOCAL int value_for_key_as_string(MMDB_entry_s *start, char *key, 149 | char const **value); 150 | LOCAL int populate_languages_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, 151 | MMDB_entry_s *metadata_start); 152 | LOCAL int populate_description_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, 153 | MMDB_entry_s *metadata_start); 154 | LOCAL int resolve_any_address(const char *ipstr, struct addrinfo **addresses); 155 | LOCAL int find_address_in_search_tree(const MMDB_s *const mmdb, 156 | uint8_t *address, 157 | sa_family_t address_family, 158 | MMDB_lookup_result_s *result); 159 | LOCAL record_info_s record_info_for_database(const MMDB_s *const mmdb); 160 | LOCAL int find_ipv4_start_node(MMDB_s *const mmdb); 161 | LOCAL uint8_t record_type(const MMDB_s *const mmdb, uint64_t record); 162 | LOCAL uint32_t get_left_28_bit_record(const uint8_t *record); 163 | LOCAL uint32_t get_right_28_bit_record(const uint8_t *record); 164 | LOCAL uint32_t data_section_offset_for_record(const MMDB_s *const mmdb, 165 | uint64_t record); 166 | LOCAL int path_length(va_list va_path); 167 | LOCAL int lookup_path_in_array(const char *path_elem, const MMDB_s *const mmdb, 168 | MMDB_entry_data_s *entry_data); 169 | LOCAL int lookup_path_in_map(const char *path_elem, const MMDB_s *const mmdb, 170 | MMDB_entry_data_s *entry_data); 171 | LOCAL int skip_map_or_array(const MMDB_s *const mmdb, 172 | MMDB_entry_data_s *entry_data); 173 | LOCAL int decode_one_follow(const MMDB_s *const mmdb, uint32_t offset, 174 | MMDB_entry_data_s *entry_data); 175 | LOCAL int decode_one(const MMDB_s *const mmdb, uint32_t offset, 176 | MMDB_entry_data_s *entry_data); 177 | LOCAL int get_ext_type(int raw_ext_type); 178 | LOCAL uint32_t get_ptr_from(uint8_t ctrl, uint8_t const *const ptr, 179 | int ptr_size); 180 | LOCAL int get_entry_data_list(const MMDB_s *const mmdb, 181 | uint32_t offset, 182 | MMDB_entry_data_list_s *const entry_data_list, 183 | MMDB_data_pool_s *const pool, 184 | int depth); 185 | LOCAL float get_ieee754_float(const uint8_t *restrict p); 186 | LOCAL double get_ieee754_double(const uint8_t *restrict p); 187 | LOCAL uint32_t get_uint32(const uint8_t *p); 188 | LOCAL uint32_t get_uint24(const uint8_t *p); 189 | LOCAL uint32_t get_uint16(const uint8_t *p); 190 | LOCAL uint64_t get_uintX(const uint8_t *p, int length); 191 | LOCAL int32_t get_sintX(const uint8_t *p, int length); 192 | LOCAL void free_mmdb_struct(MMDB_s *const mmdb); 193 | LOCAL void free_languages_metadata(MMDB_s *mmdb); 194 | LOCAL void free_descriptions_metadata(MMDB_s *mmdb); 195 | LOCAL MMDB_entry_data_list_s *dump_entry_data_list( 196 | FILE *stream, MMDB_entry_data_list_s *entry_data_list, int indent, 197 | int *status); 198 | LOCAL void print_indentation(FILE *stream, int i); 199 | LOCAL char *bytes_to_hex(uint8_t *bytes, uint32_t size); 200 | 201 | #define CHECKED_DECODE_ONE(mmdb, offset, entry_data) \ 202 | do { \ 203 | int status = decode_one(mmdb, offset, entry_data); \ 204 | if (MMDB_SUCCESS != status) { \ 205 | DEBUG_MSGF("CHECKED_DECODE_ONE failed." \ 206 | " status = %d (%s)", status, MMDB_strerror(status)); \ 207 | return status; \ 208 | } \ 209 | } while (0) 210 | 211 | #define CHECKED_DECODE_ONE_FOLLOW(mmdb, offset, entry_data) \ 212 | do { \ 213 | int status = decode_one_follow(mmdb, offset, entry_data); \ 214 | if (MMDB_SUCCESS != status) { \ 215 | DEBUG_MSGF("CHECKED_DECODE_ONE_FOLLOW failed." \ 216 | " status = %d (%s)", status, MMDB_strerror(status)); \ 217 | return status; \ 218 | } \ 219 | } while (0) 220 | 221 | #define FREE_AND_SET_NULL(p) { free((void *)(p)); (p) = NULL; } 222 | 223 | int MMDB_open(const char *const filename, uint32_t flags, MMDB_s *const mmdb) 224 | { 225 | int status = MMDB_SUCCESS; 226 | 227 | mmdb->file_content = NULL; 228 | mmdb->data_section = NULL; 229 | mmdb->metadata.database_type = NULL; 230 | mmdb->metadata.languages.count = 0; 231 | mmdb->metadata.languages.names = NULL; 232 | mmdb->metadata.description.count = 0; 233 | 234 | mmdb->filename = mmdb_strdup(filename); 235 | if (NULL == mmdb->filename) { 236 | status = MMDB_OUT_OF_MEMORY_ERROR; 237 | goto cleanup; 238 | } 239 | 240 | if ((flags & MMDB_MODE_MASK) == 0) { 241 | flags |= MMDB_MODE_MMAP; 242 | } 243 | mmdb->flags = flags; 244 | 245 | if (MMDB_SUCCESS != (status = map_file(mmdb))) { 246 | goto cleanup; 247 | } 248 | 249 | #ifdef _WIN32 250 | WSADATA wsa; 251 | WSAStartup(MAKEWORD(2, 2), &wsa); 252 | #endif 253 | 254 | uint32_t metadata_size = 0; 255 | const uint8_t *metadata = find_metadata(mmdb->file_content, mmdb->file_size, 256 | &metadata_size); 257 | if (NULL == metadata) { 258 | status = MMDB_INVALID_METADATA_ERROR; 259 | goto cleanup; 260 | } 261 | 262 | mmdb->metadata_section = metadata; 263 | mmdb->metadata_section_size = metadata_size; 264 | 265 | status = read_metadata(mmdb); 266 | if (MMDB_SUCCESS != status) { 267 | goto cleanup; 268 | } 269 | 270 | if (mmdb->metadata.binary_format_major_version != 2) { 271 | status = MMDB_UNKNOWN_DATABASE_FORMAT_ERROR; 272 | goto cleanup; 273 | } 274 | 275 | uint32_t search_tree_size = mmdb->metadata.node_count * 276 | mmdb->full_record_byte_size; 277 | 278 | mmdb->data_section = mmdb->file_content + search_tree_size 279 | + MMDB_DATA_SECTION_SEPARATOR; 280 | if (search_tree_size + MMDB_DATA_SECTION_SEPARATOR > 281 | (uint32_t)mmdb->file_size) { 282 | status = MMDB_INVALID_METADATA_ERROR; 283 | goto cleanup; 284 | } 285 | mmdb->data_section_size = (uint32_t)mmdb->file_size - search_tree_size - 286 | MMDB_DATA_SECTION_SEPARATOR; 287 | 288 | // Although it is likely not possible to construct a database with valid 289 | // valid metadata, as parsed above, and a data_section_size less than 3, 290 | // we do this check as later we assume it is at least three when doing 291 | // bound checks. 292 | if (mmdb->data_section_size < 3) { 293 | status = MMDB_INVALID_DATA_ERROR; 294 | goto cleanup; 295 | } 296 | 297 | mmdb->metadata_section = metadata; 298 | mmdb->ipv4_start_node.node_value = 0; 299 | mmdb->ipv4_start_node.netmask = 0; 300 | 301 | // We do this immediately as otherwise there is a race to set 302 | // ipv4_start_node.node_value and ipv4_start_node.netmask. 303 | if (mmdb->metadata.ip_version == 6) { 304 | status = find_ipv4_start_node(mmdb); 305 | if (status != MMDB_SUCCESS) { 306 | goto cleanup; 307 | } 308 | } 309 | 310 | cleanup: 311 | if (MMDB_SUCCESS != status) { 312 | int saved_errno = errno; 313 | free_mmdb_struct(mmdb); 314 | errno = saved_errno; 315 | } 316 | return status; 317 | } 318 | 319 | #ifdef _WIN32 320 | 321 | LOCAL LPWSTR utf8_to_utf16(const char *utf8_str) 322 | { 323 | int wide_chars = MultiByteToWideChar(CP_UTF8, 0, utf8_str, -1, NULL, 0); 324 | wchar_t *utf16_str = (wchar_t *)malloc(wide_chars * sizeof(wchar_t)); 325 | 326 | if (MultiByteToWideChar(CP_UTF8, 0, utf8_str, -1, utf16_str, 327 | wide_chars) < 1) { 328 | free(utf16_str); 329 | return NULL; 330 | } 331 | 332 | return utf16_str; 333 | } 334 | 335 | LOCAL int map_file(MMDB_s *const mmdb) 336 | { 337 | DWORD size; 338 | int status = MMDB_SUCCESS; 339 | HANDLE mmh = NULL; 340 | HANDLE fd = INVALID_HANDLE_VALUE; 341 | LPWSTR utf16_filename = utf8_to_utf16(mmdb->filename); 342 | if (!utf16_filename) { 343 | status = MMDB_FILE_OPEN_ERROR; 344 | goto cleanup; 345 | } 346 | fd = CreateFile(utf16_filename, GENERIC_READ, FILE_SHARE_READ, NULL, 347 | OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 348 | if (fd == INVALID_HANDLE_VALUE) { 349 | status = MMDB_FILE_OPEN_ERROR; 350 | goto cleanup; 351 | } 352 | size = GetFileSize(fd, NULL); 353 | if (size == INVALID_FILE_SIZE) { 354 | status = MMDB_FILE_OPEN_ERROR; 355 | goto cleanup; 356 | } 357 | mmh = CreateFileMapping(fd, NULL, PAGE_READONLY, 0, size, NULL); 358 | /* Microsoft documentation for CreateFileMapping indicates this returns 359 | NULL not INVALID_HANDLE_VALUE on error */ 360 | if (NULL == mmh) { 361 | status = MMDB_IO_ERROR; 362 | goto cleanup; 363 | } 364 | uint8_t *file_content = 365 | (uint8_t *)MapViewOfFile(mmh, FILE_MAP_READ, 0, 0, 0); 366 | if (file_content == NULL) { 367 | status = MMDB_IO_ERROR; 368 | goto cleanup; 369 | } 370 | 371 | mmdb->file_size = size; 372 | mmdb->file_content = file_content; 373 | 374 | cleanup:; 375 | int saved_errno = errno; 376 | if (INVALID_HANDLE_VALUE != fd) { 377 | CloseHandle(fd); 378 | } 379 | if (NULL != mmh) { 380 | CloseHandle(mmh); 381 | } 382 | errno = saved_errno; 383 | free(utf16_filename); 384 | 385 | return status; 386 | } 387 | 388 | #else // _WIN32 389 | 390 | LOCAL int map_file(MMDB_s *const mmdb) 391 | { 392 | ssize_t size; 393 | int status = MMDB_SUCCESS; 394 | 395 | int flags = O_RDONLY; 396 | #ifdef O_CLOEXEC 397 | flags |= O_CLOEXEC; 398 | #endif 399 | int fd = open(mmdb->filename, flags); 400 | struct stat s; 401 | if (fd < 0 || fstat(fd, &s)) { 402 | status = MMDB_FILE_OPEN_ERROR; 403 | goto cleanup; 404 | } 405 | 406 | size = s.st_size; 407 | if (size < 0 || size != s.st_size) { 408 | status = MMDB_OUT_OF_MEMORY_ERROR; 409 | goto cleanup; 410 | } 411 | 412 | uint8_t *file_content = 413 | (uint8_t *)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); 414 | if (MAP_FAILED == file_content) { 415 | if (ENOMEM == errno) { 416 | status = MMDB_OUT_OF_MEMORY_ERROR; 417 | } else { 418 | status = MMDB_IO_ERROR; 419 | } 420 | goto cleanup; 421 | } 422 | 423 | mmdb->file_size = size; 424 | mmdb->file_content = file_content; 425 | 426 | cleanup:; 427 | int saved_errno = errno; 428 | if (fd >= 0) { 429 | close(fd); 430 | } 431 | errno = saved_errno; 432 | 433 | return status; 434 | } 435 | 436 | #endif // _WIN32 437 | 438 | LOCAL const uint8_t *find_metadata(const uint8_t *file_content, 439 | ssize_t file_size, uint32_t *metadata_size) 440 | { 441 | const ssize_t marker_len = sizeof(METADATA_MARKER) - 1; 442 | ssize_t max_size = file_size > 443 | METADATA_BLOCK_MAX_SIZE ? METADATA_BLOCK_MAX_SIZE : 444 | file_size; 445 | 446 | uint8_t *search_area = (uint8_t *)(file_content + (file_size - max_size)); 447 | uint8_t *start = search_area; 448 | uint8_t *tmp; 449 | do { 450 | tmp = mmdb_memmem(search_area, max_size, 451 | METADATA_MARKER, marker_len); 452 | 453 | if (NULL != tmp) { 454 | max_size -= tmp - search_area; 455 | search_area = tmp; 456 | 457 | /* Continue searching just after the marker we just read, in case 458 | * there are multiple markers in the same file. This would be odd 459 | * but is certainly not impossible. */ 460 | max_size -= marker_len; 461 | search_area += marker_len; 462 | } 463 | } while (NULL != tmp); 464 | 465 | if (search_area == start) { 466 | return NULL; 467 | } 468 | 469 | *metadata_size = (uint32_t)max_size; 470 | 471 | return search_area; 472 | } 473 | 474 | LOCAL int read_metadata(MMDB_s *mmdb) 475 | { 476 | /* We need to create a fake MMDB_s struct in order to decode values from 477 | the metadata. The metadata is basically just like the data section, so we 478 | want to use the same functions we use for the data section to get metadata 479 | values. */ 480 | MMDB_s metadata_db = make_fake_metadata_db(mmdb); 481 | 482 | MMDB_entry_s metadata_start = { 483 | .mmdb = &metadata_db, 484 | .offset = 0 485 | }; 486 | 487 | int status = 488 | value_for_key_as_uint32(&metadata_start, "node_count", 489 | &mmdb->metadata.node_count); 490 | if (MMDB_SUCCESS != status) { 491 | return status; 492 | } 493 | if (!mmdb->metadata.node_count) { 494 | DEBUG_MSG("could not find node_count value in metadata"); 495 | return MMDB_INVALID_METADATA_ERROR; 496 | } 497 | 498 | status = value_for_key_as_uint16(&metadata_start, "record_size", 499 | &mmdb->metadata.record_size); 500 | if (MMDB_SUCCESS != status) { 501 | return status; 502 | } 503 | if (!mmdb->metadata.record_size) { 504 | DEBUG_MSG("could not find record_size value in metadata"); 505 | return MMDB_INVALID_METADATA_ERROR; 506 | } 507 | 508 | if (mmdb->metadata.record_size != 24 && mmdb->metadata.record_size != 28 509 | && mmdb->metadata.record_size != 32) { 510 | DEBUG_MSGF("bad record size in metadata: %i", 511 | mmdb->metadata.record_size); 512 | return MMDB_UNKNOWN_DATABASE_FORMAT_ERROR; 513 | } 514 | 515 | status = value_for_key_as_uint16(&metadata_start, "ip_version", 516 | &mmdb->metadata.ip_version); 517 | if (MMDB_SUCCESS != status) { 518 | return status; 519 | } 520 | if (!mmdb->metadata.ip_version) { 521 | DEBUG_MSG("could not find ip_version value in metadata"); 522 | return MMDB_INVALID_METADATA_ERROR; 523 | } 524 | if (!(mmdb->metadata.ip_version == 4 || mmdb->metadata.ip_version == 6)) { 525 | DEBUG_MSGF("ip_version value in metadata is not 4 or 6 - it was %i", 526 | mmdb->metadata.ip_version); 527 | return MMDB_INVALID_METADATA_ERROR; 528 | } 529 | 530 | status = value_for_key_as_string(&metadata_start, "database_type", 531 | &mmdb->metadata.database_type); 532 | if (MMDB_SUCCESS != status) { 533 | DEBUG_MSG("error finding database_type value in metadata"); 534 | return status; 535 | } 536 | 537 | status = 538 | populate_languages_metadata(mmdb, &metadata_db, &metadata_start); 539 | if (MMDB_SUCCESS != status) { 540 | DEBUG_MSG("could not populate languages from metadata"); 541 | return status; 542 | } 543 | 544 | status = value_for_key_as_uint16( 545 | &metadata_start, "binary_format_major_version", 546 | &mmdb->metadata.binary_format_major_version); 547 | if (MMDB_SUCCESS != status) { 548 | return status; 549 | } 550 | if (!mmdb->metadata.binary_format_major_version) { 551 | DEBUG_MSG( 552 | "could not find binary_format_major_version value in metadata"); 553 | return MMDB_INVALID_METADATA_ERROR; 554 | } 555 | 556 | status = value_for_key_as_uint16( 557 | &metadata_start, "binary_format_minor_version", 558 | &mmdb->metadata.binary_format_minor_version); 559 | if (MMDB_SUCCESS != status) { 560 | return status; 561 | } 562 | 563 | status = value_for_key_as_uint64(&metadata_start, "build_epoch", 564 | &mmdb->metadata.build_epoch); 565 | if (MMDB_SUCCESS != status) { 566 | return status; 567 | } 568 | if (!mmdb->metadata.build_epoch) { 569 | DEBUG_MSG("could not find build_epoch value in metadata"); 570 | return MMDB_INVALID_METADATA_ERROR; 571 | } 572 | 573 | status = populate_description_metadata(mmdb, &metadata_db, &metadata_start); 574 | if (MMDB_SUCCESS != status) { 575 | DEBUG_MSG("could not populate description from metadata"); 576 | return status; 577 | } 578 | 579 | mmdb->full_record_byte_size = mmdb->metadata.record_size * 2 / 8U; 580 | 581 | mmdb->depth = mmdb->metadata.ip_version == 4 ? 32 : 128; 582 | 583 | return MMDB_SUCCESS; 584 | } 585 | 586 | LOCAL MMDB_s make_fake_metadata_db(const MMDB_s *const mmdb) 587 | { 588 | MMDB_s fake_metadata_db = { 589 | .data_section = mmdb->metadata_section, 590 | .data_section_size = mmdb->metadata_section_size 591 | }; 592 | 593 | return fake_metadata_db; 594 | } 595 | 596 | LOCAL int value_for_key_as_uint16(MMDB_entry_s *start, char *key, 597 | uint16_t *value) 598 | { 599 | MMDB_entry_data_s entry_data; 600 | const char *path[] = { key, NULL }; 601 | int status = MMDB_aget_value(start, &entry_data, path); 602 | if (MMDB_SUCCESS != status) { 603 | return status; 604 | } 605 | if (MMDB_DATA_TYPE_UINT16 != entry_data.type) { 606 | DEBUG_MSGF("expect uint16 for %s but received %s", key, 607 | type_num_to_name( 608 | entry_data.type)); 609 | return MMDB_INVALID_METADATA_ERROR; 610 | } 611 | *value = entry_data.uint16; 612 | return MMDB_SUCCESS; 613 | } 614 | 615 | LOCAL int value_for_key_as_uint32(MMDB_entry_s *start, char *key, 616 | uint32_t *value) 617 | { 618 | MMDB_entry_data_s entry_data; 619 | const char *path[] = { key, NULL }; 620 | int status = MMDB_aget_value(start, &entry_data, path); 621 | if (MMDB_SUCCESS != status) { 622 | return status; 623 | } 624 | if (MMDB_DATA_TYPE_UINT32 != entry_data.type) { 625 | DEBUG_MSGF("expect uint32 for %s but received %s", key, 626 | type_num_to_name( 627 | entry_data.type)); 628 | return MMDB_INVALID_METADATA_ERROR; 629 | } 630 | *value = entry_data.uint32; 631 | return MMDB_SUCCESS; 632 | } 633 | 634 | LOCAL int value_for_key_as_uint64(MMDB_entry_s *start, char *key, 635 | uint64_t *value) 636 | { 637 | MMDB_entry_data_s entry_data; 638 | const char *path[] = { key, NULL }; 639 | int status = MMDB_aget_value(start, &entry_data, path); 640 | if (MMDB_SUCCESS != status) { 641 | return status; 642 | } 643 | if (MMDB_DATA_TYPE_UINT64 != entry_data.type) { 644 | DEBUG_MSGF("expect uint64 for %s but received %s", key, 645 | type_num_to_name( 646 | entry_data.type)); 647 | return MMDB_INVALID_METADATA_ERROR; 648 | } 649 | *value = entry_data.uint64; 650 | return MMDB_SUCCESS; 651 | } 652 | 653 | LOCAL int value_for_key_as_string(MMDB_entry_s *start, char *key, 654 | char const **value) 655 | { 656 | MMDB_entry_data_s entry_data; 657 | const char *path[] = { key, NULL }; 658 | int status = MMDB_aget_value(start, &entry_data, path); 659 | if (MMDB_SUCCESS != status) { 660 | return status; 661 | } 662 | if (MMDB_DATA_TYPE_UTF8_STRING != entry_data.type) { 663 | DEBUG_MSGF("expect string for %s but received %s", key, 664 | type_num_to_name( 665 | entry_data.type)); 666 | return MMDB_INVALID_METADATA_ERROR; 667 | } 668 | *value = mmdb_strndup((char *)entry_data.utf8_string, entry_data.data_size); 669 | if (NULL == *value) { 670 | return MMDB_OUT_OF_MEMORY_ERROR; 671 | } 672 | return MMDB_SUCCESS; 673 | } 674 | 675 | LOCAL int populate_languages_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, 676 | MMDB_entry_s *metadata_start) 677 | { 678 | MMDB_entry_data_s entry_data; 679 | 680 | const char *path[] = { "languages", NULL }; 681 | int status = MMDB_aget_value(metadata_start, &entry_data, path); 682 | if (MMDB_SUCCESS != status) { 683 | return status; 684 | } 685 | if (MMDB_DATA_TYPE_ARRAY != entry_data.type) { 686 | return MMDB_INVALID_METADATA_ERROR; 687 | } 688 | 689 | MMDB_entry_s array_start = { 690 | .mmdb = metadata_db, 691 | .offset = entry_data.offset 692 | }; 693 | 694 | MMDB_entry_data_list_s *member; 695 | status = MMDB_get_entry_data_list(&array_start, &member); 696 | if (MMDB_SUCCESS != status) { 697 | return status; 698 | } 699 | 700 | MMDB_entry_data_list_s *first_member = member; 701 | 702 | uint32_t array_size = member->entry_data.data_size; 703 | MAYBE_CHECK_SIZE_OVERFLOW(array_size, SIZE_MAX / sizeof(char *), 704 | MMDB_INVALID_METADATA_ERROR); 705 | 706 | mmdb->metadata.languages.count = 0; 707 | mmdb->metadata.languages.names = malloc(array_size * sizeof(char *)); 708 | if (NULL == mmdb->metadata.languages.names) { 709 | return MMDB_OUT_OF_MEMORY_ERROR; 710 | } 711 | 712 | for (uint32_t i = 0; i < array_size; i++) { 713 | member = member->next; 714 | if (MMDB_DATA_TYPE_UTF8_STRING != member->entry_data.type) { 715 | return MMDB_INVALID_METADATA_ERROR; 716 | } 717 | 718 | mmdb->metadata.languages.names[i] = 719 | mmdb_strndup((char *)member->entry_data.utf8_string, 720 | member->entry_data.data_size); 721 | 722 | if (NULL == mmdb->metadata.languages.names[i]) { 723 | return MMDB_OUT_OF_MEMORY_ERROR; 724 | } 725 | // We assign this as we go so that if we fail a malloc and need to 726 | // free it, the count is right. 727 | mmdb->metadata.languages.count = i + 1; 728 | } 729 | 730 | MMDB_free_entry_data_list(first_member); 731 | 732 | return MMDB_SUCCESS; 733 | } 734 | 735 | LOCAL int populate_description_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, 736 | MMDB_entry_s *metadata_start) 737 | { 738 | MMDB_entry_data_s entry_data; 739 | 740 | const char *path[] = { "description", NULL }; 741 | int status = MMDB_aget_value(metadata_start, &entry_data, path); 742 | if (MMDB_SUCCESS != status) { 743 | return status; 744 | } 745 | 746 | if (MMDB_DATA_TYPE_MAP != entry_data.type) { 747 | DEBUG_MSGF("Unexpected entry_data type: %d", entry_data.type); 748 | return MMDB_INVALID_METADATA_ERROR; 749 | } 750 | 751 | MMDB_entry_s map_start = { 752 | .mmdb = metadata_db, 753 | .offset = entry_data.offset 754 | }; 755 | 756 | MMDB_entry_data_list_s *member; 757 | status = MMDB_get_entry_data_list(&map_start, &member); 758 | if (MMDB_SUCCESS != status) { 759 | DEBUG_MSGF( 760 | "MMDB_get_entry_data_list failed while populating description." 761 | " status = %d (%s)", status, MMDB_strerror(status)); 762 | return status; 763 | } 764 | 765 | MMDB_entry_data_list_s *first_member = member; 766 | 767 | uint32_t map_size = member->entry_data.data_size; 768 | mmdb->metadata.description.count = 0; 769 | if (0 == map_size) { 770 | mmdb->metadata.description.descriptions = NULL; 771 | goto cleanup; 772 | } 773 | MAYBE_CHECK_SIZE_OVERFLOW(map_size, SIZE_MAX / sizeof(MMDB_description_s *), 774 | MMDB_INVALID_METADATA_ERROR); 775 | 776 | mmdb->metadata.description.descriptions = 777 | malloc(map_size * sizeof(MMDB_description_s *)); 778 | if (NULL == mmdb->metadata.description.descriptions) { 779 | status = MMDB_OUT_OF_MEMORY_ERROR; 780 | goto cleanup; 781 | } 782 | 783 | for (uint32_t i = 0; i < map_size; i++) { 784 | mmdb->metadata.description.descriptions[i] = 785 | malloc(sizeof(MMDB_description_s)); 786 | if (NULL == mmdb->metadata.description.descriptions[i]) { 787 | status = MMDB_OUT_OF_MEMORY_ERROR; 788 | goto cleanup; 789 | } 790 | 791 | mmdb->metadata.description.count = i + 1; 792 | mmdb->metadata.description.descriptions[i]->language = NULL; 793 | mmdb->metadata.description.descriptions[i]->description = NULL; 794 | 795 | member = member->next; 796 | 797 | if (MMDB_DATA_TYPE_UTF8_STRING != member->entry_data.type) { 798 | status = MMDB_INVALID_METADATA_ERROR; 799 | goto cleanup; 800 | } 801 | 802 | mmdb->metadata.description.descriptions[i]->language = 803 | mmdb_strndup((char *)member->entry_data.utf8_string, 804 | member->entry_data.data_size); 805 | 806 | if (NULL == mmdb->metadata.description.descriptions[i]->language) { 807 | status = MMDB_OUT_OF_MEMORY_ERROR; 808 | goto cleanup; 809 | } 810 | 811 | member = member->next; 812 | 813 | if (MMDB_DATA_TYPE_UTF8_STRING != member->entry_data.type) { 814 | status = MMDB_INVALID_METADATA_ERROR; 815 | goto cleanup; 816 | } 817 | 818 | mmdb->metadata.description.descriptions[i]->description = 819 | mmdb_strndup((char *)member->entry_data.utf8_string, 820 | member->entry_data.data_size); 821 | 822 | if (NULL == mmdb->metadata.description.descriptions[i]->description) { 823 | status = MMDB_OUT_OF_MEMORY_ERROR; 824 | goto cleanup; 825 | } 826 | } 827 | 828 | cleanup: 829 | MMDB_free_entry_data_list(first_member); 830 | 831 | return status; 832 | } 833 | 834 | MMDB_lookup_result_s MMDB_lookup_string(const MMDB_s *const mmdb, 835 | const char *const ipstr, 836 | int *const gai_error, 837 | int *const mmdb_error) 838 | { 839 | MMDB_lookup_result_s result = { 840 | .found_entry = false, 841 | .netmask = 0, 842 | .entry = { 843 | .mmdb = mmdb, 844 | .offset = 0 845 | } 846 | }; 847 | 848 | struct addrinfo *addresses = NULL; 849 | *gai_error = resolve_any_address(ipstr, &addresses); 850 | 851 | if (!*gai_error) { 852 | result = MMDB_lookup_sockaddr(mmdb, addresses->ai_addr, mmdb_error); 853 | } 854 | 855 | if (NULL != addresses) { 856 | freeaddrinfo(addresses); 857 | } 858 | 859 | return result; 860 | } 861 | 862 | LOCAL int resolve_any_address(const char *ipstr, struct addrinfo **addresses) 863 | { 864 | struct addrinfo hints = { 865 | .ai_family = AF_UNSPEC, 866 | .ai_flags = AI_NUMERICHOST, 867 | // We set ai_socktype so that we only get one result back 868 | .ai_socktype = SOCK_STREAM 869 | }; 870 | 871 | int gai_status = getaddrinfo(ipstr, NULL, &hints, addresses); 872 | if (gai_status) { 873 | return gai_status; 874 | } 875 | 876 | return 0; 877 | } 878 | 879 | MMDB_lookup_result_s MMDB_lookup_sockaddr( 880 | const MMDB_s *const mmdb, 881 | const struct sockaddr *const sockaddr, 882 | int *const mmdb_error) 883 | { 884 | MMDB_lookup_result_s result = { 885 | .found_entry = false, 886 | .netmask = 0, 887 | .entry = { 888 | .mmdb = mmdb, 889 | .offset = 0 890 | } 891 | }; 892 | 893 | uint8_t mapped_address[16], *address; 894 | if (mmdb->metadata.ip_version == 4) { 895 | if (sockaddr->sa_family == AF_INET6) { 896 | *mmdb_error = MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR; 897 | return result; 898 | } 899 | address = (uint8_t *)&((struct sockaddr_in *)sockaddr)->sin_addr.s_addr; 900 | } else { 901 | if (sockaddr->sa_family == AF_INET6) { 902 | address = 903 | (uint8_t *)&((struct sockaddr_in6 *)sockaddr)->sin6_addr. 904 | s6_addr; 905 | } else { 906 | address = mapped_address; 907 | memset(address, 0, 12); 908 | memcpy(address + 12, 909 | &((struct sockaddr_in *)sockaddr)->sin_addr.s_addr, 4); 910 | } 911 | } 912 | 913 | *mmdb_error = 914 | find_address_in_search_tree(mmdb, address, sockaddr->sa_family, 915 | &result); 916 | 917 | return result; 918 | } 919 | 920 | LOCAL int find_address_in_search_tree(const MMDB_s *const mmdb, 921 | uint8_t *address, 922 | sa_family_t address_family, 923 | MMDB_lookup_result_s *result) 924 | { 925 | record_info_s record_info = record_info_for_database(mmdb); 926 | if (0 == record_info.right_record_offset) { 927 | return MMDB_UNKNOWN_DATABASE_FORMAT_ERROR; 928 | } 929 | 930 | uint32_t value = 0; 931 | uint16_t current_bit = 0; 932 | if (mmdb->metadata.ip_version == 6 && address_family == AF_INET) { 933 | value = mmdb->ipv4_start_node.node_value; 934 | current_bit = mmdb->ipv4_start_node.netmask; 935 | } 936 | 937 | uint32_t node_count = mmdb->metadata.node_count; 938 | const uint8_t *search_tree = mmdb->file_content; 939 | const uint8_t *record_pointer; 940 | for (; current_bit < mmdb->depth && value < node_count; current_bit++) { 941 | uint8_t bit = 1U & 942 | (address[current_bit >> 3] >> (7 - (current_bit % 8))); 943 | 944 | record_pointer = &search_tree[value * record_info.record_length]; 945 | if (record_pointer + record_info.record_length > mmdb->data_section) { 946 | return MMDB_CORRUPT_SEARCH_TREE_ERROR; 947 | } 948 | if (bit) { 949 | record_pointer += record_info.right_record_offset; 950 | value = record_info.right_record_getter(record_pointer); 951 | } else { 952 | value = record_info.left_record_getter(record_pointer); 953 | } 954 | } 955 | 956 | result->netmask = current_bit; 957 | 958 | if (value >= node_count + mmdb->data_section_size) { 959 | // The pointer points off the end of the database. 960 | return MMDB_CORRUPT_SEARCH_TREE_ERROR; 961 | } 962 | 963 | if (value == node_count) { 964 | // record is empty 965 | result->found_entry = false; 966 | return MMDB_SUCCESS; 967 | } 968 | result->found_entry = true; 969 | result->entry.offset = data_section_offset_for_record(mmdb, value); 970 | 971 | return MMDB_SUCCESS; 972 | } 973 | 974 | LOCAL record_info_s record_info_for_database(const MMDB_s *const mmdb) 975 | { 976 | record_info_s record_info = { 977 | .record_length = mmdb->full_record_byte_size, 978 | .right_record_offset = 0 979 | }; 980 | 981 | if (record_info.record_length == 6) { 982 | record_info.left_record_getter = &get_uint24; 983 | record_info.right_record_getter = &get_uint24; 984 | record_info.right_record_offset = 3; 985 | } else if (record_info.record_length == 7) { 986 | record_info.left_record_getter = &get_left_28_bit_record; 987 | record_info.right_record_getter = &get_right_28_bit_record; 988 | record_info.right_record_offset = 3; 989 | } else if (record_info.record_length == 8) { 990 | record_info.left_record_getter = &get_uint32; 991 | record_info.right_record_getter = &get_uint32; 992 | record_info.right_record_offset = 4; 993 | } else { 994 | assert(false); 995 | } 996 | 997 | return record_info; 998 | } 999 | 1000 | LOCAL int find_ipv4_start_node(MMDB_s *const mmdb) 1001 | { 1002 | /* In a pathological case of a database with a single node search tree, 1003 | * this check will be true even after we've found the IPv4 start node, but 1004 | * that doesn't seem worth trying to fix. */ 1005 | if (mmdb->ipv4_start_node.node_value != 0) { 1006 | return MMDB_SUCCESS; 1007 | } 1008 | 1009 | record_info_s record_info = record_info_for_database(mmdb); 1010 | 1011 | const uint8_t *search_tree = mmdb->file_content; 1012 | uint32_t node_value = 0; 1013 | const uint8_t *record_pointer; 1014 | uint16_t netmask; 1015 | uint32_t node_count = mmdb->metadata.node_count; 1016 | 1017 | for (netmask = 0; netmask < 96 && node_value < node_count; netmask++) { 1018 | record_pointer = &search_tree[node_value * record_info.record_length]; 1019 | if (record_pointer + record_info.record_length > mmdb->data_section) { 1020 | return MMDB_CORRUPT_SEARCH_TREE_ERROR; 1021 | } 1022 | node_value = record_info.left_record_getter(record_pointer); 1023 | } 1024 | 1025 | mmdb->ipv4_start_node.node_value = node_value; 1026 | mmdb->ipv4_start_node.netmask = netmask; 1027 | 1028 | return MMDB_SUCCESS; 1029 | } 1030 | 1031 | LOCAL uint8_t record_type(const MMDB_s *const mmdb, uint64_t record) 1032 | { 1033 | uint32_t node_count = mmdb->metadata.node_count; 1034 | 1035 | /* Ideally we'd check to make sure that a record never points to a 1036 | * previously seen value, but that's more complicated. For now, we can 1037 | * at least check that we don't end up at the top of the tree again. */ 1038 | if (record == 0) { 1039 | DEBUG_MSG("record has a value of 0"); 1040 | return MMDB_RECORD_TYPE_INVALID; 1041 | } 1042 | 1043 | if (record < node_count) { 1044 | return MMDB_RECORD_TYPE_SEARCH_NODE; 1045 | } 1046 | 1047 | if (record == node_count) { 1048 | return MMDB_RECORD_TYPE_EMPTY; 1049 | } 1050 | 1051 | if (record - node_count < mmdb->data_section_size) { 1052 | return MMDB_RECORD_TYPE_DATA; 1053 | } 1054 | 1055 | DEBUG_MSG("record has a value that points outside of the database"); 1056 | return MMDB_RECORD_TYPE_INVALID; 1057 | } 1058 | 1059 | LOCAL uint32_t get_left_28_bit_record(const uint8_t *record) 1060 | { 1061 | return record[0] * 65536 + record[1] * 256 + record[2] + 1062 | ((record[3] & 0xf0) << 20); 1063 | } 1064 | 1065 | LOCAL uint32_t get_right_28_bit_record(const uint8_t *record) 1066 | { 1067 | uint32_t value = get_uint32(record); 1068 | return value & 0xfffffff; 1069 | } 1070 | 1071 | int MMDB_read_node(const MMDB_s *const mmdb, uint32_t node_number, 1072 | MMDB_search_node_s *const node) 1073 | { 1074 | record_info_s record_info = record_info_for_database(mmdb); 1075 | if (0 == record_info.right_record_offset) { 1076 | return MMDB_UNKNOWN_DATABASE_FORMAT_ERROR; 1077 | } 1078 | 1079 | if (node_number > mmdb->metadata.node_count) { 1080 | return MMDB_INVALID_NODE_NUMBER_ERROR; 1081 | } 1082 | 1083 | const uint8_t *search_tree = mmdb->file_content; 1084 | const uint8_t *record_pointer = 1085 | &search_tree[node_number * record_info.record_length]; 1086 | node->left_record = record_info.left_record_getter(record_pointer); 1087 | record_pointer += record_info.right_record_offset; 1088 | node->right_record = record_info.right_record_getter(record_pointer); 1089 | 1090 | node->left_record_type = record_type(mmdb, node->left_record); 1091 | node->right_record_type = record_type(mmdb, node->right_record); 1092 | 1093 | // Note that offset will be invalid if the record type is not 1094 | // MMDB_RECORD_TYPE_DATA, but that's ok. Any use of the record entry 1095 | // for other data types is a programming error. 1096 | node->left_record_entry = (struct MMDB_entry_s) { 1097 | .mmdb = mmdb, 1098 | .offset = data_section_offset_for_record(mmdb, node->left_record), 1099 | }; 1100 | node->right_record_entry = (struct MMDB_entry_s) { 1101 | .mmdb = mmdb, 1102 | .offset = data_section_offset_for_record(mmdb, node->right_record), 1103 | }; 1104 | 1105 | return MMDB_SUCCESS; 1106 | } 1107 | 1108 | LOCAL uint32_t data_section_offset_for_record(const MMDB_s *const mmdb, 1109 | uint64_t record) 1110 | { 1111 | return (uint32_t)record - mmdb->metadata.node_count - 1112 | MMDB_DATA_SECTION_SEPARATOR; 1113 | } 1114 | 1115 | int MMDB_get_value(MMDB_entry_s *const start, 1116 | MMDB_entry_data_s *const entry_data, 1117 | ...) 1118 | { 1119 | va_list path; 1120 | va_start(path, entry_data); 1121 | int status = MMDB_vget_value(start, entry_data, path); 1122 | va_end(path); 1123 | return status; 1124 | } 1125 | 1126 | int MMDB_vget_value(MMDB_entry_s *const start, 1127 | MMDB_entry_data_s *const entry_data, 1128 | va_list va_path) 1129 | { 1130 | int length = path_length(va_path); 1131 | const char *path_elem; 1132 | int i = 0; 1133 | 1134 | MAYBE_CHECK_SIZE_OVERFLOW(length, SIZE_MAX / sizeof(const char *) - 1, 1135 | MMDB_INVALID_METADATA_ERROR); 1136 | 1137 | const char **path = malloc((length + 1) * sizeof(const char *)); 1138 | if (NULL == path) { 1139 | return MMDB_OUT_OF_MEMORY_ERROR; 1140 | } 1141 | 1142 | while (NULL != (path_elem = va_arg(va_path, char *))) { 1143 | path[i] = path_elem; 1144 | i++; 1145 | } 1146 | path[i] = NULL; 1147 | 1148 | int status = MMDB_aget_value(start, entry_data, path); 1149 | 1150 | free((char **)path); 1151 | 1152 | return status; 1153 | } 1154 | 1155 | LOCAL int path_length(va_list va_path) 1156 | { 1157 | int i = 0; 1158 | const char *ignore; 1159 | va_list path_copy; 1160 | va_copy(path_copy, va_path); 1161 | 1162 | while (NULL != (ignore = va_arg(path_copy, char *))) { 1163 | i++; 1164 | } 1165 | 1166 | va_end(path_copy); 1167 | 1168 | return i; 1169 | } 1170 | 1171 | int MMDB_aget_value(MMDB_entry_s *const start, 1172 | MMDB_entry_data_s *const entry_data, 1173 | const char *const *const path) 1174 | { 1175 | const MMDB_s *const mmdb = start->mmdb; 1176 | uint32_t offset = start->offset; 1177 | 1178 | memset(entry_data, 0, sizeof(MMDB_entry_data_s)); 1179 | DEBUG_NL; 1180 | DEBUG_MSG("looking up value by path"); 1181 | 1182 | CHECKED_DECODE_ONE_FOLLOW(mmdb, offset, entry_data); 1183 | 1184 | DEBUG_NL; 1185 | DEBUG_MSGF("top level element is a %s", type_num_to_name(entry_data->type)); 1186 | 1187 | /* Can this happen? It'd probably represent a pathological case under 1188 | * normal use, but there's nothing preventing someone from passing an 1189 | * invalid MMDB_entry_s struct to this function */ 1190 | if (!entry_data->has_data) { 1191 | return MMDB_INVALID_LOOKUP_PATH_ERROR; 1192 | } 1193 | 1194 | const char *path_elem; 1195 | int i = 0; 1196 | while (NULL != (path_elem = path[i++])) { 1197 | DEBUG_NL; 1198 | DEBUG_MSGF("path elem = %s", path_elem); 1199 | 1200 | /* XXX - it'd be good to find a quicker way to skip through these 1201 | entries that doesn't involve decoding them 1202 | completely. Basically we need to just use the size from the 1203 | control byte to advance our pointer rather than calling 1204 | decode_one(). */ 1205 | if (entry_data->type == MMDB_DATA_TYPE_ARRAY) { 1206 | int status = lookup_path_in_array(path_elem, mmdb, entry_data); 1207 | if (MMDB_SUCCESS != status) { 1208 | memset(entry_data, 0, sizeof(MMDB_entry_data_s)); 1209 | return status; 1210 | } 1211 | } else if (entry_data->type == MMDB_DATA_TYPE_MAP) { 1212 | int status = lookup_path_in_map(path_elem, mmdb, entry_data); 1213 | if (MMDB_SUCCESS != status) { 1214 | memset(entry_data, 0, sizeof(MMDB_entry_data_s)); 1215 | return status; 1216 | } 1217 | } else { 1218 | /* Once we make the code traverse maps & arrays without calling 1219 | * decode_one() we can get rid of this. */ 1220 | memset(entry_data, 0, sizeof(MMDB_entry_data_s)); 1221 | return MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR; 1222 | } 1223 | } 1224 | 1225 | return MMDB_SUCCESS; 1226 | } 1227 | 1228 | LOCAL int lookup_path_in_array(const char *path_elem, 1229 | const MMDB_s *const mmdb, 1230 | MMDB_entry_data_s *entry_data) 1231 | { 1232 | uint32_t size = entry_data->data_size; 1233 | char *first_invalid; 1234 | 1235 | int saved_errno = errno; 1236 | errno = 0; 1237 | int array_index = strtol(path_elem, &first_invalid, 10); 1238 | if (ERANGE == errno) { 1239 | errno = saved_errno; 1240 | return MMDB_INVALID_LOOKUP_PATH_ERROR; 1241 | } 1242 | errno = saved_errno; 1243 | 1244 | if (array_index < 0) { 1245 | array_index += size; 1246 | 1247 | if (array_index < 0) { 1248 | return MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR; 1249 | } 1250 | } 1251 | 1252 | if (*first_invalid || (uint32_t)array_index >= size) { 1253 | return MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR; 1254 | } 1255 | 1256 | for (int i = 0; i < array_index; i++) { 1257 | /* We don't want to follow a pointer here. If the next element is a 1258 | * pointer we simply skip it and keep going */ 1259 | CHECKED_DECODE_ONE(mmdb, entry_data->offset_to_next, entry_data); 1260 | int status = skip_map_or_array(mmdb, entry_data); 1261 | if (MMDB_SUCCESS != status) { 1262 | return status; 1263 | } 1264 | } 1265 | 1266 | MMDB_entry_data_s value; 1267 | CHECKED_DECODE_ONE_FOLLOW(mmdb, entry_data->offset_to_next, &value); 1268 | memcpy(entry_data, &value, sizeof(MMDB_entry_data_s)); 1269 | 1270 | return MMDB_SUCCESS; 1271 | } 1272 | 1273 | LOCAL int lookup_path_in_map(const char *path_elem, 1274 | const MMDB_s *const mmdb, 1275 | MMDB_entry_data_s *entry_data) 1276 | { 1277 | uint32_t size = entry_data->data_size; 1278 | uint32_t offset = entry_data->offset_to_next; 1279 | size_t path_elem_len = strlen(path_elem); 1280 | 1281 | while (size-- > 0) { 1282 | MMDB_entry_data_s key, value; 1283 | CHECKED_DECODE_ONE_FOLLOW(mmdb, offset, &key); 1284 | 1285 | uint32_t offset_to_value = key.offset_to_next; 1286 | 1287 | if (MMDB_DATA_TYPE_UTF8_STRING != key.type) { 1288 | return MMDB_INVALID_DATA_ERROR; 1289 | } 1290 | 1291 | if (key.data_size == path_elem_len && 1292 | !memcmp(path_elem, key.utf8_string, path_elem_len)) { 1293 | 1294 | DEBUG_MSG("found key matching path elem"); 1295 | 1296 | CHECKED_DECODE_ONE_FOLLOW(mmdb, offset_to_value, &value); 1297 | memcpy(entry_data, &value, sizeof(MMDB_entry_data_s)); 1298 | return MMDB_SUCCESS; 1299 | } else { 1300 | /* We don't want to follow a pointer here. If the next element is 1301 | * a pointer we simply skip it and keep going */ 1302 | CHECKED_DECODE_ONE(mmdb, offset_to_value, &value); 1303 | int status = skip_map_or_array(mmdb, &value); 1304 | if (MMDB_SUCCESS != status) { 1305 | return status; 1306 | } 1307 | offset = value.offset_to_next; 1308 | } 1309 | } 1310 | 1311 | memset(entry_data, 0, sizeof(MMDB_entry_data_s)); 1312 | return MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR; 1313 | } 1314 | 1315 | LOCAL int skip_map_or_array(const MMDB_s *const mmdb, 1316 | MMDB_entry_data_s *entry_data) 1317 | { 1318 | if (entry_data->type == MMDB_DATA_TYPE_MAP) { 1319 | uint32_t size = entry_data->data_size; 1320 | while (size-- > 0) { 1321 | CHECKED_DECODE_ONE(mmdb, entry_data->offset_to_next, entry_data); // key 1322 | CHECKED_DECODE_ONE(mmdb, entry_data->offset_to_next, entry_data); // value 1323 | int status = skip_map_or_array(mmdb, entry_data); 1324 | if (MMDB_SUCCESS != status) { 1325 | return status; 1326 | } 1327 | } 1328 | } else if (entry_data->type == MMDB_DATA_TYPE_ARRAY) { 1329 | uint32_t size = entry_data->data_size; 1330 | while (size-- > 0) { 1331 | CHECKED_DECODE_ONE(mmdb, entry_data->offset_to_next, entry_data); // value 1332 | int status = skip_map_or_array(mmdb, entry_data); 1333 | if (MMDB_SUCCESS != status) { 1334 | return status; 1335 | } 1336 | } 1337 | } 1338 | 1339 | return MMDB_SUCCESS; 1340 | } 1341 | 1342 | LOCAL int decode_one_follow(const MMDB_s *const mmdb, uint32_t offset, 1343 | MMDB_entry_data_s *entry_data) 1344 | { 1345 | CHECKED_DECODE_ONE(mmdb, offset, entry_data); 1346 | if (entry_data->type == MMDB_DATA_TYPE_POINTER) { 1347 | uint32_t next = entry_data->offset_to_next; 1348 | CHECKED_DECODE_ONE(mmdb, entry_data->pointer, entry_data); 1349 | /* Pointers to pointers are illegal under the spec */ 1350 | if (entry_data->type == MMDB_DATA_TYPE_POINTER) { 1351 | DEBUG_MSG("pointer points to another pointer"); 1352 | return MMDB_INVALID_DATA_ERROR; 1353 | } 1354 | 1355 | /* The pointer could point to any part of the data section but the 1356 | * next entry for this particular offset may be the one after the 1357 | * pointer, not the one after whatever the pointer points to. This 1358 | * depends on whether the pointer points to something that is a simple 1359 | * value or a compound value. For a compound value, the next one is 1360 | * the one after the pointer result, not the one after the pointer. */ 1361 | if (entry_data->type != MMDB_DATA_TYPE_MAP 1362 | && entry_data->type != MMDB_DATA_TYPE_ARRAY) { 1363 | 1364 | entry_data->offset_to_next = next; 1365 | } 1366 | } 1367 | 1368 | return MMDB_SUCCESS; 1369 | } 1370 | 1371 | #if !MMDB_UINT128_IS_BYTE_ARRAY 1372 | LOCAL mmdb_uint128_t get_uint128(const uint8_t *p, int length) 1373 | { 1374 | mmdb_uint128_t value = 0; 1375 | while (length-- > 0) { 1376 | value <<= 8; 1377 | value += *p++; 1378 | } 1379 | return value; 1380 | } 1381 | #endif 1382 | 1383 | LOCAL int decode_one(const MMDB_s *const mmdb, uint32_t offset, 1384 | MMDB_entry_data_s *entry_data) 1385 | { 1386 | const uint8_t *mem = mmdb->data_section; 1387 | 1388 | // We subtract rather than add as it possible that offset + 1 1389 | // could overflow for a corrupt database while an underflow 1390 | // from data_section_size - 1 should not be possible. 1391 | if (offset > mmdb->data_section_size - 1) { 1392 | DEBUG_MSGF("Offset (%d) past data section (%d)", offset, 1393 | mmdb->data_section_size); 1394 | return MMDB_INVALID_DATA_ERROR; 1395 | } 1396 | 1397 | entry_data->offset = offset; 1398 | entry_data->has_data = true; 1399 | 1400 | DEBUG_NL; 1401 | DEBUG_MSGF("Offset: %i", offset); 1402 | 1403 | uint8_t ctrl = mem[offset++]; 1404 | DEBUG_BINARY("Control byte: %s", ctrl); 1405 | 1406 | int type = (ctrl >> 5) & 7; 1407 | DEBUG_MSGF("Type: %i (%s)", type, type_num_to_name(type)); 1408 | 1409 | if (type == MMDB_DATA_TYPE_EXTENDED) { 1410 | // Subtracting 1 to avoid possible overflow on offset + 1 1411 | if (offset > mmdb->data_section_size - 1) { 1412 | DEBUG_MSGF("Extended type offset (%d) past data section (%d)", 1413 | offset, 1414 | mmdb->data_section_size); 1415 | return MMDB_INVALID_DATA_ERROR; 1416 | } 1417 | type = get_ext_type(mem[offset++]); 1418 | DEBUG_MSGF("Extended type: %i (%s)", type, type_num_to_name(type)); 1419 | } 1420 | 1421 | entry_data->type = type; 1422 | 1423 | if (type == MMDB_DATA_TYPE_POINTER) { 1424 | uint8_t psize = ((ctrl >> 3) & 3) + 1; 1425 | DEBUG_MSGF("Pointer size: %i", psize); 1426 | 1427 | // We check that the offset does not extend past the end of the 1428 | // database and that the subtraction of psize did not underflow. 1429 | if (offset > mmdb->data_section_size - psize || 1430 | mmdb->data_section_size < psize) { 1431 | DEBUG_MSGF("Pointer offset (%d) past data section (%d)", offset + 1432 | psize, 1433 | mmdb->data_section_size); 1434 | return MMDB_INVALID_DATA_ERROR; 1435 | } 1436 | entry_data->pointer = get_ptr_from(ctrl, &mem[offset], psize); 1437 | DEBUG_MSGF("Pointer to: %i", entry_data->pointer); 1438 | 1439 | entry_data->data_size = psize; 1440 | entry_data->offset_to_next = offset + psize; 1441 | return MMDB_SUCCESS; 1442 | } 1443 | 1444 | uint32_t size = ctrl & 31; 1445 | switch (size) { 1446 | case 29: 1447 | // We subtract when checking offset to avoid possible overflow 1448 | if (offset > mmdb->data_section_size - 1) { 1449 | DEBUG_MSGF("String end (%d, case 29) past data section (%d)", 1450 | offset, 1451 | mmdb->data_section_size); 1452 | return MMDB_INVALID_DATA_ERROR; 1453 | } 1454 | size = 29 + mem[offset++]; 1455 | break; 1456 | case 30: 1457 | // We subtract when checking offset to avoid possible overflow 1458 | if (offset > mmdb->data_section_size - 2) { 1459 | DEBUG_MSGF("String end (%d, case 30) past data section (%d)", 1460 | offset, 1461 | mmdb->data_section_size); 1462 | return MMDB_INVALID_DATA_ERROR; 1463 | } 1464 | size = 285 + get_uint16(&mem[offset]); 1465 | offset += 2; 1466 | break; 1467 | case 31: 1468 | // We subtract when checking offset to avoid possible overflow 1469 | if (offset > mmdb->data_section_size - 3) { 1470 | DEBUG_MSGF("String end (%d, case 31) past data section (%d)", 1471 | offset, 1472 | mmdb->data_section_size); 1473 | return MMDB_INVALID_DATA_ERROR; 1474 | } 1475 | size = 65821 + get_uint24(&mem[offset]); 1476 | offset += 3; 1477 | default: 1478 | break; 1479 | } 1480 | 1481 | DEBUG_MSGF("Size: %i", size); 1482 | 1483 | if (type == MMDB_DATA_TYPE_MAP || type == MMDB_DATA_TYPE_ARRAY) { 1484 | entry_data->data_size = size; 1485 | entry_data->offset_to_next = offset; 1486 | return MMDB_SUCCESS; 1487 | } 1488 | 1489 | if (type == MMDB_DATA_TYPE_BOOLEAN) { 1490 | entry_data->boolean = size ? true : false; 1491 | entry_data->data_size = 0; 1492 | entry_data->offset_to_next = offset; 1493 | DEBUG_MSGF("boolean value: %s", entry_data->boolean ? "true" : "false"); 1494 | return MMDB_SUCCESS; 1495 | } 1496 | 1497 | // Check that the data doesn't extend past the end of the memory 1498 | // buffer and that the calculation in doing this did not underflow. 1499 | if (offset > mmdb->data_section_size - size || 1500 | mmdb->data_section_size < size) { 1501 | DEBUG_MSGF("Data end (%d) past data section (%d)", offset + size, 1502 | mmdb->data_section_size); 1503 | return MMDB_INVALID_DATA_ERROR; 1504 | } 1505 | 1506 | if (type == MMDB_DATA_TYPE_UINT16) { 1507 | if (size > 2) { 1508 | DEBUG_MSGF("uint16 of size %d", size); 1509 | return MMDB_INVALID_DATA_ERROR; 1510 | } 1511 | entry_data->uint16 = (uint16_t)get_uintX(&mem[offset], size); 1512 | DEBUG_MSGF("uint16 value: %u", entry_data->uint16); 1513 | } else if (type == MMDB_DATA_TYPE_UINT32) { 1514 | if (size > 4) { 1515 | DEBUG_MSGF("uint32 of size %d", size); 1516 | return MMDB_INVALID_DATA_ERROR; 1517 | } 1518 | entry_data->uint32 = (uint32_t)get_uintX(&mem[offset], size); 1519 | DEBUG_MSGF("uint32 value: %u", entry_data->uint32); 1520 | } else if (type == MMDB_DATA_TYPE_INT32) { 1521 | if (size > 4) { 1522 | DEBUG_MSGF("int32 of size %d", size); 1523 | return MMDB_INVALID_DATA_ERROR; 1524 | } 1525 | entry_data->int32 = get_sintX(&mem[offset], size); 1526 | DEBUG_MSGF("int32 value: %i", entry_data->int32); 1527 | } else if (type == MMDB_DATA_TYPE_UINT64) { 1528 | if (size > 8) { 1529 | DEBUG_MSGF("uint64 of size %d", size); 1530 | return MMDB_INVALID_DATA_ERROR; 1531 | } 1532 | entry_data->uint64 = get_uintX(&mem[offset], size); 1533 | DEBUG_MSGF("uint64 value: %" PRIu64, entry_data->uint64); 1534 | } else if (type == MMDB_DATA_TYPE_UINT128) { 1535 | if (size > 16) { 1536 | DEBUG_MSGF("uint128 of size %d", size); 1537 | return MMDB_INVALID_DATA_ERROR; 1538 | } 1539 | #if MMDB_UINT128_IS_BYTE_ARRAY 1540 | memset(entry_data->uint128, 0, 16); 1541 | if (size > 0) { 1542 | memcpy(entry_data->uint128 + 16 - size, &mem[offset], size); 1543 | } 1544 | #else 1545 | entry_data->uint128 = get_uint128(&mem[offset], size); 1546 | #endif 1547 | } else if (type == MMDB_DATA_TYPE_FLOAT) { 1548 | if (size != 4) { 1549 | DEBUG_MSGF("float of size %d", size); 1550 | return MMDB_INVALID_DATA_ERROR; 1551 | } 1552 | size = 4; 1553 | entry_data->float_value = get_ieee754_float(&mem[offset]); 1554 | DEBUG_MSGF("float value: %f", entry_data->float_value); 1555 | } else if (type == MMDB_DATA_TYPE_DOUBLE) { 1556 | if (size != 8) { 1557 | DEBUG_MSGF("double of size %d", size); 1558 | return MMDB_INVALID_DATA_ERROR; 1559 | } 1560 | size = 8; 1561 | entry_data->double_value = get_ieee754_double(&mem[offset]); 1562 | DEBUG_MSGF("double value: %f", entry_data->double_value); 1563 | } else if (type == MMDB_DATA_TYPE_UTF8_STRING) { 1564 | entry_data->utf8_string = size == 0 ? "" : (char *)&mem[offset]; 1565 | entry_data->data_size = size; 1566 | #ifdef MMDB_DEBUG 1567 | char *string = mmdb_strndup(entry_data->utf8_string, 1568 | size > 50 ? 50 : size); 1569 | if (NULL == string) { 1570 | abort(); 1571 | } 1572 | DEBUG_MSGF("string value: %s", string); 1573 | free(string); 1574 | #endif 1575 | } else if (type == MMDB_DATA_TYPE_BYTES) { 1576 | entry_data->bytes = &mem[offset]; 1577 | entry_data->data_size = size; 1578 | } 1579 | 1580 | entry_data->offset_to_next = offset + size; 1581 | 1582 | return MMDB_SUCCESS; 1583 | } 1584 | 1585 | LOCAL int get_ext_type(int raw_ext_type) 1586 | { 1587 | return 7 + raw_ext_type; 1588 | } 1589 | 1590 | LOCAL uint32_t get_ptr_from(uint8_t ctrl, uint8_t const *const ptr, 1591 | int ptr_size) 1592 | { 1593 | uint32_t new_offset; 1594 | switch (ptr_size) { 1595 | case 1: 1596 | new_offset = ( (ctrl & 7) << 8) + ptr[0]; 1597 | break; 1598 | case 2: 1599 | new_offset = 2048 + ( (ctrl & 7) << 16 ) + ( ptr[0] << 8) + ptr[1]; 1600 | break; 1601 | case 3: 1602 | new_offset = 2048 + 524288 + ( (ctrl & 7) << 24 ) + get_uint24(ptr); 1603 | break; 1604 | case 4: 1605 | default: 1606 | new_offset = get_uint32(ptr); 1607 | break; 1608 | } 1609 | return new_offset; 1610 | } 1611 | 1612 | int MMDB_get_metadata_as_entry_data_list( 1613 | const MMDB_s *const mmdb, MMDB_entry_data_list_s **const entry_data_list) 1614 | { 1615 | MMDB_s metadata_db = make_fake_metadata_db(mmdb); 1616 | 1617 | MMDB_entry_s metadata_start = { 1618 | .mmdb = &metadata_db, 1619 | .offset = 0 1620 | }; 1621 | 1622 | return MMDB_get_entry_data_list(&metadata_start, entry_data_list); 1623 | } 1624 | 1625 | int MMDB_get_entry_data_list( 1626 | MMDB_entry_s *start, MMDB_entry_data_list_s **const entry_data_list) 1627 | { 1628 | MMDB_data_pool_s *const pool = data_pool_new(MMDB_POOL_INIT_SIZE); 1629 | if (!pool) { 1630 | return MMDB_OUT_OF_MEMORY_ERROR; 1631 | } 1632 | 1633 | MMDB_entry_data_list_s *const list = data_pool_alloc(pool); 1634 | if (!list) { 1635 | data_pool_destroy(pool); 1636 | return MMDB_OUT_OF_MEMORY_ERROR; 1637 | } 1638 | 1639 | int const status = get_entry_data_list(start->mmdb, start->offset, list, 1640 | pool, 0); 1641 | 1642 | *entry_data_list = data_pool_to_list(pool); 1643 | if (!*entry_data_list) { 1644 | data_pool_destroy(pool); 1645 | return MMDB_OUT_OF_MEMORY_ERROR; 1646 | } 1647 | 1648 | return status; 1649 | } 1650 | 1651 | LOCAL int get_entry_data_list(const MMDB_s *const mmdb, 1652 | uint32_t offset, 1653 | MMDB_entry_data_list_s *const entry_data_list, 1654 | MMDB_data_pool_s *const pool, 1655 | int depth) 1656 | { 1657 | if (depth >= MAXIMUM_DATA_STRUCTURE_DEPTH) { 1658 | DEBUG_MSG("reached the maximum data structure depth"); 1659 | return MMDB_INVALID_DATA_ERROR; 1660 | } 1661 | depth++; 1662 | CHECKED_DECODE_ONE(mmdb, offset, &entry_data_list->entry_data); 1663 | 1664 | switch (entry_data_list->entry_data.type) { 1665 | case MMDB_DATA_TYPE_POINTER: 1666 | { 1667 | uint32_t next_offset = entry_data_list->entry_data.offset_to_next; 1668 | uint32_t last_offset; 1669 | CHECKED_DECODE_ONE(mmdb, last_offset = 1670 | entry_data_list->entry_data.pointer, 1671 | &entry_data_list->entry_data); 1672 | 1673 | /* Pointers to pointers are illegal under the spec */ 1674 | if (entry_data_list->entry_data.type == MMDB_DATA_TYPE_POINTER) { 1675 | DEBUG_MSG("pointer points to another pointer"); 1676 | return MMDB_INVALID_DATA_ERROR; 1677 | } 1678 | 1679 | if (entry_data_list->entry_data.type == MMDB_DATA_TYPE_ARRAY 1680 | || entry_data_list->entry_data.type == MMDB_DATA_TYPE_MAP) { 1681 | 1682 | int status = 1683 | get_entry_data_list(mmdb, last_offset, entry_data_list, 1684 | pool, depth); 1685 | if (MMDB_SUCCESS != status) { 1686 | DEBUG_MSG("get_entry_data_list on pointer failed."); 1687 | return status; 1688 | } 1689 | } 1690 | entry_data_list->entry_data.offset_to_next = next_offset; 1691 | } 1692 | break; 1693 | case MMDB_DATA_TYPE_ARRAY: 1694 | { 1695 | uint32_t array_size = entry_data_list->entry_data.data_size; 1696 | uint32_t array_offset = entry_data_list->entry_data.offset_to_next; 1697 | while (array_size-- > 0) { 1698 | MMDB_entry_data_list_s *entry_data_list_to = 1699 | data_pool_alloc(pool); 1700 | if (!entry_data_list_to) { 1701 | return MMDB_OUT_OF_MEMORY_ERROR; 1702 | } 1703 | 1704 | int status = 1705 | get_entry_data_list(mmdb, array_offset, entry_data_list_to, 1706 | pool, depth); 1707 | if (MMDB_SUCCESS != status) { 1708 | DEBUG_MSG("get_entry_data_list on array element failed."); 1709 | return status; 1710 | } 1711 | 1712 | array_offset = entry_data_list_to->entry_data.offset_to_next; 1713 | } 1714 | entry_data_list->entry_data.offset_to_next = array_offset; 1715 | 1716 | } 1717 | break; 1718 | case MMDB_DATA_TYPE_MAP: 1719 | { 1720 | uint32_t size = entry_data_list->entry_data.data_size; 1721 | 1722 | offset = entry_data_list->entry_data.offset_to_next; 1723 | while (size-- > 0) { 1724 | MMDB_entry_data_list_s *list_key = data_pool_alloc(pool); 1725 | if (!list_key) { 1726 | return MMDB_OUT_OF_MEMORY_ERROR; 1727 | } 1728 | 1729 | int status = 1730 | get_entry_data_list(mmdb, offset, list_key, pool, depth); 1731 | if (MMDB_SUCCESS != status) { 1732 | DEBUG_MSG("get_entry_data_list on map key failed."); 1733 | return status; 1734 | } 1735 | 1736 | offset = list_key->entry_data.offset_to_next; 1737 | 1738 | MMDB_entry_data_list_s *list_value = data_pool_alloc(pool); 1739 | if (!list_value) { 1740 | return MMDB_OUT_OF_MEMORY_ERROR; 1741 | } 1742 | 1743 | status = get_entry_data_list(mmdb, offset, list_value, pool, 1744 | depth); 1745 | if (MMDB_SUCCESS != status) { 1746 | DEBUG_MSG("get_entry_data_list on map element failed."); 1747 | return status; 1748 | } 1749 | offset = list_value->entry_data.offset_to_next; 1750 | } 1751 | entry_data_list->entry_data.offset_to_next = offset; 1752 | } 1753 | break; 1754 | default: 1755 | break; 1756 | } 1757 | 1758 | return MMDB_SUCCESS; 1759 | } 1760 | 1761 | LOCAL float get_ieee754_float(const uint8_t *restrict p) 1762 | { 1763 | volatile float f; 1764 | uint8_t *q = (void *)&f; 1765 | /* Windows builds don't use autoconf but we can assume they're all 1766 | * little-endian. */ 1767 | #if MMDB_LITTLE_ENDIAN || _WIN32 1768 | q[3] = p[0]; 1769 | q[2] = p[1]; 1770 | q[1] = p[2]; 1771 | q[0] = p[3]; 1772 | #else 1773 | memcpy(q, p, 4); 1774 | #endif 1775 | return f; 1776 | } 1777 | 1778 | LOCAL double get_ieee754_double(const uint8_t *restrict p) 1779 | { 1780 | volatile double d; 1781 | uint8_t *q = (void *)&d; 1782 | #if MMDB_LITTLE_ENDIAN || _WIN32 1783 | q[7] = p[0]; 1784 | q[6] = p[1]; 1785 | q[5] = p[2]; 1786 | q[4] = p[3]; 1787 | q[3] = p[4]; 1788 | q[2] = p[5]; 1789 | q[1] = p[6]; 1790 | q[0] = p[7]; 1791 | #else 1792 | memcpy(q, p, 8); 1793 | #endif 1794 | 1795 | return d; 1796 | } 1797 | 1798 | LOCAL uint32_t get_uint32(const uint8_t *p) 1799 | { 1800 | return p[0] * 16777216U + p[1] * 65536 + p[2] * 256 + p[3]; 1801 | } 1802 | 1803 | LOCAL uint32_t get_uint24(const uint8_t *p) 1804 | { 1805 | return p[0] * 65536U + p[1] * 256 + p[2]; 1806 | } 1807 | 1808 | LOCAL uint32_t get_uint16(const uint8_t *p) 1809 | { 1810 | return p[0] * 256U + p[1]; 1811 | } 1812 | 1813 | LOCAL uint64_t get_uintX(const uint8_t *p, int length) 1814 | { 1815 | uint64_t value = 0; 1816 | while (length-- > 0) { 1817 | value <<= 8; 1818 | value += *p++; 1819 | } 1820 | return value; 1821 | } 1822 | 1823 | LOCAL int32_t get_sintX(const uint8_t *p, int length) 1824 | { 1825 | return (int32_t)get_uintX(p, length); 1826 | } 1827 | 1828 | void MMDB_free_entry_data_list(MMDB_entry_data_list_s *const entry_data_list) 1829 | { 1830 | if (entry_data_list == NULL) { 1831 | return; 1832 | } 1833 | data_pool_destroy(entry_data_list->pool); 1834 | } 1835 | 1836 | void MMDB_close(MMDB_s *const mmdb) 1837 | { 1838 | free_mmdb_struct(mmdb); 1839 | } 1840 | 1841 | LOCAL void free_mmdb_struct(MMDB_s *const mmdb) 1842 | { 1843 | if (!mmdb) { 1844 | return; 1845 | } 1846 | 1847 | if (NULL != mmdb->filename) { 1848 | FREE_AND_SET_NULL(mmdb->filename); 1849 | } 1850 | if (NULL != mmdb->file_content) { 1851 | #ifdef _WIN32 1852 | UnmapViewOfFile(mmdb->file_content); 1853 | /* Winsock is only initialized if open was successful so we only have 1854 | * to cleanup then. */ 1855 | WSACleanup(); 1856 | #else 1857 | munmap((void *)mmdb->file_content, mmdb->file_size); 1858 | #endif 1859 | } 1860 | 1861 | if (NULL != mmdb->metadata.database_type) { 1862 | FREE_AND_SET_NULL(mmdb->metadata.database_type); 1863 | } 1864 | 1865 | free_languages_metadata(mmdb); 1866 | free_descriptions_metadata(mmdb); 1867 | } 1868 | 1869 | LOCAL void free_languages_metadata(MMDB_s *mmdb) 1870 | { 1871 | if (!mmdb->metadata.languages.names) { 1872 | return; 1873 | } 1874 | 1875 | for (size_t i = 0; i < mmdb->metadata.languages.count; i++) { 1876 | FREE_AND_SET_NULL(mmdb->metadata.languages.names[i]); 1877 | } 1878 | FREE_AND_SET_NULL(mmdb->metadata.languages.names); 1879 | } 1880 | 1881 | LOCAL void free_descriptions_metadata(MMDB_s *mmdb) 1882 | { 1883 | if (!mmdb->metadata.description.count) { 1884 | return; 1885 | } 1886 | 1887 | for (size_t i = 0; i < mmdb->metadata.description.count; i++) { 1888 | if (NULL != mmdb->metadata.description.descriptions[i]) { 1889 | if (NULL != 1890 | mmdb->metadata.description.descriptions[i]->language) { 1891 | FREE_AND_SET_NULL( 1892 | mmdb->metadata.description.descriptions[i]->language); 1893 | } 1894 | 1895 | if (NULL != 1896 | mmdb->metadata.description.descriptions[i]->description) { 1897 | FREE_AND_SET_NULL( 1898 | mmdb->metadata.description.descriptions[i]->description); 1899 | } 1900 | FREE_AND_SET_NULL(mmdb->metadata.description.descriptions[i]); 1901 | } 1902 | } 1903 | 1904 | FREE_AND_SET_NULL(mmdb->metadata.description.descriptions); 1905 | } 1906 | 1907 | const char *MMDB_lib_version(void) 1908 | { 1909 | return PACKAGE_VERSION; 1910 | } 1911 | 1912 | int MMDB_dump_entry_data_list(FILE *const stream, 1913 | MMDB_entry_data_list_s *const entry_data_list, 1914 | int indent) 1915 | { 1916 | int status; 1917 | dump_entry_data_list(stream, entry_data_list, indent, &status); 1918 | return status; 1919 | } 1920 | 1921 | LOCAL MMDB_entry_data_list_s *dump_entry_data_list( 1922 | FILE *stream, MMDB_entry_data_list_s *entry_data_list, int indent, 1923 | int *status) 1924 | { 1925 | switch (entry_data_list->entry_data.type) { 1926 | case MMDB_DATA_TYPE_MAP: 1927 | { 1928 | uint32_t size = entry_data_list->entry_data.data_size; 1929 | 1930 | print_indentation(stream, indent); 1931 | fprintf(stream, "{\n"); 1932 | indent += 2; 1933 | 1934 | for (entry_data_list = entry_data_list->next; 1935 | size && entry_data_list; size--) { 1936 | 1937 | if (MMDB_DATA_TYPE_UTF8_STRING != 1938 | entry_data_list->entry_data.type) { 1939 | *status = MMDB_INVALID_DATA_ERROR; 1940 | return NULL; 1941 | } 1942 | char *key = 1943 | mmdb_strndup( 1944 | (char *)entry_data_list->entry_data.utf8_string, 1945 | entry_data_list->entry_data.data_size); 1946 | if (NULL == key) { 1947 | *status = MMDB_OUT_OF_MEMORY_ERROR; 1948 | return NULL; 1949 | } 1950 | 1951 | print_indentation(stream, indent); 1952 | fprintf(stream, "\"%s\": \n", key); 1953 | free(key); 1954 | 1955 | entry_data_list = entry_data_list->next; 1956 | entry_data_list = 1957 | dump_entry_data_list(stream, entry_data_list, indent + 2, 1958 | status); 1959 | 1960 | if (MMDB_SUCCESS != *status) { 1961 | return NULL; 1962 | } 1963 | } 1964 | 1965 | indent -= 2; 1966 | print_indentation(stream, indent); 1967 | fprintf(stream, "}\n"); 1968 | } 1969 | break; 1970 | case MMDB_DATA_TYPE_ARRAY: 1971 | { 1972 | uint32_t size = entry_data_list->entry_data.data_size; 1973 | 1974 | print_indentation(stream, indent); 1975 | fprintf(stream, "[\n"); 1976 | indent += 2; 1977 | 1978 | for (entry_data_list = entry_data_list->next; 1979 | size && entry_data_list; size--) { 1980 | entry_data_list = 1981 | dump_entry_data_list(stream, entry_data_list, indent, 1982 | status); 1983 | if (MMDB_SUCCESS != *status) { 1984 | return NULL; 1985 | } 1986 | } 1987 | 1988 | indent -= 2; 1989 | print_indentation(stream, indent); 1990 | fprintf(stream, "]\n"); 1991 | } 1992 | break; 1993 | case MMDB_DATA_TYPE_UTF8_STRING: 1994 | { 1995 | char *string = 1996 | mmdb_strndup((char *)entry_data_list->entry_data.utf8_string, 1997 | entry_data_list->entry_data.data_size); 1998 | if (NULL == string) { 1999 | *status = MMDB_OUT_OF_MEMORY_ERROR; 2000 | return NULL; 2001 | } 2002 | print_indentation(stream, indent); 2003 | fprintf(stream, "\"%s\" \n", string); 2004 | free(string); 2005 | entry_data_list = entry_data_list->next; 2006 | } 2007 | break; 2008 | case MMDB_DATA_TYPE_BYTES: 2009 | { 2010 | char *hex_string = 2011 | bytes_to_hex((uint8_t *)entry_data_list->entry_data.bytes, 2012 | entry_data_list->entry_data.data_size); 2013 | if (NULL == hex_string) { 2014 | *status = MMDB_OUT_OF_MEMORY_ERROR; 2015 | return NULL; 2016 | } 2017 | 2018 | print_indentation(stream, indent); 2019 | fprintf(stream, "%s \n", hex_string); 2020 | free(hex_string); 2021 | 2022 | entry_data_list = entry_data_list->next; 2023 | } 2024 | break; 2025 | case MMDB_DATA_TYPE_DOUBLE: 2026 | print_indentation(stream, indent); 2027 | fprintf(stream, "%f \n", 2028 | entry_data_list->entry_data.double_value); 2029 | entry_data_list = entry_data_list->next; 2030 | break; 2031 | case MMDB_DATA_TYPE_FLOAT: 2032 | print_indentation(stream, indent); 2033 | fprintf(stream, "%f \n", 2034 | entry_data_list->entry_data.float_value); 2035 | entry_data_list = entry_data_list->next; 2036 | break; 2037 | case MMDB_DATA_TYPE_UINT16: 2038 | print_indentation(stream, indent); 2039 | fprintf(stream, "%u \n", entry_data_list->entry_data.uint16); 2040 | entry_data_list = entry_data_list->next; 2041 | break; 2042 | case MMDB_DATA_TYPE_UINT32: 2043 | print_indentation(stream, indent); 2044 | fprintf(stream, "%u \n", entry_data_list->entry_data.uint32); 2045 | entry_data_list = entry_data_list->next; 2046 | break; 2047 | case MMDB_DATA_TYPE_BOOLEAN: 2048 | print_indentation(stream, indent); 2049 | fprintf(stream, "%s \n", 2050 | entry_data_list->entry_data.boolean ? "true" : "false"); 2051 | entry_data_list = entry_data_list->next; 2052 | break; 2053 | case MMDB_DATA_TYPE_UINT64: 2054 | print_indentation(stream, indent); 2055 | fprintf(stream, "%" PRIu64 " \n", 2056 | entry_data_list->entry_data.uint64); 2057 | entry_data_list = entry_data_list->next; 2058 | break; 2059 | case MMDB_DATA_TYPE_UINT128: 2060 | print_indentation(stream, indent); 2061 | #if MMDB_UINT128_IS_BYTE_ARRAY 2062 | char *hex_string = 2063 | bytes_to_hex((uint8_t *)entry_data_list->entry_data.uint128, 16); 2064 | if (NULL == hex_string) { 2065 | *status = MMDB_OUT_OF_MEMORY_ERROR; 2066 | return NULL; 2067 | } 2068 | fprintf(stream, "0x%s \n", hex_string); 2069 | free(hex_string); 2070 | #else 2071 | uint64_t high = entry_data_list->entry_data.uint128 >> 64; 2072 | uint64_t low = (uint64_t)entry_data_list->entry_data.uint128; 2073 | fprintf(stream, "0x%016" PRIX64 "%016" PRIX64 " \n", high, 2074 | low); 2075 | #endif 2076 | entry_data_list = entry_data_list->next; 2077 | break; 2078 | case MMDB_DATA_TYPE_INT32: 2079 | print_indentation(stream, indent); 2080 | fprintf(stream, "%d \n", entry_data_list->entry_data.int32); 2081 | entry_data_list = entry_data_list->next; 2082 | break; 2083 | default: 2084 | *status = MMDB_INVALID_DATA_ERROR; 2085 | return NULL; 2086 | } 2087 | 2088 | *status = MMDB_SUCCESS; 2089 | return entry_data_list; 2090 | } 2091 | 2092 | LOCAL void print_indentation(FILE *stream, int i) 2093 | { 2094 | char buffer[1024]; 2095 | int size = i >= 1024 ? 1023 : i; 2096 | memset(buffer, 32, size); 2097 | buffer[size] = '\0'; 2098 | fputs(buffer, stream); 2099 | } 2100 | 2101 | LOCAL char *bytes_to_hex(uint8_t *bytes, uint32_t size) 2102 | { 2103 | char *hex_string; 2104 | MAYBE_CHECK_SIZE_OVERFLOW(size, SIZE_MAX / 2 - 1, NULL); 2105 | 2106 | hex_string = malloc((size * 2) + 1); 2107 | if (NULL == hex_string) { 2108 | return NULL; 2109 | } 2110 | 2111 | for (uint32_t i = 0; i < size; i++) { 2112 | sprintf(hex_string + (2 * i), "%02X", bytes[i]); 2113 | } 2114 | 2115 | return hex_string; 2116 | } 2117 | 2118 | const char *MMDB_strerror(int error_code) 2119 | { 2120 | switch (error_code) { 2121 | case MMDB_SUCCESS: 2122 | return "Success (not an error)"; 2123 | case MMDB_FILE_OPEN_ERROR: 2124 | return "Error opening the specified MaxMind DB file"; 2125 | case MMDB_CORRUPT_SEARCH_TREE_ERROR: 2126 | return "The MaxMind DB file's search tree is corrupt"; 2127 | case MMDB_INVALID_METADATA_ERROR: 2128 | return "The MaxMind DB file contains invalid metadata"; 2129 | case MMDB_IO_ERROR: 2130 | return "An attempt to read data from the MaxMind DB file failed"; 2131 | case MMDB_OUT_OF_MEMORY_ERROR: 2132 | return "A memory allocation call failed"; 2133 | case MMDB_UNKNOWN_DATABASE_FORMAT_ERROR: 2134 | return 2135 | "The MaxMind DB file is in a format this library can't handle (unknown record size or binary format version)"; 2136 | case MMDB_INVALID_DATA_ERROR: 2137 | return 2138 | "The MaxMind DB file's data section contains bad data (unknown data type or corrupt data)"; 2139 | case MMDB_INVALID_LOOKUP_PATH_ERROR: 2140 | return 2141 | "The lookup path contained an invalid value (like a negative integer for an array index)"; 2142 | case MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR: 2143 | return 2144 | "The lookup path does not match the data (key that doesn't exist, array index bigger than the array, expected array or map where none exists)"; 2145 | case MMDB_INVALID_NODE_NUMBER_ERROR: 2146 | return 2147 | "The MMDB_read_node function was called with a node number that does not exist in the search tree"; 2148 | case MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR: 2149 | return 2150 | "You attempted to look up an IPv6 address in an IPv4-only database"; 2151 | default: 2152 | return "Unknown error code"; 2153 | } 2154 | } 2155 | -------------------------------------------------------------------------------- /Sources/libmaxminddb/maxminddb.h: -------------------------------------------------------------------------------- 1 | #ifdef __cplusplus 2 | extern "C" { 3 | #endif 4 | 5 | #ifndef MAXMINDDB_H 6 | #define MAXMINDDB_H 7 | 8 | /* Request POSIX.1-2008. However, we want to remain compatible with 9 | * POSIX.1-2001 (since we have been historically and see no reason to drop 10 | * compatibility). By requesting POSIX.1-2008, we can conditionally use 11 | * features provided by that standard if the implementation provides it. We can 12 | * check for what the implementation provides by checking the _POSIX_VERSION 13 | * macro after including unistd.h. If a feature is in POSIX.1-2008 but not 14 | * POSIX.1-2001, check that macro before using the feature (or check for the 15 | * feature directly if possible). */ 16 | #ifndef _POSIX_C_SOURCE 17 | #define _POSIX_C_SOURCE 200809L 18 | #endif 19 | 20 | /* libmaxminddb package version from configure */ 21 | #define PACKAGE_VERSION "1.4.2" 22 | 23 | #include "maxminddb_config.h" 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #ifdef _WIN32 31 | #include 32 | #include 33 | 34 | typedef ADDRESS_FAMILY sa_family_t; 35 | 36 | #if defined(_MSC_VER) 37 | /* MSVC doesn't define signed size_t, copy it from configure */ 38 | #define ssize_t SSIZE_T 39 | 40 | /* MSVC doesn't support restricted pointers */ 41 | #define restrict 42 | #endif 43 | #else 44 | #include 45 | #include 46 | #include 47 | #endif 48 | 49 | #define MMDB_DATA_TYPE_EXTENDED (0) 50 | #define MMDB_DATA_TYPE_POINTER (1) 51 | #define MMDB_DATA_TYPE_UTF8_STRING (2) 52 | #define MMDB_DATA_TYPE_DOUBLE (3) 53 | #define MMDB_DATA_TYPE_BYTES (4) 54 | #define MMDB_DATA_TYPE_UINT16 (5) 55 | #define MMDB_DATA_TYPE_UINT32 (6) 56 | #define MMDB_DATA_TYPE_MAP (7) 57 | #define MMDB_DATA_TYPE_INT32 (8) 58 | #define MMDB_DATA_TYPE_UINT64 (9) 59 | #define MMDB_DATA_TYPE_UINT128 (10) 60 | #define MMDB_DATA_TYPE_ARRAY (11) 61 | #define MMDB_DATA_TYPE_CONTAINER (12) 62 | #define MMDB_DATA_TYPE_END_MARKER (13) 63 | #define MMDB_DATA_TYPE_BOOLEAN (14) 64 | #define MMDB_DATA_TYPE_FLOAT (15) 65 | 66 | #define MMDB_RECORD_TYPE_SEARCH_NODE (0) 67 | #define MMDB_RECORD_TYPE_EMPTY (1) 68 | #define MMDB_RECORD_TYPE_DATA (2) 69 | #define MMDB_RECORD_TYPE_INVALID (3) 70 | 71 | /* flags for open */ 72 | #define MMDB_MODE_MMAP (1) 73 | #define MMDB_MODE_MASK (7) 74 | 75 | /* error codes */ 76 | #define MMDB_SUCCESS (0) 77 | #define MMDB_FILE_OPEN_ERROR (1) 78 | #define MMDB_CORRUPT_SEARCH_TREE_ERROR (2) 79 | #define MMDB_INVALID_METADATA_ERROR (3) 80 | #define MMDB_IO_ERROR (4) 81 | #define MMDB_OUT_OF_MEMORY_ERROR (5) 82 | #define MMDB_UNKNOWN_DATABASE_FORMAT_ERROR (6) 83 | #define MMDB_INVALID_DATA_ERROR (7) 84 | #define MMDB_INVALID_LOOKUP_PATH_ERROR (8) 85 | #define MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR (9) 86 | #define MMDB_INVALID_NODE_NUMBER_ERROR (10) 87 | #define MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR (11) 88 | 89 | #if !(MMDB_UINT128_IS_BYTE_ARRAY) 90 | #if MMDB_UINT128_USING_MODE 91 | typedef unsigned int mmdb_uint128_t __attribute__ ((__mode__(TI))); 92 | #else 93 | typedef unsigned __int128 mmdb_uint128_t; 94 | #endif 95 | #endif 96 | 97 | /* This is a pointer into the data section for a given IP address lookup */ 98 | typedef struct MMDB_entry_s { 99 | const struct MMDB_s *mmdb; 100 | uint32_t offset; 101 | } MMDB_entry_s; 102 | 103 | typedef struct MMDB_lookup_result_s { 104 | bool found_entry; 105 | MMDB_entry_s entry; 106 | uint16_t netmask; 107 | } MMDB_lookup_result_s; 108 | 109 | typedef struct MMDB_entry_data_s { 110 | bool has_data; 111 | union { 112 | uint32_t pointer; 113 | const char *utf8_string; 114 | double double_value; 115 | const uint8_t *bytes; 116 | uint16_t uint16; 117 | uint32_t uint32; 118 | int32_t int32; 119 | uint64_t uint64; 120 | #if MMDB_UINT128_IS_BYTE_ARRAY 121 | uint8_t uint128[16]; 122 | #else 123 | mmdb_uint128_t uint128; 124 | #endif 125 | bool boolean; 126 | float float_value; 127 | }; 128 | /* This is a 0 if a given entry cannot be found. This can only happen 129 | * when a call to MMDB_(v)get_value() asks for hash keys or array 130 | * indices that don't exist. */ 131 | uint32_t offset; 132 | /* This is the next entry in the data section, but it's really only 133 | * relevant for entries that part of a larger map or array 134 | * struct. There's no good reason for an end user to look at this 135 | * directly. */ 136 | uint32_t offset_to_next; 137 | /* This is only valid for strings, utf8_strings or binary data */ 138 | uint32_t data_size; 139 | /* This is an MMDB_DATA_TYPE_* constant */ 140 | uint32_t type; 141 | } MMDB_entry_data_s; 142 | 143 | /* This is the return type when someone asks for all the entry data in a map or array */ 144 | typedef struct MMDB_entry_data_list_s { 145 | MMDB_entry_data_s entry_data; 146 | struct MMDB_entry_data_list_s *next; 147 | void *pool; 148 | } MMDB_entry_data_list_s; 149 | 150 | typedef struct MMDB_description_s { 151 | const char *language; 152 | const char *description; 153 | } MMDB_description_s; 154 | 155 | /* WARNING: do not add new fields to this struct without bumping the SONAME. 156 | * The struct is allocated by the users of this library and increasing the 157 | * size will cause existing users to allocate too little space when the shared 158 | * library is upgraded */ 159 | typedef struct MMDB_metadata_s { 160 | uint32_t node_count; 161 | uint16_t record_size; 162 | uint16_t ip_version; 163 | const char *database_type; 164 | struct { 165 | size_t count; 166 | const char **names; 167 | } languages; 168 | uint16_t binary_format_major_version; 169 | uint16_t binary_format_minor_version; 170 | uint64_t build_epoch; 171 | struct { 172 | size_t count; 173 | MMDB_description_s **descriptions; 174 | } description; 175 | /* See above warning before adding fields */ 176 | } MMDB_metadata_s; 177 | 178 | /* WARNING: do not add new fields to this struct without bumping the SONAME. 179 | * The struct is allocated by the users of this library and increasing the 180 | * size will cause existing users to allocate too little space when the shared 181 | * library is upgraded */ 182 | typedef struct MMDB_ipv4_start_node_s { 183 | uint16_t netmask; 184 | uint32_t node_value; 185 | /* See above warning before adding fields */ 186 | } MMDB_ipv4_start_node_s; 187 | 188 | /* WARNING: do not add new fields to this struct without bumping the SONAME. 189 | * The struct is allocated by the users of this library and increasing the 190 | * size will cause existing users to allocate too little space when the shared 191 | * library is upgraded */ 192 | typedef struct MMDB_s { 193 | uint32_t flags; 194 | const char *filename; 195 | ssize_t file_size; 196 | const uint8_t *file_content; 197 | const uint8_t *data_section; 198 | uint32_t data_section_size; 199 | const uint8_t *metadata_section; 200 | uint32_t metadata_section_size; 201 | uint16_t full_record_byte_size; 202 | uint16_t depth; 203 | MMDB_ipv4_start_node_s ipv4_start_node; 204 | MMDB_metadata_s metadata; 205 | /* See above warning before adding fields */ 206 | } MMDB_s; 207 | 208 | typedef struct MMDB_search_node_s { 209 | uint64_t left_record; 210 | uint64_t right_record; 211 | uint8_t left_record_type; 212 | uint8_t right_record_type; 213 | MMDB_entry_s left_record_entry; 214 | MMDB_entry_s right_record_entry; 215 | } MMDB_search_node_s; 216 | 217 | extern int MMDB_open(const char *const filename, uint32_t flags, 218 | MMDB_s *const mmdb); 219 | extern MMDB_lookup_result_s MMDB_lookup_string(const MMDB_s *const mmdb, 220 | const char *const ipstr, 221 | int *const gai_error, 222 | int *const mmdb_error); 223 | extern MMDB_lookup_result_s MMDB_lookup_sockaddr( 224 | const MMDB_s *const mmdb, 225 | const struct sockaddr *const sockaddr, 226 | int *const mmdb_error); 227 | extern int MMDB_read_node(const MMDB_s *const mmdb, 228 | uint32_t node_number, 229 | MMDB_search_node_s *const node); 230 | extern int MMDB_get_value(MMDB_entry_s *const start, 231 | MMDB_entry_data_s *const entry_data, 232 | ...); 233 | extern int MMDB_vget_value(MMDB_entry_s *const start, 234 | MMDB_entry_data_s *const entry_data, 235 | va_list va_path); 236 | extern int MMDB_aget_value(MMDB_entry_s *const start, 237 | MMDB_entry_data_s *const entry_data, 238 | const char *const *const path); 239 | extern int MMDB_get_metadata_as_entry_data_list( 240 | const MMDB_s *const mmdb, MMDB_entry_data_list_s **const entry_data_list); 241 | extern int MMDB_get_entry_data_list( 242 | MMDB_entry_s *start, MMDB_entry_data_list_s **const entry_data_list); 243 | extern void MMDB_free_entry_data_list( 244 | MMDB_entry_data_list_s *const entry_data_list); 245 | extern void MMDB_close(MMDB_s *const mmdb); 246 | extern const char *MMDB_lib_version(void); 247 | extern int MMDB_dump_entry_data_list(FILE *const stream, 248 | MMDB_entry_data_list_s *const entry_data_list, 249 | int indent); 250 | extern const char *MMDB_strerror(int error_code); 251 | 252 | #endif /* MAXMINDDB_H */ 253 | 254 | #ifdef __cplusplus 255 | } 256 | #endif 257 | -------------------------------------------------------------------------------- /Sources/libmaxminddb/maxminddb_config.h: -------------------------------------------------------------------------------- 1 | #ifndef MAXMINDDB_CONFIG_H 2 | #define MAXMINDDB_CONFIG_H 3 | 4 | #ifndef MMDB_UINT128_USING_MODE 5 | /* Define as 1 if we we use unsigned int __atribute__ ((__mode__(TI))) for uint128 values */ 6 | #define MMDB_UINT128_USING_MODE 0 7 | #endif 8 | 9 | #ifndef MMDB_UINT128_IS_BYTE_ARRAY 10 | /* Define as 1 if we don't have an unsigned __int128 type */ 11 | #define MMDB_UINT128_IS_BYTE_ARRAY 1 12 | #endif 13 | 14 | #endif /* MAXMINDDB_CONFIG_H */ 15 | -------------------------------------------------------------------------------- /Sources/libmaxminddb/maxminddb_config.h.in: -------------------------------------------------------------------------------- 1 | #ifndef MAXMINDDB_CONFIG_H 2 | #define MAXMINDDB_CONFIG_H 3 | 4 | #ifndef MMDB_UINT128_USING_MODE 5 | /* Define as 1 if we we use unsigned int __atribute__ ((__mode__(TI))) for uint128 values */ 6 | #define MMDB_UINT128_USING_MODE 0 7 | #endif 8 | 9 | #ifndef MMDB_UINT128_IS_BYTE_ARRAY 10 | /* Define as 1 if we don't have an unsigned __int128 type */ 11 | #undef MMDB_UINT128_IS_BYTE_ARRAY 12 | #endif 13 | 14 | #endif /* MAXMINDDB_CONFIG_H */ 15 | -------------------------------------------------------------------------------- /Sources/libmaxminddb/maxminddb_unions.c: -------------------------------------------------------------------------------- 1 | // 2 | // maxminddb_unions.c 3 | // MMDB 4 | // 5 | // Created by Lex on 12/17/15. 6 | // Copyright © 2017 lexrus.com. All rights reserved. 7 | // 8 | 9 | #include "maxminddb_unions.h" 10 | 11 | const char *MMDB_get_entry_data_char(MMDB_entry_data_s *ptr) { 12 | return ptr->utf8_string; 13 | } 14 | 15 | uint32_t *MMDB_get_entry_data_uint32(MMDB_entry_data_s *ptr) { 16 | return &ptr->uint32; 17 | } 18 | 19 | bool MMDB_get_entry_data_bool(MMDB_entry_data_s *ptr) { 20 | return ptr->boolean; 21 | } 22 | -------------------------------------------------------------------------------- /Sources/libmaxminddb/maxminddb_unions.h: -------------------------------------------------------------------------------- 1 | // 2 | // maxminddb_unions.h 3 | // MMDB 4 | // 5 | // Created by Lex on 12/17/15. 6 | // Copyright © 2017 lexrus.com. All rights reserved. 7 | // 8 | 9 | #ifndef maxminddb_unions_h 10 | #define maxminddb_unions_h 11 | 12 | #include 13 | #include "maxminddb.h" 14 | 15 | const char *MMDB_get_entry_data_char(MMDB_entry_data_s *ptr); 16 | uint32_t *MMDB_get_entry_data_uint32(MMDB_entry_data_s *ptr); 17 | bool MMDB_get_entry_data_bool(MMDB_entry_data_s *ptr); 18 | 19 | 20 | #endif /* maxminddb_unions_h */ 21 | -------------------------------------------------------------------------------- /Tests/MMDBTests/Info-OSX.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/MMDBTests/Info-iOS.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/MMDBTests/MMDBTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import MMDB 3 | 4 | class MMDBTests: XCTestCase { 5 | var database: MMDB! 6 | 7 | override func setUp() { 8 | super.setUp() 9 | database = MMDB(Bundle(for: MMDBTests.self).path(forResource: "GeoLite2-Country", ofType: "mmdb")!) 10 | } 11 | 12 | func testExample() { 13 | XCTAssertEqual(database.lookup("202.108.22.220")?.isoCode, "CN") 14 | XCTAssertEqual(database.lookup("8.8.8.8")?.isoCode, "US") 15 | XCTAssertEqual(database.lookup("8.8.4.4")?.isoCode, "US") 16 | 17 | XCTAssertNotNil(database.lookup(IPOfHost("youtube.com")!)) 18 | XCTAssertNotNil(database.lookup(IPOfHost("facebook.com")!)) 19 | XCTAssertNotNil(database.lookup(IPOfHost("twitter.com")!)) 20 | XCTAssertNotNil(database.lookup(IPOfHost("instagram.com")!)) 21 | XCTAssertNotNil(database.lookup(IPOfHost("google.com")!)) 22 | } 23 | 24 | func testCloudFlare() { 25 | let cloudflareDNS = database.lookup("1.1.1.1") 26 | XCTAssertNotNil(cloudflareDNS) 27 | } 28 | } 29 | 30 | // See http://stackoverflow.com/questions/25890533/how-can-i-get-a-real-ip-address-from-dns-query-in-swift 31 | func IPOfHost(_ host: String) -> String? { 32 | let host = CFHostCreateWithName(nil, host as CFString).takeRetainedValue() 33 | CFHostStartInfoResolution(host, .addresses, nil) 34 | var success = DarwinBoolean(false) 35 | guard let addressing = CFHostGetAddressing(host, &success) else { 36 | return nil 37 | } 38 | 39 | let addresses = addressing.takeUnretainedValue() as NSArray 40 | if addresses.count > 0 { 41 | let theAddress = addresses[0] as! Data 42 | var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST)) 43 | let infoResult = getnameinfo( 44 | (theAddress as NSData).bytes.bindMemory(to: sockaddr.self, capacity: theAddress.count), 45 | socklen_t(theAddress.count), 46 | &hostname, 47 | socklen_t(hostname.count), 48 | nil, 49 | 0, 50 | NI_NUMERICHOST 51 | ) 52 | if infoResult == 0 { 53 | if let numAddress = String(validatingUTF8: hostname) { 54 | return numAddress 55 | } 56 | } 57 | } 58 | 59 | return nil 60 | } 61 | --------------------------------------------------------------------------------