feat(transport): 完成安全架构拆分并收口 stream/bulk 传输优化
- 新增 managed/external/nested 三种传输保护模式 - 新增 peer attach 显式认证、抗重放、channel binding 和可选前向保密协商 - 明确单连接注入与可重拨连接源的语义边界 - 禁止 ConnectByConn 场景下 dedicated bulk 走 sidecar,auto 模式自动回退 shared - 修正 dedicated attach 在 bootstrap/steady profile 切换下的处理逻辑 - 优化 shared bulk super-batch 与批量 framed write 路径 - 降低 stream/bulk fast path 的复制和分发损耗 - 补齐 benchmark、回归测试、运行时快照和 README 文档
This commit is contained in:
@@ -0,0 +1,517 @@
|
||||
package notify
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
cryptorand "crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
peerAttachFeatureExplicitAuth uint64 = 1 << iota
|
||||
peerAttachFeatureChannelBinding
|
||||
peerAttachFeatureForwardSecrecy
|
||||
)
|
||||
|
||||
const (
|
||||
peerAttachNonceSize = 16
|
||||
peerAttachReplayTTL = 30 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
errPeerAttachAuthInvalid = errors.New("peer attach auth invalid")
|
||||
errPeerAttachReplayRejected = errors.New("peer attach replay rejected")
|
||||
errPeerAttachReplayWindowFull = errors.New("peer attach replay window full")
|
||||
errPeerAttachExplicitAuthRequired = errors.New("peer attach explicit auth required")
|
||||
errPeerAttachChannelBindingRequired = errors.New("peer attach channel binding required")
|
||||
errPeerAttachChannelBindingUnavailable = errors.New("peer attach channel binding unavailable")
|
||||
errPeerAttachForwardSecrecyRequired = errors.New("peer attach forward secrecy required")
|
||||
)
|
||||
|
||||
type peerAttachAuthResult struct {
|
||||
explicit bool
|
||||
fallback bool
|
||||
clientNonce []byte
|
||||
serverNonce []byte
|
||||
channelBinding []byte
|
||||
clientECDHEPublicKey []byte
|
||||
}
|
||||
|
||||
type peerAttachReplayCache struct {
|
||||
mu sync.Mutex
|
||||
entries map[string]time.Time
|
||||
rejects atomic.Int64
|
||||
overflowRejects atomic.Int64
|
||||
}
|
||||
|
||||
func newPeerAttachNonce() ([]byte, error) {
|
||||
buf := make([]byte, peerAttachNonceSize)
|
||||
if _, err := cryptorand.Read(buf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func appendPeerAttachAuthBytes(dst []byte, data []byte) []byte {
|
||||
dst = binary.BigEndian.AppendUint32(dst, uint32(len(data)))
|
||||
return append(dst, data...)
|
||||
}
|
||||
|
||||
func appendPeerAttachAuthString(dst []byte, value string) []byte {
|
||||
return appendPeerAttachAuthBytes(dst, []byte(value))
|
||||
}
|
||||
|
||||
func appendPeerAttachAuthBool(dst []byte, value bool) []byte {
|
||||
if value {
|
||||
return append(dst, 1)
|
||||
}
|
||||
return append(dst, 0)
|
||||
}
|
||||
|
||||
func peerAttachRequestAuthPayload(req peerAttachRequest, channelBinding []byte) []byte {
|
||||
buf := make([]byte, 0, 96+len(req.PeerID)+len(channelBinding))
|
||||
buf = appendPeerAttachAuthString(buf, "notify/peer-attach/request-auth/v1")
|
||||
buf = binary.BigEndian.AppendUint64(buf, req.Features)
|
||||
buf = appendPeerAttachAuthString(buf, req.PeerID)
|
||||
buf = appendPeerAttachAuthBytes(buf, req.ClientNonce)
|
||||
if supportsPeerAttachChannelBinding(req.Features) {
|
||||
buf = appendPeerAttachAuthBytes(buf, channelBinding)
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
func peerAttachResponseAuthPayload(req peerAttachRequest, resp peerAttachResponse, channelBinding []byte) []byte {
|
||||
buf := make([]byte, 0, 160+len(req.PeerID)+len(resp.PeerID)+len(resp.Error)+len(channelBinding))
|
||||
buf = appendPeerAttachAuthString(buf, "notify/peer-attach/response-auth/v1")
|
||||
buf = binary.BigEndian.AppendUint64(buf, req.Features)
|
||||
buf = appendPeerAttachAuthString(buf, req.PeerID)
|
||||
buf = appendPeerAttachAuthBytes(buf, req.ClientNonce)
|
||||
buf = binary.BigEndian.AppendUint64(buf, resp.Features)
|
||||
buf = appendPeerAttachAuthString(buf, resp.PeerID)
|
||||
buf = appendPeerAttachAuthBool(buf, resp.Accepted)
|
||||
buf = appendPeerAttachAuthBool(buf, resp.Reused)
|
||||
buf = appendPeerAttachAuthString(buf, resp.Error)
|
||||
buf = appendPeerAttachAuthBytes(buf, resp.ServerNonce)
|
||||
if supportsPeerAttachChannelBinding(resp.Features) {
|
||||
buf = appendPeerAttachAuthBytes(buf, channelBinding)
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
func signPeerAttachPayload(secretKey []byte, payload []byte) []byte {
|
||||
if len(secretKey) == 0 {
|
||||
return nil
|
||||
}
|
||||
mac := hmac.New(sha256.New, secretKey)
|
||||
_, _ = mac.Write(payload)
|
||||
return mac.Sum(nil)
|
||||
}
|
||||
|
||||
func computePeerAttachRequestAuthTag(secretKey []byte, req peerAttachRequest, channelBinding []byte) []byte {
|
||||
return signPeerAttachPayload(secretKey, peerAttachRequestAuthPayload(req, channelBinding))
|
||||
}
|
||||
|
||||
func computePeerAttachResponseAuthTag(secretKey []byte, req peerAttachRequest, resp peerAttachResponse, channelBinding []byte) []byte {
|
||||
return signPeerAttachPayload(secretKey, peerAttachResponseAuthPayload(req, resp, channelBinding))
|
||||
}
|
||||
|
||||
func supportsExplicitPeerAttachAuth(features uint64) bool {
|
||||
return features&peerAttachFeatureExplicitAuth != 0
|
||||
}
|
||||
|
||||
func supportsPeerAttachChannelBinding(features uint64) bool {
|
||||
return features&peerAttachFeatureChannelBinding != 0
|
||||
}
|
||||
|
||||
func supportsPeerAttachForwardSecrecy(features uint64) bool {
|
||||
return features&peerAttachFeatureForwardSecrecy != 0
|
||||
}
|
||||
|
||||
func classifyPeerAttachRejectCounter(s *ServerCommon, err error) {
|
||||
if s == nil || err == nil {
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case errors.Is(err, errPeerAttachReplayRejected):
|
||||
s.peerAttachReplay.rejects.Add(1)
|
||||
case errors.Is(err, errPeerAttachReplayWindowFull):
|
||||
s.peerAttachReplay.overflowRejects.Add(1)
|
||||
case errors.Is(err, errPeerAttachExplicitAuthRequired), errors.Is(err, errPeerAttachChannelBindingRequired), errors.Is(err, errPeerAttachForwardSecrecyRequired):
|
||||
s.peerAttachDowngradeRejectCount.Add(1)
|
||||
case errors.Is(err, errPeerAttachChannelBindingUnavailable):
|
||||
s.peerAttachBindingRejectCount.Add(1)
|
||||
default:
|
||||
s.peerAttachAuthRejectCount.Add(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientCommon) shouldUseExplicitPeerAttachAuth() bool {
|
||||
if c == nil || !c.securityConfigured {
|
||||
return false
|
||||
}
|
||||
return c.securityAuthMode == AuthPSK && len(c.securityBootstrap.secretKey) != 0
|
||||
}
|
||||
|
||||
func resolvePeerAttachChannelBinding(provider PeerAttachChannelBindingProvider, role PeerAttachChannelBindingRole, peerID string, conn net.Conn) ([]byte, error) {
|
||||
if provider == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if conn == nil {
|
||||
return nil, errPeerAttachChannelBindingUnavailable
|
||||
}
|
||||
binding, err := provider(PeerAttachChannelBindingContext{
|
||||
Role: role,
|
||||
PeerID: peerID,
|
||||
Conn: conn,
|
||||
})
|
||||
if err != nil || len(binding) == 0 {
|
||||
return nil, errPeerAttachChannelBindingUnavailable
|
||||
}
|
||||
return bytes.Clone(binding), nil
|
||||
}
|
||||
|
||||
func (c *ClientCommon) buildPeerAttachRequest(peerID string) (peerAttachRequest, peerAttachRequestState, error) {
|
||||
cfg := c.peerAttachSecuritySnapshot()
|
||||
req := peerAttachRequest{
|
||||
PeerID: stringsTrimSpaceNoAlloc(peerID),
|
||||
}
|
||||
if !c.shouldUseExplicitPeerAttachAuth() {
|
||||
if c.clientRequiresForwardSecrecy() {
|
||||
return peerAttachRequest{}, peerAttachRequestState{}, errPeerAttachForwardSecrecyRequired
|
||||
}
|
||||
if cfg.requireExplicitAuth {
|
||||
return peerAttachRequest{}, peerAttachRequestState{}, errPeerAttachExplicitAuthRequired
|
||||
}
|
||||
return req, peerAttachRequestState{}, nil
|
||||
}
|
||||
nonce, err := newPeerAttachNonce()
|
||||
if err != nil {
|
||||
return peerAttachRequest{}, peerAttachRequestState{}, err
|
||||
}
|
||||
req.Features = peerAttachFeatureExplicitAuth
|
||||
requestState := peerAttachRequestState{}
|
||||
var channelBinding []byte
|
||||
if cfg.channelBinding != nil {
|
||||
channelBinding, err = resolvePeerAttachChannelBinding(cfg.channelBinding, PeerAttachChannelBindingRoleClient, req.PeerID, c.clientTransportConnSnapshot())
|
||||
if err != nil {
|
||||
return peerAttachRequest{}, peerAttachRequestState{}, err
|
||||
}
|
||||
}
|
||||
if len(channelBinding) != 0 {
|
||||
req.Features |= peerAttachFeatureChannelBinding
|
||||
}
|
||||
if cfg.requireChannelBinding && !supportsPeerAttachChannelBinding(req.Features) {
|
||||
return peerAttachRequest{}, peerAttachRequestState{}, errPeerAttachChannelBindingRequired
|
||||
}
|
||||
if c.clientSupportsForwardSecrecy() {
|
||||
requestState.forwardSecrecy, err = newPeerAttachForwardSecrecyClientState()
|
||||
if err != nil {
|
||||
return peerAttachRequest{}, peerAttachRequestState{}, err
|
||||
}
|
||||
req.Features |= peerAttachFeatureForwardSecrecy
|
||||
req.ClientECDHEPublicKey = bytes.Clone(requestState.forwardSecrecy.publicKey)
|
||||
}
|
||||
req.ClientNonce = nonce
|
||||
req.AuthTag = computePeerAttachRequestAuthTag(c.securityBootstrap.secretKey, req, channelBinding)
|
||||
return req, requestState, nil
|
||||
}
|
||||
|
||||
func (c *ClientCommon) verifyPeerAttachResponse(req peerAttachRequest, resp peerAttachResponse, requestState peerAttachRequestState) (peerAttachResponseVerifyResult, error) {
|
||||
cfg := c.peerAttachSecuritySnapshot()
|
||||
baseSteady := transportProtectionProfile{}
|
||||
if c != nil {
|
||||
baseSteady = c.securitySteady.clone().withForwardSecrecyFallback(false)
|
||||
}
|
||||
result := peerAttachResponseVerifyResult{steadyProfile: baseSteady}
|
||||
if c == nil || !c.shouldUseExplicitPeerAttachAuth() {
|
||||
if c.clientRequiresForwardSecrecy() {
|
||||
return peerAttachResponseVerifyResult{}, errPeerAttachForwardSecrecyRequired
|
||||
}
|
||||
if cfg.requireExplicitAuth {
|
||||
return peerAttachResponseVerifyResult{}, errPeerAttachExplicitAuthRequired
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
if !supportsExplicitPeerAttachAuth(resp.Features) {
|
||||
if c.clientRequiresForwardSecrecy() {
|
||||
return peerAttachResponseVerifyResult{}, errPeerAttachForwardSecrecyRequired
|
||||
}
|
||||
if cfg.requireExplicitAuth {
|
||||
return peerAttachResponseVerifyResult{}, errPeerAttachExplicitAuthRequired
|
||||
}
|
||||
if c.clientSupportsForwardSecrecy() {
|
||||
result.steadyProfile = result.steadyProfile.withForwardSecrecyFallback(true)
|
||||
}
|
||||
result.authFallback = true
|
||||
return result, nil
|
||||
}
|
||||
var channelBinding []byte
|
||||
if supportsPeerAttachChannelBinding(req.Features) {
|
||||
if cfg.channelBinding == nil {
|
||||
return peerAttachResponseVerifyResult{}, errPeerAttachChannelBindingUnavailable
|
||||
}
|
||||
if !supportsPeerAttachChannelBinding(resp.Features) {
|
||||
return peerAttachResponseVerifyResult{}, errPeerAttachChannelBindingRequired
|
||||
}
|
||||
var err error
|
||||
channelBinding, err = resolvePeerAttachChannelBinding(cfg.channelBinding, PeerAttachChannelBindingRoleClient, req.PeerID, c.clientTransportConnSnapshot())
|
||||
if err != nil {
|
||||
return peerAttachResponseVerifyResult{}, err
|
||||
}
|
||||
} else if cfg.requireChannelBinding {
|
||||
return peerAttachResponseVerifyResult{}, errPeerAttachChannelBindingRequired
|
||||
}
|
||||
if len(resp.ServerNonce) != peerAttachNonceSize {
|
||||
return peerAttachResponseVerifyResult{}, errPeerAttachAuthInvalid
|
||||
}
|
||||
expected := computePeerAttachResponseAuthTag(c.securityBootstrap.secretKey, req, peerAttachResponse{
|
||||
PeerID: resp.PeerID,
|
||||
Accepted: resp.Accepted,
|
||||
Reused: resp.Reused,
|
||||
Error: resp.Error,
|
||||
Features: resp.Features,
|
||||
ServerNonce: resp.ServerNonce,
|
||||
}, channelBinding)
|
||||
if !hmac.Equal(resp.AuthTag, expected) {
|
||||
return peerAttachResponseVerifyResult{}, errPeerAttachAuthInvalid
|
||||
}
|
||||
if requestState.forwardSecrecy == nil || !supportsPeerAttachForwardSecrecy(req.Features) {
|
||||
return result, nil
|
||||
}
|
||||
if !supportsPeerAttachForwardSecrecy(resp.Features) {
|
||||
if c.clientRequiresForwardSecrecy() {
|
||||
return peerAttachResponseVerifyResult{}, errPeerAttachForwardSecrecyRequired
|
||||
}
|
||||
result.steadyProfile = result.steadyProfile.withForwardSecrecyFallback(true)
|
||||
return result, nil
|
||||
}
|
||||
if resp.KeyMode != "" && resp.KeyMode != peerAttachKeyModeECDHE {
|
||||
return peerAttachResponseVerifyResult{}, errPeerAttachForwardSecrecyInvalid
|
||||
}
|
||||
profile, err := derivePeerAttachForwardSecrecyTransportProfile(c.securitySteady, c.securityBootstrap.secretKey, requestState.forwardSecrecy.privateKey, resp.ServerECDHEPublicKey, req, resp)
|
||||
if err != nil {
|
||||
return peerAttachResponseVerifyResult{}, err
|
||||
}
|
||||
result.steadyProfile = profile
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *ServerCommon) validatePeerAttachRequestAuth(logical *LogicalConn, transport net.Conn, req peerAttachRequest) (peerAttachAuthResult, error) {
|
||||
cfg := s.peerAttachSecuritySnapshot()
|
||||
if !supportsExplicitPeerAttachAuth(req.Features) {
|
||||
if s.serverRequiresForwardSecrecy() {
|
||||
return peerAttachAuthResult{}, errPeerAttachForwardSecrecyRequired
|
||||
}
|
||||
if cfg.requireExplicitAuth {
|
||||
return peerAttachAuthResult{}, errPeerAttachExplicitAuthRequired
|
||||
}
|
||||
return peerAttachAuthResult{fallback: true}, nil
|
||||
}
|
||||
if logical == nil {
|
||||
return peerAttachAuthResult{}, errPeerAttachAuthInvalid
|
||||
}
|
||||
var channelBinding []byte
|
||||
if supportsPeerAttachChannelBinding(req.Features) {
|
||||
if cfg.channelBinding == nil {
|
||||
return peerAttachAuthResult{}, errPeerAttachChannelBindingUnavailable
|
||||
}
|
||||
var err error
|
||||
channelBinding, err = resolvePeerAttachChannelBinding(cfg.channelBinding, PeerAttachChannelBindingRoleServer, req.PeerID, transport)
|
||||
if err != nil {
|
||||
return peerAttachAuthResult{}, err
|
||||
}
|
||||
} else if cfg.requireChannelBinding {
|
||||
return peerAttachAuthResult{}, errPeerAttachChannelBindingRequired
|
||||
}
|
||||
secretKey := logical.secretKeySnapshot()
|
||||
if len(secretKey) == 0 {
|
||||
return peerAttachAuthResult{}, errPeerAttachAuthInvalid
|
||||
}
|
||||
if len(req.ClientNonce) != peerAttachNonceSize {
|
||||
return peerAttachAuthResult{}, errPeerAttachAuthInvalid
|
||||
}
|
||||
expected := computePeerAttachRequestAuthTag(secretKey, peerAttachRequest{
|
||||
PeerID: req.PeerID,
|
||||
Features: req.Features,
|
||||
ClientNonce: req.ClientNonce,
|
||||
}, channelBinding)
|
||||
if !hmac.Equal(req.AuthTag, expected) {
|
||||
return peerAttachAuthResult{}, errPeerAttachAuthInvalid
|
||||
}
|
||||
if supportsPeerAttachForwardSecrecy(req.Features) {
|
||||
if len(req.ClientECDHEPublicKey) != peerAttachECDHEPublicKeySize {
|
||||
return peerAttachAuthResult{}, errPeerAttachForwardSecrecyInvalid
|
||||
}
|
||||
}
|
||||
if err := s.acceptPeerAttachReplay(req.PeerID, req.ClientNonce, time.Now(), cfg.replayWindow, cfg.replayCapacity); err != nil {
|
||||
return peerAttachAuthResult{}, err
|
||||
}
|
||||
serverNonce, err := newPeerAttachNonce()
|
||||
if err != nil {
|
||||
return peerAttachAuthResult{}, err
|
||||
}
|
||||
return peerAttachAuthResult{
|
||||
explicit: true,
|
||||
clientNonce: bytes.Clone(req.ClientNonce),
|
||||
serverNonce: serverNonce,
|
||||
channelBinding: channelBinding,
|
||||
clientECDHEPublicKey: bytes.Clone(req.ClientECDHEPublicKey),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *ServerCommon) signPeerAttachResponse(logical *LogicalConn, req peerAttachRequest, resp *peerAttachResponse, auth peerAttachAuthResult) {
|
||||
if s == nil || logical == nil || resp == nil || !auth.explicit {
|
||||
return
|
||||
}
|
||||
secretKey := logical.secretKeySnapshot()
|
||||
if len(secretKey) == 0 {
|
||||
return
|
||||
}
|
||||
resp.Features |= peerAttachFeatureExplicitAuth
|
||||
if len(auth.channelBinding) != 0 {
|
||||
resp.Features |= peerAttachFeatureChannelBinding
|
||||
}
|
||||
resp.ServerNonce = bytes.Clone(auth.serverNonce)
|
||||
resp.AuthTag = computePeerAttachResponseAuthTag(secretKey, req, *resp, auth.channelBinding)
|
||||
}
|
||||
|
||||
func (s *ServerCommon) preparePeerAttachSteadyTransportProfile(logical *LogicalConn, req peerAttachRequest, resp *peerAttachResponse, auth peerAttachAuthResult) (transportProtectionProfile, error) {
|
||||
if s == nil {
|
||||
return transportProtectionProfile{}, nil
|
||||
}
|
||||
profile := s.securitySteady.clone().withForwardSecrecyFallback(false)
|
||||
if resp != nil && auth.explicit {
|
||||
resp.Features |= peerAttachFeatureExplicitAuth
|
||||
if len(auth.channelBinding) != 0 {
|
||||
resp.Features |= peerAttachFeatureChannelBinding
|
||||
}
|
||||
resp.ServerNonce = bytes.Clone(auth.serverNonce)
|
||||
}
|
||||
if resp != nil && resp.KeyMode == "" {
|
||||
resp.KeyMode = profile.keyMode
|
||||
}
|
||||
if !s.serverSupportsForwardSecrecy() {
|
||||
return profile, nil
|
||||
}
|
||||
if !auth.explicit || !supportsPeerAttachForwardSecrecy(req.Features) {
|
||||
if s.serverRequiresForwardSecrecy() {
|
||||
return transportProtectionProfile{}, errPeerAttachForwardSecrecyRequired
|
||||
}
|
||||
return profile.withForwardSecrecyFallback(true), nil
|
||||
}
|
||||
fsState, err := newPeerAttachForwardSecrecyClientState()
|
||||
if err != nil {
|
||||
return transportProtectionProfile{}, err
|
||||
}
|
||||
if resp != nil {
|
||||
resp.Features |= peerAttachFeatureForwardSecrecy
|
||||
resp.KeyMode = peerAttachKeyModeECDHE
|
||||
resp.ServerECDHEPublicKey = bytes.Clone(fsState.publicKey)
|
||||
}
|
||||
return derivePeerAttachForwardSecrecyTransportProfile(s.securitySteady, logical.secretKeySnapshot(), fsState.privateKey, auth.clientECDHEPublicKey, req, *resp)
|
||||
}
|
||||
|
||||
func (s *ServerCommon) acceptPeerAttachReplay(peerID string, nonce []byte, now time.Time, window time.Duration, capacity int) error {
|
||||
if s == nil || len(nonce) == 0 {
|
||||
return nil
|
||||
}
|
||||
cache := &s.peerAttachReplay
|
||||
key := peerID + "\x00" + string(nonce)
|
||||
expireBefore := now.Add(-window)
|
||||
cache.mu.Lock()
|
||||
defer cache.mu.Unlock()
|
||||
if cache.entries == nil {
|
||||
cache.entries = make(map[string]time.Time)
|
||||
}
|
||||
for replayKey, seenAt := range cache.entries {
|
||||
if seenAt.Before(expireBefore) {
|
||||
delete(cache.entries, replayKey)
|
||||
}
|
||||
}
|
||||
if seenAt, ok := cache.entries[key]; ok && !seenAt.Before(expireBefore) {
|
||||
return errPeerAttachReplayRejected
|
||||
}
|
||||
if capacity > 0 && len(cache.entries) >= capacity {
|
||||
return errPeerAttachReplayWindowFull
|
||||
}
|
||||
cache.entries[key] = now
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ServerCommon) peerAttachReplayRejectCountSnapshot() int64 {
|
||||
if s == nil {
|
||||
return 0
|
||||
}
|
||||
return s.peerAttachReplay.rejects.Load()
|
||||
}
|
||||
|
||||
func (s *ServerCommon) peerAttachReplayOverflowRejectCountSnapshot() int64 {
|
||||
if s == nil {
|
||||
return 0
|
||||
}
|
||||
return s.peerAttachReplay.overflowRejects.Load()
|
||||
}
|
||||
|
||||
func (c *ClientCommon) markClientPeerAttachAuthenticated(fallback bool, at time.Time) {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.peerAttachAuthenticated = true
|
||||
c.peerAttachAuthFallback = fallback
|
||||
c.peerAttachAt = at.UnixNano()
|
||||
}
|
||||
|
||||
func (c *ClientCommon) resetClientPeerAttachAuth() {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.peerAttachAuthenticated = false
|
||||
c.peerAttachAuthFallback = false
|
||||
c.peerAttachAt = 0
|
||||
}
|
||||
|
||||
func (c *ClientCommon) clientPeerAttachAuthSnapshot() (bool, bool, time.Time) {
|
||||
if c == nil {
|
||||
return false, false, time.Time{}
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if c.peerAttachAt == 0 {
|
||||
return c.peerAttachAuthenticated, c.peerAttachAuthFallback, time.Time{}
|
||||
}
|
||||
return c.peerAttachAuthenticated, c.peerAttachAuthFallback, time.Unix(0, c.peerAttachAt)
|
||||
}
|
||||
|
||||
func stringsTrimSpaceNoAlloc(value string) string {
|
||||
start := 0
|
||||
for start < len(value) {
|
||||
switch value[start] {
|
||||
case ' ', '\t', '\n', '\r':
|
||||
start++
|
||||
default:
|
||||
goto endStart
|
||||
}
|
||||
}
|
||||
return ""
|
||||
endStart:
|
||||
end := len(value)
|
||||
for end > start {
|
||||
switch value[end-1] {
|
||||
case ' ', '\t', '\n', '\r':
|
||||
end--
|
||||
default:
|
||||
return value[start:end]
|
||||
}
|
||||
}
|
||||
return value[start:end]
|
||||
}
|
||||
Reference in New Issue
Block a user