blob: 961955381648cee4753d73a4dd6d0a45ea999246 [file] [log] [blame]
Harald Welte4ffb43f2017-01-27 10:29:49 +01001/*
2 * (C) 2017 by Harald Welte <laforge@gnumonks.org>
3 * All Rights Reserved
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 */
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <unistd.h>
24#include <string.h>
25#include <fcntl.h>
Pau Espin Pedrolcd133312020-08-19 17:25:04 +020026#include <inttypes.h>
Pau Espin Pedrold9e04172020-08-31 18:56:10 +020027#include <errno.h>
Harald Welte4ffb43f2017-01-27 10:29:49 +010028
29#include <sys/socket.h>
30#include <arpa/inet.h>
31#include <netinet/in.h>
32
Philipp Maier6f0f5602017-02-09 14:09:06 +010033#include <osmocom/core/application.h>
Harald Welte4ffb43f2017-01-27 10:29:49 +010034#include <osmocom/core/utils.h>
35#include <osmocom/core/socket.h>
Philipp Maier6f0f5602017-02-09 14:09:06 +010036#include <osmocom/core/logging.h>
Pau Espin Pedrolcd133312020-08-19 17:25:04 +020037#include <osmocom/core/bits.h>
Harald Welte4ffb43f2017-01-27 10:29:49 +010038
39#include "../config.h"
40
Neels Hofmeyra829b452018-04-05 03:02:35 +020041void *ctx = NULL;
42
Harald Welte4ffb43f2017-01-27 10:29:49 +010043static int test_sockinit(void)
44{
45 int fd, rc;
46 char *name;
47
48 printf("Checking osmo_sock_init() with bind to a random local UDP port\n");
49 fd = osmo_sock_init(AF_INET, SOCK_DGRAM, IPPROTO_UDP,
50 "0.0.0.0", 0, OSMO_SOCK_F_BIND);
51 OSMO_ASSERT(fd >= 0);
Neels Hofmeyra829b452018-04-05 03:02:35 +020052 name = osmo_sock_get_name(ctx, fd);
Harald Welte4ffb43f2017-01-27 10:29:49 +010053 /* expect it to be not connected. We cannot match on INADDR_ANY,
54 * as apparently that won't work on FreeBSD if there's only one
55 * address (e.g. 127.0.0.1) assigned to the entire system, like
56 * the Osmocom FreeBSD build slaves */
Neels Hofmeyr1f82d0a2017-06-21 22:54:46 +020057 OSMO_ASSERT(!strncmp(name, "(r=NULL<->", 9));
Harald Welte4ffb43f2017-01-27 10:29:49 +010058 talloc_free(name);
59 /* expect it to be blocking */
60 rc = fcntl(fd, F_GETFL);
61 OSMO_ASSERT(!(rc & O_NONBLOCK));
62 close(fd);
63
64 printf("Checking for OSMO_SOCK_F_NONBLOCK\n");
65 fd = osmo_sock_init(AF_INET, SOCK_DGRAM, IPPROTO_UDP,
66 "0.0.0.0", 0, OSMO_SOCK_F_BIND|OSMO_SOCK_F_NONBLOCK);
67 OSMO_ASSERT(fd >= 0);
68 /* expect it to be blocking */
69 rc = fcntl(fd, F_GETFL);
70 OSMO_ASSERT(rc & O_NONBLOCK);
71 close(fd);
72
73 printf("Checking for invalid flags\n");
74 fd = osmo_sock_init(AF_INET, SOCK_DGRAM, IPPROTO_UDP,
75 "0.0.0.0", 0, OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
76 OSMO_ASSERT(fd < 0);
77
78 return 0;
79}
80
Harald Weltedda70fc2017-04-08 20:52:33 +020081static int test_sockinit2(void)
82{
83 int fd, rc;
84 char *name;
85
86 printf("Checking osmo_sock_init2() with bind to a random local UDP port\n");
87 fd = osmo_sock_init2(AF_INET, SOCK_DGRAM, IPPROTO_UDP,
88 "0.0.0.0", 0, NULL, 0, OSMO_SOCK_F_BIND);
89 OSMO_ASSERT(fd >= 0);
Neels Hofmeyra829b452018-04-05 03:02:35 +020090 name = osmo_sock_get_name(ctx, fd);
Harald Weltedda70fc2017-04-08 20:52:33 +020091 /* expect it to be not connected. We cannot match on INADDR_ANY,
92 * as apparently that won't work on FreeBSD if there's only one
93 * address (e.g. 127.0.0.1) assigned to the entire system, like
94 * the Osmocom FreeBSD build slaves */
Neels Hofmeyr1f82d0a2017-06-21 22:54:46 +020095 OSMO_ASSERT(!strncmp(name, "(r=NULL<->", 9));
Harald Weltedda70fc2017-04-08 20:52:33 +020096 talloc_free(name);
97 /* expect it to be blocking */
98 rc = fcntl(fd, F_GETFL);
99 OSMO_ASSERT(!(rc & O_NONBLOCK));
100 close(fd);
101
102 printf("Checking osmo_sock_init2() for OSMO_SOCK_F_NONBLOCK\n");
103 fd = osmo_sock_init2(AF_INET, SOCK_DGRAM, IPPROTO_UDP,
104 "0.0.0.0", 0, NULL, 0, OSMO_SOCK_F_BIND|OSMO_SOCK_F_NONBLOCK);
105 OSMO_ASSERT(fd >= 0);
106 /* expect it to be blocking */
107 rc = fcntl(fd, F_GETFL);
108 OSMO_ASSERT(rc & O_NONBLOCK);
109 close(fd);
110
111 printf("Checking osmo_sock_init2() for invalid flags\n");
112 fd = osmo_sock_init2(AF_INET, SOCK_DGRAM, IPPROTO_UDP, "0.0.0.0", 0, NULL, 0, 0);
113 OSMO_ASSERT(fd < 0);
114
115 printf("Checking osmo_sock_init2() for combined BIND + CONNECT\n");
116 fd = osmo_sock_init2(AF_INET, SOCK_DGRAM, IPPROTO_UDP, "127.0.0.1", 0, "127.0.0.1", 53,
117 OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
118 OSMO_ASSERT(fd >= 0);
Neels Hofmeyra829b452018-04-05 03:02:35 +0200119 name = osmo_sock_get_name(ctx, fd);
Harald Weltedda70fc2017-04-08 20:52:33 +0200120#ifndef __FreeBSD__
121 /* For some reason, on the jenkins.osmocom.org build slave with
122 * FreeBSD 10 inside a jail, it fails. Works fine on laforge's
123 * FreeBSD 10 or 11 VM at home */
Neels Hofmeyr1f82d0a2017-06-21 22:54:46 +0200124 OSMO_ASSERT(!strncmp(name, "(r=127.0.0.1:53<->l=127.0.0.1", 29));
Harald Weltedda70fc2017-04-08 20:52:33 +0200125#endif
Alexander Couzens2c962f52020-06-03 00:28:02 +0200126
127 printf("Checking osmo_sock_init2(AF_UNSPEC) must fail on mixed IPv4 & IPv6\n");
128 fd = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "127.0.0.1", 0, "::1", 53,
129 OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
130 OSMO_ASSERT(fd < 0);
131
132 printf("Checking osmo_sock_init2(AF_UNSPEC) must fail on mixed IPv6 & IPv4\n");
133 fd = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "::1", 0, "127.0.0.1", 53,
134 OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
135 OSMO_ASSERT(fd < 0);
136
137 printf("Checking osmo_sock_init2(AF_UNSPEC) BIND + CONNECT on IPv4\n");
138 fd = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "127.0.0.1", 0, "127.0.0.1", 53,
139 OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
140 OSMO_ASSERT(fd >= 0);
141
142 printf("Checking osmo_sock_init2(AF_UNSPEC) BIND + CONNECT on IPv6\n");
143 fd = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "::1", 0, "::1", 53,
144 OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
145 OSMO_ASSERT(fd >= 0);
146
Pau Espin Pedrold9e04172020-08-31 18:56:10 +0200147 printf("Checking osmo_sock_init2(AF_UNSPEC) BIND on IPv4\n");
148 fd = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "127.0.0.1", 0, NULL, 0,
149 OSMO_SOCK_F_BIND);
Pau Espin Pedrold8cf52b2020-08-31 19:00:59 +0200150 OSMO_ASSERT(fd >= 0);
Pau Espin Pedrold9e04172020-08-31 18:56:10 +0200151
Harald Weltedda70fc2017-04-08 20:52:33 +0200152 talloc_free(name);
153
154 return 0;
155}
156
Alexander Couzensea198392020-08-01 22:02:03 +0200157static int test_get_ip_and_port()
158{
159 int fd, rc;
160 char ip[INET6_ADDRSTRLEN] = { };
161 char port[6] = { };
162
163 printf("Checking test_get_ip_and_port() for combined BIND + CONNECT on IPv4\n");
164 fd = osmo_sock_init2(AF_INET, SOCK_DGRAM, IPPROTO_UDP, "127.0.0.1", 0, "127.0.0.1", 55,
165 OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
166
167 OSMO_ASSERT(fd >= 0);
168
169 /* get the remote */
170 rc = osmo_sock_get_ip_and_port(fd, ip, sizeof(ip), port, sizeof(port), false);
171 OSMO_ASSERT(rc == 0);
172 OSMO_ASSERT(strncmp(ip, "127.0.0.1", INET6_ADDRSTRLEN) == 0);
173 OSMO_ASSERT(strncmp(port, "55", 6) == 0);
174
175 printf("Checking test_get_ip_and_port() for combined BIND + CONNECT on IPv6\n");
176 fd = osmo_sock_init2(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, "::1", 0, "::1", 55,
177 OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
178 OSMO_ASSERT(fd >= 0);
179
180 /* get the remote */
181 rc = osmo_sock_get_ip_and_port(fd, ip, sizeof(ip), port, sizeof(port), false);
182 OSMO_ASSERT(rc == 0);
183 OSMO_ASSERT(strncmp(ip, "::1", INET6_ADDRSTRLEN) == 0);
184 OSMO_ASSERT(strncmp(port, "55", 6) == 0);
185
186 return 0;
187}
188
Alexander Couzens43957e62020-08-01 21:56:45 +0200189static int test_sockinit_osa(void)
190{
191 int fd, rc;
192 char *name;
193
194 struct osmo_sockaddr localhost4 = {};
195 struct osmo_sockaddr localhost6 = {};
196 struct osmo_sockaddr localhost4_noport = {};
197 struct osmo_sockaddr localhost6_noport = {};
198 struct osmo_sockaddr any4 = {};
199 struct osmo_sockaddr any6 = {};
200 struct osmo_sockaddr invalid = {};
201
202 localhost4.u.sin = (struct sockaddr_in){
203 .sin_family = AF_INET,
204 .sin_addr.s_addr = inet_addr("127.0.0.1"),
205 .sin_port = htons(42),
206 };
207
208 localhost6.u.sin6 = (struct sockaddr_in6){
209 .sin6_family = AF_INET6,
210 .sin6_port = htons(42),
211 };
212 inet_pton(AF_INET6, "::1", &localhost6.u.sin6.sin6_addr);
213
214 localhost4_noport = localhost4;
215 localhost4_noport.u.sin.sin_port = htons(0);
216 localhost6_noport = localhost6;
217 localhost6_noport.u.sin6.sin6_port = htons(0);
218
219 any4.u.sin = (struct sockaddr_in){
220 .sin_family = AF_INET,
221 .sin_addr.s_addr = inet_addr("0.0.0.0"),
222 .sin_port = htons(0),
223 };
224 any6.u.sin6 = (struct sockaddr_in6){
225 .sin6_family = AF_INET6,
226 .sin6_port = htons(0),
227 };
228 inet_pton(AF_INET6, "::", &any6.u.sin6.sin6_addr);
229
230 invalid.u.sa.sa_family = AF_UNSPEC;
231
232 printf("Checking osmo_sock_init_osa() with bind to a random local UDP port\n");
233 fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP,
234 &any4, NULL, OSMO_SOCK_F_BIND);
235 OSMO_ASSERT(fd >= 0);
236 name = osmo_sock_get_name(ctx, fd);
237 /* expect it to be not connected. We cannot match on INADDR_ANY,
238 * as apparently that won't work on FreeBSD if there's only one
239 * address (e.g. 137.0.0.1) assigned to the entire system, like
240 * the Osmocom FreeBSD build slaves */
241 OSMO_ASSERT(!strncmp(name, "(r=NULL<->", 9));
242 talloc_free(name);
243 /* expect it to be blocking */
244 rc = fcntl(fd, F_GETFL);
245 OSMO_ASSERT(!(rc & O_NONBLOCK));
246 close(fd);
247
248 printf("Checking osmo_sock_init_osa() IPv4 for OSMO_SOCK_F_NONBLOCK\n");
249 fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP,
250 &any4, NULL, OSMO_SOCK_F_BIND|OSMO_SOCK_F_NONBLOCK);
251 OSMO_ASSERT(fd >= 0);
252 /* expect it to be blocking */
253 rc = fcntl(fd, F_GETFL);
254 OSMO_ASSERT(rc & O_NONBLOCK);
255 close(fd);
256
257 printf("Checking osmo_sock_init_osa() IPv6 for OSMO_SOCK_F_NONBLOCK\n");
258 fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP,
259 &any6, NULL, OSMO_SOCK_F_BIND|OSMO_SOCK_F_NONBLOCK);
260 OSMO_ASSERT(fd >= 0);
261 /* expect it to be blocking */
262 rc = fcntl(fd, F_GETFL);
263 OSMO_ASSERT(rc & O_NONBLOCK);
264 close(fd);
265
266 printf("Checking osmo_sock_init_osa() for invalid flags\n");
267 fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &any4, NULL, 0);
268 OSMO_ASSERT(fd < 0);
269
270 printf("Checking osmo_sock_init_osa() for combined BIND + CONNECT on IPv4\n");
271 fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &localhost4_noport, &localhost4,
272 OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
273 OSMO_ASSERT(fd >= 0);
274 name = osmo_sock_get_name(ctx, fd);
275#ifndef __FreeBSD__
276 /* For some reason, on the jenkins.osmocom.org build slave with
277 * FreeBSD 10 inside a jail, it fails. Works fine on laforge's
278 * FreeBSD 10 or 11 VM at home */
279 OSMO_ASSERT(!strncmp(name, "(r=127.0.0.1:42<->l=127.0.0.1", 29));
280#endif
281 close(fd);
282
283 printf("Checking osmo_sock_init_osa() for combined BIND + CONNECT on IPv6\n");
284 fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &localhost6_noport, &localhost6,
285 OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
286 OSMO_ASSERT(fd >= 0);
287 name = osmo_sock_get_name(ctx, fd);
288#ifndef __FreeBSD__
289 /* For some reason, on the jenkins.osmocom.org build slave with
290 * FreeBSD 10 inside a jail, it fails. Works fine on laforge's
291 * FreeBSD 10 or 11 VM at home */
292 OSMO_ASSERT(!strncmp(name, "(r=::1:42<->l=::1", 17));
293#endif
294 close(fd);
295
296 printf("Checking osmo_sock_init_osa() must fail on mixed IPv4 & IPv6\n");
297 fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &localhost4_noport, &localhost6,
298 OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
299 OSMO_ASSERT(fd < 0);
300
301 printf("Checking osmo_sock_init_osa() must fail on mixed IPv6 & IPv4\n");
302 fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &localhost6_noport, &localhost4,
303 OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
304 OSMO_ASSERT(fd < 0);
305
306 printf("Checking osmo_sock_init_osa() must fail on invalid osmo_sockaddr\n");
307 fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &invalid, &localhost4,
308 OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
309 OSMO_ASSERT(fd < 0);
310
311 talloc_free(name);
312
313 return 0;
314}
315
Alexander Couzens80788fa2020-10-12 01:11:20 +0200316static void test_osa_str(void)
317{
318 char buf[256];
319 const char *result;
320 struct osmo_sockaddr localhost4 = {};
321 struct osmo_sockaddr localhost6 = {};
322
323 localhost4.u.sin = (struct sockaddr_in){
324 .sin_family = AF_INET,
325 .sin_addr.s_addr = inet_addr("127.0.0.1"),
326 .sin_port = htons(42),
327 };
328
329 localhost6.u.sin6 = (struct sockaddr_in6){
330 .sin6_family = AF_INET6,
331 .sin6_port = htons(42),
332 };
333 inet_pton(AF_INET6, "::1", &localhost6.u.sin6.sin6_addr);
334
335 /* test a too short str */
336 memset(&buf[0], 0, sizeof(buf));
337 result = osmo_sockaddr_to_str_buf(buf, 1, &localhost4);
338 printf("Checking osmo_sockaddr_to_str_buf to small IPv4\n");
339 OSMO_ASSERT(result == NULL);
340
341 memset(&buf[0], 0, sizeof(buf));
342 result = osmo_sockaddr_to_str_buf(buf, sizeof(buf), &localhost4);
343 printf("Checking osmo_sockaddr_to_str_buf IPv4\n");
344 OSMO_ASSERT(!strncmp("127.0.0.1:42", result, sizeof(buf)));
345
346 memset(&buf[0], 0, sizeof(buf));
347 result = osmo_sockaddr_to_str_buf(buf, 256, &localhost6);
348 printf("Checking osmo_sockaddr_to_str_buf IPv6\n");
349 OSMO_ASSERT(!strncmp("[::1]:42", result, sizeof(buf)));
350
351 memset(&buf[0], 0, sizeof(buf));
352 result = osmo_sockaddr_to_str_buf(buf, 8, &localhost6);
353 printf("Checking osmo_sockaddr_to_str_buf too short IPv6\n");
354 OSMO_ASSERT(!strncmp("[::1]:4", result, sizeof(buf)));
355
356 memset(&buf[0], 0, sizeof(buf));
357 result = osmo_sockaddr_to_str_buf(buf, 5, &localhost6);
358 printf("Checking osmo_sockaddr_to_str_buf too short IPv6\n");
359 OSMO_ASSERT(result == NULL);
360
361 localhost6.u.sin6.sin6_port = 0;
362 memset(&buf[0], 0, sizeof(buf));
363 result = osmo_sockaddr_to_str_buf(buf, 5, &localhost6);
364 printf("Checking osmo_sockaddr_to_str_buf only 5 bytes IPv6\n");
365 OSMO_ASSERT(result == NULL);
366
367 inet_pton(AF_INET6, "::", &localhost6.u.sin6.sin6_addr);
368 memset(&buf[0], 0, sizeof(buf));
369 result = osmo_sockaddr_to_str_buf(buf, 5, &localhost6);
370 printf("Checking osmo_sockaddr_to_str_buf only 5 bytes IPv6\n");
371 OSMO_ASSERT(!strncmp("[::]", result, sizeof(buf)));
372
373 inet_pton(AF_INET6, "2003:1234:5678:90ab:cdef:1234:4321:4321", &localhost6.u.sin6.sin6_addr);
374 memset(&buf[0], 0, sizeof(buf));
375 result = osmo_sockaddr_to_str_buf(buf, sizeof(buf), &localhost6);
376 printf("Checking osmo_sockaddr_to_str_buf long IPv6\n");
377 OSMO_ASSERT(!strncmp("[2003:1234:5678:90ab:cdef:1234:4321:4321]", result, sizeof(buf)));
378
379 localhost6.u.sin6.sin6_port = htons(23420);
380 memset(&buf[0], 0, sizeof(buf));
381 result = osmo_sockaddr_to_str_buf(buf, sizeof(buf), &localhost6);
382 printf("Checking osmo_sockaddr_to_str_buf long IPv6 port\n");
383 OSMO_ASSERT(!strncmp("[2003:1234:5678:90ab:cdef:1234:4321:4321]:23420", result, sizeof(buf)));
384
385 result = osmo_sockaddr_to_str(&localhost6);
386 printf("Checking osmo_sockaddr_to_str_buf long IPv6 port static buffer\n");
387 OSMO_ASSERT(!strncmp("[2003:1234:5678:90ab:cdef:1234:4321:4321]:23420", result, sizeof(buf)));
388}
389
Philipp Maier6f0f5602017-02-09 14:09:06 +0100390const struct log_info_cat default_categories[] = {
391};
392
393static struct log_info info = {
394 .cat = default_categories,
395 .num_cat = ARRAY_SIZE(default_categories),
396};
397
Harald Welte4ffb43f2017-01-27 10:29:49 +0100398int main(int argc, char *argv[])
399{
Neels Hofmeyra829b452018-04-05 03:02:35 +0200400 ctx = talloc_named_const(NULL, 0, "socket_test");
401 osmo_init_logging2(ctx, &info);
Philipp Maier6f0f5602017-02-09 14:09:06 +0100402 log_set_use_color(osmo_stderr_target, 0);
403 log_set_print_filename(osmo_stderr_target, 0);
Pau Espin Pedrol690b6612021-02-18 19:10:28 +0100404 log_set_print_category(osmo_stderr_target, 0);
405 log_set_print_category_hex(osmo_stderr_target, 0);
Philipp Maier6f0f5602017-02-09 14:09:06 +0100406
Harald Welte4ffb43f2017-01-27 10:29:49 +0100407 test_sockinit();
Harald Weltedda70fc2017-04-08 20:52:33 +0200408 test_sockinit2();
Alexander Couzensea198392020-08-01 22:02:03 +0200409 test_get_ip_and_port();
Alexander Couzens43957e62020-08-01 21:56:45 +0200410 test_sockinit_osa();
Alexander Couzens80788fa2020-10-12 01:11:20 +0200411 test_osa_str();
Philipp Maier6f0f5602017-02-09 14:09:06 +0100412
413 return EXIT_SUCCESS;
Harald Welte4ffb43f2017-01-27 10:29:49 +0100414}