674 lines
16 KiB
C++
674 lines
16 KiB
C++
/*
|
|
* bsk_tftpd.cpp
|
|
*
|
|
* Created on: 25/ago/2016
|
|
* Author: chessaa
|
|
*/
|
|
|
|
#include "bsk_tftpd.h"
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
/*
|
|
* Trivial File Transfer Protocol (IEN-133)
|
|
*/
|
|
#define TFTP_SEGSIZE 512 /* data segment size */
|
|
//#define TFTP_MAXSEGSIZE 16384 /* data segment size */
|
|
#define TFTP_MAXSEGSIZE 1428 /*16384 */ /* data segment size */
|
|
#define TFTP_MINSEGSIZE 8 /* data segment size */
|
|
/*
|
|
* Packet types.
|
|
*/
|
|
#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 */
|
|
|
|
|
|
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 */
|
|
} th_u;
|
|
} tftphdr_t;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
tftphdr_t header;
|
|
char th_data[2]; /* 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);
|
|
}
|
|
|
|
#define th_block th_u.tu_block
|
|
#define th_code th_u.tu_code
|
|
#define th_stuff th_u.tu_stuff
|
|
#define th_msg th_data
|
|
|
|
#define TFTP_DATA_HEADERSIZE ( offsetof (struct tftphdr, th_data ) )
|
|
#define TFTP_ACK_HEADERSIZE ( offsetof (struct tftphdr, th_block ) \
|
|
+ sizeof ( ((struct tftphdr *) (0))->th_block) )
|
|
|
|
/*
|
|
* Error codes.
|
|
*/
|
|
#define EUNDEF 0 /* not defined */
|
|
#define ENOTFOUND 1 /* file not found */
|
|
#define EACCESS 2 /* access violation */
|
|
#define ENOSPACE 3 /* disk full or allocation exceeded */
|
|
#define EBADOP 4 /* illegal TFTP operation */
|
|
#define EBADID 5 /* unknown transfer ID */
|
|
#define EEXISTS 6 /* file already exists */
|
|
#define ENOUSER 7 /* no such user */
|
|
#define EBADOPTION 8 /* bad option */
|
|
#define ECANCELLED 99 /* cancelled by administrator */
|
|
|
|
|
|
/*
|
|
* options
|
|
*/
|
|
#define TFTP_OPT_TSIZE "tsize"
|
|
#define TFTP_OPT_TIMEOUT "timeout"
|
|
#define TFTP_OPT_BLKSIZE "blksize"
|
|
#define TFTP_OPT_MCAST "multicast"
|
|
#define TFTP_OPT_PORT "udpport"
|
|
|
|
#define IS_OPT(s,opt) (lstrcmpi (s, opt)==0)
|
|
|
|
#define PKTSIZE TFTP_SEGSIZE+4
|
|
#define MAXPKTSIZE TFTP_MAXSEGSIZE+4
|
|
|
|
static int fake_log_delegate(bsk_tftpd_context_id_t, const char* msg, ...)
|
|
{
|
|
(void)msg;
|
|
return 0;
|
|
}
|
|
|
|
static int fake_send_delegate(bsk_tftpd_context_id_t, unsigned int, unsigned int, const void* const data, unsigned int size)
|
|
{
|
|
(void)data;
|
|
(void)size;
|
|
return 0;
|
|
}
|
|
|
|
static bsk_tftpd_receive_status_t fake_receive_delegate(bsk_tftpd_context_id_t, bsk_tftpd_receive_delegate_operation_t op, const void* const data, unsigned int block, unsigned int offset, unsigned int size_bytes)
|
|
{
|
|
static bsk_tftpd_receive_status_t reply={bsk_tftpd_ok, 0};
|
|
|
|
(void)op;
|
|
(void)data;
|
|
(void)block;
|
|
(void)offset;
|
|
(void)size_bytes;
|
|
return reply;
|
|
}
|
|
|
|
|
|
typedef enum { t_unconnected=0, t_connected, t_pending} tftp_state_t;
|
|
|
|
struct bsk_tftpd_cx_t
|
|
{
|
|
bsk_tftpd_context_info_t info;
|
|
|
|
bsk_tftpd_log_delegate_t log_delegate;
|
|
bsk_tftpd_send_delegate_t send_delegate;
|
|
bsk_tftpd_receive_delegate_t receive_delegate;
|
|
|
|
tftp_state_t state;
|
|
|
|
unsigned int client_port;
|
|
unsigned int client_address;
|
|
|
|
unsigned int block_size;
|
|
|
|
const char* busy_msg;
|
|
|
|
char filename[1024];
|
|
char optmp[1024];
|
|
char optval[1024];
|
|
unsigned long tsize;
|
|
|
|
unsigned int ack_buff[1024];
|
|
tftppkt_t* ackmsg;
|
|
unsigned int ack_len;
|
|
|
|
int force_blocksize;
|
|
};
|
|
|
|
#define MAX_CX 8
|
|
|
|
static struct bsk_tftpd_cx_t cxdb[MAX_CX];
|
|
|
|
static void validate_delegates(bsk_tftpd_context_id_t id)
|
|
{
|
|
bsk_tftpd_cx_t& cx=cxdb[id];
|
|
|
|
//static struct bsk_tftpd_cx_t cx={fake_log_delegate, fake_send_delegate, fake_receive_delegate};
|
|
if (!cx.receive_delegate)
|
|
cx.receive_delegate=fake_receive_delegate;
|
|
if (!cx.log_delegate)
|
|
cx.log_delegate=fake_log_delegate;
|
|
if (!cx.send_delegate)
|
|
cx.send_delegate=fake_send_delegate;
|
|
}
|
|
|
|
const bsk_tftpd_context_info_t* bsk_tftpd_context_info(bsk_tftpd_context_id_t id)
|
|
{
|
|
return &cxdb[id].info;
|
|
}
|
|
|
|
bsk_tftpd_context_id_t bsk_tftpd_context_initialize(bsk_tftpd_context_id_t id, const char* name, unsigned int port, bsk_tftp_cookie_t cookie)
|
|
{
|
|
static int allocated_cx;
|
|
if (id<0)
|
|
{
|
|
id=allocated_cx;
|
|
++allocated_cx;
|
|
}
|
|
cxdb[id].info.name=name;
|
|
cxdb[id].info.port=port;
|
|
cxdb[id].info.cookie=cookie;
|
|
return id;
|
|
}
|
|
|
|
void bsk_tftpd_set_receive_delegate(bsk_tftpd_context_id_t id, bsk_tftpd_receive_delegate_t receive_delegate)
|
|
{
|
|
cxdb[id].receive_delegate=receive_delegate ? receive_delegate : fake_receive_delegate;
|
|
}
|
|
|
|
void bsk_tftpd_set_send_delegate(bsk_tftpd_context_id_t id, bsk_tftpd_send_delegate_t send_delegate)
|
|
{
|
|
cxdb[id].send_delegate=send_delegate ? send_delegate : fake_send_delegate;
|
|
}
|
|
|
|
void bsk_tftpd_set_log_delegate(bsk_tftpd_context_id_t id, bsk_tftpd_log_delegate_t log_delegate)
|
|
{
|
|
cxdb[id].log_delegate=log_delegate ? log_delegate : fake_log_delegate;
|
|
}
|
|
|
|
#define LOG(msg_, ...) cx.log_delegate(id, msg_, ##__VA_ARGS__)
|
|
//#define LOG_DBG(msg_) /*cx.log_delegate msg_*/ ((void)0)
|
|
//#define LOG_DBG(msg_) cx.log_delegate msg_
|
|
#define LOG_DBG(msg_, ...) cx.log_delegate(id, msg_, ##__VA_ARGS__)
|
|
|
|
|
|
static void nakmsg(bsk_tftpd_context_id_t id, unsigned short code, const char* msg, unsigned int adr=0, unsigned int port=0)
|
|
{
|
|
const struct errmsg {
|
|
int e_code;
|
|
const char *e_msg;
|
|
} errmsgs[] = {
|
|
{ EUNDEF, "Undefined error code" },
|
|
{ ENOTFOUND, "File not found" },
|
|
{ EACCESS, "Access violation" },
|
|
{ ENOSPACE, "Disk full or allocation exceeded" },
|
|
{ EBADOP, "Illegal TFTP operation" },
|
|
{ EBADID, "Unknown transfer ID" },
|
|
{ EEXISTS, "File already exists" },
|
|
{ ENOUSER, "No such user" },
|
|
{ ECANCELLED, "Cancelled by administrator" },
|
|
{ -1, 0 }
|
|
};
|
|
|
|
if (msg==0)
|
|
{
|
|
int i;
|
|
int n=0;
|
|
for(i=0; /*i<*/errmsgs[i].e_code!=-1; ++i)
|
|
if (errmsgs[i].e_code==code)
|
|
{
|
|
n=i;
|
|
break;
|
|
}
|
|
msg=errmsgs[n].e_msg;
|
|
}
|
|
|
|
bsk_tftpd_cx_t& cx=cxdb[id];
|
|
|
|
tftp_htons(cx.ackmsg->header.th_opcode, TFTP_ERROR);
|
|
tftp_htons(cx.ackmsg->header.th_u.tu_code, code);
|
|
strcpy((char*)cx.ackmsg->th_data, msg);
|
|
if (adr==0)
|
|
adr=cx.client_address;
|
|
if (port==0)
|
|
port=cx.client_port;
|
|
|
|
cx.send_delegate(id, adr, port, cx.ackmsg, sizeof(cx.ackmsg->header)+2+strlen(msg));
|
|
LOG("%d: %u %s (%x:%d)", id, tftp_ntos(cx.ackmsg->header.th_u.tu_code), cx.ackmsg->th_data, adr, port);
|
|
}
|
|
|
|
static void nak(bsk_tftpd_context_id_t id, unsigned short code)
|
|
{
|
|
nakmsg(id, code, 0);
|
|
}
|
|
|
|
static int extract_str(char* dst, unsigned int dsize, char** src, unsigned int* ssize, const char* errmsg)
|
|
{
|
|
bsk_tftpd_context_id_t id=0;
|
|
|
|
bsk_tftpd_cx_t& cx=cxdb[id];
|
|
|
|
char* const p=*src;
|
|
unsigned int s=*ssize;
|
|
unsigned int i;
|
|
for(i=0; (i<s) && (p[i]!=0); ++i)
|
|
{
|
|
if (i>=(dsize-1))
|
|
{
|
|
LOG("TFTP: %s too long", errmsg);
|
|
return -1;
|
|
}
|
|
if (p[i]>='A' && p[i]<='Z')
|
|
dst[i]=p[i]; //disable case conversion!!!! +('a'-'A');
|
|
else
|
|
dst[i]=p[i];
|
|
dst[i+1]=0;
|
|
}
|
|
if (i==0)
|
|
return -1;
|
|
|
|
s=s-i;
|
|
if (s>1 && p[i]==0)
|
|
{
|
|
--s;
|
|
++i;
|
|
}
|
|
*ssize=s;
|
|
*src=&p[i];
|
|
return 0;
|
|
}
|
|
|
|
static void tftp_itoa(int v, char* const dst)
|
|
{
|
|
int i;
|
|
char str[16];
|
|
for(i=0; v; v/=10, ++i)
|
|
{
|
|
int tmp=v/10;
|
|
tmp=v-tmp*10;
|
|
str[i]='0'+tmp;
|
|
}
|
|
str[i]=0;
|
|
--i;
|
|
if (i<0)
|
|
i=0;
|
|
int j;
|
|
for(j=0; i>=0; --i, ++j)
|
|
{
|
|
dst[j]=str[i];
|
|
}
|
|
dst[j]=0;
|
|
}
|
|
|
|
static int tftp_strcmp(const char* const s1, const char* const s2)
|
|
{
|
|
int i;
|
|
for(i=0; s1[i] && s2[i]; ++i)
|
|
{
|
|
char c1=s1[i];
|
|
if (c1>='A' && c1<='Z')
|
|
c1=c1+('a'-'A');
|
|
char c2=s2[i];
|
|
if (c2>='A' && c2<='Z')
|
|
c2=c2+('a'-'A');
|
|
|
|
if (c1!=c2)
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int tftp_atoi(const char* s)
|
|
{
|
|
unsigned int value=0;
|
|
for(; *s; ++s)
|
|
{
|
|
value=value*10;
|
|
value=value+*s-'0';
|
|
}
|
|
return value;
|
|
}
|
|
|
|
static int doConnect(bsk_tftpd_context_id_t id, unsigned int c_adr, unsigned int c_port, const tftppkt_t* const pkt, unsigned int size)
|
|
{
|
|
char* p=(char*)pkt->header.th_u.tu_stuff;
|
|
unsigned int s=size-sizeof(tftphdr_t);
|
|
int opterr=0;
|
|
int ack_counter=0;
|
|
int has_bklsize=0;
|
|
bsk_tftpd_receive_status_t sts;
|
|
|
|
bsk_tftpd_cx_t& cx=cxdb[id];
|
|
|
|
cx.block_size=512;
|
|
cx.tsize=0;
|
|
|
|
memset(cx.ack_buff, 0, sizeof cx.ack_buff);
|
|
cx.ackmsg=(tftppkt_t*)cx.ack_buff;
|
|
tftp_htons(cx.ackmsg->header.th_opcode, TFTP_OACK);
|
|
|
|
if (extract_str(cx.filename, sizeof cx.filename, &p, &s, "filename"))
|
|
{
|
|
LOG("TFTP: invalid filename");
|
|
nak(id, EBADOP);
|
|
return 1;
|
|
}
|
|
|
|
/*TODO: add grant request*/
|
|
LOG("TFTP: filename <%s>\n", cx.filename);
|
|
|
|
if (extract_str(cx.optmp, sizeof cx.optmp, &p, &s, "mode"))
|
|
{
|
|
LOG("TFTP: unsupported mode");
|
|
nak(id, EBADOP);
|
|
return 1;
|
|
}
|
|
|
|
if (tftp_strcmp(cx.optmp, "octet") && tftp_strcmp(cx.optmp, "binary"))
|
|
{
|
|
LOG(("TFTP: unsupported mode\n"));
|
|
nak(id, EBADOP);
|
|
return 1;
|
|
}
|
|
|
|
while(extract_str(cx.optmp, sizeof cx.optmp, &p, &s, "opt")==0)
|
|
{
|
|
if (extract_str(cx.optval, sizeof cx.optval, &p, &s, "optval"))
|
|
{
|
|
LOG("TFTP: opt=%s, value=ERROR", cx.optmp);
|
|
nak(id, EBADOP);
|
|
return 1;
|
|
}
|
|
|
|
if (tftp_strcmp(cx.optmp, TFTP_OPT_BLKSIZE)==0)
|
|
{
|
|
unsigned int v=tftp_atoi(cx.optval);
|
|
LOG_DBG("TFTP: opt=%s, value=%s (%u)", cx.optmp, cx.optval, v);
|
|
if (v<TFTP_MINSEGSIZE)
|
|
v=TFTP_MINSEGSIZE;
|
|
if (v>TFTP_MAXSEGSIZE)
|
|
v=TFTP_MAXSEGSIZE;
|
|
strcpy((char*)&cx.ackmsg->header.th_u.tu_stuff[ack_counter], TFTP_OPT_BLKSIZE);
|
|
ack_counter+=sizeof(TFTP_OPT_BLKSIZE);
|
|
char* p=(char*)&cx.ackmsg->header.th_u.tu_stuff[ack_counter];
|
|
tftp_itoa(v, p);
|
|
ack_counter+=strlen((char*)&cx.ackmsg->header.th_u.tu_stuff[ack_counter]);
|
|
has_bklsize=1;
|
|
cx.block_size=v;
|
|
LOG("TFTP: block size=%d (%s)", cx.block_size, p);
|
|
}
|
|
else if (tftp_strcmp(cx.optmp, TFTP_OPT_TSIZE)==0)
|
|
{
|
|
cx.tsize=tftp_atoi(cx.optval);
|
|
LOG("TFTP: opt=%s, value=%s", cx.optmp, cx.optval);
|
|
}
|
|
else
|
|
{
|
|
LOG("TFTP: opt=%s, value=%s - unsupported option", cx.optmp, cx.optval);
|
|
++opterr;
|
|
}
|
|
}
|
|
|
|
if (opterr)
|
|
{
|
|
nak(id, EBADOP);
|
|
return 1;
|
|
}
|
|
|
|
sts=cx.receive_delegate(id, bsk_tftpd_open_file, cx.filename, 0, 0, cx.tsize);
|
|
if (sts.sts)
|
|
{
|
|
nakmsg(id, sts.sts, sts.ack_msg); //EBADOP);
|
|
return 1;
|
|
}
|
|
|
|
if (!has_bklsize && cx.force_blocksize)
|
|
{
|
|
strcpy((char*)&cx.ackmsg->header.th_u.tu_stuff[ack_counter], TFTP_OPT_BLKSIZE);
|
|
ack_counter+=sizeof(TFTP_OPT_BLKSIZE);
|
|
tftp_itoa(TFTP_MAXSEGSIZE, (char*)&cx.ackmsg->header.th_u.tu_stuff[ack_counter]);
|
|
ack_counter+=strlen((char*)&cx.ackmsg->header.th_u.tu_stuff[ack_counter]);
|
|
}
|
|
|
|
if (ack_counter==0)
|
|
{
|
|
tftp_htons(cx.ackmsg->header.th_opcode, TFTP_ACK);
|
|
cx.ackmsg->header.th_u.tu_block[0]=0;
|
|
cx.ackmsg->header.th_u.tu_block[1]=0;
|
|
ack_counter=1;
|
|
}
|
|
|
|
cx.send_delegate(id, c_adr, c_port, cx.ackmsg, (ack_counter+sizeof(tftphdr_t))-1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int doProcessData(bsk_tftpd_context_id_t id, const tftppkt_t* const pkt, unsigned int size)
|
|
{
|
|
bsk_tftpd_receive_status_t sts;
|
|
unsigned short block=tftp_ntos(pkt->header.th_u.tu_block);
|
|
unsigned int s=size-sizeof(pkt->header);
|
|
|
|
bsk_tftpd_cx_t& cx=cxdb[id];
|
|
|
|
LOG_DBG("TFTP: receive b%u, %ubytes\n", block, s);
|
|
|
|
|
|
int end=s<cx.block_size;
|
|
|
|
unsigned long offset=0;
|
|
if (block>0)
|
|
offset=(block-1)*cx.block_size;
|
|
sts=cx.receive_delegate(id, end ? bsk_tftpd_end : bsk_tftpd_data, pkt->th_data, block, offset, s);
|
|
if (sts.sts && sts.sts<bsk_tftpd_dont_ack)
|
|
{
|
|
nakmsg(id, sts.sts, sts.ack_msg); //EBADOP);
|
|
return -1;
|
|
}
|
|
|
|
tftp_htons(cx.ackmsg->header.th_opcode, TFTP_ACK);
|
|
cx.ackmsg->header.th_u.tu_block[0]=pkt->header.th_u.tu_block[0];
|
|
cx.ackmsg->header.th_u.tu_block[1]=pkt->header.th_u.tu_block[1];
|
|
|
|
if (sts.sts!=bsk_tftpd_dont_ack)
|
|
{
|
|
cx.send_delegate(id, cx.client_address, cx.client_port, cx.ackmsg, sizeof(cx.ackmsg->header));
|
|
}
|
|
|
|
if (end)
|
|
return sts.sts!=bsk_tftpd_dont_ack ? 1 : 2;
|
|
return 0;
|
|
}
|
|
|
|
int bsk_tftpd_process_packet(bsk_tftpd_context_id_t id, unsigned int client_address, unsigned int client_port, const void* packet, unsigned int size_bytes)
|
|
{
|
|
//static int initialized;
|
|
unsigned short opcode;
|
|
tftphdr_t* hdr=(tftphdr_t*)packet;
|
|
tftppkt_t* pkt=(tftppkt_t*)packet;
|
|
|
|
bsk_tftpd_cx_t& cx=cxdb[id];
|
|
|
|
//if (!initialized)
|
|
{
|
|
cx.ackmsg=(tftppkt_t*)cx.ack_buff;
|
|
memset(cx.ack_buff, 0, sizeof cx.ack_buff);
|
|
//initialized=1;
|
|
}
|
|
|
|
LOG_DBG("TFTP: process packet: %u %x %u", size_bytes, client_address, client_port);
|
|
|
|
if (size_bytes<sizeof(tftphdr_t))
|
|
return -1;
|
|
|
|
validate_delegates(id);
|
|
|
|
opcode=tftp_ntos(hdr->th_opcode);
|
|
|
|
if (opcode==TFTP_ERROR)
|
|
{
|
|
unsigned short code=tftp_ntos(pkt->header.th_u.tu_code);
|
|
char* p=(char*)pkt->th_data;
|
|
unsigned int s=size_bytes-sizeof(pkt->header);
|
|
if (!extract_str(cx.optmp, sizeof cx.optmp, &p, &s, 0))
|
|
LOG("TFTP: error from peer: %u %s\n", code, cx.optmp);
|
|
else
|
|
LOG("TFTP: error from peer: %u (unknown)\n", code);
|
|
if (cx.state==t_connected)
|
|
{
|
|
cx.receive_delegate(id, bsk_tftpd_error, 0, 0, 0, 0);
|
|
cx.state=t_unconnected;
|
|
}
|
|
return -1;
|
|
}
|
|
if (opcode==TFTP_ACK)
|
|
{
|
|
LOG(("TFTP: unexpected ACK from peer\n"));
|
|
return -1;
|
|
}
|
|
|
|
|
|
if (opcode==TFTP_RRQ)
|
|
{
|
|
char* p=(char*)pkt->header.th_u.tu_stuff;
|
|
#if 0
|
|
unsigned int s=size_bytes-sizeof(tftphdr_t);
|
|
if (extract_str(cx.filename, sizeof cx.filename, &p, &s, "filename"))
|
|
{
|
|
LOG("TFTP: invalid filename");
|
|
nak(id, EBADOP);
|
|
return 0;
|
|
}
|
|
#endif
|
|
bsk_tftpd_receive_status_t sts=cx.receive_delegate(id, bsk_tftpd_open_read_not_supported, p, 0, 0, 0);
|
|
nakmsg(id, sts.sts, sts.ack_msg, client_address, client_port);
|
|
return -100;
|
|
}
|
|
|
|
if (cx.busy_msg)
|
|
{
|
|
nakmsg(id, EACCESS, cx.busy_msg, client_address, client_port);
|
|
return -10;
|
|
}
|
|
|
|
switch(cx.state)
|
|
{
|
|
case t_unconnected:
|
|
cx.client_address=client_address;
|
|
cx.client_port=client_port;
|
|
if (opcode!=TFTP_WRQ)
|
|
{
|
|
LOG("TFTP: Unexpected request %d from peer", opcode);
|
|
nak(id, EBADOP);
|
|
return -2;
|
|
}
|
|
if (!doConnect(id, client_address, client_port, pkt, size_bytes))
|
|
{
|
|
LOG("TFTP: connected: %x %u", client_address, client_port);
|
|
cx.state=t_connected;
|
|
}
|
|
break;
|
|
|
|
case t_connected:
|
|
case t_pending:
|
|
if (opcode==TFTP_WRQ)
|
|
{
|
|
if (cx.state==t_pending)
|
|
{
|
|
nakmsg(id, EACCESS, "pending operation");
|
|
return 2;
|
|
}
|
|
}
|
|
if ((cx.client_address!=client_address) || (cx.client_port!=client_port))
|
|
{
|
|
LOG("TFTP: ERROR - already connected!");
|
|
nak(id, EBADID);
|
|
return -5;
|
|
}
|
|
if (opcode==TFTP_DATA)
|
|
{
|
|
if (cx.state==t_pending)
|
|
{
|
|
return 2;
|
|
}
|
|
int res=doProcessData(id, pkt, size_bytes);
|
|
if (res<0)
|
|
{
|
|
LOG("TFTP: ERROR - disconnect!\n");
|
|
cx.state=t_unconnected;
|
|
return -3;
|
|
}
|
|
if (res>0)
|
|
{
|
|
if (res==2)
|
|
{
|
|
cx.state=t_pending;
|
|
LOG("TFTP: completed - pending ack\n");
|
|
}
|
|
else
|
|
{
|
|
LOG("TFTP: completed (%d blocks)", 0);
|
|
cx.state=t_unconnected;
|
|
}
|
|
return res;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG("TFTP: disconnect - invalid opcode: %u\n", opcode);
|
|
nak(id, EBADOP);
|
|
cx.state=t_unconnected;
|
|
return -4;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
int bsk_tftpd_send_ack(bsk_tftpd_context_id_t id, bsk_tftpd_status_t /*code*/, const char* /*msg*/)
|
|
{
|
|
bsk_tftpd_cx_t& cx=cxdb[id];
|
|
|
|
LOG("ACK!");
|
|
tftp_htons(cx.ackmsg->header.th_opcode, TFTP_ACK);
|
|
cx.ackmsg->header.th_u.tu_block[0]=0;
|
|
cx.ackmsg->header.th_u.tu_block[1]=0;
|
|
cx.send_delegate(id, cx.client_address, cx.client_port, cx.ackmsg, sizeof(cx.ackmsg->header));
|
|
cx.state=t_unconnected;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bsk_tftpd_set_busy(bsk_tftpd_context_id_t id, const char* msg) //msg=0 means NO BUSY
|
|
{
|
|
bsk_tftpd_cx_t& cx=cxdb[id];
|
|
|
|
cx.busy_msg=msg;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//Client I/F
|