Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IPv6 support #22

Merged
merged 33 commits into from May 2, 2016
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
ae95476
Extending Types, Bytes and Address data types in order to support IPv…
MatthiasKreileder Mar 25, 2016
a20ec37
Adding IPv6 hexadecimal representation to colon separated string repr…
MatthiasKreileder Mar 27, 2016
e27a7b6
Removing false Cocoa dependency
MatthiasKreileder Mar 28, 2016
213f00d
Starting to build a IPv6/IPv4 capable TCP socket
MatthiasKreileder Mar 28, 2016
9d6f10a
Merge branch 'master' into addIPv6Support
MatthiasKreileder Apr 2, 2016
eb62931
Adding test for name resolution
MatthiasKreileder Apr 3, 2016
9eac4bb
Simplifying getaddrinfo argument creation(still not working though)
MatthiasKreileder Apr 4, 2016
4ae18f0
work in progress... trying to make successful calls to getaddrinfo()
MatthiasKreileder Apr 4, 2016
92ddc05
Use typealias to refer to a type instead of a variable
czechboy0 Apr 4, 2016
945afd0
Removing depricated cast method
MatthiasKreileder Apr 4, 2016
2a26df1
Adding SocketConfig struct. Enables application code to guide the nam…
MatthiasKreileder Apr 5, 2016
ea2d414
Finished first version of TCP Client with IPv4/IPv6 support
MatthiasKreileder Apr 5, 2016
628f711
Adding comments
MatthiasKreileder Apr 11, 2016
66af97f
Adding comments for code review
MatthiasKreileder Apr 11, 2016
4e9b7e1
Changing Internet Address representation, Name to Address resolving, …
MatthiasKreileder Apr 12, 2016
c37a511
Adding KclInternetSocket class as a prototype for an IPv4/IPv6 capabl…
MatthiasKreileder Apr 13, 2016
93b2154
Supporting IPv6 on the Server side
MatthiasKreileder Apr 24, 2016
c1fcbf3
Completing IPv6 support for SocksCore
MatthiasKreileder Apr 25, 2016
a0b37e6
Updating swift version
MatthiasKreileder Apr 25, 2016
12e6d3f
Updating Sources/Socks, Tests and Examples to IPv6 support modifications
MatthiasKreileder Apr 25, 2016
07be0bb
Removing deprecated conversion function
MatthiasKreileder Apr 25, 2016
bf0badd
Adding manual casting for Linux
MatthiasKreileder Apr 25, 2016
af83c46
Adding manual casting for Linux Part 2 :)
MatthiasKreileder Apr 25, 2016
8fd0536
Adding comments, removing deprecated code
MatthiasKreileder Apr 25, 2016
520149a
Removing code that is no longer needed
MatthiasKreileder Apr 25, 2016
21e4539
Merge branch 'master' into addIPv6Support
MatthiasKreileder Apr 25, 2016
715890b
Renaming Internet_Address to InternetAddress
MatthiasKreileder Apr 26, 2016
dcec368
Incorporating requested changes that were made after the pull request…
MatthiasKreileder Apr 27, 2016
bb23498
Moving Address Family resolution for SocketConfig from RawSocket to I…
MatthiasKreileder Apr 27, 2016
73ae0d6
Cleaning up: Removing SocketFactory.swift, removing wrong whitespace …
MatthiasKreileder Apr 27, 2016
ad298b4
Adding static functions to SocketConfig for "easy initialising". Chan…
MatthiasKreileder Apr 27, 2016
8482ffb
Changing the "Created by" comment in the two files which contain the …
MatthiasKreileder May 2, 2016
9c70732
Cosmetic changes before merging
czechboy0 May 2, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 7 additions & 12 deletions Sources/Socks/InternetActor.swift
Expand Up @@ -10,25 +10,20 @@ 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: Internet_Address

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

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

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

self.socket = try InternetSocket(socketConfig: self.config, address: self.address)
return self.socket!
}
}
Expand Down
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 : Internet_Address)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
28 changes: 28 additions & 0 deletions Sources/Socks/SocketFactory.swift
@@ -0,0 +1,28 @@
//
// SocketFactory.swift
// Socks
//
// Created by Matthias Kreileder on 28/03/2016.
//
//

import Foundation
import SocksCore

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

// DEPRECATED
/*
public class SocketFactory {


}
*/
8 changes: 4 additions & 4 deletions Sources/Socks/SynchronousTCPServer.swift
Expand Up @@ -10,11 +10,11 @@ import SocksCore

public class SynchronousTCPServer: SynchronousServer {

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

let server = try InternetServer(hostname: hostname, port: port) {
return try RawSocket.TCP()
}
let socketConfig = SocketConfig(addressFamily: .UNSPECIFIED, socketType: .Stream, protocolType: .TCP)

let server = try InternetServer(socketConfig : socketConfig, internetAddress: internetAddress)
super.init(server: server)
}
}
9 changes: 5 additions & 4 deletions Sources/Socks/TCPClient.swift
Expand Up @@ -10,9 +10,10 @@ 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 : Internet_Address) throws {

let socketConfig = SocketConfig(addressFamily: .UNSPECIFIED, socketType: .Stream, protocolType: .TCP)

try super.init(socketConfig : socketConfig, 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 : Internet_Address) {
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
153 changes: 114 additions & 39 deletions Sources/SocksCore/Address+C.swift
Expand Up @@ -8,55 +8,128 @@

#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 : Internet_Address) -> Array<ResolvedInternetAddress>
}

extension InternetAddress {
public struct Resolver : InternetAddressResolver{
private let config : SocketConfig
public init(config : SocketConfig){
self.config = config
}

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
public func resolve(internetAddress : Internet_Address) -> Array<ResolvedInternetAddress>{
let resolvedInternetAddressesArray = try!resolveHostnameAndServiceToIPAddresses(socketConfig: self.config, internetAddress: internetAddress)
//
// TODO: Consider try and catch or other tests (if array contains 0 elements or something like that)
//
return resolvedInternetAddressesArray
}

private static func getAddressFromHostname(hostname: String) throws -> in_addr {
private func resolveHostnameAndServiceToIPAddresses(socketConfig : SocketConfig,
internetAddress : Internet_Address) throws
-> Array<ResolvedInternetAddress>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Array<ResolvedInternetAddress> -> [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 = Array<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
}

}

// Brief: Given given a hostname and a service this methods return 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
//
// socketConfig - 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.
//
// hostname - e.g. "www.google.com" or "localhost"
//
// service - can be a service string e.g. "echo" or a well-know port e.g. "7"

// Return Array of ResolvedInternetAddress

// Resolver guts
public func resolveHostnameAndServiceToIPAddresses(socketConfig : SocketConfig,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably can be removed

hostname : String,
service : String)throws
-> UnsafeMutablePointer<addrinfo>
{
//
// 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.
var servinfo = UnsafeMutablePointer<socket_addrinfo>.init(nil)

let getaddrinfoReturnValue = getaddrinfo(hostname, service, &addressCriteria, &servinfo)
guard getaddrinfoReturnValue == 0 else { throw Error(.IPAddressValidationFailed) }

// Wrap linked list into array of ResolvedInternetAddress

// Prevent memory leaks: getaddrinfo creates an unmanaged linked list on the heap
// freeaddrinfo(servinfo)

return servinfo!
}

//Pointer casting
Expand All @@ -65,6 +138,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)
}


63 changes: 46 additions & 17 deletions Sources/SocksCore/Address.swift
Expand Up @@ -12,32 +12,61 @@
import Darwin
#endif

/*
// Generic data type for specifying addresses associated with a socket
// Note: That does not necessarily mean internet addresses
struct RawAddress {
let family: AddressFamily
let bytes: Bytes14
}

let family: AddressFamily // e.g. AF_INET, which means IPv4
let bytes: Bytes14 // Family-specific address information
}*/
/*
// Eventually we will make calls to the berkley sockets api provided by the kernel.
// Hence, address objects need to be convertable to the sockaddr structure.
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
public let port: Port
//
// Brief: Specify an internet address
//
// Example of the (assumend) main use case:
// assign a string to the hostname e.g. www.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 Internet_Address {
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 unresoved InternetAddress
private let internetAddress : Internet_Address
public var InternetAddress : Internet_Address{
return internetAddress
}

public let resolvedCTypeAddress : addrinfo

public init(internetAddress : Internet_Address, resolvedCTypeAddress : addrinfo){
self.internetAddress = internetAddress
self.resolvedCTypeAddress = resolvedCTypeAddress
}
}