From 19d0c55aae2748141b1a04419865c72078fc9af2 Mon Sep 17 00:00:00 2001 From: uoosef Date: Sun, 6 Aug 2023 20:58:05 +0330 Subject: [PATCH] revert to old sni extraction method because new method is a wip and unstable, also fix error problems + overall stability --- bepass-cli/config.json | 6 +- bepass-cli/server/server.go | 226 +++++++++++++++++----- bepass-cli/socks5/handle.go | 4 +- bepass-cli/transport/transport.go | 3 + bepass-cli/wsconnadapter/wsconnadapter.go | 3 +- 5 files changed, 183 insertions(+), 59 deletions(-) diff --git a/bepass-cli/config.json b/bepass-cli/config.json index a34a11b..26d7720 100644 --- a/bepass-cli/config.json +++ b/bepass-cli/config.json @@ -3,9 +3,9 @@ "RemoteDNSAddr": "https://yarp.lefolgoc.net/dns-query", "DnsCacheTTL": 30, "BindAddress": "0.0.0.0:8085", - "ChunksLengthBeforeSni": [1, 5], - "SniChunksLength": [1, 5], - "ChunksLengthAfterSni": [1, 5], + "ChunksLengthBeforeSni": [1000, 2000], + "SniChunksLength": [100, 100], + "ChunksLengthAfterSni": [1000, 2000], "DelayBetweenChunks": [20, 50], "WorkerAddress": "https://hello-world-rapid-boat-c250.uoosef.workers.dev/dns-query", "WorkerIPPortAddress": "104.31.16.161:443", diff --git a/bepass-cli/server/server.go b/bepass-cli/server/server.go index 7c4910e..a6bc4d5 100644 --- a/bepass-cli/server/server.go +++ b/bepass-cli/server/server.go @@ -50,95 +50,215 @@ type Server struct { var sniRegex = regexp.MustCompile(`^(?:[a-z0-9-]+\.)+[a-z]+$`) -// getHostname returns the Server Name Indication (SNI) from a TLS Client Hello message. +// getHostname /* This function is basically all most folks want to invoke out of this func (s *Server) getHostname(data []byte) ([]byte, error) { - const ( - sniTypeByte = 0x00 - sniLengthOffset = 2 - ) + extensions, err := s.getExtensionBlock(data) + if err != nil { + return nil, err + } + sn, err := s.getSNBlock(extensions) + if err != nil { + return nil, err + } + sni, err := s.getSNIBlock(sn) + if err != nil { + return nil, err + } + return sni, nil +} - if data[0] != 0x16 { - return nil, fmt.Errorf("not a tls packet") +/* Return the length computed from the two octets starting at index */ +func (s *Server) lengthFromData(data []byte, index int) int { + if index < 0 || index+1 >= len(data) { + return 0 } - // Find the SNI type byte - sniTypeIndex := bytes.IndexByte(data, sniTypeByte) - if sniTypeIndex == -1 { - return nil, fmt.Errorf("could not find SNI type byte in Server Hello message") + b1 := int(data[index]) + b2 := int(data[index+1]) + + return (b1 << 8) + b2 +} + +// getSNIBlock /* Given a Server Name TLS Extension block, parse out and return the SNI +func (s *Server) getSNIBlock(data []byte) ([]byte, error) { + index := 0 + + for { + if index >= len(data) { + break + } + length := s.lengthFromData(data, index) + endIndex := index + 2 + length + if data[index+2] == 0x00 { /* SNI */ + sni := data[index+3:] + sniLength := s.lengthFromData(sni, 0) + return sni[2 : sniLength+2], nil + } + index = endIndex } + return []byte{}, fmt.Errorf( + "finished parsing the SN block without finding an SNI", + ) +} - // Ensure sufficient data to read the SNI length and value - if len(data) < sniTypeIndex+sniLengthOffset+1 { - return nil, fmt.Errorf("insufficient data to read SNI length") +// getSNBlock finds the SN block given a TLS Extensions data block. +func (s *Server) getSNBlock(data []byte) ([]byte, error) { + if len(data) < 4 { + return nil, fmt.Errorf("not enough bytes to be an SN block") } - var sni string - var prev byte - for i := 0; i < len(data); i++ { - if prev == 0 && data[i] == 0 { - start := i + 2 - end := start + int(data[i+1]) - if start < end && end < len(data) { - str := string(data[start:end]) - if sniRegex.MatchString(str) { - sni = str - break - } - } + extensionLength := s.lengthFromData(data, 0) + if extensionLength+4 > len(data) { + return nil, fmt.Errorf("extension size is invalid") + } + data = data[2 : extensionLength+2] + + for index := 0; index+4 < len(data); { + blockLength := s.lengthFromData(data, index+2) + endIndex := index + 4 + blockLength + if data[index] == 0x00 && data[index+1] == 0x00 { + return data[index+4 : endIndex], nil } - prev = data[i] + + index = endIndex } - return []byte(sni), nil + + return nil, fmt.Errorf("SN block not found within the Extension block") } -// getChunkedPackets splits the data into chunks based on SNI and chunk lengths. -func (s *Server) getChunkedPackets(data []byte) map[int][]byte { - const ( - chunkIndexHostname = 0 - chunkIndexSNIValue = 1 - chunkIndexRemainder = 2 - ) +// getExtensionBlock finds the extension block given a raw TLS Client Hello. +func (s *Server) getExtensionBlock(data []byte) ([]byte, error) { + dataLen := len(data) + index := 5 + 38 + + if dataLen <= index+1 { + return nil, fmt.Errorf("not enough bits to be a Client Hello") + } + _, newIndex, err := s.getSessionIDLength(data, index) + if err != nil { + return nil, err + } + index = newIndex + + _, newIndex, err = s.getCipherListLength(data, index) + if err != nil { + return nil, err + } + index = newIndex + + _, newIndex, err = s.getCompressionLength(data, index) + if err != nil { + return nil, err + } + index = newIndex + + if len(data[index:]) == 0 { + return nil, fmt.Errorf("no extensions") + } + + return data[index:], nil +} + +// getSessionIDLength retrieves the session ID length from the TLS Client Hello data. +func (s *Server) getSessionIDLength(data []byte, index int) (int, int, error) { + dataLen := len(data) + + if index+1 >= dataLen { + return 0, 0, fmt.Errorf("not enough bytes for the SessionID") + } + + sessionIDLength := int(data[index]) + newIndex := index + 1 + sessionIDLength + + if newIndex+2 >= dataLen { + return 0, 0, fmt.Errorf("not enough bytes for the SessionID") + } + + return sessionIDLength, newIndex, nil +} + +// getCipherListLength retrieves the cipher list length from the TLS Client Hello data. +func (s *Server) getCipherListLength(data []byte, index int) (int, int, error) { + dataLen := len(data) + + if index+2 >= dataLen { + return 0, 0, fmt.Errorf("not enough bytes for the Cipher List") + } + + cipherListLength := s.lengthFromData(data, index) + newIndex := index + 2 + cipherListLength + + if newIndex+1 >= dataLen { + return 0, 0, fmt.Errorf("not enough bytes for the Cipher List") + } + + return cipherListLength, newIndex, nil +} + +// getCompressionLength retrieves the compression length from the TLS Client Hello data. +func (s *Server) getCompressionLength(data []byte, index int) (int, int, error) { + dataLen := len(data) + + if index+1 >= dataLen { + return 0, 0, fmt.Errorf("not enough bytes for the compression length") + } + + compressionLength := int(data[index]) + newIndex := index + 1 + compressionLength + + if newIndex >= dataLen { + return 0, 0, fmt.Errorf("not enough bytes for the compression length") + } + + return compressionLength, newIndex, nil +} +func (s *Server) getChunkedPackets(data []byte) map[int][]byte { chunks := make(map[int][]byte) hostname, err := s.getHostname(data) if err != nil { - s.Logger.Errorf("get hostname error: %v", err) - chunks[chunkIndexRemainder] = data + chunks[0] = data return chunks } - s.Logger.Printf("Hostname %s", string(hostname)) index := bytes.Index(data, hostname) if index == -1 { return nil } - - chunks[chunkIndexHostname] = make([]byte, index) - copy(chunks[chunkIndexHostname], data[:index]) - chunks[chunkIndexSNIValue] = make([]byte, len(hostname)) - copy(chunks[chunkIndexSNIValue], data[index:index+len(hostname)]) - chunks[chunkIndexRemainder] = make([]byte, len(data)-index-len(hostname)) - copy(chunks[chunkIndexRemainder], data[index+len(hostname):]) + // before sni + chunks[0] = make([]byte, index) + copy(chunks[0], data[:index]) + // sni + chunks[1] = make([]byte, len(hostname)) + copy(chunks[1], data[index:index+len(hostname)]) + // after sni + chunks[2] = make([]byte, len(data)-index-len(hostname)) + copy(chunks[2], data[index+len(hostname):]) return chunks } -// sendSplitChunks sends the chunks to the destination with specified delays. -func (s *Server) sendSplitChunks(dst io.Writer, chunks map[int][]byte, config ChunkConfig) { - chunkLengthMin, chunkLengthMax := config.BeforeSniLength[0], config.BeforeSniLength[1] +func (s *Server) sendSplitChunks(dst io.Writer, chunks map[int][]byte) { + chunkLengthMin, chunkLengthMax := s.ChunkConfig.BeforeSniLength[0], s.ChunkConfig.BeforeSniLength[1] if len(chunks) > 1 { - chunkLengthMin, chunkLengthMax = config.AfterSniLength[0], config.AfterSniLength[1] + chunkLengthMin, chunkLengthMax = s.ChunkConfig.AfterSniLength[0], s.ChunkConfig.AfterSniLength[1] } for _, chunk := range chunks { position := 0 for position < len(chunk) { - chunkLength := rand.Intn(chunkLengthMax-chunkLengthMin) + chunkLengthMin + var chunkLength int + if chunkLengthMax-chunkLengthMin > 0 { + chunkLength = rand.Intn(chunkLengthMax-chunkLengthMin) + chunkLengthMin + } else { + chunkLength = chunkLengthMin + } + if chunkLength > len(chunk)-position { chunkLength = len(chunk) - position } - delay := rand.Intn(config.Delay[1]-config.Delay[0]) + config.Delay[0] + delay := rand.Intn(s.ChunkConfig.Delay[1]-s.ChunkConfig.Delay[0]) + s.ChunkConfig.Delay[0] _, errWrite := dst.Write(chunk[position : position+chunkLength]) if errWrite != nil { @@ -241,7 +361,7 @@ func (s *Server) sendChunks(dst io.Writer, src io.Reader, shouldSplit bool, wg * // check if it's the first packet and its tls packet if index == 0 && dataBuffer[0] == 0x16 && shouldSplit { chunks := s.getChunkedPackets(dataBuffer[:bytesRead]) - s.sendSplitChunks(dst, chunks, s.ChunkConfig) + s.sendSplitChunks(dst, chunks) } else { _, _ = dst.Write(dataBuffer[:bytesRead]) } diff --git a/bepass-cli/socks5/handle.go b/bepass-cli/socks5/handle.go index 0fe5c11..799f87b 100644 --- a/bepass-cli/socks5/handle.go +++ b/bepass-cli/socks5/handle.go @@ -215,9 +215,9 @@ func (sf *Server) handleAssociate(ctx context.Context, writer io.Writer, request } return fmt.Errorf("listen udp failed, %v", err) } - defer bindLn.Close() + //defer bindLn.Close() - sf.logger.Errorf("target addr %v, listen addr: %s", target.RemoteAddr(), bindLn.LocalAddr()) + sf.logger.Info("target addr ", target.RemoteAddr(), "listen addr: ", bindLn.LocalAddr()) // send BND.ADDR and BND.PORT, client used if err = SendReply(writer, statute.RepSuccess, bindLn.LocalAddr()); err != nil { return fmt.Errorf("failed to send reply, %v", err) diff --git a/bepass-cli/transport/transport.go b/bepass-cli/transport/transport.go index 583e485..2d93613 100644 --- a/bepass-cli/transport/transport.go +++ b/bepass-cli/transport/transport.go @@ -38,11 +38,14 @@ func wsDialer(workerAddress, socks5BindAddress string) (*websocket.Conn, error) config := tls.Config{ ServerName: strings.Split(addr, ":")[0], InsecureSkipVerify: true, + NextProtos: []string{"http/1.1"}, + MinVersion: tls.VersionTLS10, } utlsConn := tls.UClient(plainConn, &config, tls.HelloAndroid_11_OkHttp) err = utlsConn.Handshake() if err != nil { _ = plainConn.Close() + fmt.Println(err) return nil, err } return utlsConn, nil diff --git a/bepass-cli/wsconnadapter/wsconnadapter.go b/bepass-cli/wsconnadapter/wsconnadapter.go index 7d18be0..74ca934 100644 --- a/bepass-cli/wsconnadapter/wsconnadapter.go +++ b/bepass-cli/wsconnadapter/wsconnadapter.go @@ -1,6 +1,7 @@ package wsconnadapter import ( + "errors" "github.com/gorilla/websocket" "io" "net" @@ -36,7 +37,7 @@ func (a *Adapter) Read(b []byte) (int, error) { } if messageType != websocket.BinaryMessage { - //return 0, errors.New("unexpected websocket message type") + return 0, errors.New("unexpected websocket message type") } a.reader = reader