Skip to content

Commit 7034dd7

Browse files
authored
Merge pull request #8 from rafaelferrari0/master
Update proxy.c to support IPv6
2 parents 62f0b99 + 666a6b6 commit 7034dd7

File tree

2 files changed

+117
-26
lines changed

2 files changed

+117
-26
lines changed

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,32 @@ Add the IP to static hosts list ("/etc/hosts" on Linux, "C:\Windows\System32\Dri
106106
sudo proxy -l 80 -h 93.184.216.119 -p 80 -i "tee input.log" -o "tee output.log"
107107
```
108108
Point your browser to http://www.example.com and watch the contents of input.log and output.log files.
109+
110+
### IPv6 support
111+
112+
The proxy normally will accept IPv4 and IPv6 connections if your system support it.
113+
You can even forward IPv6 clients to any legacy IPv4 service.
114+
By using numeric IPs on -b and -h parameters, proxy will use corresponding IPv4 or IPv6 socket to listen or connect.
115+
116+
The option [-b bind_address] force binding on specifc socket. It must be a local interface address.
117+
118+
119+
Accepting IPv6 connections and forwarding to IPv6 service: (still accepting IPv4 connections)
120+
```
121+
proxy -l 8080 -h fdd0:beef:c4ea:2016::1 -p 8080
122+
```
123+
124+
Accepting IPv6 connections and forwarding to legacy IPv4 service: (still accepting IPv4 connections)
125+
```
126+
proxy -l 8080 -h 192.168.1.2 -p 8080
127+
```
128+
129+
Accepting IPv4 only connections and forwarding to IPv6 service:
130+
```
131+
proxy -l 8080 -b 192.168.1.1 -h fdd0:beef:c4ea:2016::1 -p 8080
132+
```
133+
134+
Accepting IPv4 only connections and forwarding to IPv4 service:
135+
```
136+
proxy -l 8080 -b 192.168.1.1 -h 192.168.1.2 -p 8080
137+
```

proxy.c

Lines changed: 88 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*
44
* Author: Krzysztof Kliś <[email protected]>
55
* Fixes and improvements: Jérôme Poulin <[email protected]>
6+
* IPv6 support: 04/2019 Rafael Ferrari <[email protected]>
67
*
78
* This program is free software: you can redistribute it and/or modify
89
* it under the terms of the GNU Lesser General Public License as published by
@@ -67,11 +68,12 @@
6768

6869
typedef enum {TRUE = 1, FALSE = 0} bool;
6970

71+
int check_ipversion(char * address);
7072
int create_socket(int port);
7173
void sigchld_handler(int signal);
7274
void sigterm_handler(int signal);
7375
void server_loop();
74-
void handle_client(int client_sock, struct sockaddr_in client_addr);
76+
void handle_client(int client_sock, struct sockaddr_storage client_addr);
7577
void forward_data(int source_sock, int destination_sock);
7678
void forward_data_ext(int source_sock, int destination_sock, char *cmd);
7779
int create_connection();
@@ -84,6 +86,8 @@ char *bind_addr, *remote_host, *cmd_in, *cmd_out;
8486
bool foreground = FALSE;
8587
bool use_syslog = FALSE;
8688

89+
#define BACKLOG 20 // how many pending connections queue will hold
90+
8791
/* Program start */
8892
int main(int argc, char *argv[]) {
8993
int local_port;
@@ -170,36 +174,77 @@ int parse_options(int argc, char *argv[]) {
170174
}
171175
}
172176

177+
int check_ipversion(char * address)
178+
{
179+
/* Check for valid IPv4 or Iv6 string. Returns AF_INET for IPv4, AF_INET6 for IPv6 */
180+
181+
struct in6_addr bindaddr;
182+
183+
if (inet_pton(AF_INET, address, &bindaddr) == 1) {
184+
return AF_INET;
185+
} else {
186+
if (inet_pton(AF_INET6, address, &bindaddr) == 1) {
187+
return AF_INET6;
188+
}
189+
}
190+
return 0;
191+
}
192+
173193
/* Create server socket */
174194
int create_socket(int port) {
175195
int server_sock, optval = 1;
176-
struct sockaddr_in server_addr;
196+
int validfamily=0;
197+
struct addrinfo hints, *res=NULL;
198+
char portstr[12];
199+
200+
memset(&hints, 0x00, sizeof(hints));
201+
server_sock = -1;
202+
203+
hints.ai_flags = AI_NUMERICSERV; /* numeric service number, not resolve */
204+
hints.ai_family = AF_UNSPEC;
205+
hints.ai_socktype = SOCK_STREAM;
206+
207+
/* prepare to bind on specified numeric address */
208+
if (bind_addr != NULL) {
209+
/* check for numeric IP to specify IPv6 or IPv4 socket */
210+
if (validfamily = check_ipversion(bind_addr)) {
211+
hints.ai_family = validfamily;
212+
hints.ai_flags |= AI_NUMERICHOST; /* bind_addr is a valid numeric ip, skip resolve */
213+
}
214+
} else {
215+
/* if bind_address is NULL, will bind to IPv6 wildcard */
216+
hints.ai_family = AF_INET6; /* Specify IPv6 socket, also allow ipv4 clients */
217+
hints.ai_flags |= AI_PASSIVE; /* Wildcard address */
218+
}
219+
220+
sprintf(portstr, "%d", port);
221+
222+
/* Check if specified socket is valid. Try to resolve address if bind_address is a hostname */
223+
if (getaddrinfo(bind_addr, portstr, &hints, &res) != 0) {
224+
return CLIENT_RESOLVE_ERROR;
225+
}
177226

178-
if ((server_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
227+
if ((server_sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) {
179228
return SERVER_SOCKET_ERROR;
180229
}
181230

231+
182232
if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) {
183233
return SERVER_SETSOCKOPT_ERROR;
184234
}
185235

186-
memset(&server_addr, 0, sizeof(server_addr));
187-
server_addr.sin_family = AF_INET;
188-
server_addr.sin_port = htons(port);
189-
if (bind_addr == NULL) {
190-
server_addr.sin_addr.s_addr = INADDR_ANY;
191-
} else {
192-
server_addr.sin_addr.s_addr = inet_addr(bind_addr);
193-
}
194-
195-
if (bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) != 0) {
236+
if (bind(server_sock, res->ai_addr, res->ai_addrlen) == -1) {
237+
close(server_sock);
196238
return SERVER_BIND_ERROR;
197239
}
198240

199-
if (listen(server_sock, 20) < 0) {
241+
if (listen(server_sock, BACKLOG) < 0) {
200242
return SERVER_LISTEN_ERROR;
201243
}
202244

245+
if (res != NULL)
246+
freeaddrinfo(res);
247+
203248
return server_sock;
204249
}
205250

@@ -242,7 +287,7 @@ void sigterm_handler(int signal) {
242287

243288
/* Main server loop */
244289
void server_loop() {
245-
struct sockaddr_in client_addr;
290+
struct sockaddr_storage client_addr;
246291
socklen_t addrlen = sizeof(client_addr);
247292

248293
#ifdef USE_SYSTEMD
@@ -258,14 +303,17 @@ void server_loop() {
258303
exit(0);
259304
} else
260305
connections_processed++;
306+
261307
close(client_sock);
262308
}
263309

264310
}
265311

312+
266313
/* Handle client connection */
267-
void handle_client(int client_sock, struct sockaddr_in client_addr)
314+
void handle_client(int client_sock, struct sockaddr_storage client_addr)
268315
{
316+
269317
if ((remote_sock = create_connection()) < 0) {
270318
plog(LOG_ERR, "Cannot connect to host: %m");
271319
goto cleanup;
@@ -378,28 +426,42 @@ void forward_data_ext(int source_sock, int destination_sock, char *cmd) {
378426

379427
/* Create client connection */
380428
int create_connection() {
381-
struct sockaddr_in server_addr;
382-
struct hostent *server;
429+
struct addrinfo hints, *res=NULL;
383430
int sock;
431+
int validfamily=0;
432+
char portstr[12];
384433

385-
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
386-
return CLIENT_SOCKET_ERROR;
434+
memset(&hints, 0x00, sizeof(hints));
435+
436+
hints.ai_flags = AI_NUMERICSERV; /* numeric service number, not resolve */
437+
hints.ai_family = AF_UNSPEC;
438+
hints.ai_socktype = SOCK_STREAM;
439+
440+
sprintf(portstr, "%d", remote_port);
441+
442+
/* check for numeric IP to specify IPv6 or IPv4 socket */
443+
if (validfamily = check_ipversion(remote_host)) {
444+
hints.ai_family = validfamily;
445+
hints.ai_flags |= AI_NUMERICHOST; /* remote_host is a valid numeric ip, skip resolve */
387446
}
388447

389-
if ((server = gethostbyname(remote_host)) == NULL) {
448+
/* Check if specified host is valid. Try to resolve address if remote_host is a hostname */
449+
if (getaddrinfo(remote_host,portstr , &hints, &res) != 0) {
390450
errno = EFAULT;
391451
return CLIENT_RESOLVE_ERROR;
392452
}
393453

394-
memset(&server_addr, 0, sizeof(server_addr));
395-
server_addr.sin_family = AF_INET;
396-
memcpy(&server_addr.sin_addr.s_addr, server->h_addr, server->h_length);
397-
server_addr.sin_port = htons(remote_port);
454+
if ((sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) {
455+
return CLIENT_SOCKET_ERROR;
456+
}
398457

399-
if (connect(sock, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
458+
if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
400459
return CLIENT_CONNECT_ERROR;
401460
}
402461

462+
if (res != NULL)
463+
freeaddrinfo(res);
464+
403465
return sock;
404466
}
405467
/* vim: set et ts=4 sw=4: */

0 commit comments

Comments
 (0)