Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: oldheating gps motorhome heating
udp/tftp.cpp
- Committer:
- andrewboyson
- Date:
- 2017-12-14
- Revision:
- 59:e0e556c8bd46
- Parent:
- 57:e0fb648acf48
File content as of revision 59:e0e556c8bd46:
#include "mbed.h"
#include "log.h"
#include "net.h"
#include "action.h"
#include "ip6addr.h"
#include "udp.h"
#include "ar4.h"
#include "ar6.h"
#include "arp.h"
#include "io.h"
#include "eth.h"
#include "nr4.h"
#include "nr6.h"
#include "dns.h"
#include "mac.h"
#include "tftp.h"
#include "clock.h"
bool TftpTrace = false;
#define WRITE_TIMEOUT_SECS 5
#define TFTP_RRQ 1
#define TFTP_WRQ 2
#define TFTP_DATA 3
#define TFTP_ACK 4
#define TFTP_ERROR 5
static void logOp(char* p)
{
if (*p)
{
LogF("Unknown op code %02x%02x", *p, *++p);
return;
}
p++;
switch (*p)
{
case TFTP_RRQ: Log ("RRQ") ; break;
case TFTP_WRQ: Log ("WRQ") ; break;
case TFTP_DATA: Log ("DATA") ; break;
case TFTP_ACK: Log ("ACK") ; break;
case TFTP_ERROR: Log ("ERROR") ; break;
default: LogF("Unknown op code 00%02x", *p); break;
}
}
static void logError(char* p)
{
if (*p)
{
LogF("Unknown error code %02x%02x", *p, *++p);
return;
}
p++;
switch (*p)
{
case 0: Log ("Not defined, see error message." ); break;
case 1: Log ("File not found." ); break;
case 2: Log ("Access violation." ); break;
case 3: Log ("Disk full or allocation exceeded."); break;
case 4: Log ("Illegal TFTP operation." ); break;
case 5: Log ("Unknown transfer ID." ); break;
case 6: Log ("File already exists." ); break;
case 7: Log ("No such user." ); break;
default: LogF("Unknown error code 00%02x", *p ); break;
}
}
uint32_t TftpServerIp4;
char TftpServerIp6[16];
int TftpWriteStatus = TFTP_WRITE_STATUS_NONE;
char TftpServerName[DNS_MAX_LABEL_LENGTH+1];
char TftpFileName [DNS_MAX_LABEL_LENGTH+1];
int (*TftpGetNextByteFunction)();
static int size;
static void logHeader(char* p)
{
if (NetTraceVerbose)
{
Log ("TFTP header\r\n");
Log (" Op code "); logOp(p); Log("\r\n");
Log (" Size "); LogF("%d", size); Log("\r\n");
}
else
{
Log ("TFTP header");
Log (": Op "); logOp(p);
LogF(", %d bytes", size);
Log ("\r\n");
}
}
static int sendBlock(uint16_t block, char* pHeader)
{
/*2 bytes 2 bytes n bytes
----------------------------------
| Opcode | Block # | Data |
---------------------------------- */
char* p = pHeader;
*p++ = 0;
*p++ = TFTP_DATA;
*p++ = block >> 8;
*p++ = block & 0xFF;
int len = 0;
while (len < 512)
{
int c = TftpGetNextByteFunction();
if (c == -1) break;
*p++ = c;
len++;
}
if (len < 512) TftpWriteStatus = TFTP_WRITE_STATUS_NONE;
size = p - pHeader;
return UNICAST_TFTP;
}
static void handleError(char* p)
{
/*2 bytes 2 bytes string 1 byte
-----------------------------------------
| ERROR | ErrorCode | ErrMsg | 0 |
----------------------------------------- */
p += 2; //Skip the op code which we already know
if (NetTraceNewLine) Log("\r\n");
LogTime("TFTP error - ");
logError(p); p += 2;
Log(p);
Log("\r\n");
TftpWriteStatus = TFTP_WRITE_STATUS_NONE;
}
static int handleAck(char* pHeaderRx, char* pHeaderTx)
{
/* 2 bytes 2 bytes
-----------------------
| ACK | Block # |
----------------------- */
char* p = pHeaderRx;
p += 2; //Skip the op code which we already know
uint16_t block = *p++;
block <<= 8;
block += *p++;
return sendBlock(block + 1, pHeaderTx);
}
static int sendRequest(char* pHeader)
{
/*2 bytes string 1 byte string 1 byte
-----------------------------------------------
| WRQ | Filename | 0 | Mode | 0 |
----------------------------------------------- */
char* p = pHeader;
*p++ = 0;
*p++ = TFTP_WRQ;
char* pName = TftpFileName;
while (*pName) *p++ = *pName++;
*p++ = 0;
const char* pMode = "octet";
while (*pMode) *p++ = *pMode++;
*p++ = 0;
size = p - pHeader;
return UNICAST_TFTP;
}
static void (*pTraceBack)(void);
int TftpHandlePacketReceived(void (*traceback)(void), int sizeRx, void * pPacketRx, int* pSizeTx, void* pPacketTx)
{
pTraceBack = traceback;
char* pHeaderRx = (char*)pPacketRx;
char* pHeaderTx = (char*)pPacketTx;
size = sizeRx;
char* p = pHeaderRx;
if (*p)
{
LogTimeF("Expected high byte of op code to be zero not %u\r\n", *p);
return DO_NOTHING;
}
if (TftpTrace)
{
if (NetTraceNewLine) Log("\r\n");
LogTimeF("TFTP received packet\r\n");
if (NetTraceStack) pTraceBack();
logHeader(pHeaderRx);
}
p++;
int dest = DO_NOTHING;
switch (*p)
{
case TFTP_ACK:
if (TftpWriteStatus == TFTP_WRITE_STATUS_NONE) return DO_NOTHING;
dest = handleAck(pHeaderRx, pHeaderTx);
break;
case TFTP_ERROR:
handleError(pHeaderRx);
return DO_NOTHING;
default:
LogTimeF("\r\nTFTP packet unknown mode %d\r\n", *p);
return DO_NOTHING;
}
if (TftpTrace) logHeader(pHeaderTx);
*pSizeTx = size;
return ActionMakeFromDestAndTrace(dest, TftpTrace && NetTraceStack);
}
static bool resolve4(char* server, uint32_t* pIp)
{
//Check if have IP, if not, then request it and stop
Nr4NameToIp(server, pIp);
if (!*pIp)
{
Nr4MakeRequestForIpFromName(server); //The request is only repeated if made after a freeze time - call as often as you want.
return false;
}
//Check if have MAC and, if not, request it and stop
char mac[6];
Ar4IpToMac(*pIp, mac);
if (MacIsEmpty(mac))
{
Ar4MakeRequestForMacFromIp(*pIp); //The request is only repeated if made after a freeze time - call as often as you want.
return false;
}
return true;
}
static bool resolve6(char* server, char* ip)
{
//Check if have IP, if not, then request it and stop
Nr6NameToIp(server, ip);
if (Ip6AddressIsEmpty(ip))
{
Nr6MakeRequestForIpFromName(server); //The request is only repeated if made after a freeze time - call as often as you want.
return false;
}
//Check if have MAC and, if not, request it and stop
char mac[6];
Ar6IpToMac(ip, mac);
if (MacIsEmpty(mac))
{
Ar6MakeRequestForMacFromIp(ip); //The request is only repeated if made after a freeze time - call as often as you want.
return false;
}
return true;
}
int TftpPollForPacketToSend(int type, void* pPacket, int* pSize)
{
if (TftpWriteStatus != TFTP_WRITE_STATUS_REQUEST) return DO_NOTHING; //Wait until a request to send a file
char* pHeader = (char*)pPacket;
size = *pSize;
if (!TftpServerName[0])
{
LogTimeF("TftpPollForRequestToSend - A request to send a client message has been made but no server name has been specified\r\n");
TftpWriteStatus = TFTP_WRITE_STATUS_NONE;
return DO_NOTHING;
}
if (!TftpGetNextByteFunction)
{
LogTimeF("TftpPollForRequestToSend - A request to send a client message has been made but TFTP has not been plumbed into a file stream\r\n");
TftpWriteStatus = TFTP_WRITE_STATUS_NONE;
return DO_NOTHING;
}
if (type == IPV4)
{
if (!resolve4(TftpServerName, &TftpServerIp4)) return DO_NOTHING;
}
else if (type == IPV6)
{
if (!resolve6(TftpServerName, TftpServerIp6)) return DO_NOTHING;
}
else
{
return DO_NOTHING;
}
//Have IP and MAC so send request
TftpWriteStatus = TFTP_WRITE_STATUS_IN_PROGRESS;
int dest = sendRequest(pHeader);
if (TftpTrace)
{
if (NetTraceNewLine) Log("\r\n");
LogTimeF("TFTP Sending request\r\n");
logHeader(pHeader);
}
*pSize = size;
return ActionMakeFromDestAndTrace(dest, TftpTrace && NetTraceStack);
}
int elapsed = 0;
void TftpMain()
{
if (TftpWriteStatus == TFTP_WRITE_STATUS_IN_PROGRESS)
{
if (ClockTicked) elapsed++;
if (elapsed > WRITE_TIMEOUT_SECS)
{
TftpWriteStatus = TFTP_WRITE_STATUS_NONE;
LogTime("TFTP - write operation timed out so reset\r\n");
}
}
else
{
elapsed = 0;
}
}