feat: Initial code
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
obj/
|
||||||
|
fireproxy
|
||||||
28
Makefile
Normal file
28
Makefile
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
CC = gcc
|
||||||
|
CFLAGS = -Wall -Wextra -pthread
|
||||||
|
LDFLAGS = -lcurl -ljansson
|
||||||
|
|
||||||
|
SRC_DIR = src
|
||||||
|
OBJ_DIR = obj
|
||||||
|
BIN_DIR = .
|
||||||
|
|
||||||
|
SRCS = $(wildcard $(SRC_DIR)/*.c)
|
||||||
|
OBJS = $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(SRCS))
|
||||||
|
TARGET = $(BIN_DIR)/fireproxy
|
||||||
|
|
||||||
|
all: directories $(TARGET)
|
||||||
|
|
||||||
|
directories:
|
||||||
|
mkdir -p $(OBJ_DIR)
|
||||||
|
mkdir -p $(BIN_DIR)
|
||||||
|
|
||||||
|
$(TARGET): $(OBJS)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
|
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(OBJ_DIR) $(TARGET)
|
||||||
|
|
||||||
|
.PHONY: all clean directories
|
||||||
34
README.md
Normal file
34
README.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# FireProxy - C Proxy Server with DoH Support
|
||||||
|
|
||||||
|
FireProxy is a lightweight HTTP proxy server written in C that intercepts web requests and resolves DNS queries using DNS-over-HTTPS (DoH).
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- HTTP proxy functionality
|
||||||
|
- DNS resolution using DoH (DNS-over-HTTPS)
|
||||||
|
- Uses hnsdoh.com as the DoH provider
|
||||||
|
- Multithreaded connection handling
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./fireproxy [port]
|
||||||
|
```
|
||||||
|
|
||||||
|
Default port is 8080 if not specified.
|
||||||
|
|
||||||
|
## Configure Your Browser
|
||||||
|
|
||||||
|
Configure your browser's proxy settings to use localhost with the port you specified when running FireProxy.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- libcurl for HTTP requests
|
||||||
|
- OpenSSL for HTTPS support
|
||||||
|
- pthread for multi-threading
|
||||||
97
TESTING.md
Normal file
97
TESTING.md
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# Testing FireProxy
|
||||||
|
|
||||||
|
This document provides instructions for testing the FireProxy server.
|
||||||
|
|
||||||
|
## Building the Proxy
|
||||||
|
|
||||||
|
First, build the proxy server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make clean
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running the Proxy
|
||||||
|
|
||||||
|
Start the proxy server on port 8080 (or another port of your choice):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./fireproxy 8080
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing with a Web Browser
|
||||||
|
|
||||||
|
### Firefox Configuration
|
||||||
|
|
||||||
|
1. Open Firefox and go to Settings
|
||||||
|
2. Search for "proxy" and click on "Settings" in the Network Settings section
|
||||||
|
3. Select "Manual proxy configuration"
|
||||||
|
4. Set HTTP Proxy to "localhost" and Port to "8080"
|
||||||
|
5. Leave other proxy fields empty
|
||||||
|
6. Check "Also use this proxy for HTTPS"
|
||||||
|
7. Click "OK"
|
||||||
|
|
||||||
|
### Chrome Configuration
|
||||||
|
|
||||||
|
1. Open Chrome and go to Settings
|
||||||
|
2. Search for "proxy" and click on "Open your computer's proxy settings"
|
||||||
|
3. Enable proxy settings according to your operating system:
|
||||||
|
- **Windows**: Set the HTTP proxy to "localhost:8080"
|
||||||
|
- **macOS**: Set the Web Proxy (HTTP) to "localhost" with port "8080"
|
||||||
|
- **Linux**: Set the HTTP proxy to "localhost" with port "8080"
|
||||||
|
|
||||||
|
## Testing with cURL
|
||||||
|
|
||||||
|
You can use cURL to test your proxy:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test HTTP request through proxy
|
||||||
|
curl -v --proxy http://localhost:8080 http://example.com/
|
||||||
|
|
||||||
|
# Test HTTPS request through proxy (if supported)
|
||||||
|
curl -v --proxy http://localhost:8080 https://example.com/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verifying DoH Functionality
|
||||||
|
|
||||||
|
To verify that your proxy is using the DoH server for DNS resolution:
|
||||||
|
|
||||||
|
1. Run the proxy with increased verbosity (if available)
|
||||||
|
2. In another terminal, monitor the proxy output while making requests
|
||||||
|
3. You should see messages indicating DoH lookups to hnsdoh.com
|
||||||
|
4. The proxy should log the resolved IP addresses
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
1. **Connection refused**: Make sure the proxy is running and listening on the configured port
|
||||||
|
2. **DNS resolution failures**: Check your internet connection and access to hnsdoh.com
|
||||||
|
3. **Memory leaks**: For long-running tests, monitor memory usage to ensure proper cleanup
|
||||||
|
|
||||||
|
### Using Network Monitoring Tools
|
||||||
|
|
||||||
|
You can use tools like Wireshark to monitor the traffic:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Capture traffic on loopback interface
|
||||||
|
sudo tcpdump -i lo port 8080 -vv
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Testing
|
||||||
|
|
||||||
|
For load testing the proxy:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install Apache Bench (ab) if not already installed
|
||||||
|
# Then test with multiple concurrent connections
|
||||||
|
ab -n 1000 -c 10 -X localhost:8080 http://example.com/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Testing
|
||||||
|
|
||||||
|
Since your proxy handles web traffic, consider testing for:
|
||||||
|
|
||||||
|
1. Buffer overflow vulnerabilities using oversized requests
|
||||||
|
2. Handling of malformed HTTP requests
|
||||||
|
3. Proper handling of connection termination
|
||||||
226
src/doh.c
Normal file
226
src/doh.c
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
#include "doh.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
#define DOH_SERVER "https://hnsdoh.com/dns-query"
|
||||||
|
|
||||||
|
// DNS header structure
|
||||||
|
typedef struct {
|
||||||
|
unsigned short id;
|
||||||
|
unsigned char rd :1;
|
||||||
|
unsigned char tc :1;
|
||||||
|
unsigned char aa :1;
|
||||||
|
unsigned char opcode :4;
|
||||||
|
unsigned char qr :1;
|
||||||
|
unsigned char rcode :4;
|
||||||
|
unsigned char z :3;
|
||||||
|
unsigned char ra :1;
|
||||||
|
unsigned short qdcount;
|
||||||
|
unsigned short ancount;
|
||||||
|
unsigned short nscount;
|
||||||
|
unsigned short arcount;
|
||||||
|
} dns_header;
|
||||||
|
|
||||||
|
// Struct to hold response data
|
||||||
|
typedef struct {
|
||||||
|
char* data;
|
||||||
|
size_t size;
|
||||||
|
} response_data;
|
||||||
|
|
||||||
|
// Callback function for cURL
|
||||||
|
static size_t write_callback(void* contents, size_t size, size_t nmemb, void* userp) {
|
||||||
|
size_t realsize = size * nmemb;
|
||||||
|
response_data* resp = (response_data*)userp;
|
||||||
|
|
||||||
|
char* ptr = realloc(resp->data, resp->size + realsize + 1);
|
||||||
|
if (!ptr) {
|
||||||
|
printf("Error: Failed to allocate memory\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
resp->data = ptr;
|
||||||
|
memcpy(&(resp->data[resp->size]), contents, realsize);
|
||||||
|
resp->size += realsize;
|
||||||
|
resp->data[resp->size] = 0;
|
||||||
|
|
||||||
|
return realsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a DNS query in wire format
|
||||||
|
void create_dns_query(const char* hostname, char** query, int* query_len) {
|
||||||
|
// Calculate the size of the query
|
||||||
|
int hostname_len = strlen(hostname);
|
||||||
|
int packet_size = sizeof(dns_header) + hostname_len + 2 + 4; // +2 for length bytes, +4 for qtype and qclass
|
||||||
|
|
||||||
|
*query = malloc(packet_size);
|
||||||
|
if (!*query) {
|
||||||
|
printf("Error: Failed to allocate memory for DNS query\n");
|
||||||
|
*query_len = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the entire buffer to 0
|
||||||
|
memset(*query, 0, packet_size);
|
||||||
|
|
||||||
|
// Setup DNS header
|
||||||
|
dns_header* header = (dns_header*)*query;
|
||||||
|
header->id = htons(12345); // Random ID
|
||||||
|
header->rd = 1; // Recursion desired
|
||||||
|
header->qdcount = htons(1); // One question
|
||||||
|
|
||||||
|
// Add the query
|
||||||
|
char* qname = *query + sizeof(dns_header);
|
||||||
|
char* hostname_copy = strdup(hostname);
|
||||||
|
|
||||||
|
// Convert hostname to DNS format (length byte + name part)
|
||||||
|
char* token = strtok(hostname_copy, ".");
|
||||||
|
char* cursor = qname;
|
||||||
|
while (token) {
|
||||||
|
size_t len = strlen(token);
|
||||||
|
*cursor = len;
|
||||||
|
cursor++;
|
||||||
|
memcpy(cursor, token, len);
|
||||||
|
cursor += len;
|
||||||
|
token = strtok(NULL, ".");
|
||||||
|
}
|
||||||
|
free(hostname_copy);
|
||||||
|
|
||||||
|
// Set the query type and class after the name
|
||||||
|
char* qinfo = qname + hostname_len + 2;
|
||||||
|
unsigned short qtype = htons(1); // A record
|
||||||
|
unsigned short qclass = htons(1); // IN class
|
||||||
|
|
||||||
|
memcpy(qinfo, &qtype, sizeof(qtype));
|
||||||
|
memcpy(qinfo + sizeof(qtype), &qclass, sizeof(qclass));
|
||||||
|
|
||||||
|
*query_len = packet_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract an IP address from the DNS response
|
||||||
|
int extract_ip_from_dns_response(const char* response, int response_len, char* ip_buffer, size_t buffer_size) {
|
||||||
|
if (response_len < sizeof(dns_header)) {
|
||||||
|
printf("Error: Response too short\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip the header
|
||||||
|
int offset = sizeof(dns_header);
|
||||||
|
|
||||||
|
// Skip the question section
|
||||||
|
// First find the end of the domain name
|
||||||
|
while (offset < response_len && response[offset] != 0) {
|
||||||
|
offset += response[offset] + 1;
|
||||||
|
}
|
||||||
|
offset += 5; // Skip the null byte, qtype, and qclass
|
||||||
|
|
||||||
|
// Now parse the answer section
|
||||||
|
if (offset + 12 > response_len) { // At least 12 bytes for a DNS RR
|
||||||
|
printf("Error: Response too short for answer section\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip the name pointer in the answer
|
||||||
|
offset += 2;
|
||||||
|
|
||||||
|
// Read the type
|
||||||
|
unsigned short type;
|
||||||
|
memcpy(&type, response + offset, sizeof(type));
|
||||||
|
type = ntohs(type);
|
||||||
|
offset += 2;
|
||||||
|
|
||||||
|
// Skip class
|
||||||
|
offset += 2;
|
||||||
|
|
||||||
|
// Skip TTL
|
||||||
|
offset += 4;
|
||||||
|
|
||||||
|
// Read the data length
|
||||||
|
unsigned short rdlength;
|
||||||
|
memcpy(&rdlength, response + offset, sizeof(rdlength));
|
||||||
|
rdlength = ntohs(rdlength);
|
||||||
|
offset += 2;
|
||||||
|
|
||||||
|
// Check if it's an A record (type 1) and the length is 4 (IPv4 address)
|
||||||
|
if (type == 1 && rdlength == 4) {
|
||||||
|
unsigned char ip_bytes[4];
|
||||||
|
memcpy(ip_bytes, response + offset, 4);
|
||||||
|
|
||||||
|
// Convert to string representation
|
||||||
|
snprintf(ip_buffer, buffer_size, "%d.%d.%d.%d",
|
||||||
|
ip_bytes[0], ip_bytes[1], ip_bytes[2], ip_bytes[3]);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Error: No valid A record found in response\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int resolve_doh(const char* hostname, char* ip_buffer, size_t buffer_size) {
|
||||||
|
CURL* curl;
|
||||||
|
CURLcode res;
|
||||||
|
response_data resp;
|
||||||
|
resp.data = malloc(1);
|
||||||
|
resp.size = 0;
|
||||||
|
|
||||||
|
curl = curl_easy_init();
|
||||||
|
if (!curl) {
|
||||||
|
printf("Error: Failed to initialize cURL\n");
|
||||||
|
free(resp.data);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create DNS query in wire format
|
||||||
|
char* dns_query = NULL;
|
||||||
|
int query_len = 0;
|
||||||
|
create_dns_query(hostname, &dns_query, &query_len);
|
||||||
|
|
||||||
|
if (!dns_query || query_len == 0) {
|
||||||
|
printf("Error: Failed to create DNS query\n");
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
free(resp.data);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up cURL options for DoH wire format
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, DOH_SERVER);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&resp);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POST, 1L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, dns_query);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, query_len);
|
||||||
|
|
||||||
|
// Set the content-type header for DNS wireformat
|
||||||
|
struct curl_slist* headers = NULL;
|
||||||
|
headers = curl_slist_append(headers, "Content-Type: application/dns-message");
|
||||||
|
headers = curl_slist_append(headers, "Accept: application/dns-message");
|
||||||
|
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||||
|
|
||||||
|
// Perform the request
|
||||||
|
res = curl_easy_perform(curl);
|
||||||
|
if (res != CURLE_OK) {
|
||||||
|
printf("Error: cURL request failed: %s\n", curl_easy_strerror(res));
|
||||||
|
curl_slist_free_all(headers);
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
free(dns_query);
|
||||||
|
free(resp.data);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("DoH response received, size: %zu bytes\n", resp.size);
|
||||||
|
|
||||||
|
// Parse the DNS wire format response to extract the IP
|
||||||
|
int result = extract_ip_from_dns_response(resp.data, resp.size, ip_buffer, buffer_size);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
curl_slist_free_all(headers);
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
free(dns_query);
|
||||||
|
free(resp.data);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
16
src/doh.h
Normal file
16
src/doh.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#ifndef DOH_H
|
||||||
|
#define DOH_H
|
||||||
|
|
||||||
|
#include <stddef.h> // Add this to define size_t type
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a hostname using DNS-over-HTTPS (DoH)
|
||||||
|
*
|
||||||
|
* @param hostname The hostname to resolve
|
||||||
|
* @param ip_buffer Buffer to store the resulting IP address
|
||||||
|
* @param buffer_size Size of the IP buffer
|
||||||
|
* @return 0 on success, non-zero on failure
|
||||||
|
*/
|
||||||
|
int resolve_doh(const char* hostname, char* ip_buffer, size_t buffer_size);
|
||||||
|
|
||||||
|
#endif // DOH_H
|
||||||
28
src/main.c
Normal file
28
src/main.c
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "proxy.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
int port = 8080; // Default port
|
||||||
|
|
||||||
|
// Check if port was provided as command line argument
|
||||||
|
if (argc > 1) {
|
||||||
|
port = atoi(argv[1]);
|
||||||
|
if (port <= 0 || port > 65535) {
|
||||||
|
fprintf(stderr, "Invalid port number. Using default port 8080.\n");
|
||||||
|
port = 8080;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Starting FireProxy on port %d...\n", port);
|
||||||
|
printf("Using DoH server: https://hnsdoh.com/dns-query\n");
|
||||||
|
|
||||||
|
// Start the proxy server
|
||||||
|
if (start_proxy_server(port) != 0) {
|
||||||
|
fprintf(stderr, "Failed to start proxy server.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
197
src/proxy.c
Normal file
197
src/proxy.c
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
#include "proxy.h"
|
||||||
|
#include "doh.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#define MAX_REQUEST_SIZE 8192
|
||||||
|
#define MAX_URL_LENGTH 2048
|
||||||
|
#define THREAD_POOL_SIZE 20
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int client_sock;
|
||||||
|
} thread_arg_t;
|
||||||
|
|
||||||
|
// Extract hostname from HTTP request
|
||||||
|
char* extract_host(const char* request) {
|
||||||
|
static char host[MAX_URL_LENGTH];
|
||||||
|
const char* host_header = strstr(request, "Host: ");
|
||||||
|
|
||||||
|
if (host_header) {
|
||||||
|
host_header += 6; // Skip "Host: "
|
||||||
|
int i = 0;
|
||||||
|
while (host_header[i] && host_header[i] != '\r' && host_header[i] != '\n' && i < MAX_URL_LENGTH - 1) {
|
||||||
|
host[i] = host_header[i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
host[i] = '\0';
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle client connection in a separate thread
|
||||||
|
void* handle_client(void* arg) {
|
||||||
|
thread_arg_t* thread_arg = (thread_arg_t*)arg;
|
||||||
|
int client_sock = thread_arg->client_sock;
|
||||||
|
free(thread_arg);
|
||||||
|
|
||||||
|
char request[MAX_REQUEST_SIZE];
|
||||||
|
char buffer[MAX_REQUEST_SIZE];
|
||||||
|
ssize_t bytes_received = recv(client_sock, request, sizeof(request) - 1, 0);
|
||||||
|
|
||||||
|
if (bytes_received <= 0) {
|
||||||
|
close(client_sock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
request[bytes_received] = '\0';
|
||||||
|
|
||||||
|
// Extract host from request
|
||||||
|
char* host = extract_host(request);
|
||||||
|
if (!host) {
|
||||||
|
printf("Failed to extract host from request\n");
|
||||||
|
close(client_sock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Proxying request to: %s\n", host);
|
||||||
|
|
||||||
|
// Resolve hostname using DoH
|
||||||
|
char ip_addr[INET6_ADDRSTRLEN];
|
||||||
|
if (resolve_doh(host, ip_addr, sizeof(ip_addr)) != 0) {
|
||||||
|
printf("Failed to resolve hostname using DoH: %s\n", host);
|
||||||
|
close(client_sock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Resolved %s to %s\n", host, ip_addr);
|
||||||
|
|
||||||
|
// Connect to the target server
|
||||||
|
struct sockaddr_in server_addr;
|
||||||
|
int server_sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (server_sock < 0) {
|
||||||
|
perror("Cannot create socket to server");
|
||||||
|
close(client_sock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&server_addr, 0, sizeof(server_addr));
|
||||||
|
server_addr.sin_family = AF_INET;
|
||||||
|
server_addr.sin_addr.s_addr = inet_addr(ip_addr);
|
||||||
|
server_addr.sin_port = htons(80); // Default to HTTP port
|
||||||
|
|
||||||
|
if (connect(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
|
||||||
|
perror("Cannot connect to server");
|
||||||
|
close(server_sock);
|
||||||
|
close(client_sock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward the request to the server
|
||||||
|
if (send(server_sock, request, bytes_received, 0) < 0) {
|
||||||
|
perror("Failed to send request to server");
|
||||||
|
close(server_sock);
|
||||||
|
close(client_sock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receive response from server and forward to client
|
||||||
|
while ((bytes_received = recv(server_sock, buffer, sizeof(buffer), 0)) > 0) {
|
||||||
|
if (send(client_sock, buffer, bytes_received, 0) < 0) {
|
||||||
|
perror("Failed to send response to client");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(server_sock);
|
||||||
|
close(client_sock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int start_proxy_server(int port) {
|
||||||
|
int server_sock, client_sock;
|
||||||
|
struct sockaddr_in server_addr, client_addr;
|
||||||
|
socklen_t client_len = sizeof(client_addr);
|
||||||
|
|
||||||
|
// Create socket
|
||||||
|
server_sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (server_sock < 0) {
|
||||||
|
perror("Cannot create socket");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow reuse of address
|
||||||
|
int opt = 1;
|
||||||
|
if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
|
||||||
|
perror("setsockopt failed");
|
||||||
|
close(server_sock);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize server address
|
||||||
|
memset(&server_addr, 0, sizeof(server_addr));
|
||||||
|
server_addr.sin_family = AF_INET;
|
||||||
|
server_addr.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
server_addr.sin_port = htons(port);
|
||||||
|
|
||||||
|
// Bind socket
|
||||||
|
if (bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
|
||||||
|
perror("Bind failed");
|
||||||
|
close(server_sock);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for connections
|
||||||
|
if (listen(server_sock, 10) < 0) {
|
||||||
|
perror("Listen failed");
|
||||||
|
close(server_sock);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Proxy server listening on port %d\n", port);
|
||||||
|
|
||||||
|
// Accept and handle client connections
|
||||||
|
while (1) {
|
||||||
|
client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &client_len);
|
||||||
|
if (client_sock < 0) {
|
||||||
|
perror("Accept failed");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("New connection from %s:%d\n",
|
||||||
|
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
|
||||||
|
|
||||||
|
// Create thread argument
|
||||||
|
thread_arg_t* thread_arg = malloc(sizeof(thread_arg_t));
|
||||||
|
if (!thread_arg) {
|
||||||
|
perror("Failed to allocate memory for thread argument");
|
||||||
|
close(client_sock);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
thread_arg->client_sock = client_sock;
|
||||||
|
|
||||||
|
// Create thread to handle client
|
||||||
|
pthread_t thread_id;
|
||||||
|
if (pthread_create(&thread_id, NULL, handle_client, thread_arg) != 0) {
|
||||||
|
perror("Failed to create thread");
|
||||||
|
free(thread_arg);
|
||||||
|
close(client_sock);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detach thread to allow it to clean up automatically
|
||||||
|
pthread_detach(thread_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(server_sock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
12
src/proxy.h
Normal file
12
src/proxy.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#ifndef PROXY_H
|
||||||
|
#define PROXY_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the proxy server on the specified port
|
||||||
|
*
|
||||||
|
* @param port The port to listen on
|
||||||
|
* @return 0 on success, non-zero on failure
|
||||||
|
*/
|
||||||
|
int start_proxy_server(int port);
|
||||||
|
|
||||||
|
#endif // PROXY_H
|
||||||
Reference in New Issue
Block a user