• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 doesn't 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's 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 
80   if(fit > s->toobig) {
81     Curl_dyn_free(s);
82     return CURLE_OUT_OF_MEMORY;
83   }
84   else if(!a) {
85     DEBUGASSERT(!indx);
86     /* first invoke */
87     if(fit < MIN_FIRST_ALLOC)
88       a = MIN_FIRST_ALLOC;
89     else
90       a = fit;
91   }
92   else {
93     while(a < fit)
94       a *= 2;
95   }
96 
97   if(a != s->allc) {
98     /* this logic is not using Curl_saferealloc() to make the tool not have to
99        include that as well when it uses this code */
100     void *p = realloc(s->bufr, a);
101     if(!p) {
102       Curl_dyn_free(s);
103       return CURLE_OUT_OF_MEMORY;
104     }
105     s->bufr = p;
106     s->allc = a;
107   }
108 
109   if(len)
110     memcpy(&s->bufr[indx], mem, len);
111   s->leng = indx + len;
112   s->bufr[s->leng] = 0;
113   return CURLE_OK;
114 }
115 
116 /*
117  * Clears the string, keeps the allocation. This can also be called on a
118  * buffer that already was freed.
119  */
Curl_dyn_reset(struct dynbuf * s)120 void Curl_dyn_reset(struct dynbuf *s)
121 {
122   DEBUGASSERT(s);
123   DEBUGASSERT(s->init == DYNINIT);
124   DEBUGASSERT(!s->leng || s->bufr);
125   if(s->leng)
126     s->bufr[0] = 0;
127   s->leng = 0;
128 }
129 
130 /*
131  * Specify the size of the tail to keep (number of bytes from the end of the
132  * buffer). The rest will be dropped.
133  */
Curl_dyn_tail(struct dynbuf * s,size_t trail)134 CURLcode Curl_dyn_tail(struct dynbuf *s, size_t trail)
135 {
136   DEBUGASSERT(s);
137   DEBUGASSERT(s->init == DYNINIT);
138   DEBUGASSERT(!s->leng || s->bufr);
139   if(trail > s->leng)
140     return CURLE_BAD_FUNCTION_ARGUMENT;
141   else if(trail == s->leng)
142     return CURLE_OK;
143   else if(!trail) {
144     Curl_dyn_reset(s);
145   }
146   else {
147     memmove(&s->bufr[0], &s->bufr[s->leng - trail], trail);
148     s->leng = trail;
149     s->bufr[s->leng] = 0;
150   }
151   return CURLE_OK;
152 
153 }
154 
155 /*
156  * Appends a buffer with length.
157  */
Curl_dyn_addn(struct dynbuf * s,const void * mem,size_t len)158 CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
159 {
160   DEBUGASSERT(s);
161   DEBUGASSERT(s->init == DYNINIT);
162   DEBUGASSERT(!s->leng || s->bufr);
163   return dyn_nappend(s, mem, len);
164 }
165 
166 /*
167  * Append a null-terminated string at the end.
168  */
Curl_dyn_add(struct dynbuf * s,const char * str)169 CURLcode Curl_dyn_add(struct dynbuf *s, const char *str)
170 {
171   size_t n = strlen(str);
172   DEBUGASSERT(s);
173   DEBUGASSERT(s->init == DYNINIT);
174   DEBUGASSERT(!s->leng || s->bufr);
175   return dyn_nappend(s, (unsigned char *)str, n);
176 }
177 
178 /*
179  * Append a string vprintf()-style
180  */
Curl_dyn_vaddf(struct dynbuf * s,const char * fmt,va_list ap)181 CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
182 {
183 #ifdef BUILDING_LIBCURL
184   int rc;
185   DEBUGASSERT(s);
186   DEBUGASSERT(s->init == DYNINIT);
187   DEBUGASSERT(!s->leng || s->bufr);
188   rc = Curl_dyn_vprintf(s, fmt, ap);
189 
190   if(!rc)
191     return CURLE_OK;
192 #else
193   char *str;
194   str = vaprintf(fmt, ap); /* this allocs a new string to append */
195 
196   if(str) {
197     CURLcode result = dyn_nappend(s, (unsigned char *)str, strlen(str));
198     free(str);
199     return result;
200   }
201   /* If we failed, we cleanup the whole buffer and return error */
202   Curl_dyn_free(s);
203 #endif
204   return CURLE_OUT_OF_MEMORY;
205 }
206 
207 /*
208  * Append a string printf()-style
209  */
Curl_dyn_addf(struct dynbuf * s,const char * fmt,...)210 CURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...)
211 {
212   CURLcode result;
213   va_list ap;
214   DEBUGASSERT(s);
215   DEBUGASSERT(s->init == DYNINIT);
216   DEBUGASSERT(!s->leng || s->bufr);
217   va_start(ap, fmt);
218   result = Curl_dyn_vaddf(s, fmt, ap);
219   va_end(ap);
220   return result;
221 }
222 
223 /*
224  * Returns a pointer to the buffer.
225  */
Curl_dyn_ptr(const struct dynbuf * s)226 char *Curl_dyn_ptr(const struct dynbuf *s)
227 {
228   DEBUGASSERT(s);
229   DEBUGASSERT(s->init == DYNINIT);
230   DEBUGASSERT(!s->leng || s->bufr);
231   return s->bufr;
232 }
233 
234 /*
235  * Returns an unsigned pointer to the buffer.
236  */
Curl_dyn_uptr(const struct dynbuf * s)237 unsigned char *Curl_dyn_uptr(const struct dynbuf *s)
238 {
239   DEBUGASSERT(s);
240   DEBUGASSERT(s->init == DYNINIT);
241   DEBUGASSERT(!s->leng || s->bufr);
242   return (unsigned char *)s->bufr;
243 }
244 
245 /*
246  * Returns the length of the buffer.
247  */
Curl_dyn_len(const struct dynbuf * s)248 size_t Curl_dyn_len(const struct dynbuf *s)
249 {
250   DEBUGASSERT(s);
251   DEBUGASSERT(s->init == DYNINIT);
252   DEBUGASSERT(!s->leng || s->bufr);
253   return s->leng;
254 }
255 
256 /*
257  * Set a new (smaller) length.
258  */
Curl_dyn_setlen(struct dynbuf * s,size_t set)259 CURLcode Curl_dyn_setlen(struct dynbuf *s, size_t set)
260 {
261   DEBUGASSERT(s);
262   DEBUGASSERT(s->init == DYNINIT);
263   DEBUGASSERT(!s->leng || s->bufr);
264   if(set > s->leng)
265     return CURLE_BAD_FUNCTION_ARGUMENT;
266   s->leng = set;
267   s->bufr[s->leng] = 0;
268   return CURLE_OK;
269 }
270