/*
* parser.c - argument parser & dispatcher module - implementation
*
* nc6 - an advanced netcat clone
* Copyright (C) 2001-2006 Mauro Tortonesi <mauro _at_ deepspace6.net>
* Copyright (C) 2002-2006 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 "system.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.67 2006/01/19 22:46:23 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 BLUETOOTH MTU is 672b */
static const size_t DEFAULT_BLUETOOTH_MTU = 672;
/* 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", no_argument, NULL, 'h'},
#define OPT_VERSION 1
{"version", no_argument, NULL, 0 },
#define OPT_LISTEN 2
{"listen", no_argument, NULL, 'l'},
#define OPT_PORT 3
{"port", required_argument, NULL, 'p'},
#define OPT_HOLD_TIMEOUT 4
{"hold-timeout", required_argument, NULL, 'q'},
#define OPT_ADDRESS 5
{"address", required_argument, NULL, 's'},
#define OPT_UDP 6
{"udp", no_argument, NULL, 'u'},
#define OPT_TIMEOUT 7
{"timeout", required_argument, NULL, 'w'},
#define OPT_IDLE_TIMEOUT 8
{"idle-timeout", required_argument, NULL, 't'},
#define OPT_TRANSFER 9
{"transfer", no_argument, NULL, 'x'},
#define OPT_REV_TRANSFER 10
{"rev-transfer", no_argument, NULL, 'X'},
#define OPT_RECV_ONLY 11
{"recv-only", no_argument, NULL, 0 },
#define OPT_SEND_ONLY 12
{"send-only", no_argument, NULL, 0 },
#define OPT_BUFFER_SIZE 13
{"buffer-size", required_argument, NULL, 0 },
#define OPT_MTU 14
{"mtu", required_argument, NULL, 0 },
#define OPT_NRU 15
{"nru", required_argument, NULL, 0 },
#define OPT_HALF_CLOSE 16
{"half-close", no_argument, NULL, 0 },
#define OPT_DISABLE_NAGLE 17
{"disable-nagle", no_argument, NULL, 0 },
#define OPT_NO_REUSEADDR 18
{"no-reuseaddr", no_argument, NULL, 0 },
#define OPT_SNDBUF_SIZE 19
{"sndbuf-size", required_argument, NULL, 0 },
#define OPT_RCVBUF_SIZE 20
{"rcvbuf-size", required_argument, NULL, 0 },
#define OPT_EXEC 21
{"exec", required_argument, NULL, 'e'},
#define OPT_CONTINUOUS 22
{"continuous", no_argument, NULL, 0 },
#define OPT_BLUETOOTH 23
{"bluetooth", no_argument, NULL, 'b'},
#define OPT_SCO 24
{"sco", no_argument, NULL, 0 },
#define OPT_MAX 25
{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_t *attrs)
{
int c;
int option_index = 0;
/* configurable parameters and default values */
sock_family_t family = PROTO_UNSPECIFIED;
sock_protocol_t protocol = PROTO_UNSPECIFIED;
address_t local_address, remote_address;
bool listen_mode = false;
bool file_transfer = false;
bool rev_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 = 0;
bool set_remote_hold_timeout = false;
int remote_hold_timeout = 0;
int remote_mtu = 0;
int remote_nru = 0;
int buffer_size = 0;
int sndbuf_size = 0;
int 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, "46be:hlnp:q:s:uvw:xX",
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);
if (safe_atoi(optarg, &buffer_size))
fatal(_("invalid argument to "
"--buffer-size"));
break;
case OPT_MTU:
assert(optarg != NULL);
if (safe_atoi(optarg, &remote_mtu))
fatal(_("invalid argument to --mtu"));
break;
case OPT_NRU:
assert(optarg != NULL);
if (safe_atoi(optarg, &remote_nru))
fatal(_("invalid argument to --nru"));
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);
if (safe_atoi(optarg, &sndbuf_size))
fatal(_("invalid argument to "
"--sndbuf-size"));
break;
case OPT_RCVBUF_SIZE:
assert(optarg != NULL);
if (safe_atoi(optarg, &rcvbuf_size))
fatal(_("invalid argument to "
"--rcvbuf-size"));
break;
case OPT_CONTINUOUS:
ca_set_flag(attrs, CA_CONTINUOUS_ACCEPT);
break;
case OPT_SCO:
protocol = SCO_PROTOCOL;
break;
default:
fatal_internal(
"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 'b':
family = PROTO_BLUEZ;
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; /* continue */
case 1: set_local_hold_timeout = true; break;
default:
fatal(_("invalid argument to -q"));
};
break;
case 's':
assert(optarg != NULL);
local_address.address = xstrdup(optarg);
break;
case 't':
assert(optarg != NULL);
if (safe_atoi(optarg, &idle_timeout))
fatal(_("invalid argument to -t"));
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);
if (safe_atoi(optarg, &connect_timeout))
fatal(_("invalid argument to -w"));
break;
case 'x':
file_transfer = true;
break;
case 'X':
rev_file_transfer = true;
break;
case '?':
print_usage(stderr);
exit(EXIT_FAILURE);
default:
fatal_internal(
"getopt returned unexpected character 0%o\n", c);
}
}
argv += optind;
argc -= optind;
/* 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);
}
/* set default protocols */
if (protocol == PROTO_UNSPECIFIED) {
switch (family) {
case PROTO_BLUEZ:
protocol = L2CAP_PROTOCOL;
break;
default:
protocol = TCP_PROTOCOL;
break;
}
}
/* check protocol and family combinations are valid */
if (protocol == UDP_PROTOCOL && family == PROTO_BLUEZ)
fatal(_("cannot specify UDP protocol and bluetooth"));
if (protocol == SCO_PROTOCOL && family != PROTO_BLUEZ)
fatal(_("--sco requires --bluetooth (-b)"));
/* check compiled options */
#ifndef ENABLE_BLUEZ
if (family == PROTO_BLUEZ)
fatal(_("system does not support bluetooth"));
#endif
#ifndef ENABLE_IPV6
if (family == PROTO_IPv6)
fatal(_("system does not support IPv6"));
#endif
/* 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;
}
switch (family) {
case PROTO_UNSPECIFIED:
case PROTO_IPv4:
case PROTO_IPv6:
if (protocol != UDP_PROTOCOL && protocol != TCP_PROTOCOL)
fatal_internal("unknown/unsupported transport "
"protocol %d", protocol);
break;
case PROTO_BLUEZ:
if (protocol != SCO_PROTOCOL && protocol != L2CAP_PROTOCOL)
fatal_internal("unknown/unsupported bluetooth "
"protocol %d", protocol);
break;
default:
fatal_internal("invalid protocol family %d", family);
}
/* 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);
}
/* check to make sure the user didn't set both
* --transfer and --rev-transfer */
if (file_transfer == true && rev_file_transfer == true) {
fatal(_("cannot set both --transfer (-x) "
"and --rev-transfer (-X)"));
}
/* setup file transfer depending on the mode */
if (file_transfer == true || rev_file_transfer == true) {
if (buffer_size == 0)
buffer_size = DEFAULT_FILE_TRANSFER_BUFFER_SIZE;
if (XOR(rev_file_transfer == true, 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 didn't 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"));
}
/* check ports have not been specified with --sco */
if (protocol == SCO_PROTOCOL) {
if (remote_address.service != NULL)
fatal(_("--sco does not support remote port"));
if (local_address.service != NULL)
fatal(_("--sco does not support local port (-p)"));
}
/* check mode specific option availability and interactions */
if (listen_mode == true) {
/* check port has been specified (except with sco) */
if (local_address.service == NULL && protocol != SCO_PROTOCOL) {
fatal(_("in listen mode you must specify a port "
"with the -p switch"));
}
if (ca_is_flag_set(attrs, CA_CONTINUOUS_ACCEPT) &&
ca_local_exec(attrs) == NULL)
{
fatal(_("--continuous option "
"must be used with --exec"));
}
} else {
/* check port has been specified (except with sco) */
if (remote_address.address == NULL ||
(remote_address.service == NULL &&
protocol != SCO_PROTOCOL))
{
fatal(_("you must specify the address/port couple "
"of the remote endpoint"));
}
if (ca_is_flag_set(attrs, CA_DONT_REUSE_ADDR)) {
fatal(_("--no-reuseaddr option "
"can be used only in listen mode"));
}
if (ca_is_flag_set(attrs, CA_CONTINUOUS_ACCEPT)) {
fatal(_("--continuous option "
"can be used only in listen mode"));
}
}
/* set remote buffer sizes and mtu's,
* iff they haven't already been set */
if (protocol == UDP_PROTOCOL) {
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;
}
if (family == PROTO_BLUEZ) {
/* use standard bluetooth mtu */
if (remote_mtu == 0)
remote_mtu = DEFAULT_BLUETOOTH_MTU;
}
/* 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 %s\n", _("Use only IPv4"));
fprintf(fp, " -6 %s\n", _("Use only IPv6"));
fprintf(fp, " -b, --bluetooth %s\n",
_("Use Bluetooth (defaults to L2CAP protocol)"));
fprintf(fp, " --buffer-size=BYTES %s\n", _("Set buffer size"));
fprintf(fp, " --continuous %s\n",
_("Continuously accept connections\n"
" (only in listen mode with --exec)"));
fprintf(fp, " --disable-nagle %s\n",
_("Disable nagle algorithm for TCP connections"));
fprintf(fp, " -e, --exec=CMD %s\n",
_("Exec command after connect"));
fprintf(fp, " --half-close %s\n",
_("Handle network half-closes correctly"));
fprintf(fp, " -h, --help %s\n", _("Display help"));
fprintf(fp, " -l, --listen %s\n",
_("Listen mode, for inbound connects"));
fprintf(fp, " --mtu=BYTES %s\n",
_("Set MTU for network connection transmits"));
fprintf(fp, " -n %s\n",
_("Numeric-only IP addresses, no DNS"));
fprintf(fp, " --no-reuseaddr %s\n",
_("Disable SO_REUSEADDR socket option\n"
" (only in listen mode)\n"));
fprintf(fp, " --nru=BYTES %s\n",
_("Set NRU for network connection receives"));
fprintf(fp, " -p, --port=PORT %s\n", _("Local port"));
fprintf(fp, " -q, --hold-timeout=SEC1[:SEC2]\n"
" %s\n",
_("Set hold timeout(s) for local [and remote]"));
fprintf(fp, " --rcvbuf-size %s\n",
_("Kernel receive buffer size for network sockets"));
fprintf(fp, " --recv-only %s\n",
_("Only receive data, don't transmit"));
fprintf(fp, " -s, --address=ADDRESS %s\n", _("Local source address"));
fprintf(fp, " --sco %s\n",
_("Use SCO over Bluetooth"));
fprintf(fp, " --send-only %s\n",
_("Only transmit data, don't receive"));
fprintf(fp, " --sndbuf-size %s\n",
_("Kernel send buffer size for network sockets"));
fprintf(fp, " -t, --idle-timeout=SECONDS\n"
" %s\n", _("Idle connection timeout"));
fprintf(fp, " -u, --udp %s\n", _("Require use of UDP"));
fprintf(fp, " -v %s\n",
_("Increase program verbosity\n"
" (call twice for max verbosity)"));
fprintf(fp, " --version %s\n",
_("Display nc6 version information"));
fprintf(fp, " -w, --timeout=SECONDS %s\n",
_("Timeout for connects/accepts"));
fprintf(fp, " -x, --transfer %s\n", _("File transfer mode"));
fprintf(fp, " -X, --rev-transfer %s\n",
_("File transfer mode (reverse direction)"));
fprintf(fp, "\n");
}
static void print_version(FILE *fp)
{
assert(fp != NULL);
fprintf(fp,
"%s version %s\n"
"Copyright (C) 2001-2006\n", PACKAGE, VERSION);
fprintf(fp,
"\tMauro Tortonesi\n"
"\tChris Leishman\n"
"\tSimone Piunno\n"
"\tFilippo Natali\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
#ifdef ENABLE_BLUEZ
fprintf(fp,
_("Configured with Bluetooth (bluez) support\n"));
#else
fprintf(fp,
_("Configured without Bluetooth (bluez) 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) {
if (s[0] == '-')
*second = -1;
else if (safe_atoi(s, second))
return -1;
}
count = 2;
}
if (first != NULL) {
if (str[0] == '-')
*first = -1;
else if (safe_atoi(str, first))
return -1;
}
return count;
}
syntax highlighted by Code2HTML, v. 0.9.1