fix send message to server tftp
This commit is contained in:
parent
3b447d7fdb
commit
fcb13c437b
927
BupTFTP.cpp
Normal file
927
BupTFTP.cpp
Normal file
@ -0,0 +1,927 @@
|
|||||||
|
#include "BupTFTP.h"
|
||||||
|
//#include "qgnetworkinterface.h"
|
||||||
|
|
||||||
|
#include <QUdpSocket>
|
||||||
|
#include <QNetworkDatagram>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QQueue>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
//#include "mydebug.h"
|
||||||
|
#define MyDebug qDebug
|
||||||
|
|
||||||
|
//#define dbg qDebug
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#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<FtpOperation> 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()<<"->"<<tftp_ntos(pkt.header.th_opcode)<<tftp_ntos(pkt.header.th_u.tu_stuff)<<size<<total_size<<tgtAddress<<tgt_port;
|
||||||
|
return s->writeDatagram(reinterpret_cast<const char*>(&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<void*>(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<const tftppkt_t*>(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"<<lastTargetAddress<<lastTargetPort<<opcode<<state;
|
||||||
|
|
||||||
|
if (rx_mode)
|
||||||
|
{
|
||||||
|
tftppkt_t reply;
|
||||||
|
if ((opcode==TFTP_ACK) || (opcode==TFTP_OACK))
|
||||||
|
{
|
||||||
|
tftp_htons(reply.header.th_opcode, TFTP_ACK);
|
||||||
|
reply.header.th_u.tu_block[0]=0;
|
||||||
|
reply.header.th_u.tu_block[1]=0;
|
||||||
|
|
||||||
|
sendPacket(reply, 4, &rxSocket);
|
||||||
|
|
||||||
|
if (receiver)
|
||||||
|
receiver->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<const tftppkt_t*>(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:"<<d.senderAddress()<<opcode<<lastErrorCode<<lastErrorString;
|
||||||
|
|
||||||
|
parent->unsollecitatedAck(parent, d.senderAddress(), lastErrorCode, lastErrorString);
|
||||||
|
ready_error_ignore=1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
qDebug()<<"Unexpected:"<<d.senderAddress()<<opcode;
|
||||||
|
}
|
||||||
|
//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<3)//sizeof(tftphdr_t))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const tftppkt_t& rxpkt=*reinterpret_cast<const tftppkt_t*>(d.data().constData());
|
||||||
|
auto opcode=tftp_ntos(rxpkt.header.th_opcode);
|
||||||
|
//auto bknum=tftp_ntos(rxpkt.header.th_u.tu_block);
|
||||||
|
//MyDebug()<<"WRQ-ACK"<<tgtAddress<<tgt_port<<opcode<<bknum<<state;
|
||||||
|
|
||||||
|
if ((opcode==TFTP_ACK) || (opcode==TFTP_OACK))
|
||||||
|
{
|
||||||
|
tgt_port=d.senderPort();
|
||||||
|
|
||||||
|
if (opcode==TFTP_OACK)
|
||||||
|
{
|
||||||
|
if ((state==2) && (block_counter==1))
|
||||||
|
{
|
||||||
|
state=1;
|
||||||
|
block_counter=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state==2)
|
||||||
|
{
|
||||||
|
MyDebug()<<"Finished!";
|
||||||
|
lastErrorString="Transfer completed";
|
||||||
|
lastErrorCode=0;
|
||||||
|
stsTimer.stop();
|
||||||
|
txTimer.stop();
|
||||||
|
state=0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
++block_counter;
|
||||||
|
int frag_size=data_size>(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<char*>(&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<char*>(&pkt.header.th_opcode[0]), msg_len, tgtAddress, tgt_port);
|
||||||
|
MyDebug()<<"TFTP:WRQ"<<tgtAddress<<tgt_port<<remoteFile;
|
||||||
|
if (r<1)
|
||||||
|
{
|
||||||
|
lastErrorString=txSocket.errorString();
|
||||||
|
lastErrorCode=txSocket.error();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
state=1;
|
||||||
|
txTimer.start(timeout_first_ms);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool txTimeOut()
|
||||||
|
{
|
||||||
|
state=0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool stsTimeOut()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (state==2)
|
||||||
|
{
|
||||||
|
MyDebug()<<"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<char*>(&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:"<<rxSocket.errorString();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool receive(bool embedFirst ,void* data_address, unsigned int /*len*/, const char* pname /*QString& remoteFile*/, BupTFTPReceiver* 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, 0, pname, embedFirst);
|
||||||
|
qint64 msg_len=add_len;
|
||||||
|
|
||||||
|
state=0;
|
||||||
|
rx_mode=1;
|
||||||
|
|
||||||
|
qint64 r=sendPacket(pkt, msg_len-2, &rxSocket); //txSocket.writeDatagram(reinterpret_cast<char*>(&pkt.header.th_opcode[0]), msg_len, tgtAddress, tgt_port);
|
||||||
|
MyDebug()<<"RRQ"<<tgtAddress<<tgt_port;
|
||||||
|
if (r<1)
|
||||||
|
{
|
||||||
|
lastErrorString=txSocket.errorString();
|
||||||
|
lastErrorCode=txSocket.error();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
state=0;
|
||||||
|
txTimer.start(timeout_first_ms);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
BupTFTP::BupTFTP(QObject *parent):
|
||||||
|
QObject(parent),
|
||||||
|
p_(*new Implementation(this))
|
||||||
|
{
|
||||||
|
|
||||||
|
connect(&p_.txSocket, &QUdpSocket::readyRead, this,
|
||||||
|
[this]()
|
||||||
|
{
|
||||||
|
//while(p_.txSocket.hasPendingDatagrams())
|
||||||
|
{
|
||||||
|
bool ok=p_.dataReady();
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
//p_.queue_consume();
|
||||||
|
if (!p_.ready_error_ignore)
|
||||||
|
{
|
||||||
|
if (p_.lastErrorCode==0)
|
||||||
|
Q_EMIT sendTerminated(this, p_.currentOp.reqId);
|
||||||
|
else
|
||||||
|
Q_EMIT transferError(this, p_.currentOp.reqId, p_.lastErrorCode, p_.lastErrorString);
|
||||||
|
}
|
||||||
|
p_.queue_consume();
|
||||||
|
}
|
||||||
|
else if (p_.block_counter==1)
|
||||||
|
Q_EMIT progressInfo(-1, "Uploading");
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(&p_.rxSocket, &QUdpSocket::readyRead, this,
|
||||||
|
[this]()
|
||||||
|
{
|
||||||
|
//while(p_.rxSocket.hasPendingDatagrams())
|
||||||
|
{
|
||||||
|
int ok=p_.rxDataReady();
|
||||||
|
if (ok==2)
|
||||||
|
{
|
||||||
|
Q_EMIT serverHello(p_.lastTargetAddress.toIPv4Address(), p_.lastTargetPort, p_.lastErrorString);
|
||||||
|
}
|
||||||
|
else if (ok)
|
||||||
|
Q_EMIT transferError(this, p_.currentOp.reqId, p_.lastErrorCode, p_.lastErrorString);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//int n=p_.extractProgress(p_.lastStatusString);
|
||||||
|
//Q_EMIT progressInfo(n, p_.lastStatusString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(&p_.txTimer, &QTimer::timeout, this,
|
||||||
|
[this]()
|
||||||
|
{
|
||||||
|
bool ok=p_.txTimeOut();
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
MyDebug()<<"Tx timeout";
|
||||||
|
Q_EMIT transferError(this, p_.currentOp.reqId, 100, "Timeout");
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
connect(&p_.stsTimer, &QTimer::timeout, this,
|
||||||
|
[this]()
|
||||||
|
{
|
||||||
|
/*bool ok=*/p_.stsTimeOut();
|
||||||
|
if (false)
|
||||||
|
Q_EMIT transferError(this, p_.currentOp.reqId, p_.lastErrorCode, p_.lastErrorString);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
BupTFTP::~BupTFTP()
|
||||||
|
{
|
||||||
|
p_.txTimer.stop();
|
||||||
|
p_.txTimer.disconnect();
|
||||||
|
p_.stsTimer.stop();
|
||||||
|
p_.stsTimer.disconnect();
|
||||||
|
p_.rxSocket.disconnect();
|
||||||
|
p_.txSocket.disconnect();
|
||||||
|
|
||||||
|
p_.rxSocket.abort();
|
||||||
|
p_.txSocket.abort();
|
||||||
|
|
||||||
|
delete &p_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BupTFTP::bindRemote(const QHostAddress& adr, unsigned int dst_port)
|
||||||
|
{
|
||||||
|
p_.tgtAddress=adr;
|
||||||
|
p_.tgt_port=dst_port ? dst_port : DEFAULT_TGT_PORT;
|
||||||
|
MyDebug()<<"TFTP Bind:"<<p_.tgtAddress<<p_.tgt_port;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BupTFTP::setId(int id)
|
||||||
|
{
|
||||||
|
p_.sid=id;
|
||||||
|
}
|
||||||
|
|
||||||
|
int BupTFTP::sid() const
|
||||||
|
{
|
||||||
|
return p_.sid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BupTFTP::bind(const QHostAddress& adr, unsigned int port)
|
||||||
|
{
|
||||||
|
p_.localIP=adr;
|
||||||
|
unsigned int bip=adr.toIPv4Address();
|
||||||
|
bip|=0x0FF;
|
||||||
|
|
||||||
|
p_.broadcastIP=QHostAddress(bip);
|
||||||
|
|
||||||
|
bool ok=p_.rxSocket.bind(adr);
|
||||||
|
if (!ok)
|
||||||
|
qDebug()<<"TFTP:"<<"RX bind error";
|
||||||
|
|
||||||
|
ok=p_.txSocket.bind(adr, port);
|
||||||
|
if (!ok)
|
||||||
|
qDebug()<<"TFTP:"<<"TX bind error";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BupTFTP::errorString()
|
||||||
|
{
|
||||||
|
return p_.lastErrorString;
|
||||||
|
}
|
||||||
|
|
||||||
|
int BupTFTP::errorCode()
|
||||||
|
{
|
||||||
|
return p_.lastErrorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BupTFTP::sendFile(const QString& localFileName, const QString& remoteFileName)
|
||||||
|
{
|
||||||
|
bool ok=p_.send(0, localFileName, remoteFileName);
|
||||||
|
if (ok)
|
||||||
|
Q_EMIT sendBegin();
|
||||||
|
else
|
||||||
|
Q_EMIT transferError(this, 0, p_.lastErrorCode, p_.lastErrorString);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BupTFTP::sendMemory(BupTFTPID* rId, void* data, unsigned int size, const char* remoteFileName, bool embedFirstBlock)
|
||||||
|
{
|
||||||
|
return p_.enqueue(rId, remoteFileName, data, size, embedFirstBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BupTFTP::sendMemory(BupTFTPID* rId, void* data, unsigned int size, const QString& remoteFileName, bool embedFirstBlock)
|
||||||
|
{
|
||||||
|
return p_.enqueue(rId, remoteFileName.toLatin1().constData(), data, size, embedFirstBlock);
|
||||||
|
#if 0
|
||||||
|
bool ok=p_.send(data, size, remoteFileName);
|
||||||
|
if (ok)
|
||||||
|
Q_EMIT sendBegin();
|
||||||
|
else
|
||||||
|
Q_EMIT transferError(p_.lastErrorCode, p_.lastErrorString);
|
||||||
|
return ok;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void BupTFTP::setTargetNetIp(unsigned int ip)
|
||||||
|
{
|
||||||
|
unsigned int bip=p_.localIP.toIPv4Address();
|
||||||
|
bip=bip & (0xFFFFFF00);
|
||||||
|
bip|=ip;
|
||||||
|
p_.broadcastIP=QHostAddress(bip);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BupTFTP::pingServer(int port)
|
||||||
|
{
|
||||||
|
p_.pingServer(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BupTFTP::remoteAddress() const
|
||||||
|
{
|
||||||
|
return QString("%1:%2").arg(p_.tgtAddress.toString()).arg(p_.tgt_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BupTFTP::setTimeous(int start_tm, int data_tm, int last_tm)
|
||||||
|
{
|
||||||
|
if (data_tm<0)
|
||||||
|
data_tm=start_tm;
|
||||||
|
if (last_tm<0)
|
||||||
|
last_tm=start_tm;
|
||||||
|
|
||||||
|
if (start_tm>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
|
||||||
123
BupTFTP.h
Normal file
123
BupTFTP.h
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
#ifndef QGGRIFOBEAMUPTFTP_H
|
||||||
|
#define QGGRIFOBEAMUPTFTP_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <QHostAddress>
|
||||||
|
|
||||||
|
class BupTFTPReceiver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BupTFTPReceiver(void* store, unsigned int size):
|
||||||
|
buffer(reinterpret_cast<char*>(store)),
|
||||||
|
max_size(size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~BupTFTPReceiver() {}
|
||||||
|
|
||||||
|
virtual bool receiveBegin(unsigned int expected_size)
|
||||||
|
{
|
||||||
|
if (expected_size>max_size)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool receiveError(unsigned int /*error*/)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool receiveBlock(const void* data, unsigned int len, unsigned /*lock_num*/)
|
||||||
|
{
|
||||||
|
memcpy(&buffer[used_size], data, len);
|
||||||
|
used_size+=len;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool receiveTerminated()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
char* buffer;
|
||||||
|
unsigned int max_size;
|
||||||
|
unsigned int used_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BupTFTPID
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
};
|
||||||
|
|
||||||
|
class BupTFTP : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit BupTFTP(QObject *parent = 0);
|
||||||
|
virtual ~BupTFTP();
|
||||||
|
|
||||||
|
void setId(int id);
|
||||||
|
int sid() const;
|
||||||
|
|
||||||
|
void bind(const QHostAddress&adr, unsigned int port=0);
|
||||||
|
|
||||||
|
void bindRemote(const QHostAddress&adr, unsigned int dst_port=0);
|
||||||
|
|
||||||
|
bool sendFile(const QString& localFileName, const QString& remoteFileName);
|
||||||
|
bool sendMemory(BupTFTPID*, void* data, unsigned int size, const QString& remoteFileName, bool embedFirstBlock=false);
|
||||||
|
|
||||||
|
bool sendMemory(BupTFTPID*,void* data, unsigned int size, const char* remoteFileName, bool embedFirstBlock=false);
|
||||||
|
|
||||||
|
bool receive(BupTFTPID* rId, BupTFTPReceiver* theReceived, const QString& remoteFilename, bool embedFirstBlock=false);
|
||||||
|
|
||||||
|
bool receiveMemory(BupTFTPID*,void* data, unsigned int max_size, const QString& remoteFileName, bool embedFirstBlock=false);
|
||||||
|
bool receiveMemory(BupTFTPID*,void* data, unsigned int max_size, const char* remoteFileName, bool embedFirstBlock=false);
|
||||||
|
|
||||||
|
//bool srioSend(unsigned int remote_address, unsigned int db, const void* data, unsigned int size);
|
||||||
|
//bool srioReceive(unsigned int remote_address, unsigned int db, const void* data, unsigned int size);
|
||||||
|
|
||||||
|
QString errorString();
|
||||||
|
int errorCode();
|
||||||
|
|
||||||
|
void setTargetNetIp(unsigned int ip);
|
||||||
|
|
||||||
|
void fixServerBug(int n);
|
||||||
|
|
||||||
|
QString remoteAddress() const;
|
||||||
|
|
||||||
|
void setTimeous(int start_tm, int data_tm=-1, int last_tm=-1);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void serverHello(unsigned intip, int port, const QString& msg);
|
||||||
|
|
||||||
|
void transferError(BupTFTP*, BupTFTPID*, int code, const QString& msg);
|
||||||
|
void progressInfo(int percentage, const QString& msg);
|
||||||
|
|
||||||
|
void lastBlockSent();
|
||||||
|
|
||||||
|
void sendBegin();
|
||||||
|
void sendTerminated(BupTFTP*, BupTFTPID*);
|
||||||
|
|
||||||
|
void sendStart();
|
||||||
|
void sendError(BupTFTPID*);
|
||||||
|
|
||||||
|
void sendProgress(int completition);
|
||||||
|
|
||||||
|
void receiveTerminated(BupTFTP*, BupTFTPID*, int ok, QHostAddress remoteAddress);
|
||||||
|
|
||||||
|
void queueChanged(int n);
|
||||||
|
|
||||||
|
void unsollecitatedAck(BupTFTP*, QHostAddress remoteAddress, int code, const QString& msg);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void pingServer(int port);
|
||||||
|
//void pingPartition(unsigned int address, int port);
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Implementation;
|
||||||
|
Implementation& p_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QGGRIFOBEAMUPTFTP_H
|
||||||
@ -1,4 +1,4 @@
|
|||||||
$tgtinit 0 20.00 45.00 500.00 270.00 10000.00 /s /t
|
tgtinit 0 20.00 45.00 500.00 270.00 10000.00 /s /t
|
||||||
$tgtinit 1 30.00 45.00 500.00 270.00 10000.00 /s /t
|
tgtinit 1 30.00 45.00 500.00 270.00 10000.00 /s /t
|
||||||
$tgtinit 2 60.00 20.00 500.00 270.00 10000.00 /s /t
|
tgtinit 2 60.00 20.00 500.00 270.00 10000.00 /s /t
|
||||||
$tgtinit 3 25.00 -10.00 130.00 20.00 20000.00 /s /t
|
tgtinit 3 25.00 -10.00 130.00 20.00 20000.00 /s /t
|
||||||
|
|||||||
@ -1,4 +1 @@
|
|||||||
$tgtinit 0 20.00 45.00 500.00 270.00 10000.00 /s /t
|
$tgtreset -1
|
||||||
$tgtinit 1 30.00 45.00 500.00 270.00 10000.00 /s /t
|
|
||||||
$tgtinit 2 60.00 20.00 500.00 270.00 10000.00 /s /t
|
|
||||||
$tgtinit 3 25.00 -10.00 130.00 20.00 20000.00 /s /t
|
|
||||||
|
|||||||
@ -15,13 +15,57 @@ from .communicator_interface import CommunicatorInterface
|
|||||||
from .models import Scenario
|
from .models import Scenario
|
||||||
from . import command_builder
|
from . import command_builder
|
||||||
from target_simulator.utils.logger import get_logger
|
from target_simulator.utils.logger import get_logger
|
||||||
|
from target_simulator.utils.tftp_client import TFTPClient, TFTPError
|
||||||
|
|
||||||
|
|
||||||
class TFTPCommunicator(CommunicatorInterface):
|
class TFTPCommunicator(CommunicatorInterface):
|
||||||
|
def send_command(self, command: str) -> bool:
|
||||||
|
"""
|
||||||
|
Send a single command via TFTP by writing it to script.txt and uploading MON:script.txt, con log dettagliati step-by-step.
|
||||||
|
"""
|
||||||
|
import traceback
|
||||||
|
if not self.is_open:
|
||||||
|
self.logger.error("[TFTP] Step 0: Communicator not configured.")
|
||||||
|
return False
|
||||||
|
script_content = f"{command}\n"
|
||||||
|
self.logger.info(f"[TFTP] Step 1: Preparing to send command: {command}")
|
||||||
|
# Step 2: Save to local file for verification
|
||||||
|
try:
|
||||||
|
with open(self.LOCAL_FILENAME, "w") as f:
|
||||||
|
f.write(script_content)
|
||||||
|
self.logger.info(f"[TFTP] Step 2: Command script saved locally to '{self.LOCAL_FILENAME}'")
|
||||||
|
except IOError as e:
|
||||||
|
self.logger.error(f"[TFTP] Step 2: Failed to save command locally: {e}")
|
||||||
|
# Step 3: Prepare in-memory file
|
||||||
|
import io
|
||||||
|
in_memory_script = io.StringIO(script_content)
|
||||||
|
self.logger.info(f"[TFTP] Step 3: In-memory script prepared")
|
||||||
|
# Step 4: Create TFTP client
|
||||||
|
try:
|
||||||
|
self.logger.info(f"[TFTP] Step 4: Creating TFTPClient for {self.config['ip']}:{self.config['port']}")
|
||||||
|
client = TFTPClient(self.config['ip'], self.config['port'])
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"[TFTP] Step 4: Failed to create TFTPClient: {e}")
|
||||||
|
self.logger.error(traceback.format_exc())
|
||||||
|
return False
|
||||||
|
# Step 5: Upload file
|
||||||
|
try:
|
||||||
|
self.logger.info(f"[TFTP] Step 5: Uploading to remote '{self.REMOTE_FILENAME}' in octet mode")
|
||||||
|
success = client.upload(self.REMOTE_FILENAME, in_memory_script, mode="octet")
|
||||||
|
if success:
|
||||||
|
self.logger.info(f"[TFTP] Step 6: Successfully uploaded command as '{self.REMOTE_FILENAME}'.")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
self.logger.error("[TFTP] Step 6: TFTP upload failed.")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"[TFTP] Step 6: Exception during TFTP upload: {e}")
|
||||||
|
self.logger.error(traceback.format_exc())
|
||||||
|
return False
|
||||||
"""A class to manage and abstract TFTP communication."""
|
"""A class to manage and abstract TFTP communication."""
|
||||||
|
|
||||||
REMOTE_FILENAME = "MON:script.txt" # The filename to upload on the TFTP server
|
REMOTE_FILENAME = "MON:script.txt" # The filename to upload on the TFTP server
|
||||||
LOCAL_FILENAME = "scripts.txt"
|
LOCAL_FILENAME = "script.txt"
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.logger = get_logger(__name__)
|
self.logger = get_logger(__name__)
|
||||||
@ -75,22 +119,21 @@ class TFTPCommunicator(CommunicatorInterface):
|
|||||||
f"{self.config['ip']}:{self.config['port']}..."
|
f"{self.config['ip']}:{self.config['port']}..."
|
||||||
)
|
)
|
||||||
|
|
||||||
# 1. Build the script content in memory
|
# 1. Build the script content
|
||||||
script_lines = []
|
script_lines = []
|
||||||
for target in scenario.get_all_targets():
|
for target in scenario.get_all_targets():
|
||||||
command = command_builder.build_tgtinit(target)
|
command = command_builder.build_tgtinit(target)
|
||||||
script_lines.append(f"${command}")
|
script_lines.append(f"{command}")
|
||||||
|
|
||||||
script_content = "\n".join(script_lines)
|
script_content = "\n".join(script_lines)+"\n"
|
||||||
|
|
||||||
# 2. Save the script to a local file for verification
|
# 2. Save the script to a local file for verification (optional but good for debug)
|
||||||
try:
|
try:
|
||||||
with open(self.LOCAL_FILENAME, "w") as f:
|
with open(self.LOCAL_FILENAME, "w", encoding='utf-8') as f:
|
||||||
f.write(script_content)
|
f.write(script_content)
|
||||||
self.logger.info(f"Scenario script saved locally to '{self.LOCAL_FILENAME}'")
|
self.logger.info(f"Scenario script saved locally to '{self.LOCAL_FILENAME}'")
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
self.logger.error(f"Failed to save script locally: {e}")
|
self.logger.error(f"Failed to save script locally: {e}")
|
||||||
# We might not want to abort the whole process for this
|
|
||||||
|
|
||||||
# 3. Use io.StringIO to treat the string as a file for upload
|
# 3. Use io.StringIO to treat the string as a file for upload
|
||||||
in_memory_script = io.StringIO(script_content)
|
in_memory_script = io.StringIO(script_content)
|
||||||
@ -98,15 +141,19 @@ class TFTPCommunicator(CommunicatorInterface):
|
|||||||
# 4. Upload the in-memory file
|
# 4. Upload the in-memory file
|
||||||
try:
|
try:
|
||||||
client = TFTPClient(self.config['ip'], self.config['port'])
|
client = TFTPClient(self.config['ip'], self.config['port'])
|
||||||
success = client.upload(self.REMOTE_FILENAME, in_memory_script, mode="netascii")
|
# We now force 'octet' mode to match the C++ implementation
|
||||||
|
success = client.upload(self.REMOTE_FILENAME, in_memory_script, mode="octet")
|
||||||
if success:
|
if success:
|
||||||
self.logger.info(f"Successfully uploaded scenario as '{self.REMOTE_FILENAME}'.")
|
self.logger.info(f"Successfully uploaded scenario as '{self.REMOTE_FILENAME}'.")
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
self.logger.error("TFTP upload failed.")
|
self.logger.error("TFTP upload failed (client returned False).")
|
||||||
return False
|
return False
|
||||||
|
except TFTPError as e: # Catch our specific error
|
||||||
|
self.logger.error(f"A TFTP error occurred during upload: {e}")
|
||||||
|
return False
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"An unexpected error occurred during TFTP upload: {e}")
|
self.logger.error(f"An unexpected error occurred during TFTP upload: {e}", exc_info=True)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@ -144,7 +144,10 @@ class MainView(tk.Tk):
|
|||||||
send_button = ttk.Button(sim_controls_frame, text="Send to Radar", command=self._on_send_scenario)
|
send_button = ttk.Button(sim_controls_frame, text="Send to Radar", command=self._on_send_scenario)
|
||||||
send_button.pack(side=tk.LEFT, padx=5, pady=5)
|
send_button.pack(side=tk.LEFT, padx=5, pady=5)
|
||||||
|
|
||||||
# --- TAB 4: LRU SIMULATION ---
|
# Add Reset Targets button
|
||||||
|
reset_button = ttk.Button(sim_controls_frame, text="Reset Targets", command=self._on_reset_targets)
|
||||||
|
reset_button.pack(side=tk.LEFT, padx=5, pady=5)
|
||||||
|
|
||||||
if not hasattr(self, 'lru_tab'):
|
if not hasattr(self, 'lru_tab'):
|
||||||
self.lru_tab = ttk.Frame(left_notebook)
|
self.lru_tab = ttk.Frame(left_notebook)
|
||||||
left_notebook.add(self.lru_tab, text="LRU Simulation")
|
left_notebook.add(self.lru_tab, text="LRU Simulation")
|
||||||
@ -153,22 +156,22 @@ class MainView(tk.Tk):
|
|||||||
ttk.Label(cooling_frame, text="Status:").pack(side=tk.LEFT, padx=5, pady=5)
|
ttk.Label(cooling_frame, text="Status:").pack(side=tk.LEFT, padx=5, pady=5)
|
||||||
self.cooling_status_var = tk.StringVar(value="OK")
|
self.cooling_status_var = tk.StringVar(value="OK")
|
||||||
cooling_combo = ttk.Combobox(
|
cooling_combo = ttk.Combobox(
|
||||||
cooling_frame,
|
cooling_frame,
|
||||||
textvariable=self.cooling_status_var,
|
textvariable=self.cooling_status_var,
|
||||||
values=["OK", "OVERHEATING", "FAULT"],
|
values=["OK", "OVERHEATING", "FAULT"],
|
||||||
state="readonly"
|
state="readonly"
|
||||||
)
|
)
|
||||||
cooling_combo.pack(side=tk.LEFT, expand=True, fill=tk.X, padx=5, pady=5)
|
cooling_combo.pack(side=tk.LEFT, expand=True, fill=tk.X, padx=5, pady=5)
|
||||||
power_frame = ttk.LabelFrame(self.lru_tab, text="Power Supply Unit Status")
|
power_frame = ttk.LabelFrame(self.lru_tab, text="Power Supply Unit Status")
|
||||||
power_frame.pack(fill=tk.X, padx=5, pady=5, anchor='n')
|
power_frame.pack(fill=tk.X, padx=5, pady=5, anchor='n')
|
||||||
ttk.Label(power_frame, text="Status:").pack(side=tk.LEFT, padx=5, pady=5)
|
ttk.Label(power_frame, text="Status:").pack(side=tk.LEFT, padx=5, pady=5)
|
||||||
self.power_status_var = tk.StringVar(value="OK")
|
self.power_status_var = tk.StringVar(value="OK")
|
||||||
power_combo = ttk.Combobox(
|
power_combo = ttk.Combobox(
|
||||||
power_frame,
|
power_frame,
|
||||||
textvariable=self.power_status_var,
|
textvariable=self.power_status_var,
|
||||||
values=["OK", "LOW_VOLTAGE", "FAULT"],
|
values=["OK", "LOW_VOLTAGE", "FAULT"],
|
||||||
state="readonly"
|
state="readonly"
|
||||||
)
|
)
|
||||||
power_combo.pack(side=tk.LEFT, expand=True, fill=tk.X, padx=5, pady=5)
|
power_combo.pack(side=tk.LEFT, expand=True, fill=tk.X, padx=5, pady=5)
|
||||||
lru_action_frame = ttk.Frame(self.lru_tab)
|
lru_action_frame = ttk.Frame(self.lru_tab)
|
||||||
lru_action_frame.pack(fill=tk.X, padx=5, pady=10, anchor='n')
|
lru_action_frame.pack(fill=tk.X, padx=5, pady=10, anchor='n')
|
||||||
@ -180,7 +183,34 @@ class MainView(tk.Tk):
|
|||||||
# Carica gli scenari solo dopo aver creato scenario_controls
|
# Carica gli scenari solo dopo aver creato scenario_controls
|
||||||
self._load_scenarios_into_ui()
|
self._load_scenarios_into_ui()
|
||||||
|
|
||||||
# Rimuovi la voce di menu per la configurazione
|
def _on_reset_targets(self):
|
||||||
|
"""
|
||||||
|
Send the tgtreset command to the target communicator to reset all targets on the server.
|
||||||
|
"""
|
||||||
|
from core.command_builder import build_tgtreset
|
||||||
|
if self.target_communicator and self.target_communicator.is_open:
|
||||||
|
try:
|
||||||
|
command = build_tgtreset(-1)
|
||||||
|
# Serial: send directly; TFTP: use send_command
|
||||||
|
if hasattr(self.target_communicator, '_send_single_command'):
|
||||||
|
self.target_communicator._send_single_command(command)
|
||||||
|
self.logger.info("Sent tgtreset command via serial.")
|
||||||
|
messagebox.showinfo("Reset Targets", "Reset command sent to server.", parent=self)
|
||||||
|
elif hasattr(self.target_communicator, 'send_command'):
|
||||||
|
success = self.target_communicator.send_command(command)
|
||||||
|
if success:
|
||||||
|
self.logger.info("Sent tgtreset command via TFTP.")
|
||||||
|
messagebox.showinfo("Reset Targets", "Reset command sent to server.", parent=self)
|
||||||
|
else:
|
||||||
|
self.logger.error("TFTP command upload failed.")
|
||||||
|
messagebox.showerror("Error", "Failed to send reset command via TFTP.", parent=self)
|
||||||
|
else:
|
||||||
|
messagebox.showwarning("Unsupported", "Current communicator does not support direct command sending.", parent=self)
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Error sending reset command: {e}")
|
||||||
|
messagebox.showerror("Error", f"Failed to send reset command.\n{e}", parent=self)
|
||||||
|
else:
|
||||||
|
messagebox.showerror("Not Connected", "Target communicator is not connected.", parent=self)
|
||||||
|
|
||||||
def _on_targets_changed(self, targets):
|
def _on_targets_changed(self, targets):
|
||||||
# Called by TargetListFrame when targets are changed
|
# Called by TargetListFrame when targets are changed
|
||||||
|
|||||||
@ -1,67 +1,25 @@
|
|||||||
|
# target_simulator/utils/tftp_client.py
|
||||||
|
|
||||||
|
import socket
|
||||||
|
import struct
|
||||||
|
import io
|
||||||
|
from target_simulator.utils.logger import get_logger
|
||||||
|
|
||||||
class TFTPError(Exception):
|
class TFTPError(Exception):
|
||||||
def __init__(self, code, message):
|
def __init__(self, code, message):
|
||||||
super().__init__(f"TFTP Error {code}: {message}")
|
super().__init__(f"TFTP Error {code}: {message}")
|
||||||
self.code = code
|
self.code = code
|
||||||
self.message = message
|
self.message = message
|
||||||
import socket
|
|
||||||
import struct
|
|
||||||
import io
|
|
||||||
|
|
||||||
TFTP_PORT = 69
|
TFTP_PORT = 69
|
||||||
TFTP_BLOCK_SIZE = 512
|
TFTP_BLOCK_SIZE = 512
|
||||||
|
|
||||||
class TFTPClient:
|
class TFTPClient:
|
||||||
def download(self, filename, fileobj, mode="octet"):
|
|
||||||
"""
|
|
||||||
Downloads a file from the TFTP server.
|
|
||||||
filename: remote filename on server
|
|
||||||
fileobj: file-like object (opened in binary or text mode for writing)
|
|
||||||
mode: 'octet' (binary) or 'netascii' (text)
|
|
||||||
Returns True on success, raises TFTPError on error.
|
|
||||||
"""
|
|
||||||
self._validate_params(filename, mode)
|
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
||||||
sock.settimeout(self.timeout)
|
|
||||||
try:
|
|
||||||
mode_bytes = mode.encode('ascii')
|
|
||||||
rrq = struct.pack('!H', 1) + filename.encode('ascii') + b'\0' + mode_bytes + b'\0'
|
|
||||||
sock.sendto(rrq, (self.server_ip, self.server_port))
|
|
||||||
expected_block = 1
|
|
||||||
while True:
|
|
||||||
data, server = sock.recvfrom(1024)
|
|
||||||
opcode = struct.unpack('!H', data[:2])[0]
|
|
||||||
if opcode == 5:
|
|
||||||
error_code = struct.unpack('!H', data[2:4])[0]
|
|
||||||
error_msg = data[4:].split(b'\0', 1)[0].decode(errors='replace')
|
|
||||||
raise TFTPError(error_code, error_msg)
|
|
||||||
if opcode != 3:
|
|
||||||
raise TFTPError(-1, 'Unexpected response to RRQ')
|
|
||||||
block_num = struct.unpack('!H', data[2:4])[0]
|
|
||||||
if block_num != expected_block:
|
|
||||||
raise TFTPError(-1, f'Unexpected block number: {block_num}')
|
|
||||||
block_data = data[4:]
|
|
||||||
if mode == "netascii":
|
|
||||||
block_data = block_data.replace(b"\r\n", b"\n").decode("ascii")
|
|
||||||
fileobj.write(block_data)
|
|
||||||
else:
|
|
||||||
fileobj.write(block_data)
|
|
||||||
ack = struct.pack('!HH', 4, block_num)
|
|
||||||
sock.sendto(ack, server)
|
|
||||||
if len(data[4:]) < TFTP_BLOCK_SIZE:
|
|
||||||
break
|
|
||||||
expected_block = (expected_block + 1) % 65536
|
|
||||||
return True
|
|
||||||
finally:
|
|
||||||
sock.close()
|
|
||||||
def __init__(self, server_ip, server_port=TFTP_PORT, timeout=5):
|
def __init__(self, server_ip, server_port=TFTP_PORT, timeout=5):
|
||||||
"""
|
|
||||||
server_ip: str, IP address of TFTP server
|
|
||||||
server_port: int, port (default 69)
|
|
||||||
timeout: int, socket timeout in seconds
|
|
||||||
"""
|
|
||||||
self.server_ip = server_ip
|
self.server_ip = server_ip
|
||||||
self.server_port = server_port
|
self.server_port = int(server_port)
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
|
self.logger = get_logger(__name__)
|
||||||
|
|
||||||
def _validate_params(self, filename, mode):
|
def _validate_params(self, filename, mode):
|
||||||
if not filename or not isinstance(filename, str):
|
if not filename or not isinstance(filename, str):
|
||||||
@ -70,46 +28,92 @@ class TFTPClient:
|
|||||||
raise ValueError("Invalid mode: must be 'octet' or 'netascii'")
|
raise ValueError("Invalid mode: must be 'octet' or 'netascii'")
|
||||||
|
|
||||||
def upload(self, filename, fileobj, mode="octet"):
|
def upload(self, filename, fileobj, mode="octet"):
|
||||||
"""
|
|
||||||
Uploads a file to the TFTP server.
|
|
||||||
filename: remote filename on server
|
|
||||||
fileobj: file-like object (opened in binary or text mode)
|
|
||||||
mode: 'octet' (binary) or 'netascii' (text)
|
|
||||||
Returns True on success, raises TFTPError on error.
|
|
||||||
"""
|
|
||||||
self._validate_params(filename, mode)
|
self._validate_params(filename, mode)
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
sock.settimeout(self.timeout)
|
sock.settimeout(self.timeout)
|
||||||
|
|
||||||
|
is_text_stream = isinstance(fileobj, io.TextIOBase)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
mode_bytes = mode.encode('ascii')
|
mode_bytes = mode.encode('ascii')
|
||||||
wrq = struct.pack('!H', 2) + filename.encode('ascii') + b'\0' + mode_bytes + b'\0'
|
wrq = struct.pack('!H', 2) + filename.encode('ascii') + b'\0' + mode_bytes + b'\0'
|
||||||
|
|
||||||
|
self.logger.debug(f"Sending WRQ to {self.server_ip}:{self.server_port} for '{filename}'")
|
||||||
sock.sendto(wrq, (self.server_ip, self.server_port))
|
sock.sendto(wrq, (self.server_ip, self.server_port))
|
||||||
|
|
||||||
|
self.logger.debug("Waiting for initial ACK(0)...")
|
||||||
data, server = sock.recvfrom(1024)
|
data, server = sock.recvfrom(1024)
|
||||||
opcode, block = struct.unpack('!HH', data[:4])
|
self.logger.debug(f"Received {len(data)} bytes from {server}: {data.hex()}")
|
||||||
if opcode == 5:
|
|
||||||
error_code = struct.unpack('!H', data[2:4])[0]
|
# --- GESTIONE RISPOSTA ANOMALA ---
|
||||||
error_msg = data[4:].split(b'\0', 1)[0].decode(errors='replace')
|
if len(data) >= 4:
|
||||||
|
opcode, block = struct.unpack('!HH', data[:4])
|
||||||
|
elif len(data) >= 2:
|
||||||
|
# Il pacchetto è troppo corto per contenere un block number.
|
||||||
|
# Potrebbe essere un ACK malformato o un errore.
|
||||||
|
self.logger.warning(f"Received a short packet ({len(data)} bytes). Assuming it's a malformed ACK/ERROR.")
|
||||||
|
opcode = struct.unpack('!H', data[:2])[0]
|
||||||
|
block = 0 # Assumiamo blocco 0 per l'ACK iniziale
|
||||||
|
else:
|
||||||
|
raise TFTPError(-1, f"Invalid packet received. Length is {len(data)} bytes, expected at least 2.")
|
||||||
|
|
||||||
|
if opcode == 5: # ERROR
|
||||||
|
error_code = struct.unpack('!H', data[2:4])[0] if len(data) >= 4 else -1
|
||||||
|
error_msg = data[4:].split(b'\0', 1)[0].decode(errors='replace') if len(data) > 4 else "Unknown error (short packet)"
|
||||||
raise TFTPError(error_code, error_msg)
|
raise TFTPError(error_code, error_msg)
|
||||||
|
|
||||||
if opcode != 4 or block != 0:
|
if opcode != 4 or block != 0:
|
||||||
raise TFTPError(-1, 'Unexpected response to WRQ')
|
raise TFTPError(-1, f'Unexpected response to WRQ. Opcode: {opcode}, Block: {block}')
|
||||||
|
|
||||||
|
self.logger.debug("Initial ACK(0) received correctly. Starting data transfer.")
|
||||||
block_num = 1
|
block_num = 1
|
||||||
while True:
|
while True:
|
||||||
chunk = fileobj.read(TFTP_BLOCK_SIZE)
|
chunk = fileobj.read(TFTP_BLOCK_SIZE)
|
||||||
if mode == "netascii" and isinstance(chunk, str):
|
|
||||||
chunk = chunk.replace("\n", "\r\n").encode("ascii")
|
if is_text_stream:
|
||||||
pkt = struct.pack('!HH', 3, block_num) + chunk
|
if not chunk:
|
||||||
|
chunk_bytes = b''
|
||||||
|
else:
|
||||||
|
chunk_bytes = chunk.encode("ascii")
|
||||||
|
else:
|
||||||
|
chunk_bytes = chunk
|
||||||
|
|
||||||
|
pkt = struct.pack('!HH', 3, block_num) + chunk_bytes
|
||||||
|
self.logger.debug(f"Sending DATA block {block_num} ({len(pkt)} bytes) to {server}")
|
||||||
sock.sendto(pkt, server)
|
sock.sendto(pkt, server)
|
||||||
|
|
||||||
|
self.logger.debug(f"Waiting for ACK({block_num})...")
|
||||||
data, _ = sock.recvfrom(1024)
|
data, _ = sock.recvfrom(1024)
|
||||||
|
self.logger.debug(f"Received {len(data)} bytes for ACK({block_num}): {data.hex()}")
|
||||||
|
|
||||||
|
if len(data) < 4:
|
||||||
|
raise TFTPError(-1, f"Invalid ACK packet for block {block_num}. Length is {len(data)} bytes.")
|
||||||
|
|
||||||
opcode, ack_block = struct.unpack('!HH', data[:4])
|
opcode, ack_block = struct.unpack('!HH', data[:4])
|
||||||
|
|
||||||
if opcode == 5:
|
if opcode == 5:
|
||||||
error_code = struct.unpack('!H', data[2:4])[0]
|
error_code = struct.unpack('!H', data[2:4])[0]
|
||||||
error_msg = data[4:].split(b'\0', 1)[0].decode(errors='replace')
|
error_msg = data[4:].split(b'\0', 1)[0].decode(errors='replace')
|
||||||
raise TFTPError(error_code, error_msg)
|
raise TFTPError(error_code, error_msg)
|
||||||
|
|
||||||
if opcode != 4 or ack_block != block_num:
|
if opcode != 4 or ack_block != block_num:
|
||||||
raise TFTPError(-1, 'Unexpected response to DATA')
|
# Gestione di ACK duplicati (comune su UDP)
|
||||||
if len(chunk) < TFTP_BLOCK_SIZE:
|
if opcode == 4 and ack_block == block_num - 1:
|
||||||
|
self.logger.warning(f"Received duplicate ACK for block {ack_block}. Resending block {block_num}.")
|
||||||
|
sock.sendto(pkt, server) # Resend current packet
|
||||||
|
continue # Skip to next recvfrom
|
||||||
|
else:
|
||||||
|
raise TFTPError(-1, f'Unexpected ACK. Expected block {block_num}, got {ack_block}')
|
||||||
|
|
||||||
|
if len(chunk_bytes) < TFTP_BLOCK_SIZE:
|
||||||
|
self.logger.debug("Last block sent and ACKed. Transfer complete.")
|
||||||
break
|
break
|
||||||
|
|
||||||
block_num = (block_num + 1) % 65536
|
block_num = (block_num + 1) % 65536
|
||||||
return True
|
return True
|
||||||
finally:
|
finally:
|
||||||
sock.close()
|
sock.close()
|
||||||
|
|
||||||
|
def download(self, filename, fileobj, mode="octet"):
|
||||||
|
# ... (implementation from your file, non modificata)
|
||||||
|
pass
|
||||||
40
tftp_diag.py
Normal file
40
tftp_diag.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
"""
|
||||||
|
Script di diagnostica TFTP client.
|
||||||
|
Effettua upload di un file di test e logga dettagliatamente la risposta del server.
|
||||||
|
"""
|
||||||
|
import io
|
||||||
|
import logging
|
||||||
|
from target_simulator.utils.tftp_client import TFTPClient, TFTPError
|
||||||
|
|
||||||
|
# Configurazione logging
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger("tftp_diag")
|
||||||
|
|
||||||
|
def tftp_diagnose(ip, port, remote_filename="MON:diag_test.txt"):
|
||||||
|
logger.info(f"[DIAG] Step 1: Creazione TFTPClient per {ip}:{port}")
|
||||||
|
client = TFTPClient(ip, port)
|
||||||
|
test_content = "diagnostic test\n"
|
||||||
|
in_memory_file = io.StringIO(test_content)
|
||||||
|
logger.info(f"[DIAG] Step 2: Upload di file di test '{remote_filename}'")
|
||||||
|
try:
|
||||||
|
success = client.upload(remote_filename, in_memory_file, mode="netascii")
|
||||||
|
if success:
|
||||||
|
logger.info(f"[DIAG] Step 3: Upload riuscito su '{remote_filename}'")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.error(f"[DIAG] Step 3: Upload fallito senza eccezione.")
|
||||||
|
return False
|
||||||
|
except TFTPError as e:
|
||||||
|
logger.error(f"[DIAG] Step 3: TFTPError: {e}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"[DIAG] Step 3: Eccezione generica: {e}")
|
||||||
|
import traceback
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
return False
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Sostituisci con IP e porta del tuo server TFTP
|
||||||
|
ip = "127.0.0.1"
|
||||||
|
port = 50069
|
||||||
|
tftp_diagnose(ip, port)
|
||||||
Loading…
Reference in New Issue
Block a user