1 /* membuf.c - A simple implementation of a dynamic buffer.
2 * Copyright (C) 2001, 2003, 2009, 2011 Free Software Foundation, Inc.
3 * Copyright (C) 2013 Werner Koch
4 *
5 * This file is part of GnuPG.
6 *
7 * This file is free software; you can redistribute it and/or modify
8 * it under the terms of either
9 *
10 * - the GNU Lesser General Public License as published by the Free
11 * Software Foundation; either version 3 of the License, or (at
12 * your option) any later version.
13 *
14 * or
15 *
16 * - the GNU General Public License as published by the Free
17 * Software Foundation; either version 2 of the License, or (at
18 * your option) any later version.
19 *
20 * or both in parallel, as here.
21 *
22 * This file is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, see <https://www.gnu.org/licenses/>.
29 */
30
31 #include <config.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <stdarg.h>
35
36 #include "util.h"
37 #include "membuf.h"
38
39
40 /* A simple implementation of a dynamic buffer. Use init_membuf() to
41 create a buffer, put_membuf to append bytes and get_membuf to
42 release and return the buffer. Allocation errors are detected but
43 only returned at the final get_membuf(), this helps not to clutter
44 the code with out of core checks. */
45
46 void
init_membuf(membuf_t * mb,int initiallen)47 init_membuf (membuf_t *mb, int initiallen)
48 {
49 mb->len = 0;
50 mb->size = initiallen;
51 mb->out_of_core = 0;
52 mb->buf = xtrymalloc (initiallen);
53 if (!mb->buf)
54 mb->out_of_core = errno;
55 }
56
57 /* Same as init_membuf but allocates the buffer in secure memory. */
58 void
init_membuf_secure(membuf_t * mb,int initiallen)59 init_membuf_secure (membuf_t *mb, int initiallen)
60 {
61 mb->len = 0;
62 mb->size = initiallen;
63 mb->out_of_core = 0;
64 mb->buf = xtrymalloc_secure (initiallen);
65 if (!mb->buf)
66 mb->out_of_core = errno;
67 }
68
69
70 /* Shift the content of the membuf MB by AMOUNT bytes. The next
71 operation will then behave as if AMOUNT bytes had not been put into
72 the buffer. If AMOUNT is greater than the actual accumulated
73 bytes, the membuf is basically reset to its initial state. */
74 void
clear_membuf(membuf_t * mb,size_t amount)75 clear_membuf (membuf_t *mb, size_t amount)
76 {
77 /* No need to clear if we are already out of core. */
78 if (mb->out_of_core)
79 return;
80 if (amount >= mb->len)
81 mb->len = 0;
82 else
83 {
84 mb->len -= amount;
85 memmove (mb->buf, mb->buf+amount, mb->len);
86 }
87 }
88
89
90 void
put_membuf(membuf_t * mb,const void * buf,size_t len)91 put_membuf (membuf_t *mb, const void *buf, size_t len)
92 {
93 if (mb->out_of_core || !len)
94 return;
95
96 if (mb->len + len >= mb->size)
97 {
98 char *p;
99
100 mb->size += len + 1024;
101 p = xtryrealloc (mb->buf, mb->size);
102 if (!p)
103 {
104 mb->out_of_core = errno ? errno : ENOMEM;
105 /* Wipe out what we already accumulated. This is required
106 in case we are storing sensitive data here. The membuf
107 API does not provide another way to cleanup after an
108 error. */
109 wipememory (mb->buf, mb->len);
110 return;
111 }
112 mb->buf = p;
113 }
114 if (buf)
115 memcpy (mb->buf + mb->len, buf, len);
116 else
117 memset (mb->buf + mb->len, 0, len);
118 mb->len += len;
119 }
120
121
122 /* A variant of put_membuf accepting a void * and returning a
123 gpg_error_t (which will always return 0) to be used as a generic
124 callback handler. This function also allows buffer to be NULL. */
125 gpg_error_t
put_membuf_cb(void * opaque,const void * buf,size_t len)126 put_membuf_cb (void *opaque, const void *buf, size_t len)
127 {
128 membuf_t *data = opaque;
129
130 if (buf)
131 put_membuf (data, buf, len);
132 return 0;
133 }
134
135
136 void
put_membuf_str(membuf_t * mb,const char * string)137 put_membuf_str (membuf_t *mb, const char *string)
138 {
139 put_membuf (mb, string, strlen (string));
140 }
141
142
143 void
put_membuf_printf(membuf_t * mb,const char * format,...)144 put_membuf_printf (membuf_t *mb, const char *format, ...)
145 {
146 int rc;
147 va_list arg_ptr;
148 char *buf;
149
150 va_start (arg_ptr, format);
151 rc = gpgrt_vasprintf (&buf, format, arg_ptr);
152 if (rc < 0)
153 mb->out_of_core = errno ? errno : ENOMEM;
154 va_end (arg_ptr);
155 if (rc >= 0)
156 {
157 put_membuf (mb, buf, strlen (buf));
158 xfree (buf);
159 }
160 }
161
162
163 void *
get_membuf(membuf_t * mb,size_t * len)164 get_membuf (membuf_t *mb, size_t *len)
165 {
166 char *p;
167
168 if (mb->out_of_core)
169 {
170 if (mb->buf)
171 {
172 wipememory (mb->buf, mb->len);
173 xfree (mb->buf);
174 mb->buf = NULL;
175 }
176 gpg_err_set_errno (mb->out_of_core);
177 return NULL;
178 }
179
180 p = mb->buf;
181 if (len)
182 *len = mb->len;
183 mb->buf = NULL;
184 mb->out_of_core = ENOMEM; /* hack to make sure it won't get reused. */
185 return p;
186 }
187
188
189 /* Same as get_membuf but shrinks the reallocated space to the
190 required size. */
191 void *
get_membuf_shrink(membuf_t * mb,size_t * len)192 get_membuf_shrink (membuf_t *mb, size_t *len)
193 {
194 void *p, *pp;
195 size_t dummylen;
196
197 if (!len)
198 len = &dummylen;
199
200 p = get_membuf (mb, len);
201 if (!p)
202 return NULL;
203 if (*len)
204 {
205 pp = xtryrealloc (p, *len);
206 if (pp)
207 p = pp;
208 }
209
210 return p;
211 }
212
213
214 /* Peek at the membuf MB. On success a pointer to the buffer is
215 returned which is valid until the next operation on MB. If LEN is
216 not NULL the current LEN of the buffer is stored there. On error
217 NULL is returned and ERRNO is set. */
218 const void *
peek_membuf(membuf_t * mb,size_t * len)219 peek_membuf (membuf_t *mb, size_t *len)
220 {
221 const char *p;
222
223 if (mb->out_of_core)
224 {
225 gpg_err_set_errno (mb->out_of_core);
226 return NULL;
227 }
228
229 p = mb->buf;
230 if (len)
231 *len = mb->len;
232 return p;
233 }
234