Files
proxy/server/fwd/analysis.go

311 lines
6.7 KiB
Go
Raw Normal View History

2025-02-27 18:07:00 +08:00
package fwd
import (
"bufio"
"encoding/binary"
2025-02-27 18:07:00 +08:00
"io"
"log/slog"
"proxy-server/pkg/utils"
"proxy-server/server/fwd/socks"
"strings"
"github.com/pkg/errors"
2025-02-27 18:07:00 +08:00
)
func analysisAndLog(conn socks.ProxyConn, reader io.Reader) error {
2025-02-27 18:07:00 +08:00
buf := bufio.NewReader(reader)
domain, proto, err := sniffing(buf)
2025-02-27 18:07:00 +08:00
if err != nil {
err = errors.Wrap(err, "analysis sniffing error")
2025-02-27 18:07:00 +08:00
} 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
}
}
method := string(bytes[:blankIndex])
2025-02-27 18:07:00 +08:00
switch method {
case "GET",
"POST",
"PUT",
"PATCH",
"DELETE",
"HEAD",
"OPTIONS",
"TRACE",
"CONNECT":
return method, true
}
return "", false
}
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 analysisHttp(reader *bufio.Reader) (string, error) {
slog.Debug("analysis http")
// reade top
top, err := httpReadLine(reader)
if err != nil {
return "", errors.Wrap(err, "analysis http read top error")
}
// 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: ")
2025-02-27 18:07:00 +08:00
}
}
return host, nil
2025-02-27 18:07:00 +08:00
}
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
2025-02-27 18:07:00 +08:00
}
func analysisTls(reader *bufio.Reader) (string, error) {
slog.Debug("analysis https")
2025-02-27 18:07:00 +08:00
// tls record
_, err := utils.ReadBuffer(reader, 5)
2025-02-27 18:07:00 +08:00
if err != nil {
return "", errors.Wrap(err, "analysis https read head error")
2025-02-27 18:07:00 +08:00
}
// tls type
hsType, err := reader.ReadByte()
if err != nil {
return "", errors.Wrap(err, "analysis https read hsType error")
2025-02-27 18:07:00 +08:00
}
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")
2025-02-27 18:07:00 +08:00
}
func discord(reader *bufio.Reader) {
2025-02-27 18:07:00 +08:00
_, err := io.Copy(io.Discard, reader)
if err != nil {
slog.Error("analysis discord err", "err", err)
}
}