1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 25 #include "curl_setup.h" 26 #include "dynbuf.h" 27 #include "curl_printf.h" 28 #ifdef BUILDING_LIBCURL 29 #include "curl_memory.h" 30 #endif 31 #include "memdebug.h" 32 33 #define MIN_FIRST_ALLOC 32 34 35 #define DYNINIT 0xbee51da /* random pattern */ 36 37 /* 38 * Init a dynbuf struct. 39 */ Curl_dyn_init(struct dynbuf * s,size_t toobig)40 void Curl_dyn_init(struct dynbuf *s, size_t toobig) 41 { 42 DEBUGASSERT(s); 43 DEBUGASSERT(toobig); 44 s->bufr = NULL; 45 s->leng = 0; 46 s->allc = 0; 47 s->toobig = toobig; 48 #ifdef DEBUGBUILD 49 s->init = DYNINIT; 50 #endif 51 } 52 53 /* 54 * free the buffer and re-init the necessary fields. It does not touch the 55 * 'init' field and thus this buffer can be reused to add data to again. 56 */ Curl_dyn_free(struct dynbuf * s)57 void Curl_dyn_free(struct dynbuf *s) 58 { 59 DEBUGASSERT(s); 60 Curl_safefree(s->bufr); 61 s->leng = s->allc = 0; 62 } 63 64 /* 65 * Store/append an chunk of memory to the dynbuf. 66 */ dyn_nappend(struct dynbuf * s,const unsigned char * mem,size_t len)67 static CURLcode dyn_nappend(struct dynbuf *s, 68 const unsigned char *mem, size_t len) 69 { 70 size_t indx = s->leng; 71 size_t a = s->allc; 72 size_t fit = len + indx + 1; /* new string + old string + zero byte */ 73 74 /* try to detect if there is rubbish in the struct */ 75 DEBUGASSERT(s->init == DYNINIT); 76 DEBUGASSERT(s->toobig); 77 DEBUGASSERT(indx < s->toobig); 78 DEBUGASSERT(!s->leng || s->bufr); 79 DEBUGASSERT(a <= s->toobig); 80 DEBUGASSERT(!len || mem); 81 82 if(fit > s->toobig) { 83 Curl_dyn_free(s); 84 return CURLE_TOO_LARGE; 85 } 86 else if(!a) { 87 DEBUGASSERT(!indx); 88 /* first invoke */ 89 if(MIN_FIRST_ALLOC > s->toobig) 90 a = s->toobig; 91 else if(fit < MIN_FIRST_ALLOC) 92 a = MIN_FIRST_ALLOC; 93 else 94 a = fit; 95 } 96 else { 97 while(a < fit) 98 a *= 2; 99 if(a > s->toobig) 100 /* no point in allocating a larger buffer than this is allowed to use */ 101 a = s->toobig; 102 } 103 104 if(a != s->allc) { 105 /* this logic is not using Curl_saferealloc() to make the tool not have to 106 include that as well when it uses this code */ 107 void *p = realloc(s->bufr, a); 108 if(!p) { 109 Curl_dyn_free(s); 110 return CURLE_OUT_OF_MEMORY; 111 } 112 s->bufr = p; 113 s->allc = a; 114 } 115 116 if(len) 117 memcpy(&s->bufr[indx], mem, len); 118 s->leng = indx + len; 119 s->bufr[s->leng] = 0; 120 return CURLE_OK; 121 } 122 123 /* 124 * Clears the string, keeps the allocation. This can also be called on a 125 * buffer that already was freed. 126 */ Curl_dyn_reset(struct dynbuf * s)127 void Curl_dyn_reset(struct dynbuf *s) 128 { 129 DEBUGASSERT(s); 130 DEBUGASSERT(s->init == DYNINIT); 131 DEBUGASSERT(!s->leng || s->bufr); 132 if(s->leng) 133 s->bufr[0] = 0; 134 s->leng = 0; 135 } 136 137 /* 138 * Specify the size of the tail to keep (number of bytes from the end of the 139 * buffer). The rest will be dropped. 140 */ Curl_dyn_tail(struct dynbuf * s,size_t trail)141 CURLcode Curl_dyn_tail(struct dynbuf *s, size_t trail) 142 { 143 DEBUGASSERT(s); 144 DEBUGASSERT(s->init == DYNINIT); 145 DEBUGASSERT(!s->leng || s->bufr); 146 if(trail > s->leng) 147 return CURLE_BAD_FUNCTION_ARGUMENT; 148 else if(trail == s->leng) 149 return CURLE_OK; 150 else if(!trail) { 151 Curl_dyn_reset(s); 152 } 153 else { 154 memmove(&s->bufr[0], &s->bufr[s->leng - trail], trail); 155 s->leng = trail; 156 s->bufr[s->leng] = 0; 157 } 158 return CURLE_OK; 159 160 } 161 162 /* 163 * Appends a buffer with length. 164 */ Curl_dyn_addn(struct dynbuf * s,const void * mem,size_t len)165 CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len) 166 { 167 DEBUGASSERT(s); 168 DEBUGASSERT(s->init == DYNINIT); 169 DEBUGASSERT(!s->leng || s->bufr); 170 return dyn_nappend(s, mem, len); 171 } 172 173 /* 174 * Append a null-terminated string at the end. 175 */ Curl_dyn_add(struct dynbuf * s,const char * str)176 CURLcode Curl_dyn_add(struct dynbuf *s, const char *str) 177 { 178 size_t n; 179 DEBUGASSERT(str); 180 DEBUGASSERT(s); 181 DEBUGASSERT(s->init == DYNINIT); 182 DEBUGASSERT(!s->leng || s->bufr); 183 n = strlen(str); 184 return dyn_nappend(s, (unsigned char *)str, n); 185 } 186 187 /* 188 * Append a string vprintf()-style 189 */ Curl_dyn_vaddf(struct dynbuf * s,const char * fmt,va_list ap)190 CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap) 191 { 192 #ifdef BUILDING_LIBCURL 193 int rc; 194 DEBUGASSERT(s); 195 DEBUGASSERT(s->init == DYNINIT); 196 DEBUGASSERT(!s->leng || s->bufr); 197 DEBUGASSERT(fmt); 198 rc = Curl_dyn_vprintf(s, fmt, ap); 199 200 if(!rc) 201 return CURLE_OK; 202 else if(rc == MERR_TOO_LARGE) 203 return CURLE_TOO_LARGE; 204 return CURLE_OUT_OF_MEMORY; 205 #else 206 char *str; 207 str = vaprintf(fmt, ap); /* this allocs a new string to append */ 208 209 if(str) { 210 CURLcode result = dyn_nappend(s, (unsigned char *)str, strlen(str)); 211 free(str); 212 return result; 213 } 214 /* If we failed, we cleanup the whole buffer and return error */ 215 Curl_dyn_free(s); 216 return CURLE_OUT_OF_MEMORY; 217 #endif 218 } 219 220 /* 221 * Append a string printf()-style 222 */ Curl_dyn_addf(struct dynbuf * s,const char * fmt,...)223 CURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...) 224 { 225 CURLcode result; 226 va_list ap; 227 DEBUGASSERT(s); 228 DEBUGASSERT(s->init == DYNINIT); 229 DEBUGASSERT(!s->leng || s->bufr); 230 va_start(ap, fmt); 231 result = Curl_dyn_vaddf(s, fmt, ap); 232 va_end(ap); 233 return result; 234 } 235 236 /* 237 * Returns a pointer to the buffer. 238 */ Curl_dyn_ptr(const struct dynbuf * s)239 char *Curl_dyn_ptr(const struct dynbuf *s) 240 { 241 DEBUGASSERT(s); 242 DEBUGASSERT(s->init == DYNINIT); 243 DEBUGASSERT(!s->leng || s->bufr); 244 return s->bufr; 245 } 246 247 /* 248 * Returns an unsigned pointer to the buffer. 249 */ Curl_dyn_uptr(const struct dynbuf * s)250 unsigned char *Curl_dyn_uptr(const struct dynbuf *s) 251 { 252 DEBUGASSERT(s); 253 DEBUGASSERT(s->init == DYNINIT); 254 DEBUGASSERT(!s->leng || s->bufr); 255 return (unsigned char *)s->bufr; 256 } 257 258 /* 259 * Returns the length of the buffer. 260 */ Curl_dyn_len(const struct dynbuf * s)261 size_t Curl_dyn_len(const struct dynbuf *s) 262 { 263 DEBUGASSERT(s); 264 DEBUGASSERT(s->init == DYNINIT); 265 DEBUGASSERT(!s->leng || s->bufr); 266 return s->leng; 267 } 268 269 /* 270 * Set a new (smaller) length. 271 */ Curl_dyn_setlen(struct dynbuf * s,size_t set)272 CURLcode Curl_dyn_setlen(struct dynbuf *s, size_t set) 273 { 274 DEBUGASSERT(s); 275 DEBUGASSERT(s->init == DYNINIT); 276 DEBUGASSERT(!s->leng || s->bufr); 277 if(set > s->leng) 278 return CURLE_BAD_FUNCTION_ARGUMENT; 279 s->leng = set; 280 s->bufr[s->leng] = 0; 281 return CURLE_OK; 282 } 283