Files
v2ray_simple/proxy/http/server.go
hahahrfool 447bd8749a 重构所有udp部分的代码! 摒弃了过去非常复杂的upd转发机制;
不再使用 UDP_Putter 等机制去转发udp,而是用一个 netLayer.MsgConn 结构

proxy.Server 和 proxy.Client 接口改动,

Client在握手udp时不再使用handshake方法, 而是用新的 EstablishUDPChannel 方法

Server 在 Handshake时会选择性返回两种接口,io.ReadWriteCloser 用于tcp, netLayer.MsgConn 用于 udp

此时vless、socks5、direct 的udp转发都已经成功经过了 go test 验证, 但是 main.go 还未修改。
2022-04-08 13:49:56 +08:00

160 lines
4.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//Package http implements an http proxy server
package http
import (
"io"
"net"
"net/url"
"strings"
"github.com/hahahrfool/v2ray_simple/httpLayer"
"github.com/hahahrfool/v2ray_simple/netLayer"
"github.com/hahahrfool/v2ray_simple/proxy"
"github.com/hahahrfool/v2ray_simple/utils"
)
const name = "http"
func init() {
proxy.RegisterServer(name, &ServerCreator{})
}
type ServerCreator struct{}
//只有地址和port需要配置非常简单
func (_ ServerCreator) NewServerFromURL(u *url.URL) (proxy.Server, error) {
// TODO: Support Basic Auth
s := &Server{}
return s, nil
}
func (_ ServerCreator) NewServer(dc *proxy.ListenConf) (proxy.Server, error) {
s := &Server{}
return s, nil
}
type Server struct {
proxy.ProxyCommonStruct
}
func (_ Server) CanFallback() bool {
return false //true //暂时不考虑回落,下次再说
}
func (_ Server) Name() string {
return name
}
func (s *Server) Handshake(underlay net.Conn) (newconn io.ReadWriteCloser, _ netLayer.MsgConn, targetAddr netLayer.Addr, err error) {
var b = utils.GetMTU() //一般要获取请求信息,不需要那么长; 就算是http加了path也不用太长
//因为要储存为 firstdata所以也无法直接放回
newconn = underlay
n := 0
n, err = underlay.Read(b[:])
if err != nil {
return
}
//rfc: https://datatracker.ietf.org/doc/html/rfc7231#section-4.3.6
// "CONNECT is intended only for use in requests to a proxy. " 总之CONNECT命令专门用于代理.
// GET如果 path也是带 http:// 头的话,也是可以的
method, path, failreason := httpLayer.GetRequestMethod_and_PATH_from_Bytes(b[:n], true)
if failreason != 0 {
err = utils.ErrInErr{ErrDesc: "get method/path failed, method:" + method + " ,reason:", Data: failreason}
//一个正常的http代理如果遇到了 格式不符的情况的话是要返回 400 等错误代码的
// 但是也不能说不返回400的就是异常服务器因为这可能是服务器自己的策略无视一切错误请求比如防黑客时就常常会如此.
// 所以我们就直接return即可
//
//不过另外注意连method都没有那么就没有回落的可能性
return
}
//log.Println("GetRequestMethod_and_PATH_from_Bytes", method, URL, "data:", string(b[:n]))
var isHTTPS bool
// 如果方法是CONNECT则为https协议的代理
if method == "CONNECT" {
isHTTPS = true
}
var addressStr string
if isHTTPS {
addressStr = path //实测都会自带:443, 也就不需要我们额外判断了
} else {
hostPortURL, err2 := url.Parse(path)
if err2 != nil {
err = err2
return
}
addressStr = hostPortURL.Host
if strings.Index(hostPortURL.Host, ":") == -1 { //host不带端口 默认80
addressStr = hostPortURL.Host + ":80"
}
}
targetAddr, err = netLayer.NewAddr(addressStr)
if err != nil {
return
}
//如果使用https协议需先向客户端表示连接建立完毕
if isHTTPS {
underlay.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n"))
//这个也是https代理的特征所以不适合 公网使用
//正常来说我们的服务器要先dialdial成功之后再返回200但是因为我们目前的架构是在main函数里dail
// 所以就直接写入了.
//另外nginx是没有实现 CONNECT的不过有插件
} else {
newconn = &ProxyConn{
firstData: b[:n],
Conn: underlay,
}
}
return
}
//用于纯http的 代理dial后第一次要把客户端的数据原封不动发送给远程服务端
// 就是说,第一次从 ProxyConn Read时读到的一定是之前读过的数据原理有点像 fallback
type ProxyConn struct {
net.Conn
firstData []byte
notFirst bool
}
func (pc *ProxyConn) Read(p []byte) (int, error) {
if pc.notFirst {
return pc.Conn.Read(p)
}
bs := pc.firstData
pc.firstData = nil
pc.notFirst = true
n := copy(p, bs)
utils.PutBytes(bs)
return n, nil
}
// ReadFrom implements the io.ReaderFrom ReadFrom method.
// 专门用于适配 tcp的splice.
func (pc *ProxyConn) ReadFrom(r io.Reader) (n int64, e error) {
//pc.Conn肯定不是udp但有可能是 unix domain socket。暂时先不考虑这种情况
return pc.Conn.(*net.TCPConn).ReadFrom(r)
}