Shalon

public class Shalon : NSObject, StreamDelegate

With this class you can connect to a target through a Shalon proxy. Connections to the proxy and connections to the target will each be encrypted using TLS. This means that your ISP or other people in your network cannot observe to which target you connect, because they only see connections to the proxy. The proxy itself does not see the content of your requests to the target. The target will only see the proxy’s IP address (except if the IP address is otherwise embedded into the request).

This implementation supports multiple hops. Simply add other layers.

In order to use Shalon proxies with your URLSession connections, please take look at ShalonURLProtocol.

Examples

let proxy1 = Target(withHostname: "shalon1.jondonym.net", andPort: 443)!
let proxy2 = Target(withHostname: "shalon2.jondonym.net", andPort: 443)!
let proxy3 = Target(withHostname: "shalon3.jondonym.net", andPort: 443)!
let target = Target(withHostname: "www.example.com", andPort: 443)!

let shalon = Shalon(withTarget: target)

shalon.addLayer(proxy3)
shalon.addLayer(proxy2)
shalon.addLayer(proxy1)

shalon.issue(request: Request(withMethod: .head, andUrl: url)!) {
    optionalResponse, optionalError in
    // TODO Do something
}

This will establish a nested tunnel, where proxy1 cannot see what the client sends to proxy2, as depicted:

client           proxy1           proxy2           proxy3          target
|                |                |                |               |
+----------------+                |                |               |
| CONNECT proxy2 |                |                |               |
+---------------------------------+                |               |
|                  CONNECT proxy3 |                |               |
+--------------------------------------------------+               |
|                                   CONNECT proxy4 |               |
+------------------------------------------------------------------+
|                                                    HEAD /        |
+------------------------------------------------------------------+
|                                                  |               |
+--------------------------------------------------+               |
|                                 |                |               |
+---------------------------------+                |               |
|                |                |                |               |
+----------------+                |                |               |
|                |                |                |               |
  • The state of the HTTP connection.

                  inactive
                      |
                      |
                      v
    +--- <Another layer available?> <---+
    |                 |                 |
    |                 |                 |
    |                 v                 |
    |  shouldEstablishTunnelConnection  |
    |                 |                 |
    |                 |                 |
    |                 v                 |
    | expectTunnelConnectionEstablished |
    |                 |                 |
    |                 +-----------------+
    |
    +-----------------+
                      |
                      v
            shouldSendHttpRequest
                      |
                      |
                      v
             expectHttpResponse
    
    See more

    Declaration

    Swift

    private enum State
  • A helper type for our callbacks, a function that gets either a HTTP response or an error.

    Declaration

    Swift

    public typealias CompletionHandler = (Http.Response?, Error?) -> Void
  • The state of the tunnel connection to the target.

    Declaration

    Swift

    private var state: Shalon.State
  • A list of hops. The last element is the actual target. All other elements are Shalon proxies. The first element is the proxy the user actually connects to.

    Declaration

    Swift

    private var targets: [Target]
  • A list of streams that are already established. The first element is the network stream to the first hop. All other elements are TLS encrypted streams to each hop (including the first one), i.e.

    1. TCP stream to first hop
    2. TLS stream to first hop
    3. TLS stream to second hop
    4. TLS stream to third hop…

    Declaration

    Swift

    private var streams: [PairedStream]
  • Returns the current layer. A layer is a TLS encrypted stream.

    Declaration

    Swift

    private var currentLayer: Int { get }
  • The HTTP request, that should be sent to the target.

    Declaration

    Swift

    private var request: Http.Request!
  • A callback that is called, once the HTTP response from the target has arrived or when an error occurred.

    Declaration

    Swift

    private var completionHandler: Shalon.CompletionHandler!
  • Construct a Shalon object with a given target. After calling this, proxies can be added by calling addLayer(_:).

    Warning

    If no layer is added, a direct connection will be made. Which means that the IP address of the current device will be visible to the target.

    Declaration

    Swift

    public init(withTarget target: Target)

    Parameters

    target

    The address of the server, to which requests should be issued.

  • This adds a proxy, which is connected before each other layer that was previously added, i.e., the last layer added will be the layer the initial conenction is made to.

    Declaration

    Swift

    public func addLayer(_ target: Target)

    Parameters

    target

    A proxy address.

  • Issue an HTTP request. The request is send through a TLS tunnel via proxy servers added with addLayer(_:).

    Warning

    If no layer was added with addLayer(_:), a direct connection to the target will be established. Which means that the IP address of the current device will be visible to the target.

    Postcondition

    Either the response or the error parameter of the completionHandler is set, the other one is nil.

    Declaration

    Swift

    public func issue(request: Http.Request, completionHandler: @escaping CompletionHandler)

    Parameters

    request

    The HTTP request.

    completionHandler

    A callback function. Its parameters are either an optional HTTP response or an error.

  • Helper function that performs a TLS handshake for the current layer. This can be the initial TCP connection established by this device, i.e. by issue(request:completionHandler:) or a TCP tunnel established through a proxy, i.e., if the state is expectTunnelConnectionEstablished.

    Declaration

    Swift

    private func wrapCurrentLayerWithTls()
  • Helper function that calls the completion handler with a specific error.

    Declaration

    Swift

    private func errorOccurred(_ error: GenericError)

    Parameters

    error

    A generic error.

  • Helper function that calls the completion handler with a specific error.

    Declaration

    Swift

    private func errorOccurred(_ error: Error)

    Parameters

    error

    An error.

  • Reset the internal state machine and remove all streams.

    Declaration

    Swift

    private func reset()
  • Parse the input stream and expect an HTTP response.

    Declaration

    Swift

    private func expectHttpResponse(fromStream stream: InputStream) -> Http.Response?

    Parameters

    stream

    The input stream.

    Return Value

    The HTTP response or nil if the response is invalid.

  • Send an HTTP request to an output stream.

    Declaration

    Swift

    private func send(request: Http.Request, toStream stream: OutputStream)

    Parameters

    request

    The HTTP request.

    stream

    The output stream.

  • The next state, which is determined by the number of proxies, to which a connection has not been established.

    Note

    Only call this, after a new direct or tunnelled connection has been established. The returned state might not make much sense, else.

    Declaration

    Swift

    private var nextState: State { get }
  • The index of the current target to which a tunnel has been established.

    Declaration

    Swift

    private var currentTargetIdx: Int { get }
  • The index of the next target.

    Declaration

    Swift

    private var nextTargetIdx: Int { get }
  • The current target.

    Declaration

    Swift

    private var currentTarget: Target { get }
  • The next target.

    Declaration

    Swift

    private var nextTarget: Target { get }
  • The current stream.

    Declaration

    Swift

    private var currentStream: PairedStream { get }