tunnel_server/UDPServerConnection.swift
/* |
Copyright (C) 2016 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
This file contains the UDPServerConnection class. The UDPServerConnection class handles the encapsulation and decapsulation of datagrams in the server side of the SimpleTunnel tunneling protocol. |
*/ |
import Foundation |
import Darwin |
/// An object representing the server side of a logical flow of UDP network data in the SimpleTunnel tunneling protocol. |
class UDPServerConnection: Connection { |
// MARK: Properties |
/// The address family of the UDP socket. |
var addressFamily: Int32 = AF_UNSPEC |
/// A dispatch source for reading data from the UDP socket. |
var responseSource: dispatch_source_t? |
// MARK: Initializers |
override init(connectionIdentifier: Int, parentTunnel: Tunnel) { |
super.init(connectionIdentifier: connectionIdentifier, parentTunnel: parentTunnel) |
} |
deinit { |
if responseSource != nil { |
dispatch_source_cancel(responseSource!) |
} |
} |
// MARK: Interface |
/// Convert a sockaddr structure into an IP address string and port. |
func getEndpointFromSocketAddress(socketAddressPointer: UnsafePointer<sockaddr>) -> (host: String, port: Int)? { |
let socketAddress = UnsafePointer<sockaddr>(socketAddressPointer).memory |
switch Int32(socketAddress.sa_family) { |
case AF_INET: |
var socketAddressInet = UnsafePointer<sockaddr_in>(socketAddressPointer).memory |
let length = Int(INET_ADDRSTRLEN) + 2 |
var buffer = [CChar](count: length, repeatedValue: 0) |
let hostCString = inet_ntop(AF_INET, &socketAddressInet.sin_addr, &buffer, socklen_t(length)) |
let port = Int(UInt16(socketAddressInet.sin_port).byteSwapped) |
return (String.fromCString(hostCString)!, port) |
case AF_INET6: |
var socketAddressInet6 = UnsafePointer<sockaddr_in6>(socketAddressPointer).memory |
let length = Int(INET6_ADDRSTRLEN) + 2 |
var buffer = [CChar](count: length, repeatedValue: 0) |
let hostCString = inet_ntop(AF_INET6, &socketAddressInet6.sin6_addr, &buffer, socklen_t(length)) |
let port = Int(UInt16(socketAddressInet6.sin6_port).byteSwapped) |
return (String.fromCString(hostCString)!, port) |
default: |
return nil |
} |
} |
/// Create a UDP socket |
func createSocketWithAddressFamilyFromAddress(address: String) -> Bool { |
var sin = sockaddr_in() |
var sin6 = sockaddr_in6() |
var newSocket: Int32 = -1 |
if address.withCString({ cstring in inet_pton(AF_INET6, cstring, &sin6.sin6_addr) }) == 1 { |
// IPv6 peer. |
newSocket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP) |
addressFamily = AF_INET6 |
} |
else if address.withCString({ cstring in inet_pton(AF_INET, cstring, &sin.sin_addr) }) == 1 { |
// IPv4 peer. |
newSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) |
addressFamily = AF_INET |
} |
guard newSocket > 0 else { return false } |
guard let newResponseSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, UInt(newSocket), 0, dispatch_get_main_queue()) else { |
close(newSocket) |
return false |
} |
dispatch_source_set_cancel_handler(newResponseSource) { |
simpleTunnelLog("closing udp socket for connection \(self.identifier)") |
let UDPSocket = Int32(dispatch_source_get_handle(newResponseSource)) |
close(UDPSocket) |
} |
dispatch_source_set_event_handler(newResponseSource) { |
guard let source = self.responseSource else { return } |
var socketAddress = sockaddr_storage() |
var socketAddressLength = socklen_t(sizeof(sockaddr_storage.self)) |
let response = [UInt8](count: 4096, repeatedValue: 0) |
let UDPSocket = Int32(dispatch_source_get_handle(source)) |
let bytesRead = withUnsafeMutablePointer(&socketAddress) { |
recvfrom(UDPSocket, UnsafeMutablePointer<Void>(response), response.count, 0, UnsafeMutablePointer($0), &socketAddressLength) |
} |
guard bytesRead >= 0 else { |
if let errorString = String(UTF8String: strerror(errno)) { |
simpleTunnelLog("recvfrom failed: \(errorString)") |
} |
self.closeConnection(.All) |
return |
} |
guard bytesRead > 0 else { |
simpleTunnelLog("recvfrom returned EOF") |
self.closeConnection(.All) |
return |
} |
guard let endpoint = withUnsafePointer(&socketAddress, { self.getEndpointFromSocketAddress(UnsafePointer($0)) }) else { |
simpleTunnelLog("Failed to get the address and port from the socket address received from recvfrom") |
self.closeConnection(.All) |
return |
} |
let responseDatagram = NSData(bytes: UnsafePointer<Void>(response), length: bytesRead) |
simpleTunnelLog("UDP connection id \(self.identifier) received = \(bytesRead) bytes from host = \(endpoint.host) port = \(endpoint.port)") |
self.tunnel?.sendDataWithEndPoint(responseDatagram, forConnection: self.identifier, host: endpoint.host, port: endpoint.port) |
} |
dispatch_resume(newResponseSource) |
responseSource = newResponseSource |
return true |
} |
/// Send a datagram to a given host and port. |
override func sendDataWithEndPoint(data: NSData, host: String, port: Int) { |
if responseSource == nil { |
guard createSocketWithAddressFamilyFromAddress(host) else { |
simpleTunnelLog("UDP ServerConnection initialization failed.") |
return |
} |
} |
guard let source = responseSource else { return } |
let UDPSocket = Int32(dispatch_source_get_handle(source)) |
let sent: Int |
switch addressFamily { |
case AF_INET: |
let serverAddress = SocketAddress() |
guard serverAddress.setFromString(host) else { |
simpleTunnelLog("Failed to convert \(host) into an IPv4 address") |
return |
} |
serverAddress.setPort(port) |
sent = withUnsafePointer(&serverAddress.sin) { |
sendto(UDPSocket, data.bytes, data.length, 0, UnsafePointer($0), socklen_t(serverAddress.sin.sin_len)) |
} |
case AF_INET6: |
let serverAddress = SocketAddress6() |
guard serverAddress.setFromString(host) else { |
simpleTunnelLog("Failed to convert \(host) into an IPv6 address") |
return |
} |
serverAddress.setPort(port) |
sent = withUnsafePointer(&serverAddress.sin6) { |
sendto(UDPSocket, data.bytes, data.length, 0, UnsafePointer($0), socklen_t(serverAddress.sin6.sin6_len)) |
} |
default: |
return |
} |
guard sent > 0 else { |
if let errorString = String(UTF8String: strerror(errno)) { |
simpleTunnelLog("UDP connection id \(identifier) failed to send data to host = \(host) port \(port). error = \(errorString)") |
} |
closeConnection(.All) |
return |
} |
if sent == data.length { |
// Success |
simpleTunnelLog("UDP connection id \(identifier) sent \(data.length) bytes to host = \(host) port \(port)") |
} |
} |
/// Close the connection. |
override func closeConnection(direction: TunnelConnectionCloseDirection) { |
super.closeConnection(direction) |
if let source = responseSource where isClosedForWrite && isClosedForRead { |
dispatch_source_cancel(source) |
responseSource = nil |
} |
} |
} |
Copyright © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-10-04