Connecting To Tor From C++ (SOCKS5)

Sometimes you need to access the Tor Network from your C++ application, but you don't really want to bloat your app with some big networking library ,or simply you don't know how to do so.

Tor

Tor is free software for enabling anonymous communication. The name is an acronym derived from the original software project name The Onion Router. Tor directs Internet traffic through a free, worldwide, volunteer network consisting of more than six thousand relays to conceal a user's location and usage from anyone conducting network surveillance or traffic analysis. Wikipedia .

Connecting through Tor gives you the ability to hide your real IP address and location, well kind of since nothing is 100% safe anyways.

Installing Tor

On Linux you can simply use

sudo apt-get install tor 
or
sudo yum install tor

Note : your dist may be using some other package managment system

For Windows and Mac you can go ahead and download it from the Tor Website , or build it from source.  You only need Tor not the Tor Bundle, but you can still download that if need it.

SOCKS5

So what is SOCKS5 ?

Socket Secure (SOCKS) is an Internet protocol that routes network packets between a client and server through a proxy server. SOCKS5 additionally provides authentication so only authorized users may access a server.    Wikipedia.

A SOCKS5 server will acts as a proxy between you and the destination host, so it will forward packets sent from your computer to the other side of the connection and vice versa.  You can read more about it on Wikipedia or in the RFC .

Tor gives us a SOCKS5 interface so all we have to do is connect to the local proxy, send some commands, and then just normally send and receive packets and tor will forward  them for us.   So lets start coding .

At first we need to include our networking header. i use #ifdef's to detect the platform and include the right ones.

#ifdef _WIN32 // Windows

#include <winsock2.h>
#include <ws2tcpip.h>
#define MSG_NOSIGNAL 0

#else // Linux + Mac

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>
#include <err.h>

#define SOCKET_ERROR -1
#define INVALID_SOCKET -1 typedef int SOCKET;   
typedef sockaddr    SOCKADDR;   
typedef sockaddr_in SOCKADDR_IN;
#define closesocket close

#ifdef __APPLE__ 
#define MSG_NOSIGNAL 0
#endif #endif

#endif

Then we need to create a socket, set a socket address and connect to 127.0.0.1:9050, that's where the tor proxy normally listens.

int main()
{ 
// some windows only things
#ifdef _WIN32 WSADATA WsaData;
    WSAStartup(MAKEWORD(2, 2), &WsaData);
#endif
    SOCKET Socket;
    SOCKADDR_IN SocketAddr;
    Socket = socket(AF_INET, SOCK_STREAM, 0);
    SocketAddr.sin_family = AF_INET;
    SocketAddr.sin_port = htons(9050);
    SocketAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    connect(Socket, (SOCKADDR *)&SocketAddr, sizeof(SOCKADDR_IN));

Now to the actual SOCKS5 code.  The first thing to do when connecting to a SOCKS5 Proxy is sending the client greeting message, it consists of three parts

1 - SOCKS VERSION  
2 - NUMBER OF SUPPORTED AUTHENTICATIONS  
3 - SUPPORTED AUTHENTICATIONS

SOCKS VERSION will always be 0x05 since we are using SOCKS5. SOCKS5 supports about 5 authentication methods, but we will only use the simplest one since tor accepts it( No Authentication ). The second field will be 0x01 that means we only support one method. Field 3 will be set to 0x00 , 0x00 means no authentication. if we want to support another method we would need to set the second filed to 0x02 and send its code after the 0x00. The Tor deamon normally only listens on localhost so "No Athentication" isnt a problem.

char Req1[3] =
{
	0x05, // SOCKS 5
	0x01, // One Authentication Method
	0x00  // No AUthentication
};

send(Socket, Req1, 3, MSG_NOSIGNAL);

Next the server should reply with a 2 byte response.  First field will always be 0x05 and the second will have the authentication that the server chose. since we only sent one authentication method, the second field should have 0x00 in it, else there's an error.

char Resp1[2];
recv(Socket, Resp1, 2, 0);
if (Resp1[1] != 0x00)
	return (-1); // Error

The second thing to do is sending the Client Connection Request. This will tell the proxy server ( Tor ) to connect to a remote host for us then we will be able to send and receive data normally.  There are two ways to do this ( actually 3 but we will only use two ). using an IPV4 ADDRESS or a DOMAIN.

The Client Connection Request consists of 6 parts

1. SOCKS VERSION  
2. COMMAND  
3. RESERVED  
4. ADDRESS TYPE  
5. ADDRESS  
6. PORT 

SOCKS VERSION is 0x05 as always .

COMMAND is CONNECT wich is 0x01.

RESERVED is a reserved filed and should always be 0x00.

ADDRESS TYPE should be set to 0x01 for an IPV4 Address or 0x03 for a Domain.

ADDRESS is different for every Address Type. in IPV4 its a 4 byte array containing the IP Address in the network byte order.

Or if its a Domain it should be an array containing the domain length in the first byte and the actual domain in the rest.

PORT is 2 byte array containing the port in network byte order .

IPV4
char *IpAddr = "104.236.192.175";
int IpAddrInt = inet_addr(IpAddr);
short Port = htons(80);
char Req2[10] = {
    0x05, // SOCKS5
    0x01, // CONNECT
    0x00, // RESERVED
    0x01, // IPV4
};

// Copy the IP
memcpy(Req2 + 4, &IpAddrInt, 4);
// Copy The Port
memcpy(Req2 + 4 + 4, &Port, 2);
// Send
send(Socket, Req2, 10, 0);
Domain
char *Domain = "facebookcorewwwi.onion";
char DomainLen = (char)strlen(Domain);
short Port = htons(80);
char TmpReq[4] = {
    0x05, // SOCKS5
    0x01, // CONNECT
    0x00, // RESERVED
    0x03, // DOMAIN
};

char *Req2 = new char[4 + 1 + DomainLen + 2];
memcpy(Req2, TmpReq, 4);                // 5, 1, 0, 3
memcpy(Req2 + 4, &DomainLen, 1);        // Domain Length
memcpy(Req2 + 5, Domain, DomainLen);    // Domain
memcpy(Req2 + 5 + DomainLen, &Port, 2); // Port
send(Socket, (char *)Req2, 4 + 1 + DomainLen + 2, MSG_NOSIGNAL);
delete[] Req2;

After that the server will reply with a 10 byte reply,  we only care about the second byte as it will contain the status value.

char Resp2[10];
recv(Socket, Resp2, 10, 0);
if (Resp2[1] != 0x00)
{
	return (-1); // ERROR
}

Resp2 should be 0x00 if everything went OK, else some error happen,  you can check Wikipedia for error codes.

Now we have a working connection  and we can normally send recive data as we normally do.

Here's the Full Code :

// License : MIT
// Yaseen Mohamed Twati

#ifdef _WIN32           // Windows
#include <winsock2.h>
#include <ws2tcpip.h>
#define MSG_NOSIGNAL 0
#else                   // Linuc + Max

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>
#include <err.h>

#define SOCKET_ERROR    -1
#define INVALID_SOCKET  -1

typedef int         SOCKET;
typedef sockaddr    SOCKADDR;
typedef sockaddr_in SOCKADDR_IN;

#define closesocket close

#ifdef __APPLE__

#define MSG_NOSIGNAL 0

#endif

#endif

#include <iostream>

int main()
{
    std::cout << " [*] C++ Tor Exmaple " << std::endl;
    std::cout << " [*] By Yaseen MT " << std::endl;
    std::cout << " [*] Connecting " << std::endl;

    SOCKET      Socket;
    SOCKADDR_IN SocketAddr;

    Socket = socket(AF_INET, SOCK_STREAM, 0);
    SocketAddr.sin_family       = AF_INET;
    SocketAddr.sin_port         = htons(9050);
    SocketAddr.sin_addr.s_addr  = inet_addr("127.0.0.1");

    connect(Socket, (SOCKADDR*)&SocketAddr, sizeof(SOCKADDR_IN));

    char Req1[3] =
    {
        0x05, // SOCKS 5
        0x01, // One Authentication Method
        0x00  // No AUthentication
    };
    send(Socket, Req1, 3, MSG_NOSIGNAL);

    char Resp1[2];
    recv(Socket, Resp1, 2, 0);
    if(Resp1[1] != 0x00)
    {
        std::cout << " [*] Error Authenticating " << std::endl;
        return(-1); // Error
    }

#ifdef IPV4_TEST

    char* IpAddr = "1.1.1.1"; // replace this
    int   IpAddrInt = inet_addr(IpAddr);
    short Port = htons(80);

    char Req2[10] = {
         0x05, // SOCKS5
         0x01, // CONNECT
         0x00, // RESERVED
         0x01, // IPV4
        };
    // Copy the IP
    memcpy(Req2+4, &IpAddrInt, 4);
    // Copy The Port
    memcpy(Req2+4+4, &Port, 2);
    // Send
    send(Socket, Req2, 10, 0);

#else

    char* Domain = "facebookcorewwwi.onion";
    char  DomainLen = (char)strlen(Domain);
    short Port = htons(80);

    char TmpReq[4] = {
          0x05, // SOCKS5
          0x01, // CONNECT
          0x00, // RESERVED
          0x03, // DOMAIN
        };

    char* Req2 = new char[4 + 1 + DomainLen + 2];

    memcpy(Req2, TmpReq, 4);                // 5, 1, 0, 3
    memcpy(Req2 + 4, &DomainLen, 1);        // Domain Length
    memcpy(Req2 + 5, Domain, DomainLen);    // Domain
    memcpy(Req2 + 5 + DomainLen, &Port, 2); // Port

    send(Socket, (char*)Req2, 4 + 1 + DomainLen + 2, MSG_NOSIGNAL);

    delete[] Req2;

#endif

    char Resp2[10];
    recv(Socket, Resp2, 10, 0);
    if(Resp2[1] != 0x00)
    {
      std::cout << " [*] Error : " << Resp2[1] << std::endl;
      return(-1); // ERROR
    }

    std::cout << " [*] Connected " << std::endl;

    // Here you can normally use send and recv
    // Testing With a HTTP GET Request
    std::cout << " [*] Testing with GET Request \n" << std::endl;
    send(Socket, "GET / \n\r\n\r", strlen("GET / \n\r\n\r"), MSG_NOSIGNAL);
    char RecvBuffer[2048];
    size_t Rcved = recv(Socket, RecvBuffer, 2048, 0);
    std::cout.write(RecvBuffer, Rcved);

    std::cout << std::endl;

    return(0);
}

add this to the top of the file if you want to use IPV4 else it will use the domain.

#define IPV4_TEST