1 /*
2 * buffer.c - buffer functions
3 *
4 * This file is part of the SSH Library
5 *
6 * Copyright (c) 2003-2009 by Aris Adamantiadis
7 *
8 * The SSH Library is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or (at your
11 * option) any later version.
12 *
13 * The SSH Library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
16 * License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with the SSH Library; see the file COPYING. If not, write to
20 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
21 * MA 02111-1307, USA.
22 */
23
24 #include <stdlib.h>
25 #include <string.h>
26
27 #ifndef _WIN32
28 #include <arpa/inet.h>
29 #endif
30
31 #include "libssh/priv.h"
32 #include "libssh/buffer.h"
33
34 /**
35 * @defgroup libssh_buffer The SSH buffer functions.
36 * @ingroup libssh
37 *
38 * Functions to handle SSH buffers.
39 *
40 * @{
41 */
42
43
44 #ifdef DEBUG_BUFFER
45 /**
46 * @internal
47 *
48 * @brief Check that preconditions and postconditions are valid.
49 *
50 * @param[in] buf The buffer to check.
51 */
buffer_verify(ssh_buffer buf)52 static void buffer_verify(ssh_buffer buf){
53 int doabort=0;
54 if(buf->data == NULL)
55 return;
56 if(buf->used > buf->allocated){
57 fprintf(stderr,"Buffer error : allocated %u, used %u\n",buf->allocated, buf->used);
58 doabort=1;
59 }
60 if(buf->pos > buf->used){
61 fprintf(stderr,"Buffer error : position %u, used %u\n",buf->pos, buf->used);
62 doabort=1;
63 }
64 if(buf->pos > buf->allocated){
65 fprintf(stderr,"Buffer error : position %u, allocated %u\n",buf->pos, buf->allocated);
66 doabort=1;
67 }
68 if(doabort)
69 abort();
70 }
71
72 #else
73 #define buffer_verify(x)
74 #endif
75
76 /**
77 * @brief Create a new SSH buffer.
78 *
79 * @return A newly initialized SSH buffer, NULL on error.
80 */
ssh_buffer_new(void)81 struct ssh_buffer_struct *ssh_buffer_new(void) {
82 struct ssh_buffer_struct *buf = malloc(sizeof(struct ssh_buffer_struct));
83
84 if (buf == NULL) {
85 return NULL;
86 }
87 memset(buf, 0, sizeof(struct ssh_buffer_struct));
88 buffer_verify(buf);
89 return buf;
90 }
91
92 /**
93 * @brief Deallocate a SSH buffer.
94 *
95 * \param[in] buffer The buffer to free.
96 */
ssh_buffer_free(struct ssh_buffer_struct * buffer)97 void ssh_buffer_free(struct ssh_buffer_struct *buffer) {
98 if (buffer == NULL) {
99 return;
100 }
101 buffer_verify(buffer);
102
103 if (buffer->data) {
104 /* burn the data */
105 memset(buffer->data, 0, buffer->allocated);
106 SAFE_FREE(buffer->data);
107 }
108 memset(buffer, 'X', sizeof(*buffer));
109 SAFE_FREE(buffer);
110 }
111
realloc_buffer(struct ssh_buffer_struct * buffer,int needed)112 static int realloc_buffer(struct ssh_buffer_struct *buffer, int needed) {
113 int smallest = 1;
114 char *new = NULL;
115 buffer_verify(buffer);
116 /* Find the smallest power of two which is greater or equal to needed */
117 while(smallest <= needed) {
118 smallest <<= 1;
119 }
120 needed = smallest;
121 new = realloc(buffer->data, needed);
122 if (new == NULL) {
123 return -1;
124 }
125 buffer->data = new;
126 buffer->allocated = needed;
127 buffer_verify(buffer);
128 return 0;
129 }
130
131 /** @internal
132 * @brief shifts a buffer to remove unused data in the beginning
133 * @param buffer SSH buffer
134 */
buffer_shift(ssh_buffer buffer)135 static void buffer_shift(ssh_buffer buffer){
136 buffer_verify(buffer);
137 if(buffer->pos==0)
138 return;
139 memmove(buffer->data, buffer->data + buffer->pos, buffer->used - buffer->pos);
140 buffer->used -= buffer->pos;
141 buffer->pos=0;
142 buffer_verify(buffer);
143 }
144
145 /**
146 * @internal
147 *
148 * @brief Reinitialize a SSH buffer.
149 *
150 * @param[in] buffer The buffer to reinitialize.
151 *
152 * @return 0 on success, < 0 on error.
153 */
buffer_reinit(struct ssh_buffer_struct * buffer)154 int buffer_reinit(struct ssh_buffer_struct *buffer) {
155 buffer_verify(buffer);
156 memset(buffer->data, 0, buffer->used);
157 buffer->used = 0;
158 buffer->pos = 0;
159 if(buffer->allocated > 127) {
160 if (realloc_buffer(buffer, 127) < 0) {
161 return -1;
162 }
163 }
164 buffer_verify(buffer);
165 return 0;
166 }
167
168 /**
169 * @internal
170 *
171 * @brief Add data at the tail of a buffer.
172 *
173 * @param[in] buffer The buffer to add the data.
174 *
175 * @param[in] data A pointer to the data to add.
176 *
177 * @param[in] len The length of the data to add.
178 *
179 * @return 0 on success, < 0 on error.
180 */
buffer_add_data(struct ssh_buffer_struct * buffer,const void * data,uint32_t len)181 int buffer_add_data(struct ssh_buffer_struct *buffer, const void *data, uint32_t len) {
182 buffer_verify(buffer);
183 if (buffer->allocated < (buffer->used + len)) {
184 if(buffer->pos > 0)
185 {
186 buffer_shift(buffer);
187 }
188 if (realloc_buffer(buffer, buffer->used + len) < 0) {
189 return -1;
190 }
191 }
192
193 memcpy(buffer->data+buffer->used, data, len);
194 buffer->used+=len;
195 buffer_verify(buffer);
196 return 0;
197 }
198
199 /**
200 * @internal
201 *
202 * @brief Add a SSH string to the tail of a buffer.
203 *
204 * @param[in] buffer The buffer to add the string.
205 *
206 * @param[in] string The SSH String to add.
207 *
208 * @return 0 on success, < 0 on error.
209 */
buffer_add_ssh_string(struct ssh_buffer_struct * buffer,struct ssh_string_struct * string)210 int buffer_add_ssh_string(struct ssh_buffer_struct *buffer,
211 struct ssh_string_struct *string) {
212 uint32_t len = 0;
213
214 len = ssh_string_len(string);
215 if (buffer_add_data(buffer, string, len + sizeof(uint32_t)) < 0) {
216 return -1;
217 }
218
219 return 0;
220 }
221
222 /**
223 * @internal
224 *
225 * @brief Add a 32 bits unsigned integer to the tail of a buffer.
226 *
227 * @param[in] buffer The buffer to add the integer.
228 *
229 * @param[in] data The 32 bits integer to add.
230 *
231 * @return 0 on success, -1 on error.
232 */
buffer_add_u32(struct ssh_buffer_struct * buffer,uint32_t data)233 int buffer_add_u32(struct ssh_buffer_struct *buffer,uint32_t data){
234 if (buffer_add_data(buffer, &data, sizeof(data)) < 0) {
235 return -1;
236 }
237
238 return 0;
239 }
240
241 /**
242 * @internal
243 *
244 * @brief Add a 16 bits unsigned integer to the tail of a buffer.
245 *
246 * @param[in] buffer The buffer to add the integer.
247 *
248 * @param[in] data The 16 bits integer to add.
249 *
250 * @return 0 on success, -1 on error.
251 */
buffer_add_u16(struct ssh_buffer_struct * buffer,uint16_t data)252 int buffer_add_u16(struct ssh_buffer_struct *buffer,uint16_t data){
253 if (buffer_add_data(buffer, &data, sizeof(data)) < 0) {
254 return -1;
255 }
256
257 return 0;
258 }
259
260 /**
261 * @internal
262 *
263 * @brief Add a 64 bits unsigned integer to the tail of a buffer.
264 *
265 * @param[in] buffer The buffer to add the integer.
266 *
267 * @param[in] data The 64 bits integer to add.
268 *
269 * @return 0 on success, -1 on error.
270 */
buffer_add_u64(struct ssh_buffer_struct * buffer,uint64_t data)271 int buffer_add_u64(struct ssh_buffer_struct *buffer, uint64_t data){
272 if (buffer_add_data(buffer, &data, sizeof(data)) < 0) {
273 return -1;
274 }
275
276 return 0;
277 }
278
279 /**
280 * @internal
281 *
282 * @brief Add a 8 bits unsigned integer to the tail of a buffer.
283 *
284 * @param[in] buffer The buffer to add the integer.
285 *
286 * @param[in] data The 8 bits integer to add.
287 *
288 * @return 0 on success, -1 on error.
289 */
buffer_add_u8(struct ssh_buffer_struct * buffer,uint8_t data)290 int buffer_add_u8(struct ssh_buffer_struct *buffer,uint8_t data){
291 if (buffer_add_data(buffer, &data, sizeof(uint8_t)) < 0) {
292 return -1;
293 }
294
295 return 0;
296 }
297
298 /**
299 * @internal
300 *
301 * @brief Add data at the head of a buffer.
302 *
303 * @param[in] buffer The buffer to add the data.
304 *
305 * @param[in] data The data to prepend.
306 *
307 * @param[in] len The length of data to prepend.
308 *
309 * @return 0 on success, -1 on error.
310 */
buffer_prepend_data(struct ssh_buffer_struct * buffer,const void * data,uint32_t len)311 int buffer_prepend_data(struct ssh_buffer_struct *buffer, const void *data,
312 uint32_t len) {
313 buffer_verify(buffer);
314
315 if(len <= buffer->pos){
316 /* It's possible to insert data between begin and pos */
317 memcpy(buffer->data + (buffer->pos - len), data, len);
318 buffer->pos -= len;
319 buffer_verify(buffer);
320 return 0;
321 }
322 /* pos isn't high enough */
323 if (buffer->allocated < (buffer->used - buffer->pos + len)) {
324 if (realloc_buffer(buffer, buffer->used - buffer->pos + len) < 0) {
325 return -1;
326 }
327 }
328 memmove(buffer->data + len, buffer->data + buffer->pos, buffer->used - buffer->pos);
329 memcpy(buffer->data, data, len);
330 buffer->used += len - buffer->pos;
331 buffer->pos = 0;
332 buffer_verify(buffer);
333 return 0;
334 }
335
336 /**
337 * @internal
338 *
339 * @brief Append data from a buffer to the tail of another buffer.
340 *
341 * @param[in] buffer The destination buffer.
342 *
343 * @param[in] source The source buffer to append. It doesn't take the
344 * position of the buffer into account.
345 *
346 * @return 0 on success, -1 on error.
347 */
buffer_add_buffer(struct ssh_buffer_struct * buffer,struct ssh_buffer_struct * source)348 int buffer_add_buffer(struct ssh_buffer_struct *buffer,
349 struct ssh_buffer_struct *source) {
350 if (buffer_add_data(buffer, buffer_get_rest(source), buffer_get_rest_len(source)) < 0) {
351 return -1;
352 }
353
354 return 0;
355 }
356
357 /**
358 * @brief Get a pointer on the head of a buffer.
359 *
360 * @param[in] buffer The buffer to get the head pointer.
361 *
362 * @return A data pointer on the head. It doesn't take the position
363 * into account.
364 *
365 * @warning Don't expect data to be nul-terminated.
366 *
367 * @see buffer_get_rest()
368 * @see buffer_get_len()
369 */
ssh_buffer_get_begin(struct ssh_buffer_struct * buffer)370 void *ssh_buffer_get_begin(struct ssh_buffer_struct *buffer){
371 return buffer->data;
372 }
373
374 /**
375 * @internal
376 *
377 * @brief Get a pointer to the head of a buffer at the current position.
378 *
379 * @param[in] buffer The buffer to get the head pointer.
380 *
381 * @return A pointer to the data from current position.
382 *
383 * @see buffer_get_rest_len()
384 * @see buffer_get()
385 */
buffer_get_rest(struct ssh_buffer_struct * buffer)386 void *buffer_get_rest(struct ssh_buffer_struct *buffer){
387 return buffer->data + buffer->pos;
388 }
389
390 /**
391 * @brief Get the length of the buffer, not counting position.
392 *
393 * @param[in] buffer The buffer to get the length from.
394 *
395 * @return The length of the buffer.
396 *
397 * @see buffer_get()
398 */
ssh_buffer_get_len(struct ssh_buffer_struct * buffer)399 uint32_t ssh_buffer_get_len(struct ssh_buffer_struct *buffer){
400 return buffer->used;
401 }
402
403 /**
404 * @internal
405 *
406 * @brief Get the length of the buffer from the current position.
407 *
408 * @param[in] buffer The buffer to get the length from.
409 *
410 * @return The length of the buffer.
411 *
412 * @see buffer_get_rest()
413 */
buffer_get_rest_len(struct ssh_buffer_struct * buffer)414 uint32_t buffer_get_rest_len(struct ssh_buffer_struct *buffer){
415 buffer_verify(buffer);
416 return buffer->used - buffer->pos;
417 }
418
419 /**
420 * @internal
421 *
422 * @brief Advance the position in the buffer.
423 *
424 * This has effect to "eat" bytes at head of the buffer.
425 *
426 * @param[in] buffer The buffer to advance the position.
427 *
428 * @param[in] len The number of bytes to eat.
429 *
430 * @return The new size of the buffer.
431 */
buffer_pass_bytes(struct ssh_buffer_struct * buffer,uint32_t len)432 uint32_t buffer_pass_bytes(struct ssh_buffer_struct *buffer, uint32_t len){
433 buffer_verify(buffer);
434 if(buffer->used < buffer->pos+len)
435 return 0;
436 buffer->pos+=len;
437 /* if the buffer is empty after having passed the whole bytes into it, we can clean it */
438 if(buffer->pos==buffer->used){
439 buffer->pos=0;
440 buffer->used=0;
441 }
442 buffer_verify(buffer);
443 return len;
444 }
445
446 /**
447 * @internal
448 *
449 * @brief Cut the end of the buffer.
450 *
451 * @param[in] buffer The buffer to cut.
452 *
453 * @param[in] len The number of bytes to remove from the tail.
454 *
455 * @return The new size of the buffer.
456 */
buffer_pass_bytes_end(struct ssh_buffer_struct * buffer,uint32_t len)457 uint32_t buffer_pass_bytes_end(struct ssh_buffer_struct *buffer, uint32_t len){
458 buffer_verify(buffer);
459 if(buffer->used < buffer->pos + len)
460 return 0;
461 buffer->used-=len;
462 buffer_verify(buffer);
463 return len;
464 }
465
466 /**
467 * @internal
468 *
469 * @brief Get the remaining data out of the buffer and adjust the read pointer.
470 *
471 * @param[in] buffer The buffer to read.
472 *
473 * @param[in] data The data buffer where to store the data.
474 *
475 * @param[in] len The length to read from the buffer.
476 *
477 * @returns 0 if there is not enough data in buffer, len otherwise.
478 */
buffer_get_data(struct ssh_buffer_struct * buffer,void * data,uint32_t len)479 uint32_t buffer_get_data(struct ssh_buffer_struct *buffer, void *data, uint32_t len){
480 /*
481 * Check for a integer overflow first, then check if not enough data is in
482 * the buffer.
483 */
484 if (buffer->pos + len < len || buffer->pos + len > buffer->used) {
485 return 0;
486 }
487 memcpy(data,buffer->data+buffer->pos,len);
488 buffer->pos+=len;
489 return len; /* no yet support for partial reads (is it really needed ?? ) */
490 }
491
492 /**
493 * @internal
494 *
495 * @brief Get a 8 bits unsigned int out of the buffer and adjusts the read
496 * pointer.
497 *
498 * @param[in] buffer The buffer to read.
499 *
500 * @param[in] data A pointer to a uint8_t where to store the data.
501 *
502 * @returns 0 if there is not enough data in buffer, 1 otherwise.
503 */
buffer_get_u8(struct ssh_buffer_struct * buffer,uint8_t * data)504 int buffer_get_u8(struct ssh_buffer_struct *buffer, uint8_t *data){
505 return buffer_get_data(buffer,data,sizeof(uint8_t));
506 }
507
508 /** \internal
509 * \brief gets a 32 bits unsigned int out of the buffer. Adjusts the read pointer.
510 * \param buffer Buffer to read
511 * \param data pointer to a uint32_t where to store the data
512 * \returns 0 if there is not enough data in buffer
513 * \returns 4 otherwise.
514 */
buffer_get_u32(struct ssh_buffer_struct * buffer,uint32_t * data)515 int buffer_get_u32(struct ssh_buffer_struct *buffer, uint32_t *data){
516 return buffer_get_data(buffer,data,sizeof(uint32_t));
517 }
518 /**
519 * @internal
520 *
521 * @brief Get a 64 bits unsigned int out of the buffer and adjusts the read
522 * pointer.
523 *
524 * @param[in] buffer The buffer to read.
525 *
526 * @param[in] data A pointer to a uint64_t where to store the data.
527 *
528 * @returns 0 if there is not enough data in buffer, 8 otherwise.
529 */
buffer_get_u64(struct ssh_buffer_struct * buffer,uint64_t * data)530 int buffer_get_u64(struct ssh_buffer_struct *buffer, uint64_t *data){
531 return buffer_get_data(buffer,data,sizeof(uint64_t));
532 }
533
534 /**
535 * @internal
536 *
537 * @brief Get a SSH String out of the buffer and adjusts the read pointer.
538 *
539 * @param[in] buffer The buffer to read.
540 *
541 * @returns The SSH String, NULL on error.
542 */
buffer_get_ssh_string(struct ssh_buffer_struct * buffer)543 struct ssh_string_struct *buffer_get_ssh_string(struct ssh_buffer_struct *buffer) {
544 uint32_t stringlen;
545 uint32_t hostlen;
546 struct ssh_string_struct *str = NULL;
547
548 if (buffer_get_u32(buffer, &stringlen) == 0) {
549 return NULL;
550 }
551 hostlen = ntohl(stringlen);
552 /* verify if there is enough space in buffer to get it */
553 if ((buffer->pos + hostlen) > buffer->used) {
554 return NULL; /* it is indeed */
555 }
556 str = ssh_string_new(hostlen);
557 if (str == NULL) {
558 return NULL;
559 }
560 if (buffer_get_data(buffer, ssh_string_data(str), hostlen) != hostlen) {
561 /* should never happen */
562 SAFE_FREE(str);
563 return NULL;
564 }
565
566 return str;
567 }
568
569 /**
570 * @internal
571 *
572 * @brief Get a mpint out of the buffer and adjusts the read pointer.
573 *
574 * @note This function is SSH-1 only.
575 *
576 * @param[in] buffer The buffer to read.
577 *
578 * @returns The SSH String containing the mpint, NULL on error.
579 */
buffer_get_mpint(struct ssh_buffer_struct * buffer)580 struct ssh_string_struct *buffer_get_mpint(struct ssh_buffer_struct *buffer) {
581 uint16_t bits;
582 uint32_t len;
583 struct ssh_string_struct *str = NULL;
584
585 if (buffer_get_data(buffer, &bits, sizeof(uint16_t)) != sizeof(uint16_t)) {
586 return NULL;
587 }
588 bits = ntohs(bits);
589 len = (bits + 7) / 8;
590 if ((buffer->pos + len) > buffer->used) {
591 return NULL;
592 }
593 str = ssh_string_new(len);
594 if (str == NULL) {
595 return NULL;
596 }
597 if (buffer_get_data(buffer, ssh_string_data(str), len) != len) {
598 SAFE_FREE(str);
599 return NULL;
600 }
601 return str;
602 }
603
604 /** @} */
605
606 /* vim: set ts=4 sw=4 et cindent: */
607