Skip to content

Commit

Permalink
IPv6 support (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
MatthiasKreileder authored and czechboy0 committed May 2, 2016
1 parent 711f9e5 commit 5d104d4
Show file tree
Hide file tree
Showing 24 changed files with 548 additions and 230 deletions.
26 changes: 12 additions & 14 deletions Sources/Socks/InternetActor.swift
Expand Up @@ -10,26 +10,24 @@ import SocksCore

public class InternetActor: Actor {

let hostname: String
let port: Int
let rawSocketProvider: () throws -> RawSocket

private var socket: InternetSocket? = nil

public init(hostname: String, port: Int, rawSocketProvider: () throws -> RawSocket) throws {
self.hostname = hostname
self.port = port
self.rawSocketProvider = rawSocketProvider
let config: SocketConfig
let address: InternetAddress

public init(socketConfig: SocketConfig, internetAddress: InternetAddress) throws {
self.config = socketConfig
self.address = internetAddress
}

public func getSocket() throws -> Socket {
guard self.socket == nil else { return self.socket! }
if let socket = self.socket {
return socket
}

let raw = try self.rawSocketProvider()
let address = InternetAddress(address: .Hostname(self.hostname), port: UInt16(self.port))
self.socket = InternetSocket(rawSocket: raw, address: address)

return self.socket!
let socket = try InternetSocket(socketConfig: self.config, address: self.address)
self.socket = socket
return socket
}
}

4 changes: 2 additions & 2 deletions Sources/Socks/InternetClient.swift
Expand Up @@ -10,8 +10,8 @@ import SocksCore

public class InternetClient: InternetActor, Client {

override init(hostname: String, port: Int, rawSocketProvider: () throws -> RawSocket) throws {
try super.init(hostname: hostname, port: port, rawSocketProvider: rawSocketProvider)
override init(socketConfig: SocketConfig, internetAddress: InternetAddress) throws {
try super.init(socketConfig : socketConfig, internetAddress : internetAddress)
guard let clientSocket = try self.getSocket() as? ClientSocket else {
fatalError("Usage error: socket should be a ClientSocket")
}
Expand Down
6 changes: 2 additions & 4 deletions Sources/Socks/SynchronousTCPServer.swift
Expand Up @@ -10,11 +10,9 @@ import SocksCore

public class SynchronousTCPServer: SynchronousServer {

public init(hostname: String, port: Int) throws {
public init(internetAddress : InternetAddress) throws {

let server = try InternetServer(hostname: hostname, port: port) {
return try RawSocket.TCP()
}
let server = try InternetServer(socketConfig: .TCP(), internetAddress: internetAddress)
super.init(server: server)
}
}
7 changes: 3 additions & 4 deletions Sources/Socks/TCPClient.swift
Expand Up @@ -10,9 +10,8 @@ import SocksCore

public class TCPClient: InternetClient {

public init(hostname: String, port: Int) throws {
try super.init(hostname: hostname, port: port) {
return try RawSocket(protocolFamily: .Inet, socketType: .Stream, protocol: .TCP)
}
public init(internetAddress: InternetAddress) throws {

try super.init(socketConfig: .TCP(), internetAddress: internetAddress)
}
}
10 changes: 9 additions & 1 deletion Sources/Socks/UDPClient.swift
Expand Up @@ -10,10 +10,18 @@ import SocksCore

public class UDPClient: InternetClient {

init(hostname: String, port: Int) {
init(internetAddress: InternetAddress) {
fatalError("Unimplemented")
// super.init(hostname: hostname, port: port) {
//THIS is where we'll create the UDP client, not implemented yet
//
// Please note: SocksCore got changed (have a look into TCPClient.swift
// for details, because it is a similar situation)
// Brief: Create a SocketConfig object with
// - SocketConfig(addressFamily: .UNSPECIFIED, socketType: .Datagram, protocolType: .UDP)
// and then call
// - try super.init(socketConfig : socketConfig, internetAddress : internetAddress)
//
// return try RawSocket(protocolFamily: .Inet, socketType: .Datagram, protocol: .UDP)
// }
}
Expand Down
114 changes: 74 additions & 40 deletions Sources/SocksCore/Address+C.swift
Expand Up @@ -2,61 +2,93 @@
// Address+C.swift
// Socks
//
// Created by Honza Dvorsky on 3/20/16.
// Created by Matthias Kreileder on 3/20/16.
//
//

#if os(Linux)
import Glibc
typealias socket_addrinfo = Glibc.addrinfo
#else
import Darwin
typealias socket_addrinfo = Darwin.addrinfo
#endif

//Pretty types -> C types

protocol InternetAddressResolver {
func resolve(internetAddress: InternetAddress) throws -> [ResolvedInternetAddress]
}

extension InternetAddress {
// Brief: Given a hostname and a service this struct returns a list of
// IP and Port adresses that where obtained during the name resolution
// e.g. "localhost" and "echo" as arguments will result in a list of
// IP addresses of the machine that runs the program and port set to 7
//
struct Resolver: InternetAddressResolver{
private let config: SocketConfig

func toCType() throws -> sockaddr {

var addr = sockaddr_in()

switch self.address {
case .Hostname(let hostname):
//hostname must be converted to ip
addr.sin_addr = try InternetAddress.getAddressFromHostname(hostname: hostname)
case .IPv4(let ipBytes4):
//we got an IP, validate it
let str = ipBytes4.toArray().periodSeparatedString()
guard inet_pton(AF_INET, str, &addr.sin_addr) == 1 else {
throw Error(ErrorReason.IPAddressValidationFailed)
}
}

addr.sin_family = sa_family_t(AF_INET)
addr.sin_port = in_port_t(htons(value: in_port_t(self.port)))
addr.sin_zero = (0, 0, 0, 0, 0, 0, 0, 0)

let res = sockaddr_cast(p: &addr).pointee
return res

// config - the provided SocketConfig object guides the name resolution
// the socketType and protocolType fields control which kind
// kind of socket you want to create.
// E.g. set them to .STREAM .TCP to obtain address for a TCP Stream socket
// - Set the addressFamily field to .UNSPECIFIED if you don't care if the
// name resolution leads to IPv4 or IPv6 addresses.
init(config: SocketConfig){
self.config = config
}

func resolve(internetAddress: InternetAddress) throws -> [ResolvedInternetAddress] {
let resolvedInternetAddressesArray = try Resolver._resolve(socketConfig: self.config, internetAddress: internetAddress)
return resolvedInternetAddressesArray
}

private static func getAddressFromHostname(hostname: String) throws -> in_addr {
private static func _resolve(socketConfig: SocketConfig, internetAddress: InternetAddress) throws -> [ResolvedInternetAddress] {
//
// Narrowing down the results we will get from the getaddrinfo call
//
var addressCriteria = socket_addrinfo.init()
// IPv4 or IPv6
addressCriteria.ai_family = socketConfig.addressFamily.toCType()
addressCriteria.ai_flags = AI_PASSIVE
addressCriteria.ai_socktype = socketConfig.socketType.toCType()
addressCriteria.ai_protocol = socketConfig.protocolType.toCType()

// The list of addresses that correspond to the hostname/service pair.
// servinfo is the first node in a linked list of addresses that is empty
// at this line
var servinfo = UnsafeMutablePointer<socket_addrinfo>.init(nil)
// perform resolution
let getaddrinfoReturnValue = getaddrinfo(internetAddress.hostname, internetAddress.port.toString(), &addressCriteria, &servinfo)
guard getaddrinfoReturnValue == 0 else { throw Error(.IPAddressValidationFailed) }

// Wrap linked list into array of ResolvedInternetAddress

// we need to remember the head of the linked list to clean up the consumed memory on the head
let head = servinfo

let _hostInfo = gethostbyname(hostname)
guard _hostInfo != nil else {
throw Error(.FailedToGetIPFromHostname(hostname))
}
let hostInfo = _hostInfo.pointee
guard hostInfo.h_addrtype == AF_INET else {
throw Error(.FailedToGetIPFromHostname("No IPv4 address"))
}
guard hostInfo.h_addr_list != nil else {
throw Error(.FailedToGetIPFromHostname("List is empty"))
}
var resolvedInternetAddressesArray = [ResolvedInternetAddress]()
while(servinfo != nil){
let singleAddress = ResolvedInternetAddress(internetAddress: internetAddress, resolvedCTypeAddress: (servinfo?.pointee)!)
resolvedInternetAddressesArray.append(singleAddress)
servinfo = servinfo?.pointee.ai_next
}

//
// FIXME: The dynamically allocated linked list of socket_addrinfo objects
// should be deleted from the heap in order to prevent memory leaks
// However, when I [Matthias Kreileder] uncomment the line 'freeaddrinfo(head)'
// my code crashes at runtime :(
// In the code above I tried to COPY the socket_addrinfo into an array
// so that I can (in theory) safely free the memory allocated on the heap.
//
// Prevent memory leaks: getaddrinfo creates an unmanaged linked list on the heap
//freeaddrinfo(head)

let addrStruct = sockadd_list_cast(p: hostInfo.h_addr_list)[0].pointee
return addrStruct
return resolvedInternetAddressesArray
}

}

//Pointer casting
Expand All @@ -65,6 +97,8 @@ func sockaddr_cast(p: UnsafeMutablePointer<Void>) -> UnsafeMutablePointer<sockad
return UnsafeMutablePointer<sockaddr>(p)
}

func sockadd_list_cast(p: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>) -> UnsafeMutablePointer<UnsafeMutablePointer<in_addr>> {
return UnsafeMutablePointer<UnsafeMutablePointer<in_addr>>(p)
func sockaddr_storage_cast(p : UnsafeMutablePointer<Void>?) -> UnsafeMutablePointer<sockaddr>? {
return UnsafeMutablePointer<sockaddr>(p)
}


58 changes: 34 additions & 24 deletions Sources/SocksCore/Address.swift
Expand Up @@ -2,7 +2,7 @@
// Address.swift
// Socks
//
// Created by Honza Dvorsky on 3/20/16.
// Created by Matthias Kreileder on 3/20/16.
//
//

Expand All @@ -12,32 +12,42 @@
import Darwin
#endif

struct RawAddress {
let family: AddressFamily
let bytes: Bytes14
}

protocol Address {
func toCType() throws -> sockaddr
}

// Internet address

typealias RawInternetAddress = Int32

public enum InternetAddressType {
case Hostname(String)
case IPv4(Bytes4)
}

public struct InternetAddress: Address {

public let address: InternetAddressType
//
// Brief: Specify an internet address
//
// Example of the (assumed) main use case:
// assign a string to the hostname e.g. google.com
// and specify the Port via an integer or a service name
//
// hostname - can be set to a string that denotes
// a hostname e.g. "localhost" or
// an IPv4 address e.g. "127.0.0.1" or
// an IPv6 address e.g. "::1"
//
// port - see comments for Port enum
//
public struct InternetAddress {
public let hostname: String
public let port: Port

public init(address: InternetAddressType, port: Port) {
self.address = address
public init(hostname: String, port: Port) {
self.hostname = hostname
self.port = port
}
}

public struct ResolvedInternetAddress {

// The unresolved InternetAddress
let internetAddress: InternetAddress
let resolvedCTypeAddress: addrinfo

func addressFamily() throws -> AddressFamily {
return try AddressFamily(fromCType: resolvedCTypeAddress.ai_family)
}

init(internetAddress: InternetAddress, resolvedCTypeAddress: addrinfo){
self.internetAddress = internetAddress
self.resolvedCTypeAddress = resolvedCTypeAddress
}
}
27 changes: 0 additions & 27 deletions Sources/SocksCore/Bytes.swift
Expand Up @@ -12,33 +12,6 @@
import Darwin
#endif

public struct Bytes4 {

public let raw: (UInt8, UInt8, UInt8, UInt8)

public init(raw: (UInt8, UInt8, UInt8, UInt8)) {
self.raw = raw
}

public static func fromArray(array a: [UInt8]) -> Bytes4 {
assert(a.count == 4, "Array must have exactly 4 elements")
return Bytes4(raw: (a[0], a[1], a[2], a[3]))
}

func toArray() -> [UInt8] {
return [raw.0, raw.1, raw.2, raw.3]
}
}

struct Bytes14 {
let raw: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8)

static func fromArray(array a: [UInt8]) -> Bytes14 {
assert(a.count == 14, "Array must have exactly 14 elements")
return Bytes14(raw: (a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13]))
}
}

let BufferCapacity = 512

class Bytes {
Expand Down
3 changes: 3 additions & 0 deletions Sources/SocksCore/Error.swift
Expand Up @@ -28,6 +28,9 @@ public enum ErrorReason {
case ListenFailed
case AcceptFailed

case UnsupportedSocketAddressFamily(Int32)
case ConcreteSocketAddressFamilyRequired

case Generic(String)
}

Expand Down
4 changes: 1 addition & 3 deletions Sources/SocksCore/InternetSocket+Client.swift
Expand Up @@ -17,9 +17,7 @@
extension InternetSocket : ClientSocket {

public func connect() throws {

var addr = try self.address.toCType()
let res = socket_connect(self.descriptor, &addr, socklen_t(sizeof(sockaddr)))
let res = socket_connect(self.descriptor, address.resolvedCTypeAddress.ai_addr, address.resolvedCTypeAddress.ai_addrlen)
guard res > -1 else { throw Error(.ConnectFailed) }
}
}

0 comments on commit 5d104d4

Please sign in to comment.