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