所谓代理服务器就是位于发起请求的客户端与原始服务器端之间的一台跳板服务器,正向代理可以隐藏客户端,反向代理可以隐藏原始服务器。
向代理对用户则是不可知的,比如我们访问百度网站,百度的代理服务器对外的域名为 https://www.baidu.com 。具体内部的服务器节点我们不知道,现实中我们通过访问百度的代理服务器后,代理服务器给我们转发请求到他们N多的服务器节点中的一个给我们进行搜索后将结果返回。
package main
import (
"fmt"
"io"
"net"
"os"
"strings"
"sync"
"github.com/urfave/cli/v2"
"golang.org/x/sys/unix"
)
type tcpproxy struct {
lock sync.Mutex
dsts []string
src string
}
var TcpProxy = &cli.Command{
Name: "tcpproxy",
Aliases: []string{""},
Usage: "tcp port proxy",
UsageText: "tcpproxy [--src=0.0.0.0:7777] [--dst=136.19.188.100:9999,136.19.188.110:9999,136.19.188.120:9999]",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "src",
Hidden: true,
},
&cli.StringFlag{
Name: "dst",
Hidden: true,
},
},
Action: func(cctx *cli.Context) error {
src := cctx.String("src")
dst := cctx.String("dst")
if src == "" || dst == "" {
fmt.Println(cctx.Command.UsageText)
return nil
}
tp := &tcpproxy{
src: src,
dsts: strings.Split(dst, ","),
}
tp.server()
return nil
},
}
func main() {
local := []*cli.Command{
TcpProxy,
}
app := &cli.App{
Name: "proxy",
Usage: "proxy tcpproxy",
Version: "v0.0.1",
EnableBashCompletion: true,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "configfile",
EnvVars: []string{""},
Hidden: true,
Value: "cfg.toml",
},
},
Commands: local,
}
if err := app.Run(os.Args); err != nil {
fmt.Fprintf(os.Stderr, "ERROR: %s\n\n", err) // nolint:errcheck
os.Exit(1)
}
}
func unixSetLimit(soft uint64, max uint64) error {
rlimit := unix.Rlimit{
Cur: soft,
Max: max,
}
return unix.Setrlimit(unix.RLIMIT_NOFILE, &rlimit)
}
func (p *tcpproxy) server() {
unixSetLimit(60000, 60000)
listen, err := net.Listen("tcp", p.src)
if err != nil {
fmt.Println(err)
return
}
defer listen.Close()
fmt.Println("listen at:", p.src)
for {
conn, err := listen.Accept()
if err != nil {
fmt.Printf("接受客户端连接错误:%v\n", err)
continue
}
fmt.Println("build new proxy connect. ", "client address =", conn.RemoteAddr(), " local server address=", conn.LocalAddr())
go p.handle(conn)
}
}
func (p *tcpproxy) handle(sconn net.Conn) {
defer sconn.Close()
dst, ok := p.select_dst()
if !ok {
return
}
dconn, err := net.Dial("tcp", dst)
if err != nil {
fmt.Printf("连接%v失败:%v\n", dst, err)
return
}
defer dconn.Close()
ExitChan := make(chan bool, 1)
// 转发到目标服务器
go func(sconn net.Conn, dconn net.Conn, Exit chan bool) {
_, err := io.Copy(dconn, sconn)
if err != nil {
fmt.Printf("往%v发送数据失败:%v\n", dst, err)
ExitChan <- true
}
}(sconn, dconn, ExitChan)
// 从目标服务器返回数据到客户端
go func(sconn net.Conn, dconn net.Conn, Exit chan bool) {
_, err := io.Copy(sconn, dconn)
if err != nil {
fmt.Printf("从%v接收数据失败:%v\n", dst, err)
ExitChan <- true
}
}(sconn, dconn, ExitChan)
<-ExitChan
}
// ip 轮询
func (p *tcpproxy) select_dst() (string, bool) {
p.lock.Lock()
defer p.lock.Unlock()
if len(p.dsts) < 1 {
fmt.Println("failed select_dst()")
return "", false
}
dst := p.dsts[0]
p.dsts = append(p.dsts[1:], dst)
return dst, true
}