error handling
[oweals/gnunet.git] / src / nat / test_stun.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009, 2015 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  * Testcase for STUN server resolution
23  *
24  * @file nat/test_stun.c
25  * @brief Testcase for STUN library
26  * @author Bruno Souza Cabral
27  * @author Christian Grothoff
28  */
29
30
31 #include "platform.h"
32 #include "gnunet_util_lib.h"
33 #include "gnunet_program_lib.h"
34 #include "gnunet_scheduler_lib.h"
35 #include "gnunet_nat_lib.h"
36
37
38 #define LOG(kind, ...) GNUNET_log_from (kind, "test-stun", __VA_ARGS__)
39
40 /**
41  * Time to wait before stopping NAT, in seconds
42  */
43 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
44
45
46 /**
47  * The port the test service is running on (default 7895)
48  */
49 static unsigned long port = 7895;
50
51 static int ret = 1;
52
53 static const char *stun_server = "stun.gnunet.org";
54
55 static int stun_port = 3478;
56
57 /**
58  * The listen socket of the service for IPv4
59  */
60 static struct GNUNET_NETWORK_Handle *lsock4;
61
62 /**
63  * The listen task ID for IPv4
64  */
65 static struct GNUNET_SCHEDULER_Task *ltask4;
66
67 /**
68  * Handle for the STUN request.
69  */
70 static struct GNUNET_NAT_STUN_Handle *rh;
71
72
73 static void
74 print_answer (struct sockaddr_in*answer)
75 {
76   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
77               "External IP is: %s , with port %d\n",
78               inet_ntoa (answer->sin_addr),
79               ntohs (answer->sin_port));
80 }
81
82
83 /**
84  * Function that terminates the test.
85  */
86 static void
87 stop ()
88 {
89   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
90               "Stopping NAT and quitting...\n");
91   if (NULL != ltask4)
92   {
93     GNUNET_SCHEDULER_cancel (ltask4);
94     ltask4 = NULL;
95   }
96   if (NULL != lsock4)
97   {
98     GNUNET_NETWORK_socket_close (lsock4);
99     lsock4 = NULL;
100   }
101   if (NULL != rh)
102   {
103     GNUNET_NAT_stun_make_request_cancel (rh);
104     rh = NULL;
105   }
106 }
107
108
109 /**
110  * Activity on our incoming socket.  Read data from the
111  * incoming connection.
112  *
113  * @param cls
114  */
115 static void
116 do_udp_read (void *cls)
117 {
118   // struct GNUNET_NAT_Test *tst = cls;
119   unsigned char reply_buf[1024];
120   ssize_t rlen;
121   struct sockaddr_in answer;
122   const struct GNUNET_SCHEDULER_TaskContext *tc;
123
124   ltask4 = NULL;
125   tc = GNUNET_SCHEDULER_get_task_context ();
126   if ((0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) ||
127       (! GNUNET_NETWORK_fdset_isset (tc->read_ready,
128                                      lsock4)))
129   {
130     fprintf (stderr,
131              "Timeout waiting for STUN response\n");
132     stop ();
133   }
134   rlen = GNUNET_NETWORK_socket_recv (lsock4,
135                                      reply_buf,
136                                      sizeof(reply_buf));
137   memset (&answer,
138           0,
139           sizeof(struct sockaddr_in));
140   if (GNUNET_OK !=
141       GNUNET_NAT_stun_handle_packet (reply_buf,
142                                      rlen,
143                                      &answer))
144   {
145     fprintf (stderr,
146              "Unexpected UDP packet, trying to read more\n");
147     ltask4 = GNUNET_SCHEDULER_add_read_net (TIMEOUT,
148                                             lsock4,
149                                             &do_udp_read, NULL);
150     return;
151   }
152   ret = 0;
153   print_answer (&answer);
154   stop ();
155 }
156
157
158 /**
159  * Create an IPv4 listen socket bound to our port.
160  *
161  * @return NULL on error
162  */
163 static struct GNUNET_NETWORK_Handle *
164 bind_v4 ()
165 {
166   struct GNUNET_NETWORK_Handle *ls;
167   struct sockaddr_in sa4;
168   int eno;
169
170   memset (&sa4, 0, sizeof(sa4));
171   sa4.sin_family = AF_INET;
172   sa4.sin_port = htons (port);
173 #if HAVE_SOCKADDR_IN_SIN_LEN
174   sa4.sin_len = sizeof(sa4);
175 #endif
176   ls = GNUNET_NETWORK_socket_create (AF_INET,
177                                      SOCK_DGRAM,
178                                      0);
179   if (NULL == ls)
180     return NULL;
181   if (GNUNET_OK !=
182       GNUNET_NETWORK_socket_bind (ls,
183                                   (const struct sockaddr *) &sa4,
184                                   sizeof(sa4)))
185   {
186     eno = errno;
187     GNUNET_NETWORK_socket_close (ls);
188     errno = eno;
189     return NULL;
190   }
191   return ls;
192 }
193
194
195 /**
196  * Function called with the result of the STUN request transmission attempt.
197  *
198  * @param cls unused
199  * @param error status code from STUN
200  */
201 static void
202 request_callback (void *cls,
203                   enum GNUNET_NAT_StatusCode error)
204 {
205   rh = NULL;
206   if (GNUNET_NAT_ERROR_SUCCESS == error)
207   {
208     /* all good, start to receive */
209     ltask4 = GNUNET_SCHEDULER_add_read_net (TIMEOUT,
210                                             lsock4,
211                                             &do_udp_read,
212                                             NULL);
213     return;
214   }
215   if (error == GNUNET_NAT_ERROR_NOT_ONLINE)
216   {
217     ret = 77;   /* report 'skip' */
218     fprintf (stderr,
219              "System is offline, cannot test STUN request.\n");
220   }
221   else
222   {
223     ret = error;
224   }
225   stop ();
226 }
227
228
229 /**
230  * Main function run with scheduler.
231  */
232 static void
233 run (void *cls,
234      char *const *args,
235      const char *cfgfile,
236      const struct GNUNET_CONFIGURATION_Handle *cfg)
237 {
238   // Lets create the socket
239   lsock4 = bind_v4 ();
240   if (NULL == lsock4)
241   {
242     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
243                          "bind");
244     GNUNET_SCHEDULER_shutdown ();
245     return;
246   }
247   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
248               "Service listens on port %u\n",
249               (unsigned int) port);
250   rh = GNUNET_NAT_stun_make_request (stun_server,
251                                      stun_port,
252                                      lsock4,
253                                      &request_callback, NULL);
254   GNUNET_SCHEDULER_add_delayed (TIMEOUT,
255                                 &stop, NULL);
256 }
257
258
259 int
260 main (int argc, char *const argv[])
261 {
262   struct GNUNET_GETOPT_CommandLineOption options[] = {
263     GNUNET_GETOPT_OPTION_END
264   };
265   char *const argv_prog[] = {
266     "test-stun",
267     "-c",
268     "test_stun.conf",
269     NULL
270   };
271   char *fn;
272   struct GNUNET_OS_Process *proc;
273
274   GNUNET_log_setup ("test-stun",
275                     "WARNING",
276                     NULL);
277
278   /* Lets start resolver */
279   fn = GNUNET_OS_get_libexec_binary_path ("gnunet-service-resolver");
280   proc = GNUNET_OS_start_process (GNUNET_YES,
281                                   GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
282                                   NULL, NULL, NULL,
283                                   fn,
284                                   "gnunet-service-resolver",
285                                   "-c", "test_stun.conf", NULL);
286
287   if (NULL == proc)
288   {
289     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
290                 "This test was unable to start gnunet-service-resolver, and it is required to run ...\n");
291     exit (1);
292   }
293
294   GNUNET_PROGRAM_run (3, argv_prog,
295                       "test-stun", "nohelp",
296                       options,
297                       &run, NULL);
298
299   /* Now kill the resolver */
300   if (0 != GNUNET_OS_process_kill (proc, GNUNET_TERM_SIG))
301   {
302     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
303   }
304   GNUNET_OS_process_wait (proc);
305   GNUNET_OS_process_destroy (proc);
306   proc = NULL;
307   GNUNET_free (fn);
308
309   return ret;
310 }
311
312
313 /* end of test_stun.c */