diff options
Diffstat (limited to 'buffer.c')
-rw-r--r-- | buffer.c | 245 |
1 files changed, 55 insertions, 190 deletions
@@ -1,253 +1,118 @@ | |||
1 | /* $OpenBSD: buffer.c,v 1.35 2014/02/02 03:44:31 djm Exp $ */ | 1 | /* $OpenBSD: buffer.c,v 1.36 2014/04/30 05:29:56 djm Exp $ */ |
2 | |||
2 | /* | 3 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 4 | * Copyright (c) 2012 Damien Miller <djm@mindrot.org> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 5 | * |
5 | * All rights reserved | 6 | * Permission to use, copy, modify, and distribute this software for any |
6 | * Functions for manipulating fifo buffers (that can grow if needed). | 7 | * purpose with or without fee is hereby granted, provided that the above |
8 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | 9 | * |
8 | * As far as I am concerned, the code I have written for this software | 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
9 | * can be used freely for any purpose. Any derived versions of this | 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
10 | * software must be clearly marked as such, and if the derived work is | 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
11 | * incompatible with the protocol description in the RFC file, it must be | 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
12 | * called by a name other than "ssh" or "Secure Shell". | 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
13 | */ | 17 | */ |
14 | 18 | ||
15 | #include "includes.h" | 19 | /* Emulation wrappers for legacy OpenSSH buffer API atop sshbuf */ |
16 | 20 | ||
17 | #include <sys/param.h> | 21 | #include "includes.h" |
18 | 22 | ||
19 | #include <stdio.h> | 23 | #include <sys/types.h> |
20 | #include <string.h> | ||
21 | #include <stdarg.h> | ||
22 | #include <stdlib.h> | ||
23 | 24 | ||
24 | #include "xmalloc.h" | ||
25 | #include "buffer.h" | 25 | #include "buffer.h" |
26 | #include "log.h" | 26 | #include "log.h" |
27 | 27 | #include "ssherr.h" | |
28 | #define BUFFER_MAX_CHUNK 0x100000 | ||
29 | #define BUFFER_MAX_LEN 0xa00000 | ||
30 | #define BUFFER_ALLOCSZ 0x008000 | ||
31 | |||
32 | /* Initializes the buffer structure. */ | ||
33 | |||
34 | void | ||
35 | buffer_init(Buffer *buffer) | ||
36 | { | ||
37 | const u_int len = 4096; | ||
38 | |||
39 | buffer->alloc = 0; | ||
40 | buffer->buf = xmalloc(len); | ||
41 | buffer->alloc = len; | ||
42 | buffer->offset = 0; | ||
43 | buffer->end = 0; | ||
44 | } | ||
45 | |||
46 | /* Frees any memory used for the buffer. */ | ||
47 | |||
48 | void | ||
49 | buffer_free(Buffer *buffer) | ||
50 | { | ||
51 | if (buffer->alloc > 0) { | ||
52 | explicit_bzero(buffer->buf, buffer->alloc); | ||
53 | buffer->alloc = 0; | ||
54 | free(buffer->buf); | ||
55 | } | ||
56 | } | ||
57 | |||
58 | /* | ||
59 | * Clears any data from the buffer, making it empty. This does not actually | ||
60 | * zero the memory. | ||
61 | */ | ||
62 | |||
63 | void | ||
64 | buffer_clear(Buffer *buffer) | ||
65 | { | ||
66 | buffer->offset = 0; | ||
67 | buffer->end = 0; | ||
68 | } | ||
69 | |||
70 | /* Appends data to the buffer, expanding it if necessary. */ | ||
71 | 28 | ||
72 | void | 29 | void |
73 | buffer_append(Buffer *buffer, const void *data, u_int len) | 30 | buffer_append(Buffer *buffer, const void *data, u_int len) |
74 | { | 31 | { |
75 | void *p; | 32 | int ret; |
76 | p = buffer_append_space(buffer, len); | ||
77 | memcpy(p, data, len); | ||
78 | } | ||
79 | 33 | ||
80 | static int | 34 | if ((ret = sshbuf_put(buffer, data, len)) != 0) |
81 | buffer_compact(Buffer *buffer) | 35 | fatal("%s: %s", __func__, ssh_err(ret)); |
82 | { | ||
83 | /* | ||
84 | * If the buffer is quite empty, but all data is at the end, move the | ||
85 | * data to the beginning. | ||
86 | */ | ||
87 | if (buffer->offset > MIN(buffer->alloc, BUFFER_MAX_CHUNK)) { | ||
88 | memmove(buffer->buf, buffer->buf + buffer->offset, | ||
89 | buffer->end - buffer->offset); | ||
90 | buffer->end -= buffer->offset; | ||
91 | buffer->offset = 0; | ||
92 | return (1); | ||
93 | } | ||
94 | return (0); | ||
95 | } | 36 | } |
96 | 37 | ||
97 | /* | ||
98 | * Appends space to the buffer, expanding the buffer if necessary. This does | ||
99 | * not actually copy the data into the buffer, but instead returns a pointer | ||
100 | * to the allocated region. | ||
101 | */ | ||
102 | |||
103 | void * | 38 | void * |
104 | buffer_append_space(Buffer *buffer, u_int len) | 39 | buffer_append_space(Buffer *buffer, u_int len) |
105 | { | 40 | { |
106 | u_int newlen; | 41 | int ret; |
107 | void *p; | 42 | u_char *p; |
108 | 43 | ||
109 | if (len > BUFFER_MAX_CHUNK) | 44 | if ((ret = sshbuf_reserve(buffer, len, &p)) != 0) |
110 | fatal("buffer_append_space: len %u not supported", len); | 45 | fatal("%s: %s", __func__, ssh_err(ret)); |
111 | 46 | return p; | |
112 | /* If the buffer is empty, start using it from the beginning. */ | ||
113 | if (buffer->offset == buffer->end) { | ||
114 | buffer->offset = 0; | ||
115 | buffer->end = 0; | ||
116 | } | ||
117 | restart: | ||
118 | /* If there is enough space to store all data, store it now. */ | ||
119 | if (buffer->end + len < buffer->alloc) { | ||
120 | p = buffer->buf + buffer->end; | ||
121 | buffer->end += len; | ||
122 | return p; | ||
123 | } | ||
124 | |||
125 | /* Compact data back to the start of the buffer if necessary */ | ||
126 | if (buffer_compact(buffer)) | ||
127 | goto restart; | ||
128 | |||
129 | /* Increase the size of the buffer and retry. */ | ||
130 | newlen = roundup(buffer->alloc + len, BUFFER_ALLOCSZ); | ||
131 | if (newlen > BUFFER_MAX_LEN) | ||
132 | fatal("buffer_append_space: alloc %u not supported", | ||
133 | newlen); | ||
134 | buffer->buf = xrealloc(buffer->buf, 1, newlen); | ||
135 | buffer->alloc = newlen; | ||
136 | goto restart; | ||
137 | /* NOTREACHED */ | ||
138 | } | 47 | } |
139 | 48 | ||
140 | /* | ||
141 | * Check whether an allocation of 'len' will fit in the buffer | ||
142 | * This must follow the same math as buffer_append_space | ||
143 | */ | ||
144 | int | 49 | int |
145 | buffer_check_alloc(Buffer *buffer, u_int len) | 50 | buffer_check_alloc(Buffer *buffer, u_int len) |
146 | { | 51 | { |
147 | if (buffer->offset == buffer->end) { | 52 | int ret = sshbuf_check_reserve(buffer, len); |
148 | buffer->offset = 0; | ||
149 | buffer->end = 0; | ||
150 | } | ||
151 | restart: | ||
152 | if (buffer->end + len < buffer->alloc) | ||
153 | return (1); | ||
154 | if (buffer_compact(buffer)) | ||
155 | goto restart; | ||
156 | if (roundup(buffer->alloc + len, BUFFER_ALLOCSZ) <= BUFFER_MAX_LEN) | ||
157 | return (1); | ||
158 | return (0); | ||
159 | } | ||
160 | |||
161 | /* Returns the number of bytes of data in the buffer. */ | ||
162 | 53 | ||
163 | u_int | 54 | if (ret == 0) |
164 | buffer_len(const Buffer *buffer) | 55 | return 1; |
165 | { | 56 | if (ret == SSH_ERR_NO_BUFFER_SPACE) |
166 | return buffer->end - buffer->offset; | 57 | return 0; |
58 | fatal("%s: %s", __func__, ssh_err(ret)); | ||
167 | } | 59 | } |
168 | 60 | ||
169 | /* Gets data from the beginning of the buffer. */ | ||
170 | |||
171 | int | 61 | int |
172 | buffer_get_ret(Buffer *buffer, void *buf, u_int len) | 62 | buffer_get_ret(Buffer *buffer, void *buf, u_int len) |
173 | { | 63 | { |
174 | if (len > buffer->end - buffer->offset) { | 64 | int ret; |
175 | error("buffer_get_ret: trying to get more bytes %d than in buffer %d", | 65 | |
176 | len, buffer->end - buffer->offset); | 66 | if ((ret = sshbuf_get(buffer, buf, len)) != 0) { |
177 | return (-1); | 67 | error("%s: %s", __func__, ssh_err(ret)); |
68 | return -1; | ||
178 | } | 69 | } |
179 | memcpy(buf, buffer->buf + buffer->offset, len); | 70 | return 0; |
180 | buffer->offset += len; | ||
181 | return (0); | ||
182 | } | 71 | } |
183 | 72 | ||
184 | void | 73 | void |
185 | buffer_get(Buffer *buffer, void *buf, u_int len) | 74 | buffer_get(Buffer *buffer, void *buf, u_int len) |
186 | { | 75 | { |
187 | if (buffer_get_ret(buffer, buf, len) == -1) | 76 | if (buffer_get_ret(buffer, buf, len) == -1) |
188 | fatal("buffer_get: buffer error"); | 77 | fatal("%s: buffer error", __func__); |
189 | } | 78 | } |
190 | 79 | ||
191 | /* Consumes the given number of bytes from the beginning of the buffer. */ | ||
192 | |||
193 | int | 80 | int |
194 | buffer_consume_ret(Buffer *buffer, u_int bytes) | 81 | buffer_consume_ret(Buffer *buffer, u_int bytes) |
195 | { | 82 | { |
196 | if (bytes > buffer->end - buffer->offset) { | 83 | int ret = sshbuf_consume(buffer, bytes); |
197 | error("buffer_consume_ret: trying to get more bytes than in buffer"); | 84 | |
198 | return (-1); | 85 | if (ret == 0) |
199 | } | 86 | return 0; |
200 | buffer->offset += bytes; | 87 | if (ret == SSH_ERR_MESSAGE_INCOMPLETE) |
201 | return (0); | 88 | return -1; |
89 | fatal("%s: %s", __func__, ssh_err(ret)); | ||
202 | } | 90 | } |
203 | 91 | ||
204 | void | 92 | void |
205 | buffer_consume(Buffer *buffer, u_int bytes) | 93 | buffer_consume(Buffer *buffer, u_int bytes) |
206 | { | 94 | { |
207 | if (buffer_consume_ret(buffer, bytes) == -1) | 95 | if (buffer_consume_ret(buffer, bytes) == -1) |
208 | fatal("buffer_consume: buffer error"); | 96 | fatal("%s: buffer error", __func__); |
209 | } | 97 | } |
210 | 98 | ||
211 | /* Consumes the given number of bytes from the end of the buffer. */ | ||
212 | |||
213 | int | 99 | int |
214 | buffer_consume_end_ret(Buffer *buffer, u_int bytes) | 100 | buffer_consume_end_ret(Buffer *buffer, u_int bytes) |
215 | { | 101 | { |
216 | if (bytes > buffer->end - buffer->offset) | 102 | int ret = sshbuf_consume_end(buffer, bytes); |
217 | return (-1); | 103 | |
218 | buffer->end -= bytes; | 104 | if (ret == 0) |
219 | return (0); | 105 | return 0; |
106 | if (ret == SSH_ERR_MESSAGE_INCOMPLETE) | ||
107 | return -1; | ||
108 | fatal("%s: %s", __func__, ssh_err(ret)); | ||
220 | } | 109 | } |
221 | 110 | ||
222 | void | 111 | void |
223 | buffer_consume_end(Buffer *buffer, u_int bytes) | 112 | buffer_consume_end(Buffer *buffer, u_int bytes) |
224 | { | 113 | { |
225 | if (buffer_consume_end_ret(buffer, bytes) == -1) | 114 | if (buffer_consume_end_ret(buffer, bytes) == -1) |
226 | fatal("buffer_consume_end: trying to get more bytes than in buffer"); | 115 | fatal("%s: buffer error", __func__); |
227 | } | ||
228 | |||
229 | /* Returns a pointer to the first used byte in the buffer. */ | ||
230 | |||
231 | void * | ||
232 | buffer_ptr(const Buffer *buffer) | ||
233 | { | ||
234 | return buffer->buf + buffer->offset; | ||
235 | } | 116 | } |
236 | 117 | ||
237 | /* Dumps the contents of the buffer to stderr. */ | ||
238 | 118 | ||
239 | void | ||
240 | buffer_dump(const Buffer *buffer) | ||
241 | { | ||
242 | u_int i; | ||
243 | u_char *ucp = buffer->buf; | ||
244 | |||
245 | for (i = buffer->offset; i < buffer->end; i++) { | ||
246 | fprintf(stderr, "%02x", ucp[i]); | ||
247 | if ((i-buffer->offset)%16==15) | ||
248 | fprintf(stderr, "\r\n"); | ||
249 | else if ((i-buffer->offset)%2==1) | ||
250 | fprintf(stderr, " "); | ||
251 | } | ||
252 | fprintf(stderr, "\r\n"); | ||
253 | } | ||