libssh2
1/* Copyright (C) Sara Golemon <sarag@libssh2.org>
2* Copyright (C) Daniel Stenberg
3* Copyright (C) Simon Josefsson
4* All rights reserved.
5*
6* Redistribution and use in source and binary forms,
7* with or without modification, are permitted provided
8* that the following conditions are met:
9*
10* Redistributions of source code must retain the above
11* copyright notice, this list of conditions and the
12* following disclaimer.
13*
14* Redistributions in binary form must reproduce the above
15* copyright notice, this list of conditions and the following
16* disclaimer in the documentation and/or other materials
17* provided with the distribution.
18*
19* Neither the name of the copyright holder nor the names
20* of any other contributors may be used to endorse or
21* promote products derived from this software without
22* specific prior written permission.
23*
24* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
25* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
26* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
29* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
34* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
36* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
37* OF SUCH DAMAGE.
38*
39* SPDX-License-Identifier: BSD-3-Clause
40*/
41
42#include "libssh2_priv.h"43
44#ifdef HAVE_UNISTD_H45#include <unistd.h>46#endif47
48#include <errno.h>49#include <assert.h>50
51#ifdef _WIN3252/* Force parameter type. */
53#define recv(s, b, l, f) recv((s), (b), (int)(l), (f))54#define send(s, b, l, f) send((s), (b), (int)(l), (f))55#endif56
57/* snprintf not in Visual Studio CRT and _snprintf dangerously incompatible.
58We provide a safe wrapper if snprintf not found */
59#ifdef LIBSSH2_SNPRINTF60#include <stdarg.h>61
62/* Want safe, 'n += snprintf(b + n ...)' like function. If cp_max_len is 1
63* then assume cp is pointing to a null char and do nothing. Returns number
64* number of chars placed in cp excluding the trailing null char. So for
65* cp_max_len > 0 the return value is always < cp_max_len; for cp_max_len
66* <= 0 the return value is 0 (and no chars are written to cp). */
67int _libssh2_snprintf(char *cp, size_t cp_max_len, const char *fmt, ...)68{
69va_list args;70int n;71
72if(cp_max_len < 2)73return 0;74va_start(args, fmt);75n = vsnprintf(cp, cp_max_len, fmt, args);76va_end(args);77return (n < (int)cp_max_len) ? n : (int)(cp_max_len - 1);78}
79#endif80
81int _libssh2_error_flags(LIBSSH2_SESSION* session, int errcode,82const char *errmsg, int errflags)83{
84if(!session) {85if(errmsg)86fprintf(stderr, "Session is NULL, error: %s\n", errmsg);87return errcode;88}89
90if(session->err_flags & LIBSSH2_ERR_FLAG_DUP)91LIBSSH2_FREE(session, (char *)session->err_msg);92
93session->err_code = errcode;94session->err_flags = 0;95
96if(errmsg && ((errflags & LIBSSH2_ERR_FLAG_DUP) != 0)) {97size_t len = strlen(errmsg);98char *copy = LIBSSH2_ALLOC(session, len + 1);99if(copy) {100memcpy(copy, errmsg, len + 1);101session->err_flags = LIBSSH2_ERR_FLAG_DUP;102session->err_msg = copy;103}104else105/* Out of memory: this code path is very unlikely */106session->err_msg = "former error forgotten (OOM)";107}108else109session->err_msg = errmsg;110
111#ifdef LIBSSH2DEBUG112if((errcode == LIBSSH2_ERROR_EAGAIN) && !session->api_block_mode)113/* if this is EAGAIN and we're in non-blocking mode, don't generate114a debug output for this */
115return errcode;116_libssh2_debug((session, LIBSSH2_TRACE_ERROR, "%d - %s", session->err_code,117session->err_msg));118#endif119
120return errcode;121}
122
123int _libssh2_error(LIBSSH2_SESSION* session, int errcode, const char *errmsg)124{
125return _libssh2_error_flags(session, errcode, errmsg, 0);126}
127
128#ifdef _WIN32129int _libssh2_wsa2errno(void)130{
131switch(WSAGetLastError()) {132case WSAEWOULDBLOCK:133return EAGAIN;134
135case WSAENOTSOCK:136return EBADF;137
138case WSAEINTR:139return EINTR;140
141default:142/* It is most important to ensure errno does not stay at EAGAIN143* when a different error occurs so just set errno to a generic
144* error */
145return EIO;146}147}
148#endif149
150/* _libssh2_recv
151*
152* Replacement for the standard recv, return -errno on failure.
153*/
154ssize_t
155_libssh2_recv(libssh2_socket_t sock, void *buffer, size_t length,156int flags, void **abstract)157{
158ssize_t rc;159
160(void)abstract;161
162rc = recv(sock, buffer, length, flags);163if(rc < 0) {164int err;165#ifdef _WIN32166err = _libssh2_wsa2errno();167#else168err = errno;169#endif170/* Profiling tools that use SIGPROF can cause EINTR responses.171recv() does not modify its arguments when it returns EINTR,
172but there may be data waiting, so the caller should try again */
173if(err == EINTR)174return -EAGAIN;175/* Sometimes the first recv() function call sets errno to ENOENT on176Solaris and HP-UX */
177if(err == ENOENT)178return -EAGAIN;179#ifdef EWOULDBLOCK /* For VMS and other special unixes */180else if(err == EWOULDBLOCK)181return -EAGAIN;182#endif183else184return -err;185}186return rc;187}
188
189/* _libssh2_send
190*
191* Replacement for the standard send, return -errno on failure.
192*/
193ssize_t
194_libssh2_send(libssh2_socket_t sock, const void *buffer, size_t length,195int flags, void **abstract)196{
197ssize_t rc;198
199(void)abstract;200
201rc = send(sock, buffer, length, flags);202if(rc < 0) {203int err;204#ifdef _WIN32205err = _libssh2_wsa2errno();206#else207err = errno;208#endif209/* Profiling tools that use SIGPROF can cause EINTR responses.210send() is defined as not yet sending any data when it returns EINTR,
211so the caller should try again */
212if(err == EINTR)213return -EAGAIN;214#ifdef EWOULDBLOCK /* For VMS and other special unixes */215if(err == EWOULDBLOCK)216return -EAGAIN;217#endif218return -err;219}220return rc;221}
222
223/* libssh2_ntohu32
224*/
225uint32_t
226_libssh2_ntohu32(const unsigned char *buf)227{
228return ((uint32_t)buf[0] << 24)229| ((uint32_t)buf[1] << 16)230| ((uint32_t)buf[2] << 8)231| ((uint32_t)buf[3]);232}
233
234
235/* _libssh2_ntohu64
236*/
237libssh2_uint64_t
238_libssh2_ntohu64(const unsigned char *buf)239{
240return ((libssh2_uint64_t)buf[0] << 56)241| ((libssh2_uint64_t)buf[1] << 48)242| ((libssh2_uint64_t)buf[2] << 40)243| ((libssh2_uint64_t)buf[3] << 32)244| ((libssh2_uint64_t)buf[4] << 24)245| ((libssh2_uint64_t)buf[5] << 16)246| ((libssh2_uint64_t)buf[6] << 8)247| ((libssh2_uint64_t)buf[7]);248}
249
250/* _libssh2_htonu32
251*/
252void
253_libssh2_htonu32(unsigned char *buf, uint32_t value)254{
255buf[0] = (unsigned char)((value >> 24) & 0xFF);256buf[1] = (value >> 16) & 0xFF;257buf[2] = (value >> 8) & 0xFF;258buf[3] = value & 0xFF;259}
260
261/* _libssh2_store_u32
262*/
263void _libssh2_store_u32(unsigned char **buf, uint32_t value)264{
265_libssh2_htonu32(*buf, value);266*buf += sizeof(uint32_t);267}
268
269/* _libssh2_store_str
270*/
271int _libssh2_store_str(unsigned char **buf, const char *str, size_t len)272{
273uint32_t len_stored = (uint32_t)len;274
275_libssh2_store_u32(buf, len_stored);276if(len_stored) {277memcpy(*buf, str, len_stored);278*buf += len_stored;279}280
281assert(len_stored == len);282return len_stored == len;283}
284
285/* _libssh2_store_bignum2_bytes
286*/
287int _libssh2_store_bignum2_bytes(unsigned char **buf,288const unsigned char *bytes,289size_t len)290{
291uint32_t len_stored;292uint32_t extraByte;293const unsigned char *p;294
295for(p = bytes; len > 0 && *p == 0; --len, ++p) {}296
297extraByte = (len > 0 && (p[0] & 0x80) != 0);298len_stored = (uint32_t)len;299if(extraByte && len_stored == 0xffffffff)300len_stored--;301_libssh2_store_u32(buf, len_stored + extraByte);302
303if(extraByte) {304*buf[0] = 0;305*buf += 1;306}307
308if(len_stored) {309memcpy(*buf, p, len_stored);310*buf += len_stored;311}312
313assert(len_stored == len);314return len_stored == len;315}
316
317/* Base64 Conversion */
318
319static const short base64_reverse_table[256] = {320-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,321-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,322-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,32352, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,324-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,32515, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,326-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,32741, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,328-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,329-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,330-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,331-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,332-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,333-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,334-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,335-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1336};337
338/* libssh2_base64_decode
339*
340* Legacy public function. DEPRECATED.
341*/
342LIBSSH2_API int343libssh2_base64_decode(LIBSSH2_SESSION *session, char **data,344unsigned int *datalen, const char *src,345unsigned int src_len)346{
347int rc;348size_t dlen;349
350rc = _libssh2_base64_decode(session, data, &dlen, src, src_len);351
352if(datalen)353*datalen = (unsigned int)dlen;354
355return rc;356}
357
358/* _libssh2_base64_decode
359*
360* Decode a base64 chunk and store it into a newly alloc'd buffer
361*/
362int _libssh2_base64_decode(LIBSSH2_SESSION *session,363char **data, size_t *datalen,364const char *src, size_t src_len)365{
366unsigned char *d;367const char *s;368short v;369ssize_t i = 0, len = 0;370
371*data = LIBSSH2_ALLOC(session, ((src_len / 4) * 3) + 1);372d = (unsigned char *) *data;373if(!d) {374return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,375"Unable to allocate memory for base64 decoding");376}377
378for(s = src; s < (src + src_len); s++) {379v = base64_reverse_table[(unsigned char)*s];380if(v < 0)381continue;382switch(i % 4) {383case 0:384d[len] = (unsigned char)(v << 2);385break;386case 1:387d[len++] |= (unsigned char)(v >> 4);388d[len] = (unsigned char)(v << 4);389break;390case 2:391d[len++] |= (unsigned char)(v >> 2);392d[len] = (unsigned char)(v << 6);393break;394case 3:395d[len++] |= (unsigned char)v;396break;397}398i++;399}400if((i % 4) == 1) {401/* Invalid -- We have a byte which belongs exclusively to a partial402octet */
403LIBSSH2_FREE(session, *data);404*data = NULL;405return _libssh2_error(session, LIBSSH2_ERROR_INVAL, "Invalid base64");406}407
408*datalen = len;409return 0;410}
411
412/* ---- Base64 Encoding/Decoding Table --- */
413static const char table64[]=414"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";415
416/*
417* _libssh2_base64_encode
418*
419* Returns the length of the newly created base64 string. The third argument
420* is a pointer to an allocated area holding the base64 data. If something
421* went wrong, 0 is returned.
422*
423*/
424size_t _libssh2_base64_encode(LIBSSH2_SESSION *session,425const char *inp, size_t insize, char **outptr)426{
427unsigned char ibuf[3];428unsigned char obuf[4];429int i;430int inputparts;431char *output;432char *base64data;433const char *indata = inp;434
435*outptr = NULL; /* set to NULL in case of failure before we reach the436end */
437
438if(insize == 0)439insize = strlen(indata);440
441base64data = output = LIBSSH2_ALLOC(session, insize * 4 / 3 + 4);442if(!output)443return 0;444
445while(insize > 0) {446for(i = inputparts = 0; i < 3; i++) {447if(insize > 0) {448inputparts++;449ibuf[i] = *indata;450indata++;451insize--;452}453else454ibuf[i] = 0;455}456
457obuf[0] = (unsigned char) ((ibuf[0] & 0xFC) >> 2);458obuf[1] = (unsigned char) (((ibuf[0] & 0x03) << 4) | \459((ibuf[1] & 0xF0) >> 4));460obuf[2] = (unsigned char) (((ibuf[1] & 0x0F) << 2) | \461((ibuf[2] & 0xC0) >> 6));462obuf[3] = (unsigned char) (ibuf[2] & 0x3F);463
464switch(inputparts) {465case 1: /* only one byte read */466output[0] = table64[obuf[0]];467output[1] = table64[obuf[1]];468output[2] = '=';469output[3] = '=';470break;471case 2: /* two bytes read */472output[0] = table64[obuf[0]];473output[1] = table64[obuf[1]];474output[2] = table64[obuf[2]];475output[3] = '=';476break;477default:478output[0] = table64[obuf[0]];479output[1] = table64[obuf[1]];480output[2] = table64[obuf[2]];481output[3] = table64[obuf[3]];482break;483}484output += 4;485}486*output = 0;487*outptr = base64data; /* make it return the actual data memory */488
489return strlen(base64data); /* return the length of the new data */490}
491/* ---- End of Base64 Encoding ---- */
492
493LIBSSH2_API void494libssh2_free(LIBSSH2_SESSION *session, void *ptr)495{
496LIBSSH2_FREE(session, ptr);497}
498
499#ifdef LIBSSH2DEBUG500#include <stdarg.h>501
502LIBSSH2_API int503libssh2_trace(LIBSSH2_SESSION * session, int bitmask)504{
505session->showmask = bitmask;506return 0;507}
508
509LIBSSH2_API int510libssh2_trace_sethandler(LIBSSH2_SESSION *session, void *handler_context,511libssh2_trace_handler_func callback)512{
513session->tracehandler = callback;514session->tracehandler_context = handler_context;515return 0;516}
517
518void
519_libssh2_debug_low(LIBSSH2_SESSION * session, int context, const char *format,520...)521{
522char buffer[1536];523int len, msglen, buflen = sizeof(buffer);524va_list vargs;525struct timeval now;526static long firstsec;527static const char *const contexts[] = {528"Unknown",529"Transport",530"Key Ex",531"Userauth",532"Conn",533"SCP",534"SFTP",535"Failure Event",536"Publickey",537"Socket",538};539const char *contexttext = contexts[0];540unsigned int contextindex;541
542if(!(session->showmask & context)) {543/* no such output asked for */544return;545}546
547/* Find the first matching context string for this message */548for(contextindex = 0; contextindex < ARRAY_SIZE(contexts);549contextindex++) {550if((context & (1 << contextindex)) != 0) {551contexttext = contexts[contextindex];552break;553}554}555
556gettimeofday(&now, NULL);557if(!firstsec) {558firstsec = now.tv_sec;559}560now.tv_sec -= firstsec;561
562len = snprintf(buffer, buflen, "[libssh2] %d.%06d %s: ",563(int)now.tv_sec, (int)now.tv_usec, contexttext);564
565if(len >= buflen)566msglen = buflen - 1;567else {568buflen -= len;569msglen = len;570va_start(vargs, format);571len = vsnprintf(buffer + msglen, buflen, format, vargs);572va_end(vargs);573msglen += len < buflen ? len : buflen - 1;574}575
576if(session->tracehandler)577(session->tracehandler)(session, session->tracehandler_context, buffer,578msglen);579else580fprintf(stderr, "%s\n", buffer);581}
582
583#else584LIBSSH2_API int585libssh2_trace(LIBSSH2_SESSION * session, int bitmask)586{
587(void)session;588(void)bitmask;589return 0;590}
591
592LIBSSH2_API int593libssh2_trace_sethandler(LIBSSH2_SESSION *session, void *handler_context,594libssh2_trace_handler_func callback)595{
596(void)session;597(void)handler_context;598(void)callback;599return 0;600}
601#endif602
603/* init the list head */
604void _libssh2_list_init(struct list_head *head)605{
606head->first = head->last = NULL;607}
608
609/* add a node to the list */
610void _libssh2_list_add(struct list_head *head,611struct list_node *entry)612{
613/* store a pointer to the head */614entry->head = head;615
616/* we add this entry at the "top" so it has no next */617entry->next = NULL;618
619/* make our prev point to what the head thinks is last */620entry->prev = head->last;621
622/* and make head's last be us now */623head->last = entry;624
625/* make sure our 'prev' node points to us next */626if(entry->prev)627entry->prev->next = entry;628else629head->first = entry;630}
631
632/* return the "first" node in the list this head points to */
633void *_libssh2_list_first(struct list_head *head)634{
635return head->first;636}
637
638/* return the next node in the list */
639void *_libssh2_list_next(struct list_node *node)640{
641return node->next;642}
643
644/* return the prev node in the list */
645void *_libssh2_list_prev(struct list_node *node)646{
647return node->prev;648}
649
650/* remove this node from the list */
651void _libssh2_list_remove(struct list_node *entry)652{
653if(entry->prev)654entry->prev->next = entry->next;655else656entry->head->first = entry->next;657
658if(entry->next)659entry->next->prev = entry->prev;660else661entry->head->last = entry->prev;662}
663
664#if 0665/* insert a node before the given 'after' entry */
666void _libssh2_list_insert(struct list_node *after, /* insert before this */667struct list_node *entry)668{
669/* 'after' is next to 'entry' */670bentry->next = after;671
672/* entry's prev is then made to be the prev after current has */673entry->prev = after->prev;674
675/* the node that is now before 'entry' was previously before 'after'676and must be made to point to 'entry' correctly */
677if(entry->prev)678entry->prev->next = entry;679else680/* there was no node before this, so we make sure we point the head681pointer to this node */
682after->head->first = entry;683
684/* after's prev entry points back to entry */685after->prev = entry;686
687/* after's next entry is still the same as before */688
689/* entry's head is the same as after's */690entry->head = after->head;691}
692
693#endif694
695/* Defined in libssh2_priv.h for the correct platforms */
696#ifdef LIBSSH2_GETTIMEOFDAY697/*
698* _libssh2_gettimeofday
699* Implementation according to:
700* The Open Group Base Specifications Issue 6
701* IEEE Std 1003.1, 2004 Edition
702*/
703
704/*
705* THIS SOFTWARE IS NOT COPYRIGHTED
706*
707* This source code is offered for use in the public domain. You may
708* use, modify or distribute it freely.
709*
710* This code is distributed in the hope that it will be useful but
711* WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
712* DISCLAIMED. This includes but is not limited to warranties of
713* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
714*
715* Contributed by:
716* Danny Smith <dannysmith@users.sourceforge.net>
717*/
718
719int _libssh2_gettimeofday(struct timeval *tp, void *tzp)720{
721(void)tzp;722if(tp) {723#ifdef _WIN32724/* Offset between 1601-01-01 and 1970-01-01 in 100 nanosec units */725#define _WIN32_FT_OFFSET (116444736000000000)726
727union {728libssh2_uint64_t ns100; /* time since 1 Jan 1601 in 100ns units */729FILETIME ft;730} _now;731GetSystemTimeAsFileTime(&_now.ft);732tp->tv_usec = (long)((_now.ns100 / 10) % 1000000);733tp->tv_sec = (long)((_now.ns100 - _WIN32_FT_OFFSET) / 10000000);734#else735/* Platforms without a native implementation or local replacement */736tp->tv_usec = 0;737tp->tv_sec = 0;738#endif739}740/* Always return 0 as per Open Group Base Specifications Issue 6.741Do not set errno on error. */
742return 0;743}
744#endif745
746void *_libssh2_calloc(LIBSSH2_SESSION* session, size_t size)747{
748void *p = LIBSSH2_ALLOC(session, size);749if(p) {750memset(p, 0, size);751}752return p;753}
754
755/* XOR operation on buffers input1 and input2, result in output.
756It is safe to use an input buffer as the output buffer. */
757void _libssh2_xor_data(unsigned char *output,758const unsigned char *input1,759const unsigned char *input2,760size_t length)761{
762size_t i;763
764for(i = 0; i < length; i++)765*output++ = *input1++ ^ *input2++;766}
767
768/* Increments an AES CTR buffer to prepare it for use with the
769next AES block. */
770void _libssh2_aes_ctr_increment(unsigned char *ctr,771size_t length)772{
773unsigned char *pc;774unsigned int val, carry;775
776pc = ctr + length - 1;777carry = 1;778
779while(pc >= ctr) {780val = (unsigned int)*pc + carry;781*pc-- = val & 0xFF;782carry = val >> 8;783}784}
785
786#ifdef LIBSSH2_MEMZERO787static void * (* const volatile memset_libssh)(void *, int, size_t) = memset;788
789void _libssh2_memzero(void *buf, size_t size)790{
791memset_libssh(buf, 0, size);792}
793#endif794
795/* String buffer */
796
797struct string_buf *_libssh2_string_buf_new(LIBSSH2_SESSION *session)798{
799struct string_buf *ret;800
801ret = _libssh2_calloc(session, sizeof(*ret));802if(!ret)803return NULL;804
805return ret;806}
807
808void _libssh2_string_buf_free(LIBSSH2_SESSION *session, struct string_buf *buf)809{
810if(!buf)811return;812
813if(buf->data)814LIBSSH2_FREE(session, buf->data);815
816LIBSSH2_FREE(session, buf);817buf = NULL;818}
819
820int _libssh2_get_byte(struct string_buf *buf, unsigned char *out)821{
822if(!_libssh2_check_length(buf, 1)) {823return -1;824}825
826*out = buf->dataptr[0];827buf->dataptr += 1;828return 0;829}
830
831int _libssh2_get_boolean(struct string_buf *buf, unsigned char *out)832{
833if(!_libssh2_check_length(buf, 1)) {834return -1;835}836
837
838*out = buf->dataptr[0] == 0 ? 0 : 1;839buf->dataptr += 1;840return 0;841}
842
843int _libssh2_get_u32(struct string_buf *buf, uint32_t *out)844{
845if(!_libssh2_check_length(buf, 4)) {846return -1;847}848
849*out = _libssh2_ntohu32(buf->dataptr);850buf->dataptr += 4;851return 0;852}
853
854int _libssh2_get_u64(struct string_buf *buf, libssh2_uint64_t *out)855{
856if(!_libssh2_check_length(buf, 8)) {857return -1;858}859
860*out = _libssh2_ntohu64(buf->dataptr);861buf->dataptr += 8;862return 0;863}
864
865int _libssh2_match_string(struct string_buf *buf, const char *match)866{
867unsigned char *out;868size_t len = 0;869if(_libssh2_get_string(buf, &out, &len) || len != strlen(match) ||870strncmp((char *)out, match, strlen(match)) != 0) {871return -1;872}873return 0;874}
875
876int _libssh2_get_string(struct string_buf *buf, unsigned char **outbuf,877size_t *outlen)878{
879uint32_t data_len;880if(!buf || _libssh2_get_u32(buf, &data_len) != 0) {881return -1;882}883if(!_libssh2_check_length(buf, data_len)) {884return -1;885}886*outbuf = buf->dataptr;887buf->dataptr += data_len;888
889if(outlen)890*outlen = (size_t)data_len;891
892return 0;893}
894
895int _libssh2_copy_string(LIBSSH2_SESSION *session, struct string_buf *buf,896unsigned char **outbuf, size_t *outlen)897{
898size_t str_len;899unsigned char *str;900
901if(_libssh2_get_string(buf, &str, &str_len)) {902return -1;903}904
905if(str_len) {906*outbuf = LIBSSH2_ALLOC(session, str_len);907if(*outbuf) {908memcpy(*outbuf, str, str_len);909}910else {911return -1;912}913}914else {915*outbuf = NULL;916}917
918if(outlen)919*outlen = str_len;920
921return 0;922}
923
924int _libssh2_get_bignum_bytes(struct string_buf *buf, unsigned char **outbuf,925size_t *outlen)926{
927uint32_t data_len;928uint32_t bn_len;929unsigned char *bnptr;930
931if(_libssh2_get_u32(buf, &data_len)) {932return -1;933}934if(!_libssh2_check_length(buf, data_len)) {935return -1;936}937
938bn_len = data_len;939bnptr = buf->dataptr;940
941/* trim leading zeros */942while(bn_len > 0 && *bnptr == 0x00) {943bn_len--;944bnptr++;945}946
947*outbuf = bnptr;948buf->dataptr += data_len;949
950if(outlen)951*outlen = (size_t)bn_len;952
953return 0;954}
955
956/* Given the current location in buf, _libssh2_check_length ensures
957callers can read the next len number of bytes out of the buffer
958before reading the buffer content */
959
960int _libssh2_check_length(struct string_buf *buf, size_t len)961{
962unsigned char *endp = &buf->data[buf->len];963size_t left = endp - buf->dataptr;964return (len <= left) && (left <= buf->len);965}
966
967int _libssh2_eob(struct string_buf *buf)968{
969unsigned char *endp = &buf->data[buf->len];970return buf->dataptr >= endp;971}
972