a geicko-2 based round robin ranking system designed to test c++ battleship submissions
battleship.dunkirk.sh
1package sshfx
2
3import (
4 "encoding/binary"
5 "errors"
6)
7
8// Various encoding errors.
9var (
10 ErrShortPacket = errors.New("packet too short")
11 ErrLongPacket = errors.New("packet too long")
12)
13
14// Buffer wraps up the various encoding details of the SSH format.
15//
16// Data types are encoded as per section 4 from https://tools.ietf.org/html/draft-ietf-secsh-architecture-09#page-8
17type Buffer struct {
18 b []byte
19 off int
20 Err error
21}
22
23// NewBuffer creates and initializes a new buffer using buf as its initial contents.
24// The new buffer takes ownership of buf, and the caller should not use buf after this call.
25//
26// In most cases, new(Buffer) (or just declaring a Buffer variable) is sufficient to initialize a Buffer.
27func NewBuffer(buf []byte) *Buffer {
28 return &Buffer{
29 b: buf,
30 }
31}
32
33// NewMarshalBuffer creates a new Buffer ready to start marshaling a Packet into.
34// It preallocates enough space for uint32(length), uint8(type), uint32(request-id) and size more bytes.
35func NewMarshalBuffer(size int) *Buffer {
36 return NewBuffer(make([]byte, 4+1+4+size))
37}
38
39// Bytes returns a slice of length b.Len() holding the unconsumed bytes in the Buffer.
40// The slice is valid for use only until the next buffer modification
41// (that is, only until the next call to an Append or Consume method).
42func (b *Buffer) Bytes() []byte {
43 return b.b[b.off:]
44}
45
46// Len returns the number of unconsumed bytes in the buffer.
47func (b *Buffer) Len() int { return len(b.b) - b.off }
48
49// Cap returns the capacity of the buffer’s underlying byte slice,
50// that is, the total space allocated for the buffer’s data.
51func (b *Buffer) Cap() int { return cap(b.b) }
52
53// Reset resets the buffer to be empty, but it retains the underlying storage for use by future Appends.
54func (b *Buffer) Reset() {
55 *b = Buffer{
56 b: b.b[:0],
57 }
58}
59
60// StartPacket resets and initializes the buffer to be ready to start marshaling a packet into.
61// It truncates the buffer, reserves space for uint32(length), then appends the given packetType and requestID.
62func (b *Buffer) StartPacket(packetType PacketType, requestID uint32) {
63 *b = Buffer{
64 b: append(b.b[:0], make([]byte, 4)...),
65 }
66
67 b.AppendUint8(uint8(packetType))
68 b.AppendUint32(requestID)
69}
70
71// Packet finalizes the packet started from StartPacket.
72// It is expected that this will end the ownership of the underlying byte-slice,
73// and so the returned byte-slices may be reused the same as any other byte-slice,
74// the caller should not use this buffer after this call.
75//
76// It writes the packet body length into the first four bytes of the buffer in network byte order (big endian).
77// The packet body length is the length of this buffer less the 4-byte length itself, plus the length of payload.
78//
79// It is assumed that no Consume methods have been called on this buffer,
80// and so it returns the whole underlying slice.
81func (b *Buffer) Packet(payload []byte) (header, payloadPassThru []byte, err error) {
82 b.PutLength(len(b.b) - 4 + len(payload))
83
84 return b.b, payload, nil
85}
86
87// ConsumeUint8 consumes a single byte from the buffer.
88// If the buffer does not have enough data, it will set Err to ErrShortPacket.
89func (b *Buffer) ConsumeUint8() uint8 {
90 if b.Err != nil {
91 return 0
92 }
93
94 if b.Len() < 1 {
95 b.off = len(b.b)
96 b.Err = ErrShortPacket
97 return 0
98 }
99
100 var v uint8
101 v, b.off = b.b[b.off], b.off+1
102 return v
103}
104
105// AppendUint8 appends a single byte into the buffer.
106func (b *Buffer) AppendUint8(v uint8) {
107 b.b = append(b.b, v)
108}
109
110// ConsumeBool consumes a single byte from the buffer, and returns true if that byte is non-zero.
111// If the buffer does not have enough data, it will set Err to ErrShortPacket.
112func (b *Buffer) ConsumeBool() bool {
113 return b.ConsumeUint8() != 0
114}
115
116// AppendBool appends a single bool into the buffer.
117// It encodes it as a single byte, with false as 0, and true as 1.
118func (b *Buffer) AppendBool(v bool) {
119 if v {
120 b.AppendUint8(1)
121 } else {
122 b.AppendUint8(0)
123 }
124}
125
126// ConsumeUint16 consumes a single uint16 from the buffer, in network byte order (big-endian).
127// If the buffer does not have enough data, it will set Err to ErrShortPacket.
128func (b *Buffer) ConsumeUint16() uint16 {
129 if b.Err != nil {
130 return 0
131 }
132
133 if b.Len() < 2 {
134 b.off = len(b.b)
135 b.Err = ErrShortPacket
136 return 0
137 }
138
139 v := binary.BigEndian.Uint16(b.b[b.off:])
140 b.off += 2
141 return v
142}
143
144// AppendUint16 appends single uint16 into the buffer, in network byte order (big-endian).
145func (b *Buffer) AppendUint16(v uint16) {
146 b.b = append(b.b,
147 byte(v>>8),
148 byte(v>>0),
149 )
150}
151
152// unmarshalUint32 is used internally to read the packet length.
153// It is unsafe, and so not exported.
154// Even within this package, its use should be avoided.
155func unmarshalUint32(b []byte) uint32 {
156 return binary.BigEndian.Uint32(b[:4])
157}
158
159// ConsumeUint32 consumes a single uint32 from the buffer, in network byte order (big-endian).
160// If the buffer does not have enough data, it will set Err to ErrShortPacket.
161func (b *Buffer) ConsumeUint32() uint32 {
162 if b.Err != nil {
163 return 0
164 }
165
166 if b.Len() < 4 {
167 b.off = len(b.b)
168 b.Err = ErrShortPacket
169 return 0
170 }
171
172 v := binary.BigEndian.Uint32(b.b[b.off:])
173 b.off += 4
174 return v
175}
176
177// AppendUint32 appends a single uint32 into the buffer, in network byte order (big-endian).
178func (b *Buffer) AppendUint32(v uint32) {
179 b.b = append(b.b,
180 byte(v>>24),
181 byte(v>>16),
182 byte(v>>8),
183 byte(v>>0),
184 )
185}
186
187// ConsumeCount consumes a single uint32 count from the buffer, in network byte order (big-endian) as an int.
188// If the buffer does not have enough data, it will set Err to ErrShortPacket.
189func (b *Buffer) ConsumeCount() int {
190 return int(b.ConsumeUint32())
191}
192
193// AppendCount appends a single int length as a uint32 into the buffer, in network byte order (big-endian).
194func (b *Buffer) AppendCount(v int) {
195 b.AppendUint32(uint32(v))
196}
197
198// ConsumeUint64 consumes a single uint64 from the buffer, in network byte order (big-endian).
199// If the buffer does not have enough data, it will set Err to ErrShortPacket.
200func (b *Buffer) ConsumeUint64() uint64 {
201 if b.Err != nil {
202 return 0
203 }
204
205 if b.Len() < 8 {
206 b.off = len(b.b)
207 b.Err = ErrShortPacket
208 return 0
209 }
210
211 v := binary.BigEndian.Uint64(b.b[b.off:])
212 b.off += 8
213 return v
214}
215
216// AppendUint64 appends a single uint64 into the buffer, in network byte order (big-endian).
217func (b *Buffer) AppendUint64(v uint64) {
218 b.b = append(b.b,
219 byte(v>>56),
220 byte(v>>48),
221 byte(v>>40),
222 byte(v>>32),
223 byte(v>>24),
224 byte(v>>16),
225 byte(v>>8),
226 byte(v>>0),
227 )
228}
229
230// ConsumeInt64 consumes a single int64 from the buffer, in network byte order (big-endian) with two’s complement.
231// If the buffer does not have enough data, it will set Err to ErrShortPacket.
232func (b *Buffer) ConsumeInt64() int64 {
233 return int64(b.ConsumeUint64())
234}
235
236// AppendInt64 appends a single int64 into the buffer, in network byte order (big-endian) with two’s complement.
237func (b *Buffer) AppendInt64(v int64) {
238 b.AppendUint64(uint64(v))
239}
240
241// ConsumeByteSlice consumes a single string of raw binary data from the buffer.
242// A string is a uint32 length, followed by that number of raw bytes.
243// If the buffer does not have enough data, or defines a length larger than available, it will set Err to ErrShortPacket.
244//
245// The returned slice aliases the buffer contents, and is valid only as long as the buffer is not reused
246// (that is, only until the next call to Reset, PutLength, StartPacket, or UnmarshalBinary).
247//
248// In no case will any Consume calls return overlapping slice aliases,
249// and Append calls are guaranteed to not disturb this slice alias.
250func (b *Buffer) ConsumeByteSlice() []byte {
251 length := int(b.ConsumeUint32())
252 if b.Err != nil {
253 return nil
254 }
255
256 if b.Len() < length || length < 0 {
257 b.off = len(b.b)
258 b.Err = ErrShortPacket
259 return nil
260 }
261
262 v := b.b[b.off:]
263 if len(v) > length || cap(v) > length {
264 v = v[:length:length]
265 }
266 b.off += int(length)
267 return v
268}
269
270// ConsumeByteSliceCopy consumes a single string of raw binary data as a copy from the buffer.
271// A string is a uint32 length, followed by that number of raw bytes.
272// If the buffer does not have enough data, or defines a length larger than available, it will set Err to ErrShortPacket.
273//
274// The returned slice does not alias any buffer contents,
275// and will therefore be valid even if the buffer is later reused.
276//
277// If hint has sufficient capacity to hold the data, it will be reused and overwritten,
278// otherwise a new backing slice will be allocated and returned.
279func (b *Buffer) ConsumeByteSliceCopy(hint []byte) []byte {
280 data := b.ConsumeByteSlice()
281
282 if grow := len(data) - len(hint); grow > 0 {
283 hint = append(hint, make([]byte, grow)...)
284 }
285
286 n := copy(hint, data)
287 hint = hint[:n]
288 return hint
289}
290
291// AppendByteSlice appends a single string of raw binary data into the buffer.
292// A string is a uint32 length, followed by that number of raw bytes.
293func (b *Buffer) AppendByteSlice(v []byte) {
294 b.AppendUint32(uint32(len(v)))
295 b.b = append(b.b, v...)
296}
297
298// ConsumeString consumes a single string of binary data from the buffer.
299// A string is a uint32 length, followed by that number of raw bytes.
300// If the buffer does not have enough data, or defines a length larger than available, it will set Err to ErrShortPacket.
301//
302// NOTE: Go implicitly assumes that strings contain UTF-8 encoded data.
303// All caveats on using arbitrary binary data in Go strings applies.
304func (b *Buffer) ConsumeString() string {
305 return string(b.ConsumeByteSlice())
306}
307
308// AppendString appends a single string of binary data into the buffer.
309// A string is a uint32 length, followed by that number of raw bytes.
310func (b *Buffer) AppendString(v string) {
311 b.AppendByteSlice([]byte(v))
312}
313
314// PutLength writes the given size into the first four bytes of the buffer in network byte order (big endian).
315func (b *Buffer) PutLength(size int) {
316 if len(b.b) < 4 {
317 b.b = append(b.b, make([]byte, 4-len(b.b))...)
318 }
319
320 binary.BigEndian.PutUint32(b.b, uint32(size))
321}
322
323// MarshalBinary returns a clone of the full internal buffer.
324func (b *Buffer) MarshalBinary() ([]byte, error) {
325 clone := make([]byte, len(b.b))
326 n := copy(clone, b.b)
327 return clone[:n], nil
328}
329
330// UnmarshalBinary sets the internal buffer of b to be a clone of data, and zeros the internal offset.
331func (b *Buffer) UnmarshalBinary(data []byte) error {
332 if grow := len(data) - len(b.b); grow > 0 {
333 b.b = append(b.b, make([]byte, grow)...)
334 }
335
336 n := copy(b.b, data)
337 b.b = b.b[:n]
338 b.off = 0
339 return nil
340}