5869 lines
160 KiB
C
5869 lines
160 KiB
C
/*
|
|
******************************************************************************
|
|
* SOURCE FILE
|
|
*
|
|
* Document no: @(#) $ipftps.c$ $RCSfile: ipftps.c,v $ $Revision: 1.84.10.45 $
|
|
* $Source: /home/interpeak/CVSRoot/ipappl/src/ipftps.c,v $
|
|
* $Author: lwang7 $
|
|
* $State: Exp $ $Locker: $
|
|
*
|
|
* INTERPEAK_COPYRIGHT_STRING
|
|
* Design and implementation by Ulf Olofsson <ulf@interpeak.se>
|
|
******************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
|
|
#include "ipftps_config.h"
|
|
|
|
/* Timeout when sending data to socket */
|
|
#define IPFTPS_SEND_TIMEOUT 30
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#define IPCOM_USE_CLIB_PROTO
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_clib2.h>
|
|
#include <ipcom_time.h>
|
|
#include <ipcom_os.h>
|
|
#include <ipcom_auth.h>
|
|
#include <ipcom_sock.h>
|
|
#include <ipcom_sock2.h>
|
|
#include <ipcom_err.h>
|
|
#include <ipcom_syslog.h>
|
|
#include <ipcom_list.h>
|
|
#include <ipcom_ipd.h>
|
|
#include <ipcom_sysvar.h>
|
|
|
|
#include "ipftps.h"
|
|
|
|
#ifdef IP_PORT_VXWORKS
|
|
#include <vxWorks.h>
|
|
#include <pathLib.h>
|
|
#include <iosLib.h>
|
|
#include <dirent.h>
|
|
#include <private/loginLibP.h>
|
|
#include <shellHook.h>
|
|
#endif
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/* The definitions below can be used to change the priorities of the main and session processes */
|
|
#define IPFTPS_MAIN_PROC_PRIO IPCOM_PRIORITY_DEFAULT
|
|
#define IPFTPS_SESSION_PROC_PRIO IPCOM_PROC_PRIO_INHERIT
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/* Public data */
|
|
typedef struct
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
Ipftps_sockcb sock4; /* Main IPv4 server socket */
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
Ipftps_sockcb sock6; /* Main IPv6 server socket */
|
|
#endif
|
|
int session_number; /* Current active session number */
|
|
int databuf_size; /* Data transfer buffer size */
|
|
Ipcom_mutex lock; /* mutex to protect FTPS global data */
|
|
Ip_fd pipe_fd[2]; /* pipe between daemon/session task */
|
|
}
|
|
Ipftps_data;
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#ifdef IP_PORT_OSE5
|
|
extern int efs_donate_fd(int fd, Ip_pid_t to);
|
|
extern int efs_receive_fd(Ip_pid_t from);
|
|
#endif
|
|
|
|
#ifdef IPFTP_TLS
|
|
IP_EXTERN int ipftps_tls_enbale;
|
|
#endif
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#define IPFTPS_C
|
|
#include "ipftps_h.h"
|
|
|
|
IP_STATIC int ipftps_filewrite_ascii(Ip_fd fd, char *buf, char *nbuf, int bufsize, Ip_bool *orphant);
|
|
IP_STATIC int ipftps_socketwrite_ascii(Ipftps_session *session, Ip_fd fd, char *buf, char *nbuf, int bufsize);
|
|
IP_STATIC int ipftps_dirindex(char *path, int idomin);
|
|
IP_STATIC int ipftps_dircmp(char *root, char *path);
|
|
IP_STATIC int ipftps_root_check(Ipftps_session *session, char **path);
|
|
IP_STATIC int ipftps_ls(Ipftps_session *session, Ip_fd fd, char *path1, char *path2);
|
|
IP_STATIC void ipftps_abor(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_acct(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_appe(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_cdup(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_cwd(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_allo(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_dele(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_eprt(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_epsv(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_help(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_list(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_mkd(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_mode(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_nlst(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_noop(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_pass(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_pasv(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_port(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_pwd(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_quit(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_retr(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_rmd(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_rnfr(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_rnto(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_stat(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_stor(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_stru(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_syst(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_type(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_user(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_size(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC int ipftps_chdir(Ipftps_session *session, char *path);
|
|
IP_STATIC char* ipftps_wildcard_check(char *path, struct Ip_stat *st);
|
|
IP_STATIC int ipftps_pattern_check(char *pattern, char *name);
|
|
IP_STATIC const char *ipftps_ntop(union Ip_sockaddr_union *addr, char *dest, int len);
|
|
IP_STATIC void ipftps_perror(Ipftps_session *session, int no);
|
|
IP_STATIC int ipftps_dsock_open(Ipftps_session *session, int passive_open);
|
|
IP_STATIC void ipftps_dsock_close(Ipftps_session *session);
|
|
IP_STATIC int ipftps_write(Ipftps_session *session, Ip_fd fd, void *buf, int len);
|
|
IP_STATIC void ipftps_reply_sock(Ipftps_session *session, Ip_fd fd, const char *fmt, ...);
|
|
IP_STATIC void ipftps_reply(Ipftps_session *session, const char *fmt, ...);
|
|
IP_STATIC int ipftps_telnet(Ipftps_session *session);
|
|
IP_STATIC int ipftps_get_command(Ipftps_session *session);
|
|
IP_STATIC int ipftps_parse_command(Ipftps_session *session);
|
|
IP_STATIC int ipftps_async(Ipftps_session *session);
|
|
|
|
#ifdef IPFTP_TLS
|
|
IP_STATIC void ipftps_auth(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_prot(Ipftps_session *session, int argc, char *argv[]);
|
|
IP_STATIC void ipftps_pbsz(Ipftps_session * session, int argc, char * argv [ ]);
|
|
IP_STATIC void ipftps_ccc(Ipftps_session *session, int argc, char *argv[]);
|
|
#endif
|
|
|
|
#ifdef IPFTPS_USE_TEST
|
|
IP_STATIC void
|
|
ipftps_xftp(Ipftps_session *session, int argc, char *argv[]);
|
|
#endif
|
|
|
|
|
|
IP_PUBLIC Ip_err ipftps_start(void);
|
|
IP_PUBLIC const char *ipftps_version(void);
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
/* List of commmands */
|
|
IP_STATIC const Ipftps_cmd ipftps_cmdlist[] =
|
|
{
|
|
/* Name Handler MinMaxLoginArgtypeDescription */
|
|
{"ABOR", ipftps_abor, 0, 0, 0, 1, "Abort data transfer"},
|
|
{"ACCT", ipftps_acct, 1, 1, 0, 1, "Select account"},
|
|
{"APPE", ipftps_appe, 1, 1, 1, 1, "Append to file"},
|
|
{"CDUP", ipftps_cdup, 0, 0, 0, 1, "Change to parent dir"},
|
|
{"CWD", ipftps_cwd, 1, 1, 1, 1, "Change directory"},
|
|
{"DELE", ipftps_dele, 1, 1, 1, 1, "Delete file"},
|
|
{"EPRT", ipftps_eprt, 3, 3, 0, 1, "Specify extended data port"},
|
|
{"EPSV", ipftps_epsv, 0, 1, 0, 1, "Enter extended passive mode"},
|
|
{"HELP", ipftps_help, 0, 1, 0, 0, "Help"},
|
|
{"LIST", ipftps_list, 0, 1, 1, 1, "List file(s)"},
|
|
{"MKD", ipftps_mkd, 1, 1, 1, 1, "Make directory"},
|
|
{"MODE", ipftps_mode, 1, 1, 0, 1, "Set transfer mode"},
|
|
{"NLST", ipftps_nlst, 0, 1, 1, 1, "List file name(s)"},
|
|
{"NOOP", ipftps_noop, 0, 0, 0, 1, "No operation"},
|
|
{"PASS", ipftps_pass, 1, 1, 0, 0, "Login password"},
|
|
{"PASV", ipftps_pasv, 0, 0, 0, 1, "Enter passive mode"},
|
|
{"PORT", ipftps_port, 6, 6, 0, 1, "Specify data port"},
|
|
{"PWD", ipftps_pwd, 0, 0, 0, 1, "Print current directory"},
|
|
{"QUIT", ipftps_quit, 0, 0, 0, 0, "Quit FTP session"},
|
|
{"RETR", ipftps_retr, 1, 1, 1, 1, "Retrieve file"},
|
|
{"RMD", ipftps_rmd, 1, 1, 1, 1, "Remove directory"},
|
|
{"RNFR", ipftps_rnfr, 1, 1, 1, 1, "Rename from"},
|
|
{"RNTO", ipftps_rnto, 1, 1, 1, 1, "Rename to"},
|
|
{"STAT", ipftps_stat, 0, 1, 1, 1, "Show current status"},
|
|
{"STOR", ipftps_stor, 1, 1, 1, 1, "Store file"},
|
|
{"STRU", ipftps_stru, 1, 1, 0, 1, "Set file structure"},
|
|
{"SYST", ipftps_syst, 0, 0, 0, 1, "Show system type"},
|
|
{"TYPE", ipftps_type, 1, 2, 0, 1, "Set representation type"},
|
|
{"USER", ipftps_user, 0, 1, 0, 0, "Login username"},
|
|
{"XCUP", ipftps_cdup, 0, 0, 0, 1, "Change to parent dir"},
|
|
{"XCWD", ipftps_cwd, 1, 1, 1, 1, "Change directory"},
|
|
{"ALLO", ipftps_allo, 1, 2, 0, 1, "Allocate storage"},
|
|
#ifdef IPFTPS_USE_TEST
|
|
{"XFTP", ipftps_xftp, 1, 4, 0, 0,"Internal test"},
|
|
#endif
|
|
{"XMKD", ipftps_mkd, 1, 1, 1, 1, "Make directory"},
|
|
{"XPWD", ipftps_pwd, 0, 0, 0, 1, "Print current directory"},
|
|
{"XRMD", ipftps_rmd, 1, 1, 1, 1, "Remove directory"},
|
|
{"SIZE", ipftps_size, 1, 1, 1, 1, "Get size of special file"},
|
|
#ifdef IPFTP_TLS
|
|
{"AUTH", ipftps_auth, 1, 1, 0, 0, "TLS auth"},
|
|
{"PROT", ipftps_prot, 1, 1, 0, 0, "TLS prot command"},
|
|
{"PBSZ", ipftps_pbsz, 1, 1, 0, 0, "pbsz command"},
|
|
{"CCC", ipftps_ccc, 0, 0, 0, 0, "CCC command"},
|
|
#endif
|
|
{0, 0, 0}
|
|
};
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps
|
|
*===========================================================================
|
|
* Global data.
|
|
*/
|
|
IP_STATIC Ipftps_data ipftps;
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 LOCAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_socketpair
|
|
*===========================================================================
|
|
* Description: Create a pair of connected sockets for FTP server daemon task.
|
|
* Parameters: domain - Address domain to use. The supported domains are
|
|
* IP_AF_INET and IP_AF_INET6.
|
|
* Returns: 0 = success, -1 = error.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipftps_socketpair(int domain)
|
|
{
|
|
struct Ip_addrinfo *res = IP_NULL;
|
|
struct Ip_addrinfo hints;
|
|
int ret;
|
|
int i, l;
|
|
Ip_socklen_t namelen;
|
|
|
|
ipftps.pipe_fd[0] = IP_INVALID_SOCKET;
|
|
ipftps.pipe_fd[1] = IP_INVALID_SOCKET;
|
|
|
|
ipcom_memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = domain;
|
|
hints.ai_socktype = IP_SOCK_STREAM;
|
|
|
|
ret = ipcom_getaddrinfo(IP_NULL, "0", &hints, &res);
|
|
if (ret)
|
|
goto errout;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
ipftps.pipe_fd[i] = ipcom_socket(res->ai_family, IP_SOCK_STREAM, 0);
|
|
if (ipftps.pipe_fd[i] < 0)
|
|
goto errout;
|
|
}
|
|
|
|
/* Bind to an ephemeral port */
|
|
if (ipcom_bind(ipftps.pipe_fd[0], res->ai_addr, (Ip_socklen_t)res->ai_addrlen) < 0)
|
|
goto errout;
|
|
|
|
/* Figure out which ephemeral port the stack selected */
|
|
namelen = (Ip_socklen_t)res->ai_addrlen;
|
|
if (ipcom_getsockname(ipftps.pipe_fd[0], res->ai_addr, &namelen) < 0)
|
|
goto errout;
|
|
|
|
if (ipcom_listen(ipftps.pipe_fd[0], 1) < 0)
|
|
goto errout;
|
|
|
|
if (ipcom_connect(ipftps.pipe_fd[1], res->ai_addr, (Ip_socklen_t)res->ai_addrlen) < 0)
|
|
goto errout;
|
|
|
|
l = ipftps.pipe_fd[0];
|
|
ipftps.pipe_fd[0] = ipcom_accept(l, IP_NULL, IP_NULL);
|
|
ipcom_socketclose(l);
|
|
if (ipftps.pipe_fd[0] < 0)
|
|
goto errout;
|
|
|
|
|
|
ipcom_freeaddrinfo(res);
|
|
return 0;
|
|
|
|
errout:
|
|
if (res != IP_NULL)
|
|
ipcom_freeaddrinfo(res);
|
|
for (i = 0; i < 2; i++)
|
|
if (ipftps.pipe_fd[i] >= 0)
|
|
ipcom_socketclose(ipftps.pipe_fd[i]);
|
|
return -1;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_filewrite_ascii
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* Write ASCII buffer to file.
|
|
*
|
|
* Parameters:
|
|
* fd File descriptor.
|
|
* buf Buffer.
|
|
* nbuf New buffer.
|
|
* bufsize Buffer size.
|
|
* orphant True if last buffer was terminated with '\r'.
|
|
*
|
|
* Returns:
|
|
* 0 if success, 1 otherwise.
|
|
*/
|
|
|
|
IP_STATIC int
|
|
ipftps_filewrite_ascii(Ip_fd fd, char *buf, char *nbuf, int bufsize,
|
|
Ip_bool *orphant)
|
|
{
|
|
char *cp = nbuf;
|
|
int begin;
|
|
int end;
|
|
int no;
|
|
|
|
/* Enter '\r' if not followed by '\n' */
|
|
if (*orphant && buf[0] != '\n')
|
|
{
|
|
/* Output missing '\r' */
|
|
*cp++ = '\r';
|
|
}
|
|
|
|
*orphant = 0;
|
|
|
|
/* Loop for entire buffer */
|
|
for (begin = end = 0; end < bufsize; end++)
|
|
{
|
|
if (buf[end] == '\r')
|
|
{
|
|
if (end == (bufsize - 1))
|
|
{
|
|
/* '\r' at end of buffer, remember */
|
|
*orphant = 1;
|
|
}
|
|
else if (buf[end + 1] != '\n')
|
|
{
|
|
/* Not followed by '\n' - ignore */
|
|
continue;
|
|
}
|
|
|
|
/* Enter everything up to '\r' */
|
|
no = end - begin;
|
|
ipcom_memcpy(cp, &buf[begin], no);
|
|
cp += no;
|
|
|
|
/* Continue at '\n' */
|
|
begin = end + 1;
|
|
}
|
|
}
|
|
|
|
/* Enter last part */
|
|
no = end - begin;
|
|
ipcom_memcpy(cp, &buf[begin], no);
|
|
cp += no;
|
|
|
|
/* Write to file */
|
|
no = (int)(cp - nbuf);
|
|
if (ipcom_filewrite(fd, nbuf, no) != no)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_socketwrite_ascii
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* Write ASCII buffer to socket.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* fd File descriptor.
|
|
* buf Buffer.
|
|
* nbuf New buffer.
|
|
* bufsize Buffer size.
|
|
*
|
|
* Returns:
|
|
* 0 if success, 1 otherwise.
|
|
*/
|
|
IP_STATIC int
|
|
ipftps_socketwrite_ascii(Ipftps_session *session, Ip_fd fd, char *buf, char *nbuf, int bufsize)
|
|
{
|
|
char *cp = nbuf;
|
|
int begin;
|
|
int end;
|
|
int no;
|
|
#ifdef IPFTP_TLS
|
|
int ret;
|
|
#endif
|
|
|
|
#ifndef IPFTP_TLS
|
|
(void)session;
|
|
#endif
|
|
|
|
/* Loop for entire buffer */
|
|
for (begin = end = 0; end < bufsize; end++)
|
|
{
|
|
if (buf[end] == '\n')
|
|
{
|
|
/* Enter everything up to '\n' */
|
|
no = end - begin;
|
|
ipcom_memcpy(cp, &buf[begin], no);
|
|
cp += no;
|
|
|
|
/* Enter newline */
|
|
*cp++ = '\r';
|
|
*cp++ = '\n';
|
|
|
|
/* Continue after '\n' */
|
|
begin = end + 1;
|
|
}
|
|
}
|
|
|
|
/* Enter last part */
|
|
no = end - begin;
|
|
ipcom_memcpy(cp, &buf[begin], no);
|
|
cp += no;
|
|
|
|
/* Write to socket */
|
|
no = (int)(cp - nbuf);
|
|
#ifdef IPFTP_TLS
|
|
if((ipftps_tls_enbale == IP_TRUE)
|
|
&& ((ret = ipftps_tls_send(session,fd,nbuf,no)) != 2))
|
|
{
|
|
return ret;
|
|
}
|
|
else
|
|
#endif
|
|
if (ipcom_socketwrite(fd, nbuf, no) != no)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_dirindex
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* Calculate level of directory path.
|
|
*
|
|
* Parameters:
|
|
* path Directory path.
|
|
* idomin Min flag.
|
|
*
|
|
* Returns:
|
|
* Final subdirectory level if idomin == 0,
|
|
* minimum subdirectory level if idomin == 1.
|
|
*/
|
|
IP_STATIC int
|
|
ipftps_dirindex(char *path, int idomin)
|
|
{
|
|
int level = 0;
|
|
int min = 0;
|
|
char *start;
|
|
char *stop;
|
|
char *cp;
|
|
|
|
cp = path;
|
|
|
|
while (*cp != '\0')
|
|
{
|
|
char c;
|
|
|
|
start = cp;
|
|
cp += ipcom_strcspn(cp, IPFTPS_DIRSEP);
|
|
stop = cp;
|
|
cp += ipcom_strspn(cp, IPFTPS_DIRSEP);
|
|
c = *stop;
|
|
*stop = '\0';
|
|
|
|
if (ipcom_strcmp(start, "..") == 0)
|
|
{
|
|
level--;
|
|
}
|
|
else if (*start != '\0' && ipcom_strcmp(start, ".") != 0)
|
|
{
|
|
level++;
|
|
}
|
|
|
|
*stop = c;
|
|
|
|
if (level < min)
|
|
{
|
|
min = level;
|
|
}
|
|
}
|
|
|
|
return (idomin ? min : level);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_dircmp
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* Check if dir is OK wrt root.
|
|
*
|
|
* Parameters:
|
|
* root Directory root.
|
|
* path Directory path.
|
|
*
|
|
* Returns:
|
|
* 0 if OK, 1 otherwise.
|
|
*/
|
|
IP_STATIC int
|
|
ipftps_dircmp(char *root, char *path)
|
|
{
|
|
Ip_size_t len;
|
|
int i;
|
|
|
|
len = ipcom_strlen(root);
|
|
|
|
for (i = 0; i < (int)len; i++)
|
|
{
|
|
if ((ipcom_strchr(IPFTPS_DIRSEP, root[i]) &&
|
|
ipcom_strchr(IPFTPS_DIRSEP, path[i])) ||
|
|
root[i] == path[i])
|
|
{
|
|
/* OK so far */
|
|
}
|
|
else
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_root_check
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* Check if path valid, and prepare for its use in next command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* path Directory path.
|
|
*
|
|
* Returns:
|
|
* 0 if path OK, 1 otherwise.
|
|
*/
|
|
IP_STATIC int
|
|
ipftps_root_check(Ipftps_session *session, char **path)
|
|
{
|
|
char *newpath = *path;
|
|
Ip_size_t len;
|
|
/* If the input *path is a root path, using path directly without changing
|
|
session->cwd to be session->root. Eg: session->root is /ram0. And *path is
|
|
/test_dir/test.txt. Then the absolute path will be /ram0/test_dir/test.txt */
|
|
Ip_bool root_path = IP_FALSE;
|
|
|
|
/* Device or drive letter? */
|
|
if (newpath != *path)
|
|
{
|
|
/* Empty session root is required to change drive/device */
|
|
if (session->root[0] != '\0')
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
newpath = *path;
|
|
}
|
|
else if (ipcom_strchr(IPFTPS_DIRSEP, *newpath) != IP_NULL)
|
|
{
|
|
char *orgpath = newpath;
|
|
|
|
/*
|
|
** Absolute path, remove initial slash
|
|
** to make it relative to root.
|
|
*/
|
|
if (*++newpath == '\0')
|
|
{
|
|
*--newpath = '.';
|
|
}
|
|
|
|
/* Check for trespassing */
|
|
if (ipftps_dirindex(newpath, 1) < 0)
|
|
{
|
|
/* Peeked above root */
|
|
return 1;
|
|
}
|
|
|
|
if (session->root[0] == '\0' ||
|
|
(ipcom_strchr(IPFTPS_DIRSEP, session->root[0]) != IP_NULL && session->root[1] == '\0'))
|
|
{
|
|
/*
|
|
** Session root is /, and it may not be safe to cd there
|
|
** since the file system may have its root at e.g. /ram.
|
|
** Use the absolute path directly.
|
|
*/
|
|
if (newpath == orgpath)
|
|
{
|
|
*newpath = '/';
|
|
}
|
|
else
|
|
{
|
|
newpath = orgpath;
|
|
}
|
|
}
|
|
else /* Session root is /ram0 */
|
|
{
|
|
root_path = IP_TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Check for trespassing */
|
|
if ((ipftps_dirindex(session->cwd, 0) + ipftps_dirindex(newpath, 1)) <
|
|
ipftps_dirindex(session->root, 0))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
/* Set CWD */
|
|
if (ipftps_chdir(session, session->cwd) != 0)
|
|
{
|
|
IPCOM_LOG1(WARNING, "Invalid current directory: %s", session->cwd);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* Remove possible trailing slash */
|
|
len = ipcom_strlen(newpath);
|
|
if (len > 1 && ipcom_strchr(IPFTPS_DIRSEP, newpath[len - 1]) != IP_NULL)
|
|
{
|
|
newpath[len - 1] = '\0';
|
|
}
|
|
|
|
*path = newpath;
|
|
|
|
#ifdef IP_PORT_VXWORKS
|
|
{
|
|
char *fullPath = IP_NULL;
|
|
const char *pTail;
|
|
char *tmpPath = IP_NULL;
|
|
size_t pathLen = ipcom_strlen(*path);
|
|
size_t dirLen = 0;
|
|
|
|
if ((fullPath = ipcom_malloc(IP_PATH_MAX)) == NULL)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if (root_path == IP_TRUE)
|
|
{
|
|
dirLen = ipcom_strlen(session->root);
|
|
if (pathLen + dirLen >= IP_PATH_MAX)
|
|
{
|
|
ipcom_errno = IP_ERRNO_ENAMETOOLONG;
|
|
ipftps_free(fullPath);
|
|
return 1;
|
|
}
|
|
/* Use the absolute path for vxworks */
|
|
if (pathCat(session->root, *path, fullPath) == ERROR)
|
|
{
|
|
ipftps_free(fullPath);
|
|
return 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dirLen = ipcom_strlen(session->cwd);
|
|
if (pathLen + dirLen >= IP_PATH_MAX)
|
|
{
|
|
ipcom_errno = IP_ERRNO_ENAMETOOLONG;
|
|
ipftps_free(fullPath);
|
|
return 1;
|
|
}
|
|
/* Use the absolute path for vxworks */
|
|
if (pathCat(session->cwd, *path, fullPath) == ERROR)
|
|
{
|
|
ipftps_free(fullPath);
|
|
return 1;
|
|
}
|
|
}
|
|
pathCondense(fullPath);
|
|
|
|
tmpPath = *path;
|
|
if ((tmpPath[strlen(tmpPath) - 1] == '/') && strcmp(fullPath, "/"))
|
|
{
|
|
ipcom_strcat(fullPath, "/");
|
|
}
|
|
|
|
/* Make sure that new path starts with a device name */
|
|
iosDevFind (fullPath, &pTail);
|
|
|
|
if (pTail != fullPath)
|
|
{
|
|
ipcom_strcpy(*path, fullPath);
|
|
ipftps_free(fullPath);
|
|
return 0;
|
|
}
|
|
|
|
if (tmpPath[strlen(tmpPath) - 1] == '.')
|
|
{
|
|
ipcom_strcat(fullPath, "/");
|
|
iosDevFind (fullPath, &pTail);
|
|
if (pTail != fullPath)
|
|
{
|
|
ipcom_strcpy(*path, fullPath);
|
|
ipftps_free(fullPath);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
ipftps_free(fullPath);
|
|
return 1; /* ENODEV */
|
|
}
|
|
#else
|
|
if(root_path == IP_TRUE)
|
|
{
|
|
if(ipftps_chdir(session, session->root) == 0)
|
|
{
|
|
session->cwdroot = 1;
|
|
}
|
|
else
|
|
{
|
|
IPCOM_LOG1(WARNING, "Invalid root dir: %s", session->root);
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_dir_check
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* Handle potential cwd change.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
*
|
|
* Returns:
|
|
* 0 if success, -1 otherwise.
|
|
*/
|
|
IP_STATIC int
|
|
ipftps_dir_check(Ipftps_session *session)
|
|
{
|
|
#ifndef IP_PORT_VXWORKS
|
|
if (session->cwd != session->root)
|
|
{
|
|
ipftps_free(session->cwd);
|
|
}
|
|
|
|
if ((session->cwd = ipcom_malloc(IP_PATH_MAX)) == IP_NULL ||
|
|
ipcom_getcwd(session->cwd, IP_PATH_MAX) == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR, "Out of memory, switched to root");
|
|
|
|
/* Avoid malloc here */
|
|
if (session->cwd != session->root)
|
|
{
|
|
ipftps_free(session->cwd);
|
|
}
|
|
session->cwd = session->root;
|
|
return -1;
|
|
} else
|
|
#endif
|
|
|
|
if (ipftps_dircmp(session->root,session->cwd) != 0)
|
|
{
|
|
/*
|
|
** This is never supposed to happen, teardown
|
|
** session to prevent from security breaches.
|
|
*/
|
|
IPCOM_LOG0(ERR, "Directory error, terminating session");
|
|
session->authentications = 0;
|
|
ipcom_errno = IP_ERRNO_ENOTDIR;
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_sysget_int
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* Get integer system variable.
|
|
*
|
|
* Parameters:
|
|
* name Variable name.
|
|
* defval Default value if not found.
|
|
* message Print message if not found.
|
|
*
|
|
* Returns:
|
|
* Variable value.
|
|
*/
|
|
IP_STATIC int
|
|
ipftps_sysget_int(const char *name, int defval, Ip_bool message)
|
|
{
|
|
Ipftps_session *session = IP_NULL;
|
|
char strval[12];
|
|
Ip_size_t strval_len = sizeof(strval);
|
|
|
|
(void)session;
|
|
|
|
if (ipcom_sysvar_get(name, strval, &strval_len) == IP_NULL)
|
|
{
|
|
if (message)
|
|
{
|
|
IPCOM_LOG2(WARNING, "Missing sysvar %s, using %d", name, defval);
|
|
}
|
|
|
|
return defval;
|
|
}
|
|
|
|
return ipcom_atoi(strval);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_sysget_str
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* Get string system variable.
|
|
*
|
|
* Parameters:
|
|
* name Variable name.
|
|
* defval Default value if not found.
|
|
* message Print message if not found.
|
|
*
|
|
* Returns:
|
|
* Variable value.
|
|
*/
|
|
IP_STATIC char *
|
|
ipftps_sysget_str(const char *name, char *buf, Ip_size_t *bufsize,
|
|
const char *defval, Ip_bool message)
|
|
{
|
|
Ip_size_t oldsize = (buf == IP_NULL ? 0 : *bufsize);
|
|
Ipftps_session *session = IP_NULL;
|
|
char *ret;
|
|
|
|
(void)session;
|
|
|
|
if ((ret = ipcom_sysvar_get(name, buf, bufsize)) == IP_NULL)
|
|
{
|
|
if (oldsize > 0 && *bufsize > oldsize)
|
|
{
|
|
if (message)
|
|
{
|
|
IPCOM_LOG2(WARNING, "Buffer overflow reading sysvar %s, using %s",
|
|
name, defval);
|
|
}
|
|
}
|
|
else if (message)
|
|
{
|
|
IPCOM_LOG2(WARNING, "Missing sysvar %s, using %s",
|
|
name, defval);
|
|
}
|
|
|
|
ret = (buf == IP_NULL ? ipcom_strdup(defval) :
|
|
ipcom_strcpy(buf, defval));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_ls
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* List file (long format).
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* fd File descriptor.
|
|
* path1 1st part of path.
|
|
* path2 2nd part of path.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC int
|
|
ipftps_ls(Ipftps_session *session, Ip_fd fd, char *path1, char *path2)
|
|
{
|
|
static const Ip_mode_t perm[] =
|
|
{
|
|
IP_S_IRUSR, IP_S_IWUSR, IP_S_IXUSR,
|
|
IP_S_IRGRP, IP_S_IWGRP, IP_S_IXGRP,
|
|
IP_S_IROTH, IP_S_IWOTH, IP_S_IXOTH
|
|
};
|
|
char mode[] = "-rwxrwxrwx";
|
|
char timestr[16];
|
|
#ifdef IPCOM_USE_TIME
|
|
Ip_time_t now;
|
|
struct Ip_tm tm;
|
|
#endif
|
|
struct Ip_stat st;
|
|
char *opath;
|
|
char *path;
|
|
Ip_size_t p1len = ipcom_strlen(path1);
|
|
int ret = 0;
|
|
Ip_size_t i;
|
|
|
|
if ((opath = path = ipcom_malloc(ipcom_strlen(path1) +
|
|
ipcom_strlen(path2) + 2)) == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR, "Out of memory");
|
|
ipftps_reply(session, "452 Out of memory"CL);
|
|
ret = 1;
|
|
goto leave2;
|
|
}
|
|
|
|
ipcom_strcpy(path, path1);
|
|
ipcom_strcat(path, (p1len != 0 && path[p1len - 1] != '/' ? "/" : ""));
|
|
ipcom_strcat(path, path2);
|
|
|
|
if (ipcom_stat(path, &st) != 0)
|
|
{
|
|
ipcom_memset(&st, 0, sizeof(st));
|
|
}
|
|
|
|
/* Set mode */
|
|
if (IP_S_ISDIR(st.st_mode))
|
|
mode[0] = 'd';
|
|
#ifdef IP_S_ISCHR
|
|
else if (IP_S_ISCHR(st.st_mode))
|
|
mode[0] = 'c';
|
|
#endif
|
|
#ifdef IP_S_ISBLK
|
|
else if (IP_S_ISBLK(st.st_mode))
|
|
mode[0] = 'b';
|
|
#endif
|
|
#ifdef IP_S_ISFIFO
|
|
else if (IP_S_ISFIFO(st.st_mode))
|
|
mode[0] = 'p';
|
|
#endif
|
|
#ifdef IP_S_ISLNK
|
|
else if (IP_S_ISLNK(st.st_mode))
|
|
mode[0] = 'l';
|
|
#endif
|
|
#ifdef IP_S_ISSOCK
|
|
else if (IP_S_ISSOCK(st.st_mode))
|
|
mode[0] = 's';
|
|
#endif
|
|
|
|
/* Set permissions */
|
|
for (i = 0; i < (sizeof(perm) / sizeof perm[0]); i++)
|
|
{
|
|
if ((st.st_mode & perm[i]) == 0)
|
|
{
|
|
mode[1 + i] = '-';
|
|
}
|
|
}
|
|
|
|
/* Create window for fine grain time */
|
|
#ifdef IPCOM_USE_TIME
|
|
(void)ipcom_time(&now);
|
|
(void)ipcom_localtime_r(&st.st_mtime, &tm);
|
|
if (st.st_mtime < (now - (6L * IPFTPS_MONTHS)) ||
|
|
st.st_mtime > (now + (1L * IPFTPS_HOURS)))
|
|
{
|
|
/* Low resolution */
|
|
i = ipcom_strftime(timestr, sizeof(timestr), "%b %d %Y", &tm);
|
|
}
|
|
else
|
|
{
|
|
/* High resolution */
|
|
i = ipcom_strftime(timestr, sizeof(timestr), "%b %d %H:%M", &tm);
|
|
}
|
|
|
|
/* Terminate timestr. */
|
|
if (i == 0)
|
|
timestr[0] = '\0';
|
|
else if (i == sizeof(timestr))
|
|
timestr[sizeof(timestr) - 1] = '\0';
|
|
#else
|
|
ipcom_strcpy(timestr, "-");
|
|
#endif
|
|
|
|
ipftps_reply_sock(session, fd,
|
|
"%s %3d %-6d %-6d %10lu %12.12s %s%s"CL,
|
|
mode, st.st_nlink, st.st_uid, st.st_gid,
|
|
(unsigned long)st.st_size, timestr,
|
|
(session->cwdroot && path1[0] == '\0' ? "/" : ""),
|
|
path2);
|
|
|
|
ipcom_free(opath);
|
|
|
|
leave2:
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_abor
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* ABOR command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_abor(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
(void)argc;
|
|
(void)argv;
|
|
|
|
if (session->dsock.fd == -1)
|
|
{
|
|
ipftps_reply(session, "226 No data connection"CL);
|
|
}
|
|
else
|
|
{
|
|
ipftps_dsock_close(session);
|
|
ipftps_reply(session, "226 Closing data connection"CL);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_acct
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* ACCT command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_acct(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
(void)argc;
|
|
(void)argv;
|
|
|
|
ipftps_reply(session, "202 Command not implemented"CL);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_appe
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* APPE command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
|
|
IP_STATIC void
|
|
ipftps_appe(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
argv[argc++] = "a";
|
|
|
|
ipftps_stor(session, argc, argv);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_cdup
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* CDUP command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
|
|
IP_STATIC void
|
|
ipftps_cdup(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
char *dotdotp;
|
|
char dotdot[IP_PATH_MAX];
|
|
|
|
(void)argc;
|
|
(void)argv;
|
|
|
|
ipcom_strcpy(dotdot,"..");
|
|
dotdotp = dotdot;
|
|
|
|
ipftps_cwd(session, 1, &dotdotp);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_cwd
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* CWD command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
|
|
IP_STATIC void
|
|
ipftps_cwd(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
(void)argc;
|
|
|
|
if (ipftps_root_check(session, &argv[0]) != 0)
|
|
{
|
|
IPCOM_LOG2(WARNING, "Access denied to %s from %s",
|
|
argv[0], session->cwd);
|
|
ipftps_reply(session, "550 Permission denied"CL);
|
|
}
|
|
else if (ipftps_chdir(session, argv[0]) != 0)
|
|
{
|
|
ipftps_perror(session, 550);
|
|
}
|
|
else if (ipftps_dir_check(session) != 0)
|
|
{
|
|
ipftps_perror(session, (ipcom_errno == IP_ERRNO_ENOMEM ? 452 : 550));
|
|
}
|
|
else
|
|
{
|
|
ipftps_reply(session, "250 CWD changed"CL);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_dele
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* DELE command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
|
|
IP_STATIC void
|
|
ipftps_dele(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
(void)argc;
|
|
|
|
if (session->readonly)
|
|
{
|
|
IPCOM_LOG0(WARNING, "File deletion denied");
|
|
ipftps_reply(session, "550 Permission denied"CL);
|
|
}
|
|
else if (ipftps_root_check(session, &argv[0]) != 0)
|
|
{
|
|
IPCOM_LOG2(WARNING, "Access denied to %s from %s",
|
|
argv[0], session->cwd);
|
|
ipftps_reply(session, "550 Permission denied"CL);
|
|
}
|
|
else if (ipcom_unlink(argv[0]) != 0)
|
|
{
|
|
ipftps_perror(session, 550);
|
|
}
|
|
else
|
|
{
|
|
ipftps_reply(session, "250 File deleted"CL);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_eprt
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* EPRT command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_eprt(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
union Ip_sockaddr_union addr;
|
|
Ip_u32 h[3];
|
|
int i;
|
|
|
|
(void)argc;
|
|
|
|
if (session->epsvonly)
|
|
{
|
|
ipftps_reply(session, "501 In extended passive mode"CL);
|
|
return;
|
|
}
|
|
|
|
session->passive = 0;
|
|
|
|
/* Parse first and last argument */
|
|
for (i = 0; i < 3; i += 2)
|
|
{
|
|
char *stop;
|
|
|
|
h[i] = (Ip_u32)ipcom_strtol(argv[i], &stop, 10);
|
|
|
|
if (*stop != '\0' || h[i] > 65535)
|
|
{
|
|
ipftps_reply(session, "501 Bad address family or port"CL);
|
|
return;
|
|
}
|
|
}
|
|
|
|
ipcom_memset(&addr, 0, sizeof(addr));
|
|
|
|
switch (h[0])
|
|
{
|
|
case 1:
|
|
/* IPv4 */
|
|
IPCOM_SA_LEN_SET(&addr.sin, sizeof(struct Ip_sockaddr_in));
|
|
addr.sin.sin_family = IP_AF_INET;
|
|
|
|
if (ipcom_inet_pton(IP_AF_INET, argv[1], &addr.sin.sin_addr) != 1)
|
|
{
|
|
ipftps_reply(session, "501 Bad IPv4 address"CL);
|
|
return;
|
|
}
|
|
addr.sin.sin_port = (Ip_u16)ip_htons(h[2]);
|
|
break;
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
case 2:
|
|
/* IPv6 */
|
|
ipcom_memset(&addr.sin6, 0, sizeof(addr.sin6));
|
|
IPCOM_SA_LEN_SET(&addr.sin6, sizeof(struct Ip_sockaddr_in6));
|
|
addr.sin6.sin6_family = IP_AF_INET6;
|
|
if (ipcom_inet_pton(IP_AF_INET6, argv[1], &addr.sin6.sin6_addr) != 1)
|
|
{
|
|
ipftps_reply(session, "501 Bad IPv6 address"CL);
|
|
return;
|
|
}
|
|
addr.sin6.sin6_port = (Ip_u16)ip_htons(h[2]);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
ipftps_reply(session, "522 Bad address family, use " IPFTPS_AFSTR CL);
|
|
return;
|
|
}
|
|
|
|
if (session->proxy == 0 &&
|
|
IPFTPS_ADDRCMP(&addr, &session->csock.paddr) != 0)
|
|
{
|
|
IPCOM_LOG1(WARNING, "EPRT IP address %s denied", argv[1]);
|
|
ipftps_reply(session, "504 Invalid host"CL);
|
|
return;
|
|
}
|
|
|
|
if (h[2] <= session->pportbase)
|
|
{
|
|
IPCOM_LOG1(WARNING, "EPRT port number %u denied",
|
|
(unsigned int)h[2]);
|
|
ipftps_reply(session, "504 Invalid port"CL);
|
|
return;
|
|
}
|
|
|
|
ipcom_memcpy(&session->dsock.paddr, &addr, sizeof(session->dsock.paddr));
|
|
|
|
ipftps_reply(session, "200 Extended port OK"CL);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_epsv
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* EPSV command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
|
|
IP_STATIC void
|
|
ipftps_epsv(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
int af = (session->csock.paddr.sa.sa_family == IP_AF_INET ? 1 : 2);
|
|
|
|
if (argc == 1)
|
|
{
|
|
if (ipcom_strcasecmp(argv[0], "ALL") == 0)
|
|
{
|
|
ipftps_reply(session, "200 Locked in extended passive mode"CL);
|
|
session->epsvonly = 1;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
char *stop;
|
|
|
|
af = (int)ipcom_strtol(argv[0], &stop, 10);
|
|
|
|
if (*stop != '\0' || af < 1 || af > IPFTPS_AFMAX)
|
|
{
|
|
ipftps_reply(session, "522 Bad address family, use "
|
|
IPFTPS_AFSTR CL);
|
|
return;
|
|
}
|
|
|
|
if (session->proxy == 0 && af != session->csock.paddr.sa.sa_family)
|
|
{
|
|
ipftps_reply(session, "522 Bad address family, use (%d)" CL,
|
|
(session->csock.paddr.sa.sa_family == IP_AF_INET ?
|
|
1 : 2));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
session->dsock.paddr.sa.sa_family = (Ip_sa_family_t)(af == 1 ? IP_AF_INET : IP_AF_INET6);
|
|
|
|
if (ipftps_dsock_open(session, 1) != 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ipftps_reply(session,
|
|
"229 Entering extended passive mode (|||%u|)"CL,
|
|
(unsigned int)ip_ntohs(IPFTPS_PORT(&session->dsock.laddr)));
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_help
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* HELP command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_help(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
const Ipftps_cmd *cmd;
|
|
|
|
if (argc == 0)
|
|
{
|
|
ipftps_reply(session, "211- Command list:"CL);
|
|
}
|
|
|
|
/* Loop for help texts */
|
|
for (cmd = ipftps_cmdlist; cmd->name != IP_NULL; cmd++)
|
|
{
|
|
if (argc == 0)
|
|
{
|
|
/* Help on all commands */
|
|
ipftps_reply(session, " %4s - %s"CL, cmd->name, cmd->help);
|
|
}
|
|
else if (ipcom_strcasecmp(argv[0], cmd->name) == 0)
|
|
{
|
|
/* Help on specific command */
|
|
ipftps_reply(session, "214 %s - %s"CL, cmd->name, cmd->help);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Terminate */
|
|
if (argc == 0)
|
|
{
|
|
ipftps_reply(session, "211 Use HELP <cmd> for help on cmd"CL);
|
|
}
|
|
else
|
|
{
|
|
ipftps_reply(session, "501 No help available"CL);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_list
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* LIST command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
|
|
IP_STATIC void
|
|
ipftps_list(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
struct Ip_stat st;
|
|
char *path = argv[0];
|
|
struct dirent *ent;
|
|
char dot[IP_PATH_MAX];
|
|
int ret = 0;
|
|
Ip_bool aflag = IP_FALSE;
|
|
|
|
ipcom_strcpy(dot,".");
|
|
|
|
if (argc == 0)
|
|
{
|
|
path = dot;
|
|
}
|
|
else if ((ipcom_strncmp(path, "-l", 2) == 0) ||
|
|
(ipcom_strncmp(path, "-al", 3) == 0))
|
|
{
|
|
if (ipcom_strncmp(path, "-al", 3) == 0)
|
|
aflag = IP_TRUE;
|
|
path = dot;
|
|
}
|
|
|
|
if (ipftps_root_check(session, &path) != 0)
|
|
{
|
|
IPCOM_LOG2(WARNING, "Access denied to %s from %s",
|
|
path, session->cwd);
|
|
ipftps_reply(session, "550 Permission denied"CL);
|
|
return;
|
|
}
|
|
|
|
if (ipcom_stat(path, &st) != 0)
|
|
{
|
|
ipftps_perror(session, 550);
|
|
return;
|
|
}
|
|
|
|
/* Open data socket */
|
|
if (ipftps_dsock_open(session, 0) != 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Dir or file? */
|
|
if (IP_S_ISDIR(st.st_mode))
|
|
{
|
|
DIR *dir;
|
|
|
|
dir = opendir(path);
|
|
if (dir == IP_NULL)
|
|
{
|
|
ipftps_perror(session, 550);
|
|
ipftps_dsock_close(session);
|
|
return;
|
|
}
|
|
|
|
/* Loop for files */
|
|
while ((ent = readdir(dir)) != 0)
|
|
{
|
|
if ((aflag == IP_TRUE) || (ent->d_name[0] != '.'))
|
|
{
|
|
if ((ret = ipftps_ls(session, session->dsock.fd, path, ent->d_name)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (closedir(dir) != 0)
|
|
{
|
|
IPCOM_LOG2(WARNING, "Directory close failed for %s: %s",
|
|
path, ipcom_strerror(ipcom_errno));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret = ipftps_ls(session, session->dsock.fd, "", path);
|
|
}
|
|
|
|
ipftps_dsock_close(session);
|
|
|
|
if (ret == 0)
|
|
{
|
|
ipftps_reply(session, "226 Transfer complete"CL);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_mkd
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* MKD command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_mkd(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
char *path = argv[0];
|
|
#ifdef IP_PORT_VXWORKS
|
|
char *argv0 = IP_NULL;
|
|
#endif
|
|
(void)argc;
|
|
|
|
#ifdef IP_PORT_VXWORKS
|
|
if(argv[0])
|
|
{
|
|
if ((argv0 = ipcom_malloc(IP_PATH_MAX)) == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR, "Out of memory");
|
|
ipftps_reply(session, "452 Out of memory"CL);
|
|
return;
|
|
}
|
|
ipcom_strcpy(argv0, argv[0]);
|
|
}
|
|
#endif
|
|
if (session->readonly)
|
|
{
|
|
IPCOM_LOG0(WARNING, "Make dir denied");
|
|
ipftps_reply(session, "550 Permission denied"CL);
|
|
}
|
|
else if (ipftps_root_check(session, &path) != 0)
|
|
{
|
|
IPCOM_LOG2(WARNING, "Access denied to %s from %s",
|
|
argv[0], session->cwd);
|
|
ipftps_reply(session, "550 Permission denied"CL);
|
|
}
|
|
else if (ipcom_mkdir(path, 0777) != 0)
|
|
{
|
|
ipftps_perror(session, 550);
|
|
}
|
|
else
|
|
{
|
|
#ifdef IP_PORT_VXWORKS
|
|
ipftps_reply(session, "257 \"%s\" directory created"CL, argv0);
|
|
#else
|
|
ipftps_reply(session, "257 \"%s\" directory created"CL, argv[0]);
|
|
#endif
|
|
}
|
|
#ifdef IP_PORT_VXWORKS
|
|
ipftps_free(argv0);
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_mode
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* MODE command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_mode(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
char *mode = argv[0];
|
|
|
|
(void)argc;
|
|
|
|
if (ipcom_toupper(mode[0]) != 'S' || mode[1] != '\0')
|
|
{
|
|
ipftps_reply(session, "504 Only S supported"CL);
|
|
}
|
|
else
|
|
{
|
|
ipftps_reply(session, "200 Stream mode selected"CL);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_nlst
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* NLST command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_nlst(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
struct Ip_stat st;
|
|
char *path = argv[0];
|
|
struct dirent *ent;
|
|
char dot[IP_PATH_MAX];
|
|
char *pat = IP_NULL;
|
|
char *path1 = IP_NULL;
|
|
char *nbuf = IP_NULL;
|
|
char *ptr = IP_NULL;
|
|
|
|
ipcom_strcpy(dot,".");
|
|
|
|
if (argc == 0)
|
|
{
|
|
path = dot;
|
|
}
|
|
else if((ipcom_strncmp(path, "-l", 2) == 0) ||
|
|
(ipcom_strncmp(path, "-al", 3) == 0))
|
|
{
|
|
ipftps_list(session, argc, argv);
|
|
return;
|
|
}
|
|
|
|
path1= ipcom_malloc(IP_PATH_MAX);
|
|
if (path1 == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR, "Out of memory");
|
|
ipftps_reply(session, "452 Out of memory"CL);
|
|
return;
|
|
}
|
|
nbuf= ipcom_malloc(IP_PATH_MAX);
|
|
if (nbuf == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR, "Out of memory");
|
|
ipftps_reply(session, "452 Out of memory"CL);
|
|
goto exit;
|
|
}
|
|
ipcom_strcpy(path1, path);
|
|
if (ipftps_root_check(session, &path) != 0)
|
|
{
|
|
IPCOM_LOG2(WARNING, "Access denied to %s from %s",
|
|
path, session->cwd);
|
|
ipftps_reply(session, "550 Permission denied"CL);
|
|
goto exit;
|
|
}
|
|
|
|
if (ipcom_stat(path, &st) != 0)
|
|
{
|
|
/* Stat failed. Check for a wild cards in the path */
|
|
if((pat = ipftps_wildcard_check(path, &st)) == IP_NULL)
|
|
{
|
|
ipftps_perror(session, 550);
|
|
goto exit;
|
|
}
|
|
if ((ptr = ipcom_strrchr(path1,'/')) == IP_NULL)
|
|
ptr = ipcom_strrchr(path1,'\\');
|
|
if (ptr != IP_NULL) *(++ptr) = '\0';
|
|
}
|
|
|
|
/* Open data socket */
|
|
if (ipftps_dsock_open(session, 0) != 0)
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
/* Dir or file? */
|
|
if (IP_S_ISDIR(st.st_mode))
|
|
{
|
|
DIR *dir;
|
|
|
|
dir = opendir(path);
|
|
if (dir == IP_NULL)
|
|
{
|
|
ipftps_perror(session, 550);
|
|
ipftps_dsock_close(session);
|
|
goto exit;
|
|
}
|
|
|
|
/* Loop for files */
|
|
ent = readdir(dir);
|
|
|
|
while (ent != 0)
|
|
{
|
|
if (ent->d_name[0] != '.')
|
|
{
|
|
if(pat == IP_NULL)
|
|
{
|
|
ipftps_reply_sock(session, session->dsock.fd,
|
|
"%s"CL, ent->d_name);
|
|
}
|
|
else
|
|
{
|
|
if (ipftps_pattern_check(pat, ent->d_name))
|
|
{
|
|
if (ptr == IP_NULL)
|
|
ipcom_snprintf(nbuf, IP_PATH_MAX,"%s", ent->d_name);
|
|
else
|
|
ipcom_snprintf(nbuf, IP_PATH_MAX,"%s%s",path1, ent->d_name);
|
|
ipftps_reply_sock(session, session->dsock.fd,
|
|
"%s"CL, nbuf);
|
|
}
|
|
}
|
|
}
|
|
ent = readdir(dir);
|
|
}
|
|
|
|
if (closedir(dir) != 0)
|
|
{
|
|
IPCOM_LOG2(WARNING, "Directory close failed for %s: %s",
|
|
path, ipcom_strerror(ipcom_errno));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ipftps_reply_sock(session, session->dsock.fd, "%s%s"CL,
|
|
(session->cwdroot ? "/" : ""), path);
|
|
}
|
|
|
|
ipftps_dsock_close(session);
|
|
|
|
ipftps_reply(session, "226 Transfer complete"CL);
|
|
exit:
|
|
ipcom_free(pat);
|
|
ipcom_free(path1);
|
|
ipcom_free(nbuf);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_noop
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* NOOP command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_noop(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
(void)argc;
|
|
(void)argv;
|
|
|
|
ipftps_reply(session, "200 OK"CL);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_pass
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* PASS command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_pass(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
int ret;
|
|
#if defined(IP_PORT_VXWORKS) && defined(LOGINEVENT_CHECK_AUTH_STAT)
|
|
int event_err;
|
|
int auth_result = 0;
|
|
#endif
|
|
|
|
(void)argc;
|
|
|
|
if (session->login != 0)
|
|
{
|
|
ipftps_reply(session, "503 Already logged in"CL);
|
|
return;
|
|
}
|
|
|
|
if (session->last == IP_NULL ||
|
|
ipcom_strcasecmp(session->last->name, "USER") != 0 ||
|
|
session->username == IP_NULL)
|
|
{
|
|
ipftps_reply(session, "503 Login with USER first"CL);
|
|
return;
|
|
}
|
|
|
|
ret = ipftps_authenticate(session, argv[0]);
|
|
|
|
#ifdef IP_PORT_VXWORKS
|
|
#ifdef LOGINEVENT_CHECK_AUTH_STAT
|
|
if (ret != 0)
|
|
auth_result = SEC_EVENT_AUTH_FAILED;
|
|
else
|
|
auth_result = SEC_EVENT_AUTH_SUCCESS;
|
|
|
|
if (_func_loginEvent != IP_NULL)
|
|
event_err = _func_loginEvent(SEC_EVENT_FTP_LOGIN, session->username, session->csock.fd, SEC_EVENT_AUTH_PASSWD, auth_result);
|
|
if (event_err != 0)
|
|
ret = -1;
|
|
#else
|
|
if (ret == 0)
|
|
{
|
|
if (_func_loginEvent != IP_NULL)
|
|
_func_loginEvent(SEC_EVENT_FTP_LOGIN, session->username, session->csock.fd, SEC_EVENT_AUTH_PASSWD);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
if (ret != 0)
|
|
{
|
|
int secs;
|
|
|
|
if ((secs = ipftps_sysget_int("ipftps.authsleep", 5, 1)) != 0)
|
|
{
|
|
(void)ipcom_sleep(secs);
|
|
}
|
|
|
|
ipftps_reply(session, (--session->authentications <= 0 ?
|
|
"421 Service not available, closing control connection"CL :
|
|
"530 Authorization failed"CL));
|
|
return;
|
|
}
|
|
|
|
/* Change to selected directory */
|
|
if (ipftps_chdir(session, session->cwd) != 0 ||
|
|
ipftps_dir_check(session) != 0)
|
|
{
|
|
IPCOM_LOG1(ERR, "Invalid working directory: %s", session->cwd);
|
|
ipftps_reply(session, "421 Service not available, "
|
|
"closing control connection"CL);
|
|
session->authentications = 0;
|
|
return;
|
|
}
|
|
|
|
session->login = 1;
|
|
|
|
ipftps_reply(session, "230 Login OK"CL);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_pasv
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* PASV command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_pasv(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
int h[4],p[2];
|
|
Ip_u32 v4addr;
|
|
Ip_u16 v4port;
|
|
|
|
(void)argc;
|
|
(void)argv;
|
|
|
|
if (session->epsvonly)
|
|
{
|
|
ipftps_reply(session, "501 In extended passive mode"CL);
|
|
return;
|
|
}
|
|
|
|
IPCOM_SA_LEN_SET(&session->dsock.paddr.sin, sizeof(struct Ip_sockaddr_in));
|
|
session->dsock.paddr.sin.sin_family = IP_AF_INET;
|
|
|
|
if (ipftps_dsock_open(session, 1) != 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
v4addr = ip_ntohl(session->dsock.laddr.sin.sin_addr.s_addr);
|
|
h[0] = (v4addr >> 24) & 0xFF;
|
|
h[1] = (v4addr >> 16) & 0xFF;
|
|
h[2] = (v4addr >> 8) & 0xFF;
|
|
h[3] = v4addr & 0xFF;
|
|
|
|
v4port = ip_ntohs(session->dsock.laddr.sin.sin_port);
|
|
p[0] = (v4port >> 8) & 0xFF;
|
|
p[1] = v4port & 0xFF;
|
|
|
|
ipftps_reply(session,
|
|
"227 Entering passive mode (%d,%d,%d,%d,%d,%d)"CL,
|
|
h[0], h[1], h[2], h[3], p[0], p[1]);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_port
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* PORT command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_port(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
Ip_u32 h[6];
|
|
int i;
|
|
Ip_u32 addr;
|
|
Ip_u16 port;
|
|
|
|
(void)argc;
|
|
|
|
if (session->epsvonly)
|
|
{
|
|
ipftps_reply(session, "501 In extended passive mode"CL);
|
|
return;
|
|
}
|
|
|
|
session->passive = 0;
|
|
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
char *stop;
|
|
|
|
h[i] = (Ip_u32)ipcom_strtol(argv[i], &stop, 10);
|
|
|
|
if (*stop != '\0' || h[i] > 255)
|
|
{
|
|
ipftps_reply(session, "501 Bad IP address or port"CL);
|
|
return;
|
|
}
|
|
}
|
|
|
|
addr = (h[0] << 24) | (h[1] << 16) | (h[2] << 8) | h[3];
|
|
port = (Ip_u16) ((h[4] << 8) | h[5]);
|
|
|
|
if (session->proxy == 0 &&
|
|
(session->csock.paddr.sa.sa_family != IP_AF_INET ||
|
|
addr != ip_ntohl(session->csock.paddr.sin.sin_addr.s_addr)))
|
|
{
|
|
char buf[IPFTPS_IPV4_MAXLEN];
|
|
Ip_u32 u = ip_htonl(addr);
|
|
|
|
(void)ipcom_inet_ntop(IP_AF_INET, &u, buf, sizeof(buf));
|
|
IPCOM_LOG1(WARNING, "PORT IP address %s denied", buf);
|
|
ipftps_reply(session, "504 Invalid host"CL);
|
|
return;
|
|
}
|
|
|
|
if (port <= session->pportbase)
|
|
{
|
|
IPCOM_LOG1(WARNING, "PORT port number %lu denied", (Ip_u32)port);
|
|
ipftps_reply(session, "504 Invalid port"CL);
|
|
return;
|
|
}
|
|
|
|
IPCOM_SA_LEN_SET(&session->dsock.paddr.sin, sizeof(struct Ip_sockaddr_in));
|
|
session->dsock.paddr.sin.sin_family = IP_AF_INET;
|
|
session->dsock.paddr.sin.sin_addr.s_addr = (Ip_u32)ip_htonl(addr);
|
|
session->dsock.paddr.sin.sin_port = ip_htons(port);
|
|
|
|
ipftps_reply(session, "200 Port OK"CL);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_pwd
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* PWD command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_pwd(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
char *wd;
|
|
char *path;
|
|
char *cp;
|
|
|
|
(void)argc;
|
|
(void)argv;
|
|
|
|
if ((path = wd = ipcom_strdup(session->cwd)) == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR, "Out of memory");
|
|
ipftps_reply(session, "452 Out of memory"CL);
|
|
return;
|
|
}
|
|
|
|
/* Use forward slashes */
|
|
for (cp = path + ipcom_strcspn(path, IPFTPS_DIRSEP); *cp != '\0'; )
|
|
{
|
|
*cp++ = '/';
|
|
cp += ipcom_strcspn(cp, IPFTPS_DIRSEP);
|
|
}
|
|
|
|
/* Paranoid check if path OK */
|
|
if (ipftps_dircmp(session->root, path) != 0)
|
|
{
|
|
/*
|
|
** This is never supposed to happen, teardown
|
|
** session to prevent from security breaches.
|
|
*/
|
|
IPCOM_LOG0(ERR, "Directory error, terminating session");
|
|
ipftps_reply(session, "421 Service not available, "
|
|
"closing control connection"CL);
|
|
session->authentications = 0;
|
|
}
|
|
else
|
|
{
|
|
Ip_size_t rlen = ipcom_strlen(session->root);
|
|
Ip_size_t plen = ipcom_strlen(path);
|
|
|
|
/* Advance past root part of path */
|
|
path += IP_MIN(rlen, plen);
|
|
|
|
if (path > wd && *path != '/')
|
|
{
|
|
*--path = '/';
|
|
}
|
|
|
|
ipftps_reply(session, "257 \"%s\" is current directory"CL, path);
|
|
}
|
|
|
|
ipcom_free(wd);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_quit
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* QUIT command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_quit(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
(void)argc;
|
|
(void)argv;
|
|
#ifdef IP_PORT_VXWORKS
|
|
if ((_func_logoutEvent != IP_NULL) && session->login)
|
|
_func_logoutEvent(SEC_EVENT_FTP_LOGOUT, session->username);
|
|
#endif
|
|
session->login = 0;
|
|
|
|
ipftps_reply(session, "221 Bye"CL);
|
|
|
|
session->authentications = 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_retr
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* RETR command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_retr(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
char *msg = "226 Transfer complete"CL;
|
|
char *nbuf = IP_NULL;
|
|
char *buf;
|
|
struct Ip_stat st;
|
|
Ip_fd fd;
|
|
int perrno = 0;
|
|
int num, no;
|
|
Ip_s32 mode;
|
|
Ip_u32 chunks = 0;
|
|
#ifdef IPFTP_TLS
|
|
int ret;
|
|
#endif
|
|
|
|
(void)argc;
|
|
|
|
if (ipftps_root_check(session, &argv[0]) != 0)
|
|
{
|
|
IPCOM_LOG2(WARNING, "Access denied to %s from %s",
|
|
argv[0], session->cwd);
|
|
ipftps_reply(session, "550 Permission denied"CL);
|
|
goto leave5;
|
|
}
|
|
|
|
if ((buf = ipcom_malloc(ipftps.databuf_size)) == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR, "Out of memory");
|
|
ipftps_reply(session, "452 Out of memory"CL);
|
|
goto leave5;
|
|
}
|
|
|
|
if (session->binary == 0 &&
|
|
(nbuf = ipcom_malloc(2 * ipftps.databuf_size)) == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR, "Out of memory");
|
|
ipftps_reply(session, "452 Out of memory"CL);
|
|
goto leave4;
|
|
}
|
|
|
|
mode = IP_O_RDONLY
|
|
#ifdef IP_O_BINARY
|
|
| (session->binary ? IP_O_BINARY : 0)
|
|
#endif
|
|
;
|
|
|
|
if ((fd = ipcom_fileopen(argv[0], mode, 0)) < 0)
|
|
{
|
|
ipftps_perror(session, 550);
|
|
goto leave3;
|
|
}
|
|
|
|
if (ipcom_fstat(fd, &st) != 0)
|
|
{
|
|
ipftps_perror(session, 550);
|
|
goto leave2;
|
|
}
|
|
|
|
if (IP_S_ISREG(st.st_mode) == 0)
|
|
{
|
|
ipftps_reply(session, "550 No regular file"CL);
|
|
goto leave2;
|
|
}
|
|
|
|
if (ipftps_dsock_open(session, 0) != 0)
|
|
{
|
|
goto leave2;
|
|
}
|
|
|
|
session->trtotal = (Ip_u32)st.st_size;
|
|
session->trnow = 0;
|
|
|
|
for (;;)
|
|
{
|
|
static struct Ip_timeval timeval = {0, 0};
|
|
Ip_fd_set cmdset;
|
|
|
|
#ifdef IPFTPS_USE_WRITESELECT
|
|
Ip_fd_set dataset;
|
|
|
|
timeval.tv_sec = ipftps_sysget_int("ipftps.send_timeout", 30, 1);
|
|
timeval.tv_usec = 0;
|
|
#endif
|
|
|
|
if ((no = (int)ipcom_fileread(fd, buf, ipftps.databuf_size)) == 0)
|
|
{
|
|
goto leave1;
|
|
}
|
|
else if (no < 0)
|
|
{
|
|
perrno = 451;
|
|
goto leave1;
|
|
}
|
|
|
|
again:
|
|
#ifdef IPFTPS_USE_WRITESELECT
|
|
/* Wait until ready for write, or async command */
|
|
IP_FD_ZERO(&cmdset);
|
|
IP_FD_SET(session->csock.fd, &cmdset);
|
|
IP_FD_ZERO(&dataset);
|
|
IP_FD_SET(session->dsock.fd, &dataset);
|
|
num = ipcom_socketselect(IP_MAX(session->csock.fd, session->dsock.fd) + 1,
|
|
&cmdset, &dataset, IP_NULL, &timeval);
|
|
if (num == -1)
|
|
{
|
|
perrno = 451;
|
|
goto leave1;
|
|
}
|
|
else if (num > 0 && IP_FD_ISSET(session->csock.fd, &cmdset))
|
|
{
|
|
if (ipftps_async(session) != 0)
|
|
{
|
|
msg = "226 Abort OK"CL;
|
|
goto leave1;
|
|
}
|
|
|
|
goto again;
|
|
}
|
|
else if (num == 0 || !IP_FD_ISSET(session->dsock.fd, &dataset))
|
|
{
|
|
msg = "426 Transfer timeout"CL;
|
|
goto leave1;
|
|
}
|
|
#else
|
|
/* Check for async command */
|
|
IP_FD_ZERO(&cmdset);
|
|
IP_FD_SET(session->csock.fd, &cmdset);
|
|
num = ipcom_socketselect(session->csock.fd + 1, &cmdset, IP_NULL, IP_NULL, &timeval);
|
|
if (num == -1)
|
|
{
|
|
perrno = 451;
|
|
goto leave1;
|
|
}
|
|
else if (num > 0 && IP_FD_ISSET(session->csock.fd, &cmdset))
|
|
{
|
|
if (ipftps_async(session) != 0)
|
|
{
|
|
msg = "226 Abort OK"CL;
|
|
goto leave1;
|
|
}
|
|
|
|
goto again;
|
|
}
|
|
#endif
|
|
if (session->binary == 0)
|
|
{
|
|
if (ipftps_socketwrite_ascii(session,session->dsock.fd,
|
|
buf, nbuf, no) != 0)
|
|
{
|
|
perrno = 451;
|
|
goto leave1;
|
|
}
|
|
}
|
|
|
|
#ifdef IPFTP_TLS
|
|
else if ((ipftps_tls_enbale == IP_TRUE) &&
|
|
(ret = ipftps_tls_send(session,session->dsock.fd,buf,no))!= 2)
|
|
{
|
|
if(ret == 1)
|
|
{
|
|
perrno = 451;
|
|
goto leave1;
|
|
}
|
|
}
|
|
#endif
|
|
else if (ipcom_socketwrite(session->dsock.fd, buf, no) != no)
|
|
{
|
|
perrno = 451;
|
|
goto leave1;
|
|
}
|
|
|
|
session->trnow += no;
|
|
chunks++;
|
|
}
|
|
|
|
leave1:
|
|
IPCOM_LOG2(DEBUG2, "Sent %lu bytes in %lu chunks",
|
|
(unsigned long)session->trnow, (unsigned long)chunks);
|
|
(void)chunks;
|
|
|
|
/* Preserve ipcom_errno */
|
|
no = ipcom_errno;
|
|
ipftps_dsock_close(session);
|
|
ipcom_errno = no;
|
|
|
|
if (perrno == 0)
|
|
{
|
|
ipftps_reply(session, msg);
|
|
}
|
|
else
|
|
{
|
|
ipftps_perror(session, perrno);
|
|
}
|
|
|
|
leave2:
|
|
(void)ipcom_fileclose(fd);
|
|
|
|
leave3:
|
|
ipftps_free(nbuf);
|
|
|
|
leave4:
|
|
ipcom_free(buf);
|
|
|
|
leave5:
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_rmd
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* RMD command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_rmd(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
(void)argc;
|
|
|
|
if (session->readonly)
|
|
{
|
|
IPCOM_LOG0(WARNING, "Remove dir denied");
|
|
ipftps_reply(session, "550 Permission denied"CL);
|
|
}
|
|
else if (ipftps_root_check(session, &argv[0]) != 0)
|
|
{
|
|
IPCOM_LOG2(WARNING, "Access denied to %s from %s",
|
|
argv[0], session->cwd);
|
|
ipftps_reply(session, "550 Permission denied"CL);
|
|
}
|
|
else if (ipcom_rmdir(argv[0]) != 0)
|
|
{
|
|
ipftps_perror(session, 550);
|
|
}
|
|
else
|
|
{
|
|
ipftps_reply(session, "250 Directory removed"CL);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_rnfr
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* RNFR command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_rnfr(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
(void)argc;
|
|
|
|
ipftps_free(session->rnfr);
|
|
|
|
if (session->readonly)
|
|
{
|
|
IPCOM_LOG0(WARNING, "Rename file denied");
|
|
ipftps_reply(session, "550 Permission denied"CL);
|
|
}
|
|
else if (ipftps_root_check(session, &argv[0]) != 0)
|
|
{
|
|
IPCOM_LOG2(WARNING, "Access denied to %s from %s",
|
|
argv[0], session->cwd);
|
|
ipftps_reply(session, "550 Permission denied"CL);
|
|
}
|
|
else if (ipcom_access(argv[0], 0) != 0)
|
|
{
|
|
ipftps_perror(session, 550);
|
|
}
|
|
else if ((session->rnfr = ipcom_strdup(argv[0])) == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR, "Out of memory");
|
|
ipftps_reply(session, "452 Out of memory"CL);
|
|
}
|
|
else
|
|
{
|
|
ipftps_reply(session, "350 Continue with RNTO"CL);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_rnto
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* RNTO command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_rnto(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
(void)argc;
|
|
|
|
if (session->readonly)
|
|
{
|
|
IPCOM_LOG0(WARNING, "Rename file denied");
|
|
ipftps_reply(session, "550 Permission denied"CL);
|
|
}
|
|
else if (session->last == IP_NULL ||
|
|
ipcom_strcasecmp(session->last->name, "RNFR") != 0 ||
|
|
session->rnfr == IP_NULL)
|
|
{
|
|
ipftps_reply(session, "503 Missing RNFR"CL);
|
|
}
|
|
else if (ipftps_root_check(session, &argv[0]) != 0)
|
|
{
|
|
IPCOM_LOG2(WARNING, "Access denied to %s from %s",
|
|
argv[0], session->cwd);
|
|
ipftps_reply(session, "550 Permission denied"CL);
|
|
}
|
|
else if (ipcom_rename(session->rnfr, argv[0]) != 0)
|
|
{
|
|
ipftps_perror(session, 553);
|
|
}
|
|
else
|
|
{
|
|
ipftps_free(session->rnfr);
|
|
ipftps_reply(session, "250 File renamed"CL);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_stat
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* STAT command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_stat(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
if (argc == 0)
|
|
{
|
|
char buf[IPFTPS_IP_MAXLEN];
|
|
|
|
(void)ipftps_ntop(&session->csock.laddr, buf, sizeof(buf));
|
|
ipftps_reply(session, "211- Session status:"CL
|
|
" Server: %s port %d"CL, buf,
|
|
(unsigned)ip_ntohs(IPFTPS_PORT(&session->csock.laddr)));
|
|
|
|
(void)ipftps_ntop(&session->csock.paddr, buf, sizeof(buf));
|
|
ipftps_reply(session,
|
|
" Client: %s port %d"CL, buf,
|
|
(unsigned)ip_ntohs(IPFTPS_PORT(&session->csock.paddr)));
|
|
|
|
ipftps_reply(session,
|
|
" Type: %s"CL
|
|
" Mode: Stream"CL
|
|
" Structure: File"CL
|
|
" Readonly: %s"CL
|
|
" Proxy FTP: %s"CL
|
|
"211 End of status"CL,
|
|
(session->binary ? "Binary" : "Ascii"),
|
|
(session->readonly ? "Yes" : "No"),
|
|
(session->proxy ? "Yes" : "No"));
|
|
}
|
|
else
|
|
{
|
|
struct Ip_stat st;
|
|
char *path = argv[0];
|
|
struct dirent *ent;
|
|
|
|
if (ipftps_root_check(session, &path) != 0)
|
|
{
|
|
IPCOM_LOG2(WARNING, "Access denied to %s from %s",
|
|
path, session->cwd);
|
|
ipftps_reply(session, "550 Permission denied"CL);
|
|
return;
|
|
}
|
|
|
|
if (ipcom_stat(path, &st) != 0)
|
|
{
|
|
ipftps_perror(session, 550);
|
|
return;
|
|
}
|
|
|
|
/* Dir or file? */
|
|
if (IP_S_ISDIR(st.st_mode))
|
|
{
|
|
DIR *dir;
|
|
int ret = 0;
|
|
|
|
dir = opendir(path);
|
|
if (dir == IP_NULL)
|
|
{
|
|
ipftps_perror(session, 550);
|
|
return;
|
|
}
|
|
|
|
/* Loop for files */
|
|
ipftps_reply(session, "212- Directory status"CL);
|
|
while ((ent = readdir(dir)) != 0)
|
|
{
|
|
if (ent->d_name[0] != '.')
|
|
{
|
|
if ((ret = ipftps_ls(session, session->csock.fd, path, ent->d_name)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ret == 0)
|
|
{
|
|
ipftps_reply(session, "212 Transfer complete"CL);
|
|
}
|
|
|
|
if (closedir(dir) != 0)
|
|
{
|
|
IPCOM_LOG2(WARNING, "Directory close failed for %s: %s",
|
|
path, ipcom_strerror(ipcom_errno));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ipftps_reply(session, "213- File status"CL);
|
|
if (ipftps_ls(session, session->csock.fd, "", path) == 0)
|
|
{
|
|
ipftps_reply(session, "213 Transfer complete"CL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_stor
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* STOR command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_stor(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
char *msg = "226 Transfer complete"CL;
|
|
char *nbuf = IP_NULL;
|
|
char *buf;
|
|
struct Ip_stat st;
|
|
Ip_fd fd;
|
|
int perrno = 0;
|
|
int num, no;
|
|
Ip_bool orphant = 0;
|
|
Ip_bool append = 0;
|
|
Ip_s32 mode;
|
|
Ip_u32 chunks = 0;
|
|
|
|
if (session->readonly)
|
|
{
|
|
IPCOM_LOG0(WARNING, "Store file denied");
|
|
ipftps_reply(session, "550 Permission denied"CL);
|
|
goto leave5;
|
|
}
|
|
|
|
if (ipftps_root_check(session, &argv[0]) != 0)
|
|
{
|
|
IPCOM_LOG2(WARNING, "Access denied to %s from %s",
|
|
argv[0], session->cwd);
|
|
ipftps_reply(session, "550 Permission denied"CL);
|
|
goto leave5;
|
|
}
|
|
|
|
if ((buf = ipcom_malloc(ipftps.databuf_size)) == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR, "Out of memory");
|
|
ipftps_reply(session, "452 Out of memory"CL);
|
|
goto leave5;
|
|
}
|
|
|
|
if (session->binary == 0 &&
|
|
(nbuf = ipcom_malloc(ipftps.databuf_size + 1)) == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR, "Out of memory");
|
|
ipftps_reply(session, "452 Out of memory"CL);
|
|
goto leave4;
|
|
}
|
|
|
|
/*
|
|
* Remove file before opening it to simulate O_TRUNC,
|
|
* which isn't supported by IPCOM.
|
|
*/
|
|
/* if append cmd, don't call ipcom_unlink() */
|
|
if(argc > 1 && ipcom_strcmp(argv[argc-1], "a") == 0)
|
|
append = 1;
|
|
else
|
|
(void)ipcom_unlink(argv[0]);
|
|
|
|
mode = IP_O_RDWR | IP_O_CREAT | (append ? IP_O_APPEND : 0)
|
|
#ifdef IP_O_BINARY
|
|
| (session->binary ? IP_O_BINARY : 0)
|
|
#endif
|
|
;
|
|
|
|
if ((fd = ipcom_fileopen(argv[0], mode,
|
|
IP_S_IRUSR | IP_S_IWUSR | IP_S_IRGRP | IP_S_IROTH)) < 0)
|
|
{
|
|
ipftps_perror(session, 550);
|
|
goto leave3;
|
|
}
|
|
|
|
if (ipcom_fstat(fd, &st) != 0)
|
|
{
|
|
ipftps_perror(session, 550);
|
|
goto leave2;
|
|
}
|
|
|
|
if (ipftps_dsock_open(session, 0) != 0)
|
|
{
|
|
goto leave2;
|
|
}
|
|
|
|
session->trtotal = 0;
|
|
session->trnow = 0;
|
|
|
|
for (;;)
|
|
{
|
|
struct Ip_timeval timeval;
|
|
Ip_fd_set set;
|
|
|
|
IP_FD_ZERO(&set);
|
|
IP_FD_SET(session->csock.fd, &set);
|
|
IP_FD_SET(session->dsock.fd, &set);
|
|
timeval.tv_sec = ipftps_sysget_int("ipftps.receive_timeout", 30, 1);
|
|
timeval.tv_usec = 0;
|
|
num = ipcom_socketselect(IP_MAX(session->csock.fd,
|
|
session->dsock.fd) + 1,
|
|
&set, IP_NULL, IP_NULL, &timeval);
|
|
if (num == -1)
|
|
{
|
|
perrno = 451;
|
|
goto leave1;
|
|
}
|
|
else if (num > 0 && IP_FD_ISSET(session->csock.fd, &set))
|
|
{
|
|
if (ipftps_async(session) != 0)
|
|
{
|
|
msg = "226 Abort OK"CL;
|
|
goto leave1;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
else if (num == 0 || !IP_FD_ISSET(session->dsock.fd, &set))
|
|
{
|
|
msg = "426 Transfer timeout"CL;
|
|
goto leave1;
|
|
}
|
|
|
|
#ifdef IPFTP_TLS
|
|
if( (ipftps_tls_enbale == IP_TRUE) &&
|
|
(ipftps_tls_read(session,session->dsock.fd,buf,ipftps.databuf_size,&no) != 1))
|
|
{
|
|
if (no == 0)
|
|
{
|
|
goto leave1;
|
|
}
|
|
else if(no < 0)
|
|
{
|
|
perrno = 451;
|
|
goto leave1;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
if ((no = (int)ipcom_socketread(session->dsock.fd, buf,
|
|
ipftps.databuf_size)) == 0)
|
|
{
|
|
goto leave1;
|
|
}
|
|
else if (no < 0)
|
|
{
|
|
perrno = 451;
|
|
goto leave1;
|
|
}
|
|
|
|
if (session->binary == 0)
|
|
{
|
|
if (ipftps_filewrite_ascii(fd, buf, nbuf, no, &orphant) != 0)
|
|
{
|
|
perrno = 451;
|
|
goto leave1;
|
|
}
|
|
}
|
|
else if (ipcom_filewrite(fd, buf, no) != no)
|
|
{
|
|
perrno = 451;
|
|
goto leave1;
|
|
}
|
|
|
|
session->trnow += no;
|
|
chunks++;
|
|
}
|
|
|
|
leave1:
|
|
/* Write orphant '\r' */
|
|
if (orphant && perrno == 0 && ipcom_filewrite(fd, "\r", 1) != 1)
|
|
{
|
|
perrno = 451;
|
|
}
|
|
|
|
IPCOM_LOG2(DEBUG2, "Received %lu bytes in %lu chunks",
|
|
(unsigned long)session->trnow, (unsigned long)chunks);
|
|
(void)chunks;
|
|
|
|
/* Preserve ipcom_errno */
|
|
no = ipcom_errno;
|
|
ipftps_dsock_close(session);
|
|
ipcom_errno = no;
|
|
|
|
if (perrno == 0)
|
|
{
|
|
ipftps_reply(session, msg);
|
|
}
|
|
else
|
|
{
|
|
ipftps_perror(session, perrno);
|
|
}
|
|
|
|
leave2:
|
|
(void)ipcom_fileclose(fd);
|
|
|
|
leave3:
|
|
ipftps_free(nbuf);
|
|
|
|
leave4:
|
|
ipcom_free(buf);
|
|
|
|
leave5:
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_stru
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* STRU command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_stru(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
char *stru = argv[0];
|
|
|
|
(void)argc;
|
|
|
|
if (ipcom_toupper(stru[0]) != 'F' || stru[1] != '\0')
|
|
{
|
|
ipftps_reply(session, "504 Only F supported"CL);
|
|
}
|
|
else
|
|
{
|
|
ipftps_reply(session, "200 File structure selected"CL);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_syst
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* SYST command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_syst(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
char ver[16];
|
|
char sys[16];
|
|
Ip_size_t versize = sizeof(ver);
|
|
Ip_size_t syssize = sizeof(sys);
|
|
|
|
(void)argc;
|
|
(void)argv;
|
|
|
|
ipftps_reply(session, "215 %s Type: L8 Version: IPFTPS %s"CL,
|
|
ipftps_sysget_str("ipftps.system", sys, &syssize, "UNIX", 1),
|
|
ipftps_sysget_str("ipftps.versionstr", ver, &versize, "-", 1));
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_type
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* TYPE command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_type(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
char *space = " ";
|
|
|
|
/* Dummy space to facilitate display */
|
|
if (argc < 2)
|
|
{
|
|
argv[1] = space;
|
|
space = "";
|
|
}
|
|
|
|
switch (ipcom_toupper(argv[0][0]))
|
|
{
|
|
case 'A':
|
|
if (ipcom_strchr("NT ", ipcom_toupper(argv[1][0])) == IP_NULL)
|
|
{
|
|
goto leave;
|
|
}
|
|
session->binary = 0;
|
|
break;
|
|
case 'I':
|
|
if (argc > 1)
|
|
{
|
|
goto leave;
|
|
}
|
|
session->binary = 1;
|
|
break;
|
|
case 'L':
|
|
if (ipcom_toupper(argv[1][0]) != '8')
|
|
{
|
|
goto leave;
|
|
}
|
|
session->binary = 1;
|
|
break;
|
|
default:
|
|
goto leave;
|
|
}
|
|
|
|
ipftps_reply(session, "200 Type %c%s%c%sselected"CL,
|
|
argv[0][0], space, argv[1][0], space);
|
|
return;
|
|
|
|
leave:
|
|
ipftps_reply(session, "504 Only TYPE A [N|T], I and L 8 supported"CL);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_user
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* USER command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_user(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
(void)argc;
|
|
(void)argv;
|
|
|
|
if (session->login != 0)
|
|
{
|
|
ipftps_reply(session, "503 Cannot change user"CL);
|
|
return;
|
|
}
|
|
|
|
if (session->username != IP_NULL)
|
|
ipftps_free(session->username);
|
|
|
|
#ifdef IP_PORT_VXWORKS
|
|
if (systemSecurityIsEnabled == TRUE)
|
|
{
|
|
if (argv[0] == IP_NULL)
|
|
{
|
|
ipftps_reply(session, "332 Need account for login"CL);
|
|
session->authentications = 0;
|
|
return;
|
|
}
|
|
if ((session->username = ipcom_strdup(argv[0])) == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR, "Out of memory");
|
|
ipftps_reply(session, "421 Service not available, "
|
|
"closing control connection"CL);
|
|
session->authentications = 0;
|
|
return;
|
|
}
|
|
|
|
ipftps_reply(session, "331 Password required for user %s"CL, argv[0]);
|
|
}
|
|
else
|
|
#endif /* IP_PORT_VXWORKS */
|
|
#ifdef IPFTPS_USE_TEST
|
|
if (IP_TRUE) /* enabled password check in IPTESTENGINE */
|
|
{
|
|
if (argv[0] == IP_NULL || ((session->username = ipcom_strdup(argv[0])) == IP_NULL))
|
|
{
|
|
IPCOM_LOG0(ERR, "Out of memory");
|
|
ipftps_reply(session, "421 Service not available, "
|
|
"closing control connection"CL);
|
|
return;
|
|
}
|
|
|
|
ipftps_reply(session, "331 Password required for user %s"CL, argv[0]);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* Change to selected directory */
|
|
if (ipftps_chdir(session, session->cwd) != 0 ||
|
|
ipftps_dir_check(session) != 0)
|
|
{
|
|
IPCOM_LOG1(ERR, "Invalid working directory: %s", session->cwd);
|
|
ipftps_reply(session, "421 Service not available, "
|
|
"closing control connection"CL);
|
|
session->authentications = 0;
|
|
return;
|
|
}
|
|
|
|
session->login = 1;
|
|
|
|
ipftps_reply(session, "230 Login OK"CL);
|
|
}
|
|
}
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_size
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* SIZE command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_size(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
|
|
struct Ip_stat st;
|
|
Ip_fd fd;
|
|
Ip_s32 mode;
|
|
Ip_ssize_t no = 0, extra,end,perrno;
|
|
char *buf;
|
|
|
|
(void)argc;
|
|
|
|
if (ipftps_root_check(session, &argv[0]) != 0)
|
|
{
|
|
IPCOM_LOG2(WARNING, "Access denied to %s from %s",
|
|
argv[0], session->cwd);
|
|
ipftps_reply(session, "550 Could not get file size"CL);
|
|
return;
|
|
}
|
|
|
|
if ((buf = ipcom_malloc(ipftps.databuf_size)) == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR, "Out of memory");
|
|
ipftps_reply(session, "452 Out of memory"CL);
|
|
return;
|
|
}
|
|
|
|
mode = IP_O_RDONLY
|
|
#ifdef IP_O_BINARY
|
|
| (session->binary ? IP_O_BINARY : 0)
|
|
#endif
|
|
;
|
|
|
|
extra = 0;
|
|
perrno = 0;
|
|
|
|
if ((fd = ipcom_fileopen(argv[0], mode, 0)) < 0)
|
|
{
|
|
ipcom_free(buf);
|
|
ipftps_perror(session, 550);
|
|
return;
|
|
}
|
|
|
|
/* Get the size of file*/
|
|
if (ipcom_fstat(fd, &st) != 0)
|
|
{
|
|
ipftps_perror(session, 550);
|
|
(void)ipcom_fileclose(fd);
|
|
ipcom_free(buf);
|
|
return;
|
|
}
|
|
|
|
/* TYPE == ASCII */
|
|
if (session->binary == 0)
|
|
{
|
|
for(;;)
|
|
{
|
|
if ((no = ipcom_fileread(fd, buf, ipftps.databuf_size)) == 0)
|
|
{
|
|
goto response;
|
|
}
|
|
else if (no < 0)
|
|
{
|
|
perrno = 451;
|
|
goto response;
|
|
}
|
|
|
|
for(end = 0; end < no; end++)
|
|
{
|
|
if (buf[end] == '\n')
|
|
extra++;
|
|
}
|
|
}
|
|
}
|
|
|
|
response:
|
|
|
|
ipcom_errno = no;
|
|
if (perrno == 0)
|
|
ipftps_reply(session,"213 %d"CL,(st.st_size+extra));
|
|
else
|
|
ipftps_perror(session, perrno);
|
|
|
|
(void)ipcom_fileclose(fd);
|
|
|
|
ipcom_free(buf);
|
|
return;
|
|
}
|
|
|
|
#ifdef IPFTP_TLS
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_auth
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* AUTH command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void ipftps_auth(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
(void)argc;
|
|
|
|
if(ipftps_tls_enbale == IP_FALSE)
|
|
{
|
|
ipftps_reply(session, "500 don't recognize the AUTH command"CL);
|
|
return;
|
|
}
|
|
|
|
if(ipcom_strcmp(argv[0],"TLS") == 0 || ipcom_strcmp(argv[0], "TLS-C") == 0)
|
|
{
|
|
ipftps_reply(session, "234 AUTH TLS OK"CL);
|
|
if (ipftps_create_new_tls_csession(session) != IPCOM_SUCCESS)
|
|
{
|
|
ipftps_reply(session, "431, TLS connection can't be created"CL);
|
|
ipftps_tls_close_session(session,IPFTPS_CTRL_FLAG);
|
|
ipftps_free_tls_session(session);
|
|
}
|
|
else
|
|
{
|
|
ipftps_tls_set_flag(session, IPFTPS_CTRL_FLAG, IP_TRUE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ipftps_reply(session, "504 the parameter (%s) is unknown"CL, argv[0]);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_prot
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* PROT command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void ipftps_prot(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
(void)argc;
|
|
|
|
if (ipftps_tls_get_ccc(session) == IP_TRUE)
|
|
{
|
|
ipftps_reply(session, "504 Control session is already cleared, don't accept PROT command"CL);
|
|
return;
|
|
}
|
|
switch (*argv[0])
|
|
{
|
|
case 'C':
|
|
{
|
|
ipftps_reply(session, "200 data is not protected"CL);
|
|
ipftps_tls_set_flag(session, IPFTPS_DATA_FLAG,IP_FALSE);
|
|
break;
|
|
}
|
|
case 'S':
|
|
case 'E':
|
|
{
|
|
ipftps_reply(session, "504 Only support parameter 'C' or 'P'"CL);
|
|
break;
|
|
}
|
|
case 'P':
|
|
ipftps_reply(session, "200 data is protected"CL);
|
|
ipftps_tls_set_flag(session, IPFTPS_DATA_FLAG,IP_TRUE);
|
|
break;
|
|
default :
|
|
ipftps_reply(session, "504 Only support parameter 'C' or 'P'"CL);
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_pbsz
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* AUTH command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void ipftps_pbsz(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
(void)argc;
|
|
(void)argv;
|
|
|
|
if (ipftps_tls_get_ccc(session) == IP_TRUE)
|
|
{
|
|
ipftps_reply(session, "504 Control session is already cleared, don't accept PBSZ command"CL);
|
|
return;
|
|
}
|
|
|
|
if(ipcom_strcmp(argv[0],"0") == 0)
|
|
{
|
|
ipftps_reply(session,"200 PBSZ=0"CL);
|
|
}
|
|
else
|
|
{
|
|
ipftps_reply(session, "504 Only PBSZ=0 supported"CL);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_ccc
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* CCC command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void ipftps_ccc(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
(void)argc;
|
|
(void)argv;
|
|
|
|
if(ipftps_tls_get_ccc(session) == IP_TRUE)
|
|
{
|
|
ipftps_reply(session, "504 control session is already cleaned, cleaning control session is invalid"CL);
|
|
return;
|
|
}
|
|
|
|
ipftps_reply(session, "200 clear control session"CL);
|
|
ipftps_tls_shutdown(session, IPFTPS_CTRL_FLAG);
|
|
ipftps_tls_set_flag(session, IPFTPS_CTRL_FLAG, IP_FALSE);
|
|
ipftps_tls_set_ccc(session,IP_TRUE);
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_xftp
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* XFTP command.
|
|
*
|
|
* WARNING--This function is designed to be used for test
|
|
* purposes and contains a number of commands that may be
|
|
* used to compromise security. It is of utmost importance
|
|
* that this function is disabled before deploying the FTP
|
|
* server. This is done by not defining IPFTPS_USE_TEST.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
#ifdef IPFTPS_USE_TEST
|
|
IP_STATIC void
|
|
ipftps_xftp(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
char *path = argv[1];
|
|
|
|
IPCOM_LOG0(EMERG,
|
|
"**** SECURITY HAZARD - TEST FUNCTIONALITY ENABLED ****");
|
|
|
|
if (ipcom_strcmp(argv[0], "creat") == 0)
|
|
{
|
|
int mode = IP_O_RDWR | IP_O_CREAT
|
|
#ifdef IP_O_BINARY
|
|
| IP_O_BINARY
|
|
#endif
|
|
;
|
|
Ip_fd fd;
|
|
|
|
#ifdef IP_PORT_VXWORKS
|
|
char *fullPath = IP_NULL;
|
|
|
|
if(argv[1])
|
|
{
|
|
if ((fullPath = ipcom_malloc(IP_PATH_MAX)) == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR, "Out of memory");
|
|
ipftps_reply(session, "452 Out of memory"CL);
|
|
return;
|
|
}
|
|
ipcom_strcpy(fullPath, argv[1]);
|
|
|
|
/* Use the absolute path for vxworks */
|
|
if (pathCat(session->cwd, path, fullPath) == ERROR)
|
|
goto leave1;
|
|
pathCondense(fullPath);
|
|
path = fullPath;
|
|
}
|
|
#endif
|
|
|
|
if ((fd = ipcom_fileopen(path, mode,
|
|
IP_S_IRUSR | IP_S_IWUSR |
|
|
IP_S_IRGRP | IP_S_IROTH)) < 0)
|
|
{
|
|
ipftps_perror(session, 550);
|
|
goto leave1;
|
|
}
|
|
else
|
|
{
|
|
static const char data[] = "\r\n\n\n\r\r\n";
|
|
int datalen = strlen(data);
|
|
int len = ipcom_strtol(argv[2], 0, 10);
|
|
int i;
|
|
|
|
for (i = 0; i < len; i += datalen)
|
|
{
|
|
int writelen = IP_MIN(datalen, len - i);
|
|
|
|
if (ipcom_filewrite(fd, data, writelen) != writelen)
|
|
{
|
|
ipftps_perror(session, 451);
|
|
(void)ipcom_fileclose(fd);
|
|
leave1:
|
|
#ifdef IP_PORT_VXWORKS
|
|
ipftps_free(fullPath);
|
|
#endif
|
|
return;
|
|
}
|
|
}
|
|
|
|
(void)ipcom_fileclose(fd);
|
|
}
|
|
}
|
|
else if (ipcom_strcmp(argv[0], "connect") == 0)
|
|
{
|
|
Ipftps_sockcb cb;
|
|
int option = 1;
|
|
|
|
ipcom_memset(&cb, 0, sizeof(cb));
|
|
cb.fd = -1;
|
|
cb.laddr.sa.sa_family = IP_AF_INET;
|
|
cb.paddr.sa.sa_family = IP_AF_INET;
|
|
|
|
if (ipcom_inet_pton(IP_AF_INET, argv[1],
|
|
&cb.laddr.sin.sin_addr) != 1 ||
|
|
ipcom_inet_pton(IP_AF_INET, argv[2],
|
|
&cb.paddr.sin.sin_addr) != 1)
|
|
{
|
|
ipftps_reply(session, "501 Address error"CL);
|
|
return;
|
|
}
|
|
|
|
cb.paddr.sin.sin_port = (Ip_u16)ipcom_strtol(argv[3], IP_NULL, 10);
|
|
cb.paddr.sin.sin_port = ip_htons(cb.paddr.sin.sin_port);
|
|
|
|
if ((cb.fd = ipcom_socket(IP_AF_INET, IP_SOCK_STREAM, IPFTPS_PROTO)) < 0 ||
|
|
ipcom_setsockopt(cb.fd, IP_SOL_SOCKET, IP_SO_REUSEADDR,
|
|
&option, sizeof(option)) < 0 ||
|
|
ipcom_bind(cb.fd, &cb.laddr.sa,
|
|
IPFTPS_SOCKADDRLEN(&cb.laddr)) < 0 ||
|
|
ipcom_connect(cb.fd, &cb.paddr.sa,
|
|
IPFTPS_SOCKADDRLEN(&cb.paddr)) < 0)
|
|
{
|
|
ipftps_reply(session, "451 Socket error"CL);
|
|
}
|
|
else
|
|
{
|
|
ipftps_reply(session, "200 OK"CL);
|
|
}
|
|
|
|
if (cb.fd != -1)
|
|
{
|
|
ipcom_socketclose(cb.fd);
|
|
}
|
|
|
|
return;
|
|
}
|
|
else if (ipcom_strcmp(argv[0], "root") == 0)
|
|
{
|
|
#ifdef IP_PORT_VXWORKS
|
|
if (session->cwd != session->root)
|
|
{
|
|
ipftps_free(session->root);
|
|
}
|
|
if(argc == 2)
|
|
{
|
|
ipcom_strcpy(session->cwd, argv[1]);
|
|
}
|
|
else
|
|
{
|
|
ipcom_strcpy(session->cwd, "/");
|
|
}
|
|
session->root = ipcom_strdup(argc == 2 ? argv[1] : "/");
|
|
#else
|
|
if (session->cwd != session->root)
|
|
{
|
|
ipftps_free(session->cwd);
|
|
}
|
|
ipftps_free(session->root);
|
|
session->cwd = session->root = ipcom_strdup(argc == 2 ? argv[1] : "/");
|
|
#endif
|
|
}
|
|
#ifdef IPCOM_USE_TIME
|
|
else if (ipcom_strcmp(argv[0], "date") == 0)
|
|
{
|
|
char timestr[16];
|
|
Ip_time_t now;
|
|
struct Ip_tm tm;
|
|
|
|
(void)ipcom_time(&now);
|
|
(void)ipcom_localtime_r(&now, &tm);
|
|
(void)ipcom_strftime(timestr, sizeof(timestr), "%b %d %H:%M", &tm);
|
|
ipftps_reply(session, " %s"CL, timestr);
|
|
}
|
|
#endif
|
|
else if (ipcom_strcmp(argv[0], "readonly") == 0)
|
|
{
|
|
session->readonly = argv[1][0] - '0';
|
|
}
|
|
else if (ipcom_strcmp(argv[0], "proxy") == 0)
|
|
{
|
|
session->proxy = argv[1][0] - '0';
|
|
}
|
|
else if (ipcom_strcmp(argv[0], "lportbase") == 0)
|
|
{
|
|
session->lportbase = (Ip_u16)ipcom_strtol(argv[1], IP_NULL, 10);
|
|
}
|
|
else if (ipcom_strcmp(argv[0], "pportbase") == 0)
|
|
{
|
|
session->pportbase = (Ip_u16)ipcom_strtol(argv[1], IP_NULL, 10);
|
|
}
|
|
else
|
|
{
|
|
ipftps_reply(session, "501 Syntax error"CL);
|
|
}
|
|
|
|
ipftps_reply(session, "200 OK"CL);
|
|
}
|
|
|
|
#ifdef IPFTP_TLS
|
|
IP_STATIC void ipftps_create_cert_and_privateKey()
|
|
{
|
|
|
|
IP_STATIC char defaultCert[]={
|
|
0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20, 0x52, 0x53, 0x41, 0x20, 0x50,
|
|
0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4B, 0x45, 0x59, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0D,
|
|
0x0A, 0x4D, 0x49, 0x49, 0x43, 0x58, 0x41, 0x49, 0x42, 0x41, 0x41, 0x4B, 0x42, 0x67, 0x51, 0x43,
|
|
0x59, 0x67, 0x6F, 0x42, 0x46, 0x76, 0x74, 0x5A, 0x2F, 0x76, 0x47, 0x4D, 0x4F, 0x50, 0x54, 0x50,
|
|
0x36, 0x70, 0x41, 0x75, 0x72, 0x47, 0x4D, 0x48, 0x4C, 0x76, 0x39, 0x64, 0x39, 0x4B, 0x6C, 0x6B,
|
|
0x67, 0x2F, 0x78, 0x44, 0x2B, 0x7A, 0x73, 0x4B, 0x31, 0x76, 0x52, 0x6A, 0x74, 0x35, 0x57, 0x2F,
|
|
0x61, 0x0D, 0x0A, 0x34, 0x2F, 0x36, 0x78, 0x48, 0x6F, 0x54, 0x2F, 0x69, 0x2B, 0x77, 0x78, 0x49,
|
|
0x46, 0x75, 0x4A, 0x72, 0x5A, 0x71, 0x55, 0x35, 0x49, 0x44, 0x6A, 0x54, 0x50, 0x77, 0x33, 0x43,
|
|
0x6D, 0x33, 0x6F, 0x76, 0x68, 0x49, 0x4A, 0x2B, 0x38, 0x6A, 0x73, 0x56, 0x6F, 0x6F, 0x46, 0x32,
|
|
0x66, 0x51, 0x50, 0x2B, 0x2B, 0x6E, 0x30, 0x30, 0x59, 0x58, 0x67, 0x58, 0x71, 0x57, 0x53, 0x2F,
|
|
0x51, 0x37, 0x2F, 0x0D, 0x0A, 0x6E, 0x44, 0x64, 0x4C, 0x6F, 0x78, 0x62, 0x4F, 0x39, 0x4E, 0x6F,
|
|
0x6D, 0x43, 0x4B, 0x43, 0x68, 0x5A, 0x61, 0x32, 0x63, 0x46, 0x6E, 0x7A, 0x57, 0x63, 0x42, 0x68,
|
|
0x54, 0x79, 0x67, 0x41, 0x2B, 0x6A, 0x31, 0x38, 0x78, 0x34, 0x4D, 0x50, 0x77, 0x36, 0x30, 0x41,
|
|
0x46, 0x33, 0x77, 0x4D, 0x59, 0x67, 0x78, 0x4E, 0x4E, 0x6E, 0x62, 0x4B, 0x37, 0x44, 0x77, 0x49,
|
|
0x44, 0x41, 0x51, 0x41, 0x42, 0x0D, 0x0A, 0x41, 0x6F, 0x47, 0x42, 0x41, 0x49, 0x30, 0x5A, 0x42,
|
|
0x38, 0x58, 0x2F, 0x52, 0x6F, 0x4A, 0x4E, 0x62, 0x37, 0x4F, 0x6E, 0x79, 0x69, 0x52, 0x55, 0x69,
|
|
0x33, 0x73, 0x54, 0x58, 0x35, 0x43, 0x36, 0x2F, 0x34, 0x6F, 0x4D, 0x6D, 0x51, 0x4E, 0x66, 0x47,
|
|
0x49, 0x45, 0x32, 0x46, 0x34, 0x78, 0x2B, 0x30, 0x38, 0x61, 0x64, 0x59, 0x2F, 0x77, 0x32, 0x76,
|
|
0x64, 0x64, 0x75, 0x62, 0x67, 0x76, 0x6A, 0x0D, 0x0A, 0x6A, 0x33, 0x54, 0x77, 0x41, 0x2B, 0x42,
|
|
0x2F, 0x33, 0x69, 0x6C, 0x73, 0x73, 0x78, 0x59, 0x59, 0x6A, 0x71, 0x76, 0x35, 0x6D, 0x54, 0x41,
|
|
0x67, 0x55, 0x5A, 0x79, 0x2F, 0x4E, 0x73, 0x38, 0x36, 0x30, 0x58, 0x73, 0x5A, 0x6B, 0x44, 0x4D,
|
|
0x44, 0x59, 0x4E, 0x50, 0x64, 0x4C, 0x6F, 0x58, 0x35, 0x66, 0x39, 0x67, 0x58, 0x65, 0x53, 0x6C,
|
|
0x4E, 0x68, 0x76, 0x63, 0x36, 0x6F, 0x72, 0x31, 0x63, 0x0D, 0x0A, 0x75, 0x54, 0x76, 0x6C, 0x50,
|
|
0x34, 0x5A, 0x4F, 0x6C, 0x79, 0x38, 0x52, 0x50, 0x79, 0x62, 0x53, 0x4E, 0x39, 0x7A, 0x6C, 0x4B,
|
|
0x43, 0x44, 0x38, 0x39, 0x39, 0x4D, 0x61, 0x30, 0x37, 0x63, 0x63, 0x30, 0x66, 0x62, 0x50, 0x52,
|
|
0x52, 0x57, 0x70, 0x32, 0x67, 0x2B, 0x74, 0x77, 0x4D, 0x65, 0x70, 0x41, 0x6B, 0x45, 0x41, 0x79,
|
|
0x76, 0x6C, 0x34, 0x73, 0x34, 0x6E, 0x43, 0x59, 0x63, 0x4A, 0x2F, 0x0D, 0x0A, 0x39, 0x41, 0x7A,
|
|
0x73, 0x6A, 0x43, 0x62, 0x67, 0x71, 0x76, 0x66, 0x41, 0x62, 0x52, 0x66, 0x52, 0x66, 0x67, 0x44,
|
|
0x58, 0x49, 0x6D, 0x6C, 0x4D, 0x6D, 0x4D, 0x69, 0x53, 0x64, 0x36, 0x33, 0x46, 0x45, 0x66, 0x72,
|
|
0x59, 0x74, 0x32, 0x4A, 0x4E, 0x61, 0x62, 0x44, 0x4D, 0x65, 0x54, 0x76, 0x71, 0x6B, 0x4D, 0x7A,
|
|
0x55, 0x34, 0x55, 0x6E, 0x69, 0x67, 0x38, 0x31, 0x44, 0x4A, 0x42, 0x75, 0x71, 0x0D, 0x0A, 0x63,
|
|
0x63, 0x4C, 0x4E, 0x4E, 0x73, 0x70, 0x2B, 0x55, 0x77, 0x4A, 0x42, 0x41, 0x4D, 0x42, 0x61, 0x44,
|
|
0x4B, 0x6F, 0x34, 0x32, 0x6C, 0x39, 0x4B, 0x54, 0x6C, 0x6C, 0x54, 0x59, 0x73, 0x62, 0x41, 0x6B,
|
|
0x43, 0x44, 0x42, 0x44, 0x43, 0x30, 0x57, 0x4B, 0x62, 0x79, 0x63, 0x37, 0x44, 0x35, 0x6D, 0x4A,
|
|
0x5A, 0x73, 0x6F, 0x52, 0x72, 0x4E, 0x4F, 0x65, 0x57, 0x4B, 0x65, 0x41, 0x53, 0x4B, 0x58, 0x0D,
|
|
0x0A, 0x58, 0x4B, 0x37, 0x47, 0x6A, 0x41, 0x62, 0x39, 0x53, 0x74, 0x6C, 0x52, 0x42, 0x6F, 0x35,
|
|
0x41, 0x75, 0x35, 0x57, 0x37, 0x47, 0x71, 0x56, 0x35, 0x46, 0x4C, 0x43, 0x56, 0x49, 0x6C, 0x69,
|
|
0x31, 0x34, 0x4E, 0x55, 0x43, 0x51, 0x41, 0x49, 0x6A, 0x45, 0x6B, 0x52, 0x42, 0x70, 0x51, 0x78,
|
|
0x47, 0x63, 0x66, 0x5A, 0x74, 0x31, 0x47, 0x65, 0x39, 0x57, 0x51, 0x77, 0x7A, 0x77, 0x35, 0x44,
|
|
0x69, 0x0D, 0x0A, 0x65, 0x56, 0x41, 0x73, 0x54, 0x68, 0x65, 0x4D, 0x71, 0x45, 0x50, 0x4C, 0x69,
|
|
0x4D, 0x38, 0x39, 0x77, 0x78, 0x39, 0x6E, 0x56, 0x72, 0x54, 0x48, 0x6B, 0x69, 0x30, 0x55, 0x73,
|
|
0x58, 0x4A, 0x74, 0x55, 0x52, 0x44, 0x70, 0x4E, 0x79, 0x79, 0x51, 0x44, 0x4A, 0x52, 0x36, 0x71,
|
|
0x73, 0x52, 0x4D, 0x6A, 0x6B, 0x54, 0x59, 0x57, 0x6A, 0x54, 0x44, 0x65, 0x51, 0x55, 0x43, 0x51,
|
|
0x44, 0x7A, 0x4B, 0x0D, 0x0A, 0x2B, 0x52, 0x2B, 0x2F, 0x6B, 0x6A, 0x7A, 0x31, 0x74, 0x69, 0x65,
|
|
0x44, 0x4E, 0x44, 0x2F, 0x4F, 0x30, 0x6E, 0x36, 0x58, 0x70, 0x4F, 0x71, 0x48, 0x43, 0x36, 0x2F,
|
|
0x69, 0x42, 0x4D, 0x52, 0x2F, 0x38, 0x6A, 0x6A, 0x45, 0x6C, 0x50, 0x76, 0x4D, 0x76, 0x49, 0x36,
|
|
0x50, 0x6B, 0x35, 0x70, 0x45, 0x4C, 0x6E, 0x58, 0x4B, 0x71, 0x4B, 0x7A, 0x34, 0x31, 0x76, 0x51,
|
|
0x79, 0x4B, 0x4A, 0x6C, 0x44, 0x0D, 0x0A, 0x47, 0x6B, 0x5A, 0x72, 0x65, 0x54, 0x4E, 0x39, 0x50,
|
|
0x69, 0x33, 0x46, 0x4A, 0x4C, 0x4C, 0x57, 0x34, 0x50, 0x55, 0x43, 0x51, 0x47, 0x36, 0x44, 0x67,
|
|
0x53, 0x65, 0x56, 0x71, 0x75, 0x37, 0x5A, 0x6D, 0x75, 0x55, 0x53, 0x35, 0x77, 0x5A, 0x50, 0x69,
|
|
0x33, 0x69, 0x63, 0x61, 0x66, 0x33, 0x5A, 0x57, 0x31, 0x31, 0x36, 0x43, 0x5A, 0x4C, 0x35, 0x77,
|
|
0x6F, 0x58, 0x68, 0x2B, 0x46, 0x47, 0x4A, 0x0D, 0x0A, 0x54, 0x77, 0x32, 0x59, 0x39, 0x76, 0x55,
|
|
0x36, 0x67, 0x50, 0x4C, 0x63, 0x56, 0x67, 0x51, 0x39, 0x6E, 0x33, 0x4E, 0x34, 0x63, 0x2F, 0x62,
|
|
0x65, 0x46, 0x51, 0x70, 0x4C, 0x48, 0x30, 0x2B, 0x55, 0x33, 0x39, 0x4D, 0x48, 0x31, 0x2B, 0x4C,
|
|
0x41, 0x6D, 0x42, 0x30, 0x3D, 0x0D, 0x0A, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x45, 0x4E, 0x44, 0x20,
|
|
0x52, 0x53, 0x41, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4B, 0x45, 0x59, 0x2D,
|
|
0x2D, 0x2D, 0x2D, 0x2D, 0x0D, 0x0A, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E,
|
|
0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2D, 0x2D, 0x2D, 0x2D,
|
|
0x2D, 0x0D, 0x0A, 0x4D, 0x49, 0x49, 0x44, 0x54, 0x44, 0x43, 0x43, 0x41, 0x72, 0x57, 0x67, 0x41,
|
|
0x77, 0x49, 0x42, 0x41, 0x67, 0x49, 0x4A, 0x41, 0x4D, 0x67, 0x68, 0x75, 0x72, 0x6B, 0x54, 0x6E,
|
|
0x6D, 0x6D, 0x48, 0x4D, 0x41, 0x30, 0x47, 0x43, 0x53, 0x71, 0x47, 0x53, 0x49, 0x62, 0x33, 0x44,
|
|
0x51, 0x45, 0x42, 0x42, 0x51, 0x55, 0x41, 0x4D, 0x48, 0x67, 0x78, 0x43, 0x7A, 0x41, 0x4A, 0x42,
|
|
0x67, 0x4E, 0x56, 0x0D, 0x0A, 0x42, 0x41, 0x59, 0x54, 0x41, 0x6B, 0x4E, 0x4F, 0x4D, 0x51, 0x73,
|
|
0x77, 0x43, 0x51, 0x59, 0x44, 0x56, 0x51, 0x51, 0x49, 0x45, 0x77, 0x4A, 0x43, 0x53, 0x6A, 0x45,
|
|
0x4C, 0x4D, 0x41, 0x6B, 0x47, 0x41, 0x31, 0x55, 0x45, 0x42, 0x78, 0x4D, 0x43, 0x51, 0x6B, 0x6F,
|
|
0x78, 0x45, 0x6A, 0x41, 0x51, 0x42, 0x67, 0x4E, 0x56, 0x42, 0x41, 0x6F, 0x54, 0x43, 0x58, 0x64,
|
|
0x70, 0x62, 0x6D, 0x52, 0x79, 0x0D, 0x0A, 0x61, 0x58, 0x5A, 0x6C, 0x63, 0x6A, 0x45, 0x4D, 0x4D,
|
|
0x41, 0x6F, 0x47, 0x41, 0x31, 0x55, 0x45, 0x43, 0x78, 0x4D, 0x44, 0x51, 0x30, 0x52, 0x44, 0x4D,
|
|
0x51, 0x38, 0x77, 0x44, 0x51, 0x59, 0x44, 0x56, 0x51, 0x51, 0x44, 0x45, 0x77, 0x5A, 0x49, 0x62,
|
|
0x33, 0x64, 0x68, 0x63, 0x6D, 0x51, 0x78, 0x48, 0x44, 0x41, 0x61, 0x42, 0x67, 0x6B, 0x71, 0x68,
|
|
0x6B, 0x69, 0x47, 0x39, 0x77, 0x30, 0x42, 0x0D, 0x0A, 0x43, 0x51, 0x45, 0x57, 0x44, 0x58, 0x68,
|
|
0x6F, 0x4E, 0x44, 0x49, 0x34, 0x51, 0x44, 0x45, 0x32, 0x4D, 0x79, 0x35, 0x6A, 0x62, 0x32, 0x30,
|
|
0x77, 0x48, 0x68, 0x63, 0x4E, 0x4D, 0x54, 0x49, 0x77, 0x4D, 0x7A, 0x45, 0x30, 0x4D, 0x44, 0x55,
|
|
0x77, 0x4D, 0x7A, 0x41, 0x35, 0x57, 0x68, 0x63, 0x4E, 0x4D, 0x54, 0x49, 0x77, 0x4E, 0x44, 0x45,
|
|
0x7A, 0x4D, 0x44, 0x55, 0x77, 0x4D, 0x7A, 0x41, 0x35, 0x0D, 0x0A, 0x57, 0x6A, 0x42, 0x34, 0x4D,
|
|
0x51, 0x73, 0x77, 0x43, 0x51, 0x59, 0x44, 0x56, 0x51, 0x51, 0x47, 0x45, 0x77, 0x4A, 0x44, 0x54,
|
|
0x6A, 0x45, 0x4C, 0x4D, 0x41, 0x6B, 0x47, 0x41, 0x31, 0x55, 0x45, 0x43, 0x42, 0x4D, 0x43, 0x51,
|
|
0x6B, 0x6F, 0x78, 0x43, 0x7A, 0x41, 0x4A, 0x42, 0x67, 0x4E, 0x56, 0x42, 0x41, 0x63, 0x54, 0x41,
|
|
0x6B, 0x4A, 0x4B, 0x4D, 0x52, 0x49, 0x77, 0x45, 0x41, 0x59, 0x44, 0x0D, 0x0A, 0x56, 0x51, 0x51,
|
|
0x4B, 0x45, 0x77, 0x6C, 0x33, 0x61, 0x57, 0x35, 0x6B, 0x63, 0x6D, 0x6C, 0x32, 0x5A, 0x58, 0x49,
|
|
0x78, 0x44, 0x44, 0x41, 0x4B, 0x42, 0x67, 0x4E, 0x56, 0x42, 0x41, 0x73, 0x54, 0x41, 0x30, 0x4E,
|
|
0x45, 0x51, 0x7A, 0x45, 0x50, 0x4D, 0x41, 0x30, 0x47, 0x41, 0x31, 0x55, 0x45, 0x41, 0x78, 0x4D,
|
|
0x47, 0x53, 0x47, 0x39, 0x33, 0x59, 0x58, 0x4A, 0x6B, 0x4D, 0x52, 0x77, 0x77, 0x0D, 0x0A, 0x47,
|
|
0x67, 0x59, 0x4A, 0x4B, 0x6F, 0x5A, 0x49, 0x68, 0x76, 0x63, 0x4E, 0x41, 0x51, 0x6B, 0x42, 0x46,
|
|
0x67, 0x31, 0x34, 0x61, 0x44, 0x51, 0x79, 0x4F, 0x45, 0x41, 0x78, 0x4E, 0x6A, 0x4D, 0x75, 0x59,
|
|
0x32, 0x39, 0x74, 0x4D, 0x49, 0x47, 0x66, 0x4D, 0x41, 0x30, 0x47, 0x43, 0x53, 0x71, 0x47, 0x53,
|
|
0x49, 0x62, 0x33, 0x44, 0x51, 0x45, 0x42, 0x41, 0x51, 0x55, 0x41, 0x41, 0x34, 0x47, 0x4E, 0x0D,
|
|
0x0A, 0x41, 0x44, 0x43, 0x42, 0x69, 0x51, 0x4B, 0x42, 0x67, 0x51, 0x43, 0x59, 0x67, 0x6F, 0x42,
|
|
0x46, 0x76, 0x74, 0x5A, 0x2F, 0x76, 0x47, 0x4D, 0x4F, 0x50, 0x54, 0x50, 0x36, 0x70, 0x41, 0x75,
|
|
0x72, 0x47, 0x4D, 0x48, 0x4C, 0x76, 0x39, 0x64, 0x39, 0x4B, 0x6C, 0x6B, 0x67, 0x2F, 0x78, 0x44,
|
|
0x2B, 0x7A, 0x73, 0x4B, 0x31, 0x76, 0x52, 0x6A, 0x74, 0x35, 0x57, 0x2F, 0x61, 0x34, 0x2F, 0x36,
|
|
0x78, 0x0D, 0x0A, 0x48, 0x6F, 0x54, 0x2F, 0x69, 0x2B, 0x77, 0x78, 0x49, 0x46, 0x75, 0x4A, 0x72,
|
|
0x5A, 0x71, 0x55, 0x35, 0x49, 0x44, 0x6A, 0x54, 0x50, 0x77, 0x33, 0x43, 0x6D, 0x33, 0x6F, 0x76,
|
|
0x68, 0x49, 0x4A, 0x2B, 0x38, 0x6A, 0x73, 0x56, 0x6F, 0x6F, 0x46, 0x32, 0x66, 0x51, 0x50, 0x2B,
|
|
0x2B, 0x6E, 0x30, 0x30, 0x59, 0x58, 0x67, 0x58, 0x71, 0x57, 0x53, 0x2F, 0x51, 0x37, 0x2F, 0x6E,
|
|
0x44, 0x64, 0x4C, 0x0D, 0x0A, 0x6F, 0x78, 0x62, 0x4F, 0x39, 0x4E, 0x6F, 0x6D, 0x43, 0x4B, 0x43,
|
|
0x68, 0x5A, 0x61, 0x32, 0x63, 0x46, 0x6E, 0x7A, 0x57, 0x63, 0x42, 0x68, 0x54, 0x79, 0x67, 0x41,
|
|
0x2B, 0x6A, 0x31, 0x38, 0x78, 0x34, 0x4D, 0x50, 0x77, 0x36, 0x30, 0x41, 0x46, 0x33, 0x77, 0x4D,
|
|
0x59, 0x67, 0x78, 0x4E, 0x4E, 0x6E, 0x62, 0x4B, 0x37, 0x44, 0x77, 0x49, 0x44, 0x41, 0x51, 0x41,
|
|
0x42, 0x6F, 0x34, 0x48, 0x64, 0x0D, 0x0A, 0x4D, 0x49, 0x48, 0x61, 0x4D, 0x42, 0x30, 0x47, 0x41,
|
|
0x31, 0x55, 0x64, 0x44, 0x67, 0x51, 0x57, 0x42, 0x42, 0x54, 0x53, 0x6E, 0x7A, 0x4C, 0x6B, 0x51,
|
|
0x41, 0x33, 0x6B, 0x6C, 0x66, 0x4D, 0x78, 0x33, 0x57, 0x69, 0x31, 0x4A, 0x52, 0x56, 0x53, 0x37,
|
|
0x70, 0x35, 0x2F, 0x33, 0x6A, 0x43, 0x42, 0x71, 0x67, 0x59, 0x44, 0x56, 0x52, 0x30, 0x6A, 0x42,
|
|
0x49, 0x47, 0x69, 0x4D, 0x49, 0x47, 0x66, 0x0D, 0x0A, 0x67, 0x42, 0x54, 0x53, 0x6E, 0x7A, 0x4C,
|
|
0x6B, 0x51, 0x41, 0x33, 0x6B, 0x6C, 0x66, 0x4D, 0x78, 0x33, 0x57, 0x69, 0x31, 0x4A, 0x52, 0x56,
|
|
0x53, 0x37, 0x70, 0x35, 0x2F, 0x33, 0x71, 0x46, 0x38, 0x70, 0x48, 0x6F, 0x77, 0x65, 0x44, 0x45,
|
|
0x4C, 0x4D, 0x41, 0x6B, 0x47, 0x41, 0x31, 0x55, 0x45, 0x42, 0x68, 0x4D, 0x43, 0x51, 0x30, 0x34,
|
|
0x78, 0x43, 0x7A, 0x41, 0x4A, 0x42, 0x67, 0x4E, 0x56, 0x0D, 0x0A, 0x42, 0x41, 0x67, 0x54, 0x41,
|
|
0x6B, 0x4A, 0x4B, 0x4D, 0x51, 0x73, 0x77, 0x43, 0x51, 0x59, 0x44, 0x56, 0x51, 0x51, 0x48, 0x45,
|
|
0x77, 0x4A, 0x43, 0x53, 0x6A, 0x45, 0x53, 0x4D, 0x42, 0x41, 0x47, 0x41, 0x31, 0x55, 0x45, 0x43,
|
|
0x68, 0x4D, 0x4A, 0x64, 0x32, 0x6C, 0x75, 0x5A, 0x48, 0x4A, 0x70, 0x64, 0x6D, 0x56, 0x79, 0x4D,
|
|
0x51, 0x77, 0x77, 0x43, 0x67, 0x59, 0x44, 0x56, 0x51, 0x51, 0x4C, 0x0D, 0x0A, 0x45, 0x77, 0x4E,
|
|
0x44, 0x52, 0x45, 0x4D, 0x78, 0x44, 0x7A, 0x41, 0x4E, 0x42, 0x67, 0x4E, 0x56, 0x42, 0x41, 0x4D,
|
|
0x54, 0x42, 0x6B, 0x68, 0x76, 0x64, 0x32, 0x46, 0x79, 0x5A, 0x44, 0x45, 0x63, 0x4D, 0x42, 0x6F,
|
|
0x47, 0x43, 0x53, 0x71, 0x47, 0x53, 0x49, 0x62, 0x33, 0x44, 0x51, 0x45, 0x4A, 0x41, 0x52, 0x59,
|
|
0x4E, 0x65, 0x47, 0x67, 0x30, 0x4D, 0x6A, 0x68, 0x41, 0x4D, 0x54, 0x59, 0x7A, 0x0D, 0x0A, 0x4C,
|
|
0x6D, 0x4E, 0x76, 0x62, 0x59, 0x49, 0x4A, 0x41, 0x4D, 0x67, 0x68, 0x75, 0x72, 0x6B, 0x54, 0x6E,
|
|
0x6D, 0x6D, 0x48, 0x4D, 0x41, 0x77, 0x47, 0x41, 0x31, 0x55, 0x64, 0x45, 0x77, 0x51, 0x46, 0x4D,
|
|
0x41, 0x4D, 0x42, 0x41, 0x66, 0x38, 0x77, 0x44, 0x51, 0x59, 0x4A, 0x4B, 0x6F, 0x5A, 0x49, 0x68,
|
|
0x76, 0x63, 0x4E, 0x41, 0x51, 0x45, 0x46, 0x42, 0x51, 0x41, 0x44, 0x67, 0x59, 0x45, 0x41, 0x0D,
|
|
0x0A, 0x4D, 0x44, 0x34, 0x41, 0x6C, 0x62, 0x4A, 0x42, 0x48, 0x6C, 0x47, 0x76, 0x70, 0x6D, 0x4B,
|
|
0x51, 0x52, 0x6F, 0x4B, 0x55, 0x4B, 0x59, 0x6F, 0x74, 0x79, 0x2B, 0x67, 0x41, 0x4C, 0x39, 0x44,
|
|
0x76, 0x6D, 0x50, 0x6F, 0x61, 0x68, 0x75, 0x6B, 0x46, 0x59, 0x6E, 0x35, 0x78, 0x33, 0x47, 0x39,
|
|
0x53, 0x58, 0x72, 0x51, 0x32, 0x57, 0x4F, 0x33, 0x4D, 0x69, 0x51, 0x65, 0x56, 0x4A, 0x65, 0x4F,
|
|
0x73, 0x0D, 0x0A, 0x4B, 0x36, 0x49, 0x6B, 0x6F, 0x35, 0x4A, 0x49, 0x69, 0x50, 0x70, 0x6F, 0x55,
|
|
0x4F, 0x30, 0x62, 0x71, 0x36, 0x6D, 0x30, 0x77, 0x64, 0x79, 0x67, 0x2F, 0x78, 0x5A, 0x41, 0x77,
|
|
0x45, 0x2F, 0x77, 0x78, 0x6B, 0x44, 0x38, 0x4B, 0x62, 0x43, 0x57, 0x32, 0x54, 0x38, 0x31, 0x37,
|
|
0x63, 0x42, 0x47, 0x58, 0x77, 0x58, 0x6B, 0x63, 0x42, 0x51, 0x65, 0x38, 0x70, 0x71, 0x64, 0x49,
|
|
0x68, 0x2F, 0x33, 0x0D, 0x0A, 0x37, 0x4B, 0x35, 0x6D, 0x75, 0x78, 0x70, 0x41, 0x31, 0x70, 0x6E,
|
|
0x41, 0x73, 0x73, 0x57, 0x46, 0x39, 0x78, 0x47, 0x46, 0x63, 0x50, 0x5A, 0x75, 0x6F, 0x68, 0x37,
|
|
0x39, 0x56, 0x78, 0x61, 0x4D, 0x44, 0x42, 0x62, 0x59, 0x33, 0x72, 0x46, 0x42, 0x4A, 0x7A, 0x73,
|
|
0x3D, 0x0D, 0x0A, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x45, 0x4E, 0x44, 0x20, 0x43, 0x45, 0x52, 0x54,
|
|
0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D};
|
|
|
|
int mode = IP_O_RDWR | IP_O_CREAT
|
|
#ifdef IP_O_BINARY
|
|
| IP_O_BINARY
|
|
#endif
|
|
;
|
|
Ip_fd fd;
|
|
|
|
fd = ipcom_fileopen("ftp_tls.cert", mode,
|
|
IP_S_IRUSR | IP_S_IWUSR |
|
|
IP_S_IRGRP | IP_S_IROTH);
|
|
if(fd < 0)
|
|
{
|
|
return;
|
|
}
|
|
ipcom_filewrite(fd, defaultCert, sizeof(defaultCert));
|
|
ipcom_fileclose(fd);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_ntop
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* Print IP address using ntop.
|
|
*
|
|
* Parameters:
|
|
* addr IP address.
|
|
* dest Result buffer.
|
|
* len Size of result buffer.
|
|
*
|
|
* Returns:
|
|
* Pointer to buffer if success, 0 otherwise.
|
|
*/
|
|
IP_STATIC const char *
|
|
ipftps_ntop(union Ip_sockaddr_union *addr, char *dest, int len)
|
|
{
|
|
switch (addr->sa.sa_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
return ipcom_inet_ntop(addr->sa.sa_family,
|
|
&addr->sin.sin_addr, dest, len);
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
return ipcom_inet_ntop(addr->sa.sa_family,
|
|
&addr->sin6.sin6_addr, dest, len);
|
|
#endif
|
|
|
|
default:
|
|
ipcom_strcpy(dest, "?");
|
|
break;
|
|
}
|
|
|
|
return (const char *)dest;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_perror
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* Send errno based error message to control socket.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* no FTP error number.
|
|
*
|
|
* Returns:
|
|
* Nothing.
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_perror(Ipftps_session *session, int no)
|
|
{
|
|
ipftps_reply(session, "%d %s"CL, no, ipcom_strerror(ipcom_errno));
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_dsock_open
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* Open data socket.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* passive_open True if opening a passive socket.
|
|
*
|
|
* Returns:
|
|
* 0 if success, 1 otherwise.
|
|
*/
|
|
|
|
IP_STATIC int
|
|
ipftps_dsock_open(Ipftps_session *session, int passive_open)
|
|
{
|
|
Ipftps_sockcb *dsock = &session->dsock;
|
|
Ip_fd fd;
|
|
int attempts;
|
|
int num, eno = 0;
|
|
int passive_accept = (passive_open == 0 && session->passive != 0);
|
|
char buf[IPFTPS_IP_MAXLEN];
|
|
#ifdef IP_SO_LINGER
|
|
struct Ip_linger ling;
|
|
#endif
|
|
|
|
/* No active connections allowed when EPSV ALL is selected */
|
|
if (session->epsvonly && passive_open == 0 && session->passive == 0)
|
|
{
|
|
ipftps_reply(session, "503 Missing EPSV after EPSV ALL"CL);
|
|
goto leave2;
|
|
}
|
|
|
|
/* Handle passive mode */
|
|
if (passive_accept)
|
|
{
|
|
struct Ip_timeval timeval;
|
|
Ip_socklen_t len = sizeof(dsock->paddr);
|
|
Ip_fd_set set;
|
|
|
|
IP_FD_ZERO(&set);
|
|
IP_FD_SET(session->dsock.fd, &set);
|
|
timeval.tv_sec = ipftps_sysget_int("ipftps.receive_timeout", 30, 1);
|
|
timeval.tv_usec = 0;
|
|
num = ipcom_socketselect(session->dsock.fd + 1, &set,
|
|
IP_NULL, IP_NULL, &timeval);
|
|
if (num == -1)
|
|
{
|
|
eno = ipcom_errno;
|
|
IPCOM_LOG1(ERR, "Cannot select on data socket: %s",
|
|
ipcom_strerror(eno));
|
|
goto leave1;
|
|
}
|
|
else if (num == 0 || !IP_FD_ISSET(session->dsock.fd, &set))
|
|
{
|
|
IPCOM_LOG0(WARNING, "No connect from peer in passive mode");
|
|
eno = 0;
|
|
goto leave1;
|
|
}
|
|
|
|
fd = ipcom_accept(dsock->fd, &dsock->paddr.sa, &len);
|
|
if (fd < 0)
|
|
{
|
|
eno = ipcom_errno;
|
|
IPCOM_LOG2(ERR, "ipcom_accept() failed : %s [%d]",
|
|
ipcom_strerror(eno), eno);
|
|
goto leave1;
|
|
}
|
|
|
|
/* Close original socket and replace with new */
|
|
if (ipcom_socketclose(dsock->fd) != 0)
|
|
{
|
|
IPCOM_LOG1(ERR, "Cannot close socket: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
}
|
|
dsock->fd = fd;
|
|
|
|
if (session->proxy == 0 &&
|
|
IPFTPS_ADDRCMP(&dsock->paddr, &session->csock.paddr) != 0)
|
|
{
|
|
(void)ipftps_ntop(&dsock->paddr, buf, sizeof(buf));
|
|
|
|
IPCOM_LOG1(WARNING, "Peer IP address %s denied", buf);
|
|
ipftps_reply(session, "425 Invalid host"CL);
|
|
eno = 0;
|
|
goto leave1;
|
|
}
|
|
|
|
ipftps_reply(session, "125 Using existing data connection"CL);
|
|
}
|
|
else
|
|
{
|
|
int option;
|
|
int port;
|
|
|
|
if (dsock->fd != -1)
|
|
{
|
|
if (ipcom_socketclose(dsock->fd) != 0)
|
|
{
|
|
IPCOM_LOG1(ERR, "Cannot close socket: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
}
|
|
|
|
session->dsock.fd = -1;
|
|
session->passive = 0;
|
|
}
|
|
|
|
if ((dsock->fd = ipcom_socket(dsock->paddr.sa.sa_family,
|
|
IP_SOCK_STREAM, IPFTPS_PROTO)) < 0)
|
|
{
|
|
eno = ipcom_errno;
|
|
IPCOM_LOG1(ERR, "Cannot allocate data socket: %s",
|
|
ipcom_strerror(eno));
|
|
goto leave2;
|
|
}
|
|
|
|
option = 1;
|
|
if (ipcom_setsockopt(dsock->fd, IP_SOL_SOCKET, IP_SO_REUSEADDR,
|
|
&option, sizeof(option)) < 0)
|
|
{
|
|
IPCOM_LOG1(WARNING, "Cannot reuse socket address: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
}
|
|
|
|
#ifdef IP_SO_LINGER
|
|
ling.l_onoff = 1;
|
|
ling.l_linger = ipftps_sysget_int("ipftps.dsock_linger", 10, IP_NULL);
|
|
if (ipcom_setsockopt(dsock->fd, IP_SOL_SOCKET, IP_SO_LINGER, &ling, sizeof(ling)) < 0)
|
|
{
|
|
IPCOM_LOG1(WARNING, "ipcom_setsockopt(IP_SO_LINGER) failed : %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
}
|
|
#endif /* IP_SO_LINGER */
|
|
|
|
attempts = IPFTPS_BIND_ATTEMPTS;
|
|
|
|
|
|
/* Loop for bind attempts */
|
|
for (;;)
|
|
{
|
|
if (passive_open == 0)
|
|
{
|
|
/* According to RFC959 an active FTP session should use a adjacent port to
|
|
* the configured ctrl port (i.e. control connection port -1).
|
|
*/
|
|
port = ipftps_sysget_int("ipftps.port_number", 21, 1) - 1;
|
|
}
|
|
else
|
|
{
|
|
/* Select random port above lportbase */
|
|
port = (int)ipcom_random();
|
|
port = (int)(((unsigned long)port %
|
|
(65535U - session->lportbase)) +
|
|
session->lportbase + 1);
|
|
ip_assert(port > session->lportbase);
|
|
ip_assert(port <= 65535);
|
|
}
|
|
|
|
IPFTPS_PORT(&dsock->laddr) = (Ip_u16)ip_htons(port);
|
|
|
|
if (ipcom_bind(dsock->fd, &dsock->laddr.sa,
|
|
(Ip_socklen_t)IPFTPS_SOCKADDRLEN(&dsock->laddr)) < 0)
|
|
{
|
|
/* Reiterate if using random port numbers */
|
|
if (passive_open != 0 && --attempts > 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
eno = ipcom_errno;
|
|
IPCOM_LOG3(ERR, "Cannot bind data socket %s:%d: %s",
|
|
ipftps_ntop(&dsock->laddr, buf, sizeof(buf)), (int)port,
|
|
ipcom_strerror(eno));
|
|
goto leave1;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
#ifdef IPIPTOS_THROUGHPUT
|
|
/* Setup for max throughput service */
|
|
option = IPIPTOS_THROUGHPUT;
|
|
if (ipcom_setsockopt(dsock->fd, IP_IPPROTO_IP, IPIP_TOS,
|
|
&option, sizeof(option)) < 0)
|
|
{
|
|
IPCOM_LOG1(WARNING,
|
|
"Cannot setup for max throughput service: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
}
|
|
#endif
|
|
|
|
/* Active or passive open */
|
|
if (passive_open)
|
|
{
|
|
if (ipcom_listen(dsock->fd, 1) != 0)
|
|
{
|
|
eno = ipcom_errno;
|
|
IPCOM_LOG1(ERR, "Cannot listen on data socket: %s",
|
|
ipcom_strerror(eno));
|
|
goto leave1;
|
|
}
|
|
session->passive = 1;
|
|
}
|
|
else
|
|
{
|
|
#ifdef IPFTPS_USE_IIS_150
|
|
/*
|
|
* The IIS FTP server sends the 150 message
|
|
* before opening the data connection.
|
|
*/
|
|
ipftps_reply(session, "150 Opening data connection"CL);
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
if (dsock->paddr.sa.sa_family == IP_AF_INET6)
|
|
{
|
|
struct Ip_sockaddr_in6 laddr;
|
|
Ip_socklen_t laddr_len = sizeof(laddr);
|
|
|
|
/* Get the scope from the local address */
|
|
if (ipcom_getsockname(dsock->fd, (struct Ip_sockaddr *) &laddr, &laddr_len) < 0)
|
|
{
|
|
eno = ipcom_errno;
|
|
IPCOM_LOG1(ERR, "Cannot get local address of socket: %s",
|
|
ipcom_strerror(eno));
|
|
goto leave1;
|
|
}
|
|
/* Use the same scope as the local address */
|
|
dsock->paddr.sin6.sin6_scope_id = laddr.sin6_scope_id;
|
|
}
|
|
#endif /* IPCOM_USE_INET6 */
|
|
|
|
if (ipcom_connect(dsock->fd, &dsock->paddr.sa,
|
|
(Ip_socklen_t)IPFTPS_SOCKADDRLEN(&dsock->paddr)) < 0)
|
|
{
|
|
eno = ipcom_errno;
|
|
IPCOM_LOG1(ERR, "Cannot connect to data socket: %s",
|
|
ipcom_strerror(eno));
|
|
goto leave1;
|
|
}
|
|
|
|
#ifndef IPFTPS_USE_IIS_150
|
|
ipftps_reply(session, "150 Opening data connection"CL);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#ifdef IPFTP_TLS
|
|
|
|
if((passive_open == 0)
|
|
&&(ipftps_tls_enbale == IP_TRUE)
|
|
&&(ipftps_create_new_tls_dsession(session,&eno) != IPCOM_SUCCESS))
|
|
{
|
|
ipftps_dsock_close(session);
|
|
ipftps_reply(session, "425 TLS data session can't be created. err = %d"CL, eno);
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
leave1:
|
|
ipftps_dsock_close(session);
|
|
|
|
leave2:
|
|
if (eno != 0)
|
|
{
|
|
ipftps_reply(session, "425 %s"CL, ipcom_strerror(eno));
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_dsock_close
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* Close data socket.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
*
|
|
* Returns:
|
|
* Nothing.
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_dsock_close(Ipftps_session *session)
|
|
{
|
|
|
|
#ifdef IPFTP_TLS
|
|
if(ipftps_tls_enbale == IP_TRUE)
|
|
{
|
|
ipftps_tls_close_session(session,IPFTPS_DATA_FLAG);
|
|
}
|
|
#endif
|
|
|
|
if (session->dsock.fd != -1 && ipcom_socketclose(session->dsock.fd) != 0)
|
|
{
|
|
IPCOM_LOG1(ERR, "Cannot close socket: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
}
|
|
|
|
/* Reset peer address to default */
|
|
|
|
session->dsock.fd = -1;
|
|
session->passive = 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_write
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* Write to socket, terminate session if control socket down.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* fd Socket descriptor.
|
|
* buf Buffer.
|
|
* len Buffer length.
|
|
*
|
|
* Returns:
|
|
* 0 if success, 1 otherwise.
|
|
*/
|
|
IP_STATIC int
|
|
ipftps_write(Ipftps_session *session, Ip_fd fd, void *buf, int len)
|
|
{
|
|
|
|
#ifdef IPFTP_TLS
|
|
int ret;
|
|
#endif
|
|
#ifdef IPFTP_TLS
|
|
if((ipftps_tls_enbale == IP_TRUE)&&
|
|
(ret = ipftps_tls_send(session,fd,buf,len)) != 2)
|
|
{
|
|
if(ret == 1)
|
|
{
|
|
IPCOM_LOG0(WARNING,"TLS write error");
|
|
goto leave;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
if (ipcom_socketwrite(fd, buf, len) != len)
|
|
{
|
|
IPCOM_LOG1(WARNING, "Socket write error: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
goto leave;
|
|
}
|
|
|
|
return 0;
|
|
leave:
|
|
/* Terminate session if control socket closed */
|
|
if (fd == session->csock.fd)
|
|
{
|
|
session->authentications = 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_reply_sock
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* Reply to peer.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* fd Socket descriptor.
|
|
* fmt Format string.
|
|
* ... Additional parms.
|
|
*
|
|
* Returns:
|
|
* Nothing.
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_reply_sock(Ipftps_session *session, Ip_fd fd, const char *fmt, ...)
|
|
{
|
|
char buf[512];
|
|
va_list argptr;
|
|
int len;
|
|
|
|
va_start(argptr, fmt);
|
|
len = ipcom_vsnprintf(buf, sizeof(buf), fmt, argptr);
|
|
va_end(argptr);
|
|
|
|
if (len < 0)
|
|
{
|
|
IPCOM_LOG0(ERR, "Error in ipcom_vsnprintf(), out of memory?");
|
|
ipcom_strcpy(buf, "452 Error in ipcom_vsnprintf(), out of memory?");
|
|
len = (int)ipcom_strlen(buf);
|
|
}
|
|
else if (len > (int)sizeof(buf))
|
|
{
|
|
IPCOM_LOG0(WARNING, "Reply buffer overflow");
|
|
buf[sizeof(buf) - 2] = '\r';
|
|
buf[sizeof(buf) - 1] = '\n';
|
|
len = sizeof(buf);
|
|
}
|
|
|
|
(void)ipftps_write(session, fd, buf, len);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_reply
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* Reply to the peer PI.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* fmt Format string.
|
|
* ... Additional parms.
|
|
*
|
|
* Returns:
|
|
* Nothing.
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_reply(Ipftps_session *session, const char *fmt, ...)
|
|
{
|
|
char buf[512];
|
|
va_list argptr;
|
|
int len;
|
|
|
|
va_start(argptr, fmt);
|
|
len = ipcom_vsnprintf(buf, sizeof(buf), fmt, argptr);
|
|
va_end(argptr);
|
|
|
|
if (len < 0)
|
|
{
|
|
IPCOM_LOG0(ERR, "Error in ipcom_vsnprintf(), out of memory?");
|
|
ipcom_strcpy(buf, "452 Error in ipcom_vsnprintf(), out of memory?");
|
|
len = (int)ipcom_strlen(buf);
|
|
}
|
|
else if (len > (int)sizeof(buf))
|
|
{
|
|
IPCOM_LOG0(WARNING, "Reply buffer overflow");
|
|
buf[sizeof(buf) - 2] = '\r';
|
|
buf[sizeof(buf) - 1] = '\n';
|
|
len = sizeof(buf);
|
|
}
|
|
|
|
(void)ipftps_write(session, session->csock.fd, buf, len);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_telnet
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* Handle telnet stuff.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
*
|
|
* Returns:
|
|
* 0 if success, 1 otherwise.
|
|
*/
|
|
IP_STATIC int
|
|
ipftps_telnet(Ipftps_session *session)
|
|
{
|
|
Ip_u8 *cp = (Ip_u8 *)(session->cmdbuf);
|
|
int i = 0;
|
|
|
|
/* Loop through entire command buffer */
|
|
while (i < session->cmdbuflen)
|
|
{
|
|
int left = session->cmdbuflen - i;
|
|
|
|
do
|
|
{
|
|
int skip = 0;
|
|
int reply = 0;
|
|
int j;
|
|
|
|
/* Check for telnet */
|
|
if (cp[0] == IPFTPS_DM)
|
|
{
|
|
/*
|
|
** This is a special case which will occur when
|
|
** inline handling of OOB data is missing. It
|
|
** seems a reasonable compromise to ignore all
|
|
** DMs in input even if not preceded by IAC.
|
|
*/
|
|
skip = 1;
|
|
}
|
|
else if (cp[0] == IPFTPS_IAC && left >= 2)
|
|
{
|
|
switch (cp[1])
|
|
{
|
|
case IPFTPS_WILL:
|
|
if (left >= 3)
|
|
{
|
|
skip = 3;
|
|
reply = 1;
|
|
cp[1] = IPFTPS_DONT;
|
|
}
|
|
break;
|
|
case IPFTPS_DO:
|
|
if (left >= 3)
|
|
{
|
|
skip = 3;
|
|
reply = 1;
|
|
cp[1] = IPFTPS_WONT;
|
|
}
|
|
break;
|
|
case IPFTPS_WONT:
|
|
case IPFTPS_DONT:
|
|
if (left >= 3)
|
|
{
|
|
skip = 3;
|
|
}
|
|
break;
|
|
default:
|
|
skip = 2;
|
|
break;
|
|
}
|
|
|
|
/* Reply if negotiation detected */
|
|
if (reply)
|
|
{
|
|
(void)ipftps_write(session, session->csock.fd, cp, skip);
|
|
}
|
|
}
|
|
|
|
/* Remove telnet stuff from command buffer */
|
|
if (skip > 0)
|
|
{
|
|
for (j = 0; j < (left - skip); j++)
|
|
{
|
|
cp[j] = cp[j + skip];
|
|
}
|
|
|
|
session->cmdbuflen -= skip;
|
|
|
|
/* Continue without advancing indices */
|
|
break;
|
|
}
|
|
|
|
i++;
|
|
cp++;
|
|
}
|
|
while (0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_get_command
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* Get a complete command from the PI socket.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
*
|
|
* Returns:
|
|
* 0 if success, 1 otherwise.
|
|
*/
|
|
IP_STATIC int
|
|
ipftps_get_command(Ipftps_session *session)
|
|
{
|
|
int len;
|
|
int num, i;
|
|
int width;
|
|
|
|
/* Put possible last command first in buffer */
|
|
for (i = 0; i < (session->cmdbuflen - session->cmdlen); i++)
|
|
{
|
|
session->cmdbuf[i] = session->cmdbuf[session->cmdlen + i];
|
|
}
|
|
session->cmdbuflen -= session->cmdlen;
|
|
session->cmdlen = 0;
|
|
|
|
/* Loop until command available */
|
|
for (;;)
|
|
{
|
|
struct Ip_timeval timeval;
|
|
Ip_fd_set set;
|
|
|
|
timeval.tv_sec = ipftps_sysget_int("ipftps.session_timeout", 300, 1);
|
|
timeval.tv_usec = 0;
|
|
|
|
/* If session->login is 1, this session must be a FTP session. */
|
|
if (session->login != 1)
|
|
/* Handle telnet stuff */
|
|
(void)ipftps_telnet(session);
|
|
|
|
/* Check if there already is a pending command */
|
|
for (i = 0; i < session->cmdbuflen; i++)
|
|
{
|
|
if (session->cmdbuf[i] == '\r')
|
|
{
|
|
if (i < (session->cmdbuflen - 1) &&
|
|
session->cmdbuf[i + 1] == '\n')
|
|
{
|
|
session->cmdbuf[i] = '\0';
|
|
session->cmdlen = i + 2;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Truncate if buffer is full but no command received */
|
|
if (session->cmdbuflen >= IPFTPS_CMDBUFSIZE)
|
|
{
|
|
session->cmdbuf[session->cmdbuflen - 1] = '\0';
|
|
session->cmdlen = session->cmdbuflen;
|
|
return 0;
|
|
}
|
|
|
|
IP_FD_ZERO(&set);
|
|
IP_FD_SET(session->csock.fd, &set);
|
|
IP_FD_SET(ipftps.pipe_fd[0], &set);
|
|
width = IP_MAX(ipftps.pipe_fd[0], session->csock.fd);
|
|
num = ipcom_socketselect(width + 1, &set,
|
|
IP_NULL, IP_NULL, &timeval);
|
|
if (num == -1)
|
|
{
|
|
IPCOM_LOG1(WARNING, "Cannot select on control socket: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
break;
|
|
}
|
|
else if (num > 0 && IP_FD_ISSET(ipftps.pipe_fd[0], &set))
|
|
{
|
|
ipftps_reply(session, "421 Timeout, closing control connection"CL);
|
|
break;
|
|
}
|
|
else if (num == 0 || !IP_FD_ISSET(session->csock.fd, &set))
|
|
{
|
|
IPCOM_LOG0(WARNING, "Session timeout");
|
|
ipftps_reply(session, "421 Timeout, closing control connection"CL);
|
|
break;
|
|
}
|
|
#ifdef IPFTP_TLS
|
|
if((ipftps_tls_enbale == IP_TRUE)&&
|
|
(ipftps_tls_read(session,
|
|
session->csock.fd,
|
|
session->cmdbuf + session->cmdbuflen,
|
|
IPFTPS_CMDBUFSIZE - session->cmdbuflen,
|
|
&len) != 1))
|
|
{
|
|
|
|
if (len == 0)
|
|
{
|
|
IPCOM_LOG0(INFO, "Connection closed by remote host");
|
|
break;
|
|
}
|
|
else if(len < 0)
|
|
{
|
|
IPCOM_LOG0(WARNING, "Cannot read from TLS control socket");
|
|
break;
|
|
}
|
|
|
|
|
|
}
|
|
else
|
|
#endif
|
|
/* Get more data */
|
|
if ((len = (int)ipcom_socketread(session->csock.fd,
|
|
session->cmdbuf + session->cmdbuflen,
|
|
IPFTPS_CMDBUFSIZE - session->cmdbuflen)) == 0)
|
|
{
|
|
IPCOM_LOG0(INFO, "Connection closed by remote host");
|
|
break;
|
|
}
|
|
else if (len < 0)
|
|
{
|
|
IPCOM_LOG1(WARNING, "Cannot read from control socket: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
break;
|
|
}
|
|
|
|
session->cmdbuflen += len;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_parse_command
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* Parse a command from the PI socket.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC int
|
|
ipftps_parse_command(Ipftps_session *session)
|
|
{
|
|
char *argv[IPFTPS_MAX_ARGS];
|
|
const Ipftps_cmd *cmd;
|
|
char *stop;
|
|
char *cp = session->cmdbuf;
|
|
int i;
|
|
#ifdef IP_PORT_VXWORKS
|
|
char *tmpPtr = IP_NULL;
|
|
char *tmpPtr1 = IP_NULL;
|
|
#endif
|
|
|
|
ipcom_memset(argv, 0, IPFTPS_MAX_ARGS * sizeof(char *));
|
|
|
|
/* Ignore initial whitespace */
|
|
cp += ipcom_strspn(cp, IPFTPS_DELIMITERS);
|
|
if (*cp == '\0')
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* Parse arguments */
|
|
/* Get the command */
|
|
for (i = 0; *cp != '\0' && i < 1; i++)
|
|
{
|
|
argv[i] = cp;
|
|
|
|
cp += ipcom_strcspn(cp, IPFTPS_DELIMITERS);
|
|
|
|
stop = cp;
|
|
if ((ipcom_strncasecmp(argv[0], "PASS", 4) == 0)||
|
|
(ipcom_strncasecmp(argv[0], "USER", 4) == 0))
|
|
{
|
|
/*
|
|
* The password will start after PASS cmd plus one blank space char.
|
|
* And it could be any character except <CR> and <LF>
|
|
*/
|
|
if (*cp == ' ')
|
|
cp++;
|
|
}
|
|
else
|
|
cp += ipcom_strspn(cp, IPFTPS_DELIMITERS);
|
|
*stop = '\0';
|
|
}
|
|
|
|
/* Scan through command list */
|
|
for (cmd = ipftps_cmdlist; cmd->name != IP_NULL; cmd++)
|
|
{
|
|
if (ipcom_strcasecmp(cmd->name, argv[0]) == 0)
|
|
{
|
|
if(cmd->argtype == 0)
|
|
{
|
|
char delimiters[16];
|
|
|
|
if ((ipcom_strncasecmp(argv[0], "PASS", 4) == 0) ||
|
|
(ipcom_strncasecmp(argv[0], "USER", 4) == 0))
|
|
ipcom_strcpy(delimiters, IPFTPS_PASS_DELIMITERS);
|
|
else
|
|
ipcom_strcpy(delimiters, IPFTPS_DELIMITERS);
|
|
|
|
for (i = i; *cp != '\0' && i < IPFTPS_MAX_ARGS; i++)
|
|
{
|
|
argv[i] = cp;
|
|
cp += ipcom_strcspn(cp, delimiters);
|
|
stop = cp;
|
|
cp += ipcom_strspn(cp, delimiters);
|
|
*stop = '\0';
|
|
}
|
|
}
|
|
else
|
|
{ /* Command argument type is string */
|
|
if (*cp != '\0')
|
|
{
|
|
argv[i] = cp;
|
|
i++;
|
|
}
|
|
}
|
|
i--;
|
|
|
|
/* Check if logged in */
|
|
if (cmd->login != 0 && session->login == 0)
|
|
{
|
|
IPCOM_LOG1(WARNING, "Attempted %s without login",
|
|
cmd->name);
|
|
ipftps_reply(session, "530 Login required"CL);
|
|
return 0;
|
|
}
|
|
|
|
/* Check argument no */
|
|
if (i < cmd->min || i > cmd->max)
|
|
{
|
|
ipftps_reply(session, "501 Invalid number of arguments"CL,
|
|
argv[0]);
|
|
return 0;
|
|
}
|
|
|
|
session->cwdroot = 0;
|
|
|
|
#ifdef IP_PORT_VXWORKS
|
|
if ((argv[1] != IP_NULL) && (i != 0))
|
|
{
|
|
tmpPtr = argv[1];
|
|
tmpPtr1 = ipcom_malloc(IP_PATH_MAX);
|
|
if(tmpPtr1 != IP_NULL)
|
|
{
|
|
argv[1] = tmpPtr1;
|
|
ipcom_strncpy(argv[1], tmpPtr, IP_PATH_MAX-1);
|
|
tmpPtr1[IP_PATH_MAX-1] = '\0';
|
|
}
|
|
}
|
|
#endif
|
|
/* Do command */
|
|
cmd->func(session, i, argv + 1);
|
|
#ifdef IP_PORT_VXWORKS
|
|
if (tmpPtr1 != IP_NULL)
|
|
{
|
|
/* Reset password buffer */
|
|
ipcom_memset(tmpPtr1, 0, IP_PATH_MAX);
|
|
ipcom_free(tmpPtr1);
|
|
argv[1] = tmpPtr;
|
|
}
|
|
#endif
|
|
/* Reset password buffer */
|
|
if(ipcom_strcasecmp(cmd->name, "PASS") == 0)
|
|
{
|
|
ipcom_memset(argv[1], 0, ipcom_strlen(argv[1]));
|
|
}
|
|
session->last = cmd;
|
|
|
|
return (session->authentications == 0 ? 1 : 0);
|
|
}
|
|
}
|
|
|
|
ipftps_reply(session, "500 Invalid command: \"%s\""CL, argv[0]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_async
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* Handle asynchronous commands during file transfer.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
*
|
|
* Returns:
|
|
* 1 if an ABOR command is found, 0 otherwise.
|
|
*/
|
|
IP_STATIC int
|
|
ipftps_async(Ipftps_session *session)
|
|
{
|
|
int closed = 0;
|
|
int aborted = 0;
|
|
int asynclen;
|
|
int len;
|
|
int i;
|
|
|
|
/* Put possible last command first in buffer */
|
|
for (i = 0; i < session->cmdlen; i++)
|
|
{
|
|
session->cmdbuf[i] = session->cmdbuf[session->cmdlen + i];
|
|
}
|
|
session->cmdbuflen -= session->cmdlen;
|
|
session->cmdlen = 0;
|
|
|
|
/* Get more data */
|
|
#ifdef IPFTP_TLS
|
|
if((ipftps_tls_enbale == IP_TRUE)&&
|
|
(ipftps_tls_read(session,
|
|
session->csock.fd,
|
|
session->cmdbuf + session->cmdbuflen,
|
|
IPFTPS_CMDBUFSIZE - session->cmdbuflen,
|
|
&len) != 1))
|
|
{
|
|
if (len > 0)
|
|
{
|
|
session->cmdbuflen += len;
|
|
|
|
}
|
|
else
|
|
{
|
|
IPCOM_LOG0(WARNING, "Cannot read from TLS control socket");
|
|
closed = 1;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if ((len = (int)ipcom_socketread(session->csock.fd,
|
|
session->cmdbuf + session->cmdbuflen,
|
|
IPFTPS_CMDBUFSIZE - session->cmdbuflen)) > 0)
|
|
{
|
|
session->cmdbuflen += len;
|
|
}
|
|
else
|
|
{
|
|
IPCOM_LOG1(WARNING, "Cannot read from control socket: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
closed = 1;
|
|
}
|
|
}
|
|
|
|
/* Handle telnet */
|
|
(void)ipftps_telnet(session);
|
|
|
|
/* Loop for multiple async commands */
|
|
do
|
|
{
|
|
char *asyncpos;
|
|
asynclen = 0;
|
|
|
|
#define IPFTPS_ABORLEN 6
|
|
#define IPFTPS_STATLEN 6
|
|
|
|
/* Do a minimum parse */
|
|
session->cmdbuf[session->cmdbuflen] = '\0';
|
|
if ((asyncpos = ipcom_strstr(session->cmdbuf, "ABOR"CL)) != IP_NULL)
|
|
{
|
|
asynclen = IPFTPS_ABORLEN;
|
|
aborted = 1;
|
|
}
|
|
else if ((asyncpos = ipcom_strstr(session->cmdbuf, "STAT"CL)) != IP_NULL)
|
|
{
|
|
asynclen = IPFTPS_STATLEN;
|
|
|
|
if (session->trtotal == 0)
|
|
{
|
|
ipftps_reply(session, "213 Received %ld bytes"CL,
|
|
(long)session->trnow);
|
|
}
|
|
else
|
|
{
|
|
ipftps_reply(session, "213 Sent %ld of %ld bytes"CL,
|
|
(long)session->trnow, (long)session->trtotal);
|
|
}
|
|
}
|
|
|
|
/*
|
|
** An async command that is handled above must be removed
|
|
** from the command buffer so it doesn't occur in the normal
|
|
** command stream later on.
|
|
*/
|
|
if (asyncpos != IP_NULL)
|
|
{
|
|
for (i = (int)(asyncpos - session->cmdbuf); i < (session->cmdbuflen - asynclen); i++)
|
|
{
|
|
session->cmdbuf[i] = session->cmdbuf[asynclen + i];
|
|
}
|
|
}
|
|
|
|
session->cmdbuflen -= asynclen;
|
|
}
|
|
while (aborted == 0 && asynclen > 0);
|
|
|
|
if (aborted)
|
|
{
|
|
ipftps_reply(session, "426 Transfer aborted"CL);
|
|
}
|
|
else if (closed)
|
|
{
|
|
ipftps_reply(session, "426 Control connection closed, transfer aborted"CL);
|
|
}
|
|
|
|
return aborted || closed;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_chdir
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* Change to the requested directory.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* path Path to change directory to
|
|
*
|
|
* Returns:
|
|
* Nothing.
|
|
*/
|
|
IP_STATIC int
|
|
ipftps_chdir(Ipftps_session *session, char *path)
|
|
{
|
|
#ifdef IP_PORT_VXWORKS
|
|
|
|
DIR *dir;
|
|
char *cwd = IP_NULL;
|
|
const char *pTail;
|
|
|
|
/*
|
|
* VxWorks filesystem does not check that the destination dir is valid--
|
|
* this must be done by the clients.
|
|
*/
|
|
if ((dir = opendir(path)) == NULL)
|
|
return -1;
|
|
|
|
(void)closedir(dir);
|
|
|
|
/*
|
|
* Store the full path on a session by session basis for vxworks.
|
|
*
|
|
* Since vxWorks is not a multi-user OS, changing directory via
|
|
* ipcom_chdir will change the cwd for every client connected
|
|
* to the server in the same RTP. To work around this limitation, we
|
|
* store the full cwd for each session and use absolute paths when
|
|
* issuing various commands (ie. mkdir, dele, etc).
|
|
*
|
|
* We make use of the existing pathCat and pathCondense
|
|
* routines to properly deal with path navigation elements
|
|
* ie '.' , '..' , '/../..', etc
|
|
*/
|
|
|
|
if ((cwd = ipcom_malloc(IP_PATH_MAX)) == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (ipcom_strcmp(session->cwd, path) != 0)
|
|
{
|
|
if (pathCat(session->cwd, path, cwd) == ERROR)
|
|
{
|
|
ipftps_free(cwd);
|
|
return -1;
|
|
}
|
|
pathCondense(cwd);
|
|
}
|
|
else
|
|
{
|
|
ipcom_strcpy(cwd, session->cwd);
|
|
pathCondense(cwd);
|
|
}
|
|
|
|
if ((path[strlen(path) - 1] == '/') && strcmp(cwd, "/"))
|
|
{
|
|
ipcom_strcat(cwd, "/");
|
|
}
|
|
|
|
/* Make sure that new path starts with a device name */
|
|
iosDevFind (cwd, &pTail);
|
|
if (pTail != cwd)
|
|
{
|
|
ipcom_strcpy(session->cwd, cwd);
|
|
ipftps_free(cwd);
|
|
return 0;
|
|
}
|
|
|
|
if (path[strlen(path) - 1] == '.')
|
|
{
|
|
ipcom_strcat(cwd, "/");
|
|
iosDevFind (cwd, &pTail);
|
|
if (pTail != cwd)
|
|
{
|
|
ipcom_strcpy(session->cwd, cwd);
|
|
ipftps_free(cwd);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
ipftps_free(cwd);
|
|
return -1;
|
|
#else
|
|
IPCOM_UNUSED_ARG(session);
|
|
|
|
if (ipcom_chdir(path) != 0)
|
|
{
|
|
return -1;
|
|
}
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_allo
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* ALLO command.
|
|
*
|
|
* Parameters:
|
|
* session Session descriptor.
|
|
* argc Number of arguments.
|
|
* argv Argument vector.
|
|
*
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_allo(Ipftps_session *session, int argc, char *argv[])
|
|
{
|
|
(void)argc;
|
|
(void)argv;
|
|
|
|
ipftps_reply(session, "202 Command not implemented"CL);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_terminate_sessions
|
|
*===========================================================================
|
|
* Description: Terminate all active FTP sessions
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipftps_terminate_sessions(void)
|
|
{
|
|
(void)ipcom_socketclose(ipftps.pipe_fd[1]);
|
|
(void)ipcom_millisleep(10);
|
|
|
|
for (;;)
|
|
{
|
|
ipcom_mutex_lock(ipftps.lock);
|
|
if (0 == ipftps.session_number)
|
|
{
|
|
ipcom_mutex_unlock(ipftps.lock);
|
|
break;
|
|
}
|
|
ipcom_mutex_unlock(ipftps.lock);
|
|
(void)ipcom_millisleep(10);
|
|
}
|
|
|
|
(void)ipcom_socketclose(ipftps.pipe_fd[0]);
|
|
|
|
ipftps.pipe_fd[0] = IP_INVALID_SOCKET;
|
|
ipftps.pipe_fd[1] = IP_INVALID_SOCKET;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_get_activesession
|
|
*===========================================================================
|
|
* Description: Get the current active session number of ftp server.
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_PUBLIC int
|
|
ipftps_get_activesession(void)
|
|
{
|
|
return ipftps.session_number;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_session
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* FTP session handler subprocess.
|
|
*
|
|
* Parameters:
|
|
*
|
|
* Returns:
|
|
*/
|
|
IPCOM_PROCESS(ipftps_session)
|
|
{
|
|
char buf[IPFTPS_IP_MAXLEN];
|
|
Ipftps_session *session = IP_NULL;
|
|
char ver[16];
|
|
Ip_size_t versize = sizeof(ver);
|
|
Ipcom_ipc ipc;
|
|
int retries = 5;
|
|
int retry_time = 1;
|
|
Ip_size_t cwdsize = IP_PATH_MAX;
|
|
#ifdef IP_PORT_VXWORKS
|
|
char *rmtBanner, *banner;
|
|
int length;
|
|
char *bannerP1, *bannerP2;
|
|
#endif
|
|
|
|
ipcom_proc_init();
|
|
|
|
/* Get session info from main */
|
|
if (ipcom_ipc_open(&ipc, "ipftps", -1) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "Failed to open IPC channel to 'ftps'");
|
|
goto leave;
|
|
}
|
|
|
|
/* Try getting memory a couple of times to avoid locking the main process */
|
|
while (--retries > 0 &&
|
|
(session = ipcom_ipc_malloc(1)) == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR, "Failed to allocate IPC buffer, retry");
|
|
(void)ipcom_sleep(retry_time);
|
|
retry_time *= 2;
|
|
}
|
|
if (session == IP_NULL)
|
|
{
|
|
goto leave;
|
|
}
|
|
|
|
if (ipcom_ipc_send(&ipc, session) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "Failed to send IPC buffer");
|
|
ipcom_ipc_free(session);
|
|
session = IP_NULL;
|
|
goto leave;
|
|
}
|
|
session = IP_NULL;
|
|
if (ipcom_ipc_receive(IP_NULL, &session, -1) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "Failed to receive IPC buffer");
|
|
goto leave;
|
|
}
|
|
#ifdef IP_PORT_OSE5
|
|
session->csock.fd = efs_receive_fd(0);
|
|
if (session->csock.fd == -1)
|
|
{
|
|
IPCOM_LOG0(ERR, "efs_receive_fd() failed");
|
|
goto leave;
|
|
}
|
|
#endif
|
|
if (ipcom_ipc_close(&ipc) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "Failed to close IPC channel");
|
|
goto leave;
|
|
}
|
|
|
|
(void)ipftps_ntop(&session->csock.paddr, buf, sizeof(buf));
|
|
IPCOM_LOG1(INFO, "Session handler start for %s", buf);
|
|
|
|
/* Use control socket as default template for data socket */
|
|
ipcom_memcpy(&session->dsock, &session->csock, sizeof(session->dsock));
|
|
session->dsock.fd = -1;
|
|
|
|
/* Set session defaults */
|
|
session->readonly = ipftps_sysget_int("ipftps.readonly", 0, 1);
|
|
session->proxy = ipftps_sysget_int("ipftps.proxy", 0, 1);
|
|
session->pportbase = (Ip_u16)ipftps_sysget_int("ipftps.pportbase", 1023, 1);
|
|
session->lportbase = (Ip_u16)ipftps_sysget_int("ipftps.lportbase", 49151, 1);
|
|
session->authentications = ipftps_sysget_int("ipftps.authentications", 3, 1);
|
|
session->root = ipftps_sysget_str("ipftps.root", IP_NULL, IP_NULL, "/", 1);
|
|
|
|
session->cwd = ipcom_malloc(cwdsize);
|
|
if (session->cwd == IP_NULL)
|
|
goto leave;
|
|
/* coverity[leaked_storage] */
|
|
(void)ipftps_sysget_str("ipftps.dir", session->cwd, &cwdsize,IPCOM_FILE_ROOT, 1);
|
|
|
|
#ifdef IPFTP_TLS
|
|
|
|
#ifdef IPFTPS_USE_TEST
|
|
ipftps_create_cert_and_privateKey();
|
|
#endif
|
|
ipcom_mutex_lock(ipftps.lock);
|
|
if(ipftps_init_tls(session) == IPCOM_ERR_FAILED)
|
|
{
|
|
ipcom_mutex_unlock(ipftps.lock);
|
|
session->tlsopt = IP_NULL;
|
|
goto leave;
|
|
}
|
|
ipcom_mutex_unlock(ipftps.lock);
|
|
if (ipftps_tls_enbale == IP_TRUE)
|
|
{
|
|
if (ipftps_init_tls_session(session) == IPCOM_ERR_FAILED)
|
|
goto leave;
|
|
}
|
|
#endif
|
|
|
|
#ifdef IP_SO_OOBINLINE
|
|
{
|
|
int option = 1;
|
|
|
|
/* Inline handling of OOB data */
|
|
if (ipcom_setsockopt(session->csock.fd, IP_SOL_SOCKET,
|
|
IP_SO_OOBINLINE, &option, sizeof(option)) < 0)
|
|
{
|
|
IPCOM_LOG1(WARNING, "Cannot setup inline OOB data: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef IPIPTOS_LOWDELAY
|
|
{
|
|
int option = IPIPTOS_LOWDELAY;
|
|
|
|
/* Setup for low delay service */
|
|
if (ipcom_setsockopt(session->csock.fd, IP_IPPROTO_IP,
|
|
IPIP_TOS, &option, sizeof(option)) < 0)
|
|
{
|
|
IPCOM_LOG1(WARNING, "Cannot setup for low delay service: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef IP_PORT_VXWORKS
|
|
if(_func_loginBanner != IP_NULL)
|
|
{
|
|
if(_func_loginBanner(&rmtBanner, &length) > 0)
|
|
{
|
|
banner = ipcom_strdup(rmtBanner);
|
|
if (IP_NULL == banner)
|
|
{
|
|
IPCOM_LOG0(ERR, "Failed to strdup");
|
|
goto leave;
|
|
}
|
|
|
|
bannerP1 = &banner[0];
|
|
while((bannerP2 = strchr(bannerP1,'\n')) != IP_NULL)
|
|
{
|
|
*bannerP2 = '\0';
|
|
if(bannerP1 == &banner[0])
|
|
ipftps_reply(session, "220- %s"CL, bannerP1);
|
|
else
|
|
ipftps_reply(session, "%s"CL, bannerP1);
|
|
bannerP1 = bannerP2 + 1;
|
|
}
|
|
|
|
if(bannerP1 == &banner[0])
|
|
ipftps_reply(session, "220- %s"CL, bannerP1);
|
|
else
|
|
ipftps_reply(session, "%s"CL, bannerP1);
|
|
|
|
if (banner != IP_NULL)
|
|
ipcom_free(banner);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
ipftps_reply(session, "220 Wind River FTP server %s ready."CL,
|
|
ipftps_sysget_str("ipftps.versionstr", ver, &versize, "-", 1));
|
|
|
|
/* Command loop */
|
|
while (ipftps_get_command(session) == 0 &&
|
|
ipftps_parse_command(session) == 0)
|
|
{
|
|
}
|
|
|
|
leave:
|
|
IPCOM_LOG0(INFO, "Session handler stop");
|
|
|
|
/* Cleanup */
|
|
if (session != IP_NULL)
|
|
{
|
|
/* Close data socket */
|
|
ipftps_dsock_close(session);
|
|
|
|
#ifdef IPFTP_TLS
|
|
if(ipftps_tls_enbale == IP_TRUE)
|
|
{
|
|
ipftps_tls_close_session(session,IPFTPS_CTRL_FLAG);
|
|
ipftps_free_tls_session(session);
|
|
}
|
|
#endif
|
|
/* Close control socket */
|
|
if (session->csock.fd != IP_INVALID_SOCKET &&
|
|
ipcom_socketclose(session->csock.fd) != 0)
|
|
{
|
|
IPCOM_LOG1(ERR, "Cannot close socket: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
}
|
|
|
|
/* Free session descriptor */
|
|
if (session->cwd != session->root)
|
|
{
|
|
ipftps_free(session->cwd);
|
|
}
|
|
ipftps_free(session->root);
|
|
if(session->username != IP_NULL)
|
|
ipftps_free(session->username);
|
|
ipftps_free(session->rnfr);
|
|
|
|
ipcom_mutex_lock(ipftps.lock);
|
|
/* Decrement number of sessions. */
|
|
if (--ipftps.session_number < 0)
|
|
ipftps.session_number = 0;
|
|
ipcom_mutex_unlock(ipftps.lock);
|
|
ipcom_ipc_free(session);
|
|
}
|
|
|
|
if (ipcom_ipc_isopen(&ipc))
|
|
{
|
|
(void)ipcom_ipc_close(&ipc);
|
|
}
|
|
|
|
ipcom_proc_exit();
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_main
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* FTP main process.
|
|
*
|
|
* Parameters:
|
|
*
|
|
* Returns:
|
|
*/
|
|
IPCOM_PROCESS(ipftps_main)
|
|
{
|
|
Ipftps_session *session = IP_NULL;
|
|
Ip_fd ipd_fd = IP_INVALID_SOCKET;
|
|
char bind_str[IPFTPS_IP_MAXLEN];
|
|
Ip_size_t bind_size;
|
|
int ipd_init = 0;
|
|
int option;
|
|
Ip_u16 port_number;
|
|
Ip_pid_t pid;
|
|
Ipcom_proc_attr attr;
|
|
#ifdef IPFTP_TLS
|
|
char *sysvar;
|
|
#endif
|
|
|
|
IPCOM_LOG0(INFO, "Starting server daemon");
|
|
|
|
/* Init data */
|
|
ipcom_proc_init();
|
|
|
|
ipcom_memset(&ipftps, 0, sizeof(ipftps));
|
|
|
|
ipftps.pipe_fd[0] = IP_INVALID_SOCKET;
|
|
ipftps.pipe_fd[1] = IP_INVALID_SOCKET;
|
|
#ifdef IPCOM_USE_INET
|
|
ipftps.sock4.fd = IP_INVALID_SOCKET;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
ipftps.sock6.fd = IP_INVALID_SOCKET;
|
|
#endif
|
|
|
|
/* create mutex to lock ipftps.session_number */
|
|
if (ipcom_mutex_create(&ipftps.lock) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG1(ERR, "Cannot create ipftps lock: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
goto leave;
|
|
}
|
|
ipftps.databuf_size = ipftps_sysget_int("ipftps.databuf_size", 4096, 1);
|
|
|
|
|
|
if (ipcom_ipc_install() != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG1(ERR, "Cannot install IPC service: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
goto leave;
|
|
}
|
|
|
|
if (ipcom_ipd_init("ipftps", IPCOM_SUCCESS, &ipd_fd) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG1(ERR, "Cannot initialize ipd: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
goto leave;
|
|
}
|
|
|
|
ipd_init = 1;
|
|
ipcom_proc_attr_init(&attr);
|
|
attr.stacksize = IPCOM_PROC_STACK_LARGE;
|
|
attr.priority = IPFTPS_SESSION_PROC_PRIO;
|
|
#ifdef IPFTP_TLS
|
|
sysvar = ipcom_sysvar_get("ipftps.tls.enable", IP_NULL, IP_NULL);
|
|
if (sysvar == IP_NULL)
|
|
goto leave;
|
|
if(ipcom_atoi(sysvar) == 1)
|
|
attr.flags |= IPCOM_PROC_FLAG_FP;
|
|
if (sysvar != IP_NULL)
|
|
ipcom_free(sysvar);
|
|
#endif
|
|
|
|
reconfigure:
|
|
port_number = (Ip_u16)ipftps_sysget_int("ipftps.port_number", 21, 1);
|
|
|
|
if (ipftps_socketpair(IP_AF_UNSPEC) != 0)
|
|
goto leave;
|
|
|
|
#ifdef IPCOM_USE_INET
|
|
/* Get IPv4 socket */
|
|
ipftps.sock4.fd = ipcom_socket(IP_AF_INET, IP_SOCK_STREAM, IPFTPS_PROTO);
|
|
if (ipftps.sock4.fd == IP_INVALID_SOCKET)
|
|
{
|
|
IPCOM_LOG1(ERR, "Cannot allocate IPv4 server socket: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
goto done4;
|
|
}
|
|
|
|
option = 1;
|
|
if (ipcom_setsockopt(ipftps.sock4.fd, IP_SOL_SOCKET, IP_SO_REUSEADDR,
|
|
&option, sizeof(option)) < 0)
|
|
{
|
|
IPCOM_LOG1(WARNING, "Cannot reuse IPv4 server socket address: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
}
|
|
|
|
/* Set IPv4 defaults */
|
|
IPCOM_SA_LEN_SET(&ipftps.sock4.laddr.sin, sizeof(struct Ip_sockaddr_in));
|
|
ipftps.sock4.laddr.sin.sin_family = IP_AF_INET;
|
|
ipftps.sock4.laddr.sin.sin_addr.s_addr = ip_htonl(IP_INADDR_ANY);
|
|
ipftps.sock4.laddr.sin.sin_port = ip_htons(port_number);
|
|
|
|
/* Get local IPv4 bind address */
|
|
bind_size = sizeof(bind_str);
|
|
if (ipcom_sysvar_get("ipftps.local_ip4", bind_str, &bind_size) != IP_NULL)
|
|
{
|
|
if (ipcom_inet_pton(IP_AF_INET, bind_str,
|
|
&ipftps.sock4.laddr.sin.sin_addr) != 1)
|
|
{
|
|
IPCOM_LOG2(ERR, "Cannot convert IPv4 local address %s: %s",
|
|
bind_str, ipcom_strerror(ipcom_errno));
|
|
goto fail4;
|
|
}
|
|
}
|
|
|
|
/* Bind to FTP port */
|
|
if (ipcom_bind(ipftps.sock4.fd, &ipftps.sock4.laddr.sa,
|
|
sizeof(ipftps.sock4.laddr.sin)) < 0)
|
|
{
|
|
IPCOM_LOG1(ERR, "Cannot bind IPv4 server socket: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
goto fail4;
|
|
}
|
|
|
|
/* Enter listen state. */
|
|
if (ipcom_listen(ipftps.sock4.fd, ipcom_sysvar_get_as_int("ipftps.socket_backlog", 0)) != 0)
|
|
{
|
|
IPCOM_LOG1(ERR, "Cannot listen on IPv4 server socket: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
fail4:
|
|
(void)ipcom_socketclose(ipftps.sock4.fd);
|
|
ipftps.sock4.fd = IP_INVALID_SOCKET;
|
|
}
|
|
|
|
done4:
|
|
#endif /* IPCOM_USE_INET */
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
/* Get IPv6 socket */
|
|
ipftps.sock6.fd = ipcom_socket(IP_AF_INET6, IP_SOCK_STREAM, IPFTPS_PROTO);
|
|
if (ipftps.sock6.fd == IP_INVALID_SOCKET)
|
|
{
|
|
IPCOM_LOG1(ERR, "Cannot allocate IPv6 server socket: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
goto done6;
|
|
}
|
|
|
|
option = 1;
|
|
if (ipcom_setsockopt(ipftps.sock6.fd, IP_SOL_SOCKET, IP_SO_REUSEADDR,
|
|
&option, sizeof(option)) < 0)
|
|
{
|
|
IPCOM_LOG1(WARNING, "Cannot reuse IPv6 server socket address: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
}
|
|
|
|
/* Set IPv6 defaults */
|
|
ipcom_memset(&ipftps.sock6.laddr.sin6, 0, sizeof(ipftps.sock6.laddr.sin6));
|
|
IPCOM_SA_LEN_SET(&ipftps.sock6.laddr.sin6, sizeof(struct Ip_sockaddr_in6));
|
|
ipftps.sock6.laddr.sin6.sin6_family = IP_AF_INET6;
|
|
/* ipftps.sock6.laddr.sin6.sin6_addr already set to 'any' with memset() */
|
|
ipftps.sock6.laddr.sin6.sin6_port = ip_htons(port_number);
|
|
|
|
/* Get local IPv6 bind address */
|
|
bind_size = sizeof(bind_str);
|
|
if (ipcom_sysvar_get("ipftps.local_ip6", bind_str, &bind_size) != IP_NULL)
|
|
{
|
|
if (ipcom_inet_pton(IP_AF_INET, bind_str,
|
|
&ipftps.sock6.laddr.sin6.sin6_addr) != 1)
|
|
{
|
|
IPCOM_LOG2(ERR, "Cannot convert IPv6 local address %s: %s",
|
|
bind_str, ipcom_strerror(ipcom_errno));
|
|
goto fail6;
|
|
}
|
|
}
|
|
|
|
/* Bind to FTP port */
|
|
if (ipcom_bind(ipftps.sock6.fd, &ipftps.sock6.laddr.sa,
|
|
sizeof(ipftps.sock6.laddr.sin6)) < 0)
|
|
{
|
|
IPCOM_LOG1(ERR, "Cannot bind IPv6 server socket: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
goto fail6;
|
|
}
|
|
|
|
/* Enter listen state. */
|
|
if (ipcom_listen(ipftps.sock6.fd, ipcom_sysvar_get_as_int("ipftps.socket_backlog", 0)) != 0)
|
|
{
|
|
IPCOM_LOG1(ERR, "Cannot listen on IPv6 server socket: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
fail6:
|
|
(void)ipcom_socketclose(ipftps.sock6.fd);
|
|
ipftps.sock6.fd = IP_INVALID_SOCKET;
|
|
}
|
|
|
|
done6:
|
|
#endif /* IPCOM_USE_INET6 */
|
|
|
|
for (;;)
|
|
{
|
|
char buf[IPFTPS_IP_MAXLEN];
|
|
Ipftps_sockcb *sock;
|
|
Ip_fd fd;
|
|
Ip_fd_set set;
|
|
Ip_socklen_t len;
|
|
int num;
|
|
int width;
|
|
|
|
session = IP_NULL;
|
|
|
|
IP_FD_ZERO(&set);
|
|
IP_FD_SET(ipd_fd, &set);
|
|
width = (int)ipd_fd;
|
|
|
|
#ifdef IPCOM_USE_INET
|
|
if (ipftps.sock4.fd != IP_INVALID_SOCKET)
|
|
{
|
|
IP_FD_SET(ipftps.sock4.fd, &set);
|
|
width = IP_MAX(width, ipftps.sock4.fd);
|
|
}
|
|
#endif
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
if (ipftps.sock6.fd != IP_INVALID_SOCKET)
|
|
{
|
|
IP_FD_SET(ipftps.sock6.fd, &set);
|
|
width = IP_MAX(width, ipftps.sock6.fd);
|
|
}
|
|
#endif
|
|
|
|
num = ipcom_socketselect(width + 1, &set, IP_NULL, IP_NULL, IP_NULL);
|
|
if (num < 0)
|
|
{
|
|
IPCOM_LOG1(ERR, "Cannot select on server sockets: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
goto leave;
|
|
}
|
|
else if (num == 0)
|
|
{
|
|
/* Ill-behaved stack */
|
|
continue;
|
|
}
|
|
else if (IP_FD_ISSET(ipd_fd, &set))
|
|
{
|
|
int event;
|
|
|
|
/* IPD message */
|
|
event = ipcom_ipd_input(ipd_fd);
|
|
switch(event)
|
|
{
|
|
case IPCOM_IPD_EVENT_RECONFIGURE:
|
|
IPCOM_LOG0(INFO, "Reconfiguring due to IPD request");
|
|
#ifdef IPFTP_TLS
|
|
ipftps_free_tls();
|
|
#endif
|
|
#ifdef IPCOM_USE_INET
|
|
if (ipftps.sock4.fd != IP_INVALID_SOCKET)
|
|
ipcom_socketclose(ipftps.sock4.fd);
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
if (ipftps.sock6.fd != IP_INVALID_SOCKET)
|
|
ipcom_socketclose(ipftps.sock6.fd);
|
|
#endif
|
|
|
|
ipftps_terminate_sessions();
|
|
goto reconfigure;
|
|
|
|
case IPCOM_IPD_EVENT_KILL:
|
|
IPCOM_LOG0(INFO, "Aborting due to IPD request");
|
|
ipftps_terminate_sessions();
|
|
goto leave;
|
|
|
|
default:
|
|
/* Ignore, back to main loop */
|
|
continue;
|
|
}
|
|
}
|
|
#ifdef IPCOM_USE_INET
|
|
else if (ipftps.sock4.fd != IP_INVALID_SOCKET
|
|
&& IP_FD_ISSET(ipftps.sock4.fd, &set))
|
|
{
|
|
sock = &ipftps.sock4;
|
|
}
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
else if (ipftps.sock6.fd != IP_INVALID_SOCKET
|
|
&& IP_FD_ISSET(ipftps.sock6.fd, &set))
|
|
{
|
|
sock = &ipftps.sock6;
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
IPCOM_LOG1(ERR, "Cannot select on server sockets: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
goto leave;
|
|
}
|
|
|
|
/* Wait for connections */
|
|
len = sizeof(sock->paddr);
|
|
fd = ipcom_accept(sock->fd, &sock->paddr.sa, &len);
|
|
if (fd < 0)
|
|
{
|
|
IPCOM_LOG2(ERR, "ipcom_accept() failed : %s [%d]",
|
|
ipcom_strerror(ipcom_errno), ipcom_errno);
|
|
continue;
|
|
}
|
|
|
|
(void)ipftps_ntop(&sock->paddr, buf, sizeof(buf));
|
|
IPCOM_LOG2(INFO, "%s connection from %s",
|
|
(sock->paddr.sa.sa_family == IP_AF_INET ? "IPv4" : "IPv6"),
|
|
buf);
|
|
|
|
/* Get the number of sessions */
|
|
ipcom_mutex_lock(ipftps.lock);
|
|
num = ipftps.session_number;
|
|
ipcom_mutex_unlock(ipftps.lock);
|
|
|
|
#define IPFTPS_REPLY(x,y) \
|
|
do \
|
|
{ \
|
|
char *_msg = y; \
|
|
(void)ipcom_socketwrite(x, _msg, ipcom_strlen(_msg)); \
|
|
} \
|
|
while (0)
|
|
|
|
if (num >= ipftps_sysget_int("ipftps.max_sessions", 8, 1))
|
|
{
|
|
IPCOM_LOG0(WARNING, "Session limit is reached");
|
|
IPFTPS_REPLY(fd, "421 Session limit is reached"CL);
|
|
}
|
|
else if ((session = ipcom_ipc_malloc(sizeof(*session))) == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR, "Out of memory");
|
|
IPFTPS_REPLY(fd, "421 Out of memory"CL);
|
|
}
|
|
else
|
|
{
|
|
len = sizeof(sock->laddr);
|
|
if (ipcom_getsockname(fd, &sock->laddr.sa, &len) != 0)
|
|
{
|
|
IPCOM_LOG0(ERR, "Cannot get server IP address");
|
|
IPFTPS_REPLY(fd, "421 Missing server IP address"CL);
|
|
}
|
|
else
|
|
{
|
|
void *ignore = IP_NULL;
|
|
Ipcom_ipc ipc;
|
|
|
|
ipcom_memset(&ipc, 0, sizeof(ipc));
|
|
|
|
ipcom_memset(session, 0, sizeof(*session));
|
|
ipcom_memcpy(&session->csock, sock, sizeof(session->csock));
|
|
session->csock.fd = fd;
|
|
|
|
/* Increment number of sessions. */
|
|
ipcom_mutex_lock(ipftps.lock);
|
|
ipftps.session_number++;
|
|
ipcom_sprintf(session->name, "ipftps_%lu", ipftps.session_number);
|
|
ipcom_mutex_unlock(ipftps.lock);
|
|
|
|
/* Spawn session handler */
|
|
if (ipcom_proc_acreate(session->name, ipftps_session,
|
|
&attr, &pid) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG1(ERR, "Cannot spawn session handler: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
IPFTPS_REPLY(fd, "421 No session handler available"CL);
|
|
/* Decrement number of sessions. */
|
|
ipcom_mutex_lock(ipftps.lock);
|
|
ipftps.session_number--;
|
|
ipcom_mutex_unlock(ipftps.lock);
|
|
}
|
|
else if (ipcom_ipc_receive(&ipc, &ignore, -1) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG1(ERR, "Cannot receive from session handler: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
IPFTPS_REPLY(fd, "421 Session handler setup failure"CL);
|
|
/* Decrement number of sessions. */
|
|
ipcom_mutex_lock(ipftps.lock);
|
|
ipftps.session_number--;
|
|
ipcom_mutex_unlock(ipftps.lock);
|
|
}
|
|
else
|
|
{
|
|
ipcom_ipc_free(ignore);
|
|
|
|
if (ipcom_ipc_send(&ipc, session) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG1(ERR, "Cannot send to session handler: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
IPFTPS_REPLY(fd, "421 Session handler setup failure"CL);
|
|
/* Decrement number of sessions. */
|
|
ipcom_mutex_lock(ipftps.lock);
|
|
ipftps.session_number--;
|
|
ipcom_mutex_unlock(ipftps.lock);
|
|
ipcom_ipc_free(session);
|
|
session = IP_NULL;
|
|
}
|
|
#ifdef IP_PORT_OSE5
|
|
/* OSE5 has process specific sockets -> donate child fd */
|
|
else if ((Ip_err)efs_donate_fd(fd, pid) != 0)
|
|
{
|
|
IPCOM_LOG0(ERR, "Cannot donate socket descriptor");
|
|
IPFTPS_REPLY(fd, "421 Session handler setup failure"CL);
|
|
}
|
|
#endif /* IP_PORT_OSE5 */
|
|
else
|
|
{
|
|
/* Ok, skip cleanup */
|
|
(void)ipcom_ipc_close(&ipc);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (ipcom_ipc_isopen(&ipc))
|
|
{
|
|
(void)ipcom_ipc_close(&ipc);
|
|
}
|
|
}
|
|
|
|
if(session)
|
|
ipcom_ipc_free(session);
|
|
}
|
|
|
|
(void)ipcom_socketclose(fd);
|
|
}
|
|
|
|
leave:
|
|
|
|
#ifdef IPFTP_TLS
|
|
ipftps_free_tls();
|
|
#endif
|
|
|
|
#ifdef IPCOM_USE_INET
|
|
if (ipftps.sock4.fd != IP_INVALID_SOCKET)
|
|
ipcom_socketclose(ipftps.sock4.fd);
|
|
#endif
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
if (ipftps.sock6.fd != IP_INVALID_SOCKET)
|
|
ipcom_socketclose(ipftps.sock6.fd);
|
|
#endif
|
|
|
|
if (ipftps.pipe_fd[0] != IP_INVALID_SOCKET)
|
|
(void)ipcom_socketclose(ipftps.pipe_fd[0]);
|
|
if (ipftps.pipe_fd[1] != IP_INVALID_SOCKET)
|
|
(void)ipcom_socketclose(ipftps.pipe_fd[1]);
|
|
|
|
/* Have to call ipd init if not done yet */
|
|
if (ipd_init == 0 &&
|
|
ipcom_ipd_init("ipftps", IPCOM_ERR_FAILED, &ipd_fd) == IPCOM_SUCCESS)
|
|
{
|
|
ipd_init = 1;
|
|
}
|
|
if (ipd_init != 0)
|
|
{
|
|
(void)ipcom_ipd_exit("ipftps", ipd_fd);
|
|
}
|
|
|
|
(void)ipcom_ipc_uninstall();
|
|
ipcom_proc_exit();
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_create
|
|
*===========================================================================
|
|
* Description: Create FTP daemon.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC Ip_err
|
|
ipftps_create(void)
|
|
{
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_start
|
|
*===========================================================================
|
|
* Description: Start FTP daemon.
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_PUBLIC Ip_err
|
|
ipftps_start(void)
|
|
{
|
|
/* Spawn new daemon process */
|
|
Ipcom_proc_attr attr;
|
|
ipcom_proc_attr_init(&attr);
|
|
attr.priority = IPFTPS_MAIN_PROC_PRIO;
|
|
if (ipcom_proc_acreate("ipftps", ipftps_main, &attr, IP_NULL))
|
|
{
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_wildcard_check
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* Check wild cards in path.
|
|
*
|
|
* Parameters:
|
|
* path File path.
|
|
* st Stat struct.
|
|
*
|
|
* Returns: Pointer to pattern buffer if wild cards are found.
|
|
* IP_NULL otherwise. Allocated buffer should be
|
|
* removed by calling function.
|
|
*/
|
|
IP_STATIC char*
|
|
ipftps_wildcard_check(char *path, struct Ip_stat *st)
|
|
{
|
|
char* pat = IP_NULL;
|
|
char* pattern = path;
|
|
char rootdir = '\0';
|
|
|
|
if (path == IP_NULL) goto exit;
|
|
|
|
pattern = ipcom_strrchr(path, '/');
|
|
if(pattern == IP_NULL) ipcom_strrchr(path, '\\');
|
|
|
|
if (pattern != IP_NULL)
|
|
{
|
|
/* Check for wild cards */
|
|
if ((ipcom_strchr(pattern, '*') != 0) || (ipcom_strchr(pattern, '?') != 0))
|
|
{
|
|
if(path == pattern)
|
|
{
|
|
/* Could be a root dir */
|
|
rootdir = *pattern;
|
|
}
|
|
*(pattern++) = '\0';
|
|
pat = ipcom_malloc(ipcom_strlen(pattern) + 1);
|
|
|
|
if (pat == IP_NULL) goto exit;
|
|
ipcom_strcpy(pat, pattern);
|
|
|
|
if(rootdir != '\0')
|
|
{
|
|
path[0] = rootdir;
|
|
path[1] = '\0';
|
|
}
|
|
|
|
/* Validate the dir path.*/
|
|
if (ipcom_stat(path, st) != 0)
|
|
{
|
|
ipcom_free(pat);
|
|
pat = IP_NULL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* We might have file or dir. Check for wild cards */
|
|
if ((ipcom_strchr(path, '*') != 0) || (ipcom_strchr(path, '?') != 0))
|
|
{
|
|
pat = ipcom_malloc(ipcom_strlen(path) + 1);
|
|
if (pat == IP_NULL) goto exit;
|
|
ipcom_strcpy(pat, path);
|
|
/* Set the path to current dir */
|
|
ipcom_strcpy(path, ".");
|
|
/* Validate the dir path */
|
|
if (ipcom_stat(path, st) != 0)
|
|
{
|
|
ipcom_free(pat);
|
|
pat = IP_NULL;
|
|
}
|
|
}
|
|
}
|
|
exit:
|
|
return pat;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_pattern_check
|
|
*===========================================================================
|
|
*
|
|
* Description:
|
|
* Compare file name pattern with actual file name.
|
|
*
|
|
* Parameters:
|
|
* pattern File name pattern with wild cards.
|
|
* name File name.
|
|
*
|
|
* Returns: 1 if pattern and file match. 0 otherwise.
|
|
*/
|
|
IP_STATIC int ipftps_pattern_check (char * pattern, char * name)
|
|
{
|
|
char * pat, *next, *pre = IP_NULL;
|
|
const char anyone = '?' ;
|
|
const char anymany = '*' ;
|
|
const char *anymanystr = "*";
|
|
|
|
pat = pattern;
|
|
if ((ipcom_strcmp(pat, anymanystr) == 0) && (name[0] != '.'))
|
|
return 1;
|
|
|
|
for (next = name ; * next != '\0' ; pre = next ++)
|
|
{
|
|
if (*pat == anyone)
|
|
{
|
|
pat ++ ;
|
|
}
|
|
else if (*pat == anymany)
|
|
{
|
|
if ((next[0] == '.') && pre == IP_NULL)
|
|
return 0 ;
|
|
if (ipcom_toupper((int)pat[1]) == ipcom_toupper((int)next[1]))
|
|
pat ++ ;
|
|
else if (ipcom_toupper((int)pat[1]) == ipcom_toupper((int)next[0]))
|
|
pat += 2 ;
|
|
else
|
|
continue ;
|
|
}
|
|
else if (ipcom_toupper((int)*pat) != ipcom_toupper((int)*next))
|
|
{
|
|
return 0 ;
|
|
}
|
|
else
|
|
{
|
|
pat ++ ;
|
|
}
|
|
}
|
|
|
|
if ((*pat == '\0') || (pat[0] == anymany && pat[1] == '\0'))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipftps_version
|
|
*===========================================================================
|
|
* Description::
|
|
* Parameters::
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC const char *
|
|
ipftps_version(void)
|
|
{
|
|
return "@(#) IPFTPS $Name: VXWORKS_ITER40_RCPL8_2024030303 $ - INTERPEAK_COPYRIGHT_STRING";
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|