写FTP+SSL/TLS(FTPS)比SFTP客户端要麻烦点,虽然有ftplibpp这种优秀的库可以用,但是我发现ftplibpp在对有些FTPS服务端支持的并不是特别好,比如说FTPS+隐式链接时,会出现一些问题,本来想自己改一下ftplibpp但是发现改起来并非容易,索性自己重新了一个ftps客户端。
目前实现了隐式+被动的方式ftps客户端以及sftp客户端。sftp就比较方便libssh2库直接有函数支持非常方便。
注:每台服务器的ftp协议可能会有细微差别,需要自行调整。
l
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <iconv.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <netinet/in.h>
#include "libssh2.h"
#include "libssh2_publickey.h"
#include "libssh2_sftp.h"
//settings
#define IMPLICIT_CONN 0
#define EXPLICIT_CONN 1
//ftp
#define FEAT_CMD "FEAT\r\n"
#define TYPE_A_CMD "TYPE A\r\n"
#define TYPE_I_CMD "TYPE I\r\n"
#define PASV_CMD "PASV\r\n"
#define PROT_P_CMD "PROT P\r\n"
#define PBSZ_0_CMD "PBSZ 0\r\n"
#define OPTS_MLST "OPTS MLST Size;Modify;Type;Perm;\r\n"
#define SET_UTF8_CMD "OPTS UTF8 ON\r\n"
#define ABOR_CMD "ABOR\r\n"
#define SYST_CMD "SYST\r\n"
#define AUTH_SSL_CMD "AUTH SSL\r\n"
#define AUTH_TLS_CMD "AUTH_TLS\r\n"
//socket
#define SETSOCKOPT_OPTVAL_TYPE (void *)
//ssl/tls
#define _FTPLIB_SSL_CLIENT_METHOD_ TLSv1_2_client_method
typedef struct ssl_st SSL;
typedef struct ssl_ctx_st SSL_CTX;
typedef struct bio_st BIO;
typedef struct x509_st X509;
//typedef bool (*FtpCallbackCert)(void *arg, X509 *cert);
//ftp cmd codemapping
#define DOWNLOAD 0
#define UPLOAD 1
#define LS_AL 2
typedef struct _ftps_client_{
SSL* ssl;
SSL_CTX* ctx;
BIO* sbio;
int connfd;
int mode;/** pasv or port**/
char ip[200];
X509 *cert;
}FtpsClient;
struct _sftp_client_
{
LIBSSH2_SESSION* session;
LIBSSH2_SFTP* sftp_session;
LIBSSH2_SFTP_HANDLE* sftp_handle;
char curdir[2048];
int sockfd;
}SftpClient;
/**============================static function declaration==============================================**/
static int SetConnType(FtpsClient *cli, const int type);
static int CloseFtpsDataConn(FtpsClient *cli,FtpsClient *datahandle);
/**==================================================public code segment start==================================================**/
/**
测试连接是否可用
**/
int TestConnection(int sock)
{
struct sockaddr_in addr;
socklen_t addr_len = sizeof(addr);
int32_t ret = getsockname(sock, (struct sockaddr *)&addr, &addr_len);
if(ret == 0)
{
printf("getsockname succ:%s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
}
else
{
printf("getsockname failed,error=%d\n", errno);
}
memset(&addr, 0, sizeof(addr));
ret = getpeername(sock, (struct sockaddr *)&addr, &addr_len);
if(ret == 0)
{
printf("getpeername succ:%s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
}
else
{
printf("getpeername failed,error=%d\n", errno);
return -1;
}
return 0;
}
/*
函数:打开socket 客户端连接,默认与服务端连接超时5秒
*/
int OpenConnectByIp(const char* ip, const char* port)
{
struct sockaddr_in sa;
// fd_set rSet;
int connfd = 0;
// int arg = 0;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr(ip);
sa.sin_port = htons(atoi(port));
connfd = socket(AF_INET, SOCK_STREAM, 0);
if (connfd < 0) {
return -1;
}
/*
struct timeval timeo = { 5, 0 };
arg = fcntl(connfd, F_GETFL, NULL);
arg |= O_NONBLOCK;
fcntl(connfd, F_SETFL, arg);
*/
int r = connect(connfd, (struct sockaddr*)&sa, sizeof(sa));
if (r) {
perror("sock connect:");
fprintf(stderr, "connect err retcode:%d", r);
/*
FD_ZERO(&rSet);
FD_SET(connfd, &rSet);
if (select(connfd + 1, NULL, &rSet, NULL, &timeo) <= 0)
{
close(connfd);
}
*/
close(connfd);
return -1;
}
return connfd;
}
/**
ip:port或者server:port的方式进行连接
**/
int OpenConnectByHost(FtpsClient *cli, const char *host)
{
struct sockaddr_in sin;
struct linger lng = { 0, 0 };
struct hostent *phe;
struct servent *pse;
int sControl = 0;
int on=1;
int ret;
char *lhost;
char *pnum;
memset(&sin,0,sizeof(sin));
sin.sin_family = AF_INET;
lhost = strdup(host);
pnum = strchr(lhost,':');
if (pnum == NULL)
{
if ((pse = getservbyname("ftp","tcp")) == NULL)
{
perror("getservbyname");
free(lhost);
return 0;
}
sin.sin_port = pse->s_port;
}
else
{
*pnum++ = '\0';
if (isdigit(*pnum)) sin.sin_port = htons(atoi(pnum));
else
{
pse = getservbyname(pnum,"tcp");
sin.sin_port = pse->s_port;
}
}
printf("getservbyname success\n");
ret = inet_aton(lhost, &sin.sin_addr);
if (ret == 0){
if ((phe = gethostbyname(lhost)) == NULL)
{
perror("gethostbyname");
free(lhost);
return -1;
}
memcpy((char *)&sin.sin_addr, phe->h_addr, phe->h_length);
snprintf(cli->ip,sizeof(cli->ip),"%s",inet_ntoa(*(struct in_addr*)phe->h_addr_list[0]) );
}else
snprintf(cli->ip,sizeof(cli->ip),"%s",lhost);
free(lhost);
sControl = socket(AF_INET, SOCK_STREAM, 0);
if (sControl == -1)
{
perror("socket");
return -1;
}
if (setsockopt(sControl,SOL_SOCKET,SO_REUSEADDR, SETSOCKOPT_OPTVAL_TYPE &on, sizeof(on)) == -1)
{
perror("setsockopt");
return -1;
}
if (setsockopt(sControl,SOL_SOCKET,SO_LINGER, SETSOCKOPT_OPTVAL_TYPE &lng,sizeof(lng)) == -1)
{
perror("setsockopt");
close(sControl);
return -1;
}
if (connect(sControl, (struct sockaddr *)&sin, sizeof(sin)) == -1)
{
perror("connect");
return -1;
}
printf("socket connect success,connect fd = %d\n",sControl);
cli->connfd = sControl;
return 0;
}
/**==================================================public code segment end==================================================**/
/**==================================================FTP+SSL/TLS or FTPS code segment start==================================================**/
/**
初始化SSL
**/
int InitSSL(FtpsClient *cli)
{
SSL_library_init();
SSL_load_error_strings();
cli->ctx = SSL_CTX_new(_FTPLIB_SSL_CLIENT_METHOD_());
SSL_CTX_set_verify(cli->ctx, SSL_VERIFY_NONE, NULL);
cli->ssl = SSL_new(cli->ctx);
return 0;
}
/**
SSL握手
**/
int NegoSSL(FtpsClient *cli)
{
int ret = 0;
long iErr = 0;
char *pErr = NULL;
// cli->sbio = BIO_new_socket(cli->connfd, BIO_NOCLOSE);
// SSL_set_bio(cli->ssl,cli->sbio,cli->sbio);
SSL_set_fd(cli->ssl,cli->connfd);
ret = SSL_connect(cli->ssl);
printf("ssl_connect %s ret = %d \n ",ret==1?"success":"failed",ret);
if(ret<=0){
ret = SSL_get_error(cli->ssl, ret);
printf("SSL_get_error return code[%d]\n",ret);
ERR_print_errors_fp(stdout);
iErr = ERR_get_error();
pErr = ERR_error_string(ret, NULL);
printf("pErr=%s\n",pErr);
return -1;
}
cli->cert = SSL_get_peer_certificate(cli->ssl);
return 0;
}
/**
连接成功后的回应,并打印
**/
int PrintResp(FtpsClient *cli)
{
char recvbuf[10240];bzero(recvbuf,sizeof(recvbuf));
SSL_read(cli->ssl, recvbuf, sizeof(recvbuf));
printf("Resp:%s",recvbuf);
return 0;
}
/**
封装了ssl_read
**/
int EnRecv(FtpsClient *cli,char *recvbuf,int recvlen)
{
return SSL_read(cli->ssl, recvbuf, recvlen);
}
/**
封装SSL_write
**/
int EnSend(FtpsClient *cli,const char *sendbuf,int sendlen)
{
int ret = SSL_write(cli->ssl, sendbuf, sendlen);
if(ret <= 0){
printf("SSL_write error");
return -1;
}
return 0;
}
/**
创建ftps的控制链路(不发送AUTH TLS/SSL的指令)
**/
int FtpsConn(FtpsClient *cli,const char *host)
{
int ret = 0;
ret = OpenConnectByHost(cli,host);
if(ret)
return -1;
ret = InitSSL(cli);
if(ret)
return -1;
ret = NegoSSL(cli);
if(ret)
return -1;
return 0;
}
/**
登录,SSL加密登录
**/
int FtpsLogin(FtpsClient *cli,const char *user,const char *pass)
{
char buff[10240];
bzero(buff,sizeof(buff));
snprintf(buff,sizeof(buff),"USER %s\r\n",user);
EnSend(cli,buff,strlen(buff));
PrintResp(cli);
snprintf(buff,sizeof(buff),"PASS %s\r\n",pass);
EnSend(cli,buff,strlen(buff));
PrintResp(cli);
SetConnType(cli,0);
return 0;
}
/**
设置隐式or显式连接
**/
int SetConnType(FtpsClient *cli, const int type)
{
char rbuff[1024];
bzero(rbuff,sizeof(rbuff));
EnSend(cli,FEAT_CMD,strlen(FEAT_CMD));
PrintResp(cli);
EnSend(cli,SYST_CMD,strlen(SYST_CMD));
PrintResp(cli);
/** 设置与FTPS server的交互属性 START **/
EnSend(cli,SET_UTF8_CMD,strlen(SET_UTF8_CMD));
PrintResp(cli);
EnSend(cli,PROT_P_CMD,strlen(PROT_P_CMD));
PrintResp(cli);
EnSend(cli,PBSZ_0_CMD,strlen(PBSZ_0_CMD));
PrintResp(cli);
/** 设置与FTPS server的交互属性 END **/
if(EXPLICIT_CONN == type){
/** 显示连接 发送AUTH SSL/TLS **/
EnSend(cli,AUTH_SSL_CMD,strlen(AUTH_SSL_CMD));
PrintResp(cli);
EnSend(cli,AUTH_TLS_CMD,strlen(AUTH_TLS_CMD));
PrintResp(cli);
}
return 0;
}
/**
host format:"server:port"
**/
FtpsClient* OpenFtpsClient(const char *host)
{
FtpsClient* cli = NULL;
int failed = 0;
cli = (FtpsClient*)malloc(sizeof(FtpsClient));
if(cli == NULL){
perror("FtpsClient control handle malloc err\n");
return NULL;
}
failed = FtpsConn(cli,host);
if(failed){
return NULL;
}
PrintResp(cli);
return cli;
}
/**
开启被动连接时socket握手
**/
int FtpsOpenPasvSockConn(FtpsClient *cli,FtpsClient *datahandle)
{
int ret = 0;
char host[500];
int port = 0;
char *cp,buff[1024],str1[100],str2[100],str3[100];
int v[7];
/** 被动模式需要另外打开一条数据链路 **/
do{
bzero(buff,sizeof(buff));
bzero(v,sizeof(v));
EnSend(cli,PASV_CMD,strlen(PASV_CMD));/** 查询被动端口 **/
// PrintResp(cli);
EnRecv(cli,buff,sizeof(buff));
printf("PASV port :%s",buff);
cp = strchr(buff,'(');
if (cp == NULL) {
printf("PASV return may some error");
return -1;
}
cp++;
sscanf(buff,"%d %s %s %s (%d,%d,%d,%d,%d,%d).",&v[0],str1,str2,str3,&v[1],&v[2],&v[3],&v[4],&v[5],&v[6]);
printf("server ip=%s\n",cli->ip);
port=v[5]*256+v[6];
printf("port=[%d] v[5]=%d,v[6]=%d\n",port,v[5],v[6]);
snprintf(host,sizeof(host),"%s:%d",cli->ip,port);
ret = OpenConnectByHost(datahandle,host);
if(ret < 0)
printf("Host:%s data sock connect failed,To retry another port...\n",host);
else
break;
sleep(5);
}while(ret < 0);
// TestConnection(datahandle->connfd);
return 0;
}
/**
开启时被动连接时的SSL握手
**/
int FtpsOpenPasvSslConn(FtpsClient *cli,FtpsClient *datahandle)
{
int success = 0;
int failed = 0;
char *perr = NULL;
datahandle->ssl = SSL_new(cli->ctx);
if(datahandle->ssl == NULL){
printf("pdatacli->ssl err\n");
close(datahandle->connfd);
return -1;
}
SSL_set_fd(datahandle->ssl,datahandle->connfd);
success = SSL_connect(datahandle->ssl);
printf(" ssl_connect %s success = %d \n ",success==1?"success":"failed",success);
if (success != 1){
failed = SSL_get_error(datahandle->ssl, success);
perr = ERR_error_string(failed, NULL);
printf("ssl connect err:[%s] errcode:[%d]\n",perr,failed);
return -1;
}
return 0;
}
/**
开启被动模式的数据连接
**/
int FtpsOpenPasvDataConn(FtpsClient *cli,FtpsClient *datahandle,const char *cmd)
{
if(cli == NULL || datahandle == NULL || cmd == NULL){
printf("cli or datahandle or cmd is null pointer");
return -1;
}
int failed = 0;
char buff[1024];
bzero(buff,sizeof(buff));
failed = FtpsOpenPasvSockConn(cli,datahandle);
if(failed){
return -1;
}
EnSend(cli,TYPE_I_CMD,strlen(TYPE_I_CMD));
PrintResp(cli);
EnSend(cli,cmd,strlen(cmd));/** 只有发送带数据流的命令才能SSL连接 **/
EnRecv(cli,buff,sizeof(buff));
printf("%s\n",buff);
if(buff[0] == '5'){
printf("Failed to execute %s \n",cmd);
printf("%s\n",buff);
close(datahandle->connfd);
return -1;
}
failed = FtpsOpenPasvSslConn(cli,datahandle);
if(failed){
close(datahandle->connfd);
return -1;
}
return 0;
}
int FtpsAccessDataByPasvMode(FtpsClient *cli,const char *localpath,const char *cmd)
{
if(cli == NULL || cmd ==NULL)
return -1;
if(localpath == NULL){
localpath = "./temp.record";
}
int failed = 0,len = 0;
FtpsClient *datahandle = NULL;
FILE *fp = NULL;
char buff[10240];
bzero(buff,sizeof(buff));
datahandle = (FtpsClient *)malloc(sizeof(FtpsClient));
if(datahandle == NULL){
perror("datahandle malloc err:");
return -1;
}
bzero(datahandle,sizeof(FtpsClient));
fp = fopen(localpath,"wb+");
if(fp == NULL){
perror("To open local path error when download file:");
return -1;
}
failed = FtpsOpenPasvDataConn(cli,datahandle,cmd);
if(failed){
printf("Ftps Open Pasv Data Connection error\n");
return -1;
}
while( (len = SSL_read(datahandle->ssl,buff,sizeof(buff))) > 0){
if (fwrite(buff, 1, len, fp) <= 0)
{
perror("local path");
break;
}
bzero(buff,sizeof(buff));
}//end while
CloseFtpsDataConn(cli,datahandle);
fclose(fp);
return 0;
}
/**
下载文件
**/
int FtpsGet(FtpsClient *cli,const char *localpath,const char *remotepath)
{
char cmd[10240];
int failed = 0;
char buff[10240];
bzero(cmd,sizeof(cmd));
bzero(buff,sizeof(buff));
snprintf(cmd,sizeof(cmd),"RETR %s\r\n",remotepath);
failed = FtpsAccessDataByPasvMode(cli, localpath, cmd);
if(failed){
printf("Download remote File[%s] failed\n",cmd);
return -1;
}
return 0;
}
/**
获取路径下的文件大小
**/
int FtpsSize(FtpsClient *cli,const char *path, size_t *size)
{
char cmd[256],buff[1024];
size_t resp = 0,sz = 0,rv=0;
bzero(cmd,sizeof(cmd));
bzero(buff,sizeof(buff));
snprintf(cmd,sizeof(cmd), TYPE_A_CMD);
EnSend(cli,cmd,strlen(cmd));
EnRecv(cli,buff,sizeof(buff));
printf("recv = %s\n",buff);
bzero(buff,sizeof(buff));
snprintf(cmd,sizeof(cmd),"SIZE %s\r\n",path);
EnSend(cli,cmd,strlen(cmd));
EnRecv(cli,buff,sizeof(buff));
printf("recv = %s\n",buff);
if (sscanf(buff, "%lu %lu", &resp, &sz) == 2)
*size = sz;
else
rv = -1;
return rv;
}
/**
遍历目录
**/
int FtpsDir(FtpsClient *cli,const char *localpath,const char *remotepath)
{
char cmd[10240];
int failed = 0;
bzero(cmd,sizeof(cmd));
snprintf(cmd,sizeof(cmd),"LIST -aL %s\r\n",remotepath);
failed = FtpsAccessDataByPasvMode(cli, localpath, cmd);
if(failed){
printf("Download remote File[%s] failed\n",cmd);
return -1;
}
return 0;
}
/**
切换目录
**/
int FtpsChdir(FtpsClient *cli,const char *path)
{
if(path == NULL)
return 0;
char buf[256];
int ret = 0;
sprintf(buf,"CWD %s\r\n",path);
ret = EnSend(cli,buf,strlen(buf));
if(ret <0){
return -1;
}
bzero(buf,sizeof(buf));
ret = EnRecv(cli,buf,sizeof(buf));
if(ret <=0){
return -1;
}
printf("%s\n",buf);
if(buf[0] == '5')
return -1;
return 0;
}
int CloseFtpsDataConn(FtpsClient *cli,FtpsClient *datahandle)
{
EnSend(cli,ABOR_CMD,strlen(ABOR_CMD));
PrintResp(cli);
PrintResp(cli);
CloseFtpsClient(datahandle);
return 0;
}
/**
关闭socket及SSL连接
**/
int CloseFtpsClient(FtpsClient *cli)
{
if(cli == NULL)
return 0;
if(cli->ssl != NULL)
SSL_free(cli->ssl);
if(cli->ctx != NULL)
SSL_CTX_free(cli->ctx);
close(cli->connfd);
free(cli);
return 0;
}
/**==================================================FTP+SSL/TLS or FTPS code segment end==================================================**/
/*
函数:打开sftp客户端
*/
SftpClient* OpenSftpClient(const char* svrip, const char* svrport)
{
LIBSSH2_SESSION* session = NULL;
int clientfd = 0;
int failed = 0, ii = 0, finlen = 0;
SftpClient* sclient = NULL;
sclient = (SftpClient*)malloc(sizeof(SftpClient));
if (sclient == NULL) {
fprintf(stderr, "SftpClient malloc err!");
return NULL;
}
bzero(sclient, sizeof(SftpClient));
clientfd = OpenConnectByIp(svrip, svrport);
if (clientfd < 0) {
fprintf(stderr, "OpenConnectByIp err\n");
return NULL;
}
failed = libssh2_init(0);
if (failed) {
fprintf(stderr, "libssh2 initialization failed (%d)\n", failed);
close(clientfd);
return NULL;
}
session = libssh2_session_init();
if (!session)
{
close(clientfd);
return NULL;
}
libssh2_session_set_blocking(session, 1);
while ((failed = libssh2_session_handshake(session, clientfd)) == LIBSSH2_ERROR_EAGAIN);
if (failed) {
fprintf(stderr, "Call libssh2_session_handshake failed,code[%d]\n", failed);
libssh2_session_free(session);
close(clientfd);
return NULL;
}
/* 检查主机指纹 */
const char* fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
finlen = strlen(fingerprint);
for (ii = 0; ii < finlen; ii++) {
fprintf(stderr, "%08x", fingerprint[ii]);
}
fprintf(stderr, "\n");
sclient->session = session;
sclient->sockfd = clientfd;
return sclient;
}
int SftpLogin(SftpClient* sclient, const char* user, const char* pswd)
{
if (sclient == NULL)
return -1;
int failed = -1;
while ((failed = libssh2_userauth_password(sclient->session, user, pswd)) == LIBSSH2_ERROR_EAGAIN);
if (failed) {
fprintf(stderr, "Authentication by password failed user[%s] pswd[%s] libssh2 failed code[%d]\n", user, pswd, failed);
return -1;
}
sclient->sftp_session = libssh2_sftp_init(sclient->session);
if (sclient->sftp_session == NULL) {
fprintf(stderr, "Unable to init SFTP session ,libssh2 failed code[%lu]\n", libssh2_sftp_last_error(sclient->sftp_session));
return -1;
}
return 0;
}
int SftpGet(SftpClient* client, const char* dstlocal, const char* srcremote)
{
if (client == NULL)
return -1;
FILE* fp = NULL;
char buff[1024];
int readbytes = 0;
bzero(buff, sizeof(buff));
client->sftp_handle = libssh2_sftp_open(client->sftp_session, srcremote, LIBSSH2_FXF_READ, 0);
if (client->sftp_handle == NULL) {
fprintf(stderr, "sftp remote path[%s] err! errcode[%lu]\n", srcremote, libssh2_sftp_last_error(client->sftp_session));
return -1;
}
fp = fopen(dstlocal, "wb+");
if (fp == NULL) {
fprintf(stderr, "fopen local path[%s] err!\n", dstlocal);
return -1;
}
do {
readbytes = libssh2_sftp_read(client->sftp_handle, buff, sizeof(buff));
if (!readbytes) {
break;
}
fwrite(buff, 1, readbytes, fp);
bzero(buff, sizeof(buff));
} while (1);
fclose(fp);
return 0;
}
int SftpPut(SftpClient* client, const char* dstremote, const char* srclocal)
{
if (client == NULL)
return -1;
FILE* fp = NULL;
char buff[1024];
int localreadbytes = 0;
int remotewritebytes = 0;
char* curptr = NULL;
bzero(buff, sizeof(buff));
fp = fopen(srclocal, "rb");
if (fp == NULL) {
fprintf(stderr, "fopen src local path[%s] err!", srclocal);
return -1;
}
client->sftp_handle = libssh2_sftp_open(client->sftp_session, dstremote,
LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_TRUNC,
LIBSSH2_SFTP_S_IRUSR | LIBSSH2_SFTP_S_IWUSR |
LIBSSH2_SFTP_S_IRGRP | LIBSSH2_SFTP_S_IROTH);
if (client->sftp_handle == NULL) {
fprintf(stderr, "Open remote path[%s] err! %lu\n", dstremote, libssh2_sftp_last_error(client->sftp_session));
return -1;
}
do {
localreadbytes = fread(buff, 1, sizeof(buff), fp);
if (localreadbytes <= 0) {
break;
}
curptr = buff;
do {
remotewritebytes = libssh2_sftp_write(client->sftp_handle, curptr, localreadbytes);
if (remotewritebytes < 0)
break;
curptr += remotewritebytes;
localreadbytes -= remotewritebytes;
} while (localreadbytes);
} while (remotewritebytes > 0);
return 0;
}
/*
函数:遍历指定目录下的所有文件(包括目录)
*/
int SftpDir(SftpClient* client, const char* path)
{
if (client == NULL)
return -1;
LIBSSH2_SFTP_ATTRIBUTES attrs;
int success = 0;
char entry[1024];
char file[1024];
bzero(entry, sizeof(entry));
bzero(file, sizeof(file));
client->sftp_handle = libssh2_sftp_opendir(client->sftp_session, path);
if (client->sftp_handle == NULL) {
fprintf(stderr, "sftp opendir err!\n");
return -1;
}
/*
entry的数据格式:
drwxr-xr-x 4 sec comstar 31 Mar 24 01:07 .
drwxr-xr-x 4 sec comstar 31 Mar 24 01:07 ..
drwxr-xr-x 12 sec comstar 220 Mar 23 08:57 data
drwxr-xr-x 2 sec comstar 29 Mar 24 02:05 .sftp
file的数据为单个文件(普通文件,目录文件)名
*/
while (1) {
success = libssh2_sftp_readdir_ex(client->sftp_handle, file, sizeof(file), entry, sizeof(entry), &attrs);
if (success) {
if (entry[0] != '\0') {
if (!strcmp(file, ".") || !strcmp(file, "..")) {
continue;
}
if (S_ISREG(attrs.permissions)) {
fprintf(stderr, "file[%s]\n", file);
}
else if (S_ISDIR(attrs.permissions)) {
fprintf(stderr, "dir[%s]\n", file);
}
}
}
else
break;
bzero(entry, sizeof(entry));
bzero(file, sizeof(file));
}//end while 1 ...
if (0 != libssh2_sftp_closedir(client->sftp_handle)) {
fprintf(stderr, "sftp close dir err!\n");
return -1;
}
client->sftp_handle = NULL;
return 0;
}
/*
函数:切换目录
备注:由于libssh2并没有提供类似chdir的功能,或许也可能在某个角落我没有发现,暂时先不做了
*/
int SftpChdir(SftpClient* client, const char* path)
{
if (client == NULL)
return -1;
return 0;
}
int CloseSftpClient(SftpClient* client)
{
if (client == NULL)
return 0;
if (client->sftp_handle != NULL)
libssh2_sftp_close(client->sftp_handle);
if (client->sftp_session != NULL)
libssh2_sftp_shutdown(client->sftp_session);
if (client->sftp_session != NULL)
libssh2_session_disconnect(client->session, "To shutdown sftp client\n");
if (client->sftp_session != NULL)
libssh2_session_free(client->session);
client->session = NULL;
close(client->sockfd);
return 0;
}
#ifdef _SFTP_CLIENT_TEST_
int main()
{
const char* ip = "127.0.0.1";
const char* port = "8092";
char user[20] = "root";
char pswd[50] = "root";
int ret = 0;
SftpClient* sftp = OpenSftpClient(ip, port);
if (sftp == NULL) {
fprintf(stderr, "OpenSftpClient err ip[%s],port[%s] \n", ip, port);
return -1;
}
ret = SftpLogin(sftp, user, pswd);
ret = SftpDir(sftp, "./data_iftp/");
ret = SftpGet(sftp, "/home/fis/guoxf/债券收盘收益率曲线报表_20220307.csv", "/data_iftp/债券收盘收益率曲线报表_20220307.csv");
CloseSftpClient(sftp);
return 0;
}
#elif _FTPS_CLIENT_TEST_
//iconv
#define TO_UTF8 "UTF-8//IGNORE"
#define TO_GB2312 "GB2312//IGNORE"
#define FROM_UTF8 "UTF-8"
#define FROM_GB2312 "GB2312"
#define CMDS_MAX_MSG_GBK_SIZE 16384
#define CMDS_MAX_MSG_UTF8_SIZE 32768
int iconv_gbk_to_utf8(char* inbuf)
{
iconv_t cd;
int ret;
size_t ileft, oleft;
char* fptr;
char* tptr;
int count;
char iconv_tmp_string[CMDS_MAX_MSG_UTF8_SIZE];
cd = iconv_open(TO_UTF8, FROM_GB2312);
if (cd == (iconv_t)-1)
{
fprintf(stdout, "iconv_open failed[%d]\n", errno);
return 0;
}
memset(iconv_tmp_string, 0, CMDS_MAX_MSG_UTF8_SIZE);
ileft = strlen(inbuf);
oleft = CMDS_MAX_MSG_UTF8_SIZE;
tptr = &iconv_tmp_string[0];
fptr = inbuf;
if ((ret = iconv(cd, &fptr, &ileft, &tptr, &oleft)) != 0)
{
fprintf(stdout, "iconv errno code=%d.\n", errno);
fprintf(stdout, "after iconv tmp_string=%s\n", iconv_tmp_string);
//有时候会出现11的错误码,可忽略掉
if (errno != 0 && errno != 2 && errno != 11 && errno != 10 && errno != 84)
{
fprintf(stdout, "iconv error use original gb2312\n");
iconv_close(cd);
return 0;
}
}
iconv_close(cd);
count = strlen(iconv_tmp_string);
memmove(inbuf, iconv_tmp_string, count);
inbuf[count] = '\0';
return 1;
}
int main()
{
const char *host = "127.0.0.1:1250";
const char *user = "root";
const char *pass = "root";
int failed = 0;
size_t size = 0;
char remotepath[20000];
char remotedir[10000];
bzero(remotepath,sizeof(remotepath));
bzero(remotedir,sizeof(remotedir));
snprintf(remotepath,sizeof(remotepath),"/基准数据报表/债券收盘估值/债券收盘估值_20220507.csv");
iconv_gbk_to_utf8(remotepath);
snprintf(remotedir,sizeof(remotedir),"/基准数据报表/债券收盘估值");
iconv_gbk_to_utf8(remotedir);
FtpsClient *cli = NULL;
cli = OpenFtpsClient(host);
if(cli == NULL)
return -1;
failed = FtpsLogin(cli,user,pass);
if(failed)
return -1;
failed = FtpsGet(cli,"./test.csv",remotepath);
if(failed)
return -1;
failed = FtpsDir(cli,NULL,remotedir);
if(failed)
return -1;
FtpsChdir(cli,remotedir);
snprintf(remotepath,sizeof(remotepath),"/基准数据报表/债券收盘估值/债券收盘估值_20220509.csv");
iconv_gbk_to_utf8(remotepath);
failed = FtpsGet(cli,"./test.csv",remotepath);
if(failed)
return -1;
failed = FtpsSize(cli,remotepath, &size);
if(failed)
return -1;
CloseFtpsClient(cli);
return 0;
}
#endif
编译命令:
cc ftpclient.c -lssl -lssh2 -lcrypto -lz -oftpclient -g