flush peer respect value on disconnect
[oweals/gnunet.git] / src / fs / gnunet-service-fs_lc.c
1 /*
2      This file is part of GNUnet.
3      (C) 2011 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 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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, 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   GNUNET_SCHEDULER_TaskIdentifier 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 = GNUNET_SCHEDULER_NO_TASK;
197   lc = cr->lc;
198   GNUNET_CONTAINER_DLL_remove (lc->cr_head, lc->cr_tail, cr);
199   GSF_pending_request_cancel_ (cr->pr, GNUNET_YES);
200   GNUNET_STATISTICS_update (GSF_stats,
201                             gettext_noop ("# client searches active"), -1,
202                             GNUNET_NO);
203   GNUNET_free (cr);
204 }
205
206
207 /**
208  * Handle a reply to a pending request.  Also called if a request
209  * expires (then with data == NULL).  The handler may be called
210  * many times (depending on the request type), but will not be
211  * called during or after a call to #GSF_pending_request_cancel()
212  * and will also not be called anymore after a call signalling
213  * expiration.
214  *
215  * @param cls user-specified closure
216  * @param eval evaluation of the result
217  * @param pr handle to the original pending request
218  * @param reply_anonymity_level anonymity level for the reply, UINT32_MAX for "unknown"
219  * @param expiration when does @a data expire?
220  * @param last_transmission when was the last time we've tried to download this block? (FOREVER if unknown)
221  * @param type type of the block
222  * @param data response data, NULL on request expiration
223  * @param data_len number of bytes in @a data
224  */
225 static void
226 client_response_handler (void *cls,
227                          enum GNUNET_BLOCK_EvaluationResult eval,
228                          struct GSF_PendingRequest *pr,
229                          uint32_t reply_anonymity_level,
230                          struct GNUNET_TIME_Absolute expiration,
231                          struct GNUNET_TIME_Absolute last_transmission,
232                          enum GNUNET_BLOCK_Type type,
233                          const void *data,
234                          size_t data_len)
235 {
236   struct ClientRequest *cr = cls;
237   struct GSF_LocalClient *lc;
238   struct ClientPutMessage *pm;
239   const struct GSF_PendingRequestData *prd;
240   size_t msize;
241
242   if (NULL == data)
243   {
244     /* local-only request, with no result, clean up. */
245     if (GNUNET_SCHEDULER_NO_TASK == cr->kill_task)
246       cr->kill_task = GNUNET_SCHEDULER_add_now (&client_request_destroy,
247                                                 cr);
248     return;
249   }
250   prd = GSF_pending_request_get_data_ (pr);
251   GNUNET_break (type != GNUNET_BLOCK_TYPE_ANY);
252   if ((prd->type != type) && (prd->type != GNUNET_BLOCK_TYPE_ANY))
253   {
254     GNUNET_break (0);
255     return;
256   }
257   GNUNET_STATISTICS_update (GSF_stats,
258                             gettext_noop
259                             ("# replies received for local clients"), 1,
260                             GNUNET_NO);
261   GNUNET_assert (pr == cr->pr);
262   lc = cr->lc;
263   msize = sizeof (struct ClientPutMessage) + data_len;
264   {
265     char buf[msize] GNUNET_ALIGN;
266
267     pm = (struct ClientPutMessage *) buf;
268     pm->header.type = htons (GNUNET_MESSAGE_TYPE_FS_PUT);
269     pm->header.size = htons (msize);
270     pm->type = htonl (type);
271     pm->expiration = GNUNET_TIME_absolute_hton (expiration);
272     pm->last_transmission = GNUNET_TIME_absolute_hton (last_transmission);
273     pm->num_transmissions = htonl (prd->num_transmissions);
274     pm->respect_offered = htonl (prd->respect_offered);
275     memcpy (&pm[1], data, data_len);
276     GSF_local_client_transmit_ (lc, &pm->header);
277   }
278   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
279               "Queued reply to query `%s' for local client\n",
280               GNUNET_h2s (&prd->query), (unsigned int) prd->type);
281   if (GNUNET_BLOCK_EVALUATION_OK_LAST != eval)
282   {
283     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
284                 "Evaluation %d - keeping query alive\n",
285                 (int) eval);
286     return;
287   }
288   if (GNUNET_SCHEDULER_NO_TASK == cr->kill_task)
289     cr->kill_task = GNUNET_SCHEDULER_add_now (&client_request_destroy, cr);
290 }
291
292
293 /**
294  * Handle START_SEARCH-message (search request from local client).
295  * Only responsible for creating the request entry itself and setting
296  * up reply callback and cancellation on client disconnect.  Does NOT
297  * execute the actual request strategy (planning).
298  *
299  * @param client identification of the client
300  * @param message the actual message
301  * @param prptr where to store the pending request handle for the request
302  * @return #GNUNET_YES to start local processing,
303  *         #GNUNET_NO to not (yet) start local processing,
304  *         #GNUNET_SYSERR on error
305  */
306 int
307 GSF_local_client_start_search_handler_ (struct GNUNET_SERVER_Client *client,
308                                         const struct GNUNET_MessageHeader *message,
309                                         struct GSF_PendingRequest **prptr)
310 {
311   static struct GNUNET_PeerIdentity all_zeros;
312   const struct SearchMessage *sm;
313   struct GSF_LocalClient *lc;
314   struct ClientRequest *cr;
315   struct GSF_PendingRequestData *prd;
316   uint16_t msize;
317   unsigned int sc;
318   enum GNUNET_BLOCK_Type type;
319   enum GSF_PendingRequestOptions options;
320
321   msize = ntohs (message->size);
322   if ((msize < sizeof (struct SearchMessage)) ||
323       (0 != (msize - sizeof (struct SearchMessage)) % sizeof (struct GNUNET_HashCode)))
324   {
325     GNUNET_break (0);
326     *prptr = NULL;
327     return GNUNET_SYSERR;
328   }
329   GNUNET_STATISTICS_update (GSF_stats,
330                             gettext_noop ("# client searches received"), 1,
331                             GNUNET_NO);
332   sc = (msize - sizeof (struct SearchMessage)) / sizeof (struct GNUNET_HashCode);
333   sm = (const struct SearchMessage *) message;
334   type = ntohl (sm->type);
335   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
336               "Received request for `%s' of type %u from local client\n",
337               GNUNET_h2s (&sm->query), (unsigned int) type);
338   lc = GSF_local_client_lookup_ (client);
339   cr = NULL;
340   /* detect duplicate UBLOCK requests */
341   if ((type == GNUNET_BLOCK_TYPE_FS_UBLOCK) ||
342       (type == GNUNET_BLOCK_TYPE_ANY))
343   {
344     cr = lc->cr_head;
345     while (NULL != cr)
346     {
347       prd = GSF_pending_request_get_data_ (cr->pr);
348       /* only unify with queries that hae not yet started local processing
349          (SEARCH_MESSAGE_OPTION_CONTINUED was always set) and that have a
350          matching query and type */
351       if ((GNUNET_YES != prd->has_started) &&
352           (0 != memcmp (&prd->query, &sm->query, sizeof (struct GNUNET_HashCode))) &&
353           (prd->type == type))
354         break;
355       cr = cr->next;
356     }
357   }
358   if (NULL != cr)
359   {
360     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
361                 "Have existing request, merging content-seen lists.\n");
362     GSF_pending_request_update_ (cr->pr, (const struct GNUNET_HashCode *) &sm[1], sc);
363     GNUNET_STATISTICS_update (GSF_stats,
364                               gettext_noop
365                               ("# client searches updated (merged content seen list)"),
366                               1, GNUNET_NO);
367   }
368   else
369   {
370     GNUNET_STATISTICS_update (GSF_stats,
371                               gettext_noop ("# client searches active"), 1,
372                               GNUNET_NO);
373     cr = GNUNET_new (struct ClientRequest);
374     cr->lc = lc;
375     GNUNET_CONTAINER_DLL_insert (lc->cr_head, lc->cr_tail, cr);
376     options = GSF_PRO_LOCAL_REQUEST;
377     if (0 != (SEARCH_MESSAGE_OPTION_LOOPBACK_ONLY & ntohl (sm->options)))
378       options |= GSF_PRO_LOCAL_ONLY;
379     cr->pr = GSF_pending_request_create_ (options, type,
380                                           &sm->query,
381                                           (0 !=
382                                            memcmp (&sm->target, &all_zeros,
383                                                    sizeof (struct GNUNET_PeerIdentity)))
384                                           ? &sm->target : NULL, NULL, 0,
385                                           0 /* bf */ ,
386                                           ntohl (sm->anonymity_level),
387                                           0 /* priority */ ,
388                                           0 /* ttl */ ,
389                                           0 /* sender PID */ ,
390                                           0 /* origin PID */ ,
391                                           (const struct GNUNET_HashCode *) &sm[1], sc,
392                                           &client_response_handler, cr);
393   }
394   *prptr = cr->pr;
395   return (0 !=
396           (SEARCH_MESSAGE_OPTION_CONTINUED & ntohl (sm->options))) ? GNUNET_NO :
397       GNUNET_YES;
398 }
399
400
401 /**
402  * Transmit the given message by copying it to the target buffer
403  * "buf".  "buf" will be NULL and "size" zero if the socket was closed
404  * for writing in the meantime.  In that case, do nothing
405  * (the disconnect or shutdown handler will take care of the rest).
406  * If we were able to transmit messages and there are still more
407  * pending, ask core again for further calls to this function.
408  *
409  * @param cls closure, pointer to the `struct GSF_LocalClient`
410  * @param size number of bytes available in @a buf
411  * @param buf where the callee should write the message
412  * @return number of bytes written to @a buf
413  */
414 static size_t
415 transmit_to_client (void *cls,
416                     size_t size,
417                     void *buf)
418 {
419   struct GSF_LocalClient *lc = cls;
420   char *cbuf = buf;
421   struct ClientResponse *res;
422   size_t msize;
423
424   lc->th = NULL;
425   if (NULL == buf)
426     return 0;
427   msize = 0;
428   while ((NULL != (res = lc->res_head)) && (res->msize <= size))
429   {
430     memcpy (&cbuf[msize], &res[1], res->msize);
431     msize += res->msize;
432     size -= res->msize;
433     GNUNET_CONTAINER_DLL_remove (lc->res_head, lc->res_tail, res);
434     GNUNET_free (res);
435   }
436   if (NULL != res)
437     lc->th =
438         GNUNET_SERVER_notify_transmit_ready (lc->client, res->msize,
439                                              GNUNET_TIME_UNIT_FOREVER_REL,
440                                              &transmit_to_client, lc);
441   return msize;
442 }
443
444
445 /**
446  * Transmit a message to the given local client as soon as possible.
447  * If the client disconnects before transmission, the message is
448  * simply discarded.
449  *
450  * @param lc recipient
451  * @param msg message to transmit to client
452  */
453 void
454 GSF_local_client_transmit_ (struct GSF_LocalClient *lc,
455                             const struct GNUNET_MessageHeader *msg)
456 {
457   struct ClientResponse *res;
458   size_t msize;
459
460   msize = ntohs (msg->size);
461   res = GNUNET_malloc (sizeof (struct ClientResponse) + msize);
462   res->lc = lc;
463   res->msize = msize;
464   memcpy (&res[1],
465           msg,
466           msize);
467   GNUNET_CONTAINER_DLL_insert_tail (lc->res_head,
468                                     lc->res_tail,
469                                     res);
470   if (NULL == lc->th)
471     lc->th =
472         GNUNET_SERVER_notify_transmit_ready (lc->client, msize,
473                                              GNUNET_TIME_UNIT_FOREVER_REL,
474                                              &transmit_to_client, lc);
475 }
476
477
478 /**
479  * A client disconnected from us.  Tear down the local client
480  * record.
481  *
482  * @param cls unused
483  * @param client handle of the client
484  */
485 void
486 GSF_client_disconnect_handler_ (void *cls,
487                                 struct GNUNET_SERVER_Client *client)
488 {
489   struct GSF_LocalClient *pos;
490   struct ClientRequest *cr;
491   struct ClientResponse *res;
492
493   pos = client_head;
494   while ((pos != NULL) && (pos->client != client))
495     pos = pos->next;
496   if (NULL == pos)
497     return;
498   while (NULL != (cr = pos->cr_head))
499   {
500     if (GNUNET_SCHEDULER_NO_TASK != cr->kill_task)
501       GNUNET_SCHEDULER_cancel (cr->kill_task);
502     client_request_destroy (cr, NULL);
503   }
504   while (NULL != (res = pos->res_head))
505   {
506     GNUNET_CONTAINER_DLL_remove (pos->res_head, pos->res_tail, res);
507     GNUNET_free (res);
508   }
509   if (NULL != pos->th)
510   {
511     GNUNET_SERVER_notify_transmit_ready_cancel (pos->th);
512     pos->th = NULL;
513   }
514   GSF_handle_local_client_disconnect_ (pos);
515   GNUNET_CONTAINER_DLL_remove (client_head, client_tail, pos);
516   GNUNET_free (pos);
517 }
518
519
520 /* end of gnunet-service-fs_lc.c */