/* * parser.c - argument parser & dispatcher module - implementation * * nc6 - an advanced netcat clone * Copyright (C) 2001-2006 Mauro Tortonesi * Copyright (C) 2002-2006 Chris Leishman * * 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 #include #include #include #include #include #include #include #include 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" "\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; }