#include "fpgabridgetftp.h" //#include "qgnetworkinterface.h" #include #include #include #include #include #include #include #include "mydebug.h" //#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; bool read; const void* data; unsigned int size; TftpReceiver* receiver; }; class FpgaBridgeTftp::Implementation { public: FpgaBridgeTftp* 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; TftpReceiver* receiver; int rx_size; int sts_timeout_counter; QHostAddress localIP; QHostAddress broadcastIP; 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(FpgaBridgeTftp* controller): parent(controller) { memset(&pkt, 0,sizeof pkt); data_len=TFTP_MAXPKTLEN; timeout_first_ms=1000; timeout_last_ms=1000; //1000 timeout_data_ms=1000; timeout_sts_ms=1*1000; txTimer.setSingleShot(true); stsTimer.setInterval(timeout_sts_ms); rx_mode=0; state=0; } bool enqueue(const QString& fname, const void* data, unsigned int size, TftpReceiver* receiver=0, bool read_op=false) { FtpOperation op; op.fname=fname; op.data=data; op.size=size; op.read=read_op; op.receiver=receiver; 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(); Q_EMIT parent->queueChanged(queue.size()); bool ok=false; if (op.read) ok=receive(const_cast(op.data), op.size, op.fname, op.receiver); else ok=send(op.data, op.size, op.fname); if (ok) { Q_EMIT parent->sendBegin(); } else { //QString msg = QString("transferError1 %1 %2").arg(lastErrorCode).arg(lastErrorString); //MyDebug.logMsg(type_log_error, origin_msg_tftp, msg); Q_EMIT parent->transferError(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(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); } 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; } } 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]; MyDebug.logMsg(type_log_info, origin_msg_tftp, "sendPacket"); sendPacket(reply, 4, &rxSocket); MyDebug.logMsg(type_log_info, origin_msg_tftp, "sendPacket done"); if (rx_data_size<(int)data_len) { rx_mode=0; MyDebug.logMsg(type_log_info, origin_msg_tftp, "RRQ: Terminated"); if (receiver) receiver->receiveTerminated(); else Q_EMIT parent->receiveTerminated(true, lastTargetAddress); } } else { stsTimer.stop(); unsigned short ecode=tftp_ntos(rxpkt.header.th_u.tu_code); if (receiver) receiver->receiveError(ecode==3 ? 1 : 0); if (ecode==TFTP_EACCESS) { lastErrorCode=ecode; lastErrorString=QString("- access violation - %1").arg(QString::fromLatin1(&rxpkt.th_data[0])); } return 1; } //rxSocket.writeDatagram((const char*)&reply, sizeof reply, lastTargetAddress, lastTargetPort); //sendPacket(reply, 4, &rxSocket); return 0; } if (opcode!=TFTP_ERROR) { MyDebug.logMsg(type_log_error, origin_msg_tftp, "!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) { MyDebug.logMsg(type_log_error, origin_msg_tftp, "TFTP_EBADI"); 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) { MyDebug.logMsg(type_log_error, origin_msg_tftp, "TFTP_ENOTFOUND"); lastStatusString=QString::fromLatin1(&rxpkt.th_data[0]); if (lastStatusString=="ready") { stsTimer.stop(); } return 0; } else if (ecode==TFTP_EEXISTS) { MyDebug.logMsg(type_log_error, origin_msg_tftp, "TFTP_EEXISTS"); lastErrorCode=ecode; lastErrorString=QString::fromLatin1(&rxpkt.th_data[0]); return 2; } else { MyDebug.logMsg(type_log_error, origin_msg_tftp, "TFTP_EEXISTS other"); stsTimer.stop(); lastErrorCode=ecode; lastErrorString=QString::fromLatin1(&rxpkt.th_data[0]); return 1; } } return 0; } bool dataReady() { if (state<1) { lastErrorCode=1000; lastErrorString=QString("Unexpected datagram"); txSocket.reset(); return false; } while(txSocket.hasPendingDatagrams()) { QNetworkDatagram d=txSocket.receiveDatagram(); if ((d.senderAddress()!=tgtAddress))// || (d.senderPort()!=tgt_port)) continue; unsigned int size=d.data().size(); if (size(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; } 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(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 send(fileBuffer.constData(), data_size, rFile); } int addNameAndOptions(char* p, const char* name) { const char* opt[]={0, "octet", "blksize", "1024", 0}; opt[0]=name; unsigned int max_len=TFTP_MAXPKTLEN; unsigned int added_len=0; for(int i=0; opt[i]; ++i) { unsigned int len=strlen(opt[i]); strncpy(p, opt[i], max_len); added_len+=len; p+=len; ++p; p[0]=0; } p[1]=0; p[2]=0; return added_len+1; } bool send(const void* data_address, unsigned int len, const QString& remoteFile) { if (state!=0) { MyDebug.logMsg(type_log_info, origin_msg_tftp, "TFTP busy!"); return false; } data_pointer=(const char*)data_address; //fileBuffer.constData(); data_size=len; block_counter=0; sts_timeout_counter=0; 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, pname); qint64 msg_len=1+add_len; state=0; qint64 r=sendPacket(pkt, msg_len); //txSocket.writeDatagram(reinterpret_cast(&pkt.header.th_opcode[0]), msg_len, tgtAddress, tgt_port); MyDebug.logMsg(type_log_info, origin_msg_tftp, QString("WRQ %1:%2 %3").arg(tgtAddress.toString()).arg(tgt_port).arg(remoteFile)); if (r<1) { lastErrorString=txSocket.errorString(); lastErrorCode=txSocket.error(); /*MyDebug().logMsg(type_log_error, origin_msg_tftp, "error wrq");*/ return false; } state=1; txTimer.start(timeout_first_ms); return true; } bool txTimeOut() { state=0; return false; } bool stsTimeOut() { if (state==2) { MyDebug.logMsg(type_log_info, origin_msg_tftp, "Request status"); ++sts_timeout_counter; if (sts_timeout_counter>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.logMsg(type_log_error, origin_msg_tftp, QString("Ping fail: %1").arg(rxSocket.errorString())); } } bool receive(void* data_address, unsigned int /*len*/, const QString& remoteFile, TftpReceiver* therx=0) { rx_data_pointer=(char*)data_address; //fileBuffer.constData(); receiver=therx; //rx_data_size=len; block_counter=0; sts_timeout_counter=0; memset(&pkt, 0, sizeof pkt); tftp_htons(pkt.header.th_opcode, TFTP_RRQ); const char* pname=remoteFile.toLatin1().constData(); unsigned int add_len=addNameAndOptions((char*)pkt.header.th_u.tu_stuff, pname); qint64 msg_len=1+add_len; state=0; rx_mode=1; qint64 r=sendPacket(pkt, msg_len, &rxSocket); //txSocket.writeDatagram(reinterpret_cast(&pkt.header.th_opcode[0]), msg_len, tgtAddress, tgt_port); //MyDebug()<<"WRQ"<