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