tftp.h
/******************************************************************************
* 文 件 名 : tftp.h
* 负 责 人 :
* 创建日期 : 20170717
* 版 本 号 : v1.1
* 文件描述 : tftp
* 其 他 : 无
* 修改日志 : 无
******************************************************************************/
#ifndef _TFTP_H_
#define _TFTP_H_
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#if 0
#endif
#define TFTP_PORT 69
/*opcode definition*/
#define TFTP_OPCODE_RRQ 1
#define TFTP_OPCODE_WRQ 2
#define TFTP_OPCODE_DATA 3
#define TFTP_OPCODE_ACK 4
#define TFTP_OPCODE_ERROR 5
/*mode definition*/
#define TFTP_MODE_NETASCII "netascii"
#define TFTP_MODE_OCTET "octet"
#define TFTP_MODE_MAIL "mail"
#define TFTP_MAX_FILE_SIZE (32 * 1024 * 1024)
#define TFTP_CMD_SIZE 520 /*确保不会溢出*/
#define TFTP_BUF_SIZE 520 /*4字节前缀+512DATA*/
/*TFTP限制最大文件传输字节,0-511表示传输完成*/
#define TFTP_MAX_DATA_SIZE 512
#define TFTP_MIN_HEAD_SIZE 2
#define TFTP_MIN_CMD_SIZE 4
#define TFTP_MIN_DATA_SIZE 4
#define TFTP_MIN_ERROR_SIZE 5
#define TFTP_MIN_ACK_SIZE 4
#define TFTP_DATA_BLOCK_OFFSET 2
#define TFTP_ACK_BLOCK_OFFSET 2
#define TFTP_TIMEOUT_CNT 3
#define TFTP_TIMEOUT 3
#define TFTP_BUF_SIZE_ERR (-11)
#if 0
#endif
#define TFTP_SAFE_FREE(m) \
{ \
if (m != NULL) \
{ \
free (m); \
m = NULL; \
} \
}
/*TFTP功能打印开关*/
extern int gTftpCltDbgSw;
#define TFTP_DBG_PRINT(FORMAT, args...)\
{\
if (gTftpCltDbgSw)\
{\
printf("%s:%d: "FORMAT, __FUNCTION__, __LINE__, ##args);\
}\
}
#define TFTP_FLUSH_PRINT(FORMAT, args...)\
{\
if (gTftpCltDbgSw)\
{\
printf(FORMAT, ##args);\
fflush(stdout);\
}\
}
#define PRINT_TFTP_BUF(bufToPrint,lengthToPrint)\
{\
u_char printch = 0;\
u_int printIndex = 0;\
printf("\r\n%s:%d: pkt=%p, len=%d.", __FUNCTION__, __LINE__,((void *)(bufToPrint)),((int)(lengthToPrint)));\
for (printIndex = 0; printIndex < (int)(lengthToPrint); printIndex++)\
{\
if ((printIndex) % 4 == 0)\
{\
printf(" ");\
}\
if ((printIndex) % 16 == 0)\
{\
printf("\r\n");\
}\
printch = *((u_char *)(bufToPrint) + printIndex);\
printf("%02x ", (unsigned int)printch);\
}\
printf("\r\n\r\n");\
}
#define TFTP_PKT_PRINT(pktToPrint,lengthToPrint)\
{\
if (gTftpCltDbgSw)\
{\
PRINT_TFTP_BUF(pktToPrint,lengthToPrint);\
}\
}
#if 0
#endif
/*外部宏引用*/
#define COM_SET fd_set
#define COM_FD_ISSET FD_ISSET
#define COM_FD_SET FD_SET
#define COM_FD_CLR FD_CLR
#define COM_FD_ZERO FD_ZERO
#define COM_AF_INET AF_INET
#define COM_SOCK_STREAM SOCK_STREAM
#define COM_SOCK_DGRAM SOCK_DGRAM
#define COM_EINPROGRESS EINPROGRESS
#define COM_EALREADY EALREADY
#define COM_SOL_SOCKET SOL_SOCKET
#define COM_SO_LINGER SO_LINGER
#define COM_FIONBIO FIONBIO
#define COM_INADDR_ANY INADDR_ANY
#define COM_SO_SNDBUF SO_SNDBUF
#define COM_EWOULDBLOCK EWOULDBLOCK
#define COM_IPPROTO_TCP IPPROTO_TCP
#define COM_TCP_NODELAY TCP_NODELAY
#define COM_SO_ERROR SO_ERROR
#define COM_O_RDWR O_RDWR
#define COM_OPEN(name,flag,mode) open(name,flag,mode)
#define COM_CLOSE(fd) close(fd)
#define COM_WRITE(fd,buf,nbytes) write(fd,buf,nbytes)
#define COM_READ(fd,buf,maxbytes) read(fd,buf,maxbytes)
#define COM_RECV(fd, buffer, length, flags) recv(fd, buffer, length, flags)
#define COM_SEND(fd, buffer, length, flags) send(fd, buffer, length, flags)
#define COM_SELECT(width,pReadFds,pWriteFds,pExceptFds,pTimeOut) \
select(width,pReadFds,pWriteFds,pExceptFds,pTimeOut)
#define COM_IO_CTL(fd,func,arg) ioctl(fd,func,arg)
#define COM_ERR_NO_GET() errno
#define COM_SOCKET_CREATE(domain,type,protocol) socket(domain,type,protocol)
#define COM_BIND(s,name,namelen) bind(s,name,namelen)
#define COM_LISTEN(s,backlog) listen(s,backlog)
#define COM_ACCEPT(s,addr,addrlen) accept(s,addr,addrlen)
#define COM_CONNECT(s,name,namelen) connect(s,name,namelen)
#define COM_SEND_TO(s,buf,buflen,flag,to,tolen) sendto(s,buf,buflen,flag,to,tolen)
#define COM_RECV_FROM(s,buf,buflen,flag,from,plen) recvfrom(s,buf,buflen,flag,from,plen)
#define COM_GET_SOCK_OPT(s,level,optname,optval,optlen) getsockopt(s,level,optname,optval,optlen)
#define COM_SET_SOCK_OPT(s,level,optname,optval,optlen) setsockopt(s,level,optname,optval,optlen)
#define COM_GET_SOCK_NAME(s,name,namelen) getsockname(s,name,namelen)
#define COM_GET_PEER_NAME(s,name,namelen) getpeername(s,name,namelen)
typedef struct timeval com_timeval;
typedef struct linger com_linger;
typedef struct sockaddr_in com_sockaddr_in;
typedef struct sockaddr com_sockaddr;
typedef socklen_t com_socklen_t;
#ifdef __cplusplus
}
#endif
#endif/*#ifndef _TFTP_H_*/
tftp.c
/******************************************************************************
* 文 件 名 : tftp.c
* 负 责 人 :
* 创建日期 : 20170708
* 版 本 号 : v1.1
* 文件描述 : TFTP模块
* 其 他 : 无
* 修改日志 : 无
******************************************************************************/
#ifdef __cplusplus
extern "C"
{
#endif
#include "tftp.h"
/*TFTP功能打印开关*/
int gTftpCltDbgSw = 1;
/*TFTP数据请求超时时间,单位秒*/
int tftpDataTimeout = TFTP_TIMEOUT;
#if 0
#endif
/**************************************************************************
* 函 数 名 : setTftpCltDbgSw
* 负 责 人 : scyang
* 创建日期 : 201403
* 函数功能 : 调试开关
* 输入参数 :
* 输出参数 :
* 返 回 值 :
* 调用关系 :
* 其 它 :
* 修改记录 :
**************************************************************************/
int setTftpCltDbgSw(int enable)
{
gTftpCltDbgSw = enable;
return 0;
}
/**************************************************************************
* 函 数 名 : tftpGetFileSize
* 负 责 人 : scyang
* 创建日期 : 201403
* 函数功能 : 计算文件大小
* 输入参数 :
* 输出参数 :
* 返 回 值 :
* 调用关系 :
* 其 它 :
* 修改记录 :
**************************************************************************/
int tftpGetFileSize(const char *path)
{
int filesize = 0;
struct stat statbuff;
if (path == NULL)
{
return -1;
}
if (stat (path, &statbuff) < 0)
{
return 0;
}
else
{
filesize = statbuff.st_size;
}
return filesize;
}
#if 0
#endif
/**************************************************************************
* 函 数 名 : tftpSockSafeClose
* 负 责 人 : scyang
* 创建日期 : 201403
* 函数功能 : 关闭SOCKET连接
* 输入参数 : int *pSockfd --- SOCKET描述符
* 输出参数 :
* 返 回 值 : 0 --- 成功;-1 --- 失败;
* 调用关系 :
* 其 它 :
* 修改记录 :
**************************************************************************/
int tftpSockSafeClose(int *pSockfd)
{
if (pSockfd == NULL)
{
TFTP_DBG_PRINT("tftpSockSafeClose para is null.\n");
return -1;
}
if (*pSockfd >= 0)
{
TFTP_DBG_PRINT("Close socket %d connect...\n", *pSockfd);
if (COM_CLOSE(*pSockfd) < 0)
{
TFTP_DBG_PRINT("Close socket error(%d)...\n", COM_ERR_NO_GET());
}
*pSockfd = -1;
}
return 0;
}
/***************************************************************
* 函 数 名 : tftpSocketCreate
* 负 责 人 : scyang
* 创建日期 : 20170720
* 函数功能 : 创建UDP连接
* 输入参数 :
* 输出参数 :
* 返 回 值 :
* 调用关系 :
* 其 它 :
****************************************************************/
int tftpSocketCreate(int *pSockfd)
{
int sockfd = -1;
unsigned long flags = 0;
TFTP_DBG_PRINT("Init create tftp socket ...\n");
if(pSockfd == NULL)
{
TFTP_DBG_PRINT("tftpSocketCreate para is null.\n");
return -1;
}
sockfd = COM_SOCKET_CREATE(COM_AF_INET, COM_SOCK_DGRAM, 0);
if(sockfd < 0)
{
TFTP_DBG_PRINT("create socket error(%d).\n", COM_ERR_NO_GET());
return -1;
}
flags = 1; /*非阻塞模式*/
if(COM_IO_CTL(sockfd, COM_FIONBIO, (void *)(&flags)) < 0)
{
COM_CLOSE(sockfd);
TFTP_DBG_PRINT("ioctl FIONBIO error(%d).\n", COM_ERR_NO_GET());
return -1;
}
*pSockfd = sockfd;
return 0;
}
/***************************************************************
* 函 数 名 : tftpBuildCmd
* 负 责 人 : scyang
* 创建日期 : 20170720
* 函数功能 : 构建读写命令
* 输入参数 :
* 输出参数 :
* 返 回 值 :
* 调用关系 :
* 其 它 :
****************************************************************/
int tftpBuildCmd(unsigned short opCode, char *fileName, char *cmd, int *cmdLen)
{
int offset = 0;
if (fileName == NULL || cmd == NULL || cmdLen == NULL)
{
TFTP_DBG_PRINT("tftpCommand para is null.\n");
return -1;
}
if ((opCode != TFTP_OPCODE_RRQ) && (opCode != TFTP_OPCODE_WRQ))
{
TFTP_DBG_PRINT("tftpCommand op code %u error.\n", opCode);
return -1;
}
if ((*cmdLen) < (TFTP_MIN_CMD_SIZE + strlen(TFTP_MODE_OCTET) + strlen(fileName)))
{
TFTP_DBG_PRINT("Input para cmd length %d error.\n", (*cmdLen));
return -1;
}
/******************************************************************
**
** 2 bytes string 1 byte string 1 byte
** +--------+------------+------+------------+-----+
** | Opcode | Filename | 0 | Mode | 0 |
** +--------+------------+------+------------+-----+
**
** RRQ: opcode = 1
** WRQ: opcode = 2
**
*******************************************************************/
/*OP CODE*/
offset = 0;
*(unsigned short *)(cmd + offset) = htons(opCode);
offset += 2;
/*filename*/
memcpy((cmd + offset), fileName, strlen(fileName));
offset += strlen(fileName);
*(unsigned char *)(cmd + offset) = 0;
offset += 1;
/*set mode*/
memcpy((cmd + offset), TFTP_MODE_OCTET, strlen(TFTP_MODE_OCTET));
offset += strlen(TFTP_MODE_OCTET);
*(unsigned char *)(cmd + offset) = 0;
offset += 1;
*cmdLen = offset;
/*TFTP_PKT_PRINT(cmd, offset);*/
return 0;
}
/***************************************************************
* 函 数 名 : tftpBuildAck
* 负 责 人 : scyang
* 创建日期 : 20170720
* 函数功能 : 构建ACK
* 输入参数 :
* 输出参数 :
* 返 回 值 :
* 调用关系 :
* 其 它 :
****************************************************************/
int tftpBuildAck(unsigned short blockId, char *cmd, int *cmdLen)
{
int offset = 0;
if (cmd == NULL || cmdLen == NULL)
{
TFTP_DBG_PRINT("Input para is null.\n");
return -1;
}
if ((*cmdLen) < TFTP_MIN_CMD_SIZE)
{
TFTP_DBG_PRINT("Input para cmd length %d error.\n", (*cmdLen));
return -1;
}
/***********************************************************
** ACK packet definition
**----------------------------------------------------------
** opcode = 4
**
** 2 bytes 2 bytes
** +--------+------------+
** | Opcode | Block # |
** +--------+------------+
**
***********************************************************/
/*OP CODE*/
offset = 0;
*(unsigned short *)(cmd + offset) = htons(TFTP_OPCODE_ACK);
offset += 2;
*(unsigned short *)(cmd + offset) = htons(blockId);
offset += 2;
*cmdLen = offset;
/*TFTP_FLUSH_PRINT("K");*/
return 0;
}
/***************************************************************
* 函 数 名 : tftpCmdSend
* 负 责 人 : scyang
* 创建日期 : 20170720
* 函数功能 : 发送TFTP命令
* 输入参数 :
* 输出参数 :
* 返 回 值 :
* 调用关系 :
* 其 它 :
****************************************************************/
int tftpCmdSend(int fd, com_sockaddr_in *addr, char *cmd, int cmdLen)
{
char hostIp[32] = {0};
if (fd < 0 || addr == NULL || cmd == NULL)
{
TFTP_DBG_PRINT("Input para is null.\n");
return -1;
}
if (COM_SEND_TO(fd, cmd, cmdLen, 0, (com_sockaddr *)addr, sizeof(com_sockaddr_in)) < 0)
{
inet_ntop(AF_INET, &addr->sin_addr, hostIp, (sizeof(hostIp) - 1));
TFTP_DBG_PRINT("Send msg to %s failed: error(%d).\n", hostIp, COM_ERR_NO_GET());
return -1;
}
return 0;
}
#if 0
#endif
/***************************************************************
* 函 数 名 : tftpParseData
* 负 责 人 : scyang
* 创建日期 : 20170720
* 函数功能 : 解析TFTP数据包
* 输入参数 :
* 输出参数 :
* 返 回 值 :
* 调用关系 :
* 其 它 :
****************************************************************/
int tftpParseData(char *inBuf, int bufLen)
{
int offset = 0;
unsigned short opCode = 0;
unsigned short errCode = 0;
if (inBuf == NULL)
{
TFTP_DBG_PRINT("Input para is null.\n");
return -1;
}
if (bufLen < TFTP_MIN_HEAD_SIZE)
{
TFTP_DBG_PRINT("Input buffer length %d error.\n", bufLen);
return -1;
}
/***********************************************************
** Data package definition
**----------------------------------------------------------
** opcode = 3
**
** 2 bytes 2 bytes n bytes
** +--------+------------+------------+
** | Opcode | Block # | Data |
** +--------+------------+------------+
**
**
************************************************************
** Error package definition
**----------------------------------------------------------
** opcode = 5
**
** 2 bytes 2 bytes string 1 byte
** +--------+------------+------------+------+
** | Opcode | ErrorCode | ErrMsg | 0 |
** +--------+------------+------------+------+
**
**
************************************************************
** ACK packet definition
**----------------------------------------------------------
** opcode = 4
**
** 2 bytes 2 bytes
** +--------+------------+
** | Opcode | Block # |
** +--------+------------+
**
***********************************************************/
/*TFTP_PKT_PRINT(inBuf, (bufLen <= 16 ? bufLen : 16));*/
offset = 0;
opCode = ntohs(*(unsigned short *)inBuf);
offset += 2; /*OP CODE*/
switch (opCode)
{
case TFTP_OPCODE_DATA:
{
if (bufLen < TFTP_MIN_DATA_SIZE)
{
TFTP_DBG_PRINT("Get remote data size %u error.\n", bufLen);
return -1;
}
/*收到的数据包长度固定为512*/
if (bufLen > (TFTP_MAX_DATA_SIZE + TFTP_MIN_DATA_SIZE))
{
TFTP_DBG_PRINT("Get remote data size %u error.\n", bufLen);
return -1;
}
/*TFTP_FLUSH_PRINT("#");*/
return TFTP_OPCODE_DATA;
}
case TFTP_OPCODE_ACK:
{
if (bufLen < TFTP_MIN_ACK_SIZE)
{
TFTP_DBG_PRINT("Get remote ack size %u error.\n", bufLen);
return -1;
}
/*TFTP_FLUSH_PRINT("K");*/
return TFTP_OPCODE_ACK;
}
case TFTP_OPCODE_ERROR:
{
if (bufLen < TFTP_MIN_ERROR_SIZE)
{
TFTP_DBG_PRINT("Get remote error size %u error.\n", bufLen);
return -1;
}
errCode = ntohs(*(unsigned short *)(inBuf + offset));
offset += 2; /*error CODE*/
TFTP_DBG_PRINT("Get remote error code %u msg [%s]\n", errCode, (inBuf + offset));
return TFTP_OPCODE_ERROR;
}
default:
{
TFTP_DBG_PRINT("Get remote opcode %u error.\n", opCode);
return -1;
}
}
}
/***************************************************************
* 函 数 名 : tftpGetRemoteData
* 负 责 人 : scyang
* 创建日期 : 20170720
* 函数功能 : 获取远程TFTP数据包
* 输入参数 :
* 输出参数 :
* 返 回 值 :
* 调用关系 :
* 其 它 :
****************************************************************/
int tftpGetRemoteData(int sockfd, com_sockaddr_in *addr, char *outBuf, int *inOutBufLen)
{
int rvLen = 0;
int inBufLen = 0;
int errFlag = 1;
int counter = TFTP_TIMEOUT_CNT;
COM_SET fdset;
com_socklen_t addrLen = 0;
com_timeval time;
if (sockfd < 0 || addr == NULL || outBuf == NULL || inOutBufLen == NULL)
{
TFTP_DBG_PRINT("getRemoteData para is null.\n");
return -1;
}
inBufLen = *inOutBufLen;
*inOutBufLen = 0;
while (counter > 0)
{
counter--;
COM_FD_ZERO(&fdset);
COM_FD_SET(sockfd, &fdset);
time.tv_sec = tftpDataTimeout;
time.tv_usec = 0;
if (COM_SELECT(sockfd + 1, &fdset, 0, 0, &time) <= 0)
{
TFTP_DBG_PRINT("time out for recv remote data(%u).\n", counter);
continue;
}
if (COM_FD_ISSET(sockfd, &fdset) == 0)
{
TFTP_DBG_PRINT("fd_isset error.\n");
continue;
}
memset(outBuf, 0, inBufLen);
addrLen = sizeof(com_sockaddr_in);
rvLen = COM_RECV_FROM(sockfd, outBuf, inBufLen, 0, (com_sockaddr *)addr, &addrLen);
if (rvLen <= 0)
{
TFTP_DBG_PRINT("Recv file from remote error(%d).\n", COM_ERR_NO_GET());
break;
}
else
{
errFlag = 0;
break;
}
}
*inOutBufLen = rvLen;
if (errFlag != 0)
{
return -1;
}
return 0;
}
/***************************************************************
* 函 数 名 : tftpUploadLocalData
* 负 责 人 : scyang
* 创建日期 : 20170720
* 函数功能 : 上传TFTP数据包
* 输入参数 :
* 输出参数 :
* 返 回 值 :
* 调用关系 :
* 其 它 :
****************************************************************/
int tftpUploadLocalData(int sockfd, com_sockaddr_in *addr, char *inBuf, int bufLen)
{
int sdLen = 0;
int errFlag = 1;
int counter = TFTP_TIMEOUT_CNT;
COM_SET fdset;
com_timeval time;
if (sockfd < 0 || addr == NULL || inBuf == NULL || bufLen <= 0)
{
TFTP_DBG_PRINT("getRemoteData para is null.\n");
return -1;
}
while (counter > 0)
{
counter--;
COM_FD_ZERO(&fdset);
COM_FD_SET(sockfd, &fdset);
time.tv_sec = tftpDataTimeout;
time.tv_usec = 0;
if (COM_SELECT(sockfd + 1, 0, &fdset, 0, &time) <= 0)
{
TFTP_DBG_PRINT("time out for recv remote data(%u).\n", counter);
continue;
}
if (COM_FD_ISSET(sockfd, &fdset) == 0)
{
TFTP_DBG_PRINT("fd_isset error.\n");
continue;
}
sdLen = COM_SEND_TO(sockfd, inBuf, bufLen, 0, (com_sockaddr *)addr, sizeof(com_sockaddr_in));
if (sdLen != bufLen)
{
TFTP_DBG_PRINT("Send file to remote error(%d).\n", COM_ERR_NO_GET());
break;
}
else
{
errFlag = 0;
break;
}
}
if (errFlag != 0)
{
return -1;
}
return 0;
}
/***************************************************************
* 函 数 名 : tftpParseData
* 负 责 人 : scyang
* 创建日期 : 20170720
* 函数功能 : 发送返回ACK
* 输入参数 :
* 输出参数 :
* 返 回 值 :
* 调用关系 :
* 其 它 :
****************************************************************/
int tftpAckSend(int sockfd, com_sockaddr_in *addr, unsigned short blockId)
{
int cmdLen = TFTP_CMD_SIZE;
char cmd[TFTP_CMD_SIZE] = {0};
if (sockfd < 0 || addr == NULL)
{
TFTP_DBG_PRINT("Input para is error.\n");
return -1;
}
if ((tftpBuildAck(blockId, cmd, &cmdLen) != 0)
|| (tftpUploadLocalData(sockfd, addr, cmd, cmdLen) != 0))
{
TFTP_DBG_PRINT("Tftp send ack(%u) error.\n", blockId);
return -1;
}
return 0;
}
/***************************************************************
* 函 数 名 : tftpAckRecv
* 负 责 人 : scyang
* 创建日期 : 20170720
* 函数功能 : 接收ACK
* 输入参数 :
* 输出参数 :
* 返 回 值 :
* 调用关系 :
* 其 它 :
****************************************************************/
int tftpAckRecv(int sockfd, com_sockaddr_in *addr, unsigned short *blockId)
{
int recvLen = 0;
int counter = TFTP_TIMEOUT_CNT;
unsigned short retVal = 0;
unsigned short tBlockId = 0;
char recvBuf[TFTP_CMD_SIZE] = {0};
if (sockfd < 0 || addr == NULL || blockId == NULL)
{
TFTP_DBG_PRINT("Input para is error.\n");
return -1;
}
while(counter > 0)
{
/*发送命令后接收一次ACK*/
counter--;
recvLen = TFTP_CMD_SIZE;
memset(recvBuf, 0, TFTP_CMD_SIZE);
if (tftpGetRemoteData(sockfd, addr, recvBuf, &recvLen) != 0)
{
TFTP_DBG_PRINT("Tftp get remote file error.\n");
return 1;
}
if ((retVal = tftpParseData(recvBuf, recvLen)) < 0)
{
/*收到的不是ACK,则继续收,直到收到为止*/
TFTP_DBG_PRINT("Tftp parse data error.\n");
continue;
}
if (retVal == TFTP_OPCODE_ACK)
{
if (recvLen < TFTP_MIN_ACK_SIZE)
{
TFTP_DBG_PRINT("Tftp get remote file end.\n");
return -1;
}
/*写命令的ACK对应的BLOCKID=0*/
tBlockId = ntohs(*(unsigned short *)(recvBuf + TFTP_ACK_BLOCK_OFFSET));
*blockId = tBlockId;
return 0;
}
else if (retVal == TFTP_OPCODE_ERROR)
{
TFTP_DBG_PRINT("Tftp get error ack info...\n");
return -1;
}
else
{
/*收到的不是ACK,则继续收,直到收到为止*/
TFTP_DBG_PRINT("Tftp get wrong ack(%u).\r\n", retVal);
continue;
}
}
return -1;
}
#if 0
#endif
/***************************************************************
* 函 数 名 : tftpDownloadBase
* 负 责 人 : scyang
* 创建日期 : 20170720
* 函数功能 : TFTP下载
* 输入参数 :
* 输出参数 :
* 返 回 值 :
* 调用关系 :
* 其 它 :
****************************************************************/
int tftpDownloadBase(
char *outBuf,
int *inoutBufLen,
char *hostIp,
char *fileName)
{
int sockfd = -1;
int count = 0;
int ramsize = 0;
int cmdLen = 0;
int recvLen = 0;
int status = 0;
int errFlag = 1;
int counter = TFTP_TIMEOUT_CNT;
unsigned short blockId = 1; /*块号连续且从1开始*/
unsigned short tBlockId = 0;
unsigned short retVal = 0;
char *rambuf = NULL;
char cmd[TFTP_CMD_SIZE] = {0};
char *recvBuf = NULL;
com_sockaddr_in addr;
if (outBuf == NULL || inoutBufLen == NULL || hostIp == NULL || fileName == NULL)
{
TFTP_DBG_PRINT("Input para is null.\n");
return -1;
}
rambuf = outBuf;
ramsize = *inoutBufLen;
if ((recvBuf = (char *)malloc (TFTP_BUF_SIZE)) == NULL)
{
TFTP_DBG_PRINT("Malloc error.\r\n");
return -1;
}
memset(&addr, 0, sizeof(com_sockaddr_in));
addr.sin_family = COM_AF_INET;
addr.sin_port = htons(TFTP_PORT);
inet_pton(AF_INET, hostIp, &addr.sin_addr);
do
{
/*create socket*/
if (tftpSocketCreate(&sockfd) < 0)
{
TFTP_DBG_PRINT("Create socket failed: error(%d).\n", COM_ERR_NO_GET());
break;
}
memset(cmd, 0, sizeof(cmd));
cmdLen = TFTP_CMD_SIZE;
if (tftpBuildCmd(TFTP_OPCODE_RRQ, fileName, cmd, &cmdLen) < 0)
{
TFTP_DBG_PRINT("Build tftp get remote file command failed.\n");
break;
}
if (tftpCmdSend(sockfd, &addr, cmd, cmdLen) < 0)
{
TFTP_DBG_PRINT("Send msg to %s failed.\n", hostIp);
break;
}
while (1)
{
if (counter <= 0)
{
status = -1;
TFTP_DBG_PRINT("Tftp get remote data(%u) timeout(%d).\n", blockId, counter);
break;
}
counter--;
recvLen = TFTP_BUF_SIZE;
memset(recvBuf, 0, TFTP_BUF_SIZE);
if (tftpGetRemoteData(sockfd, &addr, recvBuf, &recvLen) != 0)
{
/*如果接收第一块DATA就超时,说明命令就没有发送成功*/
if (blockId == 1)
{
TFTP_DBG_PRINT("Tftp get remote data(%u) error.\n", blockId);
break;
}
else
{
/*不是第一块DATA,有可能是ACK没有发送成功,重发ACK*/
TFTP_DBG_PRINT("Tftp get remote data(%u) error.\n", blockId);
/*没有收到DATA,重发上一个ACK*/
if (tftpAckSend(sockfd, &addr, tBlockId) != 0)
{
status = -1;
TFTP_DBG_PRINT("Tftp send ack(%u) error.\n", tBlockId);
break;
}
continue;
}
}
if ((retVal = tftpParseData(recvBuf, recvLen)) < 0)
{
status = -1;
TFTP_DBG_PRINT("Tftp parse data error.\n");
break;
}
switch (retVal)
{
case TFTP_OPCODE_DATA:
{
/*块号连续且从1开始*/
tBlockId = ntohs(*(unsigned short*)(recvBuf + TFTP_DATA_BLOCK_OFFSET));
if (tBlockId != blockId)
{
TFTP_DBG_PRINT("Tftp get remote block id %u/%u error(%d).\n", tBlockId, blockId, counter);
if (tBlockId < blockId)
{
/*重新发ACK请求上一个数据*/
if (tftpAckSend(sockfd, &addr, tBlockId) != 0)
{
status = -1;
TFTP_DBG_PRINT("Tftp send ack(%u) counter(%d) error.\n", tBlockId, counter);
break;
}
continue;
}
else
{
/*如果发来的数据(tBlockId > blockId)则说明逻辑出问题了*/
status = -1;
break;
}
}
else
{
/*正常则COUNTER恢复*/
counter = TFTP_TIMEOUT_CNT;
blockId++;
}
/*校验长度,防止写越界*/
recvLen -= TFTP_MIN_DATA_SIZE;
if ((count + recvLen) > TFTP_MAX_FILE_SIZE) /*tftp支持的最大文件*/
{
status = -1;
TFTP_DBG_PRINT("Tftp get remote data length %d too big.\n", (count + recvLen));
break;
}
if ((recvLen > 0) && ((count + recvLen) > ramsize))
{
memcpy ((rambuf + count), (recvBuf + TFTP_MIN_DATA_SIZE), (ramsize - count));
count += (ramsize - count);
status = TFTP_BUF_SIZE_ERR;
TFTP_DBG_PRINT("Tftp buffer small: count=%d, recvLen=%d, ramsize=%d.\r\n", count, recvLen, ramsize);
break;
}
else
{
if (recvLen > 0)
{
memcpy((rambuf + count), (recvBuf + TFTP_MIN_DATA_SIZE), recvLen);
count += recvLen;
}
/*接收一个包,需要返回一个ACK*/
if (tftpAckSend(sockfd, &addr, tBlockId) != 0)
{
status = -1;
TFTP_DBG_PRINT("Tftp send ack(%u) error.\n", tBlockId);
break;
}
}
if (recvLen < TFTP_MAX_DATA_SIZE)
{
errFlag = 0;
TFTP_DBG_PRINT("Tftp get remote file end(%d).\n", recvLen);
break;
}
break;
}
case TFTP_OPCODE_ERROR:
{
status = -1;
TFTP_DBG_PRINT("Tftp parse remote data error.\n");
break;
}
default:
{
TFTP_DBG_PRINT("Tftp parse remote data(%d) error.\n", retVal);
break;
}
}
/*数据接收状态异常,或者文件传输完成,则退出循环*/
if ((status != 0) || (errFlag == 0))
{
break;
}
}
errFlag = 0;
}while(0);
*inoutBufLen = count;
tftpSockSafeClose(&sockfd);
TFTP_SAFE_FREE(recvBuf);
TFTP_DBG_PRINT("Tftp download complete(%d/%d)...\n", count, ramsize);
if (errFlag != 0)
{
return -1;
}
return status;
}
/***************************************************************
* 函 数 名 : tftpUploadBase
* 负 责 人 : scyang
* 创建日期 : 20170720
* 函数功能 : TFTP上传
* 输入参数 :
* 输出参数 :
* 返 回 值 :
* 调用关系 :
* 其 它 :
****************************************************************/
int tftpUploadBase(
char *outBuf,
int bufLen,
char *hostIp,
char *fileName)
{
int sockfd = -1;
int count = 0;
int ramsize = 0;
int cmdLen = 0;
int sendLen = 0;
int status = 0;
int retVal = 0;
int errFlag = 1;
int completion = 0;
int counter = TFTP_TIMEOUT_CNT;
unsigned short blockId = 0;
unsigned short tBlockId = 0;
char *rambuf = NULL;
char cmd[TFTP_CMD_SIZE] = {0};
char *sendBuf = NULL;
com_sockaddr_in addr;
if (outBuf == NULL || hostIp == NULL || fileName == NULL)
{
TFTP_DBG_PRINT("Input para is null.\n");
return -1;
}
if (bufLen <= 0)
{
TFTP_DBG_PRINT("Input para buffer length %d error.\n", bufLen);
return -1;
}
rambuf = outBuf;
ramsize = bufLen;
if ((sendBuf = (char *)malloc (TFTP_BUF_SIZE)) == NULL)
{
TFTP_DBG_PRINT("Malloc error.\r\n");
return -1;
}
memset(&addr, 0, sizeof(com_sockaddr_in));
addr.sin_family = COM_AF_INET;
addr.sin_port = htons(TFTP_PORT);
inet_pton(AF_INET, hostIp, &addr.sin_addr);
do
{
/*create socket*/
if (tftpSocketCreate(&sockfd) < 0)
{
TFTP_DBG_PRINT("Create socket failed: error(%d).\n", COM_ERR_NO_GET());
break;
}
memset(cmd, 0, sizeof(cmd));
cmdLen = TFTP_CMD_SIZE;
if (tftpBuildCmd(TFTP_OPCODE_WRQ, fileName, cmd, &cmdLen) < 0)
{
TFTP_DBG_PRINT("Build tftp get remote file command failed.\n");
break;
}
if (tftpCmdSend(sockfd, &addr, cmd, cmdLen) < 0)
{
TFTP_DBG_PRINT("Send tftp command to %s failed.\n", hostIp);
break;
}
retVal = tftpAckRecv(sockfd, &addr, &tBlockId);
if ((retVal != 0) || (tBlockId != 0))
{
TFTP_DBG_PRINT("Get cmd ack(%u) error.\n", tBlockId);
break;
}
/*开始上传数据*/
blockId++;
while (completion == 0)
{
sendLen = 0;
memset(sendBuf, 0, TFTP_BUF_SIZE);
*(unsigned short *)sendBuf = htons(TFTP_OPCODE_DATA);
*(unsigned short *)(sendBuf + TFTP_DATA_BLOCK_OFFSET) = htons(blockId);
if ((ramsize - count) >= TFTP_MAX_DATA_SIZE)
{
memcpy(sendBuf + TFTP_MIN_DATA_SIZE, rambuf + count, TFTP_MAX_DATA_SIZE);
sendLen = TFTP_MIN_DATA_SIZE + TFTP_MAX_DATA_SIZE;
count += TFTP_MAX_DATA_SIZE;
}
else
{
if (ramsize > count)
{
memcpy(sendBuf + TFTP_MIN_DATA_SIZE, rambuf + count, (ramsize - count));
sendLen = TFTP_MIN_DATA_SIZE + (ramsize - count);
count += ramsize - count;
}
else
{
sendLen = TFTP_MIN_DATA_SIZE;
}
/*结束发送*/
completion = 1;
}
data_send:
if (counter <= 0)
{
status = -1;
TFTP_DBG_PRINT("Tftp send data(%u) timeout(%d).\n", blockId, counter);
break;
}
counter--;
if (tftpUploadLocalData(sockfd, &addr, sendBuf, sendLen) != 0)
{
status = -1;
TFTP_DBG_PRINT("Tftp send local file(%u) error.\n", blockId);
break;
}
retVal = tftpAckRecv(sockfd, &addr, &tBlockId);
if (retVal == 0)
{
if (tBlockId == blockId)
{
counter = TFTP_TIMEOUT_CNT;
blockId++;
continue;
}
else if (tBlockId != blockId)
{
TFTP_DBG_PRINT("Tftp get remote ack(%u/%u) counter(%d) error.\n", tBlockId, blockId, counter);
/*BLOCK ID必须大于0,对于BLOCK ID非如下情况,视为异常处理*/
if ((tBlockId == (blockId - 1)) && (tBlockId != 0) && (counter > 0))
{
goto data_send;
}
else
{
status = -1;
break;
}
}
else
{
status = -1;
TFTP_DBG_PRINT("Get data ack(%u) error.\n", tBlockId);
break;
}
}
else if (retVal == 1)
{
/*接收ACK超时,有可能是DATA远程没有收到,重发DATA*/
TFTP_DBG_PRINT("Get data ack timeout(%d).\n", retVal);
goto data_send;
}
else
{
status = -1;
TFTP_DBG_PRINT("Get data ack error(%d).\n", retVal);
break;
}
}
errFlag = 0;
}while(0);
tftpSockSafeClose(&sockfd);
TFTP_SAFE_FREE(sendBuf);
TFTP_DBG_PRINT("Tftp upload complete(%d/%d)...\n", count, ramsize);
if (errFlag != 0)
{
return -1;
}
return status;
}
/***************************************************************
* 函 数 名 : tftpDownloadFile
* 负 责 人 : scyang
* 创建日期 : 20170720
* 函数功能 : TFTP下载
* 输入参数 :
* 输出参数 :
* 返 回 值 :
* 调用关系 :
* 其 它 :
****************************************************************/
int tftpDownloadFile(
char *hostIp,
char *remotefile,
char *localfile)
{
int sockfd = -1;
int filefd = -1;
int count = 0;
int cmdLen = 0;
int recvLen = 0;
int wtLen = 0;
int status = 0;
int errFlag = 1;
int counter = TFTP_TIMEOUT_CNT;
unsigned short blockId = 1; /*块号连续且从1开始*/
unsigned short tBlockId = 0;
unsigned short retVal = 0;
char cmd[TFTP_CMD_SIZE] = {0};
char *recvBuf = NULL;
com_sockaddr_in addr;
if (remotefile == NULL || localfile == NULL || hostIp == NULL)
{
TFTP_DBG_PRINT("Input para is null.\n");
return -1;
}
/*open file*/
if ((filefd = open(localfile, O_WRONLY | O_CREAT | O_TRUNC, 0x777)) < 0)
{
TFTP_DBG_PRINT("Open localfile %s error.\r\n", localfile);
return -1;
}
if ((recvBuf = (char *)malloc (TFTP_BUF_SIZE)) == NULL)
{
close(filefd);
TFTP_DBG_PRINT("Malloc error.\r\n");
return -1;
}
memset(&addr, 0, sizeof(com_sockaddr_in));
addr.sin_family = COM_AF_INET;
addr.sin_port = htons(TFTP_PORT);
inet_pton(AF_INET, hostIp, &addr.sin_addr);
do
{
/*create socket*/
if (tftpSocketCreate(&sockfd) < 0)
{
TFTP_DBG_PRINT("Create socket failed: error(%d).\n", COM_ERR_NO_GET());
break;
}
memset(cmd, 0, sizeof(cmd));
cmdLen = TFTP_CMD_SIZE;
if (tftpBuildCmd(TFTP_OPCODE_RRQ, remotefile, cmd, &cmdLen) < 0)
{
TFTP_DBG_PRINT("Build tftp get remote file command failed.\n");
break;
}
if (tftpCmdSend(sockfd, &addr, cmd, cmdLen) < 0)
{
TFTP_DBG_PRINT("Send msg to %s failed.\n", hostIp);
break;
}
while (1)
{
if (counter <= 0)
{
status = -1;
TFTP_DBG_PRINT("Tftp get remote data(%u) timeout(%d).\n", blockId, counter);
break;
}
counter--;
recvLen = TFTP_BUF_SIZE;
memset(recvBuf, 0, TFTP_BUF_SIZE);
if (tftpGetRemoteData(sockfd, &addr, recvBuf, &recvLen) != 0)
{
/*如果接收第一块DATA就超时,说明命令就没有发送成功*/
if (blockId == 1)
{
TFTP_DBG_PRINT("Tftp get remote data(%u) error.\n", blockId);
break;
}
else
{
/*不是第一块DATA,有可能是ACK没有发送成功,重发ACK*/
TFTP_DBG_PRINT("Tftp get remote data(%u) error.\n", blockId);
/*没有收到DATA,重发上一个ACK*/
if (tftpAckSend(sockfd, &addr, tBlockId) != 0)
{
status = -1;
TFTP_DBG_PRINT("Tftp send ack(%u) error.\n", tBlockId);
break;
}
continue;
}
}
if ((retVal = tftpParseData(recvBuf, recvLen)) < 0)
{
status = -1;
TFTP_DBG_PRINT("Tftp parse data error.\n");
break;
}
switch (retVal)
{
case TFTP_OPCODE_DATA:
{
/*块号连续且从1开始*/
tBlockId = ntohs(*(unsigned short*)(recvBuf + TFTP_DATA_BLOCK_OFFSET));
if (tBlockId != blockId)
{
TFTP_DBG_PRINT("Tftp get remote block id %u/%u error(%d).\n", tBlockId, blockId, counter);
if (tBlockId < blockId)
{
/*重新发ACK请求上一个数据*/
if (tftpAckSend(sockfd, &addr, tBlockId) != 0)
{
status = -1;
TFTP_DBG_PRINT("Tftp send ack(%u) counter(%d) error.\n", tBlockId, counter);
break;
}
continue;
}
else
{
/*如果发来的数据(tBlockId > blockId)则说明逻辑出问题了*/
status = -1;
break;
}
}
else
{
/*正常则COUNTER恢复*/
counter = TFTP_TIMEOUT_CNT;
blockId++;
}
/*校验长度,防止写越界*/
recvLen -= TFTP_MIN_DATA_SIZE;
count += recvLen;
if (count > TFTP_MAX_FILE_SIZE) /*tftp支持的最大文件*/
{
status = -1;
TFTP_DBG_PRINT("Tftp get remote data length %d too big.\n", count);
break;
}
if (recvLen > 0)
{
if ((wtLen = write (filefd, (recvBuf + TFTP_MIN_DATA_SIZE), recvLen)) != recvLen)
{
status = -1;
TFTP_DBG_PRINT("Tftp write file %s error.\n", localfile);
break;
}
}
/*接收一个包,需要返回一个ACK*/
if (tftpAckSend(sockfd, &addr, tBlockId) != 0)
{
status = -1;
TFTP_DBG_PRINT("Tftp send ack(%u) error.\n", tBlockId);
break;
}
if (recvLen < TFTP_MAX_DATA_SIZE)
{
errFlag = 0;
TFTP_DBG_PRINT("Tftp get remote file end(%d).\n", recvLen);
break;
}
break;
}
case TFTP_OPCODE_ERROR:
{
status = -1;
TFTP_DBG_PRINT("Tftp parse remote data error.\n");
break;
}
default:
{
TFTP_DBG_PRINT("Tftp parse remote data(%d) error.\n", retVal);
break;
}
}
/*数据接收状态异常,或者文件传输完成,则退出循环*/
if ((status != 0) || (errFlag == 0))
{
break;
}
}
errFlag = 0;
}while(0);
close(filefd);
tftpSockSafeClose(&sockfd);
TFTP_SAFE_FREE(recvBuf);
TFTP_DBG_PRINT("Tftp download file complete...\n");
if (errFlag != 0)
{
return -1;
}
return status;
}
/***************************************************************
* 函 数 名 : tftpUploadFile
* 负 责 人 : scyang
* 创建日期 : 20170720
* 函数功能 : TFTP上传
* 输入参数 :
* 输出参数 :
* 返 回 值 :
* 调用关系 :
* 其 它 :
****************************************************************/
int tftpUploadFile(
char *hostIp,
char *remotefile,
char *localfile)
{
int sockfd = -1;
int filefd = -1;
int count = 0;
int ramsize = 0;
int cmdLen = 0;
int sendLen = 0;
int rdLen = 0;
int status = 0;
int retVal = 0;
int errFlag = 1;
int completion = 0;
int counter = TFTP_TIMEOUT_CNT;
unsigned short blockId = 0;
unsigned short tBlockId = 0;
char cmd[TFTP_CMD_SIZE] = {0};
char *sendBuf = NULL;
com_sockaddr_in addr;
if (hostIp == NULL || localfile == NULL || remotefile == NULL)
{
TFTP_DBG_PRINT("Input para is null.\n");
return -1;
}
ramsize = tftpGetFileSize(localfile);
if ((ramsize <= 0) || (ramsize > TFTP_MAX_FILE_SIZE)) /*tftp支持的最大文件*/
{
TFTP_DBG_PRINT("Tftp upload file %s length %d error.\n", localfile, ramsize);
return -1;
}
if ((filefd = open(localfile, O_RDONLY, 0x777)) < 0)
{
TFTP_DBG_PRINT("Open localfile %s error.\r\n", localfile);
return -1;
}
if ((sendBuf = (char *)malloc (TFTP_BUF_SIZE)) == NULL)
{
close(filefd);
TFTP_DBG_PRINT("Malloc error.\r\n");
return -1;
}
memset(&addr, 0, sizeof(com_sockaddr_in));
addr.sin_family = COM_AF_INET;
addr.sin_port = htons(TFTP_PORT);
inet_pton(AF_INET, hostIp, &addr.sin_addr);
do
{
/*create socket*/
if (tftpSocketCreate(&sockfd) < 0)
{
TFTP_DBG_PRINT("Create socket failed: error(%d).\n", COM_ERR_NO_GET());
break;
}
memset(cmd, 0, sizeof(cmd));
cmdLen = TFTP_CMD_SIZE;
if (tftpBuildCmd(TFTP_OPCODE_WRQ, remotefile, cmd, &cmdLen) < 0)
{
TFTP_DBG_PRINT("Build tftp get remote file command failed.\n");
break;
}
if (tftpCmdSend(sockfd, &addr, cmd, cmdLen) < 0)
{
TFTP_DBG_PRINT("Send tftp command to %s failed.\n", hostIp);
break;
}
retVal = tftpAckRecv(sockfd, &addr, &tBlockId);
if ((retVal != 0) || (tBlockId != 0))
{
TFTP_DBG_PRINT("Get cmd ack(%u) error.\n", tBlockId);
break;
}
/*开始上传数据*/
blockId++;
while (completion == 0)
{
sendLen = 0;
memset(sendBuf, 0, TFTP_BUF_SIZE);
*(unsigned short *)sendBuf = htons(TFTP_OPCODE_DATA);
*(unsigned short *)(sendBuf + TFTP_DATA_BLOCK_OFFSET) = htons(blockId);
rdLen = read(filefd, (sendBuf + TFTP_MIN_DATA_SIZE), TFTP_MAX_DATA_SIZE);
if (rdLen >= 0)
{
count += rdLen;
sendLen = TFTP_MIN_DATA_SIZE + rdLen;
if (rdLen < TFTP_MAX_DATA_SIZE)
{
/*结束发送*/
TFTP_DBG_PRINT("Tftp read file end(%d/%d)...\n", count, ramsize);
completion = 1;
}
}
else
{
status = -1;
TFTP_DBG_PRINT("Tftp read file %s error(%d).\n", errno);
break;
}
data_send:
if (counter <= 0)
{
status = -1;
TFTP_DBG_PRINT("Tftp send data(%u) timeout(%d).\n", blockId, counter);
break;
}
counter--;
if (tftpUploadLocalData(sockfd, &addr, sendBuf, sendLen) != 0)
{
status = -1;
TFTP_DBG_PRINT("Tftp send local file(%u) error.\n", blockId);
break;
}
retVal = tftpAckRecv(sockfd, &addr, &tBlockId);
if (retVal == 0)
{
if (tBlockId == blockId)
{
counter = TFTP_TIMEOUT_CNT;
blockId++;
continue;
}
else if (tBlockId != blockId)
{
TFTP_DBG_PRINT("Tftp get remote ack(%u/%u) counter(%d) error.\n", tBlockId, blockId, counter);
/*BLOCK ID必须大于0,对于BLOCK ID非如下情况,视为异常处理*/
if ((tBlockId == (blockId - 1)) && (tBlockId != 0) && (counter > 0))
{
goto data_send;
}
else
{
status = -1;
break;
}
}
else
{
status = -1;
TFTP_DBG_PRINT("Get data ack(%u) error.\n", tBlockId);
break;
}
}
else if (retVal == 1)
{
/*接收ACK超时,有可能是DATA远程没有收到,重发DATA*/
TFTP_DBG_PRINT("Get data ack timeout(%d).\n", retVal);
goto data_send;
}
else
{
status = -1;
TFTP_DBG_PRINT("Get data ack error(%d).\n", retVal);
break;
}
}
errFlag = 0;
}while(0);
close(filefd);
tftpSockSafeClose(&sockfd);
TFTP_SAFE_FREE(sendBuf);
TFTP_DBG_PRINT("Tftp upload file complete...\n");
if (errFlag != 0)
{
return -1;
}
return status;
}
#if 0
#endif
int main(int argc, char *argv[])
{
int bufLen = 32 * 1024 * 1024;
char *buf = NULL;
char *ip = argv[1];
char *filename = argv[2];
buf = (char *)malloc(32 * 1024 * 1024);
if (argc != 3)
{
printf("Usage: tftp.exe ip file_name\r\n");
return -1;
}
#if 1
if (tftpDownloadFile(ip, filename, "tmp.txt") < 0)
{
printf("tftpDownloadFile error.\r\n");
return -1;
}
if (tftpUploadFile(ip, "tmp1.txt", "tmp.txt") < 0)
{
printf("tftpUploadFile error.\r\n");
return -1;
}
#endif
#if 1
if (tftpDownloadBase(buf, &bufLen, ip, filename) < 0)
{
printf("tftpDownloadBase error.\r\n");
return -1;
}
if (tftpUploadBase(buf, bufLen, ip, "tmp2.txt") < 0)
{
printf("tftpUploadBase error.\r\n");
return -1;
}
#endif
return 0;
}