Merge branch 'master' of gnunet.org:gnunet
[oweals/gnunet.git] / src / util / client.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2001-2016 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/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 #include "platform.h"
30 #include "gnunet_protocols.h"
31 #include "gnunet_util_lib.h"
32 #include "gnunet_resolver_service.h"
33 #include "gnunet_socks.h"
34
35
36 #define LOG(kind,...) GNUNET_log_from (kind, "util-client",__VA_ARGS__)
37
38 /**
39  * Timeout we use on TCP connect before trying another
40  * result from the DNS resolver.  Actual value used
41  * is this value divided by the number of address families.
42  * Default is 5s.
43  */
44 #define CONNECT_RETRY_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
45
46
47
48 /**
49  * Internal state for a client connected to a GNUnet service.
50  */
51 struct ClientState;
52
53
54 /**
55  * During connect, we try multiple possible IP addresses
56  * to find out which one might work.
57  */
58 struct AddressProbe
59 {
60
61   /**
62    * This is a linked list.
63    */
64   struct AddressProbe *next;
65
66   /**
67    * This is a doubly-linked list.
68    */
69   struct AddressProbe *prev;
70
71   /**
72    * The address; do not free (allocated at the end of this struct).
73    */
74   const struct sockaddr *addr;
75
76   /**
77    * Underlying OS's socket.
78    */
79   struct GNUNET_NETWORK_Handle *sock;
80
81   /**
82    * Connection for which we are probing.
83    */
84   struct ClientState *cstate;
85
86   /**
87    * Lenth of addr.
88    */
89   socklen_t addrlen;
90
91   /**
92    * Task waiting for the connection to finish connecting.
93    */
94   struct GNUNET_SCHEDULER_Task *task;
95 };
96
97
98 /**
99  * Internal state for a client connected to a GNUnet service.
100  */
101 struct ClientState
102 {
103
104   /**
105    * The connection handle, NULL if not live
106    */
107   struct GNUNET_NETWORK_Handle *sock;
108
109   /**
110    * Handle to a pending DNS lookup request, NULL if DNS is finished.
111    */
112   struct GNUNET_RESOLVER_RequestHandle *dns_active;
113
114   /**
115    * Our configuration.
116    */
117   const struct GNUNET_CONFIGURATION_Handle *cfg;
118
119   /**
120    * Linked list of sockets we are currently trying out
121    * (during connect).
122    */
123   struct AddressProbe *ap_head;
124
125   /**
126    * Linked list of sockets we are currently trying out
127    * (during connect).
128    */
129   struct AddressProbe *ap_tail;
130
131   /**
132    * Name of the service we interact with.
133    */
134   char *service_name;
135
136   /**
137    * Hostname, if any.
138    */
139   char *hostname;
140
141   /**
142    * Next message to transmit to the service. NULL for none.
143    */
144   const struct GNUNET_MessageHeader *msg;
145
146   /**
147    * Task for trying to connect to the service.
148    */
149   struct GNUNET_SCHEDULER_Task *retry_task;
150
151   /**
152    * Task for sending messages to the service.
153    */
154   struct GNUNET_SCHEDULER_Task *send_task;
155
156   /**
157    * Task for sending messages to the service.
158    */
159   struct GNUNET_SCHEDULER_Task *recv_task;
160
161   /**
162    * Tokenizer for inbound messages.
163    */
164   struct GNUNET_MessageStreamTokenizer *mst;
165
166   /**
167    * Message queue under our control.
168    */
169   struct GNUNET_MQ_Handle *mq;
170
171   /**
172    * Timeout for receiving a response (absolute time).
173    */
174   struct GNUNET_TIME_Absolute receive_timeout;
175
176   /**
177    * Current value for our incremental back-off (for
178    * connect re-tries).
179    */
180   struct GNUNET_TIME_Relative back_off;
181
182   /**
183    * TCP port (0 for disabled).
184    */
185   unsigned long long port;
186
187   /**
188    * Offset in the message where we are for transmission.
189    */
190   size_t msg_off;
191
192   /**
193    * How often have we tried to connect?
194    */
195   unsigned int attempts;
196
197   /**
198    * Are we supposed to die?  #GNUNET_SYSERR if destruction must be
199    * deferred, #GNUNET_NO by default, #GNUNET_YES if destruction was
200    * deferred.
201    */
202   int in_destroy;
203
204 };
205
206
207 /**
208  * Try to connect to the service.
209  *
210  * @param cls the `struct ClientState` to try to connect to the service
211  */
212 static void
213 start_connect (void *cls);
214
215
216 /**
217  * We've failed for good to establish a connection (timeout or
218  * no more addresses to try).
219  *
220  * @param cstate the connection we tried to establish
221  */
222 static void
223 connect_fail_continuation (struct ClientState *cstate)
224 {
225   GNUNET_break (NULL == cstate->ap_head);
226   GNUNET_break (NULL == cstate->ap_tail);
227   GNUNET_break (NULL == cstate->dns_active);
228   GNUNET_break (NULL == cstate->sock);
229   GNUNET_assert (NULL == cstate->send_task);
230   GNUNET_assert (NULL == cstate->recv_task);
231   // GNUNET_assert (NULL == cstate->proxy_handshake);
232
233   cstate->back_off = GNUNET_TIME_STD_BACKOFF (cstate->back_off);
234   LOG (GNUNET_ERROR_TYPE_DEBUG,
235        "Failed to establish connection to `%s', no further addresses to try, will try again in %s.\n",
236        cstate->service_name,
237        GNUNET_STRINGS_relative_time_to_string (cstate->back_off,
238                                                GNUNET_YES));
239   cstate->retry_task
240     = GNUNET_SCHEDULER_add_delayed (cstate->back_off,
241                                     &start_connect,
242                                     cstate);
243 }
244
245
246 /**
247  * We are ready to send a message to the service.
248  *
249  * @param cls the `struct ClientState` with the `msg` to transmit
250  */
251 static void
252 transmit_ready (void *cls)
253 {
254   struct ClientState *cstate = cls;
255   ssize_t ret;
256   size_t len;
257   const char *pos;
258   int notify_in_flight;
259
260   cstate->send_task = NULL;
261   pos = (const char *) cstate->msg;
262   len = ntohs (cstate->msg->size);
263   GNUNET_assert (cstate->msg_off < len);
264   LOG (GNUNET_ERROR_TYPE_DEBUG,
265        "message of type %u trying to send with socket %p (MQ: %p\n",
266        ntohs(cstate->msg->type),
267        cstate->sock,
268        cstate->mq);
269
270  RETRY:
271   ret = GNUNET_NETWORK_socket_send (cstate->sock,
272                                     &pos[cstate->msg_off],
273                                     len - cstate->msg_off);
274   if (-1 == ret)
275   {
276     LOG (GNUNET_ERROR_TYPE_WARNING,
277          "Error during sending message of type %u\n",
278          ntohs(cstate->msg->type));
279     if (EINTR == errno){
280       LOG (GNUNET_ERROR_TYPE_DEBUG,
281            "Retrying message of type %u\n",
282            ntohs(cstate->msg->type));
283       goto RETRY;
284     }
285     GNUNET_MQ_inject_error (cstate->mq,
286                             GNUNET_MQ_ERROR_WRITE);
287     return;
288   }
289   notify_in_flight = (0 == cstate->msg_off);
290   cstate->msg_off += ret;
291   if (cstate->msg_off < len)
292   {
293     LOG (GNUNET_ERROR_TYPE_DEBUG,
294          "rescheduling message of type %u\n",
295          ntohs(cstate->msg->type));
296     cstate->send_task
297       = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
298                                         cstate->sock,
299                                         &transmit_ready,
300                                         cstate);
301     if (notify_in_flight)
302       GNUNET_MQ_impl_send_in_flight (cstate->mq);
303     return;
304   }
305   LOG (GNUNET_ERROR_TYPE_DEBUG,
306        "sending message of type %u successful\n",
307        ntohs(cstate->msg->type));
308   cstate->msg = NULL;
309   GNUNET_MQ_impl_send_continue (cstate->mq);
310 }
311
312
313 /**
314  * We have received a full message, pass to the MQ dispatcher.
315  * Called by the tokenizer via #receive_ready().
316  *
317  * @param cls the `struct ClientState`
318  * @param msg message we received.
319  * @return #GNUNET_OK on success,
320  *     #GNUNET_NO to stop further processing due to disconnect (no error)
321  *     #GNUNET_SYSERR to stop further processing due to error
322  */
323 static int
324 recv_message (void *cls,
325               const struct GNUNET_MessageHeader *msg)
326 {
327   struct ClientState *cstate = cls;
328
329   if (GNUNET_YES == cstate->in_destroy)
330     return GNUNET_NO;
331   LOG (GNUNET_ERROR_TYPE_DEBUG,
332        "Received message of type %u and size %u from %s\n",
333        ntohs (msg->type),
334        ntohs (msg->size),
335        cstate->service_name);
336   GNUNET_MQ_inject_message (cstate->mq,
337                             msg);
338   if (GNUNET_YES == cstate->in_destroy)
339     return GNUNET_NO;
340   return GNUNET_OK;
341 }
342
343
344 /**
345  * Cancel all remaining connect attempts
346  *
347  * @param cstate handle of the client state to process
348  */
349 static void
350 cancel_aps (struct ClientState *cstate)
351 {
352   struct AddressProbe *pos;
353
354   while (NULL != (pos = cstate->ap_head))
355   {
356     GNUNET_break (GNUNET_OK ==
357                   GNUNET_NETWORK_socket_close (pos->sock));
358     GNUNET_SCHEDULER_cancel (pos->task);
359     GNUNET_CONTAINER_DLL_remove (cstate->ap_head,
360                                  cstate->ap_tail,
361                                  pos);
362     GNUNET_free (pos);
363   }
364 }
365
366
367 /**
368  * Implement the destruction of a message queue.  Implementations must
369  * not free @a mq, but should take care of @a impl_state.
370  *
371  * @param mq the message queue to destroy
372  * @param impl_state our `struct ClientState`
373  */
374 static void
375 connection_client_destroy_impl (struct GNUNET_MQ_Handle *mq,
376                                 void *impl_state)
377 {
378   struct ClientState *cstate = impl_state;
379
380   (void) mq;
381   if (GNUNET_SYSERR == cstate->in_destroy)
382   {
383     /* defer destruction */
384     cstate->in_destroy = GNUNET_YES;
385     cstate->mq = NULL;
386     return;
387   }
388   if (NULL != cstate->dns_active)
389     GNUNET_RESOLVER_request_cancel (cstate->dns_active);
390   if (NULL != cstate->send_task)
391     GNUNET_SCHEDULER_cancel (cstate->send_task);
392   if (NULL != cstate->recv_task)
393     GNUNET_SCHEDULER_cancel (cstate->recv_task);
394   if (NULL != cstate->retry_task)
395     GNUNET_SCHEDULER_cancel (cstate->retry_task);
396   if (NULL != cstate->sock){
397     LOG (GNUNET_ERROR_TYPE_DEBUG,
398          "destroying socket: %p\n",
399          cstate->sock);
400     GNUNET_NETWORK_socket_close (cstate->sock);
401   }
402   cancel_aps (cstate);
403   GNUNET_free (cstate->service_name);
404   GNUNET_free_non_null (cstate->hostname);
405   GNUNET_MST_destroy (cstate->mst);
406   GNUNET_free (cstate);
407 }
408
409
410 /**
411  * This function is called once we have data ready to read.
412  *
413  * @param cls `struct ClientState` with connection to read from
414  */
415 static void
416 receive_ready (void *cls)
417 {
418   struct ClientState *cstate = cls;
419   int ret;
420
421   cstate->recv_task = NULL;
422   cstate->in_destroy = GNUNET_SYSERR;
423   ret = GNUNET_MST_read (cstate->mst,
424                          cstate->sock,
425                          GNUNET_NO,
426                          GNUNET_NO);
427   if (GNUNET_SYSERR == ret)
428   {
429     if (NULL != cstate->mq)
430       GNUNET_MQ_inject_error (cstate->mq,
431                               GNUNET_MQ_ERROR_READ);
432     if (GNUNET_YES == cstate->in_destroy)
433       connection_client_destroy_impl (cstate->mq,
434                                       cstate);
435     return;
436   }
437   if (GNUNET_YES == cstate->in_destroy)
438   {
439     connection_client_destroy_impl (cstate->mq,
440                                     cstate);
441     return;
442   }
443   cstate->in_destroy = GNUNET_NO;
444   cstate->recv_task
445     = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
446                                      cstate->sock,
447                                      &receive_ready,
448                                      cstate);
449 }
450
451
452 /**
453  * We've succeeded in establishing a connection.
454  *
455  * @param cstate the connection we tried to establish
456  */
457 static void
458 connect_success_continuation (struct ClientState *cstate)
459 {
460   GNUNET_assert (NULL == cstate->recv_task);
461   cstate->recv_task
462     = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
463                                      cstate->sock,
464                                      &receive_ready,
465                                      cstate);
466   if (NULL != cstate->msg)
467   {
468     GNUNET_assert (NULL == cstate->send_task);
469     cstate->send_task
470       = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
471                                         cstate->sock,
472                                         &transmit_ready,
473                                         cstate);
474   }
475 }
476
477
478 /**
479  * Try connecting to the server using UNIX domain sockets.
480  *
481  * @param service_name name of service to connect to
482  * @param cfg configuration to use
483  * @return NULL on error, socket connected to UNIX otherwise
484  */
485 static struct GNUNET_NETWORK_Handle *
486 try_unixpath (const char *service_name,
487               const struct GNUNET_CONFIGURATION_Handle *cfg)
488 {
489 #if AF_UNIX
490   struct GNUNET_NETWORK_Handle *sock;
491   char *unixpath;
492   struct sockaddr_un s_un;
493
494   unixpath = NULL;
495   if ((GNUNET_OK ==
496        GNUNET_CONFIGURATION_get_value_filename (cfg,
497                                                 service_name,
498                                                 "UNIXPATH",
499                                                 &unixpath)) &&
500       (0 < strlen (unixpath)))
501   {
502     /* We have a non-NULL unixpath, need to validate it */
503     if (strlen (unixpath) >= sizeof (s_un.sun_path))
504     {
505       LOG (GNUNET_ERROR_TYPE_WARNING,
506            _("UNIXPATH `%s' too long, maximum length is %llu\n"),
507            unixpath,
508            (unsigned long long) sizeof (s_un.sun_path));
509       unixpath = GNUNET_NETWORK_shorten_unixpath (unixpath);
510       LOG (GNUNET_ERROR_TYPE_INFO,
511            _("Using `%s' instead\n"),
512            unixpath);
513       if (NULL == unixpath)
514         return NULL;
515     }
516     memset (&s_un,
517             0,
518             sizeof (s_un));
519     s_un.sun_family = AF_UNIX;
520     strncpy (s_un.sun_path,
521              unixpath,
522              sizeof (s_un.sun_path) - 1);
523 #ifdef LINUX
524     {
525       int abstract;
526
527       abstract = GNUNET_CONFIGURATION_get_value_yesno (cfg,
528                                                        "TESTING",
529                                                        "USE_ABSTRACT_SOCKETS");
530       if (GNUNET_YES == abstract)
531         s_un.sun_path[0] = '\0';
532     }
533 #endif
534 #if HAVE_SOCKADDR_UN_SUN_LEN
535     s_un.sun_len = (u_char) sizeof (struct sockaddr_un);
536 #endif
537     sock = GNUNET_NETWORK_socket_create (AF_UNIX,
538                                          SOCK_STREAM,
539                                          0);
540     if ( (NULL != sock) &&
541          ( (GNUNET_OK ==
542             GNUNET_NETWORK_socket_connect (sock,
543                                            (struct sockaddr *) &s_un,
544                                            sizeof (s_un))) ||
545            (EINPROGRESS == errno) ) )
546     {
547       LOG (GNUNET_ERROR_TYPE_DEBUG,
548            "Successfully connected to unixpath `%s'!\n",
549            unixpath);
550       GNUNET_free (unixpath);
551       return sock;
552     }
553     if (NULL != sock)
554       GNUNET_NETWORK_socket_close (sock);
555   }
556   GNUNET_free_non_null (unixpath);
557 #endif
558   return NULL;
559 }
560
561
562 /**
563  * Scheduler let us know that we're either ready to write on the
564  * socket OR connect timed out.  Do the right thing.
565  *
566  * @param cls the `struct AddressProbe *` with the address that we are probing
567  */
568 static void
569 connect_probe_continuation (void *cls)
570 {
571   struct AddressProbe *ap = cls;
572   struct ClientState *cstate = ap->cstate;
573   const struct GNUNET_SCHEDULER_TaskContext *tc;
574   int error;
575   socklen_t len;
576
577   ap->task = NULL;
578   GNUNET_assert (NULL != ap->sock);
579   GNUNET_CONTAINER_DLL_remove (cstate->ap_head,
580                                cstate->ap_tail,
581                                ap);
582   len = sizeof (error);
583   error = 0;
584   tc = GNUNET_SCHEDULER_get_task_context ();
585   if ( (0 == (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) ||
586        (GNUNET_OK !=
587         GNUNET_NETWORK_socket_getsockopt (ap->sock,
588                                           SOL_SOCKET,
589                                           SO_ERROR,
590                                           &error,
591                                           &len)) ||
592        (0 != error) )
593   {
594     GNUNET_break (GNUNET_OK ==
595                   GNUNET_NETWORK_socket_close (ap->sock));
596     GNUNET_free (ap);
597     if ( (NULL == cstate->ap_head) &&
598          //      (NULL == cstate->proxy_handshake) &&
599          (NULL == cstate->dns_active) )
600       connect_fail_continuation (cstate);
601     return;
602   }
603   LOG (GNUNET_ERROR_TYPE_DEBUG,
604        "Connection to `%s' succeeded!\n",
605        cstate->service_name);
606   /* trigger jobs that waited for the connection */
607   GNUNET_assert (NULL == cstate->sock);
608   cstate->sock = ap->sock;
609   GNUNET_free (ap);
610   cancel_aps (cstate);
611   connect_success_continuation (cstate);
612 }
613
614
615 /**
616  * Try to establish a connection given the specified address.
617  * This function is called by the resolver once we have a DNS reply.
618  *
619  * @param cls our `struct ClientState *`
620  * @param addr address to try, NULL for "last call"
621  * @param addrlen length of @a addr
622  */
623 static void
624 try_connect_using_address (void *cls,
625                            const struct sockaddr *addr,
626                            socklen_t addrlen)
627 {
628   struct ClientState *cstate = cls;
629   struct AddressProbe *ap;
630
631   if (NULL == addr)
632   {
633     cstate->dns_active = NULL;
634     if ( (NULL == cstate->ap_head) &&
635          //  (NULL == cstate->proxy_handshake) &&
636          (NULL == cstate->sock) )
637       connect_fail_continuation (cstate);
638     return;
639   }
640   if (NULL != cstate->sock)
641     return;                     /* already connected */
642   /* try to connect */
643   LOG (GNUNET_ERROR_TYPE_DEBUG,
644        "Trying to connect using address `%s:%u'\n",
645        GNUNET_a2s (addr,
646                    addrlen),
647        cstate->port);
648   ap = GNUNET_malloc (sizeof (struct AddressProbe) + addrlen);
649   ap->addr = (const struct sockaddr *) &ap[1];
650   GNUNET_memcpy (&ap[1],
651                  addr,
652                  addrlen);
653   ap->addrlen = addrlen;
654   ap->cstate = cstate;
655
656   switch (ap->addr->sa_family)
657   {
658   case AF_INET:
659     ((struct sockaddr_in *) ap->addr)->sin_port = htons (cstate->port);
660     break;
661   case AF_INET6:
662     ((struct sockaddr_in6 *) ap->addr)->sin6_port = htons (cstate->port);
663     break;
664   default:
665     GNUNET_break (0);
666     GNUNET_free (ap);
667     return;                     /* not supported by us */
668   }
669   ap->sock = GNUNET_NETWORK_socket_create (ap->addr->sa_family,
670                                            SOCK_STREAM,
671                                            0);
672   if (NULL == ap->sock)
673   {
674     GNUNET_free (ap);
675     return;                     /* not supported by OS */
676   }
677   if ( (GNUNET_OK !=
678         GNUNET_NETWORK_socket_connect (ap->sock,
679                                        ap->addr,
680                                        ap->addrlen)) &&
681        (EINPROGRESS != errno) )
682   {
683     /* maybe refused / unsupported address, try next */
684     GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO,
685                          "connect");
686     GNUNET_break (GNUNET_OK ==
687                   GNUNET_NETWORK_socket_close (ap->sock));
688     GNUNET_free (ap);
689     return;
690   }
691   GNUNET_CONTAINER_DLL_insert (cstate->ap_head,
692                                cstate->ap_tail,
693                                ap);
694   ap->task = GNUNET_SCHEDULER_add_write_net (CONNECT_RETRY_TIMEOUT,
695                                              ap->sock,
696                                              &connect_probe_continuation,
697                                              ap);
698 }
699
700
701 /**
702  * Test whether the configuration has proper values for connection
703  * (UNIXPATH || (PORT && HOSTNAME)).
704  *
705  * @param service_name name of service to connect to
706  * @param cfg configuration to use
707  * @return #GNUNET_OK if the configuration is valid, #GNUNET_SYSERR if not
708  */
709 static int
710 test_service_configuration (const char *service_name,
711                             const struct GNUNET_CONFIGURATION_Handle *cfg)
712 {
713   int ret = GNUNET_SYSERR;
714   char *hostname = NULL;
715   unsigned long long port;
716 #if AF_UNIX
717   char *unixpath = NULL;
718
719   if ((GNUNET_OK ==
720        GNUNET_CONFIGURATION_get_value_filename (cfg,
721                                                 service_name,
722                                                 "UNIXPATH",
723                                                 &unixpath)) &&
724       (0 < strlen (unixpath)))
725     ret = GNUNET_OK;
726   else if ((GNUNET_OK ==
727             GNUNET_CONFIGURATION_have_value (cfg,
728                                              service_name,
729                                              "UNIXPATH")))
730   {
731     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
732                                service_name,
733                                "UNIXPATH",
734                                _("not a valid filename"));
735     return GNUNET_SYSERR; /* UNIXPATH specified but invalid! */
736   }
737   GNUNET_free_non_null (unixpath);
738 #endif
739
740   if ( (GNUNET_YES ==
741         GNUNET_CONFIGURATION_have_value (cfg,
742                                          service_name,
743                                          "PORT")) &&
744        (GNUNET_OK ==
745         GNUNET_CONFIGURATION_get_value_number (cfg,
746                                                service_name,
747                                                "PORT",
748                                                &port)) &&
749        (port <= 65535) &&
750        (0 != port) &&
751        (GNUNET_OK ==
752         GNUNET_CONFIGURATION_get_value_string (cfg,
753                                                service_name,
754                                                "HOSTNAME",
755                                                &hostname)) &&
756        (0 != strlen (hostname)) )
757     ret = GNUNET_OK;
758   GNUNET_free_non_null (hostname);
759   return ret;
760 }
761
762
763 /**
764  * Try to connect to the service.
765  *
766  * @param cls the `struct ClientState` to try to connect to the service
767  */
768 static void
769 start_connect (void *cls)
770 {
771   struct ClientState *cstate = cls;
772
773   cstate->retry_task = NULL;
774 #if 0
775   /* Never use a local source if a proxy is configured */
776   if (GNUNET_YES ==
777       GNUNET_SOCKS_check_service (cstate->service_name,
778                                   cstate->cfg))
779   {
780     socks_connect (cstate);
781     return;
782   }
783 #endif
784
785   if ( (0 == (cstate->attempts++ % 2)) ||
786        (0 == cstate->port) ||
787        (NULL == cstate->hostname) )
788   {
789     /* on even rounds, try UNIX first, or always
790        if we do not have a DNS name and TCP port. */
791     cstate->sock = try_unixpath (cstate->service_name,
792                                  cstate->cfg);
793     if (NULL != cstate->sock)
794     {
795       connect_success_continuation (cstate);
796       return;
797     }
798   }
799   if ( (NULL == cstate->hostname) ||
800        (0 == cstate->port) )
801   {
802     /* All options failed. Boo! */
803     connect_fail_continuation (cstate);
804     return;
805   }
806   cstate->dns_active
807     = GNUNET_RESOLVER_ip_get (cstate->hostname,
808                               AF_UNSPEC,
809                               CONNECT_RETRY_TIMEOUT,
810                               &try_connect_using_address,
811                               cstate);
812 }
813
814
815 /**
816  * Implements the transmission functionality of a message queue.
817  *
818  * @param mq the message queue
819  * @param msg the message to send
820  * @param impl_state our `struct ClientState`
821  */
822 static void
823 connection_client_send_impl (struct GNUNET_MQ_Handle *mq,
824                              const struct GNUNET_MessageHeader *msg,
825                              void *impl_state)
826 {
827   struct ClientState *cstate = impl_state;
828
829   (void) mq;
830   /* only one message at a time allowed */
831   GNUNET_assert (NULL == cstate->msg);
832   GNUNET_assert (NULL == cstate->send_task);
833   cstate->msg = msg;
834   cstate->msg_off = 0;
835   if (NULL == cstate->sock){
836     LOG (GNUNET_ERROR_TYPE_DEBUG,
837          "message of type %u waiting for socket\n",
838          ntohs(msg->type));
839     return; /* still waiting for connection */
840    }
841   cstate->send_task
842     = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
843                                       cstate->sock,
844                                       &transmit_ready,
845                                       cstate);
846 }
847
848
849 /**
850  * Cancel the currently sent message.
851  *
852  * @param mq message queue
853  * @param impl_state our `struct ClientState`
854  */
855 static void
856 connection_client_cancel_impl (struct GNUNET_MQ_Handle *mq,
857                                void *impl_state)
858 {
859   struct ClientState *cstate = impl_state;
860
861   (void) mq;
862   GNUNET_assert (NULL != cstate->msg);
863   GNUNET_assert (0 == cstate->msg_off);
864   cstate->msg = NULL;
865   if (NULL != cstate->send_task)
866   {
867     GNUNET_SCHEDULER_cancel (cstate->send_task);
868     cstate->send_task = NULL;
869   }
870 }
871
872
873 /**
874  * Create a message queue to connect to a GNUnet service.
875  * If handlers are specfied, receive messages from the connection.
876  *
877  * @param cfg our configuration
878  * @param service_name name of the service to connect to
879  * @param handlers handlers for receiving messages, can be NULL
880  * @param error_handler error handler
881  * @param error_handler_cls closure for the @a error_handler
882  * @return the message queue, NULL on error
883  */
884 struct GNUNET_MQ_Handle *
885 GNUNET_CLIENT_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
886                        const char *service_name,
887                        const struct GNUNET_MQ_MessageHandler *handlers,
888                        GNUNET_MQ_ErrorHandler error_handler,
889                        void *error_handler_cls)
890 {
891   struct ClientState *cstate;
892
893   if (GNUNET_OK !=
894       test_service_configuration (service_name,
895                                   cfg))
896     return NULL;
897   cstate = GNUNET_new (struct ClientState);
898   cstate->service_name = GNUNET_strdup (service_name);
899   cstate->cfg = cfg;
900   cstate->retry_task = GNUNET_SCHEDULER_add_now (&start_connect,
901                                                  cstate);
902   cstate->mst = GNUNET_MST_create (&recv_message,
903                                    cstate);
904   if (GNUNET_YES ==
905       GNUNET_CONFIGURATION_have_value (cfg,
906                                        service_name,
907                                        "PORT"))
908   {
909     if (! ( (GNUNET_OK !=
910              GNUNET_CONFIGURATION_get_value_number (cfg,
911                                                     service_name,
912                                                     "PORT",
913                                                     &cstate->port)) ||
914             (cstate->port > 65535) ||
915             (GNUNET_OK !=
916              GNUNET_CONFIGURATION_get_value_string (cfg,
917                                                     service_name,
918                                                     "HOSTNAME",
919                                                     &cstate->hostname)) ) &&
920         (0 == strlen (cstate->hostname)) )
921     {
922       GNUNET_free (cstate->hostname);
923       cstate->hostname = NULL;
924       LOG (GNUNET_ERROR_TYPE_WARNING,
925            _("Need a non-empty hostname for service `%s'.\n"),
926            service_name);
927     }
928   }
929   cstate->mq = GNUNET_MQ_queue_for_callbacks (&connection_client_send_impl,
930                                               &connection_client_destroy_impl,
931                                               &connection_client_cancel_impl,
932                                               cstate,
933                                               handlers,
934                                               error_handler,
935                                               error_handler_cls);
936   return cstate->mq;
937 }
938
939 /* end of client.c */