c/c++ 实现FTP+SSL/TLS FTPS客户端(隐式+被动)和sftp客户端

写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

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值