私の考えでは

私の考えでは

私の考えでは

私は単純な「TCPプロキシサーバー」を作成しました。ローカルホストからTCPパケットを受信し、パケットIP宛先を探し、そこからデータを読み取り、応答を送信します。

いいえ、すべてのTCPパケットがこのプロキシを通過するようにLinuxシステムにインストールしたいと思います。

私が試したこと

  1. まずエージェントを起動します。localhost:1111
  2. 次に、次のことを試してください。
    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. プロキシが応答せず(プロキシポケットにログがない)、Webサイトはブラウザで実行されます。
  4. 次に、次のことを試してください。
    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. 私は得る:

    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"
    

そして同じ間違いが何度も繰り返されます。ウェブサイトがブラウザに読み込まれません。

192.168.1.90はwlp2s0です。

Golang プロキシのソースコード

~/go/src/github.com/ilyaigpetrov/proxy

ベース: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 %s\n", connection.LocalAddr().String())
      fmt.Printf("Connectoin to %s\n", 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{}
}

答え1

この回答は、他の人のチャットの回答に基づいて書き直されました。著作権はその人にあります。

この回答では、プロキシサーバーをrootとして実行することはできません。(しかし誰かがルートかもしれません)。

リダイレクトする前にプロキシアドレスを取得する必要があります。これはgetOriginalDst次の方法で行います。任意プロキシ

// 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=%+v\n", 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 %s\n", 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 %s\n", 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{}
}

その後、次のものが必要です。

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

関連情報