-remove async ecc key generation, not needed
[oweals/gnunet.git] / src / util / server_mst.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 2, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file util/server_mst.c
23  * @brief convenience functions for handling inbound message buffers
24  * @author Christian Grothoff
25  */
26
27 #include "platform.h"
28 #include "gnunet_common.h"
29 #include "gnunet_connection_lib.h"
30 #include "gnunet_scheduler_lib.h"
31 #include "gnunet_server_lib.h"
32 #include "gnunet_time_lib.h"
33
34
35 #if HAVE_UNALIGNED_64_ACCESS
36 #define ALIGN_FACTOR 4
37 #else
38 #define ALIGN_FACTOR 8
39 #endif
40
41 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
42
43
44 /**
45  * Handle to a message stream tokenizer.
46  */
47 struct GNUNET_SERVER_MessageStreamTokenizer
48 {
49
50   /**
51    * Function to call on completed messages.
52    */
53   GNUNET_SERVER_MessageTokenizerCallback cb;
54
55   /**
56    * Closure for cb.
57    */
58   void *cb_cls;
59
60   /**
61    * Size of the buffer (starting at 'hdr').
62    */
63   size_t curr_buf;
64
65   /**
66    * How many bytes in buffer have we already processed?
67    */
68   size_t off;
69
70   /**
71    * How many bytes in buffer are valid right now?
72    */
73   size_t pos;
74
75   /**
76    * Beginning of the buffer.  Typed like this to force alignment.
77    */
78   struct GNUNET_MessageHeader *hdr;
79
80 };
81
82
83
84 /**
85  * Create a message stream tokenizer.
86  *
87  * @param cb function to call on completed messages
88  * @param cb_cls closure for cb
89  * @return handle to tokenizer
90  */
91 struct GNUNET_SERVER_MessageStreamTokenizer *
92 GNUNET_SERVER_mst_create (GNUNET_SERVER_MessageTokenizerCallback cb,
93                           void *cb_cls)
94 {
95   struct GNUNET_SERVER_MessageStreamTokenizer *ret;
96
97   ret = GNUNET_malloc (sizeof (struct GNUNET_SERVER_MessageStreamTokenizer));
98   ret->hdr = GNUNET_malloc (GNUNET_SERVER_MIN_BUFFER_SIZE);
99   ret->curr_buf = GNUNET_SERVER_MIN_BUFFER_SIZE;
100   ret->cb = cb;
101   ret->cb_cls = cb_cls;
102   return ret;
103 }
104
105
106 /**
107  * Add incoming data to the receive buffer and call the
108  * callback for all complete messages.
109  *
110  * @param mst tokenizer to use
111  * @param client_identity ID of client for which this is a buffer
112  * @param buf input data to add
113  * @param size number of bytes in buf
114  * @param purge should any excess bytes in the buffer be discarded
115  *       (i.e. for packet-based services like UDP)
116  * @param one_shot only call callback once, keep rest of message in buffer
117  * @return GNUNET_OK if we are done processing (need more data)
118  *         GNUNET_NO if one_shot was set and we have another message ready
119  *         GNUNET_SYSERR if the data stream is corrupt
120  */
121 int
122 GNUNET_SERVER_mst_receive (struct GNUNET_SERVER_MessageStreamTokenizer *mst,
123                            void *client_identity, const char *buf, size_t size,
124                            int purge, int one_shot)
125 {
126   const struct GNUNET_MessageHeader *hdr;
127   size_t delta;
128   uint16_t want;
129   char *ibuf;
130   int need_align;
131   unsigned long offset;
132   int ret;
133
134   GNUNET_assert (mst->off <= mst->pos);
135   GNUNET_assert (mst->pos <= mst->curr_buf);
136   LOG (GNUNET_ERROR_TYPE_DEBUG,
137        "Server-mst receives %u bytes with %u bytes already in private buffer\n",
138        (unsigned int) size, (unsigned int) (mst->pos - mst->off));
139   ret = GNUNET_OK;
140   ibuf = (char *) mst->hdr;
141   while (mst->pos > 0)
142   {
143 do_align:
144     GNUNET_assert (mst->pos >= mst->off);
145     if ((mst->curr_buf - mst->off < sizeof (struct GNUNET_MessageHeader)) ||
146         (0 != (mst->off % ALIGN_FACTOR)))
147     {
148       /* need to align or need more space */
149       mst->pos -= mst->off;
150       memmove (ibuf, &ibuf[mst->off], mst->pos);
151       mst->off = 0;
152     }
153     if (mst->pos - mst->off < sizeof (struct GNUNET_MessageHeader))
154     {
155       delta =
156           GNUNET_MIN (sizeof (struct GNUNET_MessageHeader) -
157                       (mst->pos - mst->off), size);
158       memcpy (&ibuf[mst->pos], buf, delta);
159       mst->pos += delta;
160       buf += delta;
161       size -= delta;
162     }
163     if (mst->pos - mst->off < sizeof (struct GNUNET_MessageHeader))
164     {
165       if (purge)
166       {
167         mst->off = 0;
168         mst->pos = 0;
169       }
170       return GNUNET_OK;
171     }
172     hdr = (const struct GNUNET_MessageHeader *) &ibuf[mst->off];
173     want = ntohs (hdr->size);
174     if (want < sizeof (struct GNUNET_MessageHeader))
175     {
176       GNUNET_break_op (0);
177       return GNUNET_SYSERR;
178     }
179     if ( (mst->curr_buf - mst->off < want) &&
180          (mst->off > 0) )
181     {
182       /* can get more space by moving */
183       mst->pos -= mst->off;
184       memmove (ibuf, &ibuf[mst->off], mst->pos);
185       mst->off = 0;
186     }
187     if (mst->curr_buf < want)
188     {
189       /* need to get more space by growing buffer */
190       GNUNET_assert (0 == mst->off);
191       mst->hdr = GNUNET_realloc (mst->hdr, want);
192       ibuf = (char *) mst->hdr;
193       mst->curr_buf = want;
194     }
195     hdr = (const struct GNUNET_MessageHeader *) &ibuf[mst->off];
196     if (mst->pos - mst->off < want)
197     {
198       delta = GNUNET_MIN (want - (mst->pos - mst->off), size);
199       GNUNET_assert (mst->pos + delta <= mst->curr_buf);
200       memcpy (&ibuf[mst->pos], buf, delta);
201       mst->pos += delta;
202       buf += delta;
203       size -= delta;
204     }
205     if (mst->pos - mst->off < want)
206     {
207       if (purge)
208       {
209         mst->off = 0;
210         mst->pos = 0;
211       }
212       return GNUNET_OK;
213     }
214     if (one_shot == GNUNET_SYSERR)
215     {
216       /* cannot call callback again, but return value saying that
217        * we have another full message in the buffer */
218       ret = GNUNET_NO;
219       goto copy;
220     }
221     if (one_shot == GNUNET_YES)
222       one_shot = GNUNET_SYSERR;
223     mst->off += want;
224     if (GNUNET_SYSERR == mst->cb (mst->cb_cls, client_identity, hdr))
225       return GNUNET_SYSERR;
226     if (mst->off == mst->pos)
227     {
228       /* reset to beginning of buffer, it's free right now! */
229       mst->off = 0;
230       mst->pos = 0;
231     }
232   }
233   GNUNET_assert (0 == mst->pos);
234   while (size > 0)
235   {
236     LOG (GNUNET_ERROR_TYPE_DEBUG,
237          "Server-mst has %u bytes left in inbound buffer\n",
238          (unsigned int) size);
239     if (size < sizeof (struct GNUNET_MessageHeader))
240       break;
241     offset = (unsigned long) buf;
242     need_align = (0 != (offset % ALIGN_FACTOR)) ? GNUNET_YES : GNUNET_NO;
243     if (GNUNET_NO == need_align)
244     {
245       /* can try to do zero-copy and process directly from original buffer */
246       hdr = (const struct GNUNET_MessageHeader *) buf;
247       want = ntohs (hdr->size);
248       if (want < sizeof (struct GNUNET_MessageHeader))
249       {
250         GNUNET_break_op (0);
251         mst->off = 0;
252         return GNUNET_SYSERR;
253       }
254       if (size < want)
255         break;                  /* or not: buffer incomplete, so copy to private buffer... */
256       if (one_shot == GNUNET_SYSERR)
257       {
258         /* cannot call callback again, but return value saying that
259          * we have another full message in the buffer */
260         ret = GNUNET_NO;
261         goto copy;
262       }
263       if (one_shot == GNUNET_YES)
264         one_shot = GNUNET_SYSERR;
265       if (GNUNET_SYSERR == mst->cb (mst->cb_cls, client_identity, hdr))
266         return GNUNET_SYSERR;
267       buf += want;
268       size -= want;
269     }
270     else
271     {
272       /* need to copy to private buffer to align;
273        * yes, we go a bit more spagetti than usual here */
274       goto do_align;
275     }
276   }
277 copy:
278   if ((size > 0) && (!purge))
279   {
280     if (size + mst->pos > mst->curr_buf)
281     {
282       mst->hdr = GNUNET_realloc (mst->hdr, size + mst->pos);
283       ibuf = (char *) mst->hdr;
284       mst->curr_buf = size + mst->pos;
285     }
286     GNUNET_assert (size + mst->pos <= mst->curr_buf);
287     memcpy (&ibuf[mst->pos], buf, size);
288     mst->pos += size;
289   }
290   if (purge)
291   {
292     mst->off = 0;
293     mst->pos = 0;
294   }
295   LOG (GNUNET_ERROR_TYPE_DEBUG,
296        "Server-mst leaves %u bytes in private buffer\n",
297        (unsigned int) (mst->pos - mst->off));
298   return ret;
299 }
300
301
302 /**
303  * Destroys a tokenizer.
304  *
305  * @param mst tokenizer to destroy
306  */
307 void
308 GNUNET_SERVER_mst_destroy (struct GNUNET_SERVER_MessageStreamTokenizer *mst)
309 {
310   GNUNET_free (mst->hdr);
311   GNUNET_free (mst);
312 }
313
314
315
316 /* end of server_mst.c */