主要是实现思路。
思路
-
在函数
func (w http.ResponseWriter, r *http.Request)
中,我们可以得到每次请求的r.Context()
。 接下来我们想办法将数据放到这个context
里面来。 - 在
http.Server
的配置中,能够通过ConnContext
,将每次的net.Conn
放到context
里面去。
接下来我们想办法,将net.Conn
封装,进行二次设计。&http.Server{ Addr: BindAddr, TLSConfig: &tls.Config{ ... }, ConnContext: func(ctx context.Context, c net.Conn) context.Context { return context.WithValue(ctx, ConnContextKey, &c) }, }
-
在原来的
net.Conn
的基础上,封装的conn
存储了建立TCP连接后的第一次ClientHello消息。
接下来我们想办法,让每次accept
的不是原始net.Conn
,而是封装的conn
。
这需要对net.Listener
进行封装。type JA3Capture interface { IsCaptured() bool CapturedBytes() []byte JA3() *ja3.JA3 } type conn struct { net.Conn ja3 *ja3.JA3 firstFrame []byte isNotFirstFrame bool } func (c *conn) Read(b []byte) (int, error) { n, e := c.Conn.Read(b) if e == nil && !c.isNotFirstFrame { if c.firstFrame == nil { c.firstFrame = b[:n] } else { c.firstFrame = append(c.firstFrame, b[:n]...) } j, err := ja3.ComputeJA3FromSegment(c.firstFrame) if err == nil { c.isNotFirstFrame = true c.ja3 = j // log.Println("Success: len(c.firstFrame):", len(c.firstFrame)) } else if len(c.firstFrame) > 2048 { // log.Println("Fail: len(c.firstFrame):", len(c.firstFrame)) return n, err } } return n, e } func (c *conn) IsCaptured() bool { return c.isNotFirstFrame } func (c *conn) CapturedBytes() []byte { return c.firstFrame } func (c *conn) JA3() *ja3.JA3 { return c.ja3 }
-
在原来的
net.Listener
的基础上,封装的listener
每一次连接建立后返回的都是封装的conn
。
至此,所有关节已经全部打通,无需再想办法了。type listener struct { net.Listener } func (l *listener) Accept() (net.Conn, error) { c, err := l.Listener.Accept() if err != nil { return nil, err } return &conn{Conn: c}, nil } func NewListener(inner net.Listener) net.Listener { l := new(listener) l.Listener = inner return l }
- 初始化示例:
srv := &http.Server{ Addr: BindAddr, TLSConfig: &tls.Config{ ... }, ConnContext: func(ctx context.Context, c net.Conn) context.Context { return context.WithValue(ctx, ConnContextKey, &c) }, } // 监听TCP端口 ln, e := net.Listen("tcp", BindAddr) if e != nil { panic(e) } // defer ln.Close() lnWrap := NewListener(ln) defer lnWrap.Close() srv.ServeTLS(lnWrap, "", "")
- Handler举例:
func HandlerCapJSON(w http.ResponseWriter, r *http.Request) { // 尝试获取Client Hello消息 netConn := r.Context().Value(ConnContextKey).(*net.Conn) tlsConn, ok := (*netConn).(*tls.Conn) if !ok { w.WriteHeader(400) w.Write([]byte("The HTTP should over TLS!!!")) log.Println("The HTTP should over TLS!!!") return } cap, ok := tlsConn.NetConn().(JA3Capture) if !ok { w.WriteHeader(400) w.Write([]byte("There should be a JA3Capture")) log.Println("There should be a JA3Capture") return } // 返回结果 w.Header().Set("Content-Type", "application/json") w.WriteHeader(200) ja3 := cap.JA3() result := make(map[string]string) result["sni"] = ja3.GetSNI() result["ja3_hash"] = ja3.GetJA3Hash() result["ja3_str"] = ja3.GetJA3String() result["ua"] = r.Header.Get("User-Agent") re, _ := json.Marshal(result) w.Write(re) }