Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
W
Warp Proxy
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nanahira
Warp Proxy
Commits
ff0d6b6f
Commit
ff0d6b6f
authored
May 09, 2024
by
nanahira
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
use socat
parent
77ee9885
Pipeline
#34000
passed with stages
in 1 minute and 50 seconds
Changes
3
Pipelines
2
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
3 additions
and
499 deletions
+3
-499
Dockerfile
Dockerfile
+3
-16
Makefile
Makefile
+0
-16
proxy.c
proxy.c
+0
-467
No files found.
Dockerfile
View file @
ff0d6b6f
FROM
debian:bullseye as base
FROM
debian:bullseye-slim
RUN
apt update
&&
\
apt
-y
install
build-essential
&&
\
rm
-rf
/var/lib/apt/lists/
*
/tmp/
*
/var/tmp/
*
FROM
base as builder
WORKDIR
/build
COPY
./Makefile ./
COPY
./proxy.c ./
RUN
make
FROM
base
RUN
apt update
&&
\
RUN
apt update
&&
\
env
DEBIAN_FRONTEND
=
noninteractive apt
-y
install
curl gnupg2 libcap2-bin
&&
\
env
DEBIAN_FRONTEND
=
noninteractive apt
-y
install
curl gnupg2 libcap2-bin
socat
&&
\
curl https://pkg.cloudflareclient.com/pubkey.gpg | apt-key add -
&&
\
curl https://pkg.cloudflareclient.com/pubkey.gpg | apt-key add -
&&
\
echo
'deb http://pkg.cloudflareclient.com/ bullseye main'
|
tee
/etc/apt/sources.list.d/cloudflare-client.list
&&
\
echo
'deb http://pkg.cloudflareclient.com/ bullseye main'
|
tee
/etc/apt/sources.list.d/cloudflare-client.list
&&
\
apt update
&&
\
apt update
&&
\
...
@@ -25,7 +13,6 @@ RUN apt update && \
...
@@ -25,7 +13,6 @@ RUN apt update && \
WORKDIR
/usr/src/app
WORKDIR
/usr/src/app
COPY
--from=builder /build/proxy ./proxy
COPY
./entrypoint.sh ./
COPY
./entrypoint.sh ./
ENV
LOCAL_PROXY_PORT 10080
ENV
LOCAL_PROXY_PORT 10080
...
@@ -33,4 +20,4 @@ ENV FORWARD_PROXY_PORT 8080
...
@@ -33,4 +20,4 @@ ENV FORWARD_PROXY_PORT 8080
EXPOSE
$FORWARD_PROXY_PORT
EXPOSE
$FORWARD_PROXY_PORT
ENTRYPOINT
[ "./entrypoint.sh" ]
ENTRYPOINT
[ "./entrypoint.sh" ]
CMD
["bash", "-c", "
./proxy -l $FORWARD_PROXY_PORT -h localhost -p $LOCAL_PROXY_PORT -f
"]
CMD
["bash", "-c", "
socat tcp-listen:$FORWARD_PROXY_PORT,fork tcp:localhost:$LOCAL_PROXY_PORT
"]
Makefile
deleted
100644 → 0
View file @
77ee9885
CFLAGS
=
-O2
-std
=
c99
-Wall
OPTFLAGS
=
-s
-DUSE_SPLICE
LDFLAGS
=
all
:
gcc
$(CFLAGS)
$(OPTFLAGS)
-o
proxy proxy.c
$(LDFLAGS)
tomato
:
mipsel-uclibc-gcc
$(CFLAGS)
$(OPTFLAGS)
-o
proxy proxy.c
$(LDFLAGS)
openwrt
:
mipsel-linux-uclibc-gcc
$(CFLAGS)
$(OPTFLAGS)
-o
proxy proxy.c
$(LDFLAGS)
backfire
:
mipsel-openwrt-linux-uclibc-gcc
$(CFLAGS)
$(OPTFLAGS)
-o
proxy proxy.c
$(LDFLAGS)
darwin
:
gcc
$(CFLAGS)
-o
proxy proxy.c
$(LDFLAGS)
clean
:
rm
-f
proxy proxy.exe
proxy.c
deleted
100644 → 0
View file @
77ee9885
/*
* Tiny TCP proxy server
*
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version with the following modification:
*
* As a special exception, the copyright holders of this library give you
* permission to link this library with independent modules to produce an
* executable, regardless of the license terms of these independent modules,
* and to copy and distribute the resulting executable under terms of your choice,
* provided that you also meet, for each linked independent module, the terms
* and conditions of the license of that module. An independent module is a
* module which is not derived from or based on this library. If you modify this
* library, you may extend this exception to your version of the library, but
* you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define _GNU_SOURCE
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <netdb.h>
#include <resolv.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/wait.h>
#ifdef USE_SYSTEMD
#include <systemd/sd-daemon.h>
#endif
#define BUF_SIZE 16384
#define READ 0
#define WRITE 1
#define SERVER_SOCKET_ERROR -1
#define SERVER_SETSOCKOPT_ERROR -2
#define SERVER_BIND_ERROR -3
#define SERVER_LISTEN_ERROR -4
#define CLIENT_SOCKET_ERROR -5
#define CLIENT_RESOLVE_ERROR -6
#define CLIENT_CONNECT_ERROR -7
#define CREATE_PIPE_ERROR -8
#define BROKEN_PIPE_ERROR -9
#define SYNTAX_ERROR -10
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_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
();
int
parse_options
(
int
argc
,
char
*
argv
[]);
void
plog
(
int
priority
,
const
char
*
format
,
...);
int
server_sock
,
client_sock
,
remote_sock
,
remote_port
=
0
;
int
connections_processed
=
0
;
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
;
pid_t
pid
;
bind_addr
=
NULL
;
local_port
=
parse_options
(
argc
,
argv
);
if
(
local_port
<
0
)
{
printf
(
"Syntax: %s [-b bind_address] -l local_port -h remote_host -p remote_port [-i
\"
input parser
\"
] [-o
\"
output parser
\"
] [-f (stay in foreground)] [-s (use syslog)]
\n
"
,
argv
[
0
]);
return
local_port
;
}
if
(
use_syslog
)
openlog
(
"proxy"
,
LOG_PID
,
LOG_DAEMON
);
if
((
server_sock
=
create_socket
(
local_port
))
<
0
)
{
// start server
plog
(
LOG_CRIT
,
"Cannot run server: %m"
);
return
server_sock
;
}
signal
(
SIGCHLD
,
sigchld_handler
);
// prevent ended children from becoming zombies
signal
(
SIGTERM
,
sigterm_handler
);
// handle KILL signal
if
(
foreground
)
{
server_loop
();
}
else
{
switch
(
pid
=
fork
())
{
case
0
:
// deamonized child
server_loop
();
break
;
case
-
1
:
// error
plog
(
LOG_CRIT
,
"Cannot daemonize: %m"
);
return
pid
;
default:
// parent
close
(
server_sock
);
}
}
if
(
use_syslog
)
closelog
();
return
EXIT_SUCCESS
;
}
/* Parse command line options */
int
parse_options
(
int
argc
,
char
*
argv
[])
{
int
c
,
local_port
=
0
;
while
((
c
=
getopt
(
argc
,
argv
,
"b:l:h:p:i:o:fs"
))
!=
-
1
)
{
switch
(
c
)
{
case
'l'
:
local_port
=
atoi
(
optarg
);
break
;
case
'b'
:
bind_addr
=
optarg
;
break
;
case
'h'
:
remote_host
=
optarg
;
break
;
case
'p'
:
remote_port
=
atoi
(
optarg
);
break
;
case
'i'
:
cmd_in
=
optarg
;
break
;
case
'o'
:
cmd_out
=
optarg
;
break
;
case
'f'
:
foreground
=
TRUE
;
break
;
case
's'
:
use_syslog
=
TRUE
;
break
;
}
}
if
(
local_port
&&
remote_host
&&
remote_port
)
{
return
local_port
;
}
else
{
return
SYNTAX_ERROR
;
}
}
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
;
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
(
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
;
}
if
(
bind
(
server_sock
,
res
->
ai_addr
,
res
->
ai_addrlen
)
==
-
1
)
{
close
(
server_sock
);
return
SERVER_BIND_ERROR
;
}
if
(
listen
(
server_sock
,
BACKLOG
)
<
0
)
{
return
SERVER_LISTEN_ERROR
;
}
if
(
res
!=
NULL
)
freeaddrinfo
(
res
);
return
server_sock
;
}
/* Send log message to stderr or syslog */
void
plog
(
int
priority
,
const
char
*
format
,
...)
{
va_list
ap
;
va_start
(
ap
,
format
);
if
(
use_syslog
)
vsyslog
(
priority
,
format
,
ap
);
else
{
vfprintf
(
stderr
,
format
,
ap
);
fprintf
(
stderr
,
"
\n
"
);
}
va_end
(
ap
);
}
/* Update systemd status with connection count */
void
update_connection_count
()
{
#ifdef USE_SYSTEMD
sd_notifyf
(
0
,
"STATUS=Ready. %d connections processed.
\n
"
,
connections_processed
);
#endif
}
/* Handle finished child process */
void
sigchld_handler
(
int
signal
)
{
while
(
waitpid
(
-
1
,
NULL
,
WNOHANG
)
>
0
);
}
/* Handle term signal */
void
sigterm_handler
(
int
signal
)
{
close
(
client_sock
);
close
(
server_sock
);
exit
(
0
);
}
/* Main server loop */
void
server_loop
()
{
struct
sockaddr_storage
client_addr
;
socklen_t
addrlen
=
sizeof
(
client_addr
);
#ifdef USE_SYSTEMD
sd_notify
(
0
,
"READY=1
\n
"
);
#endif
while
(
TRUE
)
{
update_connection_count
();
client_sock
=
accept
(
server_sock
,
(
struct
sockaddr
*
)
&
client_addr
,
&
addrlen
);
if
(
fork
()
==
0
)
{
// handle client connection in a separate process
close
(
server_sock
);
handle_client
(
client_sock
,
client_addr
);
exit
(
0
);
}
else
connections_processed
++
;
close
(
client_sock
);
}
}
/* Handle client connection */
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
;
}
if
(
fork
()
==
0
)
{
// a process forwarding data from client to remote socket
if
(
cmd_out
)
{
forward_data_ext
(
client_sock
,
remote_sock
,
cmd_out
);
}
else
{
forward_data
(
client_sock
,
remote_sock
);
}
exit
(
0
);
}
if
(
fork
()
==
0
)
{
// a process forwarding data from remote socket to client
if
(
cmd_in
)
{
forward_data_ext
(
remote_sock
,
client_sock
,
cmd_in
);
}
else
{
forward_data
(
remote_sock
,
client_sock
);
}
exit
(
0
);
}
cleanup:
close
(
remote_sock
);
close
(
client_sock
);
}
/* Forward data between sockets */
void
forward_data
(
int
source_sock
,
int
destination_sock
)
{
ssize_t
n
;
#ifdef USE_SPLICE
int
buf_pipe
[
2
];
if
(
pipe
(
buf_pipe
)
==
-
1
)
{
plog
(
LOG_ERR
,
"pipe: %m"
);
exit
(
CREATE_PIPE_ERROR
);
}
while
((
n
=
splice
(
source_sock
,
NULL
,
buf_pipe
[
WRITE
],
NULL
,
SSIZE_MAX
,
SPLICE_F_NONBLOCK
|
SPLICE_F_MOVE
))
>
0
)
{
if
(
splice
(
buf_pipe
[
READ
],
NULL
,
destination_sock
,
NULL
,
SSIZE_MAX
,
SPLICE_F_MOVE
)
<
0
)
{
plog
(
LOG_ERR
,
"write: %m"
);
exit
(
BROKEN_PIPE_ERROR
);
}
}
#else
char
buffer
[
BUF_SIZE
];
while
((
n
=
recv
(
source_sock
,
buffer
,
BUF_SIZE
,
0
))
>
0
)
{
// read data from input socket
send
(
destination_sock
,
buffer
,
n
,
0
);
// send data to output socket
}
#endif
if
(
n
<
0
)
{
plog
(
LOG_ERR
,
"read: %m"
);
exit
(
BROKEN_PIPE_ERROR
);
}
#ifdef USE_SPLICE
close
(
buf_pipe
[
0
]);
close
(
buf_pipe
[
1
]);
#endif
shutdown
(
destination_sock
,
SHUT_RDWR
);
// stop other processes from using socket
close
(
destination_sock
);
shutdown
(
source_sock
,
SHUT_RDWR
);
// stop other processes from using socket
close
(
source_sock
);
}
/* Forward data between sockets through external command */
void
forward_data_ext
(
int
source_sock
,
int
destination_sock
,
char
*
cmd
)
{
char
buffer
[
BUF_SIZE
];
int
n
,
i
,
pipe_in
[
2
],
pipe_out
[
2
];
if
(
pipe
(
pipe_in
)
<
0
||
pipe
(
pipe_out
)
<
0
)
{
// create command input and output pipes
plog
(
LOG_CRIT
,
"Cannot create pipe: %m"
);
exit
(
CREATE_PIPE_ERROR
);
}
if
(
fork
()
==
0
)
{
dup2
(
pipe_in
[
READ
],
STDIN_FILENO
);
// replace standard input with input part of pipe_in
dup2
(
pipe_out
[
WRITE
],
STDOUT_FILENO
);
// replace standard output with output part of pipe_out
close
(
pipe_in
[
WRITE
]);
// close unused end of pipe_in
close
(
pipe_out
[
READ
]);
// close unused end of pipe_out
n
=
system
(
cmd
);
// execute command
exit
(
n
);
}
else
{
close
(
pipe_in
[
READ
]);
// no need to read from input pipe here
close
(
pipe_out
[
WRITE
]);
// no need to write to output pipe here
while
((
n
=
recv
(
source_sock
,
buffer
,
BUF_SIZE
,
0
))
>
0
)
{
// read data from input socket
if
(
write
(
pipe_in
[
WRITE
],
buffer
,
n
)
<
0
)
{
// write data to input pipe of external command
plog
(
LOG_ERR
,
"Cannot write to pipe: %m"
);
exit
(
BROKEN_PIPE_ERROR
);
}
if
((
i
=
read
(
pipe_out
[
READ
],
buffer
,
BUF_SIZE
))
>
0
)
{
// read command output
send
(
destination_sock
,
buffer
,
i
,
0
);
// send data to output socket
}
}
shutdown
(
destination_sock
,
SHUT_RDWR
);
// stop other processes from using socket
close
(
destination_sock
);
shutdown
(
source_sock
,
SHUT_RDWR
);
// stop other processes from using socket
close
(
source_sock
);
}
}
/* Create client connection */
int
create_connection
()
{
struct
addrinfo
hints
,
*
res
=
NULL
;
int
sock
;
int
validfamily
=
0
;
char
portstr
[
12
];
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 */
}
/* 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
;
}
if
((
sock
=
socket
(
res
->
ai_family
,
res
->
ai_socktype
,
res
->
ai_protocol
))
<
0
)
{
return
CLIENT_SOCKET_ERROR
;
}
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: */
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment