我想
我编写了一个简单的“TCP 代理服务器”:它侦听本地主机上的 TCP 包,查找数据包 IP 目的地,从中读取数据并发送回复。
不,我想将其安装到我的 Linux 系统中,以便所有 TCP 数据包都通过此代理。
我尝试过的
- 首先,我启动我的代理
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
本回答是根据别人的聊天回复重写的,版权归他所有,如果他自己发帖我会删除此帖子。
这个答案不允许以 root 身份运行代理服务器(但也许有人是 root)。
您需要在重定向之前获取代理地址,这是通过getOriginalDst
from完成的任意代理。
// 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