diff options
Diffstat (limited to 'src/util/client.c')
-rw-r--r-- | src/util/client.c | 526 |
1 files changed, 526 insertions, 0 deletions
diff --git a/src/util/client.c b/src/util/client.c new file mode 100644 index 000000000..9dd70f266 --- /dev/null +++ b/src/util/client.c | |||
@@ -0,0 +1,526 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | (C) 2001, 2002, 2006, 2008, 2009 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/client.c | ||
23 | * @brief code for access to services | ||
24 | * @author Christian Grothoff | ||
25 | * | ||
26 | * Generic TCP code for reliable, record-oriented TCP | ||
27 | * connections between clients and service providers. | ||
28 | */ | ||
29 | |||
30 | #include "platform.h" | ||
31 | #include "gnunet_common.h" | ||
32 | #include "gnunet_client_lib.h" | ||
33 | #include "gnunet_protocols.h" | ||
34 | #include "gnunet_server_lib.h" | ||
35 | #include "gnunet_scheduler_lib.h" | ||
36 | |||
37 | #define DEBUG_CLIENT GNUNET_NO | ||
38 | |||
39 | /** | ||
40 | * Struct to refer to a GNUnet TCP connection. | ||
41 | * This is more than just a socket because if the server | ||
42 | * drops the connection, the client automatically tries | ||
43 | * to reconnect (and for that needs connection information). | ||
44 | */ | ||
45 | struct GNUNET_CLIENT_Connection | ||
46 | { | ||
47 | |||
48 | /** | ||
49 | * the socket handle, NULL if not live | ||
50 | */ | ||
51 | struct GNUNET_NETWORK_SocketHandle *sock; | ||
52 | |||
53 | /** | ||
54 | * Our scheduler. | ||
55 | */ | ||
56 | struct GNUNET_SCHEDULER_Handle *sched; | ||
57 | |||
58 | /** | ||
59 | * Name of the service we interact with. | ||
60 | */ | ||
61 | char *service_name; | ||
62 | |||
63 | /** | ||
64 | * ID of task used for receiving. | ||
65 | */ | ||
66 | GNUNET_SCHEDULER_TaskIdentifier receiver_task; | ||
67 | |||
68 | /** | ||
69 | * Handler for current receiver task. | ||
70 | */ | ||
71 | GNUNET_CLIENT_MessageHandler receiver_handler; | ||
72 | |||
73 | /** | ||
74 | * Closure for receiver_handler. | ||
75 | */ | ||
76 | void *receiver_handler_cls; | ||
77 | |||
78 | /** | ||
79 | * Handler for service test completion (NULL unless in service_test) | ||
80 | */ | ||
81 | GNUNET_SCHEDULER_Task test_cb; | ||
82 | |||
83 | /** | ||
84 | * Closure for test_cb (NULL unless in service_test) | ||
85 | */ | ||
86 | void *test_cb_cls; | ||
87 | |||
88 | /** | ||
89 | * Buffer for received message. | ||
90 | */ | ||
91 | char *received_buf; | ||
92 | |||
93 | /** | ||
94 | * Timeout for receiving a response (absolute time). | ||
95 | */ | ||
96 | struct GNUNET_TIME_Absolute receive_timeout; | ||
97 | |||
98 | /** | ||
99 | * Number of bytes in received_buf that are valid. | ||
100 | */ | ||
101 | size_t received_pos; | ||
102 | |||
103 | /** | ||
104 | * Size of received_buf. | ||
105 | */ | ||
106 | size_t received_size; | ||
107 | |||
108 | /** | ||
109 | * Do we have a complete response in received_buf? | ||
110 | */ | ||
111 | int msg_complete; | ||
112 | |||
113 | }; | ||
114 | |||
115 | |||
116 | /** | ||
117 | * Get a connection with a service. | ||
118 | * | ||
119 | * @param sched scheduler to use | ||
120 | * @param service_name name of the service | ||
121 | * @param cfg configuration to use | ||
122 | * @return NULL on error (service unknown to configuration) | ||
123 | */ | ||
124 | struct GNUNET_CLIENT_Connection * | ||
125 | GNUNET_CLIENT_connect (struct GNUNET_SCHEDULER_Handle *sched, | ||
126 | const char *service_name, | ||
127 | struct GNUNET_CONFIGURATION_Handle *cfg) | ||
128 | { | ||
129 | struct GNUNET_CLIENT_Connection *ret; | ||
130 | struct GNUNET_NETWORK_SocketHandle *sock; | ||
131 | char *hostname; | ||
132 | unsigned long long port; | ||
133 | |||
134 | if ((GNUNET_OK != | ||
135 | GNUNET_CONFIGURATION_get_value_number (cfg, | ||
136 | service_name, | ||
137 | "PORT", | ||
138 | &port)) || | ||
139 | (port > 65535) || | ||
140 | (GNUNET_OK != | ||
141 | GNUNET_CONFIGURATION_get_value_string (cfg, | ||
142 | service_name, | ||
143 | "HOSTNAME", &hostname))) | ||
144 | { | ||
145 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
146 | "Could not determine valid hostname and port for service `%s' from configuration.\n", | ||
147 | service_name); | ||
148 | return NULL; | ||
149 | } | ||
150 | sock = GNUNET_NETWORK_socket_create_from_connect (sched, | ||
151 | hostname, | ||
152 | port, | ||
153 | GNUNET_SERVER_MAX_MESSAGE_SIZE); | ||
154 | GNUNET_free (hostname); | ||
155 | if (sock == NULL) | ||
156 | return NULL; | ||
157 | ret = GNUNET_malloc (sizeof (struct GNUNET_CLIENT_Connection)); | ||
158 | ret->sock = sock; | ||
159 | ret->sched = sched; | ||
160 | ret->service_name = GNUNET_strdup (service_name); | ||
161 | return ret; | ||
162 | } | ||
163 | |||
164 | |||
165 | /** | ||
166 | * Receiver task has completed, free rest of client | ||
167 | * data structures. | ||
168 | */ | ||
169 | static void | ||
170 | finish_cleanup (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
171 | { | ||
172 | struct GNUNET_CLIENT_Connection *sock = cls; | ||
173 | |||
174 | GNUNET_array_grow (sock->received_buf, sock->received_size, 0); | ||
175 | GNUNET_free (sock->service_name); | ||
176 | GNUNET_free (sock); | ||
177 | } | ||
178 | |||
179 | |||
180 | /** | ||
181 | * Destroy connection with the service. | ||
182 | */ | ||
183 | void | ||
184 | GNUNET_CLIENT_disconnect (struct GNUNET_CLIENT_Connection *sock) | ||
185 | { | ||
186 | GNUNET_assert (sock->sock != NULL); | ||
187 | GNUNET_NETWORK_socket_destroy (sock->sock); | ||
188 | sock->sock = NULL; | ||
189 | sock->receiver_handler = NULL; | ||
190 | GNUNET_SCHEDULER_add_after (sock->sched, | ||
191 | GNUNET_YES, | ||
192 | GNUNET_SCHEDULER_PRIORITY_KEEP, | ||
193 | sock->receiver_task, &finish_cleanup, sock); | ||
194 | } | ||
195 | |||
196 | |||
197 | /** | ||
198 | * check if message is complete | ||
199 | */ | ||
200 | static void | ||
201 | check_complete (struct GNUNET_CLIENT_Connection *conn) | ||
202 | { | ||
203 | if ((conn->received_pos >= sizeof (struct GNUNET_MessageHeader)) && | ||
204 | (conn->received_pos >= | ||
205 | ntohs (((const struct GNUNET_MessageHeader *) conn->received_buf)-> | ||
206 | size))) | ||
207 | conn->msg_complete = GNUNET_YES; | ||
208 | } | ||
209 | |||
210 | |||
211 | /** | ||
212 | * Callback function for data received from the network. Note that | ||
213 | * both "available" and "err" would be 0 if the read simply timed out. | ||
214 | * | ||
215 | * @param cls closure | ||
216 | * @param buf pointer to received data | ||
217 | * @param available number of bytes availabe in "buf", | ||
218 | * possibly 0 (on errors) | ||
219 | * @param addr address of the sender | ||
220 | * @param addrlen size of addr | ||
221 | * @param errCode value of errno (on errors receiving) | ||
222 | */ | ||
223 | static void | ||
224 | receive_helper (void *cls, | ||
225 | const void *buf, | ||
226 | size_t available, | ||
227 | const struct sockaddr *addr, socklen_t addrlen, int errCode) | ||
228 | { | ||
229 | struct GNUNET_CLIENT_Connection *conn = cls; | ||
230 | struct GNUNET_TIME_Relative remaining; | ||
231 | |||
232 | GNUNET_assert (conn->msg_complete == GNUNET_NO); | ||
233 | conn->receiver_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; | ||
234 | |||
235 | if ((available == 0) || (conn->sock == NULL) || (errCode != 0)) | ||
236 | { | ||
237 | /* signal timeout! */ | ||
238 | if (conn->receiver_handler != NULL) | ||
239 | { | ||
240 | conn->receiver_handler (conn->receiver_handler_cls, NULL); | ||
241 | conn->receiver_handler = NULL; | ||
242 | } | ||
243 | return; | ||
244 | } | ||
245 | |||
246 | /* FIXME: optimize for common fast case where buf contains the | ||
247 | entire message and we need no copying... */ | ||
248 | |||
249 | |||
250 | /* slow path: append to array */ | ||
251 | if (conn->received_size < conn->received_pos + available) | ||
252 | GNUNET_array_grow (conn->received_buf, | ||
253 | conn->received_size, conn->received_pos + available); | ||
254 | memcpy (&conn->received_buf[conn->received_pos], buf, available); | ||
255 | conn->received_pos += available; | ||
256 | check_complete (conn); | ||
257 | /* check for timeout */ | ||
258 | remaining = GNUNET_TIME_absolute_get_remaining (conn->receive_timeout); | ||
259 | if (remaining.value == 0) | ||
260 | { | ||
261 | /* signal timeout! */ | ||
262 | conn->receiver_handler (conn->receiver_handler_cls, NULL); | ||
263 | return; | ||
264 | } | ||
265 | /* back to receive -- either for more data or to call callback! */ | ||
266 | GNUNET_CLIENT_receive (conn, | ||
267 | conn->receiver_handler, | ||
268 | conn->receiver_handler_cls, remaining); | ||
269 | } | ||
270 | |||
271 | |||
272 | /** | ||
273 | * Continuation to call the receive callback. | ||
274 | */ | ||
275 | static void | ||
276 | receive_task (void *scls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
277 | { | ||
278 | struct GNUNET_CLIENT_Connection *sock = scls; | ||
279 | const struct GNUNET_MessageHeader *cmsg; | ||
280 | struct GNUNET_MessageHeader *msg; | ||
281 | GNUNET_CLIENT_MessageHandler handler = sock->receiver_handler; | ||
282 | void *cls = sock->receiver_handler_cls; | ||
283 | uint16_t msize; | ||
284 | |||
285 | GNUNET_assert (GNUNET_YES == sock->msg_complete); | ||
286 | sock->receiver_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; | ||
287 | cmsg = (const struct GNUNET_MessageHeader *) sock->received_buf; | ||
288 | msize = ntohs (cmsg->size); | ||
289 | GNUNET_assert (sock->received_pos >= msize); | ||
290 | msg = GNUNET_malloc (msize); | ||
291 | memcpy (msg, cmsg, msize); | ||
292 | memmove (sock->received_buf, | ||
293 | &sock->received_buf[msize], sock->received_pos - msize); | ||
294 | sock->received_pos -= msize; | ||
295 | sock->msg_complete = GNUNET_NO; | ||
296 | sock->receiver_handler = NULL; | ||
297 | check_complete (sock); | ||
298 | handler (cls, msg); | ||
299 | GNUNET_free (msg); | ||
300 | } | ||
301 | |||
302 | |||
303 | /** | ||
304 | * Read from the service. | ||
305 | * | ||
306 | * @param sched scheduler to use | ||
307 | * @param sock the service | ||
308 | * @param handler function to call with the message | ||
309 | * @param cls closure for handler | ||
310 | * @param timeout how long to wait until timing out | ||
311 | */ | ||
312 | void | ||
313 | GNUNET_CLIENT_receive (struct GNUNET_CLIENT_Connection *sock, | ||
314 | GNUNET_CLIENT_MessageHandler handler, | ||
315 | void *cls, struct GNUNET_TIME_Relative timeout) | ||
316 | { | ||
317 | if (sock->sock == NULL) | ||
318 | { | ||
319 | /* already disconnected, fail instantly! */ | ||
320 | GNUNET_break (0); /* this should not happen in well-written code! */ | ||
321 | handler (cls, NULL); | ||
322 | return; | ||
323 | } | ||
324 | GNUNET_assert (sock->receiver_task == | ||
325 | GNUNET_SCHEDULER_NO_PREREQUISITE_TASK); | ||
326 | sock->receiver_handler = handler; | ||
327 | sock->receiver_handler_cls = cls; | ||
328 | sock->receive_timeout = GNUNET_TIME_relative_to_absolute (timeout); | ||
329 | if (GNUNET_YES == sock->msg_complete) | ||
330 | sock->receiver_task = GNUNET_SCHEDULER_add_after (sock->sched, | ||
331 | GNUNET_YES, | ||
332 | GNUNET_SCHEDULER_PRIORITY_KEEP, | ||
333 | GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, | ||
334 | &receive_task, sock); | ||
335 | else | ||
336 | sock->receiver_task = GNUNET_NETWORK_receive (sock->sock, | ||
337 | GNUNET_SERVER_MAX_MESSAGE_SIZE, | ||
338 | timeout, | ||
339 | &receive_helper, sock); | ||
340 | } | ||
341 | |||
342 | |||
343 | static size_t | ||
344 | write_shutdown (void *cls, size_t size, void *buf) | ||
345 | { | ||
346 | struct GNUNET_MessageHeader *msg; | ||
347 | |||
348 | if (size < sizeof (struct GNUNET_MessageHeader)) | ||
349 | return 0; /* client disconnected */ | ||
350 | msg = (struct GNUNET_MessageHeader *) buf; | ||
351 | msg->type = htons (GNUNET_MESSAGE_TYPE_SHUTDOWN); | ||
352 | msg->size = htons (sizeof (struct GNUNET_MessageHeader)); | ||
353 | return sizeof (struct GNUNET_MessageHeader); | ||
354 | } | ||
355 | |||
356 | |||
357 | /** | ||
358 | * Request that the service should shutdown. | ||
359 | * Afterwards, the connection should be disconnected. | ||
360 | * | ||
361 | * @param sched scheduler to use | ||
362 | * @param sock the socket connected to the service | ||
363 | */ | ||
364 | void | ||
365 | GNUNET_CLIENT_service_shutdown (struct GNUNET_CLIENT_Connection *sock) | ||
366 | { | ||
367 | GNUNET_NETWORK_notify_transmit_ready (sock->sock, | ||
368 | sizeof (struct GNUNET_MessageHeader), | ||
369 | GNUNET_TIME_UNIT_FOREVER_REL, | ||
370 | &write_shutdown, NULL); | ||
371 | } | ||
372 | |||
373 | |||
374 | /** | ||
375 | * Report service unavailable. | ||
376 | */ | ||
377 | static void | ||
378 | service_test_error (struct GNUNET_SCHEDULER_Handle *s, | ||
379 | GNUNET_SCHEDULER_Task task, void *task_cls) | ||
380 | { | ||
381 | GNUNET_SCHEDULER_add_continuation (s, | ||
382 | GNUNET_YES, | ||
383 | task, | ||
384 | task_cls, | ||
385 | GNUNET_SCHEDULER_REASON_TIMEOUT); | ||
386 | } | ||
387 | |||
388 | |||
389 | /** | ||
390 | * Receive confirmation from test, service is up. | ||
391 | * | ||
392 | * @param cls closure | ||
393 | * @param msg message received, NULL on timeout or fatal error | ||
394 | */ | ||
395 | static void | ||
396 | confirm_handler (void *cls, const struct GNUNET_MessageHeader *msg) | ||
397 | { | ||
398 | struct GNUNET_CLIENT_Connection *conn = cls; | ||
399 | /* We may want to consider looking at the reply in more | ||
400 | detail in the future, for example, is this the | ||
401 | correct service? FIXME! */ | ||
402 | if (msg != NULL) | ||
403 | { | ||
404 | #if DEBUG_CLIENT | ||
405 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
406 | "Received confirmation that service is running.\n"); | ||
407 | #endif | ||
408 | GNUNET_SCHEDULER_add_continuation (conn->sched, | ||
409 | GNUNET_YES, | ||
410 | conn->test_cb, | ||
411 | conn->test_cb_cls, | ||
412 | GNUNET_SCHEDULER_REASON_PREREQ_DONE); | ||
413 | } | ||
414 | else | ||
415 | { | ||
416 | service_test_error (conn->sched, conn->test_cb, conn->test_cb_cls); | ||
417 | } | ||
418 | GNUNET_CLIENT_disconnect (conn); | ||
419 | } | ||
420 | |||
421 | |||
422 | static size_t | ||
423 | write_test (void *cls, size_t size, void *buf) | ||
424 | { | ||
425 | struct GNUNET_MessageHeader *msg; | ||
426 | |||
427 | if (size < sizeof (struct GNUNET_MessageHeader)) | ||
428 | { | ||
429 | #if DEBUG_CLIENT | ||
430 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
431 | _("Failure to transmit TEST request.\n")); | ||
432 | #endif | ||
433 | return 0; /* client disconnected */ | ||
434 | } | ||
435 | #if DEBUG_CLIENT | ||
436 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Transmitting TEST request.\n")); | ||
437 | #endif | ||
438 | msg = (struct GNUNET_MessageHeader *) buf; | ||
439 | msg->type = htons (GNUNET_MESSAGE_TYPE_TEST); | ||
440 | msg->size = htons (sizeof (struct GNUNET_MessageHeader)); | ||
441 | return sizeof (struct GNUNET_MessageHeader); | ||
442 | } | ||
443 | |||
444 | |||
445 | /** | ||
446 | * Wait until the service is running. | ||
447 | * | ||
448 | * @param sched scheduler to use | ||
449 | * @param service name of the service to wait for | ||
450 | * @param cfg configuration to use | ||
451 | * @param timeout how long to wait at most in ms | ||
452 | * @param task task to run if service is running | ||
453 | * (reason will be "PREREQ_DONE" (service running) | ||
454 | * or "TIMEOUT" (service not known to be running)) | ||
455 | * @param task_cls closure for task | ||
456 | */ | ||
457 | void | ||
458 | GNUNET_CLIENT_service_test (struct GNUNET_SCHEDULER_Handle *sched, | ||
459 | const char *service, | ||
460 | struct GNUNET_CONFIGURATION_Handle *cfg, | ||
461 | struct GNUNET_TIME_Relative timeout, | ||
462 | GNUNET_SCHEDULER_Task task, void *task_cls) | ||
463 | { | ||
464 | struct GNUNET_CLIENT_Connection *conn; | ||
465 | |||
466 | #if DEBUG_CLIENT | ||
467 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
468 | "Testing if service `%s' is running.\n", service); | ||
469 | #endif | ||
470 | conn = GNUNET_CLIENT_connect (sched, service, cfg); | ||
471 | if (conn == NULL) | ||
472 | { | ||
473 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
474 | _ | ||
475 | ("Could not connect to service `%s', must not be running.\n"), | ||
476 | service); | ||
477 | service_test_error (sched, task, task_cls); | ||
478 | return; | ||
479 | } | ||
480 | conn->test_cb = task; | ||
481 | conn->test_cb_cls = task_cls; | ||
482 | if (NULL == | ||
483 | GNUNET_NETWORK_notify_transmit_ready (conn->sock, | ||
484 | sizeof (struct | ||
485 | GNUNET_MessageHeader), | ||
486 | timeout, &write_test, NULL)) | ||
487 | { | ||
488 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
489 | _("Failure to transmit request to service `%s'\n"), | ||
490 | service); | ||
491 | service_test_error (sched, task, task_cls); | ||
492 | GNUNET_CLIENT_disconnect (conn); | ||
493 | return; | ||
494 | } | ||
495 | GNUNET_CLIENT_receive (conn, &confirm_handler, conn, timeout); | ||
496 | } | ||
497 | |||
498 | |||
499 | /** | ||
500 | * Ask the client to call us once the specified number of bytes | ||
501 | * are free in the transmission buffer. May call the notify | ||
502 | * method immediately if enough space is available. | ||
503 | * | ||
504 | * @param client connection to the service | ||
505 | * @param size number of bytes to send | ||
506 | * @param timeout after how long should we give up (and call | ||
507 | * notify with buf NULL and size 0)? | ||
508 | * @param notify function to call | ||
509 | * @param notify_cls closure for notify | ||
510 | * @return NULL if our buffer will never hold size bytes, | ||
511 | * a handle if the notify callback was queued (can be used to cancel) | ||
512 | */ | ||
513 | struct GNUNET_NETWORK_TransmitHandle * | ||
514 | GNUNET_CLIENT_notify_transmit_ready (struct GNUNET_CLIENT_Connection *sock, | ||
515 | size_t size, | ||
516 | struct GNUNET_TIME_Relative timeout, | ||
517 | GNUNET_NETWORK_TransmitReadyNotify | ||
518 | notify, void *notify_cls) | ||
519 | { | ||
520 | return GNUNET_NETWORK_notify_transmit_ready (sock->sock, | ||
521 | size, | ||
522 | timeout, notify, notify_cls); | ||
523 | } | ||
524 | |||
525 | |||
526 | /* end of client.c */ | ||