netstat.cpp 13 KB

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