Serenity Operating System
at master 292 lines 10 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/ByteBuffer.h> 8#include <AK/HashTable.h> 9#include <LibCore/ArgsParser.h> 10#include <LibCore/EventLoop.h> 11#include <LibCore/Socket.h> 12#include <LibCore/System.h> 13#include <LibMain/Main.h> 14#include <arpa/inet.h> 15#include <errno.h> 16#include <netdb.h> 17#include <netinet/in.h> 18#include <stdio.h> 19#include <string.h> 20#include <sys/select.h> 21#include <sys/socket.h> 22#include <sys/time.h> 23#include <sys/types.h> 24#include <unistd.h> 25 26// NOTE: `warnln` is used instead of `outln` because we want to redirect all 27// output to stderr to allow for commands like: 28// 29// nc -l someport > out.file 30// 31// Below man page was considered to come up default bounds 32// for SO_RCVBUF 33// https://man7.org/linux/man-pages/man7/socket.7.html 34static constexpr size_t maximum_tcp_receive_buffer_size_upper_bound = 212992; 35static constexpr size_t maximum_tcp_receive_buffer_size_lower_bound = 256; 36 37static size_t get_maximum_tcp_buffer_size(size_t input_buf_size) 38{ 39 if (input_buf_size < maximum_tcp_receive_buffer_size_lower_bound) 40 return maximum_tcp_receive_buffer_size_lower_bound; 41 if (input_buf_size > maximum_tcp_receive_buffer_size_upper_bound) 42 return maximum_tcp_receive_buffer_size_upper_bound; 43 return input_buf_size; 44}; 45 46ErrorOr<int> serenity_main(Main::Arguments arguments) 47{ 48 bool should_listen = false; 49 bool verbose = false; 50 bool should_close = false; 51 bool udp_mode = false; 52 DeprecatedString target; 53 int port = 0; 54 int maximum_tcp_receive_buffer_size_input = -1; 55 56 Core::ArgsParser args_parser; 57 args_parser.set_general_help("Network cat: Connect to network sockets as if it were a file."); 58 args_parser.add_option(should_listen, "Listen instead of connecting", "listen", 'l'); 59 args_parser.add_option(verbose, "Log everything that's happening", "verbose", 'v'); 60 args_parser.add_option(udp_mode, "UDP mode", "udp", 'u'); 61 args_parser.add_option(should_close, "Close connection after reading stdin to the end", nullptr, 'N'); 62 args_parser.add_option(maximum_tcp_receive_buffer_size_input, "Set maximum tcp receive buffer size", "length", 'I', nullptr); 63 args_parser.add_positional_argument(target, "Address to listen on, or the address or hostname to connect to", "target"); 64 args_parser.add_positional_argument(port, "Port to connect to or listen on", "port"); 65 args_parser.parse(arguments); 66 67 if (udp_mode) { 68 if (should_listen) { 69 warnln("listening on UDP not yet supported"); 70 return 1; 71 } 72 73 Core::EventLoop loop; 74 auto socket = TRY(Core::UDPSocket::connect(target, port)); 75 76 if (verbose) 77 warnln("connected to {}:{}", target, port); 78 79 Array<u8, 1024> buffer; 80 for (;;) { 81 Bytes buffer_span = buffer.span(); 82 auto nread = TRY(Core::System::read(STDIN_FILENO, buffer_span)); 83 buffer_span = buffer_span.trim(nread); 84 85 TRY(socket->write_until_depleted({ buffer_span.data(), static_cast<size_t>(nread) })); 86 } 87 } 88 89 int fd = -1; 90 int listen_fd = -1; 91 92 if (should_listen) { 93 listen_fd = TRY(Core::System::socket(AF_INET, SOCK_STREAM, 0)); 94 95 sockaddr_in sa {}; 96 sa.sin_family = AF_INET; 97 sa.sin_port = htons(port); 98 sa.sin_addr.s_addr = htonl(INADDR_ANY); 99 if (!target.is_empty()) { 100 if (inet_pton(AF_INET, target.characters(), &sa.sin_addr) <= 0) { 101 perror("inet_pton"); 102 return 1; 103 } 104 } 105 106 TRY(Core::System::bind(listen_fd, (struct sockaddr*)&sa, sizeof(sa))); 107 TRY(Core::System::listen(listen_fd, 1)); 108 109 char addr_str[INET_ADDRSTRLEN]; 110 sockaddr_in sin; 111 socklen_t len; 112 113 len = sizeof(sin); 114 TRY(Core::System::getsockname(listen_fd, (struct sockaddr*)&sin, &len)); 115 116 if (verbose) 117 warnln("waiting for a connection on {}:{}", inet_ntop(sin.sin_family, &sin.sin_addr, addr_str, sizeof(addr_str) - 1), ntohs(sin.sin_port)); 118 119 } else { 120 fd = TRY(Core::System::socket(AF_INET, SOCK_STREAM, 0)); 121 122 struct timeval timeout { 123 3, 0 124 }; 125 TRY(Core::System::setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout))); 126 TRY(Core::System::setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout))); 127 128 auto* hostent = gethostbyname(target.characters()); 129 if (!hostent) { 130 warnln("Socket::connect: Unable to resolve '{}'", target); 131 return 1; 132 } 133 134 sockaddr_in dst_addr {}; 135 dst_addr.sin_family = AF_INET; 136 dst_addr.sin_port = htons(port); 137 dst_addr.sin_addr.s_addr = *(in_addr_t const*)hostent->h_addr_list[0]; 138 139 if (verbose) { 140 char addr_str[INET_ADDRSTRLEN]; 141 warnln("connecting to {}:{}", inet_ntop(dst_addr.sin_family, &dst_addr.sin_addr, addr_str, sizeof(addr_str) - 1), ntohs(dst_addr.sin_port)); 142 } 143 144 TRY(Core::System::connect(fd, (struct sockaddr*)&dst_addr, sizeof(dst_addr))); 145 if (verbose) 146 warnln("connected!"); 147 } 148 149 HashTable<int> connected_clients; 150 151 bool stdin_closed = false; 152 bool fd_closed = false; 153 bool listen_fd_closed = false; 154 155 fd_set readfds, writefds, exceptfds; 156 157 size_t receive_buffer_size = get_maximum_tcp_buffer_size(maximum_tcp_receive_buffer_size_input); 158 if (verbose && (maximum_tcp_receive_buffer_size_input != -1)) { 159 warnln("receive_buffer_size set to {}", receive_buffer_size); 160 } 161 162 while (!stdin_closed || !fd_closed || !listen_fd_closed) { 163 FD_ZERO(&readfds); 164 FD_ZERO(&writefds); 165 FD_ZERO(&exceptfds); 166 167 int highest_fd = 0; 168 169 if (!stdin_closed) { 170 FD_SET(STDIN_FILENO, &readfds); 171 FD_SET(STDIN_FILENO, &exceptfds); 172 highest_fd = max(highest_fd, STDIN_FILENO); 173 } 174 if (!fd_closed && fd) { 175 FD_SET(fd, &readfds); 176 FD_SET(fd, &exceptfds); 177 highest_fd = max(highest_fd, fd); 178 } 179 180 if (!listen_fd_closed && listen_fd) { 181 FD_SET(listen_fd, &readfds); 182 FD_SET(listen_fd, &exceptfds); 183 highest_fd = max(highest_fd, listen_fd); 184 } 185 186 bool has_clients = (should_listen && !connected_clients.is_empty()); 187 if (has_clients) { 188 for (auto const& client_fd : connected_clients) { 189 FD_SET(client_fd, &readfds); 190 FD_SET(client_fd, &exceptfds); 191 highest_fd = max(highest_fd, client_fd); 192 } 193 } 194 195 int ready = select(highest_fd + 1, &readfds, &writefds, &exceptfds, nullptr); 196 if (ready == -1) { 197 if (errno == EINTR) 198 continue; 199 200 perror("select"); 201 return 1; 202 } 203 204 if (!stdin_closed && FD_ISSET(STDIN_FILENO, &readfds)) { 205 Array<u8, 1024> buffer; 206 Bytes buffer_span = buffer.span(); 207 auto nread = TRY(Core::System::read(STDIN_FILENO, buffer_span)); 208 buffer_span = buffer_span.trim(nread); 209 210 // stdin closed 211 if (nread == 0) { 212 stdin_closed = true; 213 if (verbose) 214 warnln("stdin closed"); 215 if (should_close) { 216 if (should_listen) { 217 TRY(Core::System::close(listen_fd)); 218 listen_fd_closed = true; 219 } else { 220 TRY(Core::System::close(fd)); 221 fd_closed = true; 222 } 223 } 224 } else { 225 if (should_listen && has_clients) { 226 for (auto const& client_fd : connected_clients) 227 TRY(Core::System::write(client_fd, buffer_span)); 228 } else { 229 TRY(Core::System::write(fd, buffer_span)); 230 } 231 } 232 } 233 234 if (!fd_closed && FD_ISSET(fd, &readfds)) { 235 auto buffer = TRY(ByteBuffer::create_uninitialized(receive_buffer_size)); 236 Bytes buffer_span = buffer.bytes(); 237 auto nread = TRY(Core::System::read(fd, buffer_span)); 238 buffer_span = buffer_span.trim(nread); 239 240 // remote end closed 241 if (nread == 0) { 242 close(STDIN_FILENO); 243 stdin_closed = true; 244 fd_closed = true; 245 if (verbose) 246 warnln("remote closed"); 247 } else { 248 TRY(Core::System::write(STDOUT_FILENO, buffer_span)); 249 } 250 } 251 252 if (!listen_fd_closed && FD_ISSET(listen_fd, &readfds)) { 253 char client_str[INET_ADDRSTRLEN]; 254 sockaddr_in client; 255 socklen_t clientlen = sizeof(client); 256 257 int new_client = TRY(Core::System::accept(listen_fd, (struct sockaddr*)&client, &clientlen)); 258 connected_clients.set(new_client); 259 260 if (verbose) 261 warnln("got connection from {}:{}", inet_ntop(client.sin_family, &client.sin_addr, client_str, sizeof(client_str) - 1), ntohs(client.sin_port)); 262 } 263 264 if (has_clients) { 265 for (auto const client_fd : connected_clients) { 266 if (FD_ISSET(client_fd, &readfds)) { 267 Array<u8, 1024> buffer; 268 Bytes buffer_span = buffer.span(); 269 auto nread = TRY(Core::System::read(client_fd, buffer_span)); 270 buffer_span = buffer_span.trim(nread); 271 272 if (nread == 0) { 273 if (verbose) { 274 struct sockaddr_in client; 275 socklen_t clientlen = sizeof(client); 276 TRY(Core::System::getpeername(client_fd, (struct sockaddr*)&client, &clientlen)); 277 warnln("remote connection closed {}:{}", inet_ntoa(client.sin_addr), ntohs(client.sin_port)); 278 } 279 connected_clients.remove(client_fd); 280 close(client_fd); 281 FD_CLR(client_fd, &readfds); 282 FD_CLR(client_fd, &exceptfds); 283 } else { 284 TRY(Core::System::write(STDOUT_FILENO, buffer_span)); 285 } 286 } 287 } 288 } 289 } 290 291 return 0; 292}