File: netbuf_read.c

package info (click to toggle)
spiped 1.6.4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,328 kB
  • sloc: ansic: 11,951; sh: 1,081; makefile: 629; perl: 121
file content (313 lines) | stat: -rw-r--r-- 7,359 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
#include <sys/types.h>

#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "events.h"
#include "network.h"

#include "netbuf.h"
#include "netbuf_ssl_internal.h"

/*
 * Set to NULL here; initialized by netbuf_ssl if SSL is being used.  This
 * allows us to avoid needing to link libssl into binaries which aren't
 * going to be using SSL.
 */
void * (* netbuf_read_ssl_func)(struct network_ssl_ctx *, uint8_t *, size_t,
    size_t, int (*)(void *, ssize_t), void *) = NULL;
void (* netbuf_read_ssl_cancel_func)(void *) = NULL;

/* Buffered reader structure. */
struct netbuf_read {
	/* Reader state. */
	int s;				/* Source socket for reads... */
	struct network_ssl_ctx * ssl;	/* ... unless we're using this. */
	int (* callback)(void *, int);	/* Callback for _wait. */
	void * cookie;			/* Cookie for _wait. */
	void * read_cookie;		/* From network_read. */
	void * immediate_cookie;	/* From events_immediate_register. */

	/* Buffer state. */
	uint8_t * buf;			/* Current read buffer. */
	size_t buflen;			/* Length of buf. */
	size_t bufpos;			/* Position of read pointer in buf. */
	size_t datalen;			/* Position of write pointer in buf. */
};

static int callback_success(void *);
static int callback_read(void *, ssize_t);

/**
 * netbuf_read_init(s):
 * Create and return a buffered reader attached to socket ${s}.  The caller
 * is responsible for ensuring that no attempts are made to read from said
 * socket except via the returned reader.
 */
struct netbuf_read *
netbuf_read_init(int s)
{

	/* Call the real function (without SSL). */
	return (netbuf_read_init2(s, NULL));
}

/**
 * netbuf_read_init2(s, ssl):
 * Behave like netbuf_read_init() if ${ssl} is NULL.  If the SSL context
 * ${ssl} is not NULL, use it and ignore ${s}.
 */
struct netbuf_read *
netbuf_read_init2(int s, struct network_ssl_ctx * ssl)
{
	struct netbuf_read * R;

	/* Bake a cookie. */
	if ((R = malloc(sizeof(struct netbuf_read))) == NULL)
		goto err0;
	R->s = s;
	R->ssl = ssl;
	R->read_cookie = NULL;
	R->immediate_cookie = NULL;

	/* Allocate buffer. */
	R->buflen = 4096;
	if ((R->buf = malloc(R->buflen)) == NULL)
		goto err1;
	R->bufpos = 0;
	R->datalen = 0;

	/* Success! */
	return (R);

err1:
	free(R);
err0:
	/* Failure! */
	return (NULL);
}

/**
 * netbuf_read_peek(R, data, datalen):
 * Set ${data} to point to the currently buffered data in the reader ${R}; set
 * ${datalen} to the number of bytes buffered.
 */
void
netbuf_read_peek(struct netbuf_read * R, uint8_t ** data, size_t * datalen)
{

	/* Point at current buffered data. */
	*data = &R->buf[R->bufpos];
	*datalen = R->datalen - R->bufpos;
}

/* Ensure that ${R} can store at least ${len} bytes. */
static int
netbuf_read_resize_buffer(struct netbuf_read * R, size_t len)
{
	uint8_t * nbuf;
	size_t nbuflen;

	/* Compute new buffer size. */
	nbuflen = R->buflen * 2;
	if (nbuflen < len)
		nbuflen = len;

	/* Allocate new buffer. */
	if ((nbuf = malloc(nbuflen)) == NULL)
		goto err0;

	/* Copy data into new buffer. */
	memcpy(nbuf, &R->buf[R->bufpos], R->datalen - R->bufpos);

	/* Free old buffer and use new buffer. */
	free(R->buf);
	R->buf = nbuf;
	R->buflen = nbuflen;
	R->datalen -= R->bufpos;
	R->bufpos = 0;

	/* Success! */
	return (0);

err0:
	/* Failure! */
	return (-1);
}

/**
 * netbuf_read_wait(R, len, callback, cookie):
 * Wait until ${R} has ${len} or more bytes of data buffered or an error
 * occurs; then invoke ${callback}(${cookie}, status) with status set to 0
 * if the data is available, -1 on error, or 1 on EOF.
 */
int
netbuf_read_wait(struct netbuf_read * R, size_t len,
    int (* callback)(void *, int), void * cookie)
{

	/* Sanity-check: We shouldn't be reading already. */
	assert(R->read_cookie == NULL);
	assert(R->immediate_cookie == NULL);

	/* Record parameters for future reference. */
	R->callback = callback;
	R->cookie = cookie;

	/* If we have enough data already, schedule a callback. */
	if (R->datalen - R->bufpos >= len) {
		if ((R->immediate_cookie =
		    events_immediate_register(callback_success, R, 0)) == NULL)
			goto err0;
		else
			goto done;
	}

	/* Resize the buffer if needed. */
	if ((R->buflen < len) && netbuf_read_resize_buffer(R, len))
		goto err0;

	/* Move data to start of buffer if needed. */
	if (R->buflen - R->bufpos < len) {
		memmove(R->buf, &R->buf[R->bufpos], R->datalen - R->bufpos);
		R->datalen -= R->bufpos;
		R->bufpos = 0;
	}

	/* Read data into the buffer. */
	if (R->ssl) {
		if ((R->read_cookie = (netbuf_read_ssl_func)(R->ssl,
		    &R->buf[R->datalen], R->buflen - R->datalen,
		    R->bufpos + len - R->datalen, callback_read, R)) == NULL)
			goto err0;
	} else {
		if ((R->read_cookie = network_read(R->s, &R->buf[R->datalen],
		    R->buflen - R->datalen, R->bufpos + len - R->datalen,
		    callback_read, R)) == NULL)
			goto err0;
	}

done:
	/* Success! */
	return (0);

err0:
	/* Failure! */
	return (-1);
}

/* Perform immediate callback for netbuf_read_wait. */
static int
callback_success(void * cookie)
{
	struct netbuf_read * R = cookie;

	/* Sanity-check: We should be expecting this callback. */
	assert(R->immediate_cookie != NULL);

	/* This callback is no longer pending. */
	R->immediate_cookie = NULL;

	/* Perform callback. */
	return ((R->callback)(R->cookie, 0));
}

/* Callback for a completed network read. */
static int
callback_read(void * cookie, ssize_t lenread)
{
	struct netbuf_read * R = cookie;

	/* Sanity-check: We should be reading. */
	assert(R->read_cookie != NULL);

	/* This callback is no longer pending. */
	R->read_cookie = NULL;

	/* Did the read fail? */
	if (lenread < 0)
		goto failed;

	/* Did we hit EOF? */
	if (lenread == 0)
		goto eof;

	/* We've got more data. */
	R->datalen += (size_t)lenread;

	/* Perform callback. */
	return ((R->callback)(R->cookie, 0));

eof:
	/* Perform EOF callback. */
	return ((R->callback)(R->cookie, 1));

failed:
	/* Perform failure callback. */
	return ((R->callback)(R->cookie, -1));
}

/**
 * netbuf_read_wait_cancel(R):
 * Cancel any in-progress wait on the reader ${R}.  Do not invoke the callback
 * associated with the wait.
 */
void
netbuf_read_wait_cancel(struct netbuf_read * R)
{

	/* If we have an in-progress read, cancel it. */
	if (R->read_cookie != NULL) {
		if (R->ssl)
			(netbuf_read_ssl_cancel_func)(R->read_cookie);
		else
			network_read_cancel(R->read_cookie);
		R->read_cookie = NULL;
	}

	/* If we have an immediate callback pending, cancel it. */
	if (R->immediate_cookie != NULL) {
		events_immediate_cancel(R->immediate_cookie);
		R->immediate_cookie = NULL;
	}
}

/**
 * netbuf_read_consume(R, len):
 * Advance the reader pointer for the reader ${R} by ${len} bytes.
 */
void
netbuf_read_consume(struct netbuf_read * R, size_t len)
{

	/* Sanity-check: We can't consume data we don't have. */
	assert(R->datalen - R->bufpos >= len);

	/* Advance the buffer pointer. */
	R->bufpos += len;
}

/**
 * netbuf_read_free(R):
 * Free the reader ${R}.  Note that an indeterminate amount of data may have
 * been buffered and will be lost.
 */
void
netbuf_read_free(struct netbuf_read * R)
{

	/* Behave consistently with free(NULL). */
	if (R == NULL)
		return;

	/* Can't free a reader which is busy. */
	assert(R->read_cookie == NULL);
	assert(R->immediate_cookie == NULL);

	/* Free the buffer and the reader. */
	free(R->buf);
	free(R);
}