Serenity Operating System
1/*
2 * Copyright (c) 2020-2022, the SerenityOS developers.
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/Function.h>
8#include <AK/IPv4Address.h>
9#include <LibCore/System.h>
10#include <LibMain/Main.h>
11#include <net/if.h>
12#include <netinet/in.h>
13#include <stdio.h>
14#include <string.h>
15#include <sys/socket.h>
16
17static void test_invalid(int);
18static void test_no_route(int);
19static void test_valid(int);
20static void test_send(int);
21
22static ErrorOr<void> test(Function<void(int)> test_fn)
23{
24 auto fd = TRY(Core::System::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP));
25 test_fn(fd);
26
27 // be a responsible boi
28 TRY(Core::System::close(fd));
29
30 return {};
31}
32
33ErrorOr<int> serenity_main(Main::Arguments)
34{
35 TRY(test(test_invalid));
36 TRY(test(test_valid));
37 TRY(test(test_no_route));
38 TRY(test(test_send));
39
40 return 0;
41}
42
43void test_invalid(int fd)
44{
45 // bind to an interface that does not exist
46 char buf[IFNAMSIZ];
47 socklen_t buflen = IFNAMSIZ;
48 memcpy(buf, "foodev", 7);
49
50 auto setsockopt_maybe_error = Core::System::setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, buf, buflen);
51 if (setsockopt_maybe_error.is_error()) {
52 warnln("setsockopt(SO_BINDTODEVICE) :: invalid (Should fail with ENODEV).");
53 puts("PASS invalid");
54 } else {
55 puts("FAIL invalid");
56 }
57}
58
59void test_valid(int fd)
60{
61 // bind to an interface that exists
62 char buf[IFNAMSIZ];
63 socklen_t buflen = IFNAMSIZ;
64 memcpy(buf, "loop", 5);
65
66 auto setsockopt_maybe_error = Core::System::setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, buf, buflen);
67 if (setsockopt_maybe_error.is_error()) {
68 warnln("setsockopt(SO_BINDTODEVICE) :: valid");
69 puts("FAIL valid");
70 } else {
71 puts("PASS valid");
72 }
73}
74
75void test_no_route(int fd)
76{
77 // bind to an interface that cannot deliver
78 char buf[IFNAMSIZ];
79 socklen_t buflen = IFNAMSIZ;
80 memcpy(buf, "loop", 5);
81
82 auto setsockopt_maybe_error = Core::System::setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, buf, buflen);
83 if (setsockopt_maybe_error.is_error()) {
84 warnln("setsockopt(SO_BINDTODEVICE) :: no_route");
85 puts("FAIL no_route");
86 return;
87 }
88 sockaddr_in sin;
89 memset(&sin, 0, sizeof(sin));
90
91 sin.sin_addr.s_addr = IPv4Address { 10, 0, 2, 15 }.to_u32();
92 sin.sin_port = 8080;
93 sin.sin_family = AF_INET;
94
95 auto bind_maybe_error = Core::System::bind(fd, (sockaddr*)&sin, sizeof(sin));
96 if (bind_maybe_error.is_error()) {
97 warnln("bind() :: no_route");
98 puts("FAIL no_route");
99 return;
100 }
101
102 auto sendto_maybe_error = Core::System::sendto(fd, "TEST", 4, 0, (sockaddr*)&sin, sizeof(sin));
103 if (sendto_maybe_error.is_error()) {
104 warnln("sendto() :: no_route (Should fail with EHOSTUNREACH)");
105 puts("PASS no_route");
106 } else
107 puts("FAIL no_route");
108}
109
110void test_send(int fd)
111{
112 // bind to an interface that cannot deliver
113 char buf[IFNAMSIZ];
114 socklen_t buflen = IFNAMSIZ;
115 // FIXME: Look up the proper device name instead of hard-coding it
116 memcpy(buf, "ep0s7", 6);
117
118 auto setsockopt_maybe_error = Core::System::setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, buf, buflen);
119 if (setsockopt_maybe_error.is_error()) {
120 warnln("setsockopt(SO_BINDTODEVICE) :: send");
121 puts("FAIL send");
122 return;
123 }
124 sockaddr_in sin;
125 memset(&sin, 0, sizeof(sin));
126
127 sin.sin_addr.s_addr = IPv4Address { 10, 0, 2, 15 }.to_u32();
128 sin.sin_port = 8080;
129 sin.sin_family = AF_INET;
130
131 auto bind_maybe_error = Core::System::bind(fd, (sockaddr*)&sin, sizeof(sin));
132 if (bind_maybe_error.is_error()) {
133 warnln("bind() :: send");
134 puts("FAIL send");
135 return;
136 }
137
138 auto sendto_maybe_error = Core::System::sendto(fd, "TEST", 4, 0, (sockaddr*)&sin, sizeof(sin));
139 if (sendto_maybe_error.is_error()) {
140 warnln("sendto() :: send");
141 puts("FAIL send");
142 return;
143 }
144 puts("PASS send");
145}