Files
zTC1/mico-os/MiCO/system/tftp_ota/tftpc.c

496 lines
15 KiB
C

/**********************************************************
Date: NOV 28th, 2006
Project : TFTP Client
Programers:
Jonathan Felske
Andrew Fullard
Craig Holmes
Reza Rahmanian
Adam Tomalty
File: TFTP Client (main)
Purpose: A TFTP client that will request a connections from
the server and transefet files.
Notes: Here we are using the sendto and recvfrom
functions so the server and client can exchange data.
***********************************************************/
#include "tftp.h"
static const char err_msg [7][40] = {"Not defined, see error message if any",
"File not fount",
"Access Violation",
"Disk full, or allocation exceeded",
"Illegal TFTP operation",
"Unknown transfer ID",
"File already exists"};
/*a function to create the request packet, read or write*/
static int req_packet (int opcode, char *filename, char *mode, unsigned char buf[]);
/*a function to creat an ACK packet*/
//static int ack_packet (int block, char buf[]);
/*a function to create the Error packets*/
static int err_packet (int err_code, const char *err_msg, unsigned char buf[]);
/* A function that will create a request packet*/
/*I'm not sure if I need the last 0x00 because sprintf will end the string with \0*/
static int
req_packet (int opcode, char *filename, char *mode, unsigned char buf[])
{
int i = 0, len;
buf[i++] = 0x00;
buf[i++] = (char)opcode;
len = strlen(filename);
memcpy(&buf[i], filename, len);
i+=len;
buf[i++] = 0x00;
len = strlen(mode);
memcpy(&buf[i], mode, len);
i+=len;
buf[i++] = 0x00;
return i;
}
/* A function that will create an ACK packet*/
/*problem that we will have here is that we can only get up to 255 blocks*/
/*
static int
ack_packet (int block, char buf[])
{
buf[0] = 0x00;
buf[1] = ACK;
buf[2] = (block & 0xFF00) >> 8;
buf[3] = (block & 0x00FF);
return 4;
}
*/
/* A function that will create an error packet based on the error code*/
static int
err_packet (int err_code, const char *err_msg, unsigned char buf[])
{
int i = 0;
int len;
len = strlen(err_msg);
memset (buf, 0, 5 + len);
buf[i++] = 0x00;
buf[i++] = ERR;
buf[i++] = 0x00;
buf[i++] = err_code;
memcpy(&buf[i], err_msg, len);
i+=len;
buf[i++] = 0x00;
return i;
}
/*
*This function is called when the client would like to upload a file to the server.
*/
int tsend (tftp_file_info_t *fileinfo, uint32_t ipaddr)
{
int len, server_len, opcode= WRQ, ssize = 0, n, i, j, tid;
unsigned short int count = 0, rcount = 0;
unsigned char filebuf[MAXDATASIZE + 1];
unsigned char packetbuf[MAXDATASIZE + 12],
recvbuf[MAXDATASIZE + 12];
char *bufindex; //fullpath[196],
struct sockaddr_in ack;
int sock;
uint32_t flashaddr = fileinfo->flashaddr;
struct sockaddr_in server;
unsigned short int ackfreq = 1;
int errno;
server.sin_family = AF_INET;
server.sin_addr.s_addr = ipaddr;
server.sin_port = htons((uint16_t)69);
/*Create the socket, a -1 will show us an error */
if ((sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
{
return -1;
}
memset (filebuf, 0, MAXDATASIZE); /*clear the buffer */
/* this is the first request message */
len = req_packet (opcode, fileinfo->filename, "octet", filebuf);
server_len = sizeof(struct sockaddr);
if (sendto (sock, filebuf, len, 0, (struct sockaddr *) &server, server_len) !=
len)
{
close(sock);
return -2;
}
/*At this point I have to wait to recieve an ACK from the server before I start sending the file*/
/*open the file to read */
//get ACK for WRQ
/* The following 'for' loop is used to recieve/timeout ACKs */
for (j = 0; j < RETRIES - 2; j++)
{
server_len = sizeof (ack);
errno = EAGAIN;
n = -1;
for (i = 0; errno == EAGAIN && i <= TIMEOUT && n < 0; i++)
{
n = recvfrom (sock, recvbuf, sizeof (recvbuf), MSG_DONTWAIT,
(struct sockaddr *) &ack, (socklen_t *) & server_len);
mico_rtos_delay_milliseconds(1);
}
tid = (ack.sin_port); //get the tid of the server.
server.sin_port = (tid); //set the tid for rest of the transfer
if (n < 0) {
}
else
{ /*changed client to server here */
if (server.sin_addr.s_addr != ack.sin_addr.s_addr ) /* checks to ensure send to ip is same from ACK IP */
{
j--; /* in this case someone else connected to our port. Ignore this fact and retry getting the ack */
continue;
}
if (tid != (server.sin_port)) /* checks to ensure get from the correct TID */
{
len = err_packet (5, err_msg[5], filebuf);
if (sendto (sock, filebuf, len, 0, (struct sockaddr *) &server, sizeof (server)) != len) /* send the data packet */
{
}
j--;
continue; /* we aren't going to let another connection spoil our first connection */
}
/* this formatting code is just like the code in the main function */
bufindex = (char *) recvbuf; //start our pointer going
if (bufindex++[0] != 0x00) {
}
opcode = *bufindex++;
rcount = *bufindex++ << 8;
rcount &= 0xff00;
rcount += (*bufindex++ & 0x00ff);
if (opcode != 4 || rcount != count) /* ack packet should have code 4 (ack) and should be acking the packet we just sent */
{
/* sending error message */
if (opcode > 5)
{
len = err_packet (4, err_msg[4], filebuf);
if (sendto (sock, filebuf, len, 0, (struct sockaddr *) &server, sizeof (server)) != len) /* send the data packet */
{
}
}
/* from here we will loop back and resend */
}
else
{
break;
}
} //end of else
}
/* The ack sending 'for' loop ends here */
memset (filebuf, 0, sizeof (filebuf)); //clear the filebuf
while (1) /* our break statement will escape us when we are done */
{
//acked = 0;
if (fileinfo->filelen > MAXDATASIZE)
ssize = MAXDATASIZE;
else
ssize = fileinfo->filelen;
fileinfo->filelen -= ssize;
MicoFlashRead(fileinfo->flashtype, &flashaddr, (uint8_t *)filebuf, ssize);
count++; /* count number of datasize byte portions we read from the file */
packetbuf[0] = 0x00;
packetbuf[1] = 0x03;
packetbuf[2] = (count & 0xFF00) >> 8; //fill in the count (top number first)
packetbuf[3] = (count & 0x00FF); //fill in the lower part of the count
memcpy ((char *) packetbuf + 4, filebuf, ssize);
len = 4 + ssize;
/* send the data packet */
if (sendto
(sock, packetbuf, len, 0, (struct sockaddr *) &server,
sizeof (server)) != len)
{
close(sock);
return -3;
}
//if ((count - 1) == 0 || ((count - 1) % ackfreq) == 0 || ssize != datasize)
if (((count) % ackfreq) == 0 || ssize != MAXDATASIZE)
{
/* The following 'for' loop is used to recieve/timeout ACKs */
for (j = 0; j < RETRIES; j++)
{
server_len = sizeof (ack);
errno = EAGAIN;
n = -1;
for (i = 0; errno == EAGAIN && i <= TIMEOUT && n < 0; i++)
{
n =
recvfrom (sock, recvbuf, sizeof (recvbuf), MSG_DONTWAIT,
(struct sockaddr *) &ack,
(socklen_t *) & server_len);
mico_rtos_delay_milliseconds(1);
}
if (n < 0) {
}
else
{ /* checks to ensure send to ip is same from ACK IP */
if (server.sin_addr.s_addr != ack.sin_addr.s_addr )
{
/* in this case someone else connected to our port. Ignore this fact and retry getting the ack */
j--;
continue;
}
if (tid != (server.sin_port)) /* checks to ensure get from the correct TID */
{
len = err_packet (5, err_msg[5], filebuf);
/* send the data packet */
if (sendto
(sock, filebuf, len, 0, (struct sockaddr *) &server,
sizeof (server)) != len)
{
}
j--;
continue; /* we aren't going to let another connection spoil our first connection */
}
/* this formatting code is just like the code in the main function */
bufindex = (char *) recvbuf; //start our pointer going
if (bufindex++[0] != 0x00) {
}
opcode = *bufindex++;
rcount = *bufindex++ << 8;
rcount &= 0xff00;
rcount += (*bufindex++ & 0x00ff);
if (opcode != 4 || rcount != count) /* ack packet should have code 4 (ack) and should be acking the packet we just sent */
{
/* sending error message */
if (opcode > 5)
{
len = err_packet (4, err_msg[4], filebuf);
if (sendto (sock, filebuf, len, 0, (struct sockaddr *) &server, sizeof (server)) != len) /* send the data packet */
{
}
}
/* from here we will loop back and resend */
}
else
{
break;
}
}
{
if (sendto (sock, packetbuf, len, 0, (struct sockaddr *) &server, sizeof (server)) != len) /* resend the data packet */
{
close(sock);
return -4;
}
}
}
/* The ack sending 'for' loop ends here */
}
if (j == RETRIES)
{
close(sock);
return -5;
}
if (ssize != MAXDATASIZE)
break;
memset (filebuf, 0, sizeof (filebuf)); /* fill the filebuf with zeros so that when the fread fills it, it is a null terminated string */
}
close(sock);
return 0;
} //end of tsend function
/*
*This function is called when the client would like to download a file from the server. Return the downloaded file length
*/
int
tget (tftp_file_info_t *fileinfo, uint32_t ipaddr)
{
/* local variables */
int len, server_len, opcode = RRQ, i, j, n, tid = 0, flag = 1;
unsigned short int count = 0, rcount = 0;
unsigned char filebuf[MAXDATASIZE + 12];
unsigned char packetbuf[128];
int errno;
char *bufindex;
struct sockaddr_in data;
int sock;
uint32_t flashaddr = fileinfo->flashaddr;
struct sockaddr_in server;
//unsigned short int ackfreq = 1;
int totalen = 0;
server.sin_family = AF_INET;
server.sin_addr.s_addr = ipaddr;
server.sin_port = htons((uint16_t)69);
/*Create the socket, a -1 will show us an error */
if ((sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
{
return -1;
}
MicoFlashErase(fileinfo->flashtype,fileinfo->flashaddr,fileinfo->filelen);
memset (packetbuf, 0, sizeof(packetbuf)); /*clear the buffer */
/* this is the first request message */
len = req_packet (opcode, fileinfo->filename, "octet", packetbuf);
server_len = sizeof(struct sockaddr);
sendto (sock, packetbuf, len, 0, (struct sockaddr *) &server, server_len);
n = MAXDATASIZE + 4;
do
{
/* zero buffers so if there are any errors only NULLs will be exposed */
memset (filebuf, 0, sizeof (filebuf));
for (j = 0; j < RETRIES; j++) /* this allows us to loop until we either break out by getting the correct ack OR time out because we've looped more than RETRIES times */
{
server_len = sizeof (data);
errno = EAGAIN; /* this allows us to enter the loop */
n = -1;
for (i = 0; errno == EAGAIN && i <= TIMEOUT && n < 0; i++) /* this for loop will just keep checking the non-blocking socket until timeout */
{
n =
recvfrom (sock, filebuf, sizeof (filebuf) - 1,
MSG_DONTWAIT, (struct sockaddr *) &data,
(socklen_t *) & server_len);
mico_rtos_delay_milliseconds(1);
}
if (!tid)
{
tid = (data.sin_port); //get the tid of the server.
server.sin_port = (tid); //set the tid for rest of the transfer
}
if (n < 0) {
sendto (sock, packetbuf, len, 0, (struct sockaddr *) &server, server_len);
}
else
{
if (server.sin_addr.s_addr != data.sin_addr.s_addr) /* checks to ensure get from ip is same from ACK IP */
{
j--;
continue; /* we aren't going to let another connection spoil our first connection */
}
if (tid != (server.sin_port)) /* checks to ensure get from the correct TID */
{
packetbuf[0] = 0x00;
packetbuf[1] = 0x05;
packetbuf[2] = 0x00;
packetbuf[3] = 0x05;
len = strlen("Bad/Unknown TID");
memcpy(&packetbuf[4], "Bad/Unknown TID", len);
len += 4;
packetbuf[len++] = 0x00;
if (sendto (sock, packetbuf, len, 0, (struct sockaddr *) &server, sizeof (server)) != len) /* send the data packet */
{
}
j--;
continue; /* we aren't going to let another connection spoil our first connection */
}
/* this formatting code is just like the code in the main function */
bufindex = (char *) filebuf; //start our pointer going
if (bufindex++[0] != 0x00) {
}
opcode = *bufindex++;
rcount = *bufindex++ << 8;
rcount &= 0xff00;
rcount += (*bufindex++ & 0x00ff);
if (flag)
{
flag = 0;
}
if (opcode != 3) /* ack packet should have code 3 (data) and should be ack+1 the packet we just sent */
{
/* sending error message */
if (opcode > 5)
{
packetbuf[0] = 0x00;
packetbuf[1] = 0x05;
packetbuf[2] = 0x00;
packetbuf[3] = 0x04;
len = strlen("Illegal operation");
memcpy(&packetbuf[4], "Illegal operation", len);
len += 4;
packetbuf[len++] = 0x00;
if (sendto (sock, packetbuf, len, 0, (struct sockaddr *) &server, sizeof (server)) != len) /* send the data packet */
{
}
}
}
else
{
if (count + 1 == rcount) {// received right seq no, save and ack
MicoFlashWrite(fileinfo->flashtype, &flashaddr, (uint8_t *)bufindex, n - 4);
totalen += n-4;
}
len = 4;
packetbuf[0] = 0x00;
packetbuf[1] = 0x04;
packetbuf[2] = (rcount & 0xFF00) >> 8; //fill in the count (top number first)
packetbuf[3] = (rcount & 0x00FF); //fill in the lower part of the count
count = rcount;
sendto(sock, packetbuf, len, 0, (struct sockaddr *) &server, sizeof (server));
break;
} //end of else
}
}
if (j == RETRIES)
{
close(sock);
return -4;
}
if (n != (MAXDATASIZE + 4)) /* remember if our datasize is less than a full packet this was the last packet to be received */
{
goto done; /* gotos are not optimal, but a good solution when exiting a multi-layer loop */
}
}
while (1);
done:
close(sock);
return totalen;
}