Building a Tor-like Anonymous Proxy in C
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:
- Accept client connections
- Encrypt the request
- Forward it through intermediate proxies
- Decrypt and forward to the final destination
- 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:~/cdev@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:~/cdev@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:~/cdev@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:~/cdev@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:~/cdev@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:~/cdev@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:~/cdev@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:~/cdev@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:~/cdev@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
- Key Management: In production, use proper key exchange protocols
- Authentication: Add node authentication to prevent MITM attacks
- Traffic Analysis: Implement padding to prevent traffic analysis
- Timing Attacks: Add random delays to prevent timing analysis
Testing
user@linux:~/bashuser@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.
Nishant Gaurav
Full Stack Developer
Related Posts
Write your own HTTP server from scratch using C
Learn how to build a complete HTTP server from scratch using C programming. Understand sockets, threading, and network programming fundamentals.
AI-First Development with Cursor
How AI-powered code editors like Cursor are revolutionizing the development workflow in 2025.