1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
|
/*
* parser.c - argument parser & dispatcher module - implementation
*
* nc6 - an advanced netcat clone
* Copyright (C) 2001-2003 Mauro Tortonesi <mauro _at_ deepspace6.net>
* Copyright (C) 2002-2003 Chris Leishman <chris _at_ leishman.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include "parser.h"
#include "misc.h"
#include "network.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netdb.h>
#include <getopt.h>
RCSID("@(#) $Header: /ds6/cvs//nc6/src/parser.c,v 1.53 2003/03/27 09:09:49 chris Exp $");
/* default UDP MTU is 8kb */
static const size_t DEFAULT_UDP_MTU = 8192;
/* default UDP NRU is the maximum allowed MTU of 64k */
static const size_t DEFAULT_UDP_NRU = 65536;
/* default UDP buffer size is 128k */
static const size_t DEFAULT_UDP_BUFFER_SIZE = 131072;
/* default buffer size for file transfers is 64k */
static const size_t DEFAULT_FILE_TRANSFER_BUFFER_SIZE = 65536;
/* these *VERBOSE* constants are defined here because they are not used
* in any other module */
static const int VERBOSE_MODE = 0x01;
static const int VERY_VERBOSE_MODE = 0x02;
/* storage for the global flags */
static int _verbosity_level = 0;
/* long options */
static const struct option long_options[] = {
#define OPT_HELP 0
{"help", FALSE, NULL, 'h'},
#define OPT_VERSION 1
{"version", FALSE, NULL, 0 },
#define OPT_LISTEN 2
{"listen", FALSE, NULL, 'l'},
#define OPT_PORT 3
{"port", TRUE, NULL, 'p'},
#define OPT_HOLD_TIMEOUT 4
{"hold-timeout", TRUE, NULL, 'q'},
#define OPT_ADDRESS 5
{"address", TRUE, NULL, 's'},
#define OPT_UDP 6
{"udp", FALSE, NULL, 'u'},
#define OPT_TIMEOUT 7
{"timeout", TRUE, NULL, 'w'},
#define OPT_IDLE_TIMEOUT 8
{"idle-timeout", TRUE, NULL, 't'},
#define OPT_TRANSFER 9
{"transfer", FALSE, NULL, 'x'},
#define OPT_RECV_ONLY 10
{"recv-only", FALSE, NULL, 0 },
#define OPT_SEND_ONLY 11
{"send-only", FALSE, NULL, 0 },
#define OPT_BUFFER_SIZE 12
{"buffer-size", TRUE, NULL, 0 },
#define OPT_MTU 13
{"mtu", TRUE, NULL, 0 },
#define OPT_NRU 14
{"nru", TRUE, NULL, 0 },
#define OPT_HALF_CLOSE 15
{"half-close", FALSE, NULL, 0 },
#define OPT_DISABLE_NAGLE 16
{"disable-nagle", FALSE, NULL, 0 },
#define OPT_NO_REUSEADDR 17
{"no-reuseaddr", FALSE, NULL, 0 },
#define OPT_SNDBUF_SIZE 18
{"sndbuf-size", TRUE, NULL, 0 },
#define OPT_RCVBUF_SIZE 19
{"rcvbuf-size", TRUE, NULL, 0 },
#define OPT_EXEC 20
{"exec", TRUE, NULL, 'e'},
#define OPT_CONTINUOUS 21
{"continuous", FALSE, NULL, 0 },
#define OPT_MAX 22
{0, 0, 0, 0}
};
static int parse_int_pair(const char *str, int *first, int *second);
static void print_usage(FILE *fp);
static void print_version(FILE *fp);
void parse_arguments(int argc, char **argv, connection_attributes *attrs)
{
int c;
int option_index = 0;
/* configurable parameters and default values */
sock_family family = PROTO_UNSPECIFIED;
sock_protocol protocol = TCP_PROTOCOL;
address local_address, remote_address;
bool listen_mode = FALSE;
bool file_transfer = FALSE;
bool half_close = FALSE;
int connect_timeout = -1;
int idle_timeout = -1;
bool set_local_hold_timeout = FALSE;
int local_hold_timeout;
bool set_remote_hold_timeout = FALSE;
int remote_hold_timeout;
size_t remote_mtu = 0;
size_t remote_nru = 0;
size_t buffer_size = 0;
size_t sndbuf_size = 0;
size_t rcvbuf_size = 0;
/* check arguments */
assert(argc > 0);
assert(argv != NULL);
assert(*argv != NULL);
assert(attrs != NULL);
/* initialize the addresses of the connection endpoints */
address_init(&remote_address);
address_init(&local_address);
/* set verbosity back to 0 */
_verbosity_level = 0;
/* option recognition loop */
while ((c = getopt_long(argc, argv, "46ehlnp:q:s:uvw:x",
long_options, &option_index)) >= 0)
{
switch (c) {
case 0:
switch (option_index) {
case OPT_VERSION:
print_version(stdout);
exit(EXIT_SUCCESS);
case OPT_RECV_ONLY:
ca_set_flag(attrs, CA_RECV_DATA_ONLY);
break;
case OPT_SEND_ONLY:
ca_set_flag(attrs, CA_SEND_DATA_ONLY);
break;
case OPT_BUFFER_SIZE:
assert(optarg != NULL);
buffer_size = safe_atoi(optarg);
break;
case OPT_MTU:
assert(optarg != NULL);
remote_mtu = safe_atoi(optarg);
break;
case OPT_NRU:
assert(optarg != NULL);
remote_nru = safe_atoi(optarg);
break;
case OPT_HALF_CLOSE:
half_close = TRUE;
break;
case OPT_DISABLE_NAGLE:
ca_set_flag(attrs, CA_DISABLE_NAGLE);
break;
case OPT_NO_REUSEADDR:
ca_set_flag(attrs, CA_DONT_REUSE_ADDR);
break;
case OPT_SNDBUF_SIZE:
assert(optarg != NULL);
sndbuf_size = safe_atoi(optarg);
break;
case OPT_RCVBUF_SIZE:
assert(optarg != NULL);
rcvbuf_size = safe_atoi(optarg);
break;
case OPT_CONTINUOUS:
ca_set_flag(attrs, CA_CONTINUOUS_ACCEPT);
break;
default:
fatal(_("getopt returned unexpected long "
"option offset index %d\n"), option_index);
}
break;
case '4':
family = PROTO_IPv4;
break;
case '6':
family = PROTO_IPv6;
ca_set_flag(attrs, CA_STRICT_IPV6);
break;
case 'e':
assert(optarg != NULL);
ca_set_local_exec(attrs, optarg);
break;
case 'h':
print_usage(stdout);
exit(EXIT_SUCCESS);
case 'l':
listen_mode = TRUE;
break;
case 'n':
ca_set_flag(attrs, CA_NUMERIC_MODE);
break;
case 'p':
assert(optarg != NULL);
local_address.service = xstrdup(optarg);
break;
case 'q':
assert(optarg != NULL);
switch (parse_int_pair(optarg, &local_hold_timeout,
&remote_hold_timeout))
{
case 2: set_remote_hold_timeout = TRUE;
case 1: set_local_hold_timeout = TRUE;
};
break;
case 's':
assert(optarg != NULL);
local_address.address = xstrdup(optarg);
break;
case 't':
assert(optarg != NULL);
idle_timeout = safe_atoi(optarg);
break;
case 'u':
protocol = UDP_PROTOCOL;
/* set remote buffer sizes and mtu's, iff they haven't
* already been set */
if (remote_mtu == 0)
remote_mtu = DEFAULT_UDP_MTU;
if (remote_nru == 0)
remote_nru = DEFAULT_UDP_NRU;
if (buffer_size == 0)
buffer_size = DEFAULT_UDP_BUFFER_SIZE;
break;
case 'v':
++_verbosity_level;
break;
case 'w':
assert(optarg != NULL);
connect_timeout = safe_atoi(optarg);
break;
case 'x':
file_transfer = TRUE;
break;
case '?':
print_usage(stderr);
exit(EXIT_FAILURE);
default:
fatal(_("getopt returned unexpected character 0%o\n"), c);
}
}
argv += optind;
argc -= optind;
/* set mode flags */
if (listen_mode == TRUE) {
ca_set_flag(attrs, CA_LISTEN_MODE);
ca_clear_flag(attrs, CA_CONNECT_MODE);
} else {
ca_set_flag(attrs, CA_CONNECT_MODE);
ca_clear_flag(attrs, CA_LISTEN_MODE);
}
/* setup file transfer depending on the mode */
if (file_transfer == TRUE) {
if (buffer_size == 0)
buffer_size = DEFAULT_FILE_TRANSFER_BUFFER_SIZE;
if (listen_mode == TRUE) {
ca_set_flag(attrs, CA_RECV_DATA_ONLY);
ca_clear_flag(attrs, CA_SEND_DATA_ONLY);
} else {
ca_set_flag(attrs, CA_SEND_DATA_ONLY);
ca_clear_flag(attrs, CA_RECV_DATA_ONLY);
}
}
/* check nru - if it's too big data will never be received */
if (remote_nru > buffer_size)
remote_nru = buffer_size;
/* check to make sure the user wasn't silly enough to set both
* --recv-only and --send-only */
if (ca_is_flag_set(attrs, CA_RECV_DATA_ONLY) &&
ca_is_flag_set(attrs, CA_SEND_DATA_ONLY))
{
fatal(_("Cannot set both --recv-only and --send-only"));
}
/* additional arguments are the remote address/service */
switch (argc) {
case 0:
remote_address.address = NULL;
remote_address.service = NULL;
break;
case 1:
remote_address.address = argv[0];
remote_address.service = NULL;
break;
case 2:
remote_address.address = argv[0];
remote_address.service = argv[1];
break;
default:
print_usage(stderr);
exit(EXIT_FAILURE);
}
/* sanity checks */
if (remote_address.address != NULL &&
strlen(remote_address.address) == 0)
{
remote_address.address = NULL;
}
if (remote_address.service != NULL &&
strlen(remote_address.service) == 0)
{
remote_address.service = NULL;
}
if (listen_mode == TRUE) {
if (local_address.service == NULL) {
warn(_("in listen mode you must specify a port "
"with the -p switch"));
print_usage(stderr);
exit(EXIT_FAILURE);
}
if (ca_is_flag_set(attrs, CA_CONTINUOUS_ACCEPT) &&
ca_local_exec(attrs) == NULL)
{
warn(_("--continuous option "
"must be used with --exec"));
print_usage(stderr);
exit(EXIT_FAILURE);
}
} else {
if (ca_is_flag_set(attrs, CA_DONT_REUSE_ADDR)) {
warn(_("--no-reuseaddr option "
"can be used only in listen mode"));
print_usage(stderr);
exit(EXIT_FAILURE);
}
if (ca_is_flag_set(attrs, CA_CONTINUOUS_ACCEPT)) {
warn(_("--continuous option "
"can be used only in listen mode"));
print_usage(stderr);
exit(EXIT_FAILURE);
}
if (remote_address.address == NULL ||
remote_address.service == NULL)
{
warn(_("you must specify the address/port couple "
"of the remote endpoint"));
print_usage(stderr);
exit(EXIT_FAILURE);
}
}
/* setup attrs */
ca_set_family(attrs, family);
ca_set_protocol(attrs, protocol);
ca_set_remote_addr(attrs, remote_address);
ca_set_local_addr(attrs, local_address);
/* setup connection timeout */
if (connect_timeout != -1)
ca_set_connect_timeout(attrs, connect_timeout);
/* setup idle timeout */
if (idle_timeout != -1)
ca_set_idle_timeout(attrs, idle_timeout);
/* setup half close mode */
if (half_close == TRUE) {
/* keep remote open after half close */
ca_set_remote_half_close_suppress(attrs, FALSE);
ca_set_remote_hold_timeout(attrs, -1);
}
/* setup hold timeout */
if (set_remote_hold_timeout == TRUE)
ca_set_remote_hold_timeout(attrs, remote_hold_timeout);
if (set_local_hold_timeout == TRUE)
ca_set_local_hold_timeout(attrs, local_hold_timeout);
/* setup mtu, nru, and buffer sizes if they were specified */
if (remote_mtu > 0)
ca_set_remote_MTU(attrs, remote_mtu);
if (remote_nru > 0)
ca_set_remote_NRU(attrs, remote_nru);
if (buffer_size > 0)
ca_set_buffer_size(attrs, buffer_size);
if (sndbuf_size > 0)
ca_set_sndbuf_size(attrs, sndbuf_size);
if (rcvbuf_size > 0)
ca_set_rcvbuf_size(attrs, rcvbuf_size);
}
bool verbose_mode()
{
return ((_verbosity_level >= VERBOSE_MODE) ? TRUE : FALSE);
}
bool very_verbose_mode()
{
return ((_verbosity_level >= VERY_VERBOSE_MODE) ? TRUE : FALSE);
}
static void print_usage(FILE *fp)
{
const char *program_name = get_program_name();
assert(fp != NULL);
assert(program_name != NULL);
fprintf(fp, _("Usage:\n"
"\t%s [options...] hostname port\n"
"\t%s -l -p port [-s addr] [options...] [hostname] [port]\n\n"
"Recognized options are:\n"), program_name, program_name);
fprintf(fp, _(
" -4 Use only IPv4\n"
" -6 Use only IPv6\n"
" --buffer-size=BYTES Set buffer size\n"
" --continuous Continuously accept connections\n"
" (only in listen mode with --exec)\n"
" --disable-nagle Disable nagle algorithm for TCP connections\n"
" -e, --exec=CMD Exec command after connect\n"
" --half-close Handle network half-closes correctly\n"
" -h, --help Display help\n"
" -l, --listen Listen mode, for inbound connects\n"
" --mtu=BYTES Set MTU for network connection transmits\n"
" -n Numeric-only IP addresses, no DNS\n"
" --no-reuseaddr Disable SO_REUSEADDR socket option\n"
" (only in listen mode)\n"
" --nru=BYTES Set NRU for network connection receives\n"
" -p, --port=PORT Local source port\n"
" -q, --hold-timeout=SEC1[:SEC2]\n"
" Set hold timeout(s) for local [and remote]\n"
" --rcvbuf-size Kernel receive buffer size for network sockets\n"
" --recv-only Only receive data, don't transmit\n"
" -s, --address=ADDRESS Local source address\n"
" --send-only Only transmit data, don't receive\n"
" --sndbuf-size Kernel send buffer size for network sockets\n"
" -t, --idle-timeout=SECONDS\n"
" Idle connection timeout\n"
" -u, --udp Require use of UDP\n"
" -v Increase program verbosity\n"
" (call twice for max verbosity)\n"
" --version Display nc6 version information\n"
" -w, --timeout=SECONDS Timeout for connects/accepts\n"
" -x, --transfer File transfer mode\n"
"\n"));
}
static void print_version(FILE *fp)
{
assert(fp != NULL);
fprintf(fp, _(
"%s version %s\n"
"Copyright (C) 2001-2003\n"), PACKAGE, VERSION);
fprintf(fp,
"\tMauro Tortonesi\n"
"\tChris Leishman\n"
"\tSimone Piunno\n"
"<http://www.deepspace6.net>\n");
#ifdef ENABLE_IPV6
fprintf(fp,
_("Configured with IPv6 support\n"));
#else
fprintf(fp,
_("Configured without IPv6 support\n"));
#endif
}
static int parse_int_pair(const char *str, int *first, int *second)
{
char *s;
int count = 1;
assert(str != NULL);
if ((s = strchr(str, ':')) != NULL) {
*s++ = '\0';
if (second != NULL)
*second = (s[0] == '-')? -1 : safe_atoi(s);
count = 2;
}
if (first != NULL)
*first = (str[0] == '-')? -1 : safe_atoi(str);
return count;
}
|