1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2020, 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  ***************************************************************************/
22 
23 #include "curl_setup.h"
24 #include "dynbuf.h"
25 #include "curl_printf.h"
26 #ifdef BUILDING_LIBCURL
27 #include "curl_memory.h"
28 #endif
29 #include "memdebug.h"
30 
31 #define MIN_FIRST_ALLOC 32
32 
33 #define DYNINIT 0xbee51da /* random pattern */
34 
35 /*
36  * Init a dynbuf struct.
37  */
Curl_dyn_init(struct dynbuf * s,size_t toobig)38 void Curl_dyn_init(struct dynbuf *s, size_t toobig)
39 {
40   DEBUGASSERT(s);
41   DEBUGASSERT(toobig);
42   s->bufr = NULL;
43   s->leng = 0;
44   s->allc = 0;
45   s->toobig = toobig;
46 #ifdef DEBUGBUILD
47   s->init = DYNINIT;
48 #endif
49 }
50 
51 /*
52  * free the buffer and re-init the necessary fields. It doesn't touch the
53  * 'init' field and thus this buffer can be reused to add data to again.
54  */
Curl_dyn_free(struct dynbuf * s)55 void Curl_dyn_free(struct dynbuf *s)
56 {
57   DEBUGASSERT(s);
58   Curl_safefree(s->bufr);
59   s->leng = s->allc = 0;
60 }
61 
62 /*
63  * Store/append an chunk of memory to the dynbuf.
64  */
dyn_nappend(struct dynbuf * s,const unsigned char * mem,size_t len)65 static CURLcode dyn_nappend(struct dynbuf *s,
66                             const unsigned char *mem, size_t len)
67 {
68   size_t indx = s->leng;
69   size_t a = s->allc;
70   size_t fit = len + indx + 1; /* new string + old string + zero byte */
71 
72   /* try to detect if there's rubbish in the struct */
73   DEBUGASSERT(s->init == DYNINIT);
74   DEBUGASSERT(s->toobig);
75   DEBUGASSERT(indx < s->toobig);
76   DEBUGASSERT(!s->leng || s->bufr);
77 
78   if(fit > s->toobig) {
79     Curl_dyn_free(s);
80     return CURLE_OUT_OF_MEMORY;
81   }
82   else if(!a) {
83     DEBUGASSERT(!indx);
84     /* first invoke */
85     if(fit < MIN_FIRST_ALLOC)
86       a = MIN_FIRST_ALLOC;
87     else
88       a = fit;
89   }
90   else {
91     while(a < fit)
92       a *= 2;
93   }
94 
95   if(a != s->allc) {
96     /* this logic is not using Curl_saferealloc() to make the tool not have to
97        include that as well when it uses this code */
98     void *p = realloc(s->bufr, a);
99     if(!p) {
100       Curl_safefree(s->bufr);
101       s->leng = s->allc = 0;
102       return CURLE_OUT_OF_MEMORY;
103     }
104     s->bufr = p;
105     s->allc = a;
106   }
107 
108   if(len)
109     memcpy(&s->bufr[indx], mem, len);
110   s->leng = indx + len;
111   s->bufr[s->leng] = 0;
112   return CURLE_OK;
113 }
114 
115 /*
116  * Clears the string, keeps the allocation. This can also be called on a
117  * buffer that already was freed.
118  */
Curl_dyn_reset(struct dynbuf * s)119 void Curl_dyn_reset(struct dynbuf *s)
120 {
121   DEBUGASSERT(s);
122   DEBUGASSERT(s->init == DYNINIT);
123   DEBUGASSERT(!s->leng || s->bufr);
124   if(s->leng)
125     s->bufr[0] = 0;
126   s->leng = 0;
127 }
128 
129 #ifdef USE_NGTCP2
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 #endif
155 
156 /*
157  * Appends a buffer with length.
158  */
Curl_dyn_addn(struct dynbuf * s,const void * mem,size_t len)159 CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
160 {
161   DEBUGASSERT(s);
162   DEBUGASSERT(s->init == DYNINIT);
163   DEBUGASSERT(!s->leng || s->bufr);
164   return dyn_nappend(s, mem, len);
165 }
166 
167 /*
168  * Append a null-terminated string at the end.
169  */
Curl_dyn_add(struct dynbuf * s,const char * str)170 CURLcode Curl_dyn_add(struct dynbuf *s, const char *str)
171 {
172   size_t n = strlen(str);
173   DEBUGASSERT(s);
174   DEBUGASSERT(s->init == DYNINIT);
175   DEBUGASSERT(!s->leng || s->bufr);
176   return dyn_nappend(s, (unsigned char *)str, n);
177 }
178 
179 /*
180  * Append a string vprintf()-style
181  */
Curl_dyn_vaddf(struct dynbuf * s,const char * fmt,va_list ap)182 CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
183 {
184 #ifdef BUILDING_LIBCURL
185   int rc;
186   DEBUGASSERT(s);
187   DEBUGASSERT(s->init == DYNINIT);
188   DEBUGASSERT(!s->leng || s->bufr);
189   rc = Curl_dyn_vprintf(s, fmt, ap);
190 
191   if(!rc)
192     return CURLE_OK;
193 #else
194   char *str;
195   str = vaprintf(fmt, ap); /* this allocs a new string to append */
196 
197   if(str) {
198     CURLcode result = dyn_nappend(s, (unsigned char *)str, strlen(str));
199     free(str);
200     return result;
201   }
202   /* If we failed, we cleanup the whole buffer and return error */
203   Curl_dyn_free(s);
204 #endif
205   return CURLE_OUT_OF_MEMORY;
206 }
207 
208 /*
209  * Append a string printf()-style
210  */
Curl_dyn_addf(struct dynbuf * s,const char * fmt,...)211 CURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...)
212 {
213   CURLcode result;
214   va_list ap;
215   DEBUGASSERT(s);
216   DEBUGASSERT(s->init == DYNINIT);
217   DEBUGASSERT(!s->leng || s->bufr);
218   va_start(ap, fmt);
219   result = Curl_dyn_vaddf(s, fmt, ap);
220   va_end(ap);
221   return result;
222 }
223 
224 /*
225  * Returns a pointer to the buffer.
226  */
Curl_dyn_ptr(const struct dynbuf * s)227 char *Curl_dyn_ptr(const struct dynbuf *s)
228 {
229   DEBUGASSERT(s);
230   DEBUGASSERT(s->init == DYNINIT);
231   DEBUGASSERT(!s->leng || s->bufr);
232   return s->bufr;
233 }
234 
235 /*
236  * Returns an unsigned pointer to the buffer.
237  */
Curl_dyn_uptr(const struct dynbuf * s)238 unsigned char *Curl_dyn_uptr(const struct dynbuf *s)
239 {
240   DEBUGASSERT(s);
241   DEBUGASSERT(s->init == DYNINIT);
242   DEBUGASSERT(!s->leng || s->bufr);
243   return (unsigned char *)s->bufr;
244 }
245 
246 /*
247  * Returns the length of the buffer.
248  */
Curl_dyn_len(const struct dynbuf * s)249 size_t Curl_dyn_len(const struct dynbuf *s)
250 {
251   DEBUGASSERT(s);
252   DEBUGASSERT(s->init == DYNINIT);
253   DEBUGASSERT(!s->leng || s->bufr);
254   return s->leng;
255 }
256