examples/ftpc: proper dual-stack support, also fix getopt re-entrancy issues

Signed-off-by: Juha Niskanen <juha.niskanen@haltian.com>
This commit is contained in:
Juha Niskanen 2020-12-14 15:59:57 +02:00 committed by Xiang Xiao
parent 28362eb6d1
commit 5105474e77
2 changed files with 202 additions and 100 deletions

View File

@ -45,18 +45,6 @@
#include "ftpc.h"
/****************************************************************************
* Public Types
****************************************************************************/
/****************************************************************************
* Private Data
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Public Functions
****************************************************************************/
@ -67,7 +55,10 @@
int cmd_rlogin(SESSION handle, int argc, char **argv)
{
struct ftpc_login_s login = {NULL, NULL, NULL, true};
struct ftpc_login_s login =
{
NULL, NULL, NULL, true
};
login.uname = argv[1];
if (argc > 2)
@ -87,8 +78,9 @@ int cmd_rquit(SESSION handle, int argc, char **argv)
int ret = ftpc_quit(handle);
if (ret < 0)
{
printf("quit failed: %d\n", errno);
fprintf(stderr, "quit failed: %d\n", errno);
}
printf("Exiting...\n");
exit(0);
return ERROR;
@ -297,6 +289,7 @@ int cmd_rget(SESSION handle, int argc, char **argv)
FAR const char *lname = NULL;
int xfrmode = FTPC_XFRMODE_ASCII;
int option;
bool badarg = false;
while ((option = getopt(argc, argv, "ab")) != ERROR)
{
@ -310,16 +303,21 @@ int cmd_rget(SESSION handle, int argc, char **argv)
}
else
{
printf("%s: Unrecognized option: '%c'\n", "rget", option);
return ERROR;
fprintf(stderr, "%s: Unrecognized option: '%c'\n", "rget", option);
badarg = true;
}
}
if (badarg)
{
return ERROR;
}
/* There should be one or two parameters remaining on the command line */
if (optind >= argc)
{
printf("%s: Missing required arguments\n", "rget");
fprintf(stderr, "%s: Missing required arguments\n", "rget");
return ERROR;
}
@ -334,7 +332,7 @@ int cmd_rget(SESSION handle, int argc, char **argv)
if (optind != argc)
{
printf("%s: Too many arguments\n", "rget");
fprintf(stderr, "%s: Too many arguments\n", "rget");
return ERROR;
}
@ -353,6 +351,7 @@ int cmd_rput(SESSION handle, int argc, char **argv)
FAR const char *rname = NULL;
int xfrmode = FTPC_XFRMODE_ASCII;
int option;
bool badarg = false;
while ((option = getopt(argc, argv, "ab")) != ERROR)
{
@ -366,16 +365,21 @@ int cmd_rput(SESSION handle, int argc, char **argv)
}
else
{
printf("%s: Unrecognized option: '%c'\n", "rput", option);
return ERROR;
fprintf(stderr, "%s: Unrecognized option: '%c'\n", "rput", option);
badarg = true;
}
}
if (badarg)
{
return ERROR;
}
/* There should be one or two parameters remaining on the command line */
if (optind >= argc)
{
printf("%s: Missing required arguments\n", "rput");
fprintf(stderr, "%s: Missing required arguments\n", "rput");
return ERROR;
}
@ -390,7 +394,7 @@ int cmd_rput(SESSION handle, int argc, char **argv)
if (optind != argc)
{
printf("%s: Too many arguments\n", "rput");
fprintf(stderr, "%s: Too many arguments\n", "rput");
return ERROR;
}

View File

@ -43,6 +43,9 @@
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include "netutils/ftpc.h"
@ -56,16 +59,6 @@
#define FTPC_MAX_ARGUMENTS 4
/* If FTP is used and both IPv6 and IPv4 are enabled, then we need to
* pick one.
*/
#ifdef CONFIG_NET_IPv6
# define ADDR_FAMILY AF_INET6
#else
# define ADDR_FAMILY AF_INET
#endif
/****************************************************************************
* Private Types
****************************************************************************/
@ -148,7 +141,7 @@ static int cmd_lhelp(SESSION handle, int argc, char **argv)
static int cmd_lunrecognized(SESSION handle, int argc, char **argv)
{
printf("Command %s unrecognized\n", argv[0]);
fprintf(stderr, "Command %s unrecognized\n", argv[0]);
return ERROR;
}
@ -220,7 +213,7 @@ char *ftpc_argument(char **saveptr)
if (*pend)
{
/* Turn the delimiter into a null terminator */
/* Turn the delimiter into a NUL terminator */
*pend++ = '\0';
}
@ -271,14 +264,14 @@ static int ftpc_execute(SESSION handle, int argc, char *argv[])
{
/* Fewer than the minimum number were provided */
printf("Too few arguments for '%s'\n", cmd);
fprintf(stderr, "Too few arguments for '%s'\n", cmd);
return ERROR;
}
else if (argc > cmdmap->maxargs)
{
/* More than the maximum number were provided */
printf("Too many arguments for '%s'\n", cmd);
fprintf(stderr, "Too many arguments for '%s'\n", cmd);
return ERROR;
}
else
@ -296,7 +289,7 @@ static int ftpc_execute(SESSION handle, int argc, char *argv[])
ret = handler(handle, argc, argv);
if (ret < 0)
{
printf("%s failed: %d\n", cmd, errno);
fprintf(stderr, "%s failed: %d\n", cmd, errno);
}
return ret;
@ -316,7 +309,7 @@ int ftpc_parse(SESSION handle, char *cmdline)
/* Initialize parser state */
memset(argv, 0, FTPC_MAX_ARGUMENTS*sizeof(FAR char *));
memset(argv, 0, FTPC_MAX_ARGUMENTS * sizeof(FAR char *));
/* Parse out the command at the beginning of the line */
@ -352,7 +345,7 @@ int ftpc_parse(SESSION handle, char *cmdline)
if (argc > FTPC_MAX_ARGUMENTS)
{
printf("Too many arguments\n");
fprintf(stderr, "Too many arguments\n");
ret = -EINVAL;
}
else
@ -365,6 +358,17 @@ int ftpc_parse(SESSION handle, char *cmdline)
return ret;
}
static void usage(void)
{
fprintf(stderr,
"Usage: ftpc [-46n] host [port]\n\
\t-4 Use IPv4\n\
\t-6 Use IPv6\n\
\t-n Allow numeric IP address only\n\
");
exit(1);
}
/****************************************************************************
* Public Functions
****************************************************************************/
@ -373,89 +377,183 @@ int main(int argc, FAR char *argv[])
{
union ftpc_sockaddr_u server;
SESSION handle;
#if ADDR_FAMILY == AF_INET
FAR char *ptr;
char *host = NULL;
char *port = NULL;
#ifdef CONFIG_LIBC_NETDB
struct addrinfo hints;
FAR struct addrinfo *info;
FAR struct addrinfo *next;
#endif
#ifndef CONFIG_EXAMPLES_FTPC_FGETS
int option;
int ret;
#endif
int family = AF_UNSPEC;
bool nflag = false;
bool badarg = false;
memset(&server, 0, sizeof(union ftpc_sockaddr_u));
if (argc != 2)
while ((option = getopt(argc, argv, "46n")) != ERROR)
{
#if ADDR_FAMILY == AF_INET6
printf("Usage:\n");
printf(" %s xx:xx:xx:xx:xx:xx:xx:xx [pp]\n", argv[0]);
printf("Where\n");
printf(" xx:xx:xx:xx:xx:xx:xx:xx is "
"the IP address of the FTP server\n");
printf(" pp is option port to use with the FTP server\n");
#else
printf("Usage:\n");
printf(" %s xx.xx.xx.xx[:pp]\n", argv[0]);
printf("Where\n");
printf(" xx.xx.xx.xx is the IP address of the FTP server\n");
printf(" pp is option port to use with the FTP server\n");
#endif
switch (option)
{
case '4':
family = AF_INET;
break;
case '6':
family = AF_INET6;
break;
case 'n':
nflag = true;
break;
default:
badarg = true;
break;
}
}
if (badarg)
{
usage();
}
/* There should be one or two parameters remaining on the command line */
if (optind >= argc)
{
fprintf(stderr, "%s: Missing required arguments\n", argv[0]);
usage();
}
host = argv[optind];
optind++;
if (optind < argc)
{
port = argv[optind];
optind++;
}
if (optind != argc)
{
fprintf(stderr, "%s: Too many arguments\n", argv[0]);
usage();
}
#ifdef CONFIG_LIBC_NETDB
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = family;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
if (nflag)
{
hints.ai_flags |= AI_NUMERICHOST;
}
/* We now get all addresses for hostname or IP address from the
* command line.
*/
ret = getaddrinfo(host, port, &hints, &info);
if (ret != OK)
{
fprintf(stderr, "ERROR: getaddrinfo: %s\n", gai_strerror(ret));
exit(1);
}
/* In any event, we can now extract the IP address from the comman-line */
#if ADDR_FAMILY == AF_INET6
server.in6.sin6_family = AF_INET6;
ret = inet_pton(AF_INET6, argv[1], &server.in6.sin6_addr);
if (ret < 0)
for (next = info; next != NULL; next = next->ai_next)
{
printf("Invalid IPv6 address\n");
exit(1);
}
if (argc > 2)
{
server.in6.sin6_port = atoi(argv[2]);
}
#else
/* Check if the argument includes a port number */
ptr = strchr(argv[1], ':');
if (ptr)
{
*ptr = '\0';
server.in4.sin_port = atoi(ptr + 1);
}
server.in4.sin_family = AF_INET;
ret = inet_pton(AF_INET, argv[1], &server.in4.sin_addr);
if (ret < 0)
{
printf("Invalid IP address\n");
exit(1);
}
#ifdef CONFIG_NET_IPv6
if (next->ai_family == AF_INET6)
{
memcpy(&server.in6, next->ai_addr, next->ai_addrlen);
}
#endif
/* Connect to the FTP server */
#ifdef CONFIG_NET_IPv4
if (next->ai_family == AF_INET)
{
memcpy(&server.in4, next->ai_addr, next->ai_addrlen);
}
#endif
/* Connect to the FTP server */
handle = ftpc_connect(&server);
if (handle)
{
break;
}
}
freeaddrinfo(info);
if (!handle)
{
fprintf(stderr, "ERROR: Failed to connect to the server: %d\n", errno);
exit(1);
}
#else
/* No getaddrinfo(), use IP address only, implies nflag. */
UNUSED(nflag);
DEBUGASSERT(host != NULL);
/* Try IPv6 first, then IPv4. */
#ifdef CONFIG_NET_IPv6
if (family != AF_INET)
{
ret = inet_pton(AF_INET6, host, &server.in6.sin6_addr);
if (ret == 1)
{
server.in6.sin6_family = AF_INET6;
if (port != NULL)
{
server.in6.sin6_port = htons(atoi(port));
}
goto do_connect;
}
}
#endif /* CONFIG_NET_IPv6 */
#ifdef CONFIG_NET_IPv4
if (family != AF_INET6)
{
ret = inet_pton(AF_INET, host, &server.in4.sin_addr);
if (ret == 1)
{
server.in4.sin_family = AF_INET;
if (port != NULL)
{
server.in4.sin_port = htons(atoi(port));
}
goto do_connect;
}
}
#endif /* CONFIG_NET_IPv4 */
/* Did not get a valid address. */
fprintf(stderr, "ERROR: Invalid IP address\n");
exit(1);
/* Connect to the FTP server. */
do_connect:
handle = ftpc_connect(&server);
if (!handle)
{
printf("Failed to connect to the server: %d\n", errno);
fprintf(stderr, "ERROR: Failed to connect to the server: %d\n", errno);
exit(1);
}
#endif /* CONFIG_LIBC_NETDB */
/* Present a greeting */
printf("NuttX FTP Client:\n");
FFLUSH();
/* Setting optind to -1 is a non-standard, backdoor way to reinitialize
* getopt(). getopt() is not thread safe and we have no idea what state
* it is in now!
*/
optind = -1;
/* Then enter the command line parsing loop */
for (; ; )
@ -472,7 +570,7 @@ int main(int argc, FAR char *argv[])
if (fgets(g_line, CONFIG_FTPC_LINELEN, stdin) == NULL)
{
printf("ERROR: fgets failed: %d\n", errno);
fprintf(stderr, "ERROR: fgets failed: %d\n", errno);
return 1;
}
#else
@ -485,7 +583,7 @@ int main(int argc, FAR char *argv[])
if (ret == EOF)
{
printf("ERROR: readline failed: %d\n", errno);
fprintf(stderr, "ERROR: readline failed: %d\n", errno);
return 1;
}
#endif