Merge branch 'master' of gnunet.org:gnunet
[oweals/gnunet.git] / src / transport / transport_api_manipulation.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009-2013, 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 transport/transport_api_manipulation.c
23  * @brief library to access the low-level P2P IO service
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_constants.h"
29 #include "gnunet_arm_service.h"
30 #include "gnunet_hello_lib.h"
31 #include "gnunet_protocols.h"
32 #include "gnunet_transport_service.h"
33 #include "transport.h"
34
35 #define LOG(kind, ...) GNUNET_log_from (kind, "transport-api", __VA_ARGS__)
36
37
38 /**
39  * Handle for the transport service (includes all of the
40  * state for the transport service).
41  */
42 struct GNUNET_TRANSPORT_ManipulationHandle
43 {
44   /**
45    * My client connection to the transport service.
46    */
47   struct GNUNET_MQ_Handle *mq;
48
49   /**
50    * My configuration.
51    */
52   const struct GNUNET_CONFIGURATION_Handle *cfg;
53
54   /**
55    * ID of the task trying to reconnect to the service.
56    */
57   struct GNUNET_SCHEDULER_Task *reconnect_task;
58
59   /**
60    * Delay until we try to reconnect.
61    */
62   struct GNUNET_TIME_Relative reconnect_delay;
63
64   /**
65    * Reconnect in progress
66    */
67   int reconnecting;
68 };
69
70
71 /**
72  * Function that will schedule the job that will try
73  * to connect us again to the client.
74  *
75  * @param h transport service to reconnect
76  */
77 static void
78 disconnect_and_schedule_reconnect (struct
79                                    GNUNET_TRANSPORT_ManipulationHandle *h);
80
81
82 /**
83  * Generic error handler, called with the appropriate
84  * error code and the same closure specified at the creation of
85  * the message queue.
86  * Not every message queue implementation supports an error handler.
87  *
88  * @param cls closure with the `struct GNUNET_TRANSPORT_ManipulationHandle *`
89  * @param error error code
90  */
91 static void
92 mq_error_handler (void *cls,
93                   enum GNUNET_MQ_Error error)
94 {
95   struct GNUNET_TRANSPORT_ManipulationHandle *h = cls;
96
97   LOG (GNUNET_ERROR_TYPE_DEBUG,
98        "Error receiving from transport service, disconnecting temporarily.\n");
99   h->reconnecting = GNUNET_YES;
100   disconnect_and_schedule_reconnect (h);
101 }
102
103
104 /**
105  * Try again to connect to transport service.
106  *
107  * @param cls the handle to the transport service
108  */
109 static void
110 reconnect (void *cls)
111 {
112   struct GNUNET_TRANSPORT_ManipulationHandle *h = cls;
113   struct GNUNET_MQ_MessageHandler handlers[] = {
114     GNUNET_MQ_handler_end ()
115   };
116   struct GNUNET_MQ_Envelope *env;
117   struct StartMessage *s;
118
119   h->reconnect_task = NULL;
120   LOG (GNUNET_ERROR_TYPE_DEBUG,
121        "Connecting to transport service.\n");
122   GNUNET_assert (NULL == h->mq);
123   h->reconnecting = GNUNET_NO;
124   h->mq = GNUNET_CLIENT_connect (h->cfg,
125                                  "transport",
126                                  handlers,
127                                  &mq_error_handler,
128                                  h);
129   if (NULL == h->mq)
130     return;
131   env = GNUNET_MQ_msg (s,
132                        GNUNET_MESSAGE_TYPE_TRANSPORT_START);
133   GNUNET_MQ_send (h->mq,
134                   env);
135 }
136
137
138 /**
139  * Function that will schedule the job that will try
140  * to connect us again to the client.
141  *
142  * @param h transport service to reconnect
143  */
144 static void
145 disconnect_and_schedule_reconnect (struct
146                                    GNUNET_TRANSPORT_ManipulationHandle *h)
147 {
148   GNUNET_assert (NULL == h->reconnect_task);
149   if (NULL != h->mq)
150   {
151     GNUNET_MQ_destroy (h->mq);
152     h->mq = NULL;
153   }
154   h->reconnect_task =
155     GNUNET_SCHEDULER_add_delayed (h->reconnect_delay,
156                                   &reconnect,
157                                   h);
158   h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay);
159 }
160
161
162 /**
163  * Set transport metrics for a peer and a direction.
164  *
165  * @param handle transport handle
166  * @param peer the peer to set the metric for
167  * @param prop the performance metrics to set
168  * @param delay_in inbound delay to introduce
169  * @param delay_out outbound delay to introduce
170  *
171  * Note: Delay restrictions in receiving direction will be enforced
172  * with one message delay.
173  */
174 void
175 GNUNET_TRANSPORT_manipulation_set (struct
176                                    GNUNET_TRANSPORT_ManipulationHandle *handle,
177                                    const struct GNUNET_PeerIdentity *peer,
178                                    const struct GNUNET_ATS_Properties *prop,
179                                    struct GNUNET_TIME_Relative delay_in,
180                                    struct GNUNET_TIME_Relative delay_out)
181 {
182   struct GNUNET_MQ_Envelope *env;
183   struct TrafficMetricMessage *msg;
184
185   if (NULL == handle->mq)
186     return;
187   env = GNUNET_MQ_msg (msg,
188                        GNUNET_MESSAGE_TYPE_TRANSPORT_TRAFFIC_METRIC);
189   msg->reserved = htonl (0);
190   msg->peer = *peer;
191   GNUNET_ATS_properties_hton (&msg->properties,
192                               prop);
193   msg->delay_in = GNUNET_TIME_relative_hton (delay_in);
194   msg->delay_out = GNUNET_TIME_relative_hton (delay_out);
195   GNUNET_MQ_send (handle->mq,
196                   env);
197 }
198
199
200 /**
201  * Connect to the transport service.  Note that the connection may
202  * complete (or fail) asynchronously.
203  *
204  * @param cfg configuration to use
205  * @return NULL on error
206  */
207 struct GNUNET_TRANSPORT_ManipulationHandle *
208 GNUNET_TRANSPORT_manipulation_connect (const struct
209                                        GNUNET_CONFIGURATION_Handle *cfg)
210 {
211   struct GNUNET_TRANSPORT_ManipulationHandle *h;
212
213   h = GNUNET_new (struct GNUNET_TRANSPORT_ManipulationHandle);
214   h->cfg = cfg;
215   LOG (GNUNET_ERROR_TYPE_DEBUG,
216        "Connecting to transport service.\n");
217   reconnect (h);
218   if (NULL == h->mq)
219   {
220     GNUNET_free (h);
221     return NULL;
222   }
223   return h;
224 }
225
226
227 /**
228  * Disconnect from the transport service.
229  *
230  * @param handle handle to the service as returned from #GNUNET_TRANSPORT_manipulation_connect()
231  */
232 void
233 GNUNET_TRANSPORT_manipulation_disconnect (struct
234                                           GNUNET_TRANSPORT_ManipulationHandle *
235                                           handle)
236 {
237   if (NULL == handle->reconnect_task)
238     disconnect_and_schedule_reconnect (handle);
239   /* and now we stop trying to connect again... */
240   if (NULL != handle->reconnect_task)
241   {
242     GNUNET_SCHEDULER_cancel (handle->reconnect_task);
243     handle->reconnect_task = NULL;
244   }
245   GNUNET_free (handle);
246 }
247
248
249 /* end of transport_api_manipulation.c */