제 생각에는
저는 간단한 "TCP 프록시 서버"를 작성했습니다. 로컬 호스트에서 TCP 패킷을 수신하고, 패킷 IP 대상을 찾고, 그로부터 데이터를 읽고 응답을 보냅니다.
아니요, 모든 TCP 패킷이 이 프록시를 통과하도록 Linux 시스템에 설치하고 싶습니다.
내가 시도한 것
- 먼저 에이전트를 시작합니다.
localhost:1111
- 그런 다음 다음을 시도합니다.
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
- 프록시가 응답하지 않고(프록시 포켓의 로그가 없음) 웹사이트가 브라우저에서 실행됩니다.
- 그런 다음 다음을 시도합니다.
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
나는 얻다:
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
이 답변은 다른 사람의 채팅 답변을 바탕으로 다시 작성되었습니다. 저작권은 해당 사람에게 있습니다.
이 답변은 프록시 서버를 루트로 실행하는 것을 허용하지 않습니다.(하지만 누군가가 루트일 수도 있습니다).
리디렉션하기 전에 프록시 주소를 가져와야 하며 이는 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