Install TCP Proxy Server on Localhost in Linux
I Want
I write a simple "TCP proxy server": it listens for TCP pockets on localhost, looks up packet IP destination, reads from it and sends reply.
No I want to install it into my Linux system so all TCP packets go through this proxy.
What I Tried
- First, I start my proxy on
localhost:1111
- Then I try:
sudo iptables -t nat -A PREROUTING -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 1111
sudo iptables -t nat -A PREROUTING -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 1111
- Proxy doesn't react (no logs of proxied pockets), sites work in browser.
- Then I try:
sudo iptables -t nat -A OUTPUT -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 1111
sudo iptables -t nat -A OUTPUT -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 1111
I get:
INFO[0000] Starting proxy from="localhost:1111"
hello world
Connectoin from 127.0.0.1:1111
Connectoin to 192.168.1.90:48618
ERRO[0123] Error dialing remote host err="dial tcp 192.168.1.90:48618: getsockopt: connection refused" from="localhost:1111"
And the same error repeating many times.
Sites don't load in a browser.
192.168.1.90 is wlp2s0
Source Codes Of The Proxy in Golang
~/go/src/github.com/ilyaigpetrov/proxy
Based on: https://gist.github.com/ericflo/7dcf4179c315d8bd714c
package proxy
import (
"io"
"net"
"sync"
"fmt"
log "github.com/Sirupsen/logrus"
)
type Proxy struct {
from string
done chan struct{}
log *log.Entry
}
func NewProxy(from string) *Proxy {
log.SetLevel(log.InfoLevel)
return &Proxy{
from: from,
done: make(chan struct{}),
log: log.WithFields(log.Fields{
"from": from,
}),
}
}
func (p *Proxy) Start() error {
p.log.Infoln("Starting proxy")
listener, err := net.Listen("tcp", p.from)
if err != nil {
return err
}
go p.run(listener)
return nil
}
func (p *Proxy) Stop() {
p.log.Infoln("Stopping proxy")
if p.done == nil {
return
}
close(p.done)
p.done = nil
}
func (p *Proxy) run(listener net.Listener) {
for {
select {
case <-p.done:
return
default:
connection, err := listener.Accept()
fmt.Printf("Connectoin from %sn", connection.LocalAddr().String())
fmt.Printf("Connectoin to %sn", connection.RemoteAddr().String())
if err == nil {
go p.handle(connection)
} else {
p.log.WithField("err", err).Errorln("Error accepting conn")
}
}
}
}
func (p *Proxy) handle(connection net.Conn) {
defer connection.Close()
p.log.Debugln("Handling", connection)
defer p.log.Debugln("Done handling", connection)
remote, err := net.Dial("tcp", connection.RemoteAddr().String())
if err != nil {
p.log.WithField("err", err).Errorln("Error dialing remote host")
return
}
defer remote.Close()
wg := &sync.WaitGroup{}
wg.Add(2)
go p.copy(remote, connection, wg)
go p.copy(connection, remote, wg)
wg.Wait()
}
func (p *Proxy) copy(from, to net.Conn, wg *sync.WaitGroup) {
defer wg.Done()
select {
case <-p.done:
return
default:
if _, err := io.Copy(to, from); err != nil {
p.log.WithField("err", err).Errorln("Error from copy")
p.Stop()
return
}
}
}
~/go/src/github.com/ilyaigpetrov/proxy-main
package main
import (
"fmt"
"github.com/ilyaigpetrov/proxy"
)
func main() {
proxy.NewProxy("localhost:1111").Start()
fmt.Println("hello world")
select{}
}
iptables proxy tcp
add a comment |
I Want
I write a simple "TCP proxy server": it listens for TCP pockets on localhost, looks up packet IP destination, reads from it and sends reply.
No I want to install it into my Linux system so all TCP packets go through this proxy.
What I Tried
- First, I start my proxy on
localhost:1111
- Then I try:
sudo iptables -t nat -A PREROUTING -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 1111
sudo iptables -t nat -A PREROUTING -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 1111
- Proxy doesn't react (no logs of proxied pockets), sites work in browser.
- Then I try:
sudo iptables -t nat -A OUTPUT -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 1111
sudo iptables -t nat -A OUTPUT -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 1111
I get:
INFO[0000] Starting proxy from="localhost:1111"
hello world
Connectoin from 127.0.0.1:1111
Connectoin to 192.168.1.90:48618
ERRO[0123] Error dialing remote host err="dial tcp 192.168.1.90:48618: getsockopt: connection refused" from="localhost:1111"
And the same error repeating many times.
Sites don't load in a browser.
192.168.1.90 is wlp2s0
Source Codes Of The Proxy in Golang
~/go/src/github.com/ilyaigpetrov/proxy
Based on: https://gist.github.com/ericflo/7dcf4179c315d8bd714c
package proxy
import (
"io"
"net"
"sync"
"fmt"
log "github.com/Sirupsen/logrus"
)
type Proxy struct {
from string
done chan struct{}
log *log.Entry
}
func NewProxy(from string) *Proxy {
log.SetLevel(log.InfoLevel)
return &Proxy{
from: from,
done: make(chan struct{}),
log: log.WithFields(log.Fields{
"from": from,
}),
}
}
func (p *Proxy) Start() error {
p.log.Infoln("Starting proxy")
listener, err := net.Listen("tcp", p.from)
if err != nil {
return err
}
go p.run(listener)
return nil
}
func (p *Proxy) Stop() {
p.log.Infoln("Stopping proxy")
if p.done == nil {
return
}
close(p.done)
p.done = nil
}
func (p *Proxy) run(listener net.Listener) {
for {
select {
case <-p.done:
return
default:
connection, err := listener.Accept()
fmt.Printf("Connectoin from %sn", connection.LocalAddr().String())
fmt.Printf("Connectoin to %sn", connection.RemoteAddr().String())
if err == nil {
go p.handle(connection)
} else {
p.log.WithField("err", err).Errorln("Error accepting conn")
}
}
}
}
func (p *Proxy) handle(connection net.Conn) {
defer connection.Close()
p.log.Debugln("Handling", connection)
defer p.log.Debugln("Done handling", connection)
remote, err := net.Dial("tcp", connection.RemoteAddr().String())
if err != nil {
p.log.WithField("err", err).Errorln("Error dialing remote host")
return
}
defer remote.Close()
wg := &sync.WaitGroup{}
wg.Add(2)
go p.copy(remote, connection, wg)
go p.copy(connection, remote, wg)
wg.Wait()
}
func (p *Proxy) copy(from, to net.Conn, wg *sync.WaitGroup) {
defer wg.Done()
select {
case <-p.done:
return
default:
if _, err := io.Copy(to, from); err != nil {
p.log.WithField("err", err).Errorln("Error from copy")
p.Stop()
return
}
}
}
~/go/src/github.com/ilyaigpetrov/proxy-main
package main
import (
"fmt"
"github.com/ilyaigpetrov/proxy"
)
func main() {
proxy.NewProxy("localhost:1111").Start()
fmt.Println("hello world")
select{}
}
iptables proxy tcp
add a comment |
I Want
I write a simple "TCP proxy server": it listens for TCP pockets on localhost, looks up packet IP destination, reads from it and sends reply.
No I want to install it into my Linux system so all TCP packets go through this proxy.
What I Tried
- First, I start my proxy on
localhost:1111
- Then I try:
sudo iptables -t nat -A PREROUTING -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 1111
sudo iptables -t nat -A PREROUTING -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 1111
- Proxy doesn't react (no logs of proxied pockets), sites work in browser.
- Then I try:
sudo iptables -t nat -A OUTPUT -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 1111
sudo iptables -t nat -A OUTPUT -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 1111
I get:
INFO[0000] Starting proxy from="localhost:1111"
hello world
Connectoin from 127.0.0.1:1111
Connectoin to 192.168.1.90:48618
ERRO[0123] Error dialing remote host err="dial tcp 192.168.1.90:48618: getsockopt: connection refused" from="localhost:1111"
And the same error repeating many times.
Sites don't load in a browser.
192.168.1.90 is wlp2s0
Source Codes Of The Proxy in Golang
~/go/src/github.com/ilyaigpetrov/proxy
Based on: https://gist.github.com/ericflo/7dcf4179c315d8bd714c
package proxy
import (
"io"
"net"
"sync"
"fmt"
log "github.com/Sirupsen/logrus"
)
type Proxy struct {
from string
done chan struct{}
log *log.Entry
}
func NewProxy(from string) *Proxy {
log.SetLevel(log.InfoLevel)
return &Proxy{
from: from,
done: make(chan struct{}),
log: log.WithFields(log.Fields{
"from": from,
}),
}
}
func (p *Proxy) Start() error {
p.log.Infoln("Starting proxy")
listener, err := net.Listen("tcp", p.from)
if err != nil {
return err
}
go p.run(listener)
return nil
}
func (p *Proxy) Stop() {
p.log.Infoln("Stopping proxy")
if p.done == nil {
return
}
close(p.done)
p.done = nil
}
func (p *Proxy) run(listener net.Listener) {
for {
select {
case <-p.done:
return
default:
connection, err := listener.Accept()
fmt.Printf("Connectoin from %sn", connection.LocalAddr().String())
fmt.Printf("Connectoin to %sn", connection.RemoteAddr().String())
if err == nil {
go p.handle(connection)
} else {
p.log.WithField("err", err).Errorln("Error accepting conn")
}
}
}
}
func (p *Proxy) handle(connection net.Conn) {
defer connection.Close()
p.log.Debugln("Handling", connection)
defer p.log.Debugln("Done handling", connection)
remote, err := net.Dial("tcp", connection.RemoteAddr().String())
if err != nil {
p.log.WithField("err", err).Errorln("Error dialing remote host")
return
}
defer remote.Close()
wg := &sync.WaitGroup{}
wg.Add(2)
go p.copy(remote, connection, wg)
go p.copy(connection, remote, wg)
wg.Wait()
}
func (p *Proxy) copy(from, to net.Conn, wg *sync.WaitGroup) {
defer wg.Done()
select {
case <-p.done:
return
default:
if _, err := io.Copy(to, from); err != nil {
p.log.WithField("err", err).Errorln("Error from copy")
p.Stop()
return
}
}
}
~/go/src/github.com/ilyaigpetrov/proxy-main
package main
import (
"fmt"
"github.com/ilyaigpetrov/proxy"
)
func main() {
proxy.NewProxy("localhost:1111").Start()
fmt.Println("hello world")
select{}
}
iptables proxy tcp
I Want
I write a simple "TCP proxy server": it listens for TCP pockets on localhost, looks up packet IP destination, reads from it and sends reply.
No I want to install it into my Linux system so all TCP packets go through this proxy.
What I Tried
- First, I start my proxy on
localhost:1111
- Then I try:
sudo iptables -t nat -A PREROUTING -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 1111
sudo iptables -t nat -A PREROUTING -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 1111
- Proxy doesn't react (no logs of proxied pockets), sites work in browser.
- Then I try:
sudo iptables -t nat -A OUTPUT -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 1111
sudo iptables -t nat -A OUTPUT -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 1111
I get:
INFO[0000] Starting proxy from="localhost:1111"
hello world
Connectoin from 127.0.0.1:1111
Connectoin to 192.168.1.90:48618
ERRO[0123] Error dialing remote host err="dial tcp 192.168.1.90:48618: getsockopt: connection refused" from="localhost:1111"
And the same error repeating many times.
Sites don't load in a browser.
192.168.1.90 is wlp2s0
Source Codes Of The Proxy in Golang
~/go/src/github.com/ilyaigpetrov/proxy
Based on: https://gist.github.com/ericflo/7dcf4179c315d8bd714c
package proxy
import (
"io"
"net"
"sync"
"fmt"
log "github.com/Sirupsen/logrus"
)
type Proxy struct {
from string
done chan struct{}
log *log.Entry
}
func NewProxy(from string) *Proxy {
log.SetLevel(log.InfoLevel)
return &Proxy{
from: from,
done: make(chan struct{}),
log: log.WithFields(log.Fields{
"from": from,
}),
}
}
func (p *Proxy) Start() error {
p.log.Infoln("Starting proxy")
listener, err := net.Listen("tcp", p.from)
if err != nil {
return err
}
go p.run(listener)
return nil
}
func (p *Proxy) Stop() {
p.log.Infoln("Stopping proxy")
if p.done == nil {
return
}
close(p.done)
p.done = nil
}
func (p *Proxy) run(listener net.Listener) {
for {
select {
case <-p.done:
return
default:
connection, err := listener.Accept()
fmt.Printf("Connectoin from %sn", connection.LocalAddr().String())
fmt.Printf("Connectoin to %sn", connection.RemoteAddr().String())
if err == nil {
go p.handle(connection)
} else {
p.log.WithField("err", err).Errorln("Error accepting conn")
}
}
}
}
func (p *Proxy) handle(connection net.Conn) {
defer connection.Close()
p.log.Debugln("Handling", connection)
defer p.log.Debugln("Done handling", connection)
remote, err := net.Dial("tcp", connection.RemoteAddr().String())
if err != nil {
p.log.WithField("err", err).Errorln("Error dialing remote host")
return
}
defer remote.Close()
wg := &sync.WaitGroup{}
wg.Add(2)
go p.copy(remote, connection, wg)
go p.copy(connection, remote, wg)
wg.Wait()
}
func (p *Proxy) copy(from, to net.Conn, wg *sync.WaitGroup) {
defer wg.Done()
select {
case <-p.done:
return
default:
if _, err := io.Copy(to, from); err != nil {
p.log.WithField("err", err).Errorln("Error from copy")
p.Stop()
return
}
}
}
~/go/src/github.com/ilyaigpetrov/proxy-main
package main
import (
"fmt"
"github.com/ilyaigpetrov/proxy"
)
func main() {
proxy.NewProxy("localhost:1111").Start()
fmt.Println("hello world")
select{}
}
iptables proxy tcp
iptables proxy tcp
edited Dec 9 '17 at 11:01
TheNamelessOne
asked Dec 9 '17 at 10:44
TheNamelessOneTheNamelessOne
5810
5810
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
This answer was rewritten from chat reply from other person, all rights belong to him, I will delete this post if he posts his own.
This answer doesn't allow to run proxy server as root (but maybe someone as root).
You need to get proxy address before REDIRECT, it is done via getOriginalDst
from go-any-proxy.
// https://gist.github.com/ericflo/7dcf4179c315d8bd714c
package main
import (
"io"
"net"
"sync"
"fmt"
"errors"
"syscall"
log "github.com/Sirupsen/logrus"
)
const SO_ORIGINAL_DST = 80
type Proxy struct {
from string
fromTCP *net.TCPAddr
done chan struct{}
log *log.Entry
}
func NewProxy(from string) *Proxy {
log.SetLevel(log.InfoLevel)
return &Proxy{
from: from,
done: make(chan struct{}),
log: log.WithFields(log.Fields{
"from": from,
}),
}
}
func (p *Proxy) Start() error {
p.log.Infoln("Starting proxy")
var err error
p.fromTCP, err = net.ResolveTCPAddr("tcp", p.from)
if (err != nil) {
panic(err)
}
listener, err := net.ListenTCP("tcp", p.fromTCP)
if err != nil {
return err
}
go p.run(*listener)
return nil
}
func (p *Proxy) Stop() {
p.log.Infoln("Stopping proxy")
if p.done == nil {
return
}
close(p.done)
p.done = nil
}
func getOriginalDst(clientConn *net.TCPConn) (ipv4 string, port uint16, newTCPConn *net.TCPConn, err error) {
if clientConn == nil {
log.Debugf("copy(): oops, dst is nil!")
err = errors.New("ERR: clientConn is nil")
return
}
// test if the underlying fd is nil
remoteAddr := clientConn.RemoteAddr()
if remoteAddr == nil {
log.Debugf("getOriginalDst(): oops, clientConn.fd is nil!")
err = errors.New("ERR: clientConn.fd is nil")
return
}
srcipport := fmt.Sprintf("%v", clientConn.RemoteAddr())
newTCPConn = nil
// net.TCPConn.File() will cause the receiver's (clientConn) socket to be placed in blocking mode.
// The workaround is to take the File returned by .File(), do getsockopt() to get the original
// destination, then create a new *net.TCPConn by calling net.TCPConn.FileConn(). The new TCPConn
// will be in non-blocking mode. What a pain.
clientConnFile, err := clientConn.File()
if err != nil {
log.Infof("GETORIGINALDST|%v->?->FAILEDTOBEDETERMINED|ERR: could not get a copy of the client connection's file object", srcipport)
return
} else {
clientConn.Close()
}
// Get original destination
// this is the only syscall in the Golang libs that I can find that returns 16 bytes
// Example result: &{Multiaddr:[2 0 31 144 206 190 36 45 0 0 0 0 0 0 0 0] Interface:0}
// port starts at the 3rd byte and is 2 bytes long (31 144 = port 8080)
// IPv4 address starts at the 5th byte, 4 bytes long (206 190 36 45)
addr, err := syscall.GetsockoptIPv6Mreq(int(clientConnFile.Fd()), syscall.IPPROTO_IP, SO_ORIGINAL_DST)
log.Debugf("getOriginalDst(): SO_ORIGINAL_DST=%+vn", addr)
if err != nil {
log.Infof("GETORIGINALDST|%v->?->FAILEDTOBEDETERMINED|ERR: getsocketopt(SO_ORIGINAL_DST) failed: %v", srcipport, err)
return
}
newConn, err := net.FileConn(clientConnFile)
if err != nil {
log.Infof("GETORIGINALDST|%v->?->%v|ERR: could not create a FileConn fron clientConnFile=%+v: %v", srcipport, addr, clientConnFile, err)
return
}
if _, ok := newConn.(*net.TCPConn); ok {
newTCPConn = newConn.(*net.TCPConn)
clientConnFile.Close()
} else {
errmsg := fmt.Sprintf("ERR: newConn is not a *net.TCPConn, instead it is: %T (%v)", newConn, newConn)
log.Infof("GETORIGINALDST|%v->?->%v|%s", srcipport, addr, errmsg)
err = errors.New(errmsg)
return
}
ipv4 = itod(uint(addr.Multiaddr[4])) + "." +
itod(uint(addr.Multiaddr[5])) + "." +
itod(uint(addr.Multiaddr[6])) + "." +
itod(uint(addr.Multiaddr[7]))
port = uint16(addr.Multiaddr[2]) << 8 + uint16(addr.Multiaddr[3])
return
}
func (p *Proxy) run(listener net.TCPListener) {
for {
select {
case <-p.done:
return
default:
connection, err := listener.AcceptTCP()
la := connection.LocalAddr()
if (la == nil) {
panic("Connection lost!")
}
fmt.Printf("Connectoin from %sn", la.String())
if err == nil {
go p.handle(*connection)
} else {
p.log.WithField("err", err).Errorln("Error accepting conn")
}
}
}
}
func (p *Proxy) handle(connection net.TCPConn) {
defer connection.Close()
p.log.Debugln("Handling", connection)
defer p.log.Debugln("Done handling", connection)
var clientConn *net.TCPConn;
ipv4, port, clientConn, err := getOriginalDst(&connection)
if (err != nil) {
panic(err)
}
connection = *clientConn;
dest := ipv4 + ":" + fmt.Sprintf("%d", port)
addr, err := net.ResolveTCPAddr("tcp", dest)
if err != nil {
panic(err)
}
fmt.Printf("Connectoin to %sn", dest)
remote, err := net.DialTCP("tcp", nil, addr)
if err != nil {
p.log.WithField("err", err).Errorln("Error dialing remote host")
return
}
defer remote.Close()
wg := &sync.WaitGroup{}
wg.Add(2)
go p.copy(*remote, connection, wg)
go p.copy(connection, *remote, wg)
wg.Wait()
}
func (p *Proxy) copy(from, to net.TCPConn, wg *sync.WaitGroup) {
defer wg.Done()
select {
case <-p.done:
return
default:
if _, err := io.Copy(&to, &from); err != nil {
p.log.WithField("err", err).Errorln("Error from copy")
p.Stop()
return
}
}
}
func itod(i uint) string {
if i == 0 {
return "0"
}
// Assemble decimal in reverse order.
var b [32]byte
bp := len(b)
for ; i > 0; i /= 10 {
bp--
b[bp] = byte(i%10) + '0'
}
return string(b[bp:])
}
func main() {
NewProxy("localhost:1111").Start()
fmt.Println("Server started.")
select{}
}
After that you need:
sudo iptables -t nat -A OUTPUT -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 1111
sudo iptables -t nat -A OUTPUT -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 1111
sudo useradd proxyrunner
go build proxy.go
sudo iptables -t nat -A OUTPUT -m tcp -p tcp --dport 80 -m owner --uid-owner proxyrunner -j RETURN
sudo iptables -t nat -A OUTPUT -m tcp -p tcp --dport 443 -m owner --uid-owner proxyrunner -j RETURN
sudo -u proxyrunner ./proxy
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "106"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f409890%2finstall-tcp-proxy-server-on-localhost-in-linux%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
This answer was rewritten from chat reply from other person, all rights belong to him, I will delete this post if he posts his own.
This answer doesn't allow to run proxy server as root (but maybe someone as root).
You need to get proxy address before REDIRECT, it is done via getOriginalDst
from go-any-proxy.
// https://gist.github.com/ericflo/7dcf4179c315d8bd714c
package main
import (
"io"
"net"
"sync"
"fmt"
"errors"
"syscall"
log "github.com/Sirupsen/logrus"
)
const SO_ORIGINAL_DST = 80
type Proxy struct {
from string
fromTCP *net.TCPAddr
done chan struct{}
log *log.Entry
}
func NewProxy(from string) *Proxy {
log.SetLevel(log.InfoLevel)
return &Proxy{
from: from,
done: make(chan struct{}),
log: log.WithFields(log.Fields{
"from": from,
}),
}
}
func (p *Proxy) Start() error {
p.log.Infoln("Starting proxy")
var err error
p.fromTCP, err = net.ResolveTCPAddr("tcp", p.from)
if (err != nil) {
panic(err)
}
listener, err := net.ListenTCP("tcp", p.fromTCP)
if err != nil {
return err
}
go p.run(*listener)
return nil
}
func (p *Proxy) Stop() {
p.log.Infoln("Stopping proxy")
if p.done == nil {
return
}
close(p.done)
p.done = nil
}
func getOriginalDst(clientConn *net.TCPConn) (ipv4 string, port uint16, newTCPConn *net.TCPConn, err error) {
if clientConn == nil {
log.Debugf("copy(): oops, dst is nil!")
err = errors.New("ERR: clientConn is nil")
return
}
// test if the underlying fd is nil
remoteAddr := clientConn.RemoteAddr()
if remoteAddr == nil {
log.Debugf("getOriginalDst(): oops, clientConn.fd is nil!")
err = errors.New("ERR: clientConn.fd is nil")
return
}
srcipport := fmt.Sprintf("%v", clientConn.RemoteAddr())
newTCPConn = nil
// net.TCPConn.File() will cause the receiver's (clientConn) socket to be placed in blocking mode.
// The workaround is to take the File returned by .File(), do getsockopt() to get the original
// destination, then create a new *net.TCPConn by calling net.TCPConn.FileConn(). The new TCPConn
// will be in non-blocking mode. What a pain.
clientConnFile, err := clientConn.File()
if err != nil {
log.Infof("GETORIGINALDST|%v->?->FAILEDTOBEDETERMINED|ERR: could not get a copy of the client connection's file object", srcipport)
return
} else {
clientConn.Close()
}
// Get original destination
// this is the only syscall in the Golang libs that I can find that returns 16 bytes
// Example result: &{Multiaddr:[2 0 31 144 206 190 36 45 0 0 0 0 0 0 0 0] Interface:0}
// port starts at the 3rd byte and is 2 bytes long (31 144 = port 8080)
// IPv4 address starts at the 5th byte, 4 bytes long (206 190 36 45)
addr, err := syscall.GetsockoptIPv6Mreq(int(clientConnFile.Fd()), syscall.IPPROTO_IP, SO_ORIGINAL_DST)
log.Debugf("getOriginalDst(): SO_ORIGINAL_DST=%+vn", addr)
if err != nil {
log.Infof("GETORIGINALDST|%v->?->FAILEDTOBEDETERMINED|ERR: getsocketopt(SO_ORIGINAL_DST) failed: %v", srcipport, err)
return
}
newConn, err := net.FileConn(clientConnFile)
if err != nil {
log.Infof("GETORIGINALDST|%v->?->%v|ERR: could not create a FileConn fron clientConnFile=%+v: %v", srcipport, addr, clientConnFile, err)
return
}
if _, ok := newConn.(*net.TCPConn); ok {
newTCPConn = newConn.(*net.TCPConn)
clientConnFile.Close()
} else {
errmsg := fmt.Sprintf("ERR: newConn is not a *net.TCPConn, instead it is: %T (%v)", newConn, newConn)
log.Infof("GETORIGINALDST|%v->?->%v|%s", srcipport, addr, errmsg)
err = errors.New(errmsg)
return
}
ipv4 = itod(uint(addr.Multiaddr[4])) + "." +
itod(uint(addr.Multiaddr[5])) + "." +
itod(uint(addr.Multiaddr[6])) + "." +
itod(uint(addr.Multiaddr[7]))
port = uint16(addr.Multiaddr[2]) << 8 + uint16(addr.Multiaddr[3])
return
}
func (p *Proxy) run(listener net.TCPListener) {
for {
select {
case <-p.done:
return
default:
connection, err := listener.AcceptTCP()
la := connection.LocalAddr()
if (la == nil) {
panic("Connection lost!")
}
fmt.Printf("Connectoin from %sn", la.String())
if err == nil {
go p.handle(*connection)
} else {
p.log.WithField("err", err).Errorln("Error accepting conn")
}
}
}
}
func (p *Proxy) handle(connection net.TCPConn) {
defer connection.Close()
p.log.Debugln("Handling", connection)
defer p.log.Debugln("Done handling", connection)
var clientConn *net.TCPConn;
ipv4, port, clientConn, err := getOriginalDst(&connection)
if (err != nil) {
panic(err)
}
connection = *clientConn;
dest := ipv4 + ":" + fmt.Sprintf("%d", port)
addr, err := net.ResolveTCPAddr("tcp", dest)
if err != nil {
panic(err)
}
fmt.Printf("Connectoin to %sn", dest)
remote, err := net.DialTCP("tcp", nil, addr)
if err != nil {
p.log.WithField("err", err).Errorln("Error dialing remote host")
return
}
defer remote.Close()
wg := &sync.WaitGroup{}
wg.Add(2)
go p.copy(*remote, connection, wg)
go p.copy(connection, *remote, wg)
wg.Wait()
}
func (p *Proxy) copy(from, to net.TCPConn, wg *sync.WaitGroup) {
defer wg.Done()
select {
case <-p.done:
return
default:
if _, err := io.Copy(&to, &from); err != nil {
p.log.WithField("err", err).Errorln("Error from copy")
p.Stop()
return
}
}
}
func itod(i uint) string {
if i == 0 {
return "0"
}
// Assemble decimal in reverse order.
var b [32]byte
bp := len(b)
for ; i > 0; i /= 10 {
bp--
b[bp] = byte(i%10) + '0'
}
return string(b[bp:])
}
func main() {
NewProxy("localhost:1111").Start()
fmt.Println("Server started.")
select{}
}
After that you need:
sudo iptables -t nat -A OUTPUT -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 1111
sudo iptables -t nat -A OUTPUT -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 1111
sudo useradd proxyrunner
go build proxy.go
sudo iptables -t nat -A OUTPUT -m tcp -p tcp --dport 80 -m owner --uid-owner proxyrunner -j RETURN
sudo iptables -t nat -A OUTPUT -m tcp -p tcp --dport 443 -m owner --uid-owner proxyrunner -j RETURN
sudo -u proxyrunner ./proxy
add a comment |
This answer was rewritten from chat reply from other person, all rights belong to him, I will delete this post if he posts his own.
This answer doesn't allow to run proxy server as root (but maybe someone as root).
You need to get proxy address before REDIRECT, it is done via getOriginalDst
from go-any-proxy.
// https://gist.github.com/ericflo/7dcf4179c315d8bd714c
package main
import (
"io"
"net"
"sync"
"fmt"
"errors"
"syscall"
log "github.com/Sirupsen/logrus"
)
const SO_ORIGINAL_DST = 80
type Proxy struct {
from string
fromTCP *net.TCPAddr
done chan struct{}
log *log.Entry
}
func NewProxy(from string) *Proxy {
log.SetLevel(log.InfoLevel)
return &Proxy{
from: from,
done: make(chan struct{}),
log: log.WithFields(log.Fields{
"from": from,
}),
}
}
func (p *Proxy) Start() error {
p.log.Infoln("Starting proxy")
var err error
p.fromTCP, err = net.ResolveTCPAddr("tcp", p.from)
if (err != nil) {
panic(err)
}
listener, err := net.ListenTCP("tcp", p.fromTCP)
if err != nil {
return err
}
go p.run(*listener)
return nil
}
func (p *Proxy) Stop() {
p.log.Infoln("Stopping proxy")
if p.done == nil {
return
}
close(p.done)
p.done = nil
}
func getOriginalDst(clientConn *net.TCPConn) (ipv4 string, port uint16, newTCPConn *net.TCPConn, err error) {
if clientConn == nil {
log.Debugf("copy(): oops, dst is nil!")
err = errors.New("ERR: clientConn is nil")
return
}
// test if the underlying fd is nil
remoteAddr := clientConn.RemoteAddr()
if remoteAddr == nil {
log.Debugf("getOriginalDst(): oops, clientConn.fd is nil!")
err = errors.New("ERR: clientConn.fd is nil")
return
}
srcipport := fmt.Sprintf("%v", clientConn.RemoteAddr())
newTCPConn = nil
// net.TCPConn.File() will cause the receiver's (clientConn) socket to be placed in blocking mode.
// The workaround is to take the File returned by .File(), do getsockopt() to get the original
// destination, then create a new *net.TCPConn by calling net.TCPConn.FileConn(). The new TCPConn
// will be in non-blocking mode. What a pain.
clientConnFile, err := clientConn.File()
if err != nil {
log.Infof("GETORIGINALDST|%v->?->FAILEDTOBEDETERMINED|ERR: could not get a copy of the client connection's file object", srcipport)
return
} else {
clientConn.Close()
}
// Get original destination
// this is the only syscall in the Golang libs that I can find that returns 16 bytes
// Example result: &{Multiaddr:[2 0 31 144 206 190 36 45 0 0 0 0 0 0 0 0] Interface:0}
// port starts at the 3rd byte and is 2 bytes long (31 144 = port 8080)
// IPv4 address starts at the 5th byte, 4 bytes long (206 190 36 45)
addr, err := syscall.GetsockoptIPv6Mreq(int(clientConnFile.Fd()), syscall.IPPROTO_IP, SO_ORIGINAL_DST)
log.Debugf("getOriginalDst(): SO_ORIGINAL_DST=%+vn", addr)
if err != nil {
log.Infof("GETORIGINALDST|%v->?->FAILEDTOBEDETERMINED|ERR: getsocketopt(SO_ORIGINAL_DST) failed: %v", srcipport, err)
return
}
newConn, err := net.FileConn(clientConnFile)
if err != nil {
log.Infof("GETORIGINALDST|%v->?->%v|ERR: could not create a FileConn fron clientConnFile=%+v: %v", srcipport, addr, clientConnFile, err)
return
}
if _, ok := newConn.(*net.TCPConn); ok {
newTCPConn = newConn.(*net.TCPConn)
clientConnFile.Close()
} else {
errmsg := fmt.Sprintf("ERR: newConn is not a *net.TCPConn, instead it is: %T (%v)", newConn, newConn)
log.Infof("GETORIGINALDST|%v->?->%v|%s", srcipport, addr, errmsg)
err = errors.New(errmsg)
return
}
ipv4 = itod(uint(addr.Multiaddr[4])) + "." +
itod(uint(addr.Multiaddr[5])) + "." +
itod(uint(addr.Multiaddr[6])) + "." +
itod(uint(addr.Multiaddr[7]))
port = uint16(addr.Multiaddr[2]) << 8 + uint16(addr.Multiaddr[3])
return
}
func (p *Proxy) run(listener net.TCPListener) {
for {
select {
case <-p.done:
return
default:
connection, err := listener.AcceptTCP()
la := connection.LocalAddr()
if (la == nil) {
panic("Connection lost!")
}
fmt.Printf("Connectoin from %sn", la.String())
if err == nil {
go p.handle(*connection)
} else {
p.log.WithField("err", err).Errorln("Error accepting conn")
}
}
}
}
func (p *Proxy) handle(connection net.TCPConn) {
defer connection.Close()
p.log.Debugln("Handling", connection)
defer p.log.Debugln("Done handling", connection)
var clientConn *net.TCPConn;
ipv4, port, clientConn, err := getOriginalDst(&connection)
if (err != nil) {
panic(err)
}
connection = *clientConn;
dest := ipv4 + ":" + fmt.Sprintf("%d", port)
addr, err := net.ResolveTCPAddr("tcp", dest)
if err != nil {
panic(err)
}
fmt.Printf("Connectoin to %sn", dest)
remote, err := net.DialTCP("tcp", nil, addr)
if err != nil {
p.log.WithField("err", err).Errorln("Error dialing remote host")
return
}
defer remote.Close()
wg := &sync.WaitGroup{}
wg.Add(2)
go p.copy(*remote, connection, wg)
go p.copy(connection, *remote, wg)
wg.Wait()
}
func (p *Proxy) copy(from, to net.TCPConn, wg *sync.WaitGroup) {
defer wg.Done()
select {
case <-p.done:
return
default:
if _, err := io.Copy(&to, &from); err != nil {
p.log.WithField("err", err).Errorln("Error from copy")
p.Stop()
return
}
}
}
func itod(i uint) string {
if i == 0 {
return "0"
}
// Assemble decimal in reverse order.
var b [32]byte
bp := len(b)
for ; i > 0; i /= 10 {
bp--
b[bp] = byte(i%10) + '0'
}
return string(b[bp:])
}
func main() {
NewProxy("localhost:1111").Start()
fmt.Println("Server started.")
select{}
}
After that you need:
sudo iptables -t nat -A OUTPUT -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 1111
sudo iptables -t nat -A OUTPUT -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 1111
sudo useradd proxyrunner
go build proxy.go
sudo iptables -t nat -A OUTPUT -m tcp -p tcp --dport 80 -m owner --uid-owner proxyrunner -j RETURN
sudo iptables -t nat -A OUTPUT -m tcp -p tcp --dport 443 -m owner --uid-owner proxyrunner -j RETURN
sudo -u proxyrunner ./proxy
add a comment |
This answer was rewritten from chat reply from other person, all rights belong to him, I will delete this post if he posts his own.
This answer doesn't allow to run proxy server as root (but maybe someone as root).
You need to get proxy address before REDIRECT, it is done via getOriginalDst
from go-any-proxy.
// https://gist.github.com/ericflo/7dcf4179c315d8bd714c
package main
import (
"io"
"net"
"sync"
"fmt"
"errors"
"syscall"
log "github.com/Sirupsen/logrus"
)
const SO_ORIGINAL_DST = 80
type Proxy struct {
from string
fromTCP *net.TCPAddr
done chan struct{}
log *log.Entry
}
func NewProxy(from string) *Proxy {
log.SetLevel(log.InfoLevel)
return &Proxy{
from: from,
done: make(chan struct{}),
log: log.WithFields(log.Fields{
"from": from,
}),
}
}
func (p *Proxy) Start() error {
p.log.Infoln("Starting proxy")
var err error
p.fromTCP, err = net.ResolveTCPAddr("tcp", p.from)
if (err != nil) {
panic(err)
}
listener, err := net.ListenTCP("tcp", p.fromTCP)
if err != nil {
return err
}
go p.run(*listener)
return nil
}
func (p *Proxy) Stop() {
p.log.Infoln("Stopping proxy")
if p.done == nil {
return
}
close(p.done)
p.done = nil
}
func getOriginalDst(clientConn *net.TCPConn) (ipv4 string, port uint16, newTCPConn *net.TCPConn, err error) {
if clientConn == nil {
log.Debugf("copy(): oops, dst is nil!")
err = errors.New("ERR: clientConn is nil")
return
}
// test if the underlying fd is nil
remoteAddr := clientConn.RemoteAddr()
if remoteAddr == nil {
log.Debugf("getOriginalDst(): oops, clientConn.fd is nil!")
err = errors.New("ERR: clientConn.fd is nil")
return
}
srcipport := fmt.Sprintf("%v", clientConn.RemoteAddr())
newTCPConn = nil
// net.TCPConn.File() will cause the receiver's (clientConn) socket to be placed in blocking mode.
// The workaround is to take the File returned by .File(), do getsockopt() to get the original
// destination, then create a new *net.TCPConn by calling net.TCPConn.FileConn(). The new TCPConn
// will be in non-blocking mode. What a pain.
clientConnFile, err := clientConn.File()
if err != nil {
log.Infof("GETORIGINALDST|%v->?->FAILEDTOBEDETERMINED|ERR: could not get a copy of the client connection's file object", srcipport)
return
} else {
clientConn.Close()
}
// Get original destination
// this is the only syscall in the Golang libs that I can find that returns 16 bytes
// Example result: &{Multiaddr:[2 0 31 144 206 190 36 45 0 0 0 0 0 0 0 0] Interface:0}
// port starts at the 3rd byte and is 2 bytes long (31 144 = port 8080)
// IPv4 address starts at the 5th byte, 4 bytes long (206 190 36 45)
addr, err := syscall.GetsockoptIPv6Mreq(int(clientConnFile.Fd()), syscall.IPPROTO_IP, SO_ORIGINAL_DST)
log.Debugf("getOriginalDst(): SO_ORIGINAL_DST=%+vn", addr)
if err != nil {
log.Infof("GETORIGINALDST|%v->?->FAILEDTOBEDETERMINED|ERR: getsocketopt(SO_ORIGINAL_DST) failed: %v", srcipport, err)
return
}
newConn, err := net.FileConn(clientConnFile)
if err != nil {
log.Infof("GETORIGINALDST|%v->?->%v|ERR: could not create a FileConn fron clientConnFile=%+v: %v", srcipport, addr, clientConnFile, err)
return
}
if _, ok := newConn.(*net.TCPConn); ok {
newTCPConn = newConn.(*net.TCPConn)
clientConnFile.Close()
} else {
errmsg := fmt.Sprintf("ERR: newConn is not a *net.TCPConn, instead it is: %T (%v)", newConn, newConn)
log.Infof("GETORIGINALDST|%v->?->%v|%s", srcipport, addr, errmsg)
err = errors.New(errmsg)
return
}
ipv4 = itod(uint(addr.Multiaddr[4])) + "." +
itod(uint(addr.Multiaddr[5])) + "." +
itod(uint(addr.Multiaddr[6])) + "." +
itod(uint(addr.Multiaddr[7]))
port = uint16(addr.Multiaddr[2]) << 8 + uint16(addr.Multiaddr[3])
return
}
func (p *Proxy) run(listener net.TCPListener) {
for {
select {
case <-p.done:
return
default:
connection, err := listener.AcceptTCP()
la := connection.LocalAddr()
if (la == nil) {
panic("Connection lost!")
}
fmt.Printf("Connectoin from %sn", la.String())
if err == nil {
go p.handle(*connection)
} else {
p.log.WithField("err", err).Errorln("Error accepting conn")
}
}
}
}
func (p *Proxy) handle(connection net.TCPConn) {
defer connection.Close()
p.log.Debugln("Handling", connection)
defer p.log.Debugln("Done handling", connection)
var clientConn *net.TCPConn;
ipv4, port, clientConn, err := getOriginalDst(&connection)
if (err != nil) {
panic(err)
}
connection = *clientConn;
dest := ipv4 + ":" + fmt.Sprintf("%d", port)
addr, err := net.ResolveTCPAddr("tcp", dest)
if err != nil {
panic(err)
}
fmt.Printf("Connectoin to %sn", dest)
remote, err := net.DialTCP("tcp", nil, addr)
if err != nil {
p.log.WithField("err", err).Errorln("Error dialing remote host")
return
}
defer remote.Close()
wg := &sync.WaitGroup{}
wg.Add(2)
go p.copy(*remote, connection, wg)
go p.copy(connection, *remote, wg)
wg.Wait()
}
func (p *Proxy) copy(from, to net.TCPConn, wg *sync.WaitGroup) {
defer wg.Done()
select {
case <-p.done:
return
default:
if _, err := io.Copy(&to, &from); err != nil {
p.log.WithField("err", err).Errorln("Error from copy")
p.Stop()
return
}
}
}
func itod(i uint) string {
if i == 0 {
return "0"
}
// Assemble decimal in reverse order.
var b [32]byte
bp := len(b)
for ; i > 0; i /= 10 {
bp--
b[bp] = byte(i%10) + '0'
}
return string(b[bp:])
}
func main() {
NewProxy("localhost:1111").Start()
fmt.Println("Server started.")
select{}
}
After that you need:
sudo iptables -t nat -A OUTPUT -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 1111
sudo iptables -t nat -A OUTPUT -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 1111
sudo useradd proxyrunner
go build proxy.go
sudo iptables -t nat -A OUTPUT -m tcp -p tcp --dport 80 -m owner --uid-owner proxyrunner -j RETURN
sudo iptables -t nat -A OUTPUT -m tcp -p tcp --dport 443 -m owner --uid-owner proxyrunner -j RETURN
sudo -u proxyrunner ./proxy
This answer was rewritten from chat reply from other person, all rights belong to him, I will delete this post if he posts his own.
This answer doesn't allow to run proxy server as root (but maybe someone as root).
You need to get proxy address before REDIRECT, it is done via getOriginalDst
from go-any-proxy.
// https://gist.github.com/ericflo/7dcf4179c315d8bd714c
package main
import (
"io"
"net"
"sync"
"fmt"
"errors"
"syscall"
log "github.com/Sirupsen/logrus"
)
const SO_ORIGINAL_DST = 80
type Proxy struct {
from string
fromTCP *net.TCPAddr
done chan struct{}
log *log.Entry
}
func NewProxy(from string) *Proxy {
log.SetLevel(log.InfoLevel)
return &Proxy{
from: from,
done: make(chan struct{}),
log: log.WithFields(log.Fields{
"from": from,
}),
}
}
func (p *Proxy) Start() error {
p.log.Infoln("Starting proxy")
var err error
p.fromTCP, err = net.ResolveTCPAddr("tcp", p.from)
if (err != nil) {
panic(err)
}
listener, err := net.ListenTCP("tcp", p.fromTCP)
if err != nil {
return err
}
go p.run(*listener)
return nil
}
func (p *Proxy) Stop() {
p.log.Infoln("Stopping proxy")
if p.done == nil {
return
}
close(p.done)
p.done = nil
}
func getOriginalDst(clientConn *net.TCPConn) (ipv4 string, port uint16, newTCPConn *net.TCPConn, err error) {
if clientConn == nil {
log.Debugf("copy(): oops, dst is nil!")
err = errors.New("ERR: clientConn is nil")
return
}
// test if the underlying fd is nil
remoteAddr := clientConn.RemoteAddr()
if remoteAddr == nil {
log.Debugf("getOriginalDst(): oops, clientConn.fd is nil!")
err = errors.New("ERR: clientConn.fd is nil")
return
}
srcipport := fmt.Sprintf("%v", clientConn.RemoteAddr())
newTCPConn = nil
// net.TCPConn.File() will cause the receiver's (clientConn) socket to be placed in blocking mode.
// The workaround is to take the File returned by .File(), do getsockopt() to get the original
// destination, then create a new *net.TCPConn by calling net.TCPConn.FileConn(). The new TCPConn
// will be in non-blocking mode. What a pain.
clientConnFile, err := clientConn.File()
if err != nil {
log.Infof("GETORIGINALDST|%v->?->FAILEDTOBEDETERMINED|ERR: could not get a copy of the client connection's file object", srcipport)
return
} else {
clientConn.Close()
}
// Get original destination
// this is the only syscall in the Golang libs that I can find that returns 16 bytes
// Example result: &{Multiaddr:[2 0 31 144 206 190 36 45 0 0 0 0 0 0 0 0] Interface:0}
// port starts at the 3rd byte and is 2 bytes long (31 144 = port 8080)
// IPv4 address starts at the 5th byte, 4 bytes long (206 190 36 45)
addr, err := syscall.GetsockoptIPv6Mreq(int(clientConnFile.Fd()), syscall.IPPROTO_IP, SO_ORIGINAL_DST)
log.Debugf("getOriginalDst(): SO_ORIGINAL_DST=%+vn", addr)
if err != nil {
log.Infof("GETORIGINALDST|%v->?->FAILEDTOBEDETERMINED|ERR: getsocketopt(SO_ORIGINAL_DST) failed: %v", srcipport, err)
return
}
newConn, err := net.FileConn(clientConnFile)
if err != nil {
log.Infof("GETORIGINALDST|%v->?->%v|ERR: could not create a FileConn fron clientConnFile=%+v: %v", srcipport, addr, clientConnFile, err)
return
}
if _, ok := newConn.(*net.TCPConn); ok {
newTCPConn = newConn.(*net.TCPConn)
clientConnFile.Close()
} else {
errmsg := fmt.Sprintf("ERR: newConn is not a *net.TCPConn, instead it is: %T (%v)", newConn, newConn)
log.Infof("GETORIGINALDST|%v->?->%v|%s", srcipport, addr, errmsg)
err = errors.New(errmsg)
return
}
ipv4 = itod(uint(addr.Multiaddr[4])) + "." +
itod(uint(addr.Multiaddr[5])) + "." +
itod(uint(addr.Multiaddr[6])) + "." +
itod(uint(addr.Multiaddr[7]))
port = uint16(addr.Multiaddr[2]) << 8 + uint16(addr.Multiaddr[3])
return
}
func (p *Proxy) run(listener net.TCPListener) {
for {
select {
case <-p.done:
return
default:
connection, err := listener.AcceptTCP()
la := connection.LocalAddr()
if (la == nil) {
panic("Connection lost!")
}
fmt.Printf("Connectoin from %sn", la.String())
if err == nil {
go p.handle(*connection)
} else {
p.log.WithField("err", err).Errorln("Error accepting conn")
}
}
}
}
func (p *Proxy) handle(connection net.TCPConn) {
defer connection.Close()
p.log.Debugln("Handling", connection)
defer p.log.Debugln("Done handling", connection)
var clientConn *net.TCPConn;
ipv4, port, clientConn, err := getOriginalDst(&connection)
if (err != nil) {
panic(err)
}
connection = *clientConn;
dest := ipv4 + ":" + fmt.Sprintf("%d", port)
addr, err := net.ResolveTCPAddr("tcp", dest)
if err != nil {
panic(err)
}
fmt.Printf("Connectoin to %sn", dest)
remote, err := net.DialTCP("tcp", nil, addr)
if err != nil {
p.log.WithField("err", err).Errorln("Error dialing remote host")
return
}
defer remote.Close()
wg := &sync.WaitGroup{}
wg.Add(2)
go p.copy(*remote, connection, wg)
go p.copy(connection, *remote, wg)
wg.Wait()
}
func (p *Proxy) copy(from, to net.TCPConn, wg *sync.WaitGroup) {
defer wg.Done()
select {
case <-p.done:
return
default:
if _, err := io.Copy(&to, &from); err != nil {
p.log.WithField("err", err).Errorln("Error from copy")
p.Stop()
return
}
}
}
func itod(i uint) string {
if i == 0 {
return "0"
}
// Assemble decimal in reverse order.
var b [32]byte
bp := len(b)
for ; i > 0; i /= 10 {
bp--
b[bp] = byte(i%10) + '0'
}
return string(b[bp:])
}
func main() {
NewProxy("localhost:1111").Start()
fmt.Println("Server started.")
select{}
}
After that you need:
sudo iptables -t nat -A OUTPUT -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 1111
sudo iptables -t nat -A OUTPUT -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 1111
sudo useradd proxyrunner
go build proxy.go
sudo iptables -t nat -A OUTPUT -m tcp -p tcp --dport 80 -m owner --uid-owner proxyrunner -j RETURN
sudo iptables -t nat -A OUTPUT -m tcp -p tcp --dport 443 -m owner --uid-owner proxyrunner -j RETURN
sudo -u proxyrunner ./proxy
edited Dec 10 '17 at 11:04
answered Dec 9 '17 at 15:22
TheNamelessOneTheNamelessOne
5810
5810
add a comment |
add a comment |
Thanks for contributing an answer to Unix & Linux Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f409890%2finstall-tcp-proxy-server-on-localhost-in-linux%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown