Commit 7034dd7e authored by Krzysztof Kliś's avatar Krzysztof Kliś Committed by GitHub

Merge pull request #8 from rafaelferrari0/master

Update proxy.c to support IPv6
parents 62f0b999 666a6b6e
......@@ -106,3 +106,32 @@ Add the IP to static hosts list ("/etc/hosts" on Linux, "C:\Windows\System32\Dri
sudo proxy -l 80 -h 93.184.216.119 -p 80 -i "tee input.log" -o "tee output.log"
```
Point your browser to http://www.example.com and watch the contents of input.log and output.log files.
### IPv6 support
The proxy normally will accept IPv4 and IPv6 connections if your system support it.
You can even forward IPv6 clients to any legacy IPv4 service.
By using numeric IPs on -b and -h parameters, proxy will use corresponding IPv4 or IPv6 socket to listen or connect.
The option [-b bind_address] force binding on specifc socket. It must be a local interface address.
Accepting IPv6 connections and forwarding to IPv6 service: (still accepting IPv4 connections)
```
proxy -l 8080 -h fdd0:beef:c4ea:2016::1 -p 8080
```
Accepting IPv6 connections and forwarding to legacy IPv4 service: (still accepting IPv4 connections)
```
proxy -l 8080 -h 192.168.1.2 -p 8080
```
Accepting IPv4 only connections and forwarding to IPv6 service:
```
proxy -l 8080 -b 192.168.1.1 -h fdd0:beef:c4ea:2016::1 -p 8080
```
Accepting IPv4 only connections and forwarding to IPv4 service:
```
proxy -l 8080 -b 192.168.1.1 -h 192.168.1.2 -p 8080
```
......@@ -3,6 +3,7 @@
*
* Author: Krzysztof Kliś <krzysztof.klis@gmail.com>
* Fixes and improvements: Jérôme Poulin <jeromepoulin@gmail.com>
* IPv6 support: 04/2019 Rafael Ferrari <rafaelbf@hotmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
......@@ -67,11 +68,12 @@
typedef enum {TRUE = 1, FALSE = 0} bool;
int check_ipversion(char * address);
int create_socket(int port);
void sigchld_handler(int signal);
void sigterm_handler(int signal);
void server_loop();
void handle_client(int client_sock, struct sockaddr_in client_addr);
void handle_client(int client_sock, struct sockaddr_storage client_addr);
void forward_data(int source_sock, int destination_sock);
void forward_data_ext(int source_sock, int destination_sock, char *cmd);
int create_connection();
......@@ -84,6 +86,8 @@ char *bind_addr, *remote_host, *cmd_in, *cmd_out;
bool foreground = FALSE;
bool use_syslog = FALSE;
#define BACKLOG 20 // how many pending connections queue will hold
/* Program start */
int main(int argc, char *argv[]) {
int local_port;
......@@ -170,36 +174,77 @@ int parse_options(int argc, char *argv[]) {
}
}
int check_ipversion(char * address)
{
/* Check for valid IPv4 or Iv6 string. Returns AF_INET for IPv4, AF_INET6 for IPv6 */
struct in6_addr bindaddr;
if (inet_pton(AF_INET, address, &bindaddr) == 1) {
return AF_INET;
} else {
if (inet_pton(AF_INET6, address, &bindaddr) == 1) {
return AF_INET6;
}
}
return 0;
}
/* Create server socket */
int create_socket(int port) {
int server_sock, optval = 1;
struct sockaddr_in server_addr;
int validfamily=0;
struct addrinfo hints, *res=NULL;
char portstr[12];
memset(&hints, 0x00, sizeof(hints));
server_sock = -1;
hints.ai_flags = AI_NUMERICSERV; /* numeric service number, not resolve */
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
/* prepare to bind on specified numeric address */
if (bind_addr != NULL) {
/* check for numeric IP to specify IPv6 or IPv4 socket */
if (validfamily = check_ipversion(bind_addr)) {
hints.ai_family = validfamily;
hints.ai_flags |= AI_NUMERICHOST; /* bind_addr is a valid numeric ip, skip resolve */
}
} else {
/* if bind_address is NULL, will bind to IPv6 wildcard */
hints.ai_family = AF_INET6; /* Specify IPv6 socket, also allow ipv4 clients */
hints.ai_flags |= AI_PASSIVE; /* Wildcard address */
}
sprintf(portstr, "%d", port);
/* Check if specified socket is valid. Try to resolve address if bind_address is a hostname */
if (getaddrinfo(bind_addr, portstr, &hints, &res) != 0) {
return CLIENT_RESOLVE_ERROR;
}
if ((server_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
if ((server_sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) {
return SERVER_SOCKET_ERROR;
}
if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) {
return SERVER_SETSOCKOPT_ERROR;
}
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
if (bind_addr == NULL) {
server_addr.sin_addr.s_addr = INADDR_ANY;
} else {
server_addr.sin_addr.s_addr = inet_addr(bind_addr);
}
if (bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) != 0) {
if (bind(server_sock, res->ai_addr, res->ai_addrlen) == -1) {
close(server_sock);
return SERVER_BIND_ERROR;
}
if (listen(server_sock, 20) < 0) {
if (listen(server_sock, BACKLOG) < 0) {
return SERVER_LISTEN_ERROR;
}
if (res != NULL)
freeaddrinfo(res);
return server_sock;
}
......@@ -242,7 +287,7 @@ void sigterm_handler(int signal) {
/* Main server loop */
void server_loop() {
struct sockaddr_in client_addr;
struct sockaddr_storage client_addr;
socklen_t addrlen = sizeof(client_addr);
#ifdef USE_SYSTEMD
......@@ -258,14 +303,17 @@ void server_loop() {
exit(0);
} else
connections_processed++;
close(client_sock);
}
}
/* Handle client connection */
void handle_client(int client_sock, struct sockaddr_in client_addr)
void handle_client(int client_sock, struct sockaddr_storage client_addr)
{
if ((remote_sock = create_connection()) < 0) {
plog(LOG_ERR, "Cannot connect to host: %m");
goto cleanup;
......@@ -378,28 +426,42 @@ void forward_data_ext(int source_sock, int destination_sock, char *cmd) {
/* Create client connection */
int create_connection() {
struct sockaddr_in server_addr;
struct hostent *server;
struct addrinfo hints, *res=NULL;
int sock;
int validfamily=0;
char portstr[12];
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
return CLIENT_SOCKET_ERROR;
memset(&hints, 0x00, sizeof(hints));
hints.ai_flags = AI_NUMERICSERV; /* numeric service number, not resolve */
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
sprintf(portstr, "%d", remote_port);
/* check for numeric IP to specify IPv6 or IPv4 socket */
if (validfamily = check_ipversion(remote_host)) {
hints.ai_family = validfamily;
hints.ai_flags |= AI_NUMERICHOST; /* remote_host is a valid numeric ip, skip resolve */
}
if ((server = gethostbyname(remote_host)) == NULL) {
/* Check if specified host is valid. Try to resolve address if remote_host is a hostname */
if (getaddrinfo(remote_host,portstr , &hints, &res) != 0) {
errno = EFAULT;
return CLIENT_RESOLVE_ERROR;
}
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
memcpy(&server_addr.sin_addr.s_addr, server->h_addr, server->h_length);
server_addr.sin_port = htons(remote_port);
if ((sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) {
return CLIENT_SOCKET_ERROR;
}
if (connect(sock, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
return CLIENT_CONNECT_ERROR;
}
if (res != NULL)
freeaddrinfo(res);
return sock;
}
/* vim: set et ts=4 sw=4: */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment