Skip to content
Open

S5 #7

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ output/

# Dependency directories (remove the comment below to include it)
# vendor/

app.yaml
5 changes: 5 additions & 0 deletions example_app.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
app:
listenAddress: 127.0.0.1:39000
ip_list:
- 127.0.0.1
- 0.0.0.0
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module httpsproxy

go 1.14

require github.com/spf13/viper v1.15.0 // indirect
1,295 changes: 1,295 additions & 0 deletions go.sum

Large diffs are not rendered by default.

51 changes: 40 additions & 11 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,33 +1,62 @@
package main

import (
"flag"
"fmt"
"github.com/spf13/viper"
"httpsproxy/httpsserve"
"httpsproxy/socket5"
"log"
"net"
"os"
)

var logger = log.New(os.Stderr, "httpsproxy:", log.Llongfile|log.LstdFlags)

func main(){
var listenAdress string
flag.StringVar(&listenAdress, "L", "0.0.0.0:8080", "listen address.eg: 127.0.0.1:8080")
flag.Parse()
func init() {
initConfig()
}

func main() {
listenAddress := viper.GetString("app.listenAddress")
fmt.Println("开始监听端口:", listenAddress)
if !checkAddress(listenAddress) {
logger.Fatal("监听端口失败")
}

if !checkAdress(listenAdress){
logger.Fatal("-L listen address format incorrect.Please check it")
ipList := viper.GetStringSlice("app.ip_list")
fmt.Println("代理ip数:", len(ipList))
if len(ipList) < 1 {
logger.Fatal("代理ip不能为空")
}

httpsserve.Serve(listenAdress)
if !checkAddress(listenAddress) {
logger.Fatal("监听端口失败")
}

if viper.GetString("app.type") == "http" {
httpsserve.Serve(listenAddress)
} else {
socket5.Serve(listenAddress)
}

}

func checkAdress(adress string) bool{
_, err := net.ResolveTCPAddr("tcp", adress)
if err != nil{
func checkAddress(listenAddress string) bool {
_, err := net.ResolveTCPAddr("tcp", listenAddress)
if err != nil {
return false
}
return true

}

func initConfig() {
viper.SetConfigName("app")
viper.AddConfigPath(".") // 添加搜索路径

err := viper.ReadInConfig()
if err != nil {
panic(fmt.Errorf("Fatal error config file: %s \n", err))
}

}
42 changes: 36 additions & 6 deletions proxy/server.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,55 @@
package proxy

import (
"fmt"
"github.com/spf13/viper"
"io"
"math/rand"
"net"
"net/http"
"time"
)

func Serve(w http.ResponseWriter, r *http.Request){
func Serve(w http.ResponseWriter, r *http.Request) {
fmt.Println("new con")

if r.Method == http.MethodConnect {
handleHttps(w, r)
} else {
handleHttp(w, r)
}

}
func DialCustom(network, address string, timeout time.Duration, localIP net.IP) (net.Conn, error) {

netAddr := &net.TCPAddr{}

if localIP != nil {
netAddr.IP = localIP
}

fmt.Println("netAddr:", netAddr)

d := net.Dialer{Timeout: timeout, LocalAddr: netAddr}
return d.Dial(network, address)
}

func GetRandomIp() net.IP {
ipList := viper.GetStringSlice("app.ip_list")

r := rand.New(rand.NewSource(time.Now().UnixNano()))
ipStr := ipList[r.Intn(len(ipList))]

ip := net.ParseIP(ipStr)
return ip
}

func handleHttps(w http.ResponseWriter, r *http.Request) {

localIP := GetRandomIp()

destConn, err := DialCustom("tcp", r.Host, time.Second*10, localIP)

func handleHttps(w http.ResponseWriter, r *http.Request){
destConn, err := net.DialTimeout("tcp", r.Host, 60*time.Second)
if err != nil {
http.Error(w, err.Error(), http.StatusServiceUnavailable)
return
Expand All @@ -39,7 +71,7 @@ func handleHttps(w http.ResponseWriter, r *http.Request){

}

func handleHttp(w http.ResponseWriter, r *http.Request){
func handleHttp(w http.ResponseWriter, r *http.Request) {
resp, err := http.DefaultTransport.RoundTrip(r)
if err != nil {
http.Error(w, err.Error(), http.StatusServiceUnavailable)
Expand All @@ -66,5 +98,3 @@ func copyHeader(dst, src http.Header) {
}
}
}


203 changes: 203 additions & 0 deletions socket5/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
package socket5

import (
"bufio"
"context"
"encoding/binary"
"errors"
"fmt"
"httpsproxy/proxy"
"io"
"log"
"net"
"time"
)

const socks5Ver = 0x05
const cmdBind = 0x01
const atypeIPV4 = 0x01
const atypeHOST = 0x03
const atypeIPV6 = 0x04

func Serve(listenAddress string) {
server, err := net.Listen("tcp", listenAddress)
if err != nil {
panic(err)
}
for {
client, err := server.Accept()
if err != nil {
log.Printf("Accept failed %v", err)
continue
}
go process(client)
}
}

func process(conn net.Conn) {
defer conn.Close()
reader := bufio.NewReader(conn)
err := auth(reader, conn)
if err != nil {
log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
return
}
err = connect(reader, conn)
if err != nil {
log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
return
}
}

func auth(reader *bufio.Reader, conn net.Conn) (err error) {
// 读取协议版本(VER)
// +----+----------+----------+
// |VER | NMETHODS | METHODS |
// +----+----------+----------+
// | 1 | 1 | 1 to 255 |
// +----+----------+----------+
// VER: 协议版本,socks5为0x05
// NMETHODS: 支持认证的方法数量
// METHODS: 对应NMETHODS,NMETHODS的值为多少,METHODS就有多少个字节。RFC预定义了一些值的含义,内容如下:
// X’00’ NO AUTHENTICATION REQUIRED
// X’02’ USERNAME/PASSWORD
ver, err := reader.ReadByte()
if err != nil {
return fmt.Errorf("read ver failed:%w", err)
}
// 检查协议版本是否为 SOCKS5
if ver != socks5Ver {
return fmt.Errorf("not supported ver:%v", ver)
}

// 读取支持的认证方法数(NMETHODS)
methodSize, err := reader.ReadByte()
if err != nil {
return fmt.Errorf("read methodSize failed:%w", err)
}
// 读取每个认证方法的字节
method := make([]byte, methodSize)
_, err = io.ReadFull(reader, method)
if err != nil {
return fmt.Errorf("read method failed:%w", err)
}
//log.Println("ver", ver, "method", method)

// 回复客户端的协议版本和认证方法(VER, METHOD)
// +----+--------+
// |VER | METHOD |
// +----+--------+
// | 1 | 1 |
// +----+--------+
_, err = conn.Write([]byte{socks5Ver, 0x00})
if err != nil {
return fmt.Errorf("write failed:%w", err)
}

return nil
}

func connect(reader *bufio.Reader, conn net.Conn) (err error) {
// +----+-----+-------+------+----------+----------+
// |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
// +----+-----+-------+------+----------+----------+
// | 1 | 1 | X'00' | 1 | Variable | 2 |
// +----+-----+-------+------+----------+----------+
// VER 版本号,socks5的值为0x05
// CMD 0x01表示CONNECT请求
// RSV 保留字段,值为0x00
// ATYP 目标地址类型,DST.ADDR的数据对应这个字段的类型。
// 0x01表示IPv4地址,DST.ADDR为4个字节
// 0x03表示域名,DST.ADDR是一个可变长度的域名
// DST.ADDR 一个可变长度的值
// DST.PORT 目标端口,固定2个字节
// 读取协议头部信息
buf := make([]byte, 4)
_, err = io.ReadFull(reader, buf)
if err != nil {
return fmt.Errorf("read header failed:%w", err)
}
ver, cmd, atyp := buf[0], buf[1], buf[3]
if ver != socks5Ver { // 检查版本号是否为 SOCKS5
return fmt.Errorf("not supported ver:%v", ver)
}
if cmd != cmdBind { // 检查指令是否为 CONNECT 请求
return fmt.Errorf("not supported cmd:%v", cmd)
}

// 解析目标地址
addr := ""
switch atyp {
case atypeIPV4: // IPv4 地址
_, err = io.ReadFull(reader, buf)
if err != nil {
return fmt.Errorf("read atyp failed:%w", err)
}
addr = fmt.Sprintf("%d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3])
case atypeHOST: // 域名
hostSize, err := reader.ReadByte()
if err != nil {
return fmt.Errorf("read hostSize failed:%w", err)
}
host := make([]byte, hostSize)
_, err = io.ReadFull(reader, host)
if err != nil {
return fmt.Errorf("read host failed:%w", err)
}
addr = string(host)
case atypeIPV6: // IPv6 地址,暂不支持
return errors.New("IPv6: no supported yet")
default: // 无效的地址类型
return errors.New("invalid atyp")
}

// 解析目标端口
_, err = io.ReadFull(reader, buf[:2])
if err != nil {
return fmt.Errorf("read port failed:%w", err)
}
port := binary.BigEndian.Uint16(buf[:2])

localIP := proxy.GetRandomIp()
// 连接到目标地址
dest, err := proxy.DialCustom("tcp", fmt.Sprintf("%v:%v", addr, port), time.Second*10, localIP)
if err != nil {
return fmt.Errorf("dial dst failed:%w", err)
}
defer dest.Close()
log.Println("dial", addr, port)

// +----+-----+-------+------+----------+----------+
// |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
// +----+-----+-------+------+----------+----------+
// | 1 | 1 | X'00' | 1 | Variable | 2 |
// +----+-----+-------+------+----------+----------+
// VER socks版本,这里为0x05
// REP Relay field,内容取值如下 X’00’ succeeded
// RSV 保留字段
// ATYPE 地址类型
// BND.ADDR 服务绑定的地址
// BND.PORT 服务绑定的端口DST.PORT

// 发送代理成功响应
_, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
if err != nil {
return fmt.Errorf("write failed: %w", err)
}

// 启动双向数据传输协程
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

go func() {
_, _ = io.Copy(dest, reader) // 从客户端读取数据并发送到目标地址
cancel()
}()
go func() {
_, _ = io.Copy(conn, dest) // 从目标地址读取数据并发送回客户端
cancel()
}()

<-ctx.Done()
return nil
}