Description
The C++ TCP Proxy server is a simple and high performance utility using the ASIO
networking library, for proxying (tunneling or redirecting) connections from external clients to a
designated server. Some of the tasks the TCP Proxy Server can be used to easily and efficiently
accomplish are as follows:
- Limit the number of client connections to the server
- Load balance client connections between multiple server instances
- Provide IP or connection time based filtering and access control mechanisms
- Inspect (log), filter or otherwise modify data flowing between the clients and the server
How does it work?
The TCP proxy server acts as an intermediary in order to 'forward' TCP based
connections from external clients onto a singular remote server.
The communication flow in the direction from the client to the proxy to
the server is called the upstream flow, and the communication
flow in the direction from the server to the proxy to the client is called the
downstream flow.
Furthermore the up and down stream connections are consolidated into a
single concept known as a bridge. In the event either the downstream or
upstream end points disconnect, the proxy server will proceed to disconnect
the other end-point and eventually destroy the associated bridge.
Usage: ./tcpproxy_server <proxy machine ip> <proxy machine port> <server ip> <server port>
Example Use-Case (Simple Proxy Server)
A simple scenario is as follows: There exists a server at 192.168.0.100 that accepts
connections on port 20000, however due to firewall rules external clients can only
access a host at 192.168.20.200 on port 8080 (eth0), which coincidentally has
access to the 192.168.0 network segment via a second NIC (eth1). A solution for allowing
the external clients access to the server is to run the TCP proxy server on the host at
192.168.0.200 with the following configuration:
tcpproxy_server 192.168.20.200 8080 192.168.0.100 20000
The above command when run upon the proxy machine at 192.168.20.200, will bind to port
8080 on eth0 in order to accept connections from external clients - which presumbly will
originate from the firewall. Upon a new client connecting, the proxy will make a connection on
behalf of the client to the server residing at 192.168.0.100 on port 20000 via eth1,
and then proceed to send all incoming data from the client to the server and vice versa. Once
either party (client or server) disconnects, the proxy will immediately disconnect the other party.
C++ TCP Proxy License
Free use of the C++ TCP Proxy is permitted under the guidelines and in accordance with the Boost Software License
Compatibility
The C++ TCP Proxy implementation is compatible with the following C++ compilers:
- GNU Compiler Collection (3.5+)
- Clang/LLVM (1.1+)
- Microsoft Visual Studio C++ Compiler (7.1+)
- Intel® C++ Compiler (8.x+)
- AMD Optimizing C++ Compiler (1.2+)
- Nvidia C++ Compiler (19.x+)
- PGI C++ (10.x+)
- IBM XL C/C++ (9.x+)
- C++ Builder (XE4+)
Download
The proxy from an implementation aspect is primarily composed of three components
named the Acceptor, Session and the ASIO I/O Service proactor.
The acceptor and session components register with the I/O service requests and
associated completion handlers (callbacks) for reading and writing from
socket(s). The state diagram below depicts the the various completion handlers
and their relationship to the I/O service component. The turquoise blocks are
associated with the Acceptor, where as the purple blocks are associated
with the Session. For exposition purposes let's assume that the completion
handlers and the I/O service component are each a unique state in a state machine
that represents the TCP proxy.
The TCP proxy server is broken down into three functional 'groupings' denoted in the diagram
by the colours blue, green and red attached to the transitions between the states (completion
handlers) and the I/O service. The groupings are summarised as follows:
Phase | Transitions | Definition |
Blue | 1 - 8 |
Start-up and client connection instantiation phase.
Associated completion handlers:
- handle_accept
- handle_upstream_connect
|
Green | A1 - A4 |
Process data flow from remote server to proxy to client.
Associated completion handlers:
- handle_upstream_read
- handle_downstream_write
|
Red | B1 - B4 |
Process data flow from client to proxy to remote server.
Associated completion handlers:
- handle_downstream_read
- handle_upstream_write
|
Associated completion handlers:
handle_accept
handle_upstream_connect
In this phase the proxy itself is setup, which includes instantiating the Acceptor, binding-to
and listening in on the given IP and port number, and invoking the accept_connections
method, which in turn will register a completion handler with the I/O service, that will later
on be invoked when new connections are made to the proxy server.
When a client makes a connection to the proxy server, the handle_accept completion handler
will be invoked by the I/O service. This handler will then proceed to instantiate and start a
client session (bridge) instance. Once that is complete, it will then invoke accept_connections
which will complete the cycle by re-registering the handle_accept method with the I/O
service as the completion handler for any new connections.
Meanwhile when the start method on the client session was invoked during the handle_accept
call, it immediately attempted to asynchronously establish a connection with the remote server.
When the remote server accepts the connection, the I/O service will then invoke the handle_upstream_connect
completion handler. This handler will in turn proceed to register two asynchronous read requests
coupled with the completion handlers handle_downstream_read and handle_upstream_read
with the I/O service, one for data coming from the client, the other being for data coming from the
remote server respectively.
Based on which end-point data arrives at the proxy, one of the following phases will be engaged:
Associated completion handlers:
handle_upstream_read
handle_downstream_write
This phase is engaged when data from the Remote Server (aka up-stream end point)
arrives at the proxy. Once some amount of data is ready, the I/O service will invoke the
handle_upstream_read completion handler. This handler will in turn take the data
and register an asynchronous write request with the I/O service in order to send the data
to the Client end-point. Once the write request has completed, the I/O service will invoke
the handle_downstream_write completion handler. This handler will complete the cycle
for the green phase by re-registering with the I/O service an asynchronous read request from
the upstream end-point coupled with the handle_upstream_read method as the associated
completion handler.
Associated completion handlers:
handle_downstream_read
handle_upstream_write
This phase is engaged when data from the Client (aka down-stream end point)
arrives at the proxy. Once some amount of data is ready, the I/O service will invoke the
handle_downstream_read completion handler. This handler will in turn take the data
and register an asynchronous write request with the I/O service in order to send the data
to the Remote Server end-point. Once the write request has been completed, the I/O service
will invoke the handle_upstream_write completion handler. This handler will complete
the cycle for the red phase by re-registering with the I/O service an asynchronous read
request from the downstream end-point coupled with the handle_downstream_read method
as the associated completion handler.
When either of the end points terminate their respective connection to the proxy, the proxy will
proceed to close (or shutdown) the other corresponding connection. This includes releasing
any outstanding asynchronous requests, culminating in the reference count of the bridge (client session)
reaching zero at which point the bridge instance itself will subsequently have its destructor called.
|