diff --git a/Makefile b/Makefile index dfb6dc0e..c273555d 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ PROJECT_NAME=$(shell basename "${ROOT}") # - pkg/version/current.go # # Use `tools/bump_version.sh` script to change all those files at one shot. -VERSION="3.4.0" +VERSION="3.5.0" # Build binaries and installation packages. .PHONY: build @@ -47,9 +47,12 @@ bin: lib client-android client-linux client-mac client-windows server-linux lib: fmt vet CGO_ENABLED=0 go build -v ./... CGO_ENABLED=0 go test -timeout=1m0s -coverprofile coverage.out ./... - CGO_ENABLED=0 go test -bench=. -benchtime=5s ./pkg/cipher go tool cover -html coverage.out -o coverage.html +# Run benchmark. +.PHONY: bench + CGO_ENABLED=0 go test -bench=. -benchtime=5s ./pkg/cipher + # Generate vendor directory. .PHONY: vendor vendor: diff --git a/build/package/mieru/amd64/debian/DEBIAN/control b/build/package/mieru/amd64/debian/DEBIAN/control index 64020609..b8cb60be 100755 --- a/build/package/mieru/amd64/debian/DEBIAN/control +++ b/build/package/mieru/amd64/debian/DEBIAN/control @@ -1,5 +1,5 @@ Package: mieru -Version: 3.4.0 +Version: 3.5.0 Section: net Priority: optional Architecture: amd64 diff --git a/build/package/mieru/amd64/rpm/mieru.spec b/build/package/mieru/amd64/rpm/mieru.spec index 879331d4..44a677a7 100644 --- a/build/package/mieru/amd64/rpm/mieru.spec +++ b/build/package/mieru/amd64/rpm/mieru.spec @@ -1,5 +1,5 @@ Name: mieru -Version: 3.4.0 +Version: 3.5.0 Release: 1%{?dist} Summary: Mieru proxy client License: GPLv3+ diff --git a/build/package/mieru/arm64/debian/DEBIAN/control b/build/package/mieru/arm64/debian/DEBIAN/control index f4143296..8a1cc7ac 100755 --- a/build/package/mieru/arm64/debian/DEBIAN/control +++ b/build/package/mieru/arm64/debian/DEBIAN/control @@ -1,5 +1,5 @@ Package: mieru -Version: 3.4.0 +Version: 3.5.0 Section: net Priority: optional Architecture: arm64 diff --git a/build/package/mieru/arm64/rpm/mieru.spec b/build/package/mieru/arm64/rpm/mieru.spec index 879331d4..44a677a7 100644 --- a/build/package/mieru/arm64/rpm/mieru.spec +++ b/build/package/mieru/arm64/rpm/mieru.spec @@ -1,5 +1,5 @@ Name: mieru -Version: 3.4.0 +Version: 3.5.0 Release: 1%{?dist} Summary: Mieru proxy client License: GPLv3+ diff --git a/build/package/mita/amd64/debian/DEBIAN/control b/build/package/mita/amd64/debian/DEBIAN/control index b7bee2ae..21d70a31 100755 --- a/build/package/mita/amd64/debian/DEBIAN/control +++ b/build/package/mita/amd64/debian/DEBIAN/control @@ -1,5 +1,5 @@ Package: mita -Version: 3.4.0 +Version: 3.5.0 Section: net Priority: optional Architecture: amd64 diff --git a/build/package/mita/amd64/rpm/mita.spec b/build/package/mita/amd64/rpm/mita.spec index 56803dd1..7ebce6b4 100644 --- a/build/package/mita/amd64/rpm/mita.spec +++ b/build/package/mita/amd64/rpm/mita.spec @@ -1,5 +1,5 @@ Name: mita -Version: 3.4.0 +Version: 3.5.0 Release: 1%{?dist} Summary: Mieru proxy server License: GPLv3+ diff --git a/build/package/mita/arm64/debian/DEBIAN/control b/build/package/mita/arm64/debian/DEBIAN/control index 54aba394..12432939 100755 --- a/build/package/mita/arm64/debian/DEBIAN/control +++ b/build/package/mita/arm64/debian/DEBIAN/control @@ -1,5 +1,5 @@ Package: mita -Version: 3.4.0 +Version: 3.5.0 Section: net Priority: optional Architecture: arm64 diff --git a/build/package/mita/arm64/rpm/mita.spec b/build/package/mita/arm64/rpm/mita.spec index 1c887d5f..8e8a99ec 100644 --- a/build/package/mita/arm64/rpm/mita.spec +++ b/build/package/mita/arm64/rpm/mita.spec @@ -1,5 +1,5 @@ Name: mita -Version: 3.4.0 +Version: 3.5.0 Release: 1%{?dist} Summary: Mieru proxy server License: GPLv3+ diff --git a/docs/server-install.md b/docs/server-install.md index 888cafe7..f98279ba 100644 --- a/docs/server-install.md +++ b/docs/server-install.md @@ -8,32 +8,32 @@ Before installation and configuration, connect to the server via SSH and then ex ```sh # Debian / Ubuntu - X86_64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.4.0/mita_3.4.0_amd64.deb +curl -LSO https://github.com/enfein/mieru/releases/download/v3.5.0/mita_3.5.0_amd64.deb # Debian / Ubuntu - ARM 64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.4.0/mita_3.4.0_arm64.deb +curl -LSO https://github.com/enfein/mieru/releases/download/v3.5.0/mita_3.5.0_arm64.deb # RedHat / CentOS / Rocky Linux - X86_64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.4.0/mita-3.4.0-1.x86_64.rpm +curl -LSO https://github.com/enfein/mieru/releases/download/v3.5.0/mita-3.5.0-1.x86_64.rpm # RedHat / CentOS / Rocky Linux - ARM 64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.4.0/mita-3.4.0-1.aarch64.rpm +curl -LSO https://github.com/enfein/mieru/releases/download/v3.5.0/mita-3.5.0-1.aarch64.rpm ``` ## Install mita package ```sh # Debian / Ubuntu - X86_64 -sudo dpkg -i mita_3.4.0_amd64.deb +sudo dpkg -i mita_3.5.0_amd64.deb # Debian / Ubuntu - ARM 64 -sudo dpkg -i mita_3.4.0_arm64.deb +sudo dpkg -i mita_3.5.0_arm64.deb # RedHat / CentOS / Rocky Linux - X86_64 -sudo rpm -Uvh --force mita-3.4.0-1.x86_64.rpm +sudo rpm -Uvh --force mita-3.5.0-1.x86_64.rpm # RedHat / CentOS / Rocky Linux - ARM 64 -sudo rpm -Uvh --force mita-3.4.0-1.aarch64.rpm +sudo rpm -Uvh --force mita-3.5.0-1.aarch64.rpm ``` Those instructions can also be used to upgrade the version of mita software package. diff --git a/docs/server-install.zh_CN.md b/docs/server-install.zh_CN.md index 6774b142..f9fddf09 100644 --- a/docs/server-install.zh_CN.md +++ b/docs/server-install.zh_CN.md @@ -8,32 +8,32 @@ ```sh # Debian / Ubuntu - X86_64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.4.0/mita_3.4.0_amd64.deb +curl -LSO https://github.com/enfein/mieru/releases/download/v3.5.0/mita_3.5.0_amd64.deb # Debian / Ubuntu - ARM 64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.4.0/mita_3.4.0_arm64.deb +curl -LSO https://github.com/enfein/mieru/releases/download/v3.5.0/mita_3.5.0_arm64.deb # RedHat / CentOS / Rocky Linux - X86_64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.4.0/mita-3.4.0-1.x86_64.rpm +curl -LSO https://github.com/enfein/mieru/releases/download/v3.5.0/mita-3.5.0-1.x86_64.rpm # RedHat / CentOS / Rocky Linux - ARM 64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.4.0/mita-3.4.0-1.aarch64.rpm +curl -LSO https://github.com/enfein/mieru/releases/download/v3.5.0/mita-3.5.0-1.aarch64.rpm ``` ## 安装 mita 软件包 ```sh # Debian / Ubuntu - X86_64 -sudo dpkg -i mita_3.4.0_amd64.deb +sudo dpkg -i mita_3.5.0_amd64.deb # Debian / Ubuntu - ARM 64 -sudo dpkg -i mita_3.4.0_arm64.deb +sudo dpkg -i mita_3.5.0_arm64.deb # RedHat / CentOS / Rocky Linux - X86_64 -sudo rpm -Uvh --force mita-3.4.0-1.x86_64.rpm +sudo rpm -Uvh --force mita-3.5.0-1.x86_64.rpm # RedHat / CentOS / Rocky Linux - ARM 64 -sudo rpm -Uvh --force mita-3.4.0-1.aarch64.rpm +sudo rpm -Uvh --force mita-3.5.0-1.aarch64.rpm ``` 上述指令也可以用来升级 mita 软件包的版本。 diff --git a/pkg/cli/client.go b/pkg/cli/client.go index 56d2ae9f..2a495f35 100644 --- a/pkg/cli/client.go +++ b/pkg/cli/client.go @@ -473,7 +473,7 @@ var clientRunFunc = func(s []string) error { } else { hashedPassword = cipher.HashPassword([]byte(user.GetPassword()), []byte(user.GetName())) } - mux = mux.SetClientPassword(hashedPassword) + mux = mux.SetClientUserNamePassword(user.GetName(), hashedPassword) mtu := util.DefaultMTU if activeProfile.GetMtu() != 0 { mtu = int(activeProfile.GetMtu()) diff --git a/pkg/protocol/mux.go b/pkg/protocol/mux.go index 1c86bc1f..5757c0bb 100644 --- a/pkg/protocol/mux.go +++ b/pkg/protocol/mux.go @@ -54,6 +54,7 @@ type Mux struct { cleaner *time.Ticker // ---- client fields ---- + username string password []byte multiplexFactor int @@ -100,8 +101,8 @@ func NewMux(isClinet bool) *Mux { return mux } -// SetClientPassword panics if the mux is already started. -func (m *Mux) SetClientPassword(password []byte) *Mux { +// SetClientUserNamePassword panics if the mux is already started. +func (m *Mux) SetClientUserNamePassword(username string, password []byte) *Mux { m.mu.Lock() defer m.mu.Unlock() if !m.isClient { @@ -110,6 +111,7 @@ func (m *Mux) SetClientPassword(password []byte) *Mux { if m.used { panic("Can't set client password after mux is used") } + m.username = username m.password = password return m } @@ -545,6 +547,9 @@ func (m *Mux) newUnderlay(ctx context.Context) (Underlay, error) { if err != nil { return nil, fmt.Errorf("cipher.BlockCipherFromPassword() failed: %v", err) } + block.SetBlockContext(cipher.BlockContext{ + UserName: m.username, + }) underlay, err = NewTCPUnderlay(ctx, p.RemoteAddr().Network(), "", p.RemoteAddr().String(), p.MTU(), block) if err != nil { return nil, fmt.Errorf("NewTCPUnderlay() failed: %v", err) @@ -554,6 +559,9 @@ func (m *Mux) newUnderlay(ctx context.Context) (Underlay, error) { if err != nil { return nil, fmt.Errorf("cipher.BlockCipherFromPassword() failed: %v", err) } + block.SetBlockContext(cipher.BlockContext{ + UserName: m.username, + }) underlay, err = NewUDPUnderlay(ctx, p.RemoteAddr().Network(), "", p.RemoteAddr().String(), p.MTU(), block) if err != nil { return nil, fmt.Errorf("NewUDPUnderlay() failed: %v", err) diff --git a/pkg/protocol/mux_test.go b/pkg/protocol/mux_test.go index cdbe0f15..6f97d2b9 100644 --- a/pkg/protocol/mux_test.go +++ b/pkg/protocol/mux_test.go @@ -43,7 +43,7 @@ var users = map[string]*appctlpb.User{ func runClient(t *testing.T, properties UnderlayProperties, username, password []byte, concurrent int) { clientMux := NewMux(true). - SetClientPassword(cipher.HashPassword(password, username)). + SetClientUserNamePassword(string(username), cipher.HashPassword(password, username)). SetClientMultiplexFactor(2). SetEndpoints([]UnderlayProperties{properties}) diff --git a/pkg/protocol/padding.go b/pkg/protocol/padding.go index 1b2efa97..9f107495 100644 --- a/pkg/protocol/padding.go +++ b/pkg/protocol/padding.go @@ -27,7 +27,7 @@ import ( var ( recommendedConsecutiveASCIILen = 24 + rng.FixedIntPerHost(17) - recommendedTargetProbability = 0.375 + recommendedTargetProbability = 0.325 ) type paddingOpts struct { @@ -58,6 +58,35 @@ type entropyPaddingOpts struct { targetProbability float64 } +func buildRecommendedPaddingOpts(maxLen, randomDataLen int, strategySource string) paddingOpts { + // strategySource decides the padding strategy. + strategy := rng.FixedInt(2, strategySource) + if strategy == 0 { + // Use ASCII. + return paddingOpts{ + maxLen: maxLen, + ascii: &asciiPaddingOpts{ + minConsecutiveASCIILen: mathext.Min(maxLen, recommendedConsecutiveASCIILen), + }, + } + } else { + // Use entropy. + randomData := make([]byte, randomDataLen) + for { + if _, err := crand.Read(randomData); err == nil { + break + } + } + return paddingOpts{ + maxLen: maxLen, + entropy: &entropyPaddingOpts{ + existingData: randomData, + targetProbability: recommendedTargetProbability, + }, + } + } +} + func newPadding(opts paddingOpts) []byte { if opts.ascii != nil { if opts.maxLen < opts.ascii.minConsecutiveASCIILen { diff --git a/pkg/protocol/session.go b/pkg/protocol/session.go index 99a98734..77c31f59 100644 --- a/pkg/protocol/session.go +++ b/pkg/protocol/session.go @@ -753,8 +753,14 @@ func (s *Session) input(seg *segment) error { if s.block != nil { prevUserName := s.block.BlockContext().UserName nextUserName := seg.block.BlockContext().UserName - if prevUserName != "" && nextUserName != "" && prevUserName != nextUserName { - panic(fmt.Sprintf("%v cipher block user name %q is different from segment cipher block user name %q", s, prevUserName, nextUserName)) + if prevUserName == "" { + panic(fmt.Sprintf("%v cipher block user name is not set", s)) + } + if nextUserName == "" { + panic(fmt.Sprintf("%v cipher block user name is not set", seg)) + } + if prevUserName != nextUserName { + panic(fmt.Sprintf("%v cipher block user name %q is different from %v cipher block user name %q", s, prevUserName, seg, nextUserName)) } } @@ -762,10 +768,10 @@ func (s *Session) input(seg *segment) error { // Register server per user metrics. if !s.isClient { - if s.uploadBytes == nil && s.block.BlockContext().UserName != "" { + if s.uploadBytes == nil { s.uploadBytes = metrics.RegisterMetric(fmt.Sprintf(metrics.UserMetricGroupFormat, s.block.BlockContext().UserName), metrics.UserMetricUploadBytes, metrics.COUNTER_TIME_SERIES) } - if s.downloadBytes == nil && s.block.BlockContext().UserName != "" { + if s.downloadBytes == nil { s.downloadBytes = metrics.RegisterMetric(fmt.Sprintf(metrics.UserMetricGroupFormat, s.block.BlockContext().UserName), metrics.UserMetricDownloadBytes, metrics.COUNTER_TIME_SERIES) } } diff --git a/pkg/protocol/underlay_tcp.go b/pkg/protocol/underlay_tcp.go index 3bda46e4..96974f73 100644 --- a/pkg/protocol/underlay_tcp.go +++ b/pkg/protocol/underlay_tcp.go @@ -25,7 +25,6 @@ import ( "github.com/enfein/mieru/pkg/appctl/appctlpb" "github.com/enfein/mieru/pkg/cipher" "github.com/enfein/mieru/pkg/log" - "github.com/enfein/mieru/pkg/mathext" "github.com/enfein/mieru/pkg/metrics" "github.com/enfein/mieru/pkg/replay" "github.com/enfein/mieru/pkg/rng" @@ -34,6 +33,10 @@ import ( "github.com/enfein/mieru/pkg/util/sockopts" ) +const ( + tcpOverhead = MetadataLength + cipher.DefaultOverhead*2 +) + type TCPUnderlay struct { baseUnderlay conn *net.TCPConn @@ -502,23 +505,21 @@ func (t *TCPUnderlay) writeOneSegment(seg *segment) error { t.sendMutex.Lock() defer t.sendMutex.Unlock() + if err := t.maybeInitSendBlockCipher(); err != nil { + return fmt.Errorf("maybeInitSendBlockCipher() failed: %w", err) + } + if ss, ok := toSessionStruct(seg.metadata); ok { maxPaddingSize := MaxPaddingSize(t.mtu, t.IPVersion(), t.TransportProtocol(), int(ss.payloadLen), 0) - padding := newPadding(paddingOpts{ - maxLen: maxPaddingSize, - ascii: &asciiPaddingOpts{ - minConsecutiveASCIILen: mathext.Min(maxPaddingSize, recommendedConsecutiveASCIILen), - }, - }) + padding := newPadding( + buildRecommendedPaddingOpts(maxPaddingSize, tcpOverhead+int(ss.payloadLen), t.send.BlockContext().UserName), + ) ss.suffixLen = uint8(len(padding)) if log.IsLevelEnabled(log.TraceLevel) { log.Tracef("%v is sending %v", t, seg) } plaintextMetadata := seg.metadata.Marshal() - if err := t.maybeInitSendBlockCipher(); err != nil { - return fmt.Errorf("maybeInitSendBlockCipher() failed: %w", err) - } encryptedMetadata, err := t.send.Encrypt(plaintextMetadata) if err != nil { return fmt.Errorf("Encrypt() failed: %w", err) diff --git a/pkg/protocol/underlay_udp.go b/pkg/protocol/underlay_udp.go index 967e3904..efbcd3bf 100644 --- a/pkg/protocol/underlay_udp.go +++ b/pkg/protocol/underlay_udp.go @@ -26,7 +26,6 @@ import ( "github.com/enfein/mieru/pkg/appctl/appctlpb" "github.com/enfein/mieru/pkg/cipher" "github.com/enfein/mieru/pkg/log" - "github.com/enfein/mieru/pkg/mathext" "github.com/enfein/mieru/pkg/metrics" "github.com/enfein/mieru/pkg/replay" "github.com/enfein/mieru/pkg/stderror" @@ -648,14 +647,15 @@ func (u *UDPUnderlay) writeOneSegment(seg *segment, addr *net.UDPAddr) error { } } + if blockCipher == nil { + panic(fmt.Sprintf("%v cipher block is not ready", u)) + } + if ss, ok := toSessionStruct(seg.metadata); ok { maxPaddingSize := MaxPaddingSize(u.mtu, u.IPVersion(), u.TransportProtocol(), int(ss.payloadLen), 0) - padding := newPadding(paddingOpts{ - maxLen: maxPaddingSize, - ascii: &asciiPaddingOpts{ - minConsecutiveASCIILen: mathext.Min(maxPaddingSize, recommendedConsecutiveASCIILen), - }, - }) + padding := newPadding( + buildRecommendedPaddingOpts(maxPaddingSize, udpOverhead+int(ss.payloadLen), blockCipher.BlockContext().UserName), + ) ss.suffixLen = uint8(len(padding)) if log.IsLevelEnabled(log.TraceLevel) { log.Tracef("%v is sending %v", u, seg) diff --git a/pkg/util/entropy.go b/pkg/util/entropy.go index 2dc6e132..21db10c4 100644 --- a/pkg/util/entropy.go +++ b/pkg/util/entropy.go @@ -15,6 +15,8 @@ package util +import "fmt" + // BitDistribution describes the probability of bit 0 and bit 1 in a byte array. type BitDistribution struct { Bit0 float64 @@ -23,6 +25,10 @@ type BitDistribution struct { Bit1Count int } +func (bd BitDistribution) String() string { + return fmt.Sprintf("BitDistribution{Bit0=%f, Bit1=%f, Bit0Count=%d, Bit1Count=%d}", bd.Bit0, bd.Bit1, bd.Bit0Count, bd.Bit1Count) +} + // ToBitDistribution returns the BitDistribution from a byte array. func ToBitDistribution(arr []byte) BitDistribution { if len(arr) == 0 { diff --git a/pkg/version/current.go b/pkg/version/current.go index bed1c024..93fb0482 100644 --- a/pkg/version/current.go +++ b/pkg/version/current.go @@ -16,5 +16,5 @@ package version const ( - AppVersion = "3.4.0" + AppVersion = "3.5.0" )