A stack which works with or without an Mbed os library. Provides IPv4 or IPv6 with a full 1500 byte buffer.

Dependents:   oldheating gps motorhome heating

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers tftp.c Source File

tftp.c

00001 #include <stdint.h>
00002 #include <stdbool.h>
00003 
00004 #include     "log.h"
00005 #include     "net.h"
00006 #include  "action.h"
00007 #include     "eth.h"
00008 #include    "dhcp.h"
00009 #include     "udp.h"
00010 #include     "dns.h"
00011 #include    "tftp.h"
00012 #include "mstimer.h"
00013 #include "resolve.h"
00014 #include "dnslabel.h"
00015 
00016 bool TftpTrace = false;
00017 
00018 #define WRITE_TIMEOUT_MS 7000 //Make this longer than the resolve time which is up to 3 seconds
00019 
00020 #define TFTP_RRQ   1
00021 #define TFTP_WRQ   2
00022 #define TFTP_DATA  3
00023 #define TFTP_ACK   4
00024 #define TFTP_ERROR 5
00025 
00026 static void logOp(char* p)
00027 {
00028     if (*p)
00029     {
00030         LogF("Unknown op code %02x", *p); LogF("%02x", *++p);
00031         return;
00032     }
00033     p++;
00034     switch (*p)
00035     {
00036         case TFTP_RRQ:   Log ("RRQ")                         ; break;
00037         case TFTP_WRQ:   Log ("WRQ")                         ; break;
00038         case TFTP_DATA:  Log ("DATA")                        ; break;
00039         case TFTP_ACK:   Log ("ACK")                         ; break;
00040         case TFTP_ERROR: Log ("ERROR")                       ; break;
00041         default:         LogF("Unknown op code 00%02x", *p); break;
00042     }
00043 }
00044 
00045 static void logError(char* p)
00046 {
00047     if (*p)
00048     {
00049         LogF("Unknown error code %02x", *p);
00050         LogF("%02x", *++p);
00051         return;
00052     }
00053     p++;
00054     switch (*p)
00055     {
00056         case 0:  Log ("Not defined, see error message."  ); break;
00057         case 1:  Log ("File not found."                  ); break;
00058         case 2:  Log ("Access violation."                ); break;
00059         case 3:  Log ("Disk full or allocation exceeded."); break;
00060         case 4:  Log ("Illegal TFTP operation."          ); break;
00061         case 5:  Log ("Unknown transfer ID."             ); break;
00062         case 6:  Log ("File already exists."             ); break;
00063         case 7:  Log ("No such user."                    ); break;
00064         default: LogF("Unknown error code 00%02x", *p    ); break;
00065     }
00066 }
00067 bool       TftpSendRequestsViaIp4 = false;
00068 uint32_t   TftpServerIp4;
00069 char       TftpServerIp6[16];
00070 int        TftpWriteStatus = TFTP_WRITE_STATUS_UNAVAILABLE;
00071 char       TftpServerName[DNS_MAX_LABEL_LENGTH+1];
00072 char       TftpFileName  [DNS_MAX_LABEL_LENGTH+1];
00073 
00074 int      (*TftpGetNextByteFunction)();
00075 
00076 static void logHeader(int size, char* p)
00077 {
00078     if (NetTraceVerbose)
00079     {
00080         Log ("TFTP header\r\n");
00081         Log ("  Op code "); logOp(p);         Log("\r\n");
00082         Log ("  Size    "); LogF("%d", size); Log("\r\n");
00083     }
00084     else
00085     {
00086         Log ("TFTP  header");
00087         Log (": Op "); logOp(p);
00088         LogF(", %d bytes", size);
00089         Log ("\r\n");
00090     }
00091 }
00092 static int sendBlock(uint16_t block, int* pSize, char* pHeader)
00093 {
00094     /*2 bytes    2 bytes     n bytes
00095      ----------------------------------
00096     | Opcode |   Block #  |   Data     |
00097      ---------------------------------- */
00098      char* p = pHeader;
00099     *p++ = 0;
00100     *p++ = TFTP_DATA;
00101     *p++ = block >> 8;
00102     *p++ = block & 0xFF;
00103     
00104     int len = 0;
00105     while (len < 512)
00106     {
00107         int c = TftpGetNextByteFunction();
00108         if (c == -1) break;
00109         *p++ = c;
00110         len++;
00111     }
00112     if (len < 512) TftpWriteStatus = TFTP_WRITE_STATUS_NONE;
00113     *pSize = p - pHeader;
00114     return UNICAST_TFTP;
00115 }
00116 static void handleError(char* p)
00117 {
00118      /*2 bytes   2 bytes      string    1 byte
00119      -----------------------------------------
00120     |  ERROR |  ErrorCode |   ErrMsg   |   0  |
00121      ----------------------------------------- */
00122     p += 2; //Skip the op code which we already know
00123     if (NetTraceNewLine) Log("\r\n");
00124     LogTime("TFTP error - ");
00125     logError(p); p += 2;
00126     Log(p);
00127     Log("\r\n");
00128     TftpWriteStatus = TFTP_WRITE_STATUS_NONE;
00129 }
00130 static int handleAck(char* pHeaderRx, int* pSizeTx, char* pHeaderTx)
00131 {
00132     /* 2 bytes    2 bytes
00133      -----------------------
00134     |   ACK    |   Block #  |
00135      ----------------------- */
00136     char* p = pHeaderRx;
00137     p += 2; //Skip the op code which we already know
00138     uint16_t block = *p++;
00139     block <<= 8;
00140     block += *p++;
00141     
00142     return sendBlock(block + 1, pSizeTx, pHeaderTx);
00143 }
00144 static int sendRequest(int* pSize, char* pHeader)
00145 {
00146     /*2 bytes    string   1 byte     string   1 byte
00147      -----------------------------------------------
00148     |   WRQ  |  Filename  |   0  |    Mode    |  0  |
00149      ----------------------------------------------- */
00150      char* p = pHeader;
00151     *p++ = 0;
00152     *p++ = TFTP_WRQ;
00153     char* pName = TftpFileName;
00154     while (*pName) *p++ = *pName++;
00155     *p++ = 0;
00156     const char* pMode = "octet";
00157     while (*pMode) *p++ = *pMode++;
00158     *p++ = 0;
00159     *pSize = p - pHeader;
00160     return UNICAST_TFTP;
00161 }
00162 int TftpHandlePacketReceived(void (*traceback)(void), int sizeRx, void * pPacketRx, int* pSizeTx, void* pPacketTx)
00163 {
00164     char* pHeaderRx = (char*)pPacketRx;
00165     char* pHeaderTx = (char*)pPacketTx;
00166     
00167     char* p = pHeaderRx;
00168     
00169     if (*p)
00170     {
00171         LogTimeF("Expected high byte of op code to be zero not %u\r\n", *p);
00172         return DO_NOTHING;
00173     }
00174     if (TftpTrace)
00175     {
00176         if (NetTraceNewLine) Log("\r\n");
00177         LogTimeF("TFTP received packet\r\n");
00178         if (NetTraceStack) traceback();
00179         logHeader(sizeRx, pHeaderRx);
00180     }
00181     p++;
00182     int dest = DO_NOTHING;
00183     switch (*p)
00184     {
00185         case TFTP_ACK:
00186             if (TftpWriteStatus == TFTP_WRITE_STATUS_NONE) return DO_NOTHING;
00187             dest = handleAck(pHeaderRx, pSizeTx, pHeaderTx);
00188             break;
00189         case TFTP_ERROR:
00190             handleError(pHeaderRx); 
00191             return DO_NOTHING;
00192         default:
00193             LogTimeF("\r\nTFTP packet unknown mode %d\r\n", *p);
00194             return DO_NOTHING;
00195     }
00196 
00197     if (TftpTrace) logHeader(*pSizeTx, pHeaderTx);
00198 
00199     return ActionMakeFromDestAndTrace(dest, TftpTrace && NetTraceStack);
00200 }
00201 static bool isTimedOut()
00202 {
00203     static uint32_t writeStartMs = 0;
00204     
00205     if (TftpWriteStatus == TFTP_WRITE_STATUS_NONE || TftpWriteStatus == TFTP_WRITE_STATUS_UNAVAILABLE) writeStartMs = MsTimerCount;
00206     
00207     if (MsTimerRelative(writeStartMs, WRITE_TIMEOUT_MS))
00208     {
00209         LogTime("TFTP - write operation timed out so reset\r\n");
00210         writeStartMs = MsTimerCount;
00211         return true;
00212     }
00213     return false;
00214 }
00215 static bool isOkToGo()
00216 {
00217     if (!TftpServerName[0])
00218     {
00219         LogTimeF("TftpPollForRequestToSend - A request to send a client message has been made but no server name has been specified\r\n");
00220         return false;
00221     }
00222     
00223     if (!TftpGetNextByteFunction)
00224     {
00225         LogTimeF("TftpPollForRequestToSend - A request to send a client message has been made but TFTP has not been plumbed into a file stream\r\n");
00226         return false;
00227     }
00228     return true;
00229 }
00230 int TftpPollForPacketToSend(int type, void* pPacket, int* pSize)
00231 {
00232     if (type == ETH_IPV4)
00233     {
00234         if (!DhcpLocalIp)
00235         {
00236             TftpWriteStatus = TFTP_WRITE_STATUS_UNAVAILABLE;
00237         }
00238         else
00239         {
00240             if (TftpWriteStatus == TFTP_WRITE_STATUS_UNAVAILABLE) TftpWriteStatus = TFTP_WRITE_STATUS_NONE;
00241         }
00242     }
00243     if (isTimedOut()                                                 ) { TftpWriteStatus = TFTP_WRITE_STATUS_NONE; return DO_NOTHING; }
00244     if (TftpWriteStatus != TFTP_WRITE_STATUS_REQUEST                 ) {                                           return DO_NOTHING; }
00245     if (!isOkToGo()                                                  ) { TftpWriteStatus = TFTP_WRITE_STATUS_NONE; return DO_NOTHING; }
00246     if (!Resolve(TftpServerName, type, &TftpServerIp4, TftpServerIp6)) {                                           return DO_NOTHING; }
00247 
00248     //Have IP and MAC so send request
00249     TftpWriteStatus = TFTP_WRITE_STATUS_IN_PROGRESS;
00250     int dest = sendRequest(pSize, (char*)pPacket);
00251     if (TftpTrace)
00252     {
00253         if (NetTraceNewLine) Log("\r\n");
00254         LogTimeF("TFTP Sending request\r\n");
00255         logHeader(*pSize, (char*)pPacket);
00256     }
00257     return ActionMakeFromDestAndTrace(dest, TftpTrace && NetTraceStack);
00258 }