-fix (C) notices
[oweals/gnunet.git] / src / fs / gnunet-service-fs_lc.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2011 GNUnet e.V.
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 3, 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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20 /**
21  * @file fs/gnunet-service-fs_lc.c
22  * @brief API to handle 'local clients'
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "gnunet-service-fs.h"
27 #include "gnunet-service-fs_lc.h"
28 #include "gnunet-service-fs_cp.h"
29 #include "gnunet-service-fs_pr.h"
30
31
32 /**
33  * Doubly-linked list of requests we are performing
34  * on behalf of the same client.
35  */
36 struct ClientRequest
37 {
38
39   /**
40    * This is a doubly-linked list.
41    */
42   struct ClientRequest *next;
43
44   /**
45    * This is a doubly-linked list.
46    */
47   struct ClientRequest *prev;
48
49   /**
50    * Request this entry represents.
51    */
52   struct GSF_PendingRequest *pr;
53
54   /**
55    * Client list this request belongs to.
56    */
57   struct GSF_LocalClient *lc;
58
59   /**
60    * Task scheduled to destroy the request.
61    */
62   struct GNUNET_SCHEDULER_Task * kill_task;
63
64 };
65
66
67 /**
68  * Replies to be transmitted to the client.  The actual
69  * response message is allocated after this struct.
70  */
71 struct ClientResponse
72 {
73   /**
74    * This is a doubly-linked list.
75    */
76   struct ClientResponse *next;
77
78   /**
79    * This is a doubly-linked list.
80    */
81   struct ClientResponse *prev;
82
83   /**
84    * Client list entry this response belongs to.
85    */
86   struct GSF_LocalClient *lc;
87
88   /**
89    * Number of bytes in the response.
90    */
91   size_t msize;
92 };
93
94
95 /**
96  * A local client.
97  */
98 struct GSF_LocalClient
99 {
100
101   /**
102    * We keep clients in a DLL.
103    */
104   struct GSF_LocalClient *next;
105
106   /**
107    * We keep clients in a DLL.
108    */
109   struct GSF_LocalClient *prev;
110
111   /**
112    * ID of the client.
113    */
114   struct GNUNET_SERVER_Client *client;
115
116   /**
117    * Head of list of requests performed on behalf
118    * of this client right now.
119    */
120   struct ClientRequest *cr_head;
121
122   /**
123    * Tail of list of requests performed on behalf
124    * of this client right now.
125    */
126   struct ClientRequest *cr_tail;
127
128   /**
129    * Head of linked list of responses.
130    */
131   struct ClientResponse *res_head;
132
133   /**
134    * Tail of linked list of responses.
135    */
136   struct ClientResponse *res_tail;
137
138   /**
139    * Context for sending replies.
140    */
141   struct GNUNET_SERVER_TransmitHandle *th;
142
143 };
144
145
146 /**
147  * Head of linked list of our local clients.
148  */
149 static struct GSF_LocalClient *client_head;
150
151 /**
152  * Head of linked list of our local clients.
153  */
154 static struct GSF_LocalClient *client_tail;
155
156
157 /**
158  * Look up a local client record or create one if it
159  * doesn't exist yet.
160  *
161  * @param client handle of the client
162  * @return handle to local client entry
163  */
164 struct GSF_LocalClient *
165 GSF_local_client_lookup_ (struct GNUNET_SERVER_Client *client)
166 {
167   struct GSF_LocalClient *pos;
168
169   pos = client_head;
170   while ((NULL != pos) && (pos->client != client))
171     pos = pos->next;
172   if (NULL != pos)
173     return pos;
174   pos = GNUNET_new (struct GSF_LocalClient);
175   pos->client = client;
176   GNUNET_CONTAINER_DLL_insert (client_head,
177                                client_tail,
178                                pos);
179   return pos;
180 }
181
182
183 /**
184  * Free the given client request.
185  *
186  * @param cls the client request to free
187  * @param tc task context
188  */
189 static void
190 client_request_destroy (void *cls,
191                         const struct GNUNET_SCHEDULER_TaskContext *tc)
192 {
193   struct ClientRequest *cr = cls;
194   struct GSF_LocalClient *lc;
195
196   cr->kill_task = NULL;
197   lc = cr->lc;
198   GNUNET_CONTAINER_DLL_remove (lc->cr_head,
199                                lc->cr_tail,
200                                cr);
201   GSF_pending_request_cancel_ (cr->pr,
202                                GNUNET_YES);
203   GNUNET_STATISTICS_update (GSF_stats,
204                             gettext_noop ("# client searches active"),
205                             -1,
206                             GNUNET_NO);
207   GNUNET_free (cr);
208 }
209
210
211 /**
212  * Handle a reply to a pending request.  Also called if a request
213  * expires (then with data == NULL).  The handler may be called
214  * many times (depending on the request type), but will not be
215  * called during or after a call to #GSF_pending_request_cancel()
216  * and will also not be called anymore after a call signalling
217  * expiration.
218  *
219  * @param cls user-specified closure
220  * @param eval evaluation of the result
221  * @param pr handle to the original pending request
222  * @param reply_anonymity_level anonymity level for the reply, UINT32_MAX for "unknown"
223  * @param expiration when does @a data expire?
224  * @param last_transmission when was the last time we've tried to download this block? (FOREVER if unknown)
225  * @param type type of the block
226  * @param data response data, NULL on request expiration
227  * @param data_len number of bytes in @a data
228  */
229 static void
230 client_response_handler (void *cls,
231                          enum GNUNET_BLOCK_EvaluationResult eval,
232                          struct GSF_PendingRequest *pr,
233                          uint32_t reply_anonymity_level,
234                          struct GNUNET_TIME_Absolute expiration,
235                          struct GNUNET_TIME_Absolute last_transmission,
236                          enum GNUNET_BLOCK_Type type,
237                          const void *data,
238                          size_t data_len)
239 {
240   struct ClientRequest *cr = cls;
241   struct GSF_LocalClient *lc;
242   struct ClientPutMessage *pm;
243   const struct GSF_PendingRequestData *prd;
244   size_t msize;
245
246   if (NULL == data)
247   {
248     /* local-only request, with no result, clean up. */
249     if (NULL == cr->kill_task)
250       cr->kill_task = GNUNET_SCHEDULER_add_now (&client_request_destroy,
251                                                 cr);
252     return;
253   }
254   prd = GSF_pending_request_get_data_ (pr);
255   GNUNET_break (type != GNUNET_BLOCK_TYPE_ANY);
256   if ((prd->type != type) && (prd->type != GNUNET_BLOCK_TYPE_ANY))
257   {
258     GNUNET_break (0);
259     return;
260   }
261   GNUNET_STATISTICS_update (GSF_stats,
262                             gettext_noop
263                             ("# replies received for local clients"), 1,
264                             GNUNET_NO);
265   GNUNET_assert (pr == cr->pr);
266   lc = cr->lc;
267   msize = sizeof (struct ClientPutMessage) + data_len;
268   {
269     char buf[msize] GNUNET_ALIGN;
270
271     pm = (struct ClientPutMessage *) buf;
272     pm->header.type = htons (GNUNET_MESSAGE_TYPE_FS_PUT);
273     pm->header.size = htons (msize);
274     pm->type = htonl (type);
275     pm->expiration = GNUNET_TIME_absolute_hton (expiration);
276     pm->last_transmission = GNUNET_TIME_absolute_hton (last_transmission);
277     pm->num_transmissions = htonl (prd->num_transmissions);
278     pm->respect_offered = htonl (prd->respect_offered);
279     memcpy (&pm[1], data, data_len);
280     GSF_local_client_transmit_ (lc, &pm->header);
281   }
282   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
283               "Queued reply to query `%s' for local client\n",
284               GNUNET_h2s (&prd->query), (unsigned int) prd->type);
285   if (GNUNET_BLOCK_EVALUATION_OK_LAST != eval)
286   {
287     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
288                 "Evaluation %d - keeping query alive\n",
289                 (int) eval);
290     return;
291   }
292   if (NULL == cr->kill_task)
293     cr->kill_task = GNUNET_SCHEDULER_add_now (&client_request_destroy, cr);
294 }
295
296
297 /**
298  * Handle START_SEARCH-message (search request from local client).
299  * Only responsible for creating the request entry itself and setting
300  * up reply callback and cancellation on client disconnect.  Does NOT
301  * execute the actual request strategy (planning).
302  *
303  * @param client identification of the client
304  * @param message the actual message
305  * @param prptr where to store the pending request handle for the request
306  * @return #GNUNET_YES to start local processing,
307  *         #GNUNET_NO to not (yet) start local processing,
308  *         #GNUNET_SYSERR on error
309  */
310 int
311 GSF_local_client_start_search_handler_ (struct GNUNET_SERVER_Client *client,
312                                         const struct GNUNET_MessageHeader *message,
313                                         struct GSF_PendingRequest **prptr)
314 {
315   static struct GNUNET_PeerIdentity all_zeros;
316   const struct SearchMessage *sm;
317   struct GSF_LocalClient *lc;
318   struct ClientRequest *cr;
319   struct GSF_PendingRequestData *prd;
320   uint16_t msize;
321   unsigned int sc;
322   enum GNUNET_BLOCK_Type type;
323   enum GSF_PendingRequestOptions options;
324
325   msize = ntohs (message->size);
326   if ((msize < sizeof (struct SearchMessage)) ||
327       (0 != (msize - sizeof (struct SearchMessage)) % sizeof (struct GNUNET_HashCode)))
328   {
329     GNUNET_break (0);
330     *prptr = NULL;
331     return GNUNET_SYSERR;
332   }
333   GNUNET_STATISTICS_update (GSF_stats,
334                             gettext_noop ("# client searches received"),
335                             1,
336                             GNUNET_NO);
337   sc = (msize - sizeof (struct SearchMessage)) / sizeof (struct GNUNET_HashCode);
338   sm = (const struct SearchMessage *) message;
339   type = ntohl (sm->type);
340   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
341               "Received request for `%s' of type %u from local client\n",
342               GNUNET_h2s (&sm->query), (unsigned int) type);
343   lc = GSF_local_client_lookup_ (client);
344   cr = NULL;
345   /* detect duplicate UBLOCK requests */
346   if ((type == GNUNET_BLOCK_TYPE_FS_UBLOCK) ||
347       (type == GNUNET_BLOCK_TYPE_ANY))
348   {
349     cr = lc->cr_head;
350     while (NULL != cr)
351     {
352       prd = GSF_pending_request_get_data_ (cr->pr);
353       /* only unify with queries that hae not yet started local processing
354          (SEARCH_MESSAGE_OPTION_CONTINUED was always set) and that have a
355          matching query and type */
356       if ((GNUNET_YES != prd->has_started) &&
357           (0 != memcmp (&prd->query,
358                         &sm->query,
359                         sizeof (struct GNUNET_HashCode))) &&
360           (prd->type == type))
361         break;
362       cr = cr->next;
363     }
364   }
365   if (NULL != cr)
366   {
367     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
368                 "Have existing request, merging content-seen lists.\n");
369     GSF_pending_request_update_ (cr->pr,
370                                  (const struct GNUNET_HashCode *) &sm[1],
371                                  sc);
372     GNUNET_STATISTICS_update (GSF_stats,
373                               gettext_noop
374                               ("# client searches updated (merged content seen list)"),
375                               1, GNUNET_NO);
376   }
377   else
378   {
379     GNUNET_STATISTICS_update (GSF_stats,
380                               gettext_noop ("# client searches active"), 1,
381                               GNUNET_NO);
382     cr = GNUNET_new (struct ClientRequest);
383     cr->lc = lc;
384     GNUNET_CONTAINER_DLL_insert (lc->cr_head,
385                                  lc->cr_tail,
386                                  cr);
387     options = GSF_PRO_LOCAL_REQUEST;
388     if (0 != (SEARCH_MESSAGE_OPTION_LOOPBACK_ONLY & ntohl (sm->options)))
389       options |= GSF_PRO_LOCAL_ONLY;
390     cr->pr = GSF_pending_request_create_ (options, type,
391                                           &sm->query,
392                                           (0 !=
393                                            memcmp (&sm->target, &all_zeros,
394                                                    sizeof (struct GNUNET_PeerIdentity)))
395                                           ? &sm->target : NULL, NULL, 0,
396                                           0 /* bf */ ,
397                                           ntohl (sm->anonymity_level),
398                                           0 /* priority */ ,
399                                           0 /* ttl */ ,
400                                           0 /* sender PID */ ,
401                                           0 /* origin PID */ ,
402                                           (const struct GNUNET_HashCode *) &sm[1], sc,
403                                           &client_response_handler, cr);
404   }
405   *prptr = cr->pr;
406   return (0 !=
407           (SEARCH_MESSAGE_OPTION_CONTINUED & ntohl (sm->options))) ? GNUNET_NO :
408       GNUNET_YES;
409 }
410
411
412 /**
413  * Transmit the given message by copying it to the target buffer
414  * "buf".  "buf" will be NULL and "size" zero if the socket was closed
415  * for writing in the meantime.  In that case, do nothing
416  * (the disconnect or shutdown handler will take care of the rest).
417  * If we were able to transmit messages and there are still more
418  * pending, ask core again for further calls to this function.
419  *
420  * @param cls closure, pointer to the `struct GSF_LocalClient`
421  * @param size number of bytes available in @a buf
422  * @param buf where the callee should write the message
423  * @return number of bytes written to @a buf
424  */
425 static size_t
426 transmit_to_client (void *cls,
427                     size_t size,
428                     void *buf)
429 {
430   struct GSF_LocalClient *lc = cls;
431   char *cbuf = buf;
432   struct ClientResponse *res;
433   size_t msize;
434
435   lc->th = NULL;
436   if (NULL == buf)
437     return 0;
438   msize = 0;
439   while ((NULL != (res = lc->res_head)) && (res->msize <= size))
440   {
441     memcpy (&cbuf[msize], &res[1], res->msize);
442     msize += res->msize;
443     size -= res->msize;
444     GNUNET_CONTAINER_DLL_remove (lc->res_head, lc->res_tail, res);
445     GNUNET_free (res);
446   }
447   if (NULL != res)
448     lc->th =
449         GNUNET_SERVER_notify_transmit_ready (lc->client, res->msize,
450                                              GNUNET_TIME_UNIT_FOREVER_REL,
451                                              &transmit_to_client, lc);
452   return msize;
453 }
454
455
456 /**
457  * Transmit a message to the given local client as soon as possible.
458  * If the client disconnects before transmission, the message is
459  * simply discarded.
460  *
461  * @param lc recipient
462  * @param msg message to transmit to client
463  */
464 void
465 GSF_local_client_transmit_ (struct GSF_LocalClient *lc,
466                             const struct GNUNET_MessageHeader *msg)
467 {
468   struct ClientResponse *res;
469   size_t msize;
470
471   msize = ntohs (msg->size);
472   res = GNUNET_malloc (sizeof (struct ClientResponse) + msize);
473   res->lc = lc;
474   res->msize = msize;
475   memcpy (&res[1],
476           msg,
477           msize);
478   GNUNET_CONTAINER_DLL_insert_tail (lc->res_head,
479                                     lc->res_tail,
480                                     res);
481   if (NULL == lc->th)
482     lc->th =
483         GNUNET_SERVER_notify_transmit_ready (lc->client, msize,
484                                              GNUNET_TIME_UNIT_FOREVER_REL,
485                                              &transmit_to_client, lc);
486 }
487
488
489 /**
490  * A client disconnected from us.  Tear down the local client
491  * record.
492  *
493  * @param cls unused
494  * @param client handle of the client
495  */
496 void
497 GSF_client_disconnect_handler_ (void *cls,
498                                 struct GNUNET_SERVER_Client *client)
499 {
500   struct GSF_LocalClient *pos;
501   struct ClientRequest *cr;
502   struct ClientResponse *res;
503
504   pos = client_head;
505   while ((pos != NULL) && (pos->client != client))
506     pos = pos->next;
507   if (NULL == pos)
508     return;
509   while (NULL != (cr = pos->cr_head))
510   {
511     if (NULL != cr->kill_task)
512       GNUNET_SCHEDULER_cancel (cr->kill_task);
513     client_request_destroy (cr, NULL);
514   }
515   while (NULL != (res = pos->res_head))
516   {
517     GNUNET_CONTAINER_DLL_remove (pos->res_head, pos->res_tail, res);
518     GNUNET_free (res);
519   }
520   if (NULL != pos->th)
521   {
522     GNUNET_SERVER_notify_transmit_ready_cancel (pos->th);
523     pos->th = NULL;
524   }
525   GSF_handle_local_client_disconnect_ (pos);
526   GNUNET_CONTAINER_DLL_remove (client_head, client_tail, pos);
527   GNUNET_free (pos);
528 }
529
530
531 /* end of gnunet-service-fs_lc.c */