Files

67 lines
3.1 KiB
Swift
Raw Permalink Normal View History

import Foundation
/// A curated CIDR owner table, used as a fallback when a host has no PTR
/// record. Many large operators (Apple's 17.0.0.0/8 famously, Telegram, the
/// cloud edges) publish no reverse DNS, so this recovers the obvious ones.
/// IPv4 only the no-PTR giants we care about are reachable on v4.
public enum IPRanges {
private struct Block { let net: UInt32; let mask: UInt32; let key: String; let name: String }
private static let blocks: [Block] = [
("17.0.0.0/8", "apple", "Apple"),
("149.154.160.0/20", "telegram", "Telegram"),
("91.108.0.0/16", "telegram", "Telegram"),
("13.64.0.0/11", "microsoft", "Microsoft"),
// Microsoft 365 / OneDrive front-ends no PTR, so without these each
// IP fragments into its own district. These are Microsoft-operated
// service ranges (not generic Azure tenant space, which would wrongly
// relabel third-party apps hosted on Azure).
("13.104.0.0/14", "microsoft", "Microsoft"), // incl. 13.107.x
("150.171.0.0/16", "microsoft", "Microsoft"),
("52.108.0.0/14", "microsoft", "Microsoft"), // O365 common
("52.112.0.0/14", "microsoft", "Microsoft"), // Teams/Skype
("40.96.0.0/13", "microsoft", "Microsoft"), // Exchange Online
("40.104.0.0/15", "microsoft", "Microsoft"),
("157.240.0.0/16", "meta", "Meta"),
("31.13.24.0/21", "meta", "Meta"),
("129.134.0.0/16", "meta", "Meta"),
("1.1.1.0/24", "cloudflare", "Cloudflare"),
("104.16.0.0/13", "cloudflare", "Cloudflare"),
// Private / link-local space collapses into one neighbourhood.
("10.0.0.0/8", "lan", "Local Network"),
("172.16.0.0/12", "lan", "Local Network"),
("192.168.0.0/16", "lan", "Local Network"),
("169.254.0.0/16", "lan", "Local Network"),
].compactMap { parse($0.0, key: $0.1, name: $0.2) }
public static func match(_ ip: String) -> (key: String, name: String)? {
guard let value = ipv4ToUInt32(ip) else { return nil }
for b in blocks where (value & b.mask) == b.net {
return (b.key, b.name)
}
return nil
}
// MARK: - Parsing
private static func parse(_ cidr: String, key: String, name: String) -> Block? {
let parts = cidr.split(separator: "/")
guard parts.count == 2, let bits = UInt32(parts[1]), bits <= 32,
let net = ipv4ToUInt32(String(parts[0])) else { return nil }
let mask: UInt32 = bits == 0 ? 0 : ~UInt32(0) << (32 - bits)
return Block(net: net & mask, mask: mask, key: key, name: name)
}
static func ipv4ToUInt32(_ ip: String) -> UInt32? {
let octets = ip.split(separator: ".")
guard octets.count == 4 else { return nil }
var result: UInt32 = 0
for octet in octets {
guard let n = UInt32(octet), n <= 255 else { return nil }
result = (result << 8) | n
}
return result
}
}