This is an automated email from the git hooks/post-receive script.
dcf pushed a commit to branch turbotunnel in repository pluggable-transports/meek.
commit b93c5a027f7f60636c84bd405ec7c8089d4a8b0e Merge: 46c988b e9eff72 Author: David Fifield david@bamsoftware.com AuthorDate: Fri Oct 28 01:11:22 2022 -0600
Merge branch 'main' into turbotunnel
common/encapsulation/encapsulation.go | 22 ++- doc/meek-client.1 | 208 ++++++++++++++++++++++- doc/meek-client.1.txt | 24 ++- doc/meek-server.1 | 24 ++- doc/meek-server.1.txt | 6 +- go.mod | 8 +- go.sum | 28 ++- meek-client-torbrowser/linux.go | 2 + meek-client-torbrowser/mac.go | 2 + meek-client-torbrowser/meek-client-torbrowser.go | 4 +- meek-client-torbrowser/terminate_other.go | 1 + meek-client-torbrowser/terminate_windows.go | 1 + meek-client-torbrowser/windows.go | 2 + meek-client/meek-client.go | 24 ++- meek-client/proxy_test.go | 2 +- meek-client/torrc | 1 - meek-client/utls.go | 68 ++++---- meek-client/utls_test.go | 45 +---- meek-server/certificate.go | 1 + meek-server/meek-server.go | 19 ++- 20 files changed, 363 insertions(+), 129 deletions(-)
diff --cc common/encapsulation/encapsulation.go index 64a58d8,0000000..9fd7449 mode 100644,000000..100644 --- a/common/encapsulation/encapsulation.go +++ b/common/encapsulation/encapsulation.go @@@ -1,194 -1,0 +1,200 @@@ +// Package encapsulation implements a way of encoding variable-size chunks of +// data and padding into a byte stream. +// +// Each chunk of data or padding starts with a variable-size length prefix. One +// bit ("d") in the first byte of the prefix indicates whether the chunk +// represents data or padding (1=data, 0=padding). Another bit ("c" for +// "continuation") is the indicates whether there are more bytes in the length +// prefix. The remaining 6 bits ("x") encode part of the length value. - // dcxxxxxx ++// ++// dcxxxxxx ++// +// If the continuation bit is set, then the next byte is also part of the length +// prefix. It lacks the "d" bit, has its own "c" bit, and 7 value-carrying bits +// ("y"). - // cyyyyyyy ++// ++// cyyyyyyy ++// +// The length is decoded by concatenating value-carrying bits, from left to +// right, of all value-carrying bits, up to and including the first byte whose +// "c" bit is 0. Although in principle this encoding would allow for length +// prefixes of any size, length prefixes are arbitrarily limited to 3 bytes and +// any attempt to read or write a longer one is an error. These are therefore +// the only valid formats: - // 00xxxxxx xxxxxx₂ bytes of padding - // 10xxxxxx xxxxxx₂ bytes of data - // 01xxxxxx 0yyyyyyy xxxxxxyyyyyyy₂ bytes of padding - // 11xxxxxx 0yyyyyyy xxxxxxyyyyyyy₂ bytes of data - // 01xxxxxx 1yyyyyyy 0zzzzzzz xxxxxxyyyyyyyzzzzzzz₂ bytes of padding - // 11xxxxxx 1yyyyyyy 0zzzzzzz xxxxxxyyyyyyyzzzzzzz₂ bytes of data ++// ++// 00xxxxxx xxxxxx₂ bytes of padding ++// 10xxxxxx xxxxxx₂ bytes of data ++// 01xxxxxx 0yyyyyyy xxxxxxyyyyyyy₂ bytes of padding ++// 11xxxxxx 0yyyyyyy xxxxxxyyyyyyy₂ bytes of data ++// 01xxxxxx 1yyyyyyy 0zzzzzzz xxxxxxyyyyyyyzzzzzzz₂ bytes of padding ++// 11xxxxxx 1yyyyyyy 0zzzzzzz xxxxxxyyyyyyyzzzzzzz₂ bytes of data ++// +// The maximum encodable length is 11111111111111111111₂ = 0xfffff = 1048575. +// There is no requirement to use a length prefix of minimum size; i.e. 00000100 +// and 01000000 00000100 are both valid encodings of the value 4. +// +// After the length prefix follow that many bytes of padding or data. There are +// no restrictions on the value of bytes comprising padding. +// +// The idea for this encapsulation is sketched here: +// https://github.com/net4people/bbs/issues/9#issuecomment-524095186 +package encapsulation + +import ( + "errors" + "io" + "io/ioutil" +) + +// ErrTooLong is the error returned when an encoded length prefix is longer than +// 3 bytes, or when ReadData receives an input whose length is too large to +// encode in a 3-byte length prefix. +var ErrTooLong = errors.New("length prefix is too long") + +// ReadData returns a new slice with the contents of the next available data +// chunk, skipping over any padding chunks that may come first. The returned +// error value is nil if and only if a data chunk was present and was read in +// its entirety. The returned error is io.EOF only if r ended before the first +// byte of a length prefix. If r ended in the middle of a length prefix or +// data/padding, the returned error is io.ErrUnexpectedEOF. +func ReadData(r io.Reader) ([]byte, error) { + for { + var b [1]byte + _, err := r.Read(b[:]) + if err != nil { + // This is the only place we may return a real io.EOF. + return nil, err + } + isData := (b[0] & 0x80) != 0 + moreLength := (b[0] & 0x40) != 0 + n := int(b[0] & 0x3f) + for i := 0; moreLength; i++ { + if i >= 2 { + return nil, ErrTooLong + } + _, err := r.Read(b[:]) + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + if err != nil { + return nil, err + } + moreLength = (b[0] & 0x80) != 0 + n = (n << 7) | int(b[0]&0x7f) + } + if isData { + p := make([]byte, n) + _, err := io.ReadFull(r, p) + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + if err != nil { + return nil, err + } + return p, err + } else { + _, err := io.CopyN(ioutil.Discard, r, int64(n)) + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + if err != nil { + return nil, err + } + } + } +} + +// dataPrefixForLength returns a length prefix for the given length, with the +// "d" bit set to 1. +func dataPrefixForLength(n int) ([]byte, error) { + switch { + case (n>>0)&0x3f == (n >> 0): + return []byte{0x80 | byte((n>>0)&0x3f)}, nil + case (n>>7)&0x3f == (n >> 7): + return []byte{0xc0 | byte((n>>7)&0x3f), byte((n >> 0) & 0x7f)}, nil + case (n>>14)&0x3f == (n >> 14): + return []byte{0xc0 | byte((n>>14)&0x3f), 0x80 | byte((n>>7)&0x7f), byte((n >> 0) & 0x7f)}, nil + default: + return nil, ErrTooLong + } +} + +// WriteData encodes a data chunk into w. It returns the total number of bytes +// written; i.e., including the length prefix. The error is ErrTooLong if the +// length of data cannot fit into a length prefix. +func WriteData(w io.Writer, data []byte) (int, error) { + prefix, err := dataPrefixForLength(len(data)) + if err != nil { + return 0, err + } + total := 0 + n, err := w.Write(prefix) + total += n + if err != nil { + return total, err + } + n, err = w.Write(data) + total += n + return total, err +} + +var paddingBuffer = make([]byte, 1024) + +// WritePadding encodes padding chunks, whose total size (including their own +// length prefixes) is n. Returns the total number of bytes written to w, which +// will be exactly n unless there was an error. The error cannot be ErrTooLong +// because this function will write multiple padding chunks if necessary to +// reach the requested size. Panics if n is negative. +func WritePadding(w io.Writer, n int) (int, error) { + if n < 0 { + panic("negative length") + } + total := 0 + for n > 0 { + p := len(paddingBuffer) + if p > n { + p = n + } + n -= p + var prefix []byte + switch { + case ((p-1)>>0)&0x3f == ((p - 1) >> 0): + p = p - 1 + prefix = []byte{byte((p >> 0) & 0x3f)} + case ((p-2)>>7)&0x3f == ((p - 2) >> 7): + p = p - 2 + prefix = []byte{0x40 | byte((p>>7)&0x3f), byte((p >> 0) & 0x7f)} + case ((p-3)>>14)&0x3f == ((p - 3) >> 14): + p = p - 3 + prefix = []byte{0x40 | byte((p>>14)&0x3f), 0x80 | byte((p>>7)&0x3f), byte((p >> 0) & 0x7f)} + } + nn, err := w.Write(prefix) + total += nn + if err != nil { + return total, err + } + nn, err = w.Write(paddingBuffer[:p]) + total += nn + if err != nil { + return total, err + } + } + return total, nil +} + +// MaxDataForSize returns the length of the longest slice that can be passed to +// WriteData, whose total encoded size (including length prefix) is no larger +// than n. Call this to find out if a chunk of data will fit into a length +// budget. Panics if n == 0. +func MaxDataForSize(n int) int { + if n == 0 { + panic("zero length") + } + prefix, err := dataPrefixForLength(n) + if err == ErrTooLong { + return (1 << (6 + 7 + 7)) - 1 - 3 + } else if err != nil { + panic(err) + } + return n - len(prefix) +} diff --cc doc/meek-client.1 index 8215537,ec64796..70ae1e1 --- a/doc/meek-client.1 +++ b/doc/meek-client.1 @@@ -2,12 -2,12 +2,12 @@@ ." Title: meek-client ." Author: [FIXME: author] [see http://docbook.sf.net/el/author] ." Generator: DocBook XSL Stylesheets v1.79.1 http://docbook.sf.net/ - ." Date: 04/29/2020 -." Date: 10/20/2022 ++." Date: 10/28/2022 ." Manual: \ & ." Source: \ & ." Language: English ." - .TH "MEEK-CLIENT" "1" "04/29/2020" "\ &" "\ &" -.TH "MEEK-CLIENT" "1" "10/20/2022" "\ &" "\ &" ++.TH "MEEK-CLIENT" "1" "10/28/2022" "\ &" "\ &" ." ----------------------------------------------------------------- ." * Define some portability stuff ." ----------------------------------------------------------------- diff --cc go.mod index e6c7dfb,cdeb2e8..99d67f7 --- a/go.mod +++ b/go.mod @@@ -4,10 -4,8 +4,10 @@@ go 1.1
require ( git.torproject.org/pluggable-transports/goptlib.git v1.1.0 - github.com/refraction-networking/utls v0.0.0-20190909200633-43c36d3c1f57 + github.com/refraction-networking/utls v1.1.5 + github.com/xtaci/kcp-go/v5 v5.5.14 + github.com/xtaci/smux v1.5.14 - golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 - golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 - golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8 + golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 + golang.org/x/net v0.0.0-20220909164309-bea034e7d591 + golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 ) diff --cc go.sum index e22f000,3b60946..7b248c4 --- a/go.sum +++ b/go.sum @@@ -1,34 -1,24 +1,50 @@@ git.torproject.org/pluggable-transports/goptlib.git v1.1.0 h1:LMQAA8pAho+QtYrrVNimJQiINNEwcwuuD99vezD/PAo= git.torproject.org/pluggable-transports/goptlib.git v1.1.0/go.mod h1:YT4XMSkuEXbtqlydr9+OxqFAyspUv0Gr9qhM3B++o/Q= + github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= + github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= + github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= + github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/cpuid v1.2.2 h1:1xAgYebNnsb9LKCdLOvFWtAxGU/33mjJtyOVbmUa0Us= +github.com/klauspost/cpuid v1.2.2/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/reedsolomon v1.9.3 h1:N/VzgeMfHmLc+KHMD1UL/tNkfXAt8FnUqlgXGIduwAY= +github.com/klauspost/reedsolomon v1.9.3/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= - github.com/refraction-networking/utls v0.0.0-20190909200633-43c36d3c1f57 h1:SL1K0QAuC1b54KoY1pjPWe6kSlsFHwK9/oC960fKrTY= - github.com/refraction-networking/utls v0.0.0-20190909200633-43c36d3c1f57/go.mod h1:tz9gX959MEFfFN5whTIocCLUG57WiILqtdVxI8c6Wj0= + github.com/refraction-networking/utls v1.1.5 h1:JtrojoNhbUQkBqEg05sP3gDgDj6hIEAAVKbI9lx4n6w= + github.com/refraction-networking/utls v1.1.5/go.mod h1:jRQxtYi7nkq1p28HF2lwOH5zQm9aC8rpK0O9lIIzGh8= +github.com/templexxx/cpu v0.0.1 h1:hY4WdLOgKdc8y13EYklu9OUTXik80BkxHoWvTO6MQQY= +github.com/templexxx/cpu v0.0.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk= +github.com/templexxx/xorsimd v0.4.1 h1:iUZcywbOYDRAZUasAs2eSCUW8eobuZDy0I9FJiORkVg= +github.com/templexxx/xorsimd v0.4.1/go.mod h1:W+ffZz8jJMH2SXwuKu9WhygqBMbFnp14G2fqEr8qaNo= +github.com/tjfoc/gmsm v1.0.1 h1:R11HlqhXkDospckjZEihx9SW/2VW0RgdwrykyWMFOQU= +github.com/tjfoc/gmsm v1.0.1/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc= +github.com/xtaci/kcp-go/v5 v5.5.14 h1:YODIwvTyZmOTj3SduJeIcQPxthDoHllMm8YIBlK44Ik= +github.com/xtaci/kcp-go/v5 v5.5.14/go.mod h1:H0T/EJ+lPNytnFYsKLH0JHUtiwZjG3KXlTM6c+Q4YUo= +github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM= +github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE= +github.com/xtaci/smux v1.5.14 h1:1j+zJYDZRv9FHaWqCJfH5RPizIm0fSzJIFbfVn8zsfg= +github.com/xtaci/smux v1.5.14/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= - golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= + golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= + golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= - golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= + golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= + golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI= + golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= - golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8 h1:JA8d3MPx/IToSyXZG/RhwYEtfrKO1Fxrqe8KrkiLXKM= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= - golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= + golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= + golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= + golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= + golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= + golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= + golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= + golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= + golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= + golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= + golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --cc meek-client/meek-client.go index 813a373,8905612..28943a8 --- a/meek-client/meek-client.go +++ b/meek-client/meek-client.go @@@ -98,14 -121,28 +104,14 @@@ type RequestInfo struct RoundTripper http.RoundTripper }
-// Make an http.Request from the payload data in buf and the request metadata in -// info. -func makeRequest(buf []byte, info *RequestInfo) (*http.Request, error) { - var body io.Reader - if len(buf) > 0 { - // Leave body == nil when buf is empty. A nil body is an - // explicit signal that the body is empty. An empty - // *bytes.Reader or the magic value http.NoBody are supposed to - // be equivalent ways to signal an empty body, but in Go 1.8 the - // HTTP/2 code only understands nil. Not leaving body == nil - // causes the Content-Length header to be omitted from HTTP/2 - // requests, which in some cases can cause the server to return - // a 411 "Length Required" error. See - // https://bugs.torproject.org/22865. - body = bytes.NewReader(buf) - } - req, err := http.NewRequest("POST", info.URL.String(), body) +func (info *RequestInfo) Poll(ctx context.Context, out io.Reader) (in io.ReadCloser, err error) { + req, err := http.NewRequest("POST", info.URL.String(), out) - // Prevent Content-Type sniffing by net/http and middleboxes. - req.Header.Set("Content-Type", "application/octet-stream") if err != nil { return nil, err } + req = req.WithContext(ctx) + // Prevent Content-Type sniffing by net/http and middleboxes. + req.Header.Set("Content-Type", "application/octet-stream") if info.Host != "" { req.Host = info.Host } diff --cc meek-server/meek-server.go index f715107,626ddb0..a4e5683 --- a/meek-server/meek-server.go +++ b/meek-server/meek-server.go @@@ -48,9 -49,13 +53,9 @@@ import )
const ( - programVersion = "0.34" + programVersion = "0.37.0"
ptMethodName = "meek" - // Reject session ids shorter than this, as a weak defense against - // client bugs that send an empty session id or something similarly - // likely to collide. - minSessionIDLength = 8 // The largest request body we are willing to process, and the largest // chunk of data we'll send back in a response. maxPayloadLength = 0x10000