summaryrefslogtreecommitdiff
path: root/src/util/netstream.h
blob: 93702eb41a26202d093f3335540ae22554f2bed9 (plain)
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
/* Copyright (c) 2012-2013 Yoran Heling

  Permission is hereby granted, free of charge, to any person obtaining
  a copy of this software and associated documentation files (the
  "Software"), to deal in the Software without restriction, including
  without limitation the rights to use, copy, modify, merge, publish,
  distribute, sublicense, and/or sell copies of the Software, and to
  permit persons to whom the Software is furnished to do so, subject to
  the following conditions:

  The above copyright notice and this permission notice shall be included
  in all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/


#ifndef UTIL_NETSTREAM_H
#define UTIL_NETSTREAM_H


/* Things that can fail. */
typedef enum {
	NETSE_WRITE,         /* write()/writev() failed, errno is in @val */
	NETSE_READ,          /* read() failed, errno is in @val */
	NETSE_TLS_WRITE,     /* gnutls_record_send() failed, return value is in @val */
	NETSE_TLS_READ,      /* gnutls_record_recv() failed, return value is in @val */
	NETSE_TLS_HANDSHAKE, /* gnutls_handshake() failed, return value is in @val */
	NETSE_TLS_BYE,       /* gnutls_bye() failed, return value is in @val */
	NETSE_ZLIB_INFLATE   /* inflate() failed, return value is in @val */
} nets_error_t;


typedef enum {
	NETSR_DISCONNECT = 1, /* Low-level read() returned 0 (and we're not in TLS) */
	NETSR_TLS_BYE    = 2, /* gnutls_record_recv() returned 0 */
	NETSR_ZLIB_END   = 4, /* We've reached the end of the zlib stream (Z_STREAM_END) */
	NETSR_CANCELLED  = 8  /* Operation cancelled */
} nets_read_t;


typedef enum { NETSS_READ = 1, NETSS_WRITE = 2, NETSS_READWRITE = 3 } nets_sync_t;

typedef struct nets_t nets_t;

typedef void (*nets_write_cb)(nets_t *n, char *buf, size_t len);
typedef void (*nets_read_cb)(nets_t *n, nets_read_t event, char *buf, size_t len);
typedef void (*nets_tls_cb)(nets_t *n, gnutls_session_t ses);
typedef void (*nets_error_cb)(nets_t *n, nets_error_t err, int val);
typedef void (*nets_sync_cb)(nets_t *n, nets_sync_t dir);
typedef void (*nets_destroy_cb)(nets_t *n, int sock);


/* Socket must be non-blocking.
 * The err function is called whenever a fatal error occurs. The callback must
 * call nets_destroy(), failing to do so may cause an infinite polling loop.*/
nets_t *nets_create(int sock, nets_error_cb err);


/* Sets the new read buffer size. The read buffer is only used for zlib
 * decompression, so this setting is ignored when that isn't used.  This
 * function allows for dynamically changing the buffer size to efficiently cope
 * with dynamic network speeds, but this function should not be called too
 * often as it may do an immediate realloc(). */
void nets_rbuf_size(nets_t *n, size_t newsize);


void nets_data_set(nets_t *n, void *d);
void *nets_data_get(nets_t *n);


/* Frees any buffers and data structures associated with the nets_t object,
 * including the object itself. This function will run any callback functions
 * queued with any of the nets_* functions, except for the error callback.  The
 * callbacks will be called with a NULL nets_t object, so be careful to check
 * for that!
 * Note that, due to the use of threading, the object may not be freed
 * immediately, and callbacks may be run shortly after this function has
 * returned. Regardless of whether this has happened, the nets_t object should
 * be considered invalid after this function.
 * If @cb is not NULL, it will be called after all other callbacks have been
 * run and right before the nets_t object is freed. This callback should not
 * use the nets_t object for anything other than nets_data_*(). If a callback
 * is specified, the socket is given back to the application. Otherwise it is
 * automatically close()d.
 */
void nets_destroy(nets_t *n, nets_destroy_cb cb);


/* Queue a buffer for writing. @buf must remain valid until the callback is
 * run. The callback may in turn call any nets_* function, but only if its
 * nets_t argument is not NULL. When @cb is NULL, the buffer will be free()'d
 * automatically after use.
 *
 * XXX: The callback currently serves two purposes: free()ing the buffer and a
 * notification that the data has been flushed. These actions are quite
 * different. The free always has to happen at some point, and that point may
 * happen before the data has been flushed (e.g. the raw buffer isn't necessary
 * anymore after it has gone through zlib compression). The notification is
 * only really interesting if the data gets flushed in the first place, not
 * when the nets_t object is destroyed. Similarly, the actions taken in the
 * callback are different: freeing only frees, but the notification may be used
 * to queue more data for writing. It may be possible to split these into two
 * separate callbacks, or auto-free() the buffer and only provide a
 * notification callback, but I'm not sure whether that is worth the effort.
 */
void nets_write(nets_t *n, const char *buf, size_t len, nets_write_cb cb);


/* Queue a read. @buf must remain valid until the callback is run. Similar to
 * nets_write(), the callback may call any nets_* function, but only if its
 * nets_t argument is not NULL. The callback will be run as soon as any data is
 * read, even if it's less than @len.
 *
 * The callback is also used to report back non-fatal read events. These are
 * reported as bit flags to the  @event argument to the callback. Note that
 * even if an @event flag has been set, something could still have been written
 * to the buffer, so always check @len to see if there's data available.
 * Possible flags are:
 *
 * NETSR_DISCONNECT
 *   The other end of the connection has been shut down. Any further
 *   nets_read() attempts will result in an error, but it's still valid to keep
 *   writing data.
 *
 * NETSR_TLS_BYE
 *   The other end of the TLS connection has been shut down. The application
 *   can queue an immediate nets_tls_disable() to reply in kind, after which
 *   further nets_read() actions may work again (without TLS, of course).
 *
 * NETSR_ZLIB_END
 *   The other end of the zlib stream has been closed (Z_FINISH). There is no
 *   need to call nets_zlibr_disable() after this, as it has been done
 *   automatically.
 *
 * NETSR_CANCELLED
 *   nets_read_cancel() has been called.
 *
 * In the current implementation, the NETSR_DISCONNECT and NETSR_TLS_BYE flags
 * will never be set in a single callback.
 *
 * Because handling the above events may require immediate action, it is
 * advisable to not have more than a single nets_read() action queued at a
 * time.
 *
 * TODO: Handle TLS rehandshake request?
 */
void nets_read(nets_t *n, char *buf, size_t len, nets_read_cb cb);


/* Cancel the nets_read() item at the top of the queue. The callback given to
 * nets_read() will be called as soon as possible with the NETSR_CANCELLED
 * event. This function is harmless if the item at the top of the read queue is
 * not a nets_read action.
 * Warning: The callback given to nets_read() may be called immediately from
 * the context of this function call. */
void nets_read_cancel(nets_t *n);


/* Push data back into the read buffer. An immediate copy of the buffer is made
 * for internal use. This function may only be called if no read action is
 * currently queued and zlib decompression is currently disabled. Can be used
 * for TLS detection as follows:
 *
 *   nets_read(..);
 *   // Callback is run with read data, and callback detects that the data has
 *   // a TLS header.
 *   nets_unread(data_where_the_tls_header_starts);
 *   nets_tls_enable(..);
 *
 * And similar with zlibr. Don't unread data when zlibr is already enabled, it
 * usually won't do what you want.
 */
void nets_unread(nets_t *n, const char *buf, size_t len);


/* Queues a TLS handshake. Ownership of @ses is transfered to the nets_t
 * object, and will be freed automatically. @cb will be called with @ses when
 * the handshake has completed or with a NULL nets_t object when it is
 * destroyed before the handshake has finished. */
void nets_tls_enable(nets_t *n, gnutls_session_t ses, nets_tls_cb cb);


/* Queues a TLS shutdown. When done, @cb will be called with a NULL session. As
 * with nets_tls_enable(), @cb will be called with a NULL nets_t object if it
 * is destroyed before the shutdown finished. */
void nets_tls_disable(nets_t *n, nets_tls_cb cb);


/* Enable zlib compression for outgoing data. The @z struct should have been
 * initialized with deflateInit() or deflateInit2(). The struct itself will be
 * copied by this function for future use, but this is not a deep copy (i.e. no
 * deflateCopy()), so it should not be used after being passed to this
 * function. @z can also be NULL, in which case a default deflate compression
 * algorithm will be used. */
void nets_zlibw_enable(nets_t *n, z_streamp z);


/* Disable zlib compression for outgoing data. */
void nets_zlibw_disable(nets_t *n);


/* Enable zlib decompression for incoming data. The @z argument has the same
 * semantics as with nets_zlibw_enable(), except you should use inflateInit()
 * to initialize the struct. Unless you have any specific requirements, NULL
 * will do fine. */
void nets_zlibr_enable(nets_t *n, z_streamp z);


/* Queue an action that does not do anything except run the specified callback.
 * When the callback is run, you can be sure that any actions that have been
 * queued before have finished. @cb is also called with a NULL nets_t object if
 * nets_destroy() has been called before the queue has been flushed.
 * Warning: If the queue is empty upon calling this function, @cb may be run
 * immediately from the context of this function. If your callback may use
 * nets_destroy(), then the nets_t object may have been destroyed by the time
 * this function returns!
 * TODO: Defer the callback to the next iteration of the event loop to prevent
 * issues with the above warning? */
void nets_sync(nets_t *n, nets_sync_t direction, nets_sync_cb cb);


/* Disable zlib decompression for incoming data. Calling this function when
 * zlib has already been disabled is harmless. Note that disabling
 * decompression is rather tricky. It can only be done reliably if the peer on
 * the other side of the connection has properly flushed its zlib buffers and
 * we have read all the remaining compressed data in a single network read().
 * Even then there's no guarantee that the other end of the connection won't
 * just continue sending compressed data.
 * Unless the network protocol uses "unclean" closing of zlib streams, it's
 * better to just wait for the other end to close the stream and catch that
 * with zlib_read() indicating a NETSR_ZLIB_END event.
 */
void nets_zlibr_disable(nets_t *n);


/* Returns the human-readable name of the action that failed,
 * e.g. NETSE_WRITE -> "WRITE" */
const char *nets_strerror_name(nets_error_t err);


/* Returns a human-readable error message. This does not include the name of
 * the action that failed, only the error that it failed with. The returned
 * string points to memory returned by strerror(), gnutls_strerror() or a local
 * string constant. */
const char *nets_strerror(nets_error_t err, int val);


#endif
/* vim: set noet sw=4 ts=4: */