优化数据分析和日志记录,重构连接管理,添加对空缓冲区的处理
This commit is contained in:
@@ -2,54 +2,307 @@ package fwd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"log/slog"
|
||||
"proxy-server/pkg/utils"
|
||||
"proxy-server/server/fwd/socks"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func analysis(reader io.Reader) {
|
||||
func analysisAndLog(conn socks.ProxyConn, reader io.Reader) error {
|
||||
buf := bufio.NewReader(reader)
|
||||
first, err := buf.Peek(8)
|
||||
if err != nil {
|
||||
slog.Error("analysis peek error", "err", err)
|
||||
} else {
|
||||
|
||||
switch {
|
||||
case first[0] == 0x16:
|
||||
analysisHttps(reader)
|
||||
case
|
||||
string(first[:4]) == "GET ",
|
||||
// string(first[:4]) == "PUT ",
|
||||
string(first[:5]) == "POST ":
|
||||
// string(first[:4]) == "HEAD ",
|
||||
// string(first[:4]) == "TRACE ",
|
||||
// string(first[:4]) == "PATCH ",
|
||||
// string(first[:4]) == "DELETE ",
|
||||
// string(first[:4]) == "CONNECT ",
|
||||
// string(first[:4]) == "OPTIONS ":
|
||||
analysisHttp(reader)
|
||||
domain, proto, err := sniffing(buf)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "analysis sniffing error")
|
||||
} else {
|
||||
slog.Info(
|
||||
"用户访问记录",
|
||||
slog.Uint64("uid", uint64(conn.Uid)),
|
||||
slog.String("user", conn.Conn.RemoteAddr().String()),
|
||||
slog.String("proxy", "socks"),
|
||||
slog.String("node", conn.Conn.LocalAddr().String()),
|
||||
slog.String("proto", proto),
|
||||
slog.String("dest", conn.Dest),
|
||||
slog.String("domain", domain),
|
||||
)
|
||||
}
|
||||
go func() {
|
||||
discord(buf)
|
||||
}()
|
||||
return err
|
||||
}
|
||||
|
||||
func sniffing(reader *bufio.Reader) (string, string, error) {
|
||||
peek, err := reader.Peek(8)
|
||||
if err != nil {
|
||||
return "", "", errors.Wrap(err, "sniffing peek error")
|
||||
}
|
||||
|
||||
method, ok := isHttp(peek)
|
||||
if ok {
|
||||
domain, err := analysisHttp(reader)
|
||||
return domain, "http(" + method + ")", err
|
||||
}
|
||||
|
||||
tlsType, tlsVersion, ok := isTls(peek)
|
||||
if ok {
|
||||
var domain string
|
||||
if tlsType == "handshake" {
|
||||
domain, err = analysisTls(reader)
|
||||
}
|
||||
return domain, "tls(" + tlsType + "," + tlsVersion + ")", err
|
||||
}
|
||||
|
||||
return "nil", "tcp", nil
|
||||
}
|
||||
|
||||
func isHttp(bytes []byte) (string, bool) {
|
||||
|
||||
var blankIndex int
|
||||
for i := range bytes {
|
||||
if bytes[i] == ' ' {
|
||||
blankIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
discord(reader)
|
||||
method := string(bytes[:blankIndex])
|
||||
|
||||
switch method {
|
||||
case "GET",
|
||||
"POST",
|
||||
"PUT",
|
||||
"PATCH",
|
||||
"DELETE",
|
||||
"HEAD",
|
||||
"OPTIONS",
|
||||
"TRACE",
|
||||
"CONNECT":
|
||||
return method, true
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
func analysisHttp(reader io.Reader) {
|
||||
func isTls(bytes []byte) (string, string, bool) {
|
||||
|
||||
var tlsType string
|
||||
switch bytes[0] {
|
||||
case 0x14:
|
||||
tlsType = "change-cipher-spec"
|
||||
case 0x15:
|
||||
tlsType = "alert"
|
||||
case 0x16:
|
||||
tlsType = "handshake"
|
||||
case 0x17:
|
||||
tlsType = "application-data"
|
||||
}
|
||||
|
||||
var tlsVersion string
|
||||
if bytes[1] == 0x03 {
|
||||
switch bytes[2] {
|
||||
case 0x00:
|
||||
tlsVersion = "SSL3.0"
|
||||
case 0x01:
|
||||
tlsVersion = "TLS1.0"
|
||||
case 0x02:
|
||||
tlsVersion = "TLS1.1"
|
||||
case 0x03:
|
||||
tlsVersion = "TLS1.2"
|
||||
}
|
||||
}
|
||||
if tlsType != "" && tlsVersion != "" {
|
||||
return tlsType, tlsVersion, true
|
||||
} else {
|
||||
return "", "", false
|
||||
}
|
||||
}
|
||||
|
||||
func analysisHttps(reader io.Reader) {
|
||||
|
||||
head, err := utils.ReadBuffer(reader, 5)
|
||||
func analysisHttp(reader *bufio.Reader) (string, error) {
|
||||
slog.Debug("analysis http")
|
||||
// reade top
|
||||
top, err := httpReadLine(reader)
|
||||
if err != nil {
|
||||
slog.Error("analysis https err", "err", err)
|
||||
return
|
||||
return "", errors.Wrap(err, "analysis http read top error")
|
||||
}
|
||||
|
||||
if head[1] == 0x03 && head[2] == 0x03 {
|
||||
// tls1.2
|
||||
// read header
|
||||
host := strings.Split(top, " ")[1]
|
||||
for {
|
||||
line, err := httpReadLine(reader)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if line == "" {
|
||||
break
|
||||
}
|
||||
if strings.HasPrefix(line, "Host: ") {
|
||||
host = strings.TrimPrefix(line, "Host: ")
|
||||
}
|
||||
}
|
||||
|
||||
return host, nil
|
||||
}
|
||||
|
||||
func discord(reader io.Reader) {
|
||||
func httpReadLine(reader *bufio.Reader) (line string, err error) {
|
||||
|
||||
var lineStr strings.Builder
|
||||
for {
|
||||
line, prefix, err := reader.ReadLine()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "analysis http read line error")
|
||||
}
|
||||
lineStr.Write(line)
|
||||
if !prefix {
|
||||
break
|
||||
}
|
||||
}
|
||||
return lineStr.String(), nil
|
||||
}
|
||||
|
||||
func analysisTls(reader *bufio.Reader) (string, error) {
|
||||
slog.Debug("analysis https")
|
||||
|
||||
// tls record
|
||||
_, err := utils.ReadBuffer(reader, 5)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "analysis https read head error")
|
||||
}
|
||||
|
||||
// tls type
|
||||
hsType, err := reader.ReadByte()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "analysis https read hsType error")
|
||||
}
|
||||
|
||||
switch hsType {
|
||||
case 0x01: // client hello
|
||||
|
||||
// length
|
||||
_, err = utils.ReadBuffer(reader, 3)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "analysis https read tls length error")
|
||||
}
|
||||
|
||||
// version
|
||||
_, err = utils.ReadBuffer(reader, 2)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "analysis https read version error")
|
||||
}
|
||||
|
||||
// random
|
||||
_, err = utils.ReadBuffer(reader, 32)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "analysis https read random error")
|
||||
}
|
||||
|
||||
// session id length
|
||||
sessionIdLen, err := reader.ReadByte()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "analysis https read sessionIdLen error")
|
||||
}
|
||||
// session id
|
||||
_, err = utils.ReadBuffer(reader, int(sessionIdLen))
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "analysis https read sessionId error")
|
||||
}
|
||||
|
||||
// cipher suites length
|
||||
cLenBuf, err := utils.ReadBuffer(reader, 2)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "analysis https read cLen error")
|
||||
}
|
||||
cLen := binary.BigEndian.Uint16(cLenBuf)
|
||||
// cipher suites
|
||||
_, err = utils.ReadBuffer(reader, int(cLen))
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "analysis https read c error")
|
||||
}
|
||||
|
||||
// compression methods length
|
||||
cmLen, err := reader.ReadByte()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "analysis https read cmLen error")
|
||||
}
|
||||
// compression methods
|
||||
_, err = utils.ReadBuffer(reader, int(cmLen))
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "analysis https read cm error")
|
||||
}
|
||||
|
||||
// extensions length
|
||||
eLenBuf, err := utils.ReadBuffer(reader, 2)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "analysis https read eLen error")
|
||||
}
|
||||
eLen := binary.BigEndian.Uint16(eLenBuf)
|
||||
|
||||
// extensions
|
||||
host := ""
|
||||
|
||||
for i := 0; i < int(eLen); {
|
||||
|
||||
// extension type
|
||||
eTypeBuf, err := utils.ReadBuffer(reader, 2)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "analysis https read extension type error")
|
||||
}
|
||||
eType := binary.BigEndian.Uint16(eTypeBuf)
|
||||
|
||||
// extension length
|
||||
eLenBuf, err := utils.ReadBuffer(reader, 2)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "analysis https read extension length error")
|
||||
}
|
||||
eLen := binary.BigEndian.Uint16(eLenBuf)
|
||||
|
||||
// server name
|
||||
if eType == 0x00 {
|
||||
// server name list length
|
||||
_, err = utils.ReadBuffer(reader, 2)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "analysis https read server name list length error")
|
||||
}
|
||||
// server name type
|
||||
_, err = reader.ReadByte()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "analysis https read server name type error")
|
||||
}
|
||||
// server name length
|
||||
sLenBuf, err := utils.ReadBuffer(reader, 2)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "analysis https read server name length error")
|
||||
}
|
||||
sLen := binary.BigEndian.Uint16(sLenBuf)
|
||||
// server name
|
||||
bytes, err := utils.ReadBuffer(reader, int(sLen))
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "analysis https read server name error")
|
||||
}
|
||||
|
||||
host = string(bytes)
|
||||
return host, nil
|
||||
|
||||
} else {
|
||||
// other extension
|
||||
_, err = utils.ReadBuffer(reader, int(eLen))
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "analysis https read extension error")
|
||||
}
|
||||
}
|
||||
i += 4 + int(eLen)
|
||||
}
|
||||
default:
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return "", errors.New("analysis https error")
|
||||
}
|
||||
|
||||
func discord(reader *bufio.Reader) {
|
||||
_, err := io.Copy(io.Discard, reader)
|
||||
if err != nil {
|
||||
slog.Error("analysis discord err", "err", err)
|
||||
|
||||
Reference in New Issue
Block a user