Back to blog
Building a Tor-like Anonymous Proxy in C
c
2025-02-20
30 min read

Building a Tor-like Anonymous Proxy in C

CNetworkingPrivacySecurityProxy

Building a Tor-like Anonymous Proxy in C

Privacy on the internet is becoming increasingly important. Today, we're going to build an anonymous proxy server in C that routes traffic through multiple layers, similar to how the Tor network works. This will help you understand the fundamentals of anonymous networking and onion routing.

What is an Anonymous Proxy?

An anonymous proxy acts as an intermediary between your computer and the internet. When you request a webpage, instead of connecting directly, your request goes through the proxy server first. The destination server only sees the proxy's IP address, not yours.

How Tor-like Routing Works

Tor (The Onion Router) uses a technique called "onion routing" where data is encrypted in multiple layers, like an onion. Each layer is decrypted by a different node in the network, making it extremely difficult to trace the original source.

Our simplified version will:

  1. Accept client connections
  2. Encrypt the request
  3. Forward it through intermediate proxies
  4. Decrypt and forward to the final destination
  5. Return the response through the same chain

Prerequisites

  • Basic understanding of C programming
  • Knowledge of sockets and networking
  • Understanding of encryption basics

Headers and Constants

dev@gcc:~/c
dev@gcc:~/cgcc> [object Object],
,[object Object],
,[object Object],
,[object Object],
,[object Object],
,[object Object],
,[object Object],
,[object Object],
,[object Object],

,[object Object],
,[object Object],
,[object Object],
,[object Object],

Understanding the Proxy Structure

dev@gcc:~/c
dev@gcc:~/cgcc> [object Object], ,[object Object],
    ,[object Object], ip[,[object Object],];
    ,[object Object], port;
    ,[object Object], socket_fd;
} ProxyNode;

,[object Object], ,[object Object],
    ProxyNode nodes[MAX_HOPS];
    ,[object Object], num_hops;
    ,[object Object], ,[object Object], encryption_key[AES_KEY_SIZE];
} ProxyChain;

Main Function: Setting Up the Proxy

dev@gcc:~/c
dev@gcc:~/cgcc> [object Object], ,[object Object],[object Object], {
    ,[object Object], server_fd;
    ,[object Object],

    ,[object Object],
    ,[object Object], ((server_fd = socket(AF_INET, SOCK_STREAM, ,[object Object],)) < ,[object Object],) {
        perror(,[object Object],);
        ,[object Object],(EXIT_FAILURE);
    }

    ,[object Object],
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(PROXY_PORT);

    ,[object Object],
    ,[object Object], (bind(server_fd, (,[object Object], sockaddr *)&server_addr, ,[object Object],(server_addr)) < ,[object Object],) {
        perror(,[object Object],);
        ,[object Object],(EXIT_FAILURE);
    }

    ,[object Object],
    ,[object Object], (listen(server_fd, ,[object Object],) < ,[object Object],) {
        perror(,[object Object],);
        ,[object Object],(EXIT_FAILURE);
    }

    ,[object Object],(,[object Object],, PROXY_PORT);

    ,[object Object],
    ,[object Object], (,[object Object],) {
        ,[object Object],
        ,[object Object], client_len = ,[object Object],(client_addr);
        ,[object Object], *client_fd = ,[object Object],(,[object Object],(,[object Object],));

        ,[object Object], ((*client_fd = accept(server_fd, (,[object Object], sockaddr *)&client_addr, &client_len)) < ,[object Object],) {
            perror(,[object Object],);
            ,[object Object],;
        }

        ,[object Object], thread_id;
        pthread_create(&thread_id, ,[object Object],, handle_client, (,[object Object], *)client_fd);
        pthread_detach(thread_id);
    }

    close(server_fd);
    ,[object Object], ,[object Object],;
}

Client Handler: Processing Requests

dev@gcc:~/c
dev@gcc:~/cgcc> [object Object], *,[object Object],[object Object], {
    ,[object Object], client_fd = *((,[object Object], *)arg);
    ,[object Object], buffer[BUFFER_SIZE];
    ,[object Object], bytes_received;

    ,[object Object],
    bytes_received = recv(client_fd, buffer, BUFFER_SIZE, ,[object Object],);
    ,[object Object], (bytes_received <= ,[object Object],) {
        close(client_fd);
        ,[object Object],(arg);
        ,[object Object], ,[object Object],;
    }

    ,[object Object],
    ,[object Object], method[,[object Object],], url[,[object Object],], version[,[object Object],];
    ,[object Object],(buffer, ,[object Object],, method, url, version);

    ,[object Object],
    ,[object Object], destination[,[object Object],];
    parse_url(url, destination);

    ,[object Object],
    ProxyChain chain;
    initialize_proxy_chain(&chain);

    ,[object Object],
    ,[object Object], *response = route_through_chain(&chain, buffer, bytes_received, destination);

    ,[object Object],
    ,[object Object], (response) {
        send(client_fd, response, ,[object Object],(response), ,[object Object],);
        ,[object Object],(response);
    }

    close(client_fd);
    ,[object Object],(arg);
    ,[object Object], ,[object Object],;
}

Encryption Functions

dev@gcc:~/c
dev@gcc:~/cgcc> [object Object], ,[object Object],[object Object], {
    AES_KEY aes_key;
    AES_set_encrypt_key(key, ,[object Object],, &aes_key);
    
    ,[object Object], (,[object Object], i = ,[object Object],; i < data_len; i += AES_BLOCK_SIZE) {
        AES_encrypt(data + i, encrypted + i, &aes_key);
    }
}

,[object Object], ,[object Object],[object Object], {
    AES_KEY aes_key;
    AES_set_decrypt_key(key, ,[object Object],, &aes_key);
    
    ,[object Object], (,[object Object], i = ,[object Object],; i < data_len; i += AES_BLOCK_SIZE) {
        AES_decrypt(encrypted + i, decrypted + i, &aes_key);
    }
}

Proxy Chain Routing

dev@gcc:~/c
dev@gcc:~/cgcc> [object Object], *,[object Object],[object Object], {
    ,[object Object], ,[object Object], *current_data = (,[object Object], ,[object Object], *)data;
    ,[object Object], current_len = data_len;

    ,[object Object],
    ,[object Object], (,[object Object], i = ,[object Object],; i < chain->num_hops; i++) {
        ,[object Object], ,[object Object], encrypted[BUFFER_SIZE];
        encrypt_data(current_data, current_len, chain->encryption_key, encrypted);

        ,[object Object],
        ,[object Object], proxy_fd = connect_to_proxy(&chain->nodes[i]);
        ,[object Object], (proxy_fd < ,[object Object],) {
            ,[object Object], ,[object Object],;
        }

        ,[object Object],
        send(proxy_fd, encrypted, current_len, ,[object Object],);

        ,[object Object],
        ,[object Object], response[BUFFER_SIZE];
        ,[object Object], response_len = recv(proxy_fd, response, BUFFER_SIZE, ,[object Object],);
        
        ,[object Object], (response_len > ,[object Object],) {
            ,[object Object],
            ,[object Object], ,[object Object], decrypted[BUFFER_SIZE];
            decrypt_data((,[object Object], ,[object Object], *)response, response_len, chain->encryption_key, decrypted);
            
            current_data = decrypted;
            current_len = response_len;
        }

        close(proxy_fd);
    }

    ,[object Object],
    ,[object Object], dest_fd = connect_to_destination(destination);
    ,[object Object], (dest_fd < ,[object Object],) {
        ,[object Object], ,[object Object],;
    }

    send(dest_fd, current_data, current_len, ,[object Object],);
    
    ,[object Object], *final_response = ,[object Object],(BUFFER_SIZE);
    ,[object Object], final_len = recv(dest_fd, final_response, BUFFER_SIZE, ,[object Object],);
    
    close(dest_fd);
    ,[object Object], final_response;
}

URL Parsing

dev@gcc:~/c
dev@gcc:~/cgcc> [object Object], ,[object Object],[object Object], {
    ,[object Object],
    ,[object Object], *host_start = ,[object Object],(url, ,[object Object],);
    ,[object Object], (host_start) {
        host_start += ,[object Object],;
    } ,[object Object], {
        host_start = url;
    }

    ,[object Object],
    ,[object Object], *port_start = ,[object Object],(host_start, ,[object Object],);
    ,[object Object], *path_start = ,[object Object],(host_start, ,[object Object],);

    ,[object Object], (port_start && (!path_start || port_start < path_start)) {
        ,[object Object],(destination, host_start, port_start - host_start);
        destination[port_start - host_start] = ,[object Object],;
    } ,[object Object], ,[object Object], (path_start) {
        ,[object Object],(destination, host_start, path_start - host_start);
        destination[path_start - host_start] = ,[object Object],;
    } ,[object Object], {
        ,[object Object],(destination, host_start);
    }
}

Connection Helpers

dev@gcc:~/c
dev@gcc:~/cgcc> [object Object], ,[object Object],[object Object], {
    ,[object Object], sock_fd = socket(AF_INET, SOCK_STREAM, ,[object Object],);
    ,[object Object], (sock_fd < ,[object Object],) {
        ,[object Object], ,[object Object],;
    }

    ,[object Object],
    proxy_addr.sin_family = AF_INET;
    proxy_addr.sin_port = htons(node->port);
    inet_pton(AF_INET, node->ip, &proxy_addr.sin_addr);

    ,[object Object], (connect(sock_fd, (,[object Object], sockaddr *)&proxy_addr, ,[object Object],(proxy_addr)) < ,[object Object],) {
        close(sock_fd);
        ,[object Object], ,[object Object],;
    }

    ,[object Object], sock_fd;
}

,[object Object], ,[object Object],[object Object], {
    ,[object Object], sock_fd = socket(AF_INET, SOCK_STREAM, ,[object Object],);
    ,[object Object], (sock_fd < ,[object Object],) {
        ,[object Object], ,[object Object],;
    }

    ,[object Object],
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons(,[object Object],); ,[object Object],
    inet_pton(AF_INET, destination, &dest_addr.sin_addr);

    ,[object Object], (connect(sock_fd, (,[object Object], sockaddr *)&dest_addr, ,[object Object],(dest_addr)) < ,[object Object],) {
        close(sock_fd);
        ,[object Object], ,[object Object],;
    }

    ,[object Object], sock_fd;
}

Initializing Proxy Chain

dev@gcc:~/c
dev@gcc:~/cgcc> [object Object], ,[object Object],[object Object], {
    ,[object Object],
    RAND_bytes(chain->encryption_key, AES_KEY_SIZE);

    ,[object Object],
    chain->num_hops = ,[object Object],;
    
    ,[object Object],(chain->nodes[,[object Object],].ip, ,[object Object],);
    chain->nodes[,[object Object],].port = ,[object Object],;
    
    ,[object Object],(chain->nodes[,[object Object],].ip, ,[object Object],);
    chain->nodes[,[object Object],].port = ,[object Object],;
    
    ,[object Object],(chain->nodes[,[object Object],].ip, ,[object Object],);
    chain->nodes[,[object Object],].port = ,[object Object],;
}

Security Considerations

  1. Key Management: In production, use proper key exchange protocols
  2. Authentication: Add node authentication to prevent MITM attacks
  3. Traffic Analysis: Implement padding to prevent traffic analysis
  4. Timing Attacks: Add random delays to prevent timing analysis

Testing

💻user@linux:~/bash
user@linux:~/bash$ [object Object],
gcc -o tor_proxy tor_proxy.c -lpthread -lssl -lcrypto

,[object Object],
./tor_proxy

,[object Object],
curl -x http://localhost:9050 http://example.com

Conclusion

You've built a simplified anonymous proxy that demonstrates the core concepts of onion routing. While this is a basic implementation, it shows how encryption layers and multiple hops can provide anonymity. For production use, consider using the actual Tor network or implementing more sophisticated security measures.

N

Nishant Gaurav

Full Stack Developer

Let Down (Choir Version) - Radiohead

0:00
0:00
nishant gaurav