Serenity Operating System
at master 329 lines 14 kB view raw
1/* 2 * Copyright (c) 2021, Brandon Pruitt <brapru@pm.me> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/DeprecatedString.h> 8#include <AK/HashMap.h> 9#include <AK/IPv4Address.h> 10#include <AK/JsonArray.h> 11#include <AK/JsonObject.h> 12#include <AK/QuickSort.h> 13#include <LibCore/ArgsParser.h> 14#include <LibCore/File.h> 15#include <LibCore/ProcessStatisticsReader.h> 16#include <LibCore/System.h> 17#include <LibMain/Main.h> 18#include <arpa/inet.h> 19#include <netdb.h> 20#include <unistd.h> 21 22constexpr int max_formatted_address_length = 21; 23 24ErrorOr<int> serenity_main(Main::Arguments arguments) 25{ 26 TRY(Core::System::pledge("stdio rpath unix")); 27 28 bool flag_all = false; 29 bool flag_list = false; 30 bool flag_tcp = false; 31 bool flag_udp = false; 32 bool flag_numeric = false; 33 bool flag_program = false; 34 bool flag_wide = false; 35 36 Core::ArgsParser args_parser; 37 args_parser.set_general_help("Display network connections"); 38 args_parser.add_option(flag_all, "Display both listening and non-listening sockets", "all", 'a'); 39 args_parser.add_option(flag_list, "Display only listening sockets", "list", 'l'); 40 args_parser.add_option(flag_tcp, "Display only TCP network connections", "tcp", 't'); 41 args_parser.add_option(flag_udp, "Display only UDP network connections", "udp", 'u'); 42 args_parser.add_option(flag_numeric, "Display numerical addresses", "numeric", 'n'); 43 args_parser.add_option(flag_program, "Show the PID and name of the program to which each socket belongs", "program", 'p'); 44 args_parser.add_option(flag_wide, "Do not truncate IP addresses by printing out the whole symbolic host", "wide", 'W'); 45 args_parser.parse(arguments); 46 47 TRY(Core::System::unveil("/sys/kernel/net", "r")); 48 TRY(Core::System::unveil("/sys/kernel/processes", "r")); 49 TRY(Core::System::unveil("/etc/passwd", "r")); 50 TRY(Core::System::unveil("/etc/services", "r")); 51 if (!flag_numeric) 52 TRY(Core::System::unveil("/tmp/portal/lookup", "rw")); 53 54 TRY(Core::System::unveil(nullptr, nullptr)); 55 56 bool has_protocol_flag = (flag_tcp || flag_udp); 57 58 uid_t current_uid = getuid(); 59 60 HashMap<pid_t, DeprecatedString> programs; 61 62 if (flag_program) { 63 auto processes = TRY(Core::ProcessStatisticsReader::get_all()); 64 65 for (auto& proc : processes.processes) { 66 programs.set(proc.pid, proc.name); 67 } 68 } 69 70 enum class Alignment { 71 Left, 72 Right 73 }; 74 75 struct Column { 76 DeprecatedString title; 77 Alignment alignment { Alignment::Left }; 78 int width { 0 }; 79 DeprecatedString buffer; 80 }; 81 82 Vector<Column> columns; 83 84 int protocol_column = -1; 85 int bytes_in_column = -1; 86 int bytes_out_column = -1; 87 int local_address_column = -1; 88 int peer_address_column = -1; 89 int state_column = -1; 90 int program_column = -1; 91 92 auto add_column = [&](auto title, auto alignment, auto width) { 93 columns.append({ title, alignment, width, {} }); 94 return columns.size() - 1; 95 }; 96 97 protocol_column = add_column("Proto", Alignment::Left, 5); 98 bytes_in_column = add_column("Bytes-In", Alignment::Right, 9); 99 bytes_out_column = add_column("Bytes-Out", Alignment::Right, 9); 100 local_address_column = add_column("Local Address", Alignment::Left, 22); 101 peer_address_column = add_column("Peer Address", Alignment::Left, 22); 102 state_column = add_column("State", Alignment::Left, 11); 103 program_column = flag_program ? add_column("PID/Program", Alignment::Left, 11) : -1; 104 105 auto print_column = [](auto& column, auto& string) { 106 if (!column.width) { 107 out("{}", string); 108 return; 109 } 110 if (column.alignment == Alignment::Right) { 111 out("{:>{1}} "sv, string, column.width); 112 } else { 113 out("{:<{1}} "sv, string, column.width); 114 } 115 }; 116 117 auto get_formatted_address = [&](DeprecatedString const& address, DeprecatedString const& port) { 118 if (flag_wide) 119 return DeprecatedString::formatted("{}:{}", address, port); 120 121 if ((address.length() + port.length()) <= max_formatted_address_length) 122 return DeprecatedString::formatted("{}:{}", address, port); 123 124 return DeprecatedString::formatted("{}:{}", address.substring_view(0, max_formatted_address_length - port.length()), port); 125 }; 126 127 auto get_formatted_program = [&](pid_t pid) { 128 if (pid == -1) 129 return DeprecatedString("-"); 130 131 auto program = programs.get(pid); 132 return DeprecatedString::formatted("{}/{}", pid, program.value()); 133 }; 134 135 if (!has_protocol_flag || flag_tcp || flag_udp) { 136 if (flag_program && current_uid != 0) { 137 outln("(Some processes could not be identified, non-owned process info will not be shown)"); 138 } 139 140 out("Active Internet connections "); 141 142 if (flag_all) { 143 outln("(servers and established)"); 144 } else { 145 if (flag_list) 146 outln("(only servers)"); 147 else 148 outln("(without servers)"); 149 } 150 151 for (auto& column : columns) 152 print_column(column, column.title); 153 outln(); 154 } 155 156 if (!has_protocol_flag || flag_tcp) { 157 auto file = TRY(Core::File::open("/sys/kernel/net/tcp"sv, Core::File::OpenMode::Read)); 158 auto file_contents = TRY(file->read_until_eof()); 159 auto json_or_error = JsonValue::from_string(file_contents); 160 if (json_or_error.is_error()) { 161 warnln("Error: {}", json_or_error.error()); 162 return 1; 163 } 164 auto json = json_or_error.release_value(); 165 166 Vector<JsonValue> sorted_regions = json.as_array().values(); 167 quick_sort(sorted_regions, [](auto& a, auto& b) { 168 return a.as_object().get_u32("local_port"sv).value_or(0) < b.as_object().get_u32("local_port"sv).value_or(0); 169 }); 170 171 for (auto& value : sorted_regions) { 172 auto& if_object = value.as_object(); 173 174 auto bytes_in = if_object.get_deprecated_string("bytes_in"sv).value_or({}); 175 auto bytes_out = if_object.get_deprecated_string("bytes_out"sv).value_or({}); 176 177 auto peer_address = if_object.get_deprecated_string("peer_address"sv).value_or({}); 178 if (!flag_numeric) { 179 auto from_string = IPv4Address::from_string(peer_address); 180 auto addr = from_string.value().to_in_addr_t(); 181 auto* hostent = gethostbyaddr(&addr, sizeof(in_addr), AF_INET); 182 if (hostent != nullptr) { 183 auto host_name = StringView { hostent->h_name, strlen(hostent->h_name) }; 184 if (!host_name.is_empty()) 185 peer_address = host_name; 186 } 187 } 188 189 auto peer_port = if_object.get_deprecated_string("peer_port"sv).value_or({}); 190 if (!flag_numeric) { 191 auto service = getservbyport(htons(if_object.get_u32("peer_port"sv).value_or(0)), "tcp"); 192 if (service != nullptr) { 193 auto s_name = StringView { service->s_name, strlen(service->s_name) }; 194 if (!s_name.is_empty()) 195 peer_port = s_name; 196 } 197 } 198 199 auto local_address = if_object.get_deprecated_string("local_address"sv).value_or({}); 200 if (!flag_numeric) { 201 auto from_string = IPv4Address::from_string(local_address); 202 auto addr = from_string.value().to_in_addr_t(); 203 auto* hostent = gethostbyaddr(&addr, sizeof(in_addr), AF_INET); 204 if (hostent != nullptr) { 205 auto host_name = StringView { hostent->h_name, strlen(hostent->h_name) }; 206 if (!host_name.is_empty()) 207 local_address = host_name; 208 } 209 } 210 211 auto local_port = if_object.get_deprecated_string("local_port"sv).value_or({}); 212 if (!flag_numeric) { 213 auto service = getservbyport(htons(if_object.get_u32("local_port"sv).value_or(0)), "tcp"); 214 if (service != nullptr) { 215 auto s_name = StringView { service->s_name, strlen(service->s_name) }; 216 if (!s_name.is_empty()) 217 local_port = s_name; 218 } 219 } 220 221 auto state = if_object.get_deprecated_string("state"sv).value_or({}); 222 auto origin_pid = (if_object.has("origin_pid"sv)) ? if_object.get_u32("origin_pid"sv).value_or(0) : -1; 223 224 if (!flag_all && ((state == "Listen" && !flag_list) || (state != "Listen" && flag_list))) 225 continue; 226 227 if (protocol_column != -1) 228 columns[protocol_column].buffer = "tcp"; 229 if (bytes_in_column != -1) 230 columns[bytes_in_column].buffer = bytes_in; 231 if (bytes_out_column != -1) 232 columns[bytes_out_column].buffer = bytes_out; 233 if (local_address_column != -1) 234 columns[local_address_column].buffer = get_formatted_address(local_address, local_port); 235 if (peer_address_column != -1) 236 columns[peer_address_column].buffer = get_formatted_address(peer_address, peer_port); 237 if (state_column != -1) 238 columns[state_column].buffer = state; 239 if (flag_program && program_column != -1) 240 columns[program_column].buffer = get_formatted_program(origin_pid); 241 242 for (auto& column : columns) 243 print_column(column, column.buffer); 244 outln(); 245 }; 246 } 247 248 if (!has_protocol_flag || flag_udp) { 249 auto file = TRY(Core::File::open("/sys/kernel/net/udp"sv, Core::File::OpenMode::Read)); 250 auto file_contents = TRY(file->read_until_eof()); 251 auto json = TRY(JsonValue::from_string(file_contents)); 252 253 Vector<JsonValue> sorted_regions = json.as_array().values(); 254 quick_sort(sorted_regions, [](auto& a, auto& b) { 255 return a.as_object().get_u32("local_port"sv).value_or(0) < b.as_object().get_u32("local_port"sv).value_or(0); 256 }); 257 258 for (auto& value : sorted_regions) { 259 auto& if_object = value.as_object(); 260 261 auto local_address = if_object.get_deprecated_string("local_address"sv).value_or({}); 262 if (!flag_numeric) { 263 auto from_string = IPv4Address::from_string(local_address); 264 auto addr = from_string.value().to_in_addr_t(); 265 auto* hostent = gethostbyaddr(&addr, sizeof(in_addr), AF_INET); 266 if (hostent != nullptr) { 267 auto host_name = StringView { hostent->h_name, strlen(hostent->h_name) }; 268 if (!host_name.is_empty()) 269 local_address = host_name; 270 } 271 } 272 273 auto local_port = if_object.get_deprecated_string("local_port"sv).value_or({}); 274 if (!flag_numeric) { 275 auto service = getservbyport(htons(if_object.get_u32("local_port"sv).value_or(0)), "udp"); 276 if (service != nullptr) { 277 auto s_name = StringView { service->s_name, strlen(service->s_name) }; 278 if (!s_name.is_empty()) 279 local_port = s_name; 280 } 281 } 282 283 auto peer_address = if_object.get_deprecated_string("peer_address"sv).value_or({}); 284 if (!flag_numeric) { 285 auto from_string = IPv4Address::from_string(peer_address); 286 auto addr = from_string.value().to_in_addr_t(); 287 auto* hostent = gethostbyaddr(&addr, sizeof(in_addr), AF_INET); 288 if (hostent != nullptr) { 289 auto host_name = StringView { hostent->h_name, strlen(hostent->h_name) }; 290 if (!host_name.is_empty()) 291 peer_address = host_name; 292 } 293 } 294 295 auto peer_port = if_object.get_deprecated_string("peer_port"sv).value_or({}); 296 if (!flag_numeric) { 297 auto service = getservbyport(htons(if_object.get_u32("peer_port"sv).value_or(0)), "udp"); 298 if (service != nullptr) { 299 auto s_name = StringView { service->s_name, strlen(service->s_name) }; 300 if (!s_name.is_empty()) 301 peer_port = s_name; 302 } 303 } 304 305 auto origin_pid = (if_object.has("origin_pid"sv)) ? if_object.get_u32("origin_pid"sv).value_or(0) : -1; 306 307 if (protocol_column != -1) 308 columns[protocol_column].buffer = "udp"; 309 if (bytes_in_column != -1) 310 columns[bytes_in_column].buffer = "-"; 311 if (bytes_out_column != -1) 312 columns[bytes_out_column].buffer = "-"; 313 if (local_address_column != -1) 314 columns[local_address_column].buffer = get_formatted_address(local_address, local_port); 315 if (peer_address_column != -1) 316 columns[peer_address_column].buffer = get_formatted_address(peer_address, peer_port); 317 if (state_column != -1) 318 columns[state_column].buffer = "-"; 319 if (flag_program && program_column != -1) 320 columns[program_column].buffer = get_formatted_program(origin_pid); 321 322 for (auto& column : columns) 323 print_column(column, column.buffer); 324 outln(); 325 }; 326 } 327 328 return 0; 329}