diff --git a/cli-runopts.c b/cli-runopts.c index 3654b9a32..e1915d040 100644 --- a/cli-runopts.c +++ b/cli-runopts.c @@ -1,19 +1,19 @@ /* * Dropbear - a SSH2 server - * + * * Copyright (c) 2002,2003 Matt Johnston * All rights reserved. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -52,61 +52,64 @@ static void printhelp() { fprintf(stderr, "Dropbear SSH client v%s https://matt.ucc.asn.au/dropbear/dropbear.html\n" #if DROPBEAR_CLI_MULTIHOP - "Usage: %s [options] [user@]host[/port][,[user@]host/port],...] [command]\n" + "Usage: %s [options] [user@]host[/port][,[user@]host/port],...] [command]\n" #else - "Usage: %s [options] [user@]host[/port] [command]\n" -#endif - "-p \n" - "-l \n" - "-t Allocate a pty\n" - "-T Don't allocate a pty\n" - "-N Don't run a remote command\n" - "-f Run in background after auth\n" - "-y Always accept remote host key if unknown\n" - "-y -y Don't perform any remote host key checking (caution)\n" - "-s Request a subsystem (use by external sftp)\n" - "-o option Set option in OpenSSH-like format ('-o help' to list options)\n" + "Usage: %s [options] [user@]host[/port] [command]\n" +#endif + "-p \n" + "-l \n" + "-t Allocate a pty\n" + "-T Don't allocate a pty\n" + "-N Don't run a remote command\n" + "-f Run in background after auth\n" +#ifndef DISABLE_ZLIB + "-n No compression\n" +#endif + "-y Always accept remote host key if unknown\n" + "-y -y Don't perform any remote host key checking (caution)\n" + "-s Request a subsystem (use by external sftp)\n" + "-o option Set option in OpenSSH-like format ('-o help' to list options)\n" #if DROPBEAR_CLI_PUBKEY_AUTH - "-i (multiple allowed, default %s)\n" + "-i (multiple allowed, default %s)\n" #endif #if DROPBEAR_CLI_AGENTFWD - "-A Enable agent auth forwarding\n" + "-A Enable agent auth forwarding\n" #endif #if DROPBEAR_CLI_LOCALTCPFWD - "-L <[listenaddress:]listenport:remotehost:remoteport> Local port forwarding\n" - "-g Allow remote hosts to connect to forwarded ports\n" + "-L <[listenaddress:]listenport:remotehost:remoteport> Local port forwarding\n" + "-g Allow remote hosts to connect to forwarded ports\n" #endif #if DROPBEAR_CLI_REMOTETCPFWD - "-R <[listenaddress:]listenport:remotehost:remoteport> Remote port forwarding\n" + "-R <[listenaddress:]listenport:remotehost:remoteport> Remote port forwarding\n" #endif - "-W (default %d, larger may be faster, max 1MB)\n" - "-K (0 is never, default %d)\n" - "-I (0 is never, default %d)\n" + "-W (default %d, larger may be faster, max 1MB)\n" + "-S{idletime} TCP Socket keepalive {optional TCP_KEEPIDLE must follow directly}\n" + "-K SSH channel keepalives (0 is never, default %d)\n" + "-I (0 is never, default %d)\n" #if DROPBEAR_CLI_NETCAT - "-B Netcat-alike forwarding\n" -#endif + "-B Netcat-alike forwarding\n" +#endif #if DROPBEAR_CLI_PROXYCMD - "-J Use program pipe rather than TCP connection\n" + "-J Use program pipe rather than TCP connection\n" #endif #if DROPBEAR_USER_ALGO_LIST - "-c Specify preferred ciphers ('-c help' to list options)\n" - "-m Specify preferred MACs for packet verification (or '-m help')\n" + "-c Specify preferred ciphers ('-c help' to list options)\n" + "-m Specify preferred MACs for packet verification (or '-m help')\n" #endif - "-b [bind_address][:bind_port]\n" - "-V Version\n" + "-b [bind_address][:bind_port]\n" + "-V Version\n" #if DEBUG_TRACE - "-v verbose (compiled with DEBUG_TRACE)\n" + "-v verbose (compiled with DEBUG_TRACE)\n" #endif - ,DROPBEAR_VERSION, cli_opts.progname, + ,DROPBEAR_VERSION, cli_opts.progname, #if DROPBEAR_CLI_PUBKEY_AUTH - DROPBEAR_DEFAULT_CLI_AUTHKEY, + DROPBEAR_DEFAULT_CLI_AUTHKEY, #endif - DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE, DEFAULT_IDLE_TIMEOUT); - + DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE, DEFAULT_IDLE_TIMEOUT); } void cli_getopts(int argc, char ** argv) { - unsigned int i, j; + unsigned i; char ** next = NULL; enum { OPT_EXTENDED_OPTIONS, @@ -186,15 +189,17 @@ void cli_getopts(int argc, char ** argv) { opts.recv_window = DEFAULT_RECV_WINDOW; opts.keepalive_secs = DEFAULT_KEEPALIVE; opts.idle_timeout_secs = DEFAULT_IDLE_TIMEOUT; + opts.tcp_keepalive = DEFAULT_TCP_ALIVE; fill_own_user(); for (i = 1; i < (unsigned int)argc; i++) { /* Handle non-flag arguments such as hostname or commands for the remote host */ - if (argv[i][0] != '-') + char *cursor = argv[i]; + if (*cursor != '-') { if (host_arg == NULL) { - host_arg = argv[i]; + host_arg = cursor; continue; } /* Commands to pass to the remote host. No more flag handling, @@ -204,7 +209,9 @@ void cli_getopts(int argc, char ** argv) { /* Begins with '-' */ opt = OPT_OTHER; - for (j = 1; (c = argv[i][j]) != '\0' && !next && opt == OPT_OTHER; j++) { + cursor++; + while ((c = *cursor) && !next && opt == OPT_OTHER) { + cursor++; switch (c) { case 'y': /* always accept the remote hostkey */ if (cli_opts.always_accept_key) { @@ -216,6 +223,11 @@ void cli_getopts(int argc, char ** argv) { case 'p': /* remoteport */ next = (char**)&cli_opts.remoteport; break; +#ifndef DISABLE_ZLIB + case 'n': /* disable stream compression */ + opts.compress_mode = DROPBEAR_COMPRESS_OFF; + break; +#endif #if DROPBEAR_CLI_PUBKEY_AUTH case 'i': /* an identityfile */ opt = OPT_AUTHKEY; @@ -278,6 +290,21 @@ void cli_getopts(int argc, char ** argv) { case 'K': next = &keepalive_arg; break; + case 'S': //require optional idletime follow directly! + opts.tcp_keepalive = -1; + { + char *end; + long idletime = strtol(cursor, &end, 10); + if (cursor != end) { + if (idletime < 1 || idletime >= (long)LONG_MAX) { + *end=0; + dropbear_exit("Bad TCP_KEEPIDLE '%s'", cursor); + } + opts.tcp_keepalive = idletime; + cursor = end; + } + } + break; case 'I': next = &idle_timeout_arg; break; @@ -329,50 +356,45 @@ void cli_getopts(int argc, char ** argv) { if (!next && opt == OPT_OTHER) /* got a flag */ continue; - if (c == '\0') { - i++; - j = 0; - if (!argv[i]) - dropbear_exit("Missing argument"); - } + if (!c && !(cursor = argv[++i])) + dropbear_exit("Missing argument"); if (opt == OPT_EXTENDED_OPTIONS) { TRACE(("opt extended")) - add_extendedopt(&argv[i][j]); + add_extendedopt(cursor); } else #if DROPBEAR_CLI_PUBKEY_AUTH if (opt == OPT_AUTHKEY) { TRACE(("opt authkey")) - loadidentityfile(&argv[i][j], 1); + loadidentityfile(cursor, 1); } else #endif #if DROPBEAR_CLI_REMOTETCPFWD if (opt == OPT_REMOTETCPFWD) { TRACE(("opt remotetcpfwd")) - addforward(&argv[i][j], cli_opts.remotefwds); + addforward(cursor, cli_opts.remotefwds); } else #endif #if DROPBEAR_CLI_LOCALTCPFWD if (opt == OPT_LOCALTCPFWD) { TRACE(("opt localtcpfwd")) - addforward(&argv[i][j], cli_opts.localfwds); + addforward(cursor, cli_opts.localfwds); } else #endif #if DROPBEAR_CLI_NETCAT if (opt == OPT_NETCAT) { TRACE(("opt netcat")) - add_netcat(&argv[i][j]); + add_netcat(cursor); } else #endif if (next) { /* The previous flag set a value to assign */ - *next = &argv[i][j]; - if (*next == NULL) + if (!(*next = cursor)) dropbear_exit("Invalid null argument"); next = NULL; } @@ -394,6 +416,7 @@ void cli_getopts(int argc, char ** argv) { if (i < (unsigned int)argc) { /* Build the command to send */ + unsigned j; cmdlen = 0; for (j = i; j < (unsigned int)argc; j++) cmdlen += strlen(argv[j]) + 1; /* +1 for spaces */ @@ -450,7 +473,7 @@ void cli_getopts(int argc, char ** argv) { && cli_opts.no_cmd == 0) { dropbear_exit("Command required for -f"); } - + if (recv_window_arg) { opts.recv_window = atol(recv_window_arg); if (opts.recv_window == 0 || opts.recv_window > MAX_RECV_WINDOW) { @@ -571,7 +594,7 @@ multihop_passthrough_args() { #endif /* DROPBEAR_CLI_PUBKEY_AUTH */ /* if args were passed, total will be not zero, and it will have a space at the end, so remove that */ - if (total > 0) + if (total > 0) { total--; } @@ -602,8 +625,8 @@ static void parse_multihop_hostname(const char* orighostarg, const char* argv0) * for our multihop syntax, so we suture it back together. * This will break usernames that have both '@' and ',' in them, * though that should be fairly uncommon. */ - if (cli_opts.username - && strchr(cli_opts.username, ',') + if (cli_opts.username + && strchr(cli_opts.username, ',') && strchr(cli_opts.username, '@')) { unsigned int len = strlen(orighostarg) + strlen(cli_opts.username) + 2; hostbuf = m_malloc(len); @@ -636,13 +659,13 @@ static void parse_multihop_hostname(const char* orighostarg, const char* argv0) if (cli_opts.remoteport == NULL) { cli_opts.remoteport = "22"; } - cmd_len = strlen(argv0) + strlen(remainder) + cmd_len = strlen(argv0) + strlen(remainder) + strlen(cli_opts.remotehost) + strlen(cli_opts.remoteport) + strlen(passthrough_args) + 30; cli_opts.proxycmd = m_malloc(cmd_len); - snprintf(cli_opts.proxycmd, cmd_len, "%s -B %s:%s %s %s", - argv0, cli_opts.remotehost, cli_opts.remoteport, + snprintf(cli_opts.proxycmd, cmd_len, "%s -B %s:%s %s %s", + argv0, cli_opts.remotehost, cli_opts.remoteport, passthrough_args, remainder); #ifndef DISABLE_ZLIB /* The stream will be incompressible since it's encrypted. */ @@ -694,9 +717,9 @@ static void parse_hostname(const char* orighostarg) { #if DROPBEAR_CLI_NETCAT static void add_netcat(const char* origstr) { char *portstr = NULL; - + char * str = m_strdup(origstr); - + portstr = strchr(str, ':'); if (portstr == NULL) { TRACE(("No netcat port")) @@ -704,25 +727,25 @@ static void add_netcat(const char* origstr) { } *portstr = '\0'; portstr++; - + if (strchr(portstr, ':')) { TRACE(("Multiple netcat colons")) goto fail; } - + if (m_str_to_uint(portstr, &cli_opts.netcat_port) == DROPBEAR_FAILURE) { TRACE(("bad netcat port")) goto fail; } - + if (cli_opts.netcat_port > 65535) { TRACE(("too large netcat port")) goto fail; } - + cli_opts.netcat_host = str; return; - + fail: dropbear_exit("Bad netcat endpoint '%s'", origstr); } @@ -730,7 +753,7 @@ static void add_netcat(const char* origstr) { static void fill_own_user() { uid_t uid; - struct passwd *pw = NULL; + struct passwd *pw = NULL; uid = getuid(); @@ -760,7 +783,7 @@ static void addforward(const char* origstr, m_list *fwdlist) { TRACE(("enter addforward")) /* We need to split the original argument up. This var - is never free()d. */ + is never free()d. */ str = m_strdup(origstr); part1 = str; @@ -820,7 +843,7 @@ static void addforward(const char* origstr, m_list *fwdlist) { TRACE(("listenport > 65535")) goto badport; } - + if (newfwd->connectport > 65535) { TRACE(("connectport > 65535")) goto badport; diff --git a/default_options.h b/default_options.h index 2ca01b3ae..3f871e1bd 100644 --- a/default_options.h +++ b/default_options.h @@ -308,6 +308,12 @@ other side, exit. Not run-time configurable - if you have a need for runtime configuration please mail the Dropbear list */ #define DEFAULT_KEEPALIVE_LIMIT 3 +/* Enable TCP socket level keep alive probes after this many seconds. This can +be overridden at runtime with -k. 0 disables TCP keepalives. +A negative value starts them ony after a the system defined default time elapsed + OR, if SSH KEEPALIVE != 0, SSH KEEPALIVE * KEEPALIVE_LIMIT seconds */ +#define DEFAULT_TCP_ALIVE 0 + /* Ensure that data is received within IDLE_TIMEOUT seconds. This can be overridden at runtime with -I. 0 disables idle timeouts */ #define DEFAULT_IDLE_TIMEOUT 0 diff --git a/netio.c b/netio.c index d84975ad3..ab9de144a 100644 --- a/netio.c +++ b/netio.c @@ -4,6 +4,11 @@ #include "session.h" #include "debug.h" +#include "runopts.h" + +extern runopts opts; /* GLOBAL */ + + struct dropbear_progress_connection { struct addrinfo *res; struct addrinfo *res_iter; @@ -84,7 +89,7 @@ static void connect_try_next(struct dropbear_progress_connection *c) { int len = 100 + strlen(gai_strerror(err)); m_free(c->errstring); c->errstring = (char*)m_malloc(len); - snprintf(c->errstring, len, "Error resolving bind address '%s' (port %s). %s", + snprintf(c->errstring, len, "Error resolving bind address '%s' (port %s). %s", c->bind_address, c->bind_port, gai_strerror(err)); TRACE(("Error resolving bind: %s", gai_strerror(err))) close(c->sock); @@ -100,7 +105,7 @@ static void connect_try_next(struct dropbear_progress_connection *c) { int len = 300; m_free(c->errstring); c->errstring = m_malloc(len); - snprintf(c->errstring, len, "Error binding local address '%s' (port %s). %s", + snprintf(c->errstring, len, "Error binding local address '%s' (port %s). %s", c->bind_address, c->bind_port, strerror(keep_errno)); close(c->sock); c->sock = -1; @@ -131,7 +136,7 @@ static void connect_try_next(struct dropbear_progress_connection *c) { if (errno != EINPROGRESS) { m_free(c->errstring); c->errstring = m_strdup(strerror(errno)); - /* Not entirely sure which kind of errors are normal - 2.6.32 seems to + /* Not entirely sure which kind of errors are normal - 2.6.32 seems to return EPIPE for any (nonblocking?) sendmsg(). just fall back */ TRACE(("sendmsg tcp_fastopen failed, falling back. %s", strerror(errno))); /* No kernel MSG_FASTOPEN support. Fall back below */ @@ -172,7 +177,7 @@ static void connect_try_next(struct dropbear_progress_connection *c) { /* Connect via TCP to a host. */ struct dropbear_progress_connection *connect_remote(const char* remotehost, const char* remoteport, - connect_callback cb, void* cb_data, + connect_callback cb, void* cb_data, const char* bind_address, const char* bind_port) { struct dropbear_progress_connection *c = NULL; @@ -197,13 +202,13 @@ struct dropbear_progress_connection *connect_remote(const char* remotehost, cons int len; len = 100 + strlen(gai_strerror(err)); c->errstring = (char*)m_malloc(len); - snprintf(c->errstring, len, "Error resolving '%s' port '%s'. %s", + snprintf(c->errstring, len, "Error resolving '%s' port '%s'. %s", remotehost, remoteport, gai_strerror(err)); TRACE(("Error resolving: %s", gai_strerror(err))) } else { c->res_iter = c->res; } - + if (bind_address) { c->bind_address = m_strdup(bind_address); } @@ -278,7 +283,7 @@ void handle_connect_fds(const fd_set *writefd) { remove_connect(c, iter); TRACE(("leave handle_connect_fds - success")) /* Must return here - remove_connect() invalidates iter */ - return; + return; } } } @@ -334,11 +339,25 @@ void packet_queue_consume(struct Queue *queue, ssize_t written) { } void set_sock_nodelay(int sock) { - int val; + time_t keepidle = opts.tcp_keepalive; - /* disable nagle */ - val = 1; - setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&val, sizeof(val)); + int val = 1; /* disable nagle and optionally enable tcp keepalive probes */ + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); + + if (keepidle) { + if (keepidle < 0 && opts.keepalive_secs) { + //start probing only after ssh keep alives should have terminated session + keepidle = opts.keepalive_secs * DEFAULT_KEEPALIVE_LIMIT; + } + if (keepidle > 0 && + setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, + &keepidle, sizeof(keepidle))) { + TRACE(("TCP_KEEPIDLE failed for socket %d: %s",sock, strerror(errno))); + } + if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val))) { + TRACE(("TCP KeepAlive failed for socket %d: %s",sock,strerror(errno))); + } + } } #if DROPBEAR_SERVER_TCP_FAST_OPEN @@ -348,7 +367,6 @@ void set_listen_fast_open(int sock) { TRACE(("set_listen_fast_open failed for socket %d: %s", sock, strerror(errno))) } } - #endif void set_sock_priority(int sock, enum dropbear_prio prio) { @@ -436,7 +454,7 @@ int get_sock_port(int sock) { return atoi(strport); } -/* Listen on address:port. +/* Listen on address:port. * Special cases are address of "" listening on everything, * and address of NULL listening on localhost only. * Returns the number of sockets bound on success, or -1 on failure. On @@ -453,7 +471,7 @@ int dropbear_listen(const char* address, const char* port, int sock; TRACE(("enter dropbear_listen")) - + memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; /* TODO: let them flag v4 only etc */ hints.ai_socktype = SOCK_STREAM; @@ -534,7 +552,7 @@ int dropbear_listen(const char* address, const char* port, #if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY) if (res->ai_family == AF_INET6) { int on = 1; - if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, + if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1) { dropbear_log(LOG_WARNING, "Couldn't set IPV6_V6ONLY"); } @@ -598,39 +616,39 @@ void get_socket_address(int fd, char **local_host, char **local_port, return; } #endif - + if (local_host || local_port) { addrlen = sizeof(addr); if (getsockname(fd, (struct sockaddr*)&addr, &addrlen) < 0) { dropbear_exit("Failed socket address: %s", strerror(errno)); } - getaddrstring(&addr, local_host, local_port, host_lookup); + getaddrstring(&addr, local_host, local_port, host_lookup); } if (remote_host || remote_port) { addrlen = sizeof(addr); if (getpeername(fd, (struct sockaddr*)&addr, &addrlen) < 0) { dropbear_exit("Failed socket address: %s", strerror(errno)); } - getaddrstring(&addr, remote_host, remote_port, host_lookup); + getaddrstring(&addr, remote_host, remote_port, host_lookup); } } /* Return a string representation of the socket address passed. The return * value is allocated with malloc() */ -void getaddrstring(struct sockaddr_storage* addr, +void getaddrstring(struct sockaddr_storage* addr, char **ret_host, char **ret_port, int host_lookup) { char host[NI_MAXHOST+1], serv[NI_MAXSERV+1]; unsigned int len; int ret; - + int flags = NI_NUMERICSERV | NI_NUMERICHOST; #if !DO_HOST_LOOKUP host_lookup = 0; #endif - + if (host_lookup) { flags = NI_NUMERICSERV; } @@ -651,7 +669,7 @@ void getaddrstring(struct sockaddr_storage* addr, #endif #endif - ret = getnameinfo((struct sockaddr*)addr, len, host, sizeof(host)-1, + ret = getnameinfo((struct sockaddr*)addr, len, host, sizeof(host)-1, serv, sizeof(serv)-1, flags); if (ret != 0) { diff --git a/runopts.h b/runopts.h index 6a4a94ccd..23fa72ffe 100644 --- a/runopts.h +++ b/runopts.h @@ -1,19 +1,19 @@ /* * Dropbear - a SSH2 server - * + * * Copyright (c) 2002,2003 Matt Johnston * All rights reserved. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -40,6 +40,8 @@ typedef struct runopts { unsigned int recv_window; time_t keepalive_secs; /* Time between sending keepalives. 0 is off */ time_t idle_timeout_secs; /* Exit if no traffic is sent/received in this time */ + time_t tcp_keepalive; /* TCP keepalive idle secs */ + /* or default tcp_keepidle if < 0 */ int usingsyslog; #ifndef DISABLE_ZLIB @@ -62,7 +64,7 @@ typedef struct runopts { extern runopts opts; -int readhostkey(const char * filename, sign_key * hostkey, +int readhostkey(const char * filename, sign_key * hostkey, enum signkey_type *type); void load_all_hostkeys(void); @@ -72,7 +74,7 @@ typedef struct svr_runopts { int forkbg; - /* ports and addresses are arrays of the portcount + /* ports and addresses are arrays of the portcount listening ports. strings are malloced. */ char *ports[DROPBEAR_MAX_PORTS]; unsigned int portcount; @@ -94,7 +96,7 @@ typedef struct svr_runopts { int norootlogin; #ifdef HAVE_GETGROUPLIST - /* restrict_group is the group name if group restriction was enabled, + /* restrict_group is the group name if group restriction was enabled, NULL otherwise */ char *restrict_group; /* restrict_group_gid is only valid if restrict_group is set */ @@ -167,7 +169,7 @@ typedef struct cli_runopts { #endif #if DROPBEAR_CLI_AGENTFWD int agent_fwd; - int agent_keys_loaded; /* whether pubkeys has been populated with a + int agent_keys_loaded; /* whether pubkeys has been populated with a list of keys held by the agent */ int agent_fd; /* The agent fd is only set during authentication. Forwarded agent sessions have their own file descriptors */ diff --git a/svr-runopts.c b/svr-runopts.c index 2c905dd8b..f5ece043b 100644 --- a/svr-runopts.c +++ b/svr-runopts.c @@ -1,19 +1,19 @@ /* * Dropbear - a SSH2 server - * + * * Copyright (c) 2002,2003 Matt Johnston * All rights reserved. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -61,64 +61,68 @@ static void printhelp(const char * progname) { " - ed25519 %s\n" #endif #if DROPBEAR_DELAY_HOSTKEY - "-R Create hostkeys as required\n" + "-R Create hostkeys as required\n" +#endif + "-F Don't fork into background\n" +#ifndef DISABLE_ZLIB + "-n No compression\n" #endif - "-F Don't fork into background\n" #ifdef DISABLE_SYSLOG - "(Syslog support not compiled in, using stderr)\n" + "(Syslog support not compiled in, using stderr)\n" #else - "-E Log to stderr rather than syslog\n" + "-E Log to stderr rather than syslog\n" #endif #if DO_MOTD - "-m Don't display the motd on login\n" + "-m Don't display the motd on login\n" #endif - "-w Disallow root logins\n" + "-w Disallow root logins\n" #ifdef HAVE_GETGROUPLIST - "-G Restrict logins to members of specified group\n" + "-G Restrict logins to members of specified group\n" #endif #if DROPBEAR_SVR_PASSWORD_AUTH || DROPBEAR_SVR_PAM_AUTH - "-s Disable password logins\n" - "-g Disable password logins for root\n" - "-B Allow blank password logins\n" + "-s Disable password logins\n" + "-g Disable password logins for root\n" + "-B Allow blank password logins\n" #endif - "-T Maximum authentication tries (default %d)\n" + "-T Maximum authentication tries (default %d)\n" #if DROPBEAR_SVR_LOCALTCPFWD - "-j Disable local port forwarding\n" + "-j Disable local port forwarding\n" #endif #if DROPBEAR_SVR_REMOTETCPFWD - "-k Disable remote port forwarding\n" - "-a Allow connections to forwarded ports from any host\n" - "-c command Force executed command\n" -#endif - "-p [address:]port\n" - " Listen on specified tcp port (and optionally address),\n" - " up to %d can be specified\n" - " (default port is %s if none specified)\n" - "-P PidFile Create pid file PidFile\n" - " (default %s)\n" + "-k Disable remote port forwarding\n" + "-a Allow connections to forwarded ports from any host\n" + "-c command Force executed command\n" +#endif + "-p [address:]port\n" + " Listen on specified tcp port (and optionally address),\n" + " up to %d can be specified\n" + " (default port is %s if none specified)\n" + "-P PidFile Create pid file PidFile\n" + " (default %s)\n" #if INETD_MODE - "-i Start for inetd\n" + "-i Start for inetd\n" #endif - "-W (default %d, larger may be faster, max 1MB)\n" - "-K (0 is never, default %d, in seconds)\n" - "-I (0 is never, default %d, in seconds)\n" + "-W (default %d, larger may be faster, max 1MB)\n" + "-S{idletime} TCP Socket keepalive {optional TCP_KEEPIDLE must follow directly}\n" + "-K (0 is never, default %d, in seconds)\n" + "-I (0 is never, default %d, in seconds)\n" #if DROPBEAR_PLUGIN "-A [,]\n" " Enable external public key auth through \n" #endif "-V Version\n" #if DEBUG_TRACE - "-v verbose (compiled with DEBUG_TRACE)\n" + "-v verbose (compiled with DEBUG_TRACE)\n" #endif - ,DROPBEAR_VERSION, progname, + ,DROPBEAR_VERSION, progname, #if DROPBEAR_DSS - DSS_PRIV_FILENAME, + DSS_PRIV_FILENAME, #endif #if DROPBEAR_RSA - RSA_PRIV_FILENAME, + RSA_PRIV_FILENAME, #endif #if DROPBEAR_ECDSA - ECDSA_PRIV_FILENAME, + ECDSA_PRIV_FILENAME, #endif #if DROPBEAR_ED25519 ED25519_PRIV_FILENAME, @@ -130,7 +134,7 @@ static void printhelp(const char * progname) { void svr_getopts(int argc, char ** argv) { - unsigned int i, j; + unsigned i; char ** next = NULL; int nextisport = 0; char* recv_window_arg = NULL; @@ -176,7 +180,7 @@ void svr_getopts(int argc, char ** argv) { #ifndef DISABLE_ZLIB opts.compress_mode = DROPBEAR_COMPRESS_DELAYED; -#endif +#endif /* not yet opts.ipv4 = 1; @@ -191,16 +195,19 @@ void svr_getopts(int argc, char ** argv) { opts.recv_window = DEFAULT_RECV_WINDOW; opts.keepalive_secs = DEFAULT_KEEPALIVE; opts.idle_timeout_secs = DEFAULT_IDLE_TIMEOUT; - + opts.tcp_keepalive = DEFAULT_TCP_ALIVE; + #if DROPBEAR_SVR_REMOTETCPFWD opts.listen_fwd_all = 0; #endif for (i = 1; i < (unsigned int)argc; i++) { - if (argv[i][0] != '-' || argv[i][1] == '\0') + char *cursor = argv[i]; + if (*cursor != '-' || !*++cursor) dropbear_exit("Invalid argument: %s", argv[i]); - for (j = 1; (c = argv[i][j]) != '\0' && !next && !nextisport; j++) { + while ((c = *cursor) && !next && !nextisport) { + cursor++; switch (c) { case 'b': next = &svr_opts.bannerfile; @@ -240,6 +247,11 @@ void svr_getopts(int argc, char ** argv) { case 'i': svr_opts.inetdmode = 1; break; +#endif +#ifndef DISABLE_ZLIB + case 'n': /* disable stream compression */ + opts.compress_mode = DROPBEAR_COMPRESS_OFF; + break; #endif case 'p': nextisport = 1; @@ -267,6 +279,21 @@ void svr_getopts(int argc, char ** argv) { case 'K': next = &keepalive_arg; break; + case 'S': //require optional idletime follow directly! + opts.tcp_keepalive = -1; + { + char *end; + long idletime = strtol(cursor, &end, 10); + if (cursor != end) { + if (idletime < 1 || idletime >= (long)LONG_MAX) { + *end=0; + dropbear_exit("Bad TCP_KEEPIDLE '%s'", cursor); + } + opts.tcp_keepalive = idletime; + cursor = end; + } + } + break; case 'I': next = &idle_timeout_arg; break; @@ -316,22 +343,15 @@ void svr_getopts(int argc, char ** argv) { if (!next && !nextisport) continue; - if (c == '\0') { - i++; - j = 0; - if (!argv[i]) { - dropbear_exit("Missing argument"); - } - } + if (!c && !(cursor = argv[++i])) + dropbear_exit("Missing argument"); if (nextisport) { - addportandaddress(&argv[i][j]); + addportandaddress(cursor); nextisport = 0; } else if (next) { - *next = &argv[i][j]; - if (*next == NULL) { + if (!(*next = cursor)) dropbear_exit("Invalid null argument"); - } next = NULL; if (keyfile) { @@ -354,7 +374,7 @@ void svr_getopts(int argc, char ** argv) { dropbear_exit("Error opening banner file '%s'", svr_opts.bannerfile); } - + if (buf.st_size > MAX_BANNER_SIZE) { dropbear_exit("Banner file too large, max is %d bytes", MAX_BANNER_SIZE); @@ -379,7 +399,7 @@ void svr_getopts(int argc, char ** argv) { } } #endif - + if (recv_window_arg) { opts.recv_window = atol(recv_window_arg); if (opts.recv_window == 0 || opts.recv_window > MAX_RECV_WINDOW) { @@ -389,14 +409,14 @@ void svr_getopts(int argc, char ** argv) { if (maxauthtries_arg) { unsigned int val = 0; - if (m_str_to_uint(maxauthtries_arg, &val) == DROPBEAR_FAILURE + if (m_str_to_uint(maxauthtries_arg, &val) == DROPBEAR_FAILURE || val == 0) { dropbear_exit("Bad maxauthtries '%s'", maxauthtries_arg); } svr_opts.maxauthtries = val; } - + if (keepalive_arg) { unsigned int val; if (m_str_to_uint(keepalive_arg, &val) == DROPBEAR_FAILURE) { @@ -461,7 +481,7 @@ static void addportandaddress(const char* spec) { port = myspec; } else { /* Split the address/port */ - port[0] = '\0'; + port[0] = '\0'; port++; address = myspec; } @@ -620,7 +640,7 @@ void load_all_hostkeys() { - Otherwise no ecdsa keys will be advertised */ /* check if any keys were loaded at startup */ - loaded_any_ecdsa = + loaded_any_ecdsa = 0 #if DROPBEAR_ECC_256 || svr_opts.hostkey->ecckey256