In its current state this hackery fits our needs.

Signed-off-by: Adam Rocska <adam.rocska@adams.solutions>
This commit is contained in:
Adam Rocska 2020-06-11 10:22:15 +02:00
parent c9bee263df
commit 7b487ccc60
9 changed files with 311 additions and 47 deletions

View File

@ -6,19 +6,6 @@ sound like to plan for a breaking change befor the first initial version's
release, but hey, there's a time pressure on my shoulder right now + MaxMind's
"binary format" "desgined" by its script kiddos slowed things down baadly._
## Replace CIDR Strings with proper intelligence
[Wikipedia](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing)
At the time of writing I just had to port the dumbass php style "String
representation" of networks. That's bad. That's so bad, that normally I wouldn't
permit such a crap in my company.
What I want is to create a unit (most probably it'll be a struct?) that not only
represents the network, but also provides utilities that can check whether an
`IpAddress` belongs to that network for example. Should be representable in many
different forms, etc.
## Error handling
There used to be an original concept of how to do proper error handling &
@ -41,4 +28,15 @@ For example if you get **128bit unsigned int index pointers**, you'll face quite
bit of fun.
I'll do my best to massage things out. Once the public API is stable, and neat,
the underlying crap can be cleaned up.
the underlying crap can be cleaned up.
## Redesign the retarded modeling
At the library's initial stage the decision was to reproduce in a somewhat
similar state what MaxMind's php scritpers made with their PHP, Java, and C#
libraries.
That modeling is awful.
The way the API will return models will definitely change, so there will be a
2.0.0 API break at some point.

View File

@ -1,11 +1 @@
# GeoIP2-swift
**HEAVILY WORK IN PROGRESS.**
Progress postponed for a few days.
In its current state it mostly works, but is unacceptably slow to initialize.
The lookup time is fast, but it's an unacceptable speed to initialize.
Initialization performance problems root mainly from the poor DB design.
**Will revisit after 8th of July.**

View File

@ -6,6 +6,12 @@ public struct CityModel: Equatable {
public let location: LocationRecord
public let postal: PostalRecord
public let subdivisions: [SubdivisionRecord]
public let continent: ContinentRecord
public let country: CountryRecord
public let maxmind: MaxMindRecord
public let registeredCountry: CountryRecord
public let representedCountry: RepresentedCountryRecord
public let traits: TraitsRecord
public let ipAddress: IpAddress
public let netmask: IpAddress
public var mostSpecificSubdivision: SubdivisionRecord { get { return subdivisions.last ?? SubdivisionRecord(nil) } }
@ -20,6 +26,12 @@ extension CityModel: DictionaryInitialisableModel {
subdivisions: (dictionary?["subdivisions"]?.unwrap() ?? [] as [Payload]).compactMap(
{ SubdivisionRecord($0.unwrap()) }
),
continent: ContinentRecord(dictionary?["continent"]?.unwrap()),
country: CountryRecord(dictionary?["country"]?.unwrap()),
maxmind: MaxMindRecord(dictionary?["maxmind"]?.unwrap()),
registeredCountry: CountryRecord(dictionary?["registered_country"]?.unwrap()),
representedCountry: RepresentedCountryRecord(dictionary?["represented_country"]?.unwrap()),
traits: TraitsRecord(dictionary?["traits"]?.unwrap()),
ipAddress: ip,
netmask: netmask
)

View File

@ -1,9 +1,39 @@
import Foundation
import enum Decoder.Payload
// TODO : Untangle php scripter retardnes. Theses people at maxmind shouldn't write code that gets executed by a processor. Honestly.
// See stupid fucking php scripter stupidity : https://github.com/maxmind/GeoIP2-php/blob/master/src/Model/Enterprise.php
// Apparently they are so dumb, they needed 2 people to produce an empty class, with a nonsense API doc'.
// Their Java, and C# code is as retaarded as the php one. I never saw throughout my career Java being raped so badly.
public struct EnterpriseModel: Equatable {
public let city: CityRecord
public let location: LocationRecord
public let postal: PostalRecord
public let subdivisions: [SubdivisionRecord]
public let continent: ContinentRecord
public let country: CountryRecord
public let maxmind: MaxMindRecord
public let registeredCountry: CountryRecord
public let representedCountry: RepresentedCountryRecord
public let traits: TraitsRecord
public let ipAddress: IpAddress
public let netmask: IpAddress
public var mostSpecificSubdivision: SubdivisionRecord { get { return subdivisions.last ?? SubdivisionRecord(nil) } }
}
extension EnterpriseModel: DictionaryInitialisableModel {
public init(ip: IpAddress, netmask: IpAddress, _ dictionary: [String: Payload]?) {
self.init(
city: CityRecord(dictionary?["city"]?.unwrap()),
location: LocationRecord(dictionary?["location"]?.unwrap()),
postal: PostalRecord(dictionary?["postal"]?.unwrap()),
subdivisions: (dictionary?["subdivisions"]?.unwrap() ?? [] as [Payload]).compactMap(
{ SubdivisionRecord($0.unwrap()) }
),
continent: ContinentRecord(dictionary?["continent"]?.unwrap()),
country: CountryRecord(dictionary?["country"]?.unwrap()),
maxmind: MaxMindRecord(dictionary?["maxmind"]?.unwrap()),
registeredCountry: CountryRecord(dictionary?["registered_country"]?.unwrap()),
representedCountry: RepresentedCountryRecord(dictionary?["represented_country"]?.unwrap()),
traits: TraitsRecord(dictionary?["traits"]?.unwrap()),
ipAddress: ip,
netmask: netmask
)
}
}

View File

@ -1,9 +1,39 @@
import Foundation
import enum Decoder.Payload
// TODO : Untangle php scripter retardnes. Theses people at maxmind shouldn't write code that gets executed by a processor. Honestly.
// See stupid fucking php scripter stupidity : https://github.com/maxmind/GeoIP2-php/blob/master/src/Model/Insights.php
// Apparently they are so dumb, they needed 2 people to produce an empty class, with a nonsense API doc'.
// Their Java, and C# code is as retaarded as the php one. I never saw throughout my career Java being raped so badly.
public struct InsightsModel: Equatable {
public let city: CityRecord
public let location: LocationRecord
public let postal: PostalRecord
public let subdivisions: [SubdivisionRecord]
public let continent: ContinentRecord
public let country: CountryRecord
public let maxmind: MaxMindRecord
public let registeredCountry: CountryRecord
public let representedCountry: RepresentedCountryRecord
public let traits: TraitsRecord
public let ipAddress: IpAddress
public let netmask: IpAddress
public var mostSpecificSubdivision: SubdivisionRecord { get { return subdivisions.last ?? SubdivisionRecord(nil) } }
}
extension InsightsModel: DictionaryInitialisableModel {
public init(ip: IpAddress, netmask: IpAddress, _ dictionary: [String: Payload]?) {
self.init(
city: CityRecord(dictionary?["city"]?.unwrap()),
location: LocationRecord(dictionary?["location"]?.unwrap()),
postal: PostalRecord(dictionary?["postal"]?.unwrap()),
subdivisions: (dictionary?["subdivisions"]?.unwrap() ?? [] as [Payload]).compactMap(
{ SubdivisionRecord($0.unwrap()) }
),
continent: ContinentRecord(dictionary?["continent"]?.unwrap()),
country: CountryRecord(dictionary?["country"]?.unwrap()),
maxmind: MaxMindRecord(dictionary?["maxmind"]?.unwrap()),
registeredCountry: CountryRecord(dictionary?["registered_country"]?.unwrap()),
representedCountry: RepresentedCountryRecord(dictionary?["represented_country"]?.unwrap()),
traits: TraitsRecord(dictionary?["traits"]?.unwrap()),
ipAddress: ip,
netmask: netmask
)
}
}

12
Sources/Api/README.md Normal file
View File

@ -0,0 +1,12 @@
# Good to know about the API module
It was intentionally designed to reproduce the php scripter fantasies, and awful
modeling to reduce risks of having a broken library behavior, and data provision.
Depending on how widespread the library may become, the new fancy API of which
I could be proud of will be delivered either :
* **as a backwards incompatible way**, by simply throwing it all out in the
garbage, and providing a proper professional api
* **as a backwards compatible way**, by doing a minor release, marking this pile
of shit I wrote as deprecated, providing a neat API module, and throwing this
trash in the garbage in the future.

View File

@ -15,7 +15,7 @@ extension SubdivisionRecord: DictionaryInitialisableRecord {
self.init(
confidence: dictionary?["confidence"]?.unwrap(),
geonameId: dictionary?["geoname_id"]?.unwrap(),
isoCode: dictionary?["iso_cod"]?.unwrap(),
isoCode: dictionary?["iso_code"]?.unwrap(),
names: (dictionary?["names"]?.unwrap() ?? [:] as [String: Payload]).compactMapValues({ $0.unwrap() })
)
}

View File

@ -6,8 +6,6 @@ public struct TraitsRecord: Equatable {
let autonomousSystemOrganization: String?
let connectionType: String?
let domain: String?
// todo
// let ipAddress: IpAddress
let isAnonymous: Bool
let isAnonymousProxy: Bool
let isAnonymousVpn: Bool
@ -17,7 +15,6 @@ public struct TraitsRecord: Equatable {
let isSatelliteProvider: Bool
let isTorExitNode: Bool
let isp: String?
let network: String
let organization: String?
let staticIPScore: Float?
let userCount: Int?
@ -31,8 +28,6 @@ extension TraitsRecord: DictionaryInitialisableRecord {
autonomousSystemOrganization: dictionary?["autonomous_system_organization"]?.unwrap(),
connectionType: dictionary?["connection_type"]?.unwrap(),
domain: dictionary?["domain"]?.unwrap(),
// TODO
// ipAddress: dictionary?["ipAddress"]?.unwrap(),
isAnonymous: dictionary?["is_anonymous"]?.unwrap() ?? false,
isAnonymousProxy: dictionary?["is_anonymous_proxy"]?.unwrap() ?? false,
isAnonymousVpn: dictionary?["is_anonymous_vpn"]?.unwrap() ?? false,
@ -42,8 +37,6 @@ extension TraitsRecord: DictionaryInitialisableRecord {
isSatelliteProvider: dictionary?["is_satellite_provider"]?.unwrap() ?? false,
isTorExitNode: dictionary?["is_tor_exit_node"]?.unwrap() ?? false,
isp: dictionary?["isp"]?.unwrap(),
// TODO
network: dictionary?["network"]?.unwrap() ?? "",
organization: dictionary?["organization"]?.unwrap(),
staticIPScore: dictionary?["static_ip_score"]?.unwrap(),
userCount: dictionary?["user_count"]?.unwrap(),

View File

@ -5,6 +5,15 @@ import class Api.Reader
import class Api.ReaderFactory
import enum Api.IpAddress
@testable import struct Api.CityModel
@testable import struct Api.CityRecord
@testable import struct Api.ContinentRecord
@testable import struct Api.CountryRecord
@testable import struct Api.LocationRecord
@testable import struct Api.MaxMindRecord
@testable import struct Api.PostalRecord
@testable import struct Api.RepresentedCountryRecord
@testable import struct Api.SubdivisionRecord
@testable import struct Api.TraitsRecord
class CityLookupTest: XCTestCase {
@ -31,11 +40,201 @@ class CityLookupTest: XCTestCase {
}
func testSuccessfulLookup() {
"ffff:ffff:ffff:ffff:ffff:ffff:ffff:C000"
"255.255.255.224"
"255.255.255.192"
// print(reader.lookup(.v6("::1.128.0.0"))?.netmask)
print(reader.lookup(.v4(80, 99, 18, 166))?.netmask)
XCTAssertEqual(
CityModel(
city: CityRecord(
confidence: nil,
geonameId: 3054643,
names: [
"de": "Budapest",
"en": "Budapest",
"es": "Budapest",
"fr": "Budapest",
"ja": "ブダペスト",
"pt-BR": "Budapeste",
"ru": "Будапешт",
"zh-CN": "布达佩斯"
]
),
location: LocationRecord(
averageIncome: nil,
accuracyRadius: 20,
latitude: 47.4984,
longitude: 19.0404,
populationDensity: nil,
metroCode: nil,
timeZone: "Europe/Budapest"
),
postal: PostalRecord(code: "1096", confidence: nil),
subdivisions: [
SubdivisionRecord(
confidence: nil,
geonameId: 3054638,
isoCode: "BU",
names: ["en": "Budapest"]
)
],
continent: ContinentRecord(
code: "EU",
geonameId: 6255148,
names: [
"de": "Europa",
"en": "Europe",
"es": "Europa",
"fr": "Europe",
"ja": "ヨーロッパ",
"pt-BR": "Europa",
"ru": "Европа",
"zh-CN": "欧洲"
]
),
country: CountryRecord(
confidence: nil,
geonameId: 719819,
isInEuropeanUnion: true,
isoCode: "HU",
names: [
"de": "Ungarn",
"en": "Hungary",
"es": "Hungría",
"fr": "Hongrie",
"ja": "ハンガリー共和国",
"pt-BR": "Hungria",
"ru": "Венгрия",
"zh-CN": "匈牙利"
]
),
maxmind: MaxMindRecord(queriesRemaining: nil),
registeredCountry: CountryRecord(
confidence: nil,
geonameId: 719819,
isInEuropeanUnion: true,
isoCode: "HU",
names: [
"de": "Ungarn",
"en": "Hungary",
"es": "Hungría",
"fr": "Hongrie",
"ja": "ハンガリー共和国",
"pt-BR": "Hungria",
"ru": "Венгрия",
"zh-CN": "匈牙利"
]
),
representedCountry: RepresentedCountryRecord(type: nil),
traits: TraitsRecord(
autonomousSystemNumber: nil,
autonomousSystemOrganization: nil,
connectionType: nil,
domain: nil,
isAnonymous: false,
isAnonymousProxy: false,
isAnonymousVpn: false,
isHostingProvider: false,
isLegitimateProxy: false,
isPublicProxy: false,
isSatelliteProvider: false,
isTorExitNode: false,
isp: nil,
organization: nil,
staticIPScore: nil,
userCount: nil,
userType: nil
),
ipAddress: .v4(80, 99, 18, 166),
netmask: IpAddress.v4Netmask(ofBitLength: 26)
),
reader.lookup(.v4(80, 99, 18, 166))
)
XCTAssertEqual(
CityModel(
city: CityRecord(confidence: nil, geonameId: nil, names: [:]),
location: LocationRecord(
averageIncome: nil,
accuracyRadius: 1000,
latitude: -33.494,
longitude: 143.2104,
populationDensity: nil,
metroCode: nil,
timeZone: "Australia/Sydney"
),
postal: PostalRecord(code: nil, confidence: nil),
subdivisions: [],
continent: ContinentRecord(
code: "OC",
geonameId: 6255151,
names: [
"de": "Ozeanien",
"en": "Oceania",
"es": "Oceanía",
"fr": "Océanie",
"ja": "オセアニア",
"pt-BR": "Oceania",
"ru": "Океания",
"zh-CN": "大洋洲"
]
),
country: CountryRecord(
confidence: nil,
geonameId: 2077456,
isInEuropeanUnion: false,
isoCode: "AU",
names: [
"de": "Australien",
"en": "Australia",
"es": "Australia",
"fr": "Australie",
"ja": "オーストラリア",
"pt-BR": "Austrália",
"ru": "Австралия",
"zh-CN": "澳大利亚"
]
),
maxmind: MaxMindRecord(queriesRemaining: nil),
registeredCountry: CountryRecord(
confidence: nil,
geonameId: 2077456,
isInEuropeanUnion: false,
isoCode: "AU",
names: [
"de": "Australien",
"en": "Australia",
"es": "Australia",
"fr": "Australie",
"ja": "オーストラリア",
"pt-BR": "Austrália",
"ru": "Австралия",
"zh-CN": "澳大利亚"
]
),
representedCountry: RepresentedCountryRecord(type: nil),
traits: TraitsRecord(
autonomousSystemNumber: nil,
autonomousSystemOrganization: nil,
connectionType: nil,
domain: nil,
isAnonymous: false,
isAnonymousProxy: false,
isAnonymousVpn: false,
isHostingProvider: false,
isLegitimateProxy: false,
isPublicProxy: false,
isSatelliteProvider: false,
isTorExitNode: false,
isp: nil,
organization: nil,
staticIPScore: nil,
userCount: nil,
userType: nil
),
ipAddress: .v6("::1.128.0.0"),
netmask: IpAddress.v6Netmask(ofBitLength: 114)
),
reader.lookup(.v6("::1.128.0.0"))
)
XCTAssertNil(reader.lookup(.v6("2600:7100::")))
}
}