#include "BupTFTP.h" //#include "qgnetworkinterface.h" #include #include #include #include #include #include #include //#include "mydebug.h" #define MyDebug qDebug //#define dbg qDebug #include #define TFTP_MAXPKTLEN 512 #define DEFAULT_TGT_PORT 50069 //-- #define TFTP_RRQ 01 /* read request */ #define TFTP_WRQ 02 /* write request */ #define TFTP_DATA 03 /* data packet */ #define TFTP_ACK 04 /* acknowledgement */ #define TFTP_ERROR 05 /* error code */ #define TFTP_OACK 06 /* option acknowledgement */ #define TFTP_EUNDEF 0 /* not defined */ #define TFTP_ENOTFOUND 1 /* file not found */ #define TFTP_EACCESS 2 /* access violation */ #define TFTP_ENOSPACE 3 /* disk full or allocation exceeded */ #define TFTP_EBADOP 4 /* illegal TFTP operation */ #define TFTP_EBADID 5 /* unknown transfer ID */ #define TFTP_EEXISTS 6 /* file already exists */ #define TFTP_ENOUSER 7 /* no such user */ #define TFTP_EBADOPTION 8 /* bad option */ #define TFTP_ECANCELLED 99 /* cancelled by administrator */ typedef struct { unsigned char th_opcode[2]; /* packet type */ union { unsigned char tu_block[2]; /* block # */ unsigned char tu_code[2]; /* error code */ unsigned char tu_stuff[1]; /* request packet stuff */ char tu_name[1]; } th_u; } tftphdr_t; typedef struct { tftphdr_t header; char th_data[2048]; /* data or error string */ } tftppkt_t; static void tftp_htons(unsigned char* const p, unsigned short v) { p[0]=(unsigned char)(v>>8); p[1]=(unsigned char)(v); } static unsigned short tftp_ntos(const unsigned char* n) { return n[1]|(n[0]<<8); } //-- class FtpOperation { public: //QString fname; char fname[512]; bool read; const void* data; unsigned int size; BupTFTPReceiver* receiver; BupTFTPID* reqId; bool embedFirstBlock; FtpOperation(): read(false), data(0), size(0), receiver(0), reqId(0), embedFirstBlock(false) { fname[0]=0; } FtpOperation(const FtpOperation& p): read(p.read), data(p.data), size(p.size), receiver(p.receiver), reqId(p.reqId), embedFirstBlock(p.embedFirstBlock) { memcpy(fname, p.fname, sizeof fname); } }; class BupTFTP::Implementation { public: int sid; BupTFTP* parent; QQueue queue; int tgt_port; QHostAddress tgtAddress; QUdpSocket rxSocket; QUdpSocket txSocket; QTimer txTimer; QTimer stsTimer; unsigned int data_len; unsigned long timeout_sts_ms; unsigned long timeout_first_ms; unsigned long timeout_last_ms; unsigned long timeout_data_ms; QString lastErrorString; int lastErrorCode; QHostAddress lastTargetAddress; int lastTargetPort; QString lastStatusString; QByteArray fileBuffer; const char* data_pointer; int data_size; unsigned short block_counter; tftppkt_t pkt; int state; int rx_mode; char* rx_data_pointer; BupTFTPReceiver* receiver; int rx_size; int sts_timeout_counter; int fixServerBug; QHostAddress localIP; QHostAddress broadcastIP; FtpOperation currentOp; qint64 sendPacket(const tftppkt_t& pkt, unsigned int size, QUdpSocket* s=0) { int total_size=size+sizeof pkt.header; if (s==0) s=&txSocket; //MyDebug()<<"->"<writeDatagram(reinterpret_cast(&pkt.header.th_opcode[0]), total_size, tgtAddress, tgt_port); } Implementation(BupTFTP* controller): sid(-1), parent(controller) { memset(&pkt, 0,sizeof pkt); data_len=TFTP_MAXPKTLEN; timeout_first_ms=2000; timeout_last_ms=60*1000; timeout_data_ms=2000; timeout_sts_ms=2*1000; txTimer.setSingleShot(true); stsTimer.setInterval(timeout_sts_ms); rx_mode=0; state=0; fixServerBug=0; } bool enqueue(BupTFTPID* reqId, const char* fname/*const QString& fname*/, const void* data, unsigned int size, bool embedFirstBlock, BupTFTPReceiver* receiver=0, bool read_op=false) { FtpOperation op; strncpy(op.fname,fname, sizeof op.fname); op.data=data; op.size=size; op.read=read_op; op.receiver=receiver; op.embedFirstBlock=embedFirstBlock; op.reqId=reqId; queue.enqueue(op); bool ok=queue_consume(); Q_EMIT parent->queueChanged(queue.size()); return ok; } bool queue_consume() { if (state!=0) return false; if (queue.isEmpty()) return false; FtpOperation op=queue.dequeue(); currentOp=op; Q_EMIT parent->queueChanged(queue.size()); bool ok=false; if (op.read) ok=receive(op.embedFirstBlock, const_cast(op.data), op.size, op.fname, op.receiver); else ok=send(op.embedFirstBlock, op.data, op.size, op.fname); if (ok) { Q_EMIT parent->sendBegin(); } else Q_EMIT parent->transferError(parent, op.reqId, lastErrorCode, lastErrorString); return ok; } int extractProgress(const QString& str) { bool ok=false; QString tmp=str.section(' ', 0, 0); int n=tmp.toInt(&ok, 0); if (ok) return n; else return -1; } int rxDataReady() { while(rxSocket.hasPendingDatagrams()) { QNetworkDatagram d=rxSocket.receiveDatagram(); unsigned int size=d.data().size(); if (size<3) //sizeof(tftphdr_t)) continue; const tftppkt_t& rxpkt=*reinterpret_cast(d.data().constData()); auto opcode=tftp_ntos(rxpkt.header.th_opcode); //if ((d.senderAddress()!=tgtAddress) || (d.senderPort()!=tgt_port)) // continue; lastTargetAddress=d.senderAddress(); lastTargetPort=d.senderPort(); MyDebug()<<"RRQ-ACK"<receiveBegin(0); txTimer.start(timeout_data_ms); } else if (opcode==TFTP_DATA) { unsigned short bn=tftp_ntos(rxpkt.header.th_u.tu_block); int rx_data_size=size-sizeof(rxpkt.header); if (rx_data_size>0) { if (receiver) receiver->receiveBlock(rxpkt.th_data, size-sizeof(rxpkt.header), bn); else if (rx_data_pointer) { memcpy(rx_data_pointer, rxpkt.th_data, size-sizeof(rxpkt.header)); rx_data_pointer+=rx_data_size; } txTimer.start(timeout_data_ms); } tftp_htons(reply.header.th_opcode, TFTP_ACK); reply.header.th_u.tu_block[0]=rxpkt.header.th_u.tu_block[0]; reply.header.th_u.tu_block[1]=rxpkt.header.th_u.tu_block[1]; sendPacket(reply, 4, &rxSocket); if (rx_data_size<(int)data_len) { txTimer.stop(); rx_mode=0; MyDebug()<<"RRQ: Terminated"; if (receiver) receiver->receiveTerminated(); else Q_EMIT parent->receiveTerminated(parent, currentOp.reqId, true, lastTargetAddress); } } else { stsTimer.stop(); unsigned short ecode=tftp_ntos(rxpkt.header.th_u.tu_code); if (receiver) receiver->receiveError(ecode==3 ? 1 : 0); return 1; } //rxSocket.writeDatagram((const char*)&reply, sizeof reply, lastTargetAddress, lastTargetPort); //sendPacket(reply, 4, &rxSocket); return 0; } if (opcode!=TFTP_ERROR) { lastErrorCode=1000; lastErrorString=QString("invalid opcode %1").arg(opcode); stsTimer.stop(); return 1; } unsigned short ecode=tftp_ntos(rxpkt.header.th_u.tu_code); if (ecode==TFTP_EBADID) { if (sts_timeout_counter) --sts_timeout_counter; lastStatusString=QString::fromLatin1(&rxpkt.th_data[0]); if (txTimer.isActive()) txTimer.start(); return 0; } else if (ecode==TFTP_ENOTFOUND) { lastStatusString=QString::fromLatin1(&rxpkt.th_data[0]); if (lastStatusString=="ready") { stsTimer.stop(); } return 0; } else if (ecode==TFTP_EEXISTS) { lastErrorCode=ecode; lastErrorString=QString::fromLatin1(&rxpkt.th_data[0]); return 2; } else { stsTimer.stop(); lastErrorCode=ecode; lastErrorString=QString::fromLatin1(&rxpkt.th_data[0]); return 1; } } return 0; } int ready_error_ignore; bool dataReady() { ready_error_ignore=0; if (state<1) { lastErrorCode=1000; lastErrorString=QString("Unexpected datagram"); while(txSocket.hasPendingDatagrams()) { QNetworkDatagram d=txSocket.receiveDatagram(); unsigned int size=d.data().size(); if (size<3)//sizeof(tftphdr_t)) continue; const tftppkt_t& rxpkt=*reinterpret_cast(d.data().constData()); auto opcode=tftp_ntos(rxpkt.header.th_opcode); if (opcode==TFTP_ERROR) { state=0; lastErrorCode=tftp_ntos(rxpkt.header.th_u.tu_code); lastErrorString=QString::fromLatin1(&rxpkt.th_data[0]); qDebug()<<"Unexpected:"<unsollecitatedAck(parent, d.senderAddress(), lastErrorCode, lastErrorString); ready_error_ignore=1; } else qDebug()<<"Unexpected:"<(d.data().constData()); auto opcode=tftp_ntos(rxpkt.header.th_opcode); //auto bknum=tftp_ntos(rxpkt.header.th_u.tu_block); //MyDebug()<<"WRQ-ACK"<(int)data_len ? (int)data_len: data_size; tftp_htons(pkt.header.th_opcode, TFTP_DATA); tftp_htons(pkt.header.th_u.tu_block, block_counter); memcpy(&pkt.th_data[0], data_pointer, frag_size); data_pointer+=frag_size; data_size-=frag_size; /*qint64 r=*/sendPacket(pkt, frag_size); //txSocket.writeDatagram(reinterpret_cast(&pkt.header.th_opcode[0]), frag_size+sizeof pkt.header, tgtAddress, tgt_port); if (frag_size<(int)data_len) { txTimer.start(timeout_last_ms); //stsTimer.start(timeout_sts_ms); state=2; Q_EMIT parent->lastBlockSent(); } else txTimer.start(timeout_data_ms); } else if (opcode==TFTP_ERROR) { state=0; lastErrorCode=tftp_ntos(rxpkt.header.th_u.tu_code); lastErrorString=QString::fromLatin1(&rxpkt.th_data[0]); txTimer.stop(); return false; } else { state=0; lastErrorCode=1000; lastErrorString=QString("Invadid opcode (%1)").arg(opcode); txTimer.stop(); return false; } } return true; } bool send(BupTFTPID* rId, const QString& localFile, const QString& remoteFile) { QFile file(localFile); if (!file.open(QIODevice::ReadOnly)) { lastErrorString=file.errorString(); return false; } QFileInfo finfo(localFile); fileBuffer=file.readAll(); file.close(); data_size=fileBuffer.size(); if (data_size==0) { return false; } QString rFile(remoteFile.isEmpty() ? finfo.fileName() : QString(remoteFile).arg(finfo.fileName())); return enqueue(rId, rFile.toLatin1().constData(), fileBuffer.constData(), data_size, false); //return send(false, fileBuffer.constData(), data_size, rFile.toLatin1().constData()); } int addNameAndOptions(char* p, unsigned int size, const char* name, bool embedFirst=false) { const char* opt[]={0, "octet", 0, 0}; //"blksize", "1024", 0, 0}; opt[0]=name; unsigned int max_len=TFTP_MAXPKTLEN; unsigned int added_len=0; if (embedFirst) opt[4]="data"; for(int i=0; opt[i]; ++i) { unsigned int len=strlen(opt[i]); strncpy(p, opt[i], max_len); added_len+=(len+1); p+=len; ++p; p[0]=0; } if (size) { static char buffer[512]; const char* tsize="tsize"; unsigned int len=strlen(tsize); strcpy(p, tsize); added_len+=(len+1); p+=len; ++p; itoa(size, buffer, 10); len=strlen(buffer); strcpy(p, buffer); added_len+=(len+1); p+=len; ++p; p[0]=0; } p[1]=0; p[2]=0; p[3]=0; p[4]=0; if (fixServerBug) added_len+=2; return added_len; } bool send(bool embedFirstBlock, const void* data_address, unsigned int len, const char* remoteFile) //QString& remoteFile) { if (state!=0) { MyDebug()<<"TFTP busy!"; return false; } data_pointer=(const char*)data_address; //fileBuffer.constData(); data_size=len; block_counter=0; sts_timeout_counter=0; if (embedFirstBlock && (len>512)) { qDebug()<<"Invalid Embed"; embedFirstBlock=false; } memset(&pkt, 0, sizeof pkt); tftp_htons(pkt.header.th_opcode, TFTP_WRQ); const char* pname=remoteFile;//.toLatin1().constData(); #if 0 unsigned int name_len=strlen(pname); strncpy((char*)pkt.header.th_u.tu_stuff, pname, TFTP_MAXPKTLEN); unsigned char* p=&pkt.header.th_u.tu_stuff[name_len+1]; strcpy((char*)p, "octet"); qint64 msg_len=name_len+strlen("octet")+1; //+(sizeof pkt.header) #endif unsigned int add_len=addNameAndOptions((char*)pkt.header.th_u.tu_stuff, len, pname, embedFirstBlock); qint64 msg_len=add_len; state=0; if (embedFirstBlock) { char* p=(char*)&pkt.header.th_u.tu_stuff; p[msg_len]=0; ++msg_len; memcpy(&p[msg_len], data_address, len); msg_len+=len; block_counter=1; state=2; } qint64 r=sendPacket(pkt, msg_len-2); //txSocket.writeDatagram(reinterpret_cast(&pkt.header.th_opcode[0]), msg_len, tgtAddress, tgt_port); MyDebug()<<"TFTP:WRQ"<10) { lastErrorCode=100; lastErrorString="status timeout"; return false; } memset(&pkt, 0, sizeof pkt); tftp_htons(pkt.header.th_opcode, TFTP_RRQ); static const char sts_fname[]="$status$"; strcpy((char*)pkt.header.th_u.tu_stuff, sts_fname); qint64 msg_len=sizeof(sts_fname); //(sizeof pkt.header) sendPacket(pkt, msg_len, &rxSocket); //rxSocket.writeDatagram(reinterpret_cast(&pkt.header.th_opcode[0]), msg_len, tgtAddress, tgt_port); stsTimer.start(); } return true; } void pingServer(int port) { memset(&pkt, 0, sizeof pkt); tftp_htons(pkt.header.th_opcode, TFTP_RRQ); static const char sts_fname[]="$hello$0octet"; strcpy((char*)pkt.header.th_u.tu_stuff, sts_fname); ((char*)pkt.header.th_u.tu_stuff)[7]=0; //static const char sts_fname[]="$list$octet"; //strcpy((char*)pkt.header.th_u.tu_stuff, sts_fname); qint64 msg_len=sizeof(sts_fname); //(sizeof pkt.header) bool ok=rxSocket.writeDatagram((char*)&pkt, msg_len+sizeof pkt.header, broadcastIP, port); //QgNetworkInterface::networkBroadcast(), port); if (!ok) MyDebug()<<"ping fial:"<(&pkt.header.th_opcode[0]), msg_len, tgtAddress, tgt_port); MyDebug()<<"RRQ"<0) p_.timeout_first_ms=start_tm*1000; if (data_tm>0) p_.timeout_data_ms=data_tm*1000; if (last_tm>0) p_.timeout_last_ms=last_tm*1000; } void BupTFTP::fixServerBug(int n) { p_.fixServerBug=n; } bool BupTFTP::receive(BupTFTPID* rId, BupTFTPReceiver *theReceived, const QString &remoteFilename, bool embedFirstBlock) { return p_.enqueue(rId, remoteFilename.toLatin1().constData(), 0, 0, embedFirstBlock, theReceived, true); } bool BupTFTP::receiveMemory(BupTFTPID* rId, void* data, unsigned int max_size, const char* remoteFileName, bool embedFirstBlock) { return p_.enqueue(rId, remoteFileName, data, max_size, embedFirstBlock, 0, true); } bool BupTFTP::receiveMemory(BupTFTPID* rId, void* data, unsigned int max_size, const QString& remoteFileName, bool embedFirstBlock) { return p_.enqueue(rId, remoteFileName.toLatin1().constData(), data, max_size, embedFirstBlock, 0, true); #if 0 bool ok=p_.receive(data, max_size, remoteFileName); if (ok) { Q_EMIT sendBegin(); } else Q_EMIT transferError(p_.lastErrorCode, p_.lastErrorString); return true; #endif } #if 0 bool FpgaBridgeTftp::srioSend(unsigned int remote_address, unsigned int db, const void* data, unsigned int size) { return false; } bool FpgaBridgeTftp::srioReceive(unsigned int remote_address, unsigned int db, const void* data, unsigned int size) { return false; } #endif