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