Initial Commit.
This commit is contained in:
commit
3b32769926
|
@ -0,0 +1,5 @@
|
||||||
|
.DS_Store
|
||||||
|
/.build
|
||||||
|
/Packages
|
||||||
|
/*.xcodeproj
|
||||||
|
xcuserdata/
|
|
@ -0,0 +1,6 @@
|
||||||
|
[submodule "libmaxminddb"]
|
||||||
|
path = libmaxminddb
|
||||||
|
url = https://github.com/maxmind/libmaxminddb
|
||||||
|
[submodule "Sources/libmaxminddb"]
|
||||||
|
path = Sources/libmaxminddb
|
||||||
|
url = https://github.com/maxmind/libmaxminddb
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module classpath="External" external.linked.project.id="GeoIP2-swift" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="SPM" type="CPP_MODULE" version="4" />
|
|
@ -0,0 +1,39 @@
|
||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<SwiftCodeStyleSettings>
|
||||||
|
<option name="INDENT_DIRECTIVE_CHILDREN" value="true" />
|
||||||
|
<option name="CONDITION_CLAUSES_WRAP" value="5" />
|
||||||
|
</SwiftCodeStyleSettings>
|
||||||
|
<codeStyleSettings language="Markdown">
|
||||||
|
<option name="RIGHT_MARGIN" value="80" />
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="Swift">
|
||||||
|
<option name="RIGHT_MARGIN" value="120" />
|
||||||
|
<option name="INDENT_CASE_FROM_SWITCH" value="true" />
|
||||||
|
<option name="ALIGN_MULTILINE_CHAINED_METHODS" value="true" />
|
||||||
|
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
|
||||||
|
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="true" />
|
||||||
|
<option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" />
|
||||||
|
<option name="ALIGN_MULTILINE_EXTENDS_LIST" value="true" />
|
||||||
|
<option name="ALIGN_MULTILINE_PARENTHESIZED_EXPRESSION" value="true" />
|
||||||
|
<option name="ALIGN_GROUP_FIELD_DECLARATIONS" value="true" />
|
||||||
|
<option name="CALL_PARAMETERS_WRAP" value="5" />
|
||||||
|
<option name="CALL_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
|
||||||
|
<option name="CALL_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
|
||||||
|
<option name="METHOD_PARAMETERS_WRAP" value="5" />
|
||||||
|
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
|
||||||
|
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
|
||||||
|
<option name="METHOD_CALL_CHAIN_WRAP" value="5" />
|
||||||
|
<option name="WRAP_FIRST_METHOD_IN_CALL_CHAIN" value="true" />
|
||||||
|
<option name="KEEP_SIMPLE_BLOCKS_IN_ONE_LINE" value="true" />
|
||||||
|
<option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="true" />
|
||||||
|
<option name="KEEP_SIMPLE_CLASSES_IN_ONE_LINE" value="true" />
|
||||||
|
<option name="SOFT_MARGINS" value="80" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="INDENT_SIZE" value="2" />
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||||
|
<option name="TAB_SIZE" value="2" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
</code_scheme>
|
||||||
|
</component>
|
|
@ -0,0 +1,5 @@
|
||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||||
|
</state>
|
||||||
|
</component>
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="JavaScriptSettings">
|
||||||
|
<option name="languageLevel" value="ES6" />
|
||||||
|
</component>
|
||||||
|
<component name="SwiftPackageManagerSettings">
|
||||||
|
<option name="linkedExternalProjectsSettings">
|
||||||
|
<SwiftPackageManagerProjectSettings>
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="modules">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</SwiftPackageManagerProjectSettings>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="SwiftPackageManagerWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||||
|
</project>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/GeoIP2-swift.iml" filepath="$PROJECT_DIR$/.idea/GeoIP2-swift.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/Sources/libmaxminddb" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/Sources/libmaxminddb/t/libtap" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/Sources/libmaxminddb/t/maxmind-db" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/libmaxminddb" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,61 @@
|
||||||
|
// swift-tools-version:5.0
|
||||||
|
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||||
|
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "GeoIP2",
|
||||||
|
products: [
|
||||||
|
// Products define the executables and libraries produced by a package, and make them visible to other packages.
|
||||||
|
.library(
|
||||||
|
name: "GeoIP2",
|
||||||
|
targets: ["GeoIP2"]),
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
// Dependencies declare other packages that this package depends on.
|
||||||
|
// .package(url: /* package url */, from: "1.0.0"),
|
||||||
|
],
|
||||||
|
targets: [
|
||||||
|
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
||||||
|
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
|
||||||
|
.target(
|
||||||
|
name: "GeoIP2",
|
||||||
|
dependencies: [
|
||||||
|
.target(name: "libmaxminddb"),
|
||||||
|
.target(name: "libmaxminddb_helper")
|
||||||
|
],
|
||||||
|
path: "Sources/Swift"
|
||||||
|
),
|
||||||
|
|
||||||
|
.target(
|
||||||
|
name: "libmaxminddb_helper",
|
||||||
|
dependencies: ["libmaxminddb"],
|
||||||
|
path: "Sources/libmaxminddb_helper",
|
||||||
|
sources: ["./src/"],
|
||||||
|
publicHeadersPath: "./include/"
|
||||||
|
),
|
||||||
|
|
||||||
|
.target(
|
||||||
|
name: "libmaxminddb",
|
||||||
|
dependencies: [],
|
||||||
|
path: "Sources/libmaxminddb",
|
||||||
|
exclude: [
|
||||||
|
"./autom4te.cache/",
|
||||||
|
"./bin/",
|
||||||
|
"./dev-bin/",
|
||||||
|
"./doc/",
|
||||||
|
"./man/",
|
||||||
|
"./projects/",
|
||||||
|
"./t/"
|
||||||
|
],
|
||||||
|
sources: ["./src/"],
|
||||||
|
publicHeadersPath: "./include/",
|
||||||
|
cSettings: [CSetting.define("PACKAGE_VERSION", to: "\"1.4.2\"")]
|
||||||
|
),
|
||||||
|
|
||||||
|
.testTarget(
|
||||||
|
name: "GeoIP2Tests",
|
||||||
|
dependencies: ["GeoIP2"]
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
|
@ -0,0 +1,36 @@
|
||||||
|
# GeoIP2-swift
|
||||||
|
|
||||||
|
A decoupled Swift wrapper for
|
||||||
|
[MaxMind's](https://www.maxmind.com/en/home) [GeoIP database's](https://dev.maxmind.com/geoip/geoip2/geolite2/)
|
||||||
|
[C Library](https://github.com/maxmind/libmaxminddb).
|
||||||
|
|
||||||
|
Inspiration, and initial state of repository from
|
||||||
|
[lexrus's](https://github.com/lexrus) [MMDB-Swift](https://github.com/lexrus/MMDB-Swift)
|
||||||
|
repository.
|
||||||
|
|
||||||
|
## Version Infos
|
||||||
|
|
||||||
|
### 1. [libmaxminddb](https://github.com/maxmind/libmaxminddb) : [1.3.2](https://github.com/maxmind/libmaxminddb/releases/tag/1.3.2)
|
||||||
|
|
||||||
|
The current latest version is
|
||||||
|
[1.4.2](https://github.com/maxmind/libmaxminddb/releases/tag/1.4.2), however I
|
||||||
|
got `MMDB_INVALID_DATA_ERROR`s, and other issues for the current latest GeoLite2
|
||||||
|
database.
|
||||||
|
The earliest version with which I could get GeoLite2 working was
|
||||||
|
[1.3.2](https://github.com/maxmind/libmaxminddb/releases/tag/1.3.2)
|
||||||
|
|
||||||
|
### 2. [Swift & Swift Package Manager](https://swift.org) : [5.0.*](https://github.com/apple/swift/releases/tag/swift-5.0.3-RELEASE)
|
||||||
|
|
||||||
|
Currently that's the newest version which is supported by
|
||||||
|
[JetBrains's CLion](https://www.jetbrains.com/clion/), our IDE of choice for
|
||||||
|
all sorts of cool stuff.
|
||||||
|
The library is supposed to behave properly with newer versions.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
I won't bother providing a usage doc' just yet, because the API will 100% change.
|
||||||
|
Right now I'm working on hammering it into a production environment, so
|
||||||
|
eventually it'll become stable, and I will bother providing a proper API
|
||||||
|
documentation, and a proper 1.0.0 release will be made.
|
||||||
|
|
||||||
|
For now **THIS IS ALL WORK IN PROGRESS IN MASTER** by design.
|
|
@ -0,0 +1,11 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct Continent: ContinentProtocol {
|
||||||
|
let code: String?
|
||||||
|
let names: [String: String]?
|
||||||
|
}
|
||||||
|
|
||||||
|
public protocol ContinentProtocol {
|
||||||
|
var code: String? { get }
|
||||||
|
var names: [String: String]? { get }
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct Country: CountryProtocol {
|
||||||
|
let continent: ContinentProtocol?
|
||||||
|
let isoCode: String?
|
||||||
|
let names: [String: String]?
|
||||||
|
|
||||||
|
init(dictionary: NSDictionary) {
|
||||||
|
if let dict = dictionary["continent"] as? NSDictionary,
|
||||||
|
let code = dict["code"] as? String,
|
||||||
|
let continentNames = dict["names"] as? [String: String] {
|
||||||
|
continent = Continent(code: code, names: continentNames)
|
||||||
|
} else {
|
||||||
|
continent = nil
|
||||||
|
}
|
||||||
|
if let dict = dictionary["country"] as? NSDictionary,
|
||||||
|
let iso = dict["iso_code"] as? String,
|
||||||
|
let countryNames = dict["names"] as? [String: String] {
|
||||||
|
isoCode = iso
|
||||||
|
names = countryNames
|
||||||
|
} else {
|
||||||
|
isoCode = nil
|
||||||
|
names = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public protocol CountryProtocol {
|
||||||
|
var continent: ContinentProtocol? { get }
|
||||||
|
var isoCode: String? { get }
|
||||||
|
var names: [String: String]? { get }
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
import Foundation
|
||||||
|
import libmaxminddb_helper
|
||||||
|
import libmaxminddb
|
||||||
|
|
||||||
|
public class GeoIP2 {
|
||||||
|
|
||||||
|
fileprivate var db = MMDB_s()
|
||||||
|
|
||||||
|
fileprivate typealias ListPtr = UnsafeMutablePointer<MMDB_entry_data_list_s>
|
||||||
|
fileprivate typealias StringPtr = UnsafeMutablePointer<String>
|
||||||
|
|
||||||
|
public init?(_ filename: String) {
|
||||||
|
if openDB(atPath: filename) { return }
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
private func openDB(atPath: String) -> Bool {
|
||||||
|
let cfilename = (atPath as NSString).utf8String
|
||||||
|
let cfilenamePtr = UnsafePointer<Int8>(cfilename)
|
||||||
|
let status = MMDB_open(cfilenamePtr, UInt32(MMDB_MODE_MASK), &db)
|
||||||
|
if status != MMDB_SUCCESS {
|
||||||
|
print(String(cString: MMDB_strerror(errno)))
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func lookupString(_ s: String) -> MMDB_lookup_result_s? {
|
||||||
|
let string = (s as NSString).utf8String
|
||||||
|
let stringPtr = UnsafePointer<Int8>(string)
|
||||||
|
|
||||||
|
var gaiError: Int32 = 0
|
||||||
|
var error: Int32 = 0
|
||||||
|
|
||||||
|
let result = MMDB_lookup_string(&db, stringPtr, &gaiError, &error)
|
||||||
|
if gaiError == noErr && error == noErr {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func getString(_ list: ListPtr) -> String {
|
||||||
|
var data = list.pointee.entry_data
|
||||||
|
let type = (Int32)(data.type)
|
||||||
|
|
||||||
|
// Ignore other useless keys
|
||||||
|
guard data.has_data && type == MMDB_DATA_TYPE_UTF8_STRING else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
let str = MMDB_get_entry_data_char(&data)
|
||||||
|
let size = size_t(data.data_size)
|
||||||
|
let cKey = mmdb_strndup(str, size)
|
||||||
|
let key = String(cString: cKey!)
|
||||||
|
free(cKey)
|
||||||
|
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func getType(_ list: ListPtr) -> Int32 {
|
||||||
|
let data = list.pointee.entry_data
|
||||||
|
return (Int32)(data.type)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func getSize(_ list: ListPtr) -> UInt32 {
|
||||||
|
return list.pointee.entry_data.data_size
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public func lookup(_ IPString: String) -> CountryProtocol? {
|
||||||
|
guard let dict = lookup(ip: IPString) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let country = Country(dictionary: dict)
|
||||||
|
|
||||||
|
return country
|
||||||
|
}
|
||||||
|
|
||||||
|
private func dump(list: ListPtr?) -> (ptr: ListPtr?, out: Any?) {
|
||||||
|
var list = list
|
||||||
|
switch getType(list!) {
|
||||||
|
|
||||||
|
case MMDB_DATA_TYPE_MAP:
|
||||||
|
let dict = NSMutableDictionary()
|
||||||
|
var size = getSize(list!)
|
||||||
|
|
||||||
|
list = list?.pointee.next
|
||||||
|
while size > 0 && list != nil {
|
||||||
|
let key = getString(list!)
|
||||||
|
list = list?.pointee.next
|
||||||
|
let sub = dump(list: list)
|
||||||
|
list = sub.ptr
|
||||||
|
if let out = sub.out, key.count > 0 {
|
||||||
|
dict[key] = out
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
size -= 1
|
||||||
|
}
|
||||||
|
return (ptr: list, out: dict)
|
||||||
|
|
||||||
|
case MMDB_DATA_TYPE_UTF8_STRING:
|
||||||
|
let str = getString(list!)
|
||||||
|
list = list?.pointee.next
|
||||||
|
return (ptr: list, out: str)
|
||||||
|
|
||||||
|
case MMDB_DATA_TYPE_UINT32:
|
||||||
|
var res: NSNumber = 0
|
||||||
|
if let entryData = list?.pointee.entry_data {
|
||||||
|
var mutableEntryData = entryData
|
||||||
|
if let uint = MMDB_get_entry_data_uint32(&mutableEntryData) {
|
||||||
|
let v: UInt32 = uint.pointee
|
||||||
|
res = NSNumber(value: v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list = list?.pointee.next
|
||||||
|
return (ptr: list, out: res)
|
||||||
|
|
||||||
|
default: ()
|
||||||
|
|
||||||
|
}
|
||||||
|
return (ptr: list, out: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func lookup(ip: String) -> NSDictionary? {
|
||||||
|
guard let result = lookupString(ip) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var entry = result.entry
|
||||||
|
var list: ListPtr?
|
||||||
|
let status = MMDB_get_entry_data_list(&entry, &list)
|
||||||
|
if status != MMDB_SUCCESS {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let res = self.dump(list: list)
|
||||||
|
if let dict = res.out, let d = dict as? NSDictionary {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
MMDB_close(&db)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit cc7b880263813fbb09b3e62b8b7f9e6e4444eb28
|
|
@ -0,0 +1,167 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
|
||||||
|
/* The memmem, strdup, and strndup functions were all copied from the
|
||||||
|
* FreeBSD source, along with the relevant copyright notice.
|
||||||
|
*
|
||||||
|
* It'd be nicer to simply use the functions available on the system if they
|
||||||
|
* exist, but there doesn't seem to be a good way to detect them without also
|
||||||
|
* defining things like _GNU_SOURCE, which we want to avoid, because then we
|
||||||
|
* end up _accidentally_ using GNU features without noticing, which then
|
||||||
|
* breaks on systems like OSX.
|
||||||
|
*
|
||||||
|
* C is fun! */
|
||||||
|
|
||||||
|
/* Applies to memmem implementation */
|
||||||
|
/*-
|
||||||
|
* Copyright (c) 2005 Pascal Gloor <pascal.gloor@spale.com>
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. The name of the author may not be used to endorse or promote
|
||||||
|
* products derived from this software without specific prior written
|
||||||
|
* permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
static void *
|
||||||
|
mmdb_memmem(const void *l, size_t l_len, const void *s, size_t s_len)
|
||||||
|
{
|
||||||
|
register char *cur, *last;
|
||||||
|
const char *cl = (const char *)l;
|
||||||
|
const char *cs = (const char *)s;
|
||||||
|
|
||||||
|
/* we need something to compare */
|
||||||
|
if (l_len == 0 || s_len == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* "s" must be smaller or equal to "l" */
|
||||||
|
if (l_len < s_len)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* special case where s_len == 1 */
|
||||||
|
if (s_len == 1)
|
||||||
|
return memchr(l, (int)*cs, l_len);
|
||||||
|
|
||||||
|
/* the last position where its possible to find "s" in "l" */
|
||||||
|
last = (char *)cl + l_len - s_len;
|
||||||
|
|
||||||
|
for (cur = (char *)cl; cur <= last; cur++)
|
||||||
|
if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0)
|
||||||
|
return cur;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Applies to strnlen implementation */
|
||||||
|
/*-
|
||||||
|
* Copyright (c) 2009 David Schultz <das@FreeBSD.org>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
static size_t
|
||||||
|
mmdb_strnlen(const char *s, size_t maxlen)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
for (len = 0; len < maxlen; len++, s++) {
|
||||||
|
if (!*s)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return (len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Applies to strdup and strndup implementation */
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1988, 1993
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
static char *
|
||||||
|
mmdb_strdup(const char *str)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
char *copy;
|
||||||
|
|
||||||
|
len = strlen(str) + 1;
|
||||||
|
if ((copy = malloc(len)) == NULL)
|
||||||
|
return (NULL);
|
||||||
|
memcpy(copy, str, len);
|
||||||
|
return (copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
mmdb_strndup(const char *str, size_t n)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
char *copy;
|
||||||
|
|
||||||
|
len = mmdb_strnlen(str, n);
|
||||||
|
if ((copy = malloc(len + 1)) == NULL)
|
||||||
|
return (NULL);
|
||||||
|
memcpy(copy, str, len);
|
||||||
|
copy[len] = '\0';
|
||||||
|
return (copy);
|
||||||
|
}
|
||||||
|
/* *INDENT-ON* */
|
|
@ -0,0 +1,14 @@
|
||||||
|
#ifndef GEOIP2_SWIFT_MAXMINDDB_TYPECAST_H
|
||||||
|
#define GEOIP2_SWIFT_MAXMINDDB_TYPECAST_H
|
||||||
|
|
||||||
|
#include <maxminddb.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
const char *MMDB_get_entry_data_char(MMDB_entry_data_s *ptr);
|
||||||
|
|
||||||
|
uint32_t *MMDB_get_entry_data_uint32(MMDB_entry_data_s *ptr);
|
||||||
|
|
||||||
|
bool MMDB_get_entry_data_bool(MMDB_entry_data_s *ptr);
|
||||||
|
|
||||||
|
#endif //GEOIP2_SWIFT_MAXMINDDB_TYPECAST_H
|
|
@ -0,0 +1,13 @@
|
||||||
|
#include <maxminddb_typecast.h>
|
||||||
|
|
||||||
|
const char *MMDB_get_entry_data_char(MMDB_entry_data_s *ptr) {
|
||||||
|
return ptr->utf8_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t *MMDB_get_entry_data_uint32(MMDB_entry_data_s *ptr) {
|
||||||
|
return &ptr->uint32;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MMDB_get_entry_data_bool(MMDB_entry_data_s *ptr) {
|
||||||
|
return ptr->boolean;
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
import GeoIP2
|
||||||
|
|
||||||
|
class GeoIP2Tests: XCTestCase {
|
||||||
|
|
||||||
|
lazy var database: GeoIP2 = {
|
||||||
|
guard let countryFilePath = bundle.path(
|
||||||
|
forResource: "GeoLite2-Country_20200421/GeoLite2-Country",
|
||||||
|
ofType: "mmdb"
|
||||||
|
) else {
|
||||||
|
fatalError("GeoLite2 Country DB file was not found.")
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let geoIp2 = GeoIP2(countryFilePath) else {
|
||||||
|
fatalError("GeoIP2 database couldn't initialize.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return geoIp2
|
||||||
|
}()
|
||||||
|
|
||||||
|
func testExample() {
|
||||||
|
XCTAssertEqual(database.lookup("202.108.22.220")?.isoCode, "CN")
|
||||||
|
XCTAssertEqual(database.lookup("8.8.8.8")?.isoCode, "US")
|
||||||
|
XCTAssertEqual(database.lookup("8.8.4.4")?.isoCode, "US")
|
||||||
|
|
||||||
|
XCTAssertNotNil(database.lookup(IPOfHost("youtube.com")!))
|
||||||
|
XCTAssertNotNil(database.lookup(IPOfHost("facebook.com")!))
|
||||||
|
XCTAssertNotNil(database.lookup(IPOfHost("twitter.com")!))
|
||||||
|
XCTAssertNotNil(database.lookup(IPOfHost("instagram.com")!))
|
||||||
|
XCTAssertNotNil(database.lookup(IPOfHost("google.com")!))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCloudFlare() {
|
||||||
|
let lookup = database.lookup("1.1.1.1")
|
||||||
|
XCTAssertNotNil(lookup)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// See http://stackoverflow.com/questions/25890533/how-can-i-get-a-real-ip-address-from-dns-query-in-swift
|
||||||
|
func IPOfHost(_ host: String) -> String? {
|
||||||
|
let host = CFHostCreateWithName(nil, host as CFString).takeRetainedValue()
|
||||||
|
CFHostStartInfoResolution(host, .addresses, nil)
|
||||||
|
var success = DarwinBoolean(false)
|
||||||
|
guard let addressing = CFHostGetAddressing(host, &success) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let addresses = addressing.takeUnretainedValue() as NSArray
|
||||||
|
if addresses.count > 0 {
|
||||||
|
let theAddress = addresses[0] as! Data
|
||||||
|
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
|
||||||
|
let infoResult = getnameinfo(
|
||||||
|
(theAddress as NSData).bytes.bindMemory(to: sockaddr.self, capacity: theAddress.count),
|
||||||
|
socklen_t(theAddress.count),
|
||||||
|
&hostname,
|
||||||
|
socklen_t(hostname.count),
|
||||||
|
nil,
|
||||||
|
0,
|
||||||
|
NI_NUMERICHOST
|
||||||
|
)
|
||||||
|
if infoResult == 0 {
|
||||||
|
if let numAddress = String(validatingUTF8: hostname) {
|
||||||
|
return numAddress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
Database and Contents Copyright (c) 2020 MaxMind, Inc.
|
Binary file not shown.
|
@ -0,0 +1,3 @@
|
||||||
|
Use of this MaxMind product is governed by MaxMind's GeoLite2 End User License Agreement, which can be viewed at https://www.maxmind.com/en/geolite2/eula.
|
||||||
|
|
||||||
|
This database incorporates GeoNames [https://www.geonames.org] geographical data, which is made available under the Creative Commons Attribution 4.0 License. To view a copy of this license, visit https://creativecommons.org/licenses/by/4.0/.
|
|
@ -0,0 +1,20 @@
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
extension XCTestCase {
|
||||||
|
|
||||||
|
var bundle: Bundle {
|
||||||
|
get {
|
||||||
|
guard let currentFileUrl = URL(string: #file) else {
|
||||||
|
return Bundle.main
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let testBundle = Bundle(path: currentFileUrl.deletingLastPathComponent().path) else {
|
||||||
|
return Bundle.main
|
||||||
|
}
|
||||||
|
|
||||||
|
return testBundle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
#if !canImport(ObjectiveC)
|
||||||
|
public func allTests() -> [XCTestCaseEntry] {
|
||||||
|
return [
|
||||||
|
testCase(GeoIP2_swiftTests.allTests),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,7 @@
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
import GeoIP2_swiftTests
|
||||||
|
|
||||||
|
var tests = [XCTestCaseEntry]()
|
||||||
|
tests += GeoIP2_swiftTests.allTests()
|
||||||
|
XCTMain(tests)
|
Loading…
Reference in New Issue