/src/gnupg/common/membuf.c
Line | Count | Source |
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 | | init_membuf (membuf_t *mb, int initiallen) |
48 | 0 | { |
49 | 0 | mb->len = 0; |
50 | 0 | mb->size = initiallen; |
51 | 0 | mb->out_of_core = 0; |
52 | 0 | mb->buf = xtrymalloc (initiallen); |
53 | 0 | if (!mb->buf) |
54 | 0 | mb->out_of_core = errno; |
55 | 0 | } |
56 | | |
57 | | /* Same as init_membuf but allocates the buffer in secure memory. */ |
58 | | void |
59 | | init_membuf_secure (membuf_t *mb, int initiallen) |
60 | 0 | { |
61 | 0 | mb->len = 0; |
62 | 0 | mb->size = initiallen; |
63 | 0 | mb->out_of_core = 0; |
64 | 0 | mb->buf = xtrymalloc_secure (initiallen); |
65 | 0 | if (!mb->buf) |
66 | 0 | mb->out_of_core = errno; |
67 | 0 | } |
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 |
75 | | clear_membuf (membuf_t *mb, size_t amount) |
76 | 0 | { |
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 | 0 | else |
83 | 0 | { |
84 | 0 | mb->len -= amount; |
85 | 0 | memmove (mb->buf, mb->buf+amount, mb->len); |
86 | 0 | } |
87 | 0 | } |
88 | | |
89 | | |
90 | | void |
91 | | put_membuf (membuf_t *mb, const void *buf, size_t len) |
92 | 0 | { |
93 | 0 | if (mb->out_of_core || !len) |
94 | 0 | return; |
95 | | |
96 | 0 | if (mb->len + len >= mb->size) |
97 | 0 | { |
98 | 0 | char *p; |
99 | |
|
100 | 0 | mb->size += len + 1024; |
101 | 0 | p = xtryrealloc (mb->buf, mb->size); |
102 | 0 | if (!p) |
103 | 0 | { |
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 | 0 | } |
112 | 0 | mb->buf = p; |
113 | 0 | } |
114 | 0 | if (buf) |
115 | 0 | memcpy (mb->buf + mb->len, buf, len); |
116 | 0 | else |
117 | 0 | memset (mb->buf + mb->len, 0, len); |
118 | 0 | mb->len += len; |
119 | 0 | } |
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 |
126 | | put_membuf_cb (void *opaque, const void *buf, size_t len) |
127 | 0 | { |
128 | 0 | membuf_t *data = opaque; |
129 | |
|
130 | 0 | if (buf) |
131 | 0 | put_membuf (data, buf, len); |
132 | 0 | return 0; |
133 | 0 | } |
134 | | |
135 | | |
136 | | void |
137 | | put_membuf_str (membuf_t *mb, const char *string) |
138 | 0 | { |
139 | 0 | put_membuf (mb, string, strlen (string)); |
140 | 0 | } |
141 | | |
142 | | |
143 | | void |
144 | | put_membuf_printf (membuf_t *mb, const char *format, ...) |
145 | 0 | { |
146 | 0 | int rc; |
147 | 0 | va_list arg_ptr; |
148 | 0 | char *buf; |
149 | |
|
150 | 0 | va_start (arg_ptr, format); |
151 | 0 | rc = gpgrt_vasprintf (&buf, format, arg_ptr); |
152 | 0 | if (rc < 0) |
153 | 0 | mb->out_of_core = errno ? errno : ENOMEM; |
154 | 0 | va_end (arg_ptr); |
155 | 0 | if (rc >= 0) |
156 | 0 | { |
157 | 0 | put_membuf (mb, buf, strlen (buf)); |
158 | 0 | xfree (buf); |
159 | 0 | } |
160 | 0 | } |
161 | | |
162 | | |
163 | | void * |
164 | | get_membuf (membuf_t *mb, size_t *len) |
165 | 0 | { |
166 | 0 | char *p; |
167 | |
|
168 | 0 | if (mb->out_of_core) |
169 | 0 | { |
170 | 0 | if (mb->buf) |
171 | 0 | { |
172 | 0 | wipememory (mb->buf, mb->len); |
173 | 0 | xfree (mb->buf); |
174 | 0 | mb->buf = NULL; |
175 | 0 | } |
176 | 0 | gpg_err_set_errno (mb->out_of_core); |
177 | 0 | return NULL; |
178 | 0 | } |
179 | | |
180 | 0 | p = mb->buf; |
181 | 0 | if (len) |
182 | 0 | *len = mb->len; |
183 | 0 | mb->buf = NULL; |
184 | 0 | mb->out_of_core = ENOMEM; /* hack to make sure it won't get reused. */ |
185 | 0 | return p; |
186 | 0 | } |
187 | | |
188 | | |
189 | | /* Same as get_membuf but shrinks the reallocated space to the |
190 | | required size. */ |
191 | | void * |
192 | | get_membuf_shrink (membuf_t *mb, size_t *len) |
193 | 0 | { |
194 | 0 | void *p, *pp; |
195 | 0 | size_t dummylen; |
196 | |
|
197 | 0 | if (!len) |
198 | 0 | len = &dummylen; |
199 | |
|
200 | 0 | p = get_membuf (mb, len); |
201 | 0 | if (!p) |
202 | 0 | return NULL; |
203 | 0 | if (*len) |
204 | 0 | { |
205 | 0 | pp = xtryrealloc (p, *len); |
206 | 0 | if (pp) |
207 | 0 | p = pp; |
208 | 0 | } |
209 | |
|
210 | 0 | return p; |
211 | 0 | } |
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 * |
219 | | peek_membuf (membuf_t *mb, size_t *len) |
220 | 0 | { |
221 | 0 | const char *p; |
222 | |
|
223 | 0 | if (mb->out_of_core) |
224 | 0 | { |
225 | 0 | gpg_err_set_errno (mb->out_of_core); |
226 | 0 | return NULL; |
227 | 0 | } |
228 | | |
229 | 0 | p = mb->buf; |
230 | 0 | if (len) |
231 | 0 | *len = mb->len; |
232 | 0 | return p; |
233 | 0 | } |
234 | | |
235 | | /* To assist using membuf with function returning an error, this |
236 | | * function sets the membuf into the error state. */ |
237 | | void |
238 | | set_membuf_err (membuf_t *mb, gpg_error_t err) |
239 | 0 | { |
240 | 0 | if (!mb->out_of_core) |
241 | 0 | { |
242 | 0 | int myerr = gpg_err_code_to_errno (gpg_err_code (err)); |
243 | 0 | mb->out_of_core = myerr? myerr : EINVAL; |
244 | 0 | } |
245 | 0 | } |