Extend PUSH_REPLY parsing

- Topology
- Routes

Use the less confusing defaultGateway vs gatewayAddress.
This commit is contained in:
Davide De Rosa 2018-08-24 11:45:41 +02:00
parent 5bf7813d56
commit b0d264889c
2 changed files with 147 additions and 13 deletions

View File

@ -459,7 +459,7 @@ extension TunnelKitProvider: SessionProxyDelegate {
log.info("Returned ifconfig parameters:") log.info("Returned ifconfig parameters:")
log.info("\tRemote: \(remoteAddress)") log.info("\tRemote: \(remoteAddress)")
log.info("\tLocal: \(reply.address)/\(reply.addressMask)") log.info("\tLocal: \(reply.address)/\(reply.addressMask)")
log.info("\tGateway: \(reply.gatewayAddress)") log.info("\tGateway: \(reply.defaultGateway)")
log.info("\tDNS: \(reply.dnsServers)") log.info("\tDNS: \(reply.dnsServers)")
bringNetworkUp(remoteAddress: remoteAddress, reply: reply) { (error) in bringNetworkUp(remoteAddress: remoteAddress, reply: reply) { (error) in
@ -493,10 +493,17 @@ extension TunnelKitProvider: SessionProxyDelegate {
// route all traffic to VPN // route all traffic to VPN
let defaultRoute = NEIPv4Route.default() let defaultRoute = NEIPv4Route.default()
defaultRoute.gatewayAddress = reply.gatewayAddress defaultRoute.gatewayAddress = reply.defaultGateway
var routes: [NEIPv4Route] = [defaultRoute]
for r in reply.routes {
let ipv4Route = NEIPv4Route(destinationAddress: r.destination, subnetMask: r.mask)
ipv4Route.gatewayAddress = r.gateway ?? reply.defaultGateway
routes.append(ipv4Route)
}
let ipv4Settings = NEIPv4Settings(addresses: [reply.address], subnetMasks: [reply.addressMask]) let ipv4Settings = NEIPv4Settings(addresses: [reply.address], subnetMasks: [reply.addressMask])
ipv4Settings.includedRoutes = [defaultRoute] ipv4Settings.includedRoutes = routes
ipv4Settings.excludedRoutes = [] ipv4Settings.excludedRoutes = []
let dnsSettings = NEDNSSettings(servers: reply.dnsServers) let dnsSettings = NEDNSSettings(servers: reply.dnsServers)

View File

@ -47,7 +47,10 @@ public protocol SessionReply {
var addressMask: String { get } var addressMask: String { get }
/// The address of the default gateway. /// The address of the default gateway.
var gatewayAddress: String { get } var defaultGateway: String { get }
/// The additional routes.
var routes: [SessionProxy.Route] { get }
/// The DNS servers set up for this session. /// The DNS servers set up for this session.
var dnsServers: [String] { get } var dnsServers: [String] { get }
@ -57,9 +60,42 @@ extension SessionProxy {
// XXX: parsing is very optimistic // XXX: parsing is very optimistic
/// Represents a route in the routing table.
public struct Route {
/// The destination host or subnet.
public let destination: String
/// The address mask.
public let mask: String
/// The address of the gateway (uses default gateway if not set).
public let gateway: String?
fileprivate init(_ destination: String, _ mask: String?, _ gateway: String?) {
self.destination = destination
self.mask = mask ?? "255.255.255.255"
self.gateway = gateway
}
}
struct PushReply: SessionReply { struct PushReply: SessionReply {
private enum Topology: String {
case net30
case p2p
case subnet
}
private static let topologyRegexp = try! NSRegularExpression(pattern: "topology (net30|p2p|subnet)", options: [])
private static let ifconfigRegexp = try! NSRegularExpression(pattern: "ifconfig [\\d\\.]+ [\\d\\.]+", options: []) private static let ifconfigRegexp = try! NSRegularExpression(pattern: "ifconfig [\\d\\.]+ [\\d\\.]+", options: [])
private static let gatewayRegexp = try! NSRegularExpression(pattern: "route-gateway [\\d\\.]+", options: [])
private static let routeRegexp = try! NSRegularExpression(pattern: "route [\\d\\.]+( [\\d\\.]+){0,2}", options: [])
private static let dnsRegexp = try! NSRegularExpression(pattern: "dhcp-option DNS [\\d\\.]+", options: []) private static let dnsRegexp = try! NSRegularExpression(pattern: "dhcp-option DNS [\\d\\.]+", options: [])
private static let authTokenRegexp = try! NSRegularExpression(pattern: "auth-token [a-zA-Z0-9/=+]+", options: []) private static let authTokenRegexp = try! NSRegularExpression(pattern: "auth-token [a-zA-Z0-9/=+]+", options: [])
@ -70,7 +106,9 @@ extension SessionProxy {
let addressMask: String let addressMask: String
let gatewayAddress: String let defaultGateway: String
let routes: [Route]
let dnsServers: [String] let dnsServers: [String]
@ -83,22 +121,84 @@ extension SessionProxy {
return nil return nil
} }
var ifconfigComponents: [String]? var optTopologyComponents: [String]?
var dnsServers = [String]() var optIfconfigComponents: [String]?
var optGatewayComponents: [String]?
let address: String
let addressMask: String
let defaultGateway: String
var routes: [Route] = []
var dnsServers: [String] = []
var authToken: String? var authToken: String?
var peerId: UInt32? var peerId: UInt32?
// MARK: Routing
PushReply.topologyRegexp.enumerateMatches(in: message, options: [], range: NSMakeRange(0, message.count)) { (result, flags, _) in
guard let range = result?.range else { return }
let match = (message as NSString).substring(with: range)
optTopologyComponents = match.components(separatedBy: " ")
}
guard let topologyComponents = optTopologyComponents, topologyComponents.count == 2 else {
throw SessionError.malformedPushReply
}
// assumes "topology" to be always pushed to clients, even when not explicitly set (defaults to net30)
guard let topology = Topology(rawValue: topologyComponents[1]) else {
fatalError("Bad topology regexp, accepted unrecognized value: \(topologyComponents[1])")
}
PushReply.ifconfigRegexp.enumerateMatches(in: message, options: [], range: NSMakeRange(0, message.count)) { (result, flags, _) in PushReply.ifconfigRegexp.enumerateMatches(in: message, options: [], range: NSMakeRange(0, message.count)) { (result, flags, _) in
guard let range = result?.range else { return } guard let range = result?.range else { return }
let match = (message as NSString).substring(with: range) let match = (message as NSString).substring(with: range)
ifconfigComponents = match.components(separatedBy: " ") optIfconfigComponents = match.components(separatedBy: " ")
} }
guard let ifconfigComponents = optIfconfigComponents, ifconfigComponents.count == 3 else {
guard let addresses = ifconfigComponents, addresses.count >= 2 else {
throw SessionError.malformedPushReply throw SessionError.malformedPushReply
} }
PushReply.gatewayRegexp.enumerateMatches(in: message, options: [], range: NSMakeRange(0, message.count)) { (result, flags, _) in
guard let range = result?.range else { return }
let match = (message as NSString).substring(with: range)
optGatewayComponents = match.components(separatedBy: " ")
}
//
// excerpts from OpenVPN manpage
//
// "--ifconfig l rn":
//
// Set TUN/TAP adapter parameters. l is the IP address of the local VPN endpoint. For TUN devices in point-to-point mode, rn is the IP address of
// the remote VPN endpoint. For TAP devices, or TUN devices used with --topology subnet, rn is the subnet mask of the virtual network segment which
// is being created or connected to.
//
// "--topology mode":
//
// Note: Using --topology subnet changes the interpretation of the arguments of --ifconfig to mean "address netmask", no longer "local remote".
//
switch topology {
case .subnet:
// default gateway required when topology is subnet
guard let gatewayComponents = optGatewayComponents, gatewayComponents.count == 2 else {
throw SessionError.malformedPushReply
}
address = ifconfigComponents[1]
addressMask = ifconfigComponents[2]
defaultGateway = gatewayComponents[1]
default:
address = ifconfigComponents[1]
addressMask = "255.255.255.255"
defaultGateway = ifconfigComponents[2]
}
// MARK: DNS
PushReply.dnsRegexp.enumerateMatches(in: message, options: [], range: NSMakeRange(0, message.count)) { (result, flags, _) in PushReply.dnsRegexp.enumerateMatches(in: message, options: [], range: NSMakeRange(0, message.count)) { (result, flags, _) in
guard let range = result?.range else { return } guard let range = result?.range else { return }
@ -108,6 +208,32 @@ extension SessionProxy {
dnsServers.append(dnsEntryComponents[2]) dnsServers.append(dnsEntryComponents[2])
} }
// MARK: Routes
PushReply.routeRegexp.enumerateMatches(in: message, options: [], range: NSMakeRange(0, message.count)) { (result, flags, _) in
guard let range = result?.range else { return }
let match = (message as NSString).substring(with: range)
let routeEntryComponents = match.components(separatedBy: " ")
let destination = routeEntryComponents[1]
let mask: String?
let gateway: String?
if routeEntryComponents.count > 2 {
mask = routeEntryComponents[2]
} else {
mask = nil
}
if routeEntryComponents.count > 3 {
gateway = routeEntryComponents[3]
} else {
gateway = defaultGateway
}
routes.append(Route(destination, mask, gateway))
}
// MARK: Authentication
PushReply.authTokenRegexp.enumerateMatches(in: message, options: [], range: NSMakeRange(0, message.count)) { (result, flags, _) in PushReply.authTokenRegexp.enumerateMatches(in: message, options: [], range: NSMakeRange(0, message.count)) { (result, flags, _) in
guard let range = result?.range else { return } guard let range = result?.range else { return }
@ -130,10 +256,11 @@ extension SessionProxy {
} }
} }
address = addresses[1] self.address = address
addressMask = "255.255.255.255" self.addressMask = addressMask
gatewayAddress = addresses[2] self.defaultGateway = defaultGateway
self.dnsServers = dnsServers self.dnsServers = dnsServers
self.routes = routes
self.authToken = authToken self.authToken = authToken
self.peerId = peerId self.peerId = peerId
} }