Skip to content

Commit

Permalink
Merge pull request #3 from keminar/todo
Browse files Browse the repository at this point in the history
跨平台编译
  • Loading branch information
keminar authored Mar 28, 2021
2 parents c4afdec + cced0cd commit f68e75d
Show file tree
Hide file tree
Showing 15 changed files with 232 additions and 116 deletions.
20 changes: 11 additions & 9 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
.history
.vscode
anyproxy
anyproxy.netgo
tunneld
tunneld.netgo
tunnel/tunneld
tunnel/tunneld.netgo
logs/
.history
.vscode
anyproxy
anyproxy-alpine
anyproxy-darwin
anyproxy-windows.exe
tunneld
tunneld-alpine
tunnel/tunneld
tunnel/tunneld-alpine
logs/
tunnel/logs/
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# Any Proxy

anyproxy 是一个部署在Linux系统上的tcp流转发器,可以直接将本地或网络收到的请求发出,也可以将tcp流转到tunneld或SOCKS代理, 达到让不支持设置代理的网络程序通过HTTP或SOCKS代理运行的目的。可以代替Proxifier做Linux下的客户端, 也可以配合Proxifier当它的服务端。
anyproxy 是一个部署在Linux系统上的tcp流转发器,可以直接将本地或网络收到的请求发出,也可以将请求转到tunneld或SOCKS或charles等代理。可以代替Proxifier做Linux下的客户端, 也可以配合Proxifier当它的服务端。经过跨平台编译,如果只做网络包的转发可以在windows等平台使用

[下载二进制包](http://cloudme.io/anyproxy)
[下载Linux包](http://cloudme.io/anyproxy)[下载Mac包](http://cloudme.io/anyproxy-darwin)
[下载Windows包](http://cloudme.io/anyproxy-windows.exe)[下载alpine包](http://cloudme.io/anyproxy-alpine)

提醒:请使用浏览器右键的“链接另存为”下载文件

tunneld 是一个anyproxy的服务端,部署在服务器上接收anyproxy的请求,并代理发出请求或是转到下一个tunneld。用于跨内网访问资源使用

Expand Down Expand Up @@ -86,7 +89,7 @@ sudo -u anyproxy ./anyproxy
./anyproxy -h
```

注:因为用到了Linux系统函数,并不能跨平台编译。对于windows系统用户可以选择在虚拟机中启动或是win10的WSL中启动
注:因为本地iptables转发是Linux功能,所以windows系统使用时精简掉了此部分功能

> 平滑重启
Expand Down Expand Up @@ -168,6 +171,7 @@ sudo iptables -t nat -D OUTPUT 2
* HTTP/1.1 keep-alive后端也能复用tcp
* ~~修复iptables转发后百度贴吧无法访问的问题~~
* 转发的mysql的连接请求会一直卡住
* ~~支持windows平台使用~~

# 感谢

Expand Down
4 changes: 3 additions & 1 deletion anyproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ var (

func init() {
flag.Usage = help.Usage
flag.StringVar(&gListenAddrPort, "l", ":3000", "Address and port to listen on")
flag.StringVar(&gListenAddrPort, "l", "", "Address and port to listen on")
flag.StringVar(&gProxyServerSpec, "p", "", "Proxy servers to use")
flag.IntVar(&gDebug, "debug", 0, "debug mode (0, 1, 2)")
flag.StringVar(&gPprof, "pprof", "", "pprof port, disable if empty")
Expand Down Expand Up @@ -61,6 +61,7 @@ func main() {
// 是否后台运行
daemon.Daemonize(envRunMode, fd)

gListenAddrPort = config.IfEmptyThen(gListenAddrPort, conf.RouterConfig.Listen, ":3000")
// 支持只输入端口的形式
if !strings.Contains(gListenAddrPort, ":") {
gListenAddrPort = ":" + gListenAddrPort
Expand All @@ -76,6 +77,7 @@ func main() {

logging.SetDefaultLogger(logDir, cmdName, true, 3, writer)
// 设置代理
gProxyServerSpec = config.IfEmptyThen(gProxyServerSpec, conf.RouterConfig.Proxy, "")
config.SetProxyServer(gProxyServerSpec)

// 调试模式
Expand Down
6 changes: 5 additions & 1 deletion conf/router.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
log:
dir: /tmp/
dir: ./logs/
# 使用的DNS服务器 local 当前环境, remote远程, 仅当target使用remote有效
dns: local
# 默认环境,local 当前环境, remote 远程, deny 禁止
Expand All @@ -8,6 +8,10 @@ target: auto
tcpTarget: remote
# 默认域名比对方案
match: equal
# 全局代理服务器, 优先级低于启动传参
proxy:
# 监听端口IP, 优先级低于启动传参
listen:
# 域名
hosts:
- name: github
Expand Down
11 changes: 11 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,14 @@ func SetListenPort(gListenAddrPort string) {
}
ListenPort = uint16(intNum)
}

// IfEmptyThen 取值
func IfEmptyThen(str string, str2 string, str3 string) string {
if str == "" {
if str2 == "" {
return str3
}
return str2
}
return str
}
61 changes: 0 additions & 61 deletions proto/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,74 +5,13 @@ import (
"fmt"
"log"
"net"
"strings"
"syscall"

"github.com/keminar/anyproxy/config"
"github.com/keminar/anyproxy/proto/tcp"
"github.com/keminar/anyproxy/utils/trace"
)

const SO_ORIGINAL_DST = 80

// GetOriginalDstAddr 目标
func GetOriginalDstAddr(tcpConn *net.TCPConn) (dstIP string, dstPort uint16, newTCPConn *net.TCPConn, err error) {
if tcpConn == nil {
err = errors.New("ERR: tcpConn is nil")
return
}

// test if the underlying fd is nil
if tcpConn.RemoteAddr() == nil {
err = errors.New("ERR: clientConn.fd is nil")
return
}

srcipport := fmt.Sprintf("%v", tcpConn.RemoteAddr())

newTCPConn = nil
// connection => file, will make a copy
// 会使得连接变成阻塞模式,需要自己手动 close 原来的 tcp 连接
tcpConnFile, err := tcpConn.File()
if err != nil {
err = fmt.Errorf("GETORIGINALDST|%v->?->FAILEDTOBEDETERMINED|ERR: %v", srcipport, err)
return
}
// 旧链接关闭
tcpConn.Close()
// 文件句柄关闭
defer tcpConnFile.Close()

mreq, err := syscall.GetsockoptIPv6Mreq(int(tcpConnFile.Fd()), syscall.IPPROTO_IP, SO_ORIGINAL_DST)
if err != nil {
err = fmt.Errorf("GETORIGINALDST|%v->?->FAILEDTOBEDETERMINED|ERR: getsocketopt(SO_ORIGINAL_DST) failed: %v", srcipport, err)
return
}

// 开新连接
newConn, err := net.FileConn(tcpConnFile)
if err != nil {
err = fmt.Errorf("GETORIGINALDST|%v->?->%v|ERR: could not create a FileConn from clientConnFile=%+v: %v", srcipport, mreq, tcpConnFile, err)
return
}
if _, ok := newConn.(*net.TCPConn); ok {
newTCPConn = newConn.(*net.TCPConn)

// only support ipv4
dstIP = net.IPv4(mreq.Multiaddr[4], mreq.Multiaddr[5], mreq.Multiaddr[6], mreq.Multiaddr[7]).String()
dstPort = uint16(mreq.Multiaddr[2])<<8 + uint16(mreq.Multiaddr[3])

ipArr := strings.Split(srcipport, ":")
// 来源和目标地址是同一个ip,且目标端口和本服务是同一个端口
if ipArr[0] == dstIP && dstPort == config.ListenPort {
err = fmt.Errorf("may be loop call: %s=>%s:%d", srcipport, dstIP, dstPort)
}
return
}
err = fmt.Errorf("GETORIGINALDST|%v|ERR: newConn is not a *net.TCPConn, instead it is: %T (%v)", srcipport, newConn, newConn)
return
}

type stream interface {
validHead() bool
readRequest(from string) (canProxy bool, err error)
Expand Down
73 changes: 73 additions & 0 deletions proto/stream_addr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// 条件编译 https://segmentfault.com/a/1190000017846997

// +build !windows

package proto

import (
"errors"
"fmt"
"net"
"strings"
"syscall"

"github.com/keminar/anyproxy/config"
)

// GetOriginalDstAddr 目标
func GetOriginalDstAddr(tcpConn *net.TCPConn) (dstIP string, dstPort uint16, newTCPConn *net.TCPConn, err error) {
if tcpConn == nil {
err = errors.New("ERR: tcpConn is nil")
return
}

// test if the underlying fd is nil
if tcpConn.RemoteAddr() == nil {
err = errors.New("ERR: clientConn.fd is nil")
return
}

srcipport := fmt.Sprintf("%v", tcpConn.RemoteAddr())

newTCPConn = nil
// connection => file, will make a copy
// 会使得连接变成阻塞模式,需要自己手动 close 原来的 tcp 连接
tcpConnFile, err := tcpConn.File()
if err != nil {
err = fmt.Errorf("GETORIGINALDST|%v->?->FAILEDTOBEDETERMINED|ERR: %v", srcipport, err)
return
}
// 旧链接关闭
tcpConn.Close()
// 文件句柄关闭
defer tcpConnFile.Close()

mreq, err := syscall.GetsockoptIPv6Mreq(int(tcpConnFile.Fd()), syscall.IPPROTO_IP, SO_ORIGINAL_DST)
if err != nil {
err = fmt.Errorf("GETORIGINALDST|%v->?->FAILEDTOBEDETERMINED|ERR: getsocketopt(SO_ORIGINAL_DST) failed: %v", srcipport, err)
return
}

// 开新连接
newConn, err := net.FileConn(tcpConnFile)
if err != nil {
err = fmt.Errorf("GETORIGINALDST|%v->?->%v|ERR: could not create a FileConn from clientConnFile=%+v: %v", srcipport, mreq, tcpConnFile, err)
return
}
if _, ok := newConn.(*net.TCPConn); ok {
newTCPConn = newConn.(*net.TCPConn)

// only support ipv4
dstIP = net.IPv4(mreq.Multiaddr[4], mreq.Multiaddr[5], mreq.Multiaddr[6], mreq.Multiaddr[7]).String()
dstPort = uint16(mreq.Multiaddr[2])<<8 + uint16(mreq.Multiaddr[3])

ipArr := strings.Split(srcipport, ":")
// 来源和目标地址是同一个ip,且目标端口和本服务是同一个端口
if ipArr[0] == dstIP && dstPort == config.ListenPort {
err = fmt.Errorf("may be loop call: %s=>%s:%d", srcipport, dstIP, dstPort)
}
return
}
err = fmt.Errorf("GETORIGINALDST|%v|ERR: newConn is not a *net.TCPConn, instead it is: %T (%v)", srcipport, newConn, newConn)
return
}
12 changes: 12 additions & 0 deletions proto/stream_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package proto

import (
"errors"
"net"
)

// GetOriginalDstAddr 目标
func GetOriginalDstAddr(tcpConn *net.TCPConn) (dstIP string, dstPort uint16, newTCPConn *net.TCPConn, err error) {
err = errors.New("ERR: windows can not work")
return
}
8 changes: 7 additions & 1 deletion proto/tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,13 @@ func (s *tunnel) dail(dstIP string, dstPort uint16) (err error) {
log.Printf("%s create a new connection to server %s:%d\n", trace.ID(s.req.ID), dstIP, dstPort)
}
connTimeout := time.Duration(5) * time.Second
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", dstIP, dstPort), connTimeout) // 3s timeout
var conn net.Conn
if strings.Contains(dstIP, ":") {
// tcp6 支持
conn, err = net.DialTimeout("tcp6", fmt.Sprintf("[%s]:%d", dstIP, dstPort), connTimeout)
} else {
conn, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", dstIP, dstPort), connTimeout)
}
if err != nil {
return
}
Expand Down
27 changes: 24 additions & 3 deletions scripts/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,30 @@ SCRIPT=$(readlink -f $0)
ROOT_DIR=$(dirname $SCRIPT)/../
cd $ROOT_DIR

# for alpine
go build -tags netgo -o anyproxy.netgo anyproxy.go

# common
# anyproxy
echo "build anyproxy"
# for linux
echo " for linux"
go build -o anyproxy anyproxy.go

# for mac
echo " for mac"
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o anyproxy-darwin anyproxy.go

# for windows
echo " for windows"
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o anyproxy-windows.exe anyproxy.go

# for alpine
echo " for alpine"
go build -tags netgo -o anyproxy-alpine anyproxy.go

# tunneld
echo "build tunneld"
echo " for linux"
go build -o tunnel/tunneld tunnel/tunneld.go

# for alpine
echo " for alpine"
go build -tags netgo -o tunnel/tunneld-alpine tunnel/tunneld.go
4 changes: 3 additions & 1 deletion tunnel/tunneld.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ var (

func init() {
flag.Usage = help.Usage
flag.StringVar(&gListenAddrPort, "l", ":3001", "Address and port to listen on")
flag.StringVar(&gListenAddrPort, "l", "", "Address and port to listen on")
flag.StringVar(&gProxyServerSpec, "p", "", "Proxy servers to use")
flag.IntVar(&gDebug, "d", 0, "debug mode")
flag.BoolVar(&gHelp, "h", false, "This usage message")
Expand Down Expand Up @@ -56,6 +56,7 @@ func main() {
// 是否后台运行
daemon.Daemonize(envRunMode, fd)

gListenAddrPort = config.IfEmptyThen(gListenAddrPort, conf.RouterConfig.Listen, ":3001")
// 支持只输入端口的形式
if !strings.Contains(gListenAddrPort, ":") {
gListenAddrPort = ":" + gListenAddrPort
Expand All @@ -70,6 +71,7 @@ func main() {

logging.SetDefaultLogger(logDir, cmdName, true, 3, writer)
// 设置代理
gProxyServerSpec = config.IfEmptyThen(gProxyServerSpec, conf.RouterConfig.Proxy, "")
config.SetProxyServer(gProxyServerSpec)

server := grace.NewServer(gListenAddrPort, proto.ServerHandler)
Expand Down
4 changes: 3 additions & 1 deletion utils/conf/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@ type Log struct {

// Router 配置文件模型
type Router struct {
Log Log `yaml:"log"`
Listen string `yaml:"listen"` //监听端口
Log Log `yaml:"log"` //日志目录
Token string `yaml:"token"` //加密值
DNS string `yaml:"dns"` //默认的DNS服务器
Target string `yaml:"target"` //http默认访问策略
TCPTarget string `yaml:"tcpTarget"` //tcp默认访问策略
Match string `yaml:"match"` //默认域名比对
Proxy string `yaml:"proxy"` //全局代理服务器
Hosts []Host `yaml:"hosts"` //域名列表
AllowIP []string `yaml:"allowIP"` //可以访问的客户端IP
}
Expand Down
Loading

0 comments on commit f68e75d

Please sign in to comment.