初次提交
This commit is contained in:
@@ -0,0 +1,605 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"log"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"rakshasa/common"
|
||||
"rakshasa/httppool"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/luyu6056/ishell"
|
||||
)
|
||||
|
||||
const CheckProxyUrl = "https://myip.fireflysoft.net/"
|
||||
|
||||
type httpProxyClient struct {
|
||||
windowsSize int64
|
||||
isclose int32
|
||||
conn net.Conn
|
||||
udpconn net.Conn
|
||||
|
||||
remote int32
|
||||
server *node
|
||||
id uint32
|
||||
wait chan int
|
||||
close string
|
||||
|
||||
udpMap sync.Map
|
||||
listenId uint32
|
||||
localAddr string
|
||||
isConnect bool
|
||||
method string
|
||||
cfg *common.Addr
|
||||
pool *httppool.HttpPool
|
||||
remoteAddr string
|
||||
remotePort string
|
||||
}
|
||||
|
||||
func (s *httpProxyClient) Write(b []byte) {
|
||||
|
||||
switch b[0] {
|
||||
|
||||
case common.CMD_CONNECT_BYIDADDR_RESULT:
|
||||
|
||||
switch common.NetWork(b[1]) {
|
||||
|
||||
case common.RAW_TCP:
|
||||
if b[2] != 1 {
|
||||
go func() { s.Close("") }()
|
||||
} else if s.method == "CONNECT" {
|
||||
s.conn.Write([]byte("HTTP/1.0 200 Connection established\r\n\r\n"))
|
||||
}
|
||||
case common.RAW_TCP_WITH_PROXY:
|
||||
|
||||
if b[2] != 1 {
|
||||
//重新拉取一个池
|
||||
s.connect()
|
||||
} else if s.method == "CONNECT" {
|
||||
s.conn.Write([]byte("HTTP/1.0 200 Connection established\r\n\r\n"))
|
||||
}
|
||||
default:
|
||||
log.Println("未处理")
|
||||
}
|
||||
|
||||
case common.CMD_CONN_MSG:
|
||||
if common.Debug {
|
||||
|
||||
fmt.Println("收到", crc32.ChecksumIEEE(b[1:]), len(b[1:]))
|
||||
}
|
||||
s.conn.Write(b[1:])
|
||||
s.Addwindow(int64(-len(b[1:])))
|
||||
default:
|
||||
log.Println("未处理")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *httpProxyClient) Close(msg string) {
|
||||
if atomic.CompareAndSwapInt32(&s.isclose, 0, 1) {
|
||||
|
||||
<-s.wait
|
||||
s.wait <- common.CONN_STATUS_CLOSE
|
||||
|
||||
s.server.connMap.Delete(s.id)
|
||||
|
||||
if msg == "" {
|
||||
msg = "未知关闭"
|
||||
}
|
||||
s.close = msg
|
||||
if msg == remoteClose {
|
||||
s.remote = CONN_REMOTE_CLOSE
|
||||
} else if s.remote == CONN_REMOTE_OPEN {
|
||||
s.remote = CONN_REMOTE_CLOSE
|
||||
s.Remoteclose()
|
||||
}
|
||||
if common.Debug {
|
||||
fmt.Println("close 原因", msg)
|
||||
}
|
||||
s.conn.Close()
|
||||
if s.udpconn != nil {
|
||||
s.udpconn.Close()
|
||||
}
|
||||
s.udpMap.Range(func(k, _ interface{}) bool {
|
||||
s.udpMap.Delete(k)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
func (s *httpProxyClient) Addwindow(window int64) {
|
||||
|
||||
windows_size := atomic.AddInt64(&s.windowsSize, window)
|
||||
windows_update_size := int64(common.INIT_WINDOWS_SIZE)
|
||||
|
||||
if windows_size < windows_update_size/2 { //扩大窗口
|
||||
if size := windows_update_size - s.windowsSize; size > 0 {
|
||||
atomic.AddInt64(&s.windowsSize, size)
|
||||
|
||||
go func() {
|
||||
buf := make([]byte, 8)
|
||||
buf[0] = byte(size & 255)
|
||||
buf[1] = byte(size >> 8 & 255)
|
||||
buf[2] = byte(size >> 16 & 255)
|
||||
buf[3] = byte(size >> 24 & 255)
|
||||
buf[4] = byte(size >> 32 & 255)
|
||||
buf[5] = byte(size >> 40 & 255)
|
||||
buf[6] = byte(size >> 48 & 255)
|
||||
buf[7] = byte(size >> 56 & 255)
|
||||
s.server.Write(common.CMD_WINDOWS_UPDATE, s.id, buf)
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func StartHttpProxy(cfg *common.Addr, dst []string, poolfile string) error {
|
||||
var pool *httppool.HttpPool
|
||||
var err error
|
||||
if poolfile != "" {
|
||||
pool, err = httppool.HttpPoolInit(poolfile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var target *node
|
||||
|
||||
if len(dst) == 0 {
|
||||
target = currentNode
|
||||
} else {
|
||||
target, err = GetNodeFromAddrs(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
l := &clientListen{
|
||||
server: target,
|
||||
localAddr: cfg.Addr(),
|
||||
id: common.GetID(),
|
||||
typ: "http",
|
||||
}
|
||||
l.listen, err = StartHttpProxyWithServer(cfg, target, l.id, pool)
|
||||
if err != nil {
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
currentNode.listenMap.Store(l.id, l)
|
||||
return nil
|
||||
}
|
||||
func StartHttpProxyWithServer(cfg *common.Addr, n *node, id uint32, pool *httppool.HttpPool) (net.Listener, error) {
|
||||
l, err := net.Listen("tcp", cfg.Addr())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Println("httpproxy start ", cfg.Addr())
|
||||
go func() {
|
||||
for {
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
if err.(*net.OpError).Err == net.ErrClosed {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
s := &httpProxyClient{
|
||||
cfg: cfg,
|
||||
conn: conn,
|
||||
server: n,
|
||||
listenId: id,
|
||||
pool: pool,
|
||||
}
|
||||
|
||||
go handleHttpProxyLocal(s)
|
||||
|
||||
}
|
||||
}()
|
||||
return l, nil
|
||||
}
|
||||
func (s *httpProxyClient) OnOpened() (close bool) {
|
||||
s.wait = make(chan int, 1)
|
||||
s.remote = CONN_REMOTE_OPEN
|
||||
s.windowsSize = 0
|
||||
s.wait <- common.CONN_STATUS_OK
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 监听本地服务
|
||||
func handleHttpProxyLocal(s *httpProxyClient) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
fmt.Println(err)
|
||||
debug.PrintStack()
|
||||
}
|
||||
}()
|
||||
b := make([]byte, common.MAX_PLAINTEXT-8)
|
||||
if s.OnOpened() {
|
||||
s.Close("无法获得服务器连接")
|
||||
}
|
||||
var data []byte
|
||||
var req = &http1request{}
|
||||
for {
|
||||
n, err := s.conn.Read(b)
|
||||
if err != nil {
|
||||
|
||||
s.Close(err.Error())
|
||||
return
|
||||
}
|
||||
data = append(data, b[:n]...)
|
||||
//尝试读取一个http消息
|
||||
l, _, err := parsereq(req, data)
|
||||
if err != nil {
|
||||
return
|
||||
} else if l == 0 {
|
||||
continue
|
||||
}
|
||||
//判断用户名密码
|
||||
if s.cfg.GetHttpAuthorizationHeader() != "" {
|
||||
var authorize bool
|
||||
for _, herder := range req.header {
|
||||
|
||||
if herder == s.cfg.GetHttpAuthorizationHeader() {
|
||||
authorize = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !authorize {
|
||||
s.conn.Write([]byte("HTTP/1.0 407 Proxy Authentication Required\r\nProxy-Authenticate: Basic realm=\"Access to internal site\"\r\nContent-Length: 0\r\n\r\n"))
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
data = data[l:]
|
||||
switch req.method {
|
||||
case "GET":
|
||||
if u, err := url.Parse(req.uri); err == nil {
|
||||
if i := strings.IndexByte(u.Host, ':'); i > -1 {
|
||||
s.remoteAddr = u.Host[:i]
|
||||
s.remotePort = u.Host[i+1:]
|
||||
} else {
|
||||
s.remoteAddr = u.Host
|
||||
s.remotePort = "80"
|
||||
}
|
||||
s.connect()
|
||||
buf := bufPool.Get().(*bytes.Buffer)
|
||||
buf.Reset()
|
||||
buf.WriteString("GET ")
|
||||
buf.WriteString(req.uri)
|
||||
buf.WriteString(" HTTP/1.1\r\n")
|
||||
for _, header := range req.header {
|
||||
buf.WriteString(header)
|
||||
buf.WriteString("\r\n")
|
||||
}
|
||||
buf.WriteString("\r\n")
|
||||
|
||||
s.write2connect(buf.Bytes())
|
||||
buf.Reset()
|
||||
bufPool.Put(buf)
|
||||
return
|
||||
} else {
|
||||
return
|
||||
}
|
||||
case "CONNECT":
|
||||
s.method = "CONNECT"
|
||||
if i := strings.IndexByte(req.uri, ':'); i > -1 {
|
||||
s.remoteAddr = req.uri[:i]
|
||||
s.remotePort = req.uri[i+1:]
|
||||
s.connect()
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
n, err = s.conn.Read(b)
|
||||
if err != nil {
|
||||
s.Close(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
s.write2connect(b[:n])
|
||||
}
|
||||
default:
|
||||
if common.Debug {
|
||||
fmt.Println("http_proxy 未处理method ", req.method)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
func (s *httpProxyClient) write2connect(data []byte) {
|
||||
var new_size int64
|
||||
if new_size = int64(common.INIT_WINDOWS_SIZE) - s.windowsSize; new_size > 0 { //扩大窗口
|
||||
atomic.AddInt64(&s.windowsSize, new_size)
|
||||
|
||||
} else {
|
||||
new_size = 0
|
||||
}
|
||||
outdata := make([]byte, 8)
|
||||
outdata[0] = byte(new_size)
|
||||
outdata[1] = byte(new_size >> 8)
|
||||
outdata[2] = byte(new_size >> 16)
|
||||
outdata[3] = byte(new_size >> 24)
|
||||
outdata[4] = byte(new_size >> 32)
|
||||
outdata[5] = byte(new_size >> 40)
|
||||
outdata[6] = byte(new_size >> 48)
|
||||
outdata[7] = byte(new_size >> 56)
|
||||
if common.Debug {
|
||||
fmt.Println("发送", crc32.ChecksumIEEE(data), len(data))
|
||||
}
|
||||
s.server.Write(common.CMD_CONN_MSG, s.id, append(outdata, data...))
|
||||
}
|
||||
func (s *httpProxyClient) connect() {
|
||||
if !s.isConnect {
|
||||
|
||||
buf := make([]byte, 2+len(s.remoteAddr)+len(s.remotePort))
|
||||
s.id = s.server.storeConn(s)
|
||||
buf[0] = byte(common.RAW_TCP)
|
||||
copy(buf[1:], s.remoteAddr)
|
||||
buf[1+len(s.remoteAddr)] = ':'
|
||||
copy(buf[2+len(s.remoteAddr):], s.remotePort)
|
||||
//添加代理信息
|
||||
if s.pool != nil {
|
||||
proxy := s.pool.Next()
|
||||
buf[0] = byte(common.RAW_TCP_WITH_PROXY)
|
||||
buf = append(buf, []byte(" "+proxy.String())...)
|
||||
}
|
||||
s.server.Write(common.CMD_CONNECT_BYIDADDR, s.id, buf)
|
||||
if value, ok := s.server.listenMap.Load(s.listenId); ok {
|
||||
switch v := value.(type) {
|
||||
case *serverListen:
|
||||
v.connMap.Store(s.id, s)
|
||||
case *clientListen:
|
||||
v.connMap.Store(s.id, s)
|
||||
}
|
||||
}
|
||||
s.isConnect = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *httpProxyClient) Remoteclose() {
|
||||
|
||||
s.close = "本地要求远程关闭"
|
||||
|
||||
buf := make([]byte, 4)
|
||||
buf[0] = byte(s.id)
|
||||
buf[1] = byte(s.id >> 8)
|
||||
buf[2] = byte(s.id >> 16)
|
||||
buf[3] = byte(s.id >> 24)
|
||||
s.server.Write(common.CMD_DELETE_LISTENCONN_BYID, s.listenId, buf)
|
||||
|
||||
}
|
||||
func init() {
|
||||
|
||||
httpShell := cliInit()
|
||||
httpShell.SetPrompt("rakshasa\\httpproxy>")
|
||||
httpShell.AddCmd(&ishell.Cmd{
|
||||
Name: "list",
|
||||
Help: "列出当前监听的ID和其他信息",
|
||||
Func: func(c *ishell.Context) {
|
||||
var list []*clientListen
|
||||
currentNode.listenMap.Range(func(key, value interface{}) bool {
|
||||
if v, ok := value.(*clientListen); ok {
|
||||
list = append(list, v)
|
||||
}
|
||||
return true
|
||||
})
|
||||
orderClientListen(list)
|
||||
fmt.Println("当前连接数量:", len(list))
|
||||
for _, v := range list {
|
||||
fmt.Println("ID", v.id, "本地端口", v.localAddr, "转发服务器uuid", v.server.uuid)
|
||||
}
|
||||
},
|
||||
})
|
||||
httpShell.AddCmd(&ishell.Cmd{
|
||||
Name: "new-httpproxy",
|
||||
Help: "新建一个httpProxy连接,使用方法 new-httpproxy 配置字串符 目标服务器 代理池文件 如 new-httpproxy admin:[email protected]:8080 127.0.0.1:8881,127.0.0.1:8882 out.txt",
|
||||
Func: func(c *ishell.Context) {
|
||||
if len(c.Args) < 1 {
|
||||
c.Println("参数错误,例子 new-httpproxy admin:[email protected]:8080 127.0.0.1:1081,127.0.0.1:1082 out.txt")
|
||||
return
|
||||
}
|
||||
cfg, err := common.ParseAddr(c.Args[0])
|
||||
if err != nil {
|
||||
c.Println(err)
|
||||
return
|
||||
}
|
||||
nodes := []string{}
|
||||
var filename string
|
||||
if len(c.Args) == 2 {
|
||||
if _, err := os.ReadFile(c.Args[1]); err == nil {
|
||||
filename = c.Args[1]
|
||||
} else {
|
||||
nodes = strings.Split(c.Args[1], ",")
|
||||
}
|
||||
} else if len(c.Args) == 3 {
|
||||
nodes = strings.Split(c.Args[1], ",")
|
||||
filename = c.Args[2]
|
||||
}
|
||||
if err := StartHttpProxy(cfg, nodes, filename); err != nil {
|
||||
c.Println("本地httpProxy启动失败", err)
|
||||
} else {
|
||||
c.Println("本地httpProxy启动成功")
|
||||
}
|
||||
},
|
||||
})
|
||||
httpShell.AddCmd(&ishell.Cmd{
|
||||
Name: "close",
|
||||
Help: "关闭一个socsk5连接,使用方法 close ID",
|
||||
Func: func(c *ishell.Context) {
|
||||
if len(c.Args) != 1 {
|
||||
c.Println("参数错误,例子 close 1")
|
||||
return
|
||||
}
|
||||
id, _ := strconv.Atoi(c.Args[0])
|
||||
var l *clientListen
|
||||
if value, ok := currentNode.listenMap.Load(uint32(id)); ok {
|
||||
if v, ok := value.(*clientListen); ok && v.typ == "http" {
|
||||
l = v
|
||||
}
|
||||
|
||||
}
|
||||
if l == nil {
|
||||
c.Println("没有找到ID为", id, "的连接")
|
||||
} else {
|
||||
l.Close("命令行关闭")
|
||||
l.server.Write(common.CMD_DELETE_LISTEN, l.id, nil)
|
||||
currentNode.listenMap.Delete(uint32(id))
|
||||
|
||||
}
|
||||
},
|
||||
})
|
||||
rootCli.AddCmd(&ishell.Cmd{
|
||||
Name: "httpproxy",
|
||||
Help: "进入httpProxy功能",
|
||||
Func: func(c *ishell.Context) {
|
||||
httpShell.Run()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
type kv struct { //kv键值对
|
||||
key string
|
||||
value string
|
||||
}
|
||||
type http1request struct {
|
||||
Status string
|
||||
|
||||
//解析相关
|
||||
Proto, method string
|
||||
path, query, uri string
|
||||
keep_alive bool
|
||||
header []string //记录整行
|
||||
body []byte
|
||||
//rawdata []byte
|
||||
|
||||
//输出buffer相关
|
||||
//data io.ReadCloser //消息主体
|
||||
//dataSize int //dataSize大于-1就输出,所以要放到最后赋值
|
||||
//out *tls.MsgBuffer //输出消息用buffer,包含header等信息
|
||||
//out1 *tls.MsgBuffer
|
||||
//流水线控制
|
||||
//next *http1request
|
||||
//num int32
|
||||
//alreadyOutHreader bool
|
||||
}
|
||||
|
||||
func (req *http1request) addheader(line string, j int) {
|
||||
if line[:j] == "Proxy-Connection" {
|
||||
req.header = append(req.header, "Connection: "+line[j+2:])
|
||||
req.keep_alive = line[j+2:] == "line[j+2:]"
|
||||
} else {
|
||||
req.header = append(req.header, line)
|
||||
}
|
||||
|
||||
}
|
||||
func parsereq(req *http1request, data []byte) (clen int, resdata []byte, err error) {
|
||||
|
||||
l := len(data)
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = fmt.Errorf("%+v", e)
|
||||
debug.PrintStack()
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
// method, path, proto line
|
||||
|
||||
req.Proto = ""
|
||||
var s = 0
|
||||
var line string
|
||||
var firstLine = true
|
||||
req.body = req.body[:0]
|
||||
req.header = req.header[:0]
|
||||
for i, j := 0, 0; j < l; j += i + 2 {
|
||||
i = bytes.IndexByte(data[j:], 13)
|
||||
|
||||
if i == -1 {
|
||||
break //跳出循环,判断是否包体过大
|
||||
}
|
||||
|
||||
line = string(data[j : j+i])
|
||||
if i > 0 {
|
||||
if firstLine {
|
||||
var q = -1
|
||||
i := strings.IndexByte(line, 32)
|
||||
if i > -1 {
|
||||
req.method = line[:i]
|
||||
line = line[i+1:]
|
||||
for i, v := range line {
|
||||
if v == 63 && q == -1 {
|
||||
q = i
|
||||
} else if v == 32 {
|
||||
if q != -1 {
|
||||
req.path = line[s:q]
|
||||
req.query = line[q+1 : i]
|
||||
} else {
|
||||
req.path = line[s:i]
|
||||
}
|
||||
req.uri = line[s:i]
|
||||
i++
|
||||
req.Proto = line[i:]
|
||||
//判断http返回
|
||||
if req.method == "HTTP/1.1" || req.method == "HTTP/1.0" {
|
||||
/*code, err := strconv.Atoi(req.path)
|
||||
if err == nil {
|
||||
//req.Code = code
|
||||
//req.CodeMsg = req.Proto
|
||||
}*/
|
||||
req.Status = line
|
||||
req.Proto = req.method
|
||||
req.method = ""
|
||||
req.path = ""
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch req.Proto {
|
||||
case "HTTP/1.0":
|
||||
req.keep_alive = false
|
||||
case "HTTP/1.1":
|
||||
req.keep_alive = true
|
||||
default:
|
||||
return 0, nil, fmt.Errorf("malformed http1request")
|
||||
}
|
||||
firstLine = false
|
||||
} else {
|
||||
k := strings.IndexByte(line, 58)
|
||||
if k > -1 && k < len(line) {
|
||||
req.addheader(line, k)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
j += i + 2
|
||||
|
||||
if l-j < clen {
|
||||
return 0, nil, nil
|
||||
}
|
||||
req.body = append(req.body, data[j:j+clen]...)
|
||||
//req.body = append(req.body, data[s:s+clen]...)
|
||||
//req.rawdata = append(req.rawdata, data[:j+clen]...)
|
||||
return j + clen, req.body, nil
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0, nil, nil
|
||||
}
|
||||
Reference in New Issue
Block a user