34cdcee00b
OneDrive's IPs (e.g. 13.107.x, 150.171.x) have no PTR and weren't in our CIDR table, so each fragmented into its own IP-named district — a long session showed dozens, all with the "OneDrive" process subtitle. Add the documented Microsoft 365 service ranges so they collapse to one Microsoft district. Deliberately excludes generic Azure tenant space, which would mislabel third-party apps hosted on Azure. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
67 lines
3.1 KiB
Swift
67 lines
3.1 KiB
Swift
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
|
|
}
|
|
}
|