github.com/psiphon-labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/server/meekBuffer.go (about) 1 /* 2 * Copyright (c) 2017, Psiphon Inc. 3 * All rights reserved. 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package server 21 22 import ( 23 "errors" 24 "io" 25 ) 26 27 // CachedResponse is a data structure that supports meek 28 // protocol connection interruption resiliency: it stores 29 // payload data from the most recent response so that it 30 // may be resent if the client fails to receive it. 31 // 32 // The meek server maintains one CachedResponse for each 33 // meek client. Psiphon's variant of meek streams response 34 // data, so responses are not fixed size. To limit the memory 35 // overhead of response caching, each CachedResponse has a 36 // fixed-size buffer that operates as a ring buffer, 37 // discarding older response bytes when the buffer fills. 38 // A CachedResponse that has discarded data may still satisfy 39 // a client retry where the client has already received part 40 // of the response payload. 41 // 42 // A CachedResponse will also extend its capacity by 43 // borrowing buffers from a CachedResponseBufferPool, if 44 // available. When Reset is called, borrowed buffers are 45 // released back to the pool. 46 type CachedResponse struct { 47 buffers [][]byte 48 readPosition int 49 readAvailable int 50 writeIndex int 51 writeBufferIndex int 52 overwriting bool 53 extendedBufferPool *CachedResponseBufferPool 54 } 55 56 // NewCachedResponse creates a CachedResponse with a fixed buffer 57 // of size bufferSize and borrowing buffers from extendedBufferPool. 58 func NewCachedResponse( 59 bufferSize int, 60 extendedBufferPool *CachedResponseBufferPool) *CachedResponse { 61 62 return &CachedResponse{ 63 buffers: [][]byte{make([]byte, bufferSize)}, 64 extendedBufferPool: extendedBufferPool, 65 } 66 } 67 68 // Reset reinitializes the CachedResponse state to have 69 // no buffered response and releases all extended buffers 70 // back to the pool. 71 // Reset _must_ be called before discarding a CachedResponse 72 // or extended buffers will not be released. 73 func (response *CachedResponse) Reset() { 74 for i, buffer := range response.buffers { 75 if i > 0 { 76 response.extendedBufferPool.Put(buffer) 77 } 78 } 79 response.buffers = response.buffers[0:1] 80 response.readPosition = 0 81 response.readAvailable = 0 82 response.writeIndex = 0 83 response.writeBufferIndex = 0 84 response.overwriting = false 85 } 86 87 // Available returns the size of the buffered response data. 88 func (response *CachedResponse) Available() int { 89 return response.readAvailable 90 } 91 92 // HasPosition checks if the CachedResponse has buffered 93 // response data starting at or before the specified 94 // position. 95 func (response *CachedResponse) HasPosition(position int) bool { 96 return response.readAvailable > 0 && response.readPosition <= position 97 } 98 99 // CopyFromPosition writes the response data, starting at 100 // the specified position, to writer. Any data before the 101 // position is skipped. CopyFromPosition will return an error 102 // if the specified position is not available. 103 // CopyFromPosition will copy no data and return no error if 104 // the position is at the end of its available data. 105 // CopyFromPosition can be called repeatedly to read the 106 // same data -- it does not advance or modify the CachedResponse. 107 func (response *CachedResponse) CopyFromPosition( 108 position int, writer io.Writer) (int, error) { 109 110 if response.readAvailable > 0 && response.readPosition > position { 111 return 0, errors.New("position unavailable") 112 } 113 114 // Special case: position is end of available data 115 if position == response.readPosition+response.readAvailable { 116 return 0, nil 117 } 118 119 // Begin at the start of the response data, which may 120 // be midway through the buffer(s). 121 122 index := 0 123 bufferIndex := 0 124 if response.overwriting { 125 index = response.writeIndex 126 bufferIndex = response.writeBufferIndex 127 if index >= len(response.buffers[bufferIndex]) { 128 index = 0 129 bufferIndex = (bufferIndex + 1) % len(response.buffers) 130 } 131 } 132 133 // Iterate over all available data, skipping until at the 134 // requested position. 135 136 n := 0 137 138 skip := position - response.readPosition 139 available := response.readAvailable 140 141 for available > 0 { 142 143 buffer := response.buffers[bufferIndex] 144 145 toCopy := min(len(buffer)-index, available) 146 147 available -= toCopy 148 149 if skip > 0 { 150 if toCopy >= skip { 151 index += skip 152 toCopy -= skip 153 skip = 0 154 } else { 155 skip -= toCopy 156 } 157 } 158 159 if skip == 0 { 160 written, err := writer.Write(buffer[index : index+toCopy]) 161 n += written 162 if err != nil { 163 return n, err 164 } 165 } 166 167 index = 0 168 bufferIndex = (bufferIndex + 1) % len(response.buffers) 169 } 170 171 return n, nil 172 } 173 174 // Write appends data to the CachedResponse. All writes will 175 // succeed, but only the most recent bytes will be retained 176 // once the fixed buffer is full and no extended buffers are 177 // available. 178 // 179 // Write may be called multiple times to record a single 180 // response; Reset should be called between responses. 181 // 182 // Write conforms to the io.Writer interface. 183 func (response *CachedResponse) Write(data []byte) (int, error) { 184 185 dataIndex := 0 186 187 for dataIndex < len(data) { 188 189 // Write into available space in the current buffer 190 191 buffer := response.buffers[response.writeBufferIndex] 192 canWriteLen := len(buffer) - response.writeIndex 193 needWriteLen := len(data) - dataIndex 194 writeLen := min(canWriteLen, needWriteLen) 195 196 if writeLen > 0 { 197 copy( 198 buffer[response.writeIndex:response.writeIndex+writeLen], 199 data[dataIndex:dataIndex+writeLen]) 200 201 response.writeIndex += writeLen 202 203 // readPosition tracks the earliest position in 204 // the response that remains in the cached response. 205 // Once the buffer is full (and cannot be extended), 206 // older data is overwritten and readPosition advances. 207 // 208 // readAvailable is the amount of data in the cached 209 // response, which may be less than the buffer capacity. 210 211 if response.overwriting { 212 response.readPosition += writeLen 213 } else { 214 response.readAvailable += writeLen 215 } 216 217 dataIndex += writeLen 218 } 219 220 if needWriteLen > canWriteLen { 221 222 // Add an extended buffer to increase capacity 223 224 // TODO: can extend whenever response.readIndex and response.readBufferIndex are 0? 225 if response.writeBufferIndex == len(response.buffers)-1 && 226 !response.overwriting { 227 228 extendedBuffer := response.extendedBufferPool.Get() 229 if extendedBuffer != nil { 230 response.buffers = append(response.buffers, extendedBuffer) 231 } 232 } 233 234 // Move to the next buffer, which may wrap around 235 236 // This isn't a general ring buffer: Reset is called at 237 // start of each response, so the initial data is always 238 // at the beginning of the first buffer. It follows that 239 // data is overwritten once the buffer wraps around back 240 // to the beginning. 241 242 response.writeBufferIndex++ 243 if response.writeBufferIndex >= len(response.buffers) { 244 response.writeBufferIndex = 0 245 response.overwriting = true 246 } 247 response.writeIndex = 0 248 } 249 } 250 251 return len(data), nil 252 } 253 254 // CachedResponseBufferPool is a fixed-size pool of 255 // fixed-size buffers that are used to temporarily extend 256 // the capacity of CachedResponses. 257 type CachedResponseBufferPool struct { 258 bufferSize int 259 buffers chan []byte 260 } 261 262 // NewCachedResponseBufferPool creates a new CachedResponseBufferPool 263 // with the specified number of buffers. Buffers are allocated on 264 // demand and once allocated remain allocated. 265 func NewCachedResponseBufferPool( 266 bufferSize, bufferCount int) *CachedResponseBufferPool { 267 268 buffers := make(chan []byte, bufferCount) 269 for i := 0; i < bufferCount; i++ { 270 buffers <- make([]byte, 0) 271 } 272 273 return &CachedResponseBufferPool{ 274 bufferSize: bufferSize, 275 buffers: buffers, 276 } 277 } 278 279 // Get returns a buffer, if one is available, or returns nil 280 // when no buffer is available. Get does not block. Call Put 281 // to release the buffer back to the pool. 282 // 283 // Note: currently, Buffers are not zeroed between use by 284 // different CachedResponses owned by different clients. 285 // A bug resulting in cross-client data transfer exposes 286 // only OSSH ciphertext in the case of meek's use of 287 // CachedResponses. 288 func (pool *CachedResponseBufferPool) Get() []byte { 289 select { 290 case buffer := <-pool.buffers: 291 if len(buffer) == 0 { 292 buffer = make([]byte, pool.bufferSize) 293 } 294 return buffer 295 default: 296 return nil 297 } 298 } 299 300 // Put releases a buffer back to the pool. The buffer must 301 // have been obtained from Get. 302 func (pool *CachedResponseBufferPool) Put(buffer []byte) { 303 pool.buffers <- buffer 304 }