Line data Source code
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
47 1212 : init_membuf (membuf_t *mb, int initiallen)
48 : {
49 1212 : mb->len = 0;
50 1212 : mb->size = initiallen;
51 1212 : mb->out_of_core = 0;
52 1212 : mb->buf = xtrymalloc (initiallen);
53 1212 : if (!mb->buf)
54 0 : mb->out_of_core = errno;
55 1212 : }
56 :
57 : /* Same as init_membuf but allocates the buffer in secure memory. */
58 : void
59 288 : init_membuf_secure (membuf_t *mb, int initiallen)
60 : {
61 288 : mb->len = 0;
62 288 : mb->size = initiallen;
63 288 : mb->out_of_core = 0;
64 288 : mb->buf = xtrymalloc_secure (initiallen);
65 288 : if (!mb->buf)
66 0 : mb->out_of_core = errno;
67 288 : }
68 :
69 :
70 : /* Shift the 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
75 0 : clear_membuf (membuf_t *mb, size_t amount)
76 : {
77 : /* No need to clear if we are already out of core. */
78 0 : if (mb->out_of_core)
79 0 : return;
80 0 : if (amount >= mb->len)
81 0 : mb->len = 0;
82 : else
83 : {
84 0 : mb->len -= amount;
85 0 : memmove (mb->buf, mb->buf+amount, mb->len);
86 : }
87 : }
88 :
89 :
90 : void
91 3372 : put_membuf (membuf_t *mb, const void *buf, size_t len)
92 : {
93 3372 : if (mb->out_of_core || !len)
94 64 : return;
95 :
96 3308 : if (mb->len + len >= mb->size)
97 : {
98 : char *p;
99 :
100 0 : mb->size += len + 1024;
101 0 : p = xtryrealloc (mb->buf, mb->size);
102 0 : if (!p)
103 : {
104 0 : 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 0 : wipememory (mb->buf, mb->len);
110 0 : return;
111 : }
112 0 : mb->buf = p;
113 : }
114 3308 : memcpy (mb->buf + mb->len, buf, len);
115 3308 : mb->len += len;
116 : }
117 :
118 :
119 : /* A variant of put_membuf accepting a void * and returning a
120 : gpg_error_t (which will always return 0) to be used as a generic
121 : callback handler. This function also allows buffer to be NULL. */
122 : gpg_error_t
123 972 : put_membuf_cb (void *opaque, const void *buf, size_t len)
124 : {
125 972 : membuf_t *data = opaque;
126 :
127 972 : if (buf)
128 972 : put_membuf (data, buf, len);
129 972 : return 0;
130 : }
131 :
132 :
133 : void
134 467 : put_membuf_str (membuf_t *mb, const char *string)
135 : {
136 467 : put_membuf (mb, string, strlen (string));
137 467 : }
138 :
139 :
140 : void
141 0 : put_membuf_printf (membuf_t *mb, const char *format, ...)
142 : {
143 : int rc;
144 : va_list arg_ptr;
145 : char *buf;
146 :
147 0 : va_start (arg_ptr, format);
148 0 : rc = gpgrt_vasprintf (&buf, format, arg_ptr);
149 0 : if (rc < 0)
150 0 : mb->out_of_core = errno ? errno : ENOMEM;
151 0 : va_end (arg_ptr);
152 0 : if (rc >= 0)
153 : {
154 0 : put_membuf (mb, buf, strlen (buf));
155 0 : xfree (buf);
156 : }
157 0 : }
158 :
159 :
160 : void *
161 1501 : get_membuf (membuf_t *mb, size_t *len)
162 : {
163 : char *p;
164 :
165 1501 : if (mb->out_of_core)
166 : {
167 1 : if (mb->buf)
168 : {
169 0 : wipememory (mb->buf, mb->len);
170 0 : xfree (mb->buf);
171 0 : mb->buf = NULL;
172 : }
173 1 : gpg_err_set_errno (mb->out_of_core);
174 1 : return NULL;
175 : }
176 :
177 1500 : p = mb->buf;
178 1500 : if (len)
179 910 : *len = mb->len;
180 1500 : mb->buf = NULL;
181 1500 : mb->out_of_core = ENOMEM; /* hack to make sure it won't get reused. */
182 1500 : return p;
183 : }
184 :
185 :
186 : /* Same as get_membuf but shrinks the reallocated space to the
187 : required size. */
188 : void *
189 67 : get_membuf_shrink (membuf_t *mb, size_t *len)
190 : {
191 : void *p, *pp;
192 : size_t dummylen;
193 :
194 67 : if (!len)
195 67 : len = &dummylen;
196 :
197 67 : p = get_membuf (mb, len);
198 67 : if (!p)
199 0 : return NULL;
200 67 : if (*len)
201 : {
202 67 : pp = xtryrealloc (p, *len);
203 67 : if (pp)
204 67 : p = pp;
205 : }
206 :
207 67 : return p;
208 : }
209 :
210 :
211 : /* Peek at the membuf MB. On success a pointer to the buffer is
212 : returned which is valid until the next operation on MB. If LEN is
213 : not NULL the current LEN of the buffer is stored there. On error
214 : NULL is returned and ERRNO is set. */
215 : const void *
216 0 : peek_membuf (membuf_t *mb, size_t *len)
217 : {
218 : const char *p;
219 :
220 0 : if (mb->out_of_core)
221 : {
222 0 : gpg_err_set_errno (mb->out_of_core);
223 0 : return NULL;
224 : }
225 :
226 0 : p = mb->buf;
227 0 : if (len)
228 0 : *len = mb->len;
229 0 : return p;
230 : }
|