Install TCP Proxy Server on Localhost in Linux












0















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




  1. First, I start my proxy on localhost:1111

  2. 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

  3. Proxy doesn't react (no logs of proxied pockets), sites work in browser.

  4. 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


  5. 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{}
}









share|improve this question





























    0















    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




    1. First, I start my proxy on localhost:1111

    2. 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

    3. Proxy doesn't react (no logs of proxied pockets), sites work in browser.

    4. 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


    5. 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{}
    }









    share|improve this question



























      0












      0








      0








      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




      1. First, I start my proxy on localhost:1111

      2. 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

      3. Proxy doesn't react (no logs of proxied pockets), sites work in browser.

      4. 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


      5. 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{}
      }









      share|improve this question
















      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




      1. First, I start my proxy on localhost:1111

      2. 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

      3. Proxy doesn't react (no logs of proxied pockets), sites work in browser.

      4. 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


      5. 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






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Dec 9 '17 at 11:01







      TheNamelessOne

















      asked Dec 9 '17 at 10:44









      TheNamelessOneTheNamelessOne

      5810




      5810






















          1 Answer
          1






          active

          oldest

          votes


















          0














          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





          share|improve this answer

























            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
            });


            }
            });














            draft saved

            draft discarded


















            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









            0














            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





            share|improve this answer






























              0














              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





              share|improve this answer




























                0












                0








                0







                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





                share|improve this answer















                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






                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited Dec 10 '17 at 11:04

























                answered Dec 9 '17 at 15:22









                TheNamelessOneTheNamelessOne

                5810




                5810






























                    draft saved

                    draft discarded




















































                    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.




                    draft saved


                    draft discarded














                    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





















































                    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







                    Popular posts from this blog

                    How to reconfigure Docker Trusted Registry 2.x.x to use CEPH FS mount instead of NFS and other traditional...

                    is 'sed' thread safe

                    How to make a Squid Proxy server?