afh: Constify definition of audio format handlers.
[paraslash.git] / sideband.c
1 /* Copyright (C) 2012 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
2
3 /** \file sideband.c Implementation of the sideband API. */
4
5 #include <regex.h>
6
7 #include "para.h"
8 #include "error.h"
9 #include "portable_io.h"
10 #include "string.h"
11 #include "sideband.h"
12
13 /** Each sideband packet consists of a header and a data part. */
14 #define SIDEBAND_HEADER_SIZE 5
15
16 struct sb_context {
17         char header[SIDEBAND_HEADER_SIZE];
18         size_t bytes_dispatched; /* including header */
19         sb_transformation trafo;
20         void *trafo_context;
21         struct sb_buffer sbb;
22         size_t max_size;
23         bool dont_free;
24 };
25
26 /**
27  * Prepare to receive a sideband packet.
28  *
29  * \param max_size Do not allocate more than this many bytes.
30  * \param t Optional sideband transformation.
31  * \param trafo_context Passed verbatim to \a t.
32  *
33  * \a trafo_context is ignored if \a t is \p NULL.
34  *
35  * \return An opaque sideband handle.
36  */
37 struct sb_context *sb_new_recv(size_t max_size, sb_transformation t,
38                 void *trafo_context)
39 {
40         struct sb_context *c = para_calloc(sizeof(*c));
41
42         c->max_size = max_size;
43         c->trafo = t;
44         c->trafo_context = trafo_context;
45         return c;
46 }
47
48 /**
49  * Prepare to write a sideband packet.
50  *
51  * \param sbb Data and meta data to send.
52  * \param dont_free Do not try to deallocate the sideband buffer.
53  * \param t See \ref sb_new_recv().
54  * \param trafo_context See \ref sb_new_recv().
55  *
56  * It's OK to supply a zero-sized buffer in \a sbb. In this case only the band
57  * designator is sent through the sideband channel. Otherwise, if \a dont_free
58  * is false, the buffer of \a sbb is freed after the data has been sent.
59  *
60  * \return See \ref sb_new_recv().
61  */
62 struct sb_context *sb_new_send(struct sb_buffer *sbb, bool dont_free,
63                 sb_transformation t, void *trafo_context)
64 {
65         struct sb_context *c = para_calloc(sizeof(*c));
66         struct iovec src, dst, *srcp, *dstp;
67
68         assert(sbb);
69         c->trafo = t;
70         c->trafo_context = trafo_context;
71         c->dont_free = dont_free;
72         c->sbb = *sbb;
73         write_u32(c->header, sbb->iov.iov_len);
74         write_u8(c->header + 4, sbb->band);
75         if (!t)
76                 goto out;
77         src = (typeof(src)){.iov_base = c->header, .iov_len = SIDEBAND_HEADER_SIZE};
78         t(&src, &dst, trafo_context);
79         if (src.iov_base != dst.iov_base) {
80                 memcpy(c->header, dst.iov_base, SIDEBAND_HEADER_SIZE);
81                 free(dst.iov_base);
82         }
83         if (!iov_valid(&sbb->iov))
84                 goto out;
85         srcp = &sbb->iov;
86         dstp = &c->sbb.iov;
87         t(srcp, dstp, trafo_context);
88         if (srcp->iov_base != dstp->iov_base) {
89                 if (!c->dont_free)
90                         free(srcp->iov_base);
91                 c->dont_free = false;
92         }
93 out:
94         return c;
95 }
96
97 /**
98  * Deallocate all memory associated with a sideband handle.
99  *
100  * \param c The sideband handle.
101  *
102  * \a c must point to a handle previously returned by \ref sb_new_recv() or
103  * \ref sb_new_send(). It \a c is \p NULL, the function does nothing.
104  */
105 void sb_free(struct sb_context *c)
106 {
107         if (!c)
108                 return;
109         if (!c->dont_free)
110                 free(c->sbb.iov.iov_base);
111         free(c);
112 }
113
114 /**
115  * Obtain pointer(s) to the sideband send buffer(s).
116  *
117  * \param c The sideband handle.
118  * \param iov Array of two I/O vectors.
119  *
120  * \return The number of buffers that need to be sent, either 1 or 2.
121  *
122  * This function fills out the buffers described by \a iov. The result can be
123  * passed to \ref xwritev() or similar.
124  *
125  * \sa \ref sb_get_recv_buffer().
126  */
127 int sb_get_send_buffers(struct sb_context *c, struct iovec iov[2])
128 {
129         struct sb_buffer *sbb = &c->sbb;
130         size_t n = c->bytes_dispatched;
131
132         if (n < SIDEBAND_HEADER_SIZE) {
133                 iov[0].iov_base = c->header + n;
134                 iov[0].iov_len = SIDEBAND_HEADER_SIZE - n;
135                 if (!iov_valid(&sbb->iov))
136                         goto out;
137                 iov[1] = sbb->iov;
138                 return 2;
139         }
140         n -= SIDEBAND_HEADER_SIZE;
141         assert(n < sbb->iov.iov_len);
142         iov[0].iov_base = sbb->iov.iov_base + n;
143         iov[0].iov_len = sbb->iov.iov_len - n;
144 out:
145         iov[1].iov_base = NULL;
146         iov[1].iov_len = 0;
147         return 1;
148 }
149
150 /**
151  * Update the sideband context after data has been sent.
152  *
153  * \param c The sideband handle.
154  * \param nbytes The number of sent bytes.
155  *
156  * \return False if more data must be sent to complete the sideband transfer,
157  * true if the transfer is complete. In this case all resources are freed and
158  * the sideband handle must not be used any more.
159  */
160 bool sb_sent(struct sb_context *c, size_t nbytes)
161 {
162         struct sb_buffer *sbb = &c->sbb;
163         size_t sz = SIDEBAND_HEADER_SIZE + sbb->iov.iov_len;
164
165         assert(c->bytes_dispatched + nbytes <= sz);
166         c->bytes_dispatched += nbytes;
167         if (c->bytes_dispatched < sz)
168                 return false;
169         sb_free(c);
170         return true;
171 }
172
173 /**
174  * Obtain a pointer to the next sideband read buffer.
175  *
176  * \param c The sideband handle.
177  * \param iov Result IO vector.
178  *
179  * This fills in \a iov to point to the buffer to which the next chunk of
180  * received data should be written.
181  */
182 void sb_get_recv_buffer(struct sb_context *c, struct iovec *iov)
183 {
184         struct sb_buffer *sbb = &c->sbb;
185         size_t n = c->bytes_dispatched;
186
187         if (n < SIDEBAND_HEADER_SIZE) {
188                 iov->iov_base = c->header + n;
189                 iov->iov_len = SIDEBAND_HEADER_SIZE - n;
190                 return;
191         }
192         n -= SIDEBAND_HEADER_SIZE;
193         assert(sbb->iov.iov_base);
194         assert(sbb->iov.iov_len > n);
195         iov->iov_base = sbb->iov.iov_base + n;
196         iov->iov_len = sbb->iov.iov_len - n;
197 }
198
199 /**
200  * Update the sideband context after data has been received.
201  *
202  * \param c The sideband handle.
203  * \param nbytes The number of bytes that have been received.
204  * \param result The received sideband packet.
205  *
206  * \return Negative on errors, zero if more data needs to be read to complete
207  * this sideband packet, one if the sideband packet has been received
208  * completely.
209  *
210  * Only if the function returns one, the sideband buffer pointed to by \a
211  * result is set to point to the received data.
212  */
213 int sb_received(struct sb_context *c, size_t nbytes, struct sb_buffer *result)
214 {
215         struct sb_buffer *sbb = &c->sbb;
216         size_t n = c->bytes_dispatched,
217                 sz = SIDEBAND_HEADER_SIZE + sbb->iov.iov_len;
218
219         assert(n + nbytes <= sz);
220         c->bytes_dispatched += nbytes;
221         if (c->bytes_dispatched < SIDEBAND_HEADER_SIZE)
222                 return 0;
223         if (n >= SIDEBAND_HEADER_SIZE) { /* header has already been received */
224                 if (c->bytes_dispatched < sz) /* need to recv more body data */
225                         return 0;
226                 /* received everything, decrypt and return sbb */
227                 if (c->trafo) {
228                         struct iovec dst;
229                         c->trafo(&sbb->iov, &dst, c->trafo_context);
230                         if (sbb->iov.iov_base != dst.iov_base) {
231                                 free(sbb->iov.iov_base);
232                                 sbb->iov.iov_base = dst.iov_base;
233                         }
234                 }
235                 ((char *)(sbb->iov.iov_base))[sbb->iov.iov_len] = '\0';
236                 goto success;
237         }
238         /* header has been received, decrypt and decode it */
239         if (c->trafo) { /* decrypt */
240                 struct iovec dst, src = (typeof(src)) {
241                         .iov_base = c->header,
242                         .iov_len = SIDEBAND_HEADER_SIZE
243                 };
244                 c->trafo(&src, &dst, c->trafo_context);
245                 if (src.iov_base != dst.iov_base) {
246                         memcpy(c->header, dst.iov_base,
247                                 SIDEBAND_HEADER_SIZE);
248                         free(dst.iov_base);
249                 }
250         }
251         /* Decode header, setup sbb */
252         sbb->iov.iov_len = read_u32(c->header);
253         sbb->band = read_u8(c->header + 4);
254         sbb->iov.iov_base = NULL;
255         if (sbb->iov.iov_len == 0) /* zero-sized msg */
256                 goto success;
257         if (c->max_size > 0 && sbb->iov.iov_len > c->max_size) {
258                 PARA_ERROR_LOG("packet too big (is %zu, max %zu)\n",
259                         sbb->iov.iov_len, c->max_size);
260                 return -E_SB_PACKET_SIZE;
261         }
262         /*
263          * We like to reserve one extra byte for the terminating NULL
264          * character. However, we must make sure the +1 below does not
265          * overflow iov_len.
266          */
267         if (sbb->iov.iov_len == (size_t)-1)
268                 return -E_SB_PACKET_SIZE;
269         sbb->iov.iov_base = para_malloc(sbb->iov.iov_len + 1);
270         return 0; /* ready to read body */
271 success:
272         *result = c->sbb;
273         free(c);
274         return 1;
275 }