前言
本项目是基于linux c ssh、openssl标准库来开发sftp软件通信模块
本项目是对ssh、openssl标准库进行一个高层封装,使得可以在 Linux 上非常容易的执行调用,方便开发者使用,实现sftp文件上传、下载等操作.
一、openssl编译
由于libssh编译时会引用到openssl的libcrypto.a、libssl.a开发库,所以先编译该开源库编译步骤如下所示:
1.模块configure配置
./Configure linux-aarch64 --cross-compile-prefix=/usr/bin/aarch64-linux-gnu- --prefix=/home/data/openssl
2.make & make install
二、libssh2编译
1.模块configure配置
引用openssl编译出来的库,设置库路径
./configure CC=/usr/bin/aarch64-linux-gnu-gcc --prefix=/home/buildlib --host=aarch64-linux-gnu --with-libssl-prefix=/home/data/openssl LDFLAGS="-Wl,-rpath-link,/home/data/openssl/lib64"
2.make & make install
生成库在/home/buildlib下。
三、sftp二次开发模块封装
1.头文件内容如下
#ifndef __SFTP_H__
#define __SFTP_H__
void *SFTP_Init(const char *pDstIp, int iPort, int *pSocket);
void *SFTP_Login(void *session, int iSocket, const char *pUserName, const char *pPassword);
int SFTP_PutFile(void *ssh2Session, void *sftpSession, int iSocket,
const char *pSrcpath, const char *pDstpath);
int SFTP_GetFile(void *ssh2Session, void *sftpSession, int Socket,
const char *pSrcpath, const char *pLocalpath);
void SFTP_SocketClose(int iSocket);
void SFTP_SftpSessionclose(void *sftpSession);
void SFTP_SshSessionClose(void *ssh2Session);
void SFTP_Exit();
#endif
2、函数体
函数体实现如下所示:
#include <stdio.h>
#include "libssh2.h"
#include "libssh2_sftp.h"
#include "libssh2_publickey.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <ctype.h>
#include "sftp.h"
static int waitsocket(int socket_fd, LIBSSH2_SESSION *session)
{
struct timeval timeout;
int rc;
fd_set fd;
fd_set *writefd = NULL;
fd_set *readfd = NULL;
int dir;
timeout.tv_sec = 10;
timeout.tv_usec = 0;
FD_ZERO(&fd);
FD_SET(socket_fd, &fd);
/* now make sure we wait in the correct direction */
dir = libssh2_session_block_directions(session);
if(dir & LIBSSH2_SESSION_BLOCK_INBOUND)
readfd = &fd;
if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND)
writefd = &fd;
rc = select(socket_fd + 1, readfd, writefd, NULL, &timeout);
return rc;
}
void *SFTP_Init(const char *pDstIp, int iPort, int *pSocket)
{
int iRet = -1;
int iSocketFd = -1;
int iErr = -1;
fd_set writeSet, exceptSet;
struct timeval wait;
int iSocketError;
socklen_t errorLen = sizeof(iSocketError);
const char *fingerprint;
struct sockaddr_in sin;
LIBSSH2_SESSION *session;
*pSocket = -1;
iRet = libssh2_init(0);
if(iRet != 0) {
printf("[%s]libssh2 initialization failed (%d)\n", __FUNCTION__, iRet);
return NULL;
}
/*
* The application code is responsible for creating the socket
* and establishing the connection
*/
iSocketFd = socket(AF_INET, SOCK_STREAM, 0);
if(iSocketFd < 0)
{
printf("[%s]create socket failed!\n", __FUNCTION__);
return NULL;
}
int flags = fcntl(iSocketFd, F_GETFL, 0);
if(-1 == fcntl(iSocketFd, F_SETFL, flags | O_NONBLOCK))
{
perror("Set socket unblock failed!");
}
sin.sin_family = AF_INET;
sin.sin_port = htons(iPort);
sin.sin_addr.s_addr = inet_addr(pDstIp);
if (connect(iSocketFd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
{
// 返回以下的标识,标识这个连接正在等待服务器accept
if((errno != EINPROGRESS)&&(errno != EWOULDBLOCK))/* connection in progress */
{
close(iSocketFd);
return NULL;
}
FD_ZERO(&writeSet);
FD_SET(iSocketFd, &writeSet);
FD_SET(iSocketFd, &exceptSet);
wait.tv_sec = 3;
wait.tv_usec = 0;
iErr = select(iSocketFd+1, NULL, &writeSet, &exceptSet, &wait);
if (iErr <= 0) //出错
{
close(iSocketFd);
return NULL;
}
if (FD_ISSET(iSocketFd, &writeSet))
{
// 判断socket是否已经连接,如果已经连接iSocketError=0
iErr = getsockopt(iSocketFd, SOL_SOCKET, SO_ERROR, (void *)&iSocketError, &errorLen);
if((iErr == 0)&& (iSocketError == 0))
{
/* connect success! */
}
else
{
close(iSocketFd);
return NULL;
}
}
if (FD_ISSET(iSocketFd, &exceptSet))
{
close(iSocketFd);
return NULL;
}
}
/* Create a session instance
*/
session = libssh2_session_init();
if(!session)
{
printf("[%s]libssh2_session_init failed!\n", __FUNCTION__);
return NULL;
}
/* set non-blocking, tell ~0 blocking 0:non-blocking */
libssh2_session_set_blocking(session, 0);
/* ... start it up. This will trade welcome banners, exchange keys,
* and setup crypto, compression, and MAC layers
*/
while((iRet = libssh2_session_handshake(session, iSocketFd))
== LIBSSH2_ERROR_EAGAIN);
if(iRet) {
libssh2_session_disconnect(session,
"Normal Shutdown, Thank you for playing");
libssh2_session_free(session);
printf("[%s]Failure establishing SSH session: %d\n",__FUNCTION__, iRet);
return NULL;
}
/* At this point we havn't yet authenticated. The first thing to do
* is check the hostkey's fingerprint against our known hosts Your app
* may have it hard coded, may go to a file, may present it to the
* user, that's your call
*/
fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
printf("Fingerprint: ");
int i;
for(i = 0; i < 20; i++) {
printf("%02X ", (unsigned char)fingerprint[i]);
}
printf("\n");
*pSocket = iSocketFd;
return (void *)session;
}
void *SFTP_Login(void *session, int iSocket, const char *pUserName, const char *pPassword)
{
int rc = 0;
int iRet = 0;
LIBSSH2_SFTP *sftp_session;
LIBSSH2_SESSION *sshSession = (LIBSSH2_SESSION *)session;
if(NULL == session)
{
printf("[%s] ssh session is null!\n", __FUNCTION__);
return NULL;
}
while((rc = libssh2_userauth_password(session, pUserName, pPassword))
== LIBSSH2_ERROR_EAGAIN);
if(rc) {
printf("[%s] libssh2_userauth_password failed!\n", __FUNCTION__);
return NULL;
}
do {
sftp_session = libssh2_sftp_init(session);
if(!sftp_session) {
if(libssh2_session_last_errno(session) ==
LIBSSH2_ERROR_EAGAIN) {
printf("non-blocking libssh2_sftp_init now we wait\n");
iRet = waitsocket(iSocket, session); /* now we wait */
if(iRet <= 0)
{
printf("[%s] waitsocket time out\n", __FUNCTION__);
return NULL;
}
}
else {
printf("[%s] Unable to init SFTP session, libssh2_sftp_init failed\n", __FUNCTION__);
return NULL;
}
}
} while(!sftp_session);
return(void *)sftp_session;
}
int SFTP_PutFile(void *ssh2Session, void *sftpSession, int iSocket,
const char *pSrcpath, const char *pDstpath)
{
/**略*/
}
int SFTP_GetFile(void *ssh2Session, void *sftpSession, int iSocket,
const char *pSrcpath, const char *pLocalpath)
{
/**略*/
}
void SFTP_SocketClose(int iSocket)
{
if(iSocket > 0)
close(iSocket);
}
void SFTP_SftpSessionclose(void *sftpSession)
{
LIBSSH2_SFTP *sftp_Session = sftpSession;
if(sftp_Session)
{
libssh2_sftp_shutdown(sftp_Session);
// sftp_Session = NULL;
}
}
void SFTP_SshSessionClose(void *ssh2Session)
{
LIBSSH2_SESSION *ssh2_Session = ssh2Session;
if(ssh2_Session)
{
libssh2_session_disconnect(ssh2_Session,
"Normal Shutdown, Thank you for playing");
libssh2_session_free(ssh2_Session);
ssh2_Session = NULL;
}
}
void SFTP_Exit()
{
libssh2_exit();
}
总结
SFTP_GetFile、SFTP_PutFile接口直接到本人博客资源文件中下载查看即可。
本文主要对libssh开源库进行二次开发,封装成可供直接调用的函数库,并且对原开发库做了以下优化:
1、解决原开源库api接口在网络异常环境中阻塞问题;
2、实现逻辑流程优化,功能api接口函数划分清晰!