MCPcopy Index your code
hub / github.com/Hidden-Node/GooseRelayVPN-AndroidClient / EncodeBatch

Function EncodeBatch

internal/frame/crypto.go:147–218  ·  view source on GitHub ↗

EncodeBatch packs zero or more frames into a base64-encoded HTTP body. Wire format (before base64): nonce (12 bytes) || AES-GCM ciphertext+tag over: flags (1 byte) — 0x00 raw | 0x01 DEFLATE-compressed body client_id (16 bytes) u16 frame_count for each frame: u32 marshaled_len

(c *Crypto, clientID [ClientIDLen]byte, frames []*Frame)

Source from the content-addressed store, hash-verified

145// not survive the hop. Sealing it under AES-GCM also means a passive observer
146// of the relay traffic cannot tell two clients apart by their IDs.
147func EncodeBatch(c *Crypto, clientID [ClientIDLen]byte, frames []*Frame) ([]byte, error) {
148 if len(frames) > 0xFFFF {
149 return nil, fmt.Errorf("batch: too many frames: %d", len(frames))
150 }
151
152 // Compute the exact plaintext size up front so the pooled buffer can be
153 // grown once and frames appended directly into it (no per-frame alloc).
154 plainSize := 1 + ClientIDLen + 2 // flags byte + client_id + u16 frame count
155 for _, f := range frames {
156 plainSize += 4 + f.EncodedLen() // u32 length prefix + frame bytes
157 }
158
159 // Pull a plaintext scratch buffer from the pool; grow if needed.
160 plainP := encPlainPool.Get().(*[]byte)
161 plain := (*plainP)[:0]
162 if cap(plain) < plainSize {
163 plain = make([]byte, 0, plainSize)
164 }
165 defer func() {
166 // Reset and return to pool. The capacity is preserved so the next
167 // EncodeBatch reuses the same underlying allocation.
168 plain = plain[:0]
169 *plainP = plain
170 encPlainPool.Put(plainP)
171 }()
172
173 plain = append(plain, 0x00) // flags placeholder at index 0
174 plain = append(plain, clientID[:]...)
175 plain = append(plain, byte(len(frames)>>8), byte(len(frames)))
176 for _, f := range frames {
177 n := f.EncodedLen()
178 plain = append(plain, byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
179 var err error
180 plain, err = f.AppendMarshal(plain)
181 if err != nil {
182 return nil, fmt.Errorf("batch: marshal frame: %w", err)
183 }
184 }
185
186 // Attempt Zstandard compression on the payload section (everything after
187 // the flags byte at index 0). Only worthwhile for batches large enough that
188 // the overhead is amortised; small control batches (SYN/FIN/keepalive) are
189 // sent raw. If compression does not shrink the data (e.g. already-encrypted
190 // TLS payloads) we fall back to raw transparently.
191 sealInput := plain // default: raw, flags byte already 0x00
192 if len(plain)-1 >= compressMinSize {
193 enc := zstdEncPool.Get().(*zstd.Encoder)
194 // EncodeAll appends compressed bytes to dst. The [:1:1] cap trick gives
195 // us a fresh backing array with the flags placeholder at [0], so the
196 // pool-owned plain buffer is never modified.
197 compressed := enc.EncodeAll(plain[1:], plain[:1:1])
198 zstdEncPool.Put(enc)
199 if len(compressed)-1 < len(plain)-1 {
200 compressed[0] = batchFlagZstd
201 sealInput = compressed
202 } else {
203 plain[0] = batchFlagRaw
204 }

Callers 15

handleTunnelMethod · 0.92
invokeExitTunnelFunction · 0.92
invokeAsClientFunction · 0.92
DiagnoseMethod · 0.92
echoServerFunction · 0.92
handlerMethod · 0.92
ShutdownMethod · 0.92
pollOnceMethod · 0.92

Calls 3

EncodedLenMethod · 0.80
AppendMarshalMethod · 0.80
SealMethod · 0.80

Tested by 15

invokeExitTunnelFunction · 0.74
invokeAsClientFunction · 0.74
echoServerFunction · 0.74
handlerMethod · 0.74
benchSealOpenBatchFunction · 0.68