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.
See moreinactive | | v +--- <Another layer available?> <---+ | | | | | | | v | | shouldEstablishTunnelConnection | | | | | | | | v | | expectTunnelConnectionEstablished | | | | | +-----------------+ | +-----------------+ | v shouldSendHttpRequest | | v expectHttpResponse
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.
- TCP stream to first hop
- TLS stream to first hop
- TLS stream to second hop
- 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 isnil
.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 isexpectTunnelConnectionEstablished
.Declaration
Swift
private func wrapCurrentLayerWithTls()
-
Implementation of the
StreamDelegate
protocol.Declaration
Swift
public func stream(_ stream: Stream, handle eventCode: Stream.Event)
Parameters
stream
The stream on which streamEvent occurred.
handle
The stream event that occurred.
-
This is a convenience function, which is called if the stream in
stream(_:handle:)
is an input stream.Declaration
Swift
private func inputStream(handle eventCode: Stream.Event)
Parameters
handle
The stream event that occurred.
-
This is a convenience function, which is called if the stream in
stream(_:handle:)
is an output stream.Declaration
Swift
private func outputStream(handle eventCode: Stream.Event)
Parameters
handle
The stream event that occurred.
-
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 }