Documentation ¶
Overview ¶
Package tlsfrag provides tools to split the TLS handshake record containing the Client Hello message into multiple TLS records. This technique, known as TLS record fragmentation, forces censors to maintain state and allocate memory for potential reassembly, making censorship more difficult and resource-intensive. For detailed explanation on how this technique works, refer to Circumventing the GFW with TLS Record Fragmentation.
This package offers convenient helper functions to create a TLS transport.StreamDialer that fragments the TLS handshake record:
- NewFixedLenStreamDialer creates a transport.StreamDialer that splits the Client Hello message into two records. One of the records will have the specified length of splitLen bytes.
- NewStreamDialerFunc offers a more flexible way to fragment Client Hello message. It accepts a callback function that determines the split point, enabling advanced splitting logic such as splitting based on the SNI extension.
Index ¶
- func NewFixedLenStreamDialer(base transport.StreamDialer, splitLen int) (transport.StreamDialer, error)
- func NewRecordLenFuncWriter(base io.Writer, frag RecordLenFragFunc) (io.Writer, error)
- func NewStreamDialerFunc(base transport.StreamDialer, frag FragFunc) (transport.StreamDialer, error)
- func WrapConnFixedLen(base transport.StreamConn, splitLen int) (conn transport.StreamConn, err error)
- func WrapConnFragFunc(base transport.StreamConn, frag FragFunc) (transport.StreamConn, error)
- type FragFunc
- type RecordLenFragFunc
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NewFixedLenStreamDialer ¶
func NewFixedLenStreamDialer(base transport.StreamDialer, splitLen int) (transport.StreamDialer, error)
NewFixedLenStreamDialer is a transport.StreamDialer that fragments the TLS handshake record. It splits the record into two records based on the given splitLen. If splitLen is positive, the first piece will contain the specified number of leading bytes from the original message. If it is negative, the second piece will contain the specified number of trailing bytes.
func NewRecordLenFuncWriter ¶ added in v0.0.16
NewRecordLenFuncWriter creates a io.Writer that splits the first TLS Client Hello record into two records based on the provided RecordLenFragFunc callback. It then writes these records and all subsequent messages to the base io.Writer. If the first message isn't a Client Hello, no splitting occurs and all messages are written directly to base.
The returned io.Writer will implement the io.ReaderFrom interface for optimized performance if the base io.Writer implements io.ReaderFrom.
func NewStreamDialerFunc ¶
func NewStreamDialerFunc(base transport.StreamDialer, frag FragFunc) (transport.StreamDialer, error)
NewStreamDialerFunc creates a transport.StreamDialer that intercepts the initial TLS Client Hello handshake record and splits it into two separate records before sending them. The split point is determined by the callback function frag. The dialer then adds appropriate headers to each record and transmits them sequentially using the base dialer. Following the fragmented Client Hello, all subsequent data is passed through directly without modification.
If you just want to split the record at a fixed position (e.g., always at the 5th byte or 2nd from the last byte), use NewFixedLenStreamDialer. It consumes less resources and is more efficient.
func WrapConnFixedLen ¶ added in v0.0.16
func WrapConnFixedLen(base transport.StreamConn, splitLen int) (conn transport.StreamConn, err error)
WrapConnFixedLen wraps the base transport.StreamConn and splits the first TLS Client Hello record into two records at a fixed position indicated by splitLen. Subsequent data is forwarded without modification.
If splitLen is positive, the first fragmented record content will have that length (excluding the 5-byte header); if splitLen is negative, the second fragmented record content will be that length.
If the first message isn't a valid Client Hello, WrapConnFixedLen forwards all data without modifying it.
This is more efficient than WrapConnFragFunc because it doesn't allocate additional buffers.
func WrapConnFragFunc ¶ added in v0.0.16
func WrapConnFragFunc(base transport.StreamConn, frag FragFunc) (transport.StreamConn, error)
WrapConnFragFunc wraps the base transport.StreamConn and splits the first TLS Client Hello packet into two records according to the frag function. After that, all subsequent data is forwarded without modification.
If the first packet isn't a valid Client Hello, WrapConnFragFunc doesn't modify anything.
The Write to the base transport.StreamConn will be buffered until we have the full initial Client Hello record. If your goal is to simply fragment the Client Hello at a fixed position, WrapConnFixedLen is more efficient as it won't allocate any additional buffers.
Types ¶
type FragFunc ¶
FragFunc takes the content of the first handshake record in a TLS session as input, and returns an integer that represents the fragmentation point index. The input content excludes the 5-byte record header. The returned integer should be in range 1 to len(record)-1. The record will then be fragmented into two parts: record[:n] and record[n:]. If the returned index is either ≤ 0 or ≥ len(record), no fragmentation will occur.
type RecordLenFragFunc ¶ added in v0.0.16
RecordLenFragFunc takes the length of the first handshake record's content (without the 5-byte header), and returns an integer that determines where the record should be fragmented.
The returned splitLen should be in range 1 to recordLen-1. The record content will then be fragmented into two parts: record[:splitLen] and record[splitLen:]. If splitLen is either ≤ 0 or ≥ recordLen, no fragmentation will occur.