diff --git a/Base/usr/share/man/man1/nc.md b/Base/usr/share/man/man1/nc.md index 3ce46466fe3..1a31d5b6c35 100644 --- a/Base/usr/share/man/man1/nc.md +++ b/Base/usr/share/man/man1/nc.md @@ -5,7 +5,7 @@ nc ## Synopsis ```sh -$ nc [--length ] [--listen] [-N] [--udp] [--verbose] +$ nc [--length ] [--listen] [-N] [--udp] [-p port] [--verbose] [target] [port] ``` ## Description @@ -18,6 +18,7 @@ Network cat: Connect to network sockets as if it were a file. * `-l`, `--listen`: Listen instead of connecting * `-N`: Close connection after reading stdin to the end * `-u`, `--udp`: UDP mode +* `-p port`: Local port for remote connections * `-v`, `--verbose`: Log everything that's happening ## Arguments diff --git a/Userland/Utilities/nc.cpp b/Userland/Utilities/nc.cpp index 23b29f7900a..7ab51b5f9c6 100644 --- a/Userland/Utilities/nc.cpp +++ b/Userland/Utilities/nc.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2023, Fabian Dellwing * * SPDX-License-Identifier: BSD-2-Clause */ @@ -51,6 +52,7 @@ ErrorOr serenity_main(Main::Arguments arguments) bool udp_mode = false; DeprecatedString target; int port = 0; + int local_port = 0; int maximum_tcp_receive_buffer_size_input = -1; Core::ArgsParser args_parser; @@ -59,9 +61,10 @@ ErrorOr serenity_main(Main::Arguments arguments) args_parser.add_option(should_listen, "Listen instead of connecting", "listen", 'l'); args_parser.add_option(should_close, "Close connection after reading stdin to the end", nullptr, 'N'); args_parser.add_option(udp_mode, "UDP mode", "udp", 'u'); + args_parser.add_option(local_port, "Local port for remote connections", nullptr, 'p', "port"); args_parser.add_option(verbose, "Log everything that's happening", "verbose", 'v'); - args_parser.add_positional_argument(target, "Address to listen on, or the address or hostname to connect to", "target"); - args_parser.add_positional_argument(port, "Port to connect to or listen on", "port"); + args_parser.add_positional_argument(target, "Address to listen on, or the address or hostname to connect to", "target", Core::ArgsParser::Required::No); + args_parser.add_positional_argument(port, "Port to connect to or listen on", "port", Core::ArgsParser::Required::No); args_parser.parse(arguments); if (udp_mode) { @@ -90,11 +93,22 @@ ErrorOr serenity_main(Main::Arguments arguments) int listen_fd = -1; if (should_listen) { + if ((!target.is_empty() && local_port > 0) || (!target.is_empty() && port == 0)) { + args_parser.print_usage(stderr, arguments.strings[0]); + return 1; + } + listen_fd = TRY(Core::System::socket(AF_INET, SOCK_STREAM, 0)); sockaddr_in sa {}; sa.sin_family = AF_INET; - sa.sin_port = htons(port); + + if (local_port > 0) { + sa.sin_port = htons(local_port); + } else if (port > 0) { + sa.sin_port = htons(port); + } + sa.sin_addr.s_addr = htonl(INADDR_ANY); if (!target.is_empty()) { if (inet_pton(AF_INET, target.characters(), &sa.sin_addr) <= 0) { @@ -117,6 +131,11 @@ ErrorOr serenity_main(Main::Arguments arguments) warnln("waiting for a connection on {}:{}", inet_ntop(sin.sin_family, &sin.sin_addr, addr_str, sizeof(addr_str)), ntohs(sin.sin_port)); } else { + if (target.is_empty() || port == 0) { + args_parser.print_usage(stderr, arguments.strings[0]); + return 1; + } + fd = TRY(Core::System::socket(AF_INET, SOCK_STREAM, 0)); struct timeval timeout { @@ -136,6 +155,8 @@ ErrorOr serenity_main(Main::Arguments arguments) dst_addr.sin_port = htons(port); dst_addr.sin_addr.s_addr = *(in_addr_t const*)hostent->h_addr_list[0]; + // FIXME: Actually use the local_port for the outgoing connection once we have a working implementation of bind and connect + if (verbose) { char addr_str[INET_ADDRSTRLEN]; warnln("connecting to {}:{}", inet_ntop(dst_addr.sin_family, &dst_addr.sin_addr, addr_str, sizeof(addr_str)), ntohs(dst_addr.sin_port));