Rtos API example

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers lwip_tftp_server.c Source File

lwip_tftp_server.c

00001 /****************************************************************//**
00002  *
00003  * @file tftp_server.c
00004  *
00005  * @author   Logan Gunthorpe <logang@deltatee.com>
00006  *           Dirk Ziegelmeier <dziegel@gmx.de>
00007  *
00008  * @brief    Trivial File Transfer Protocol (RFC 1350)
00009  *
00010  * Copyright (c) Deltatee Enterprises Ltd. 2013
00011  * All rights reserved.
00012  *
00013  ********************************************************************/
00014 
00015 /* 
00016  * Redistribution and use in source and binary forms, with or without
00017  * modification,are permitted provided that the following conditions are met:
00018  *
00019  * 1. Redistributions of source code must retain the above copyright notice,
00020  *    this list of conditions and the following disclaimer.
00021  * 2. Redistributions in binary form must reproduce the above copyright notice,
00022  *    this list of conditions and the following disclaimer in the documentation
00023  *    and/or other materials provided with the distribution.
00024  * 3. The name of the author may not be used to endorse or promote products
00025  *    derived from this software without specific prior written permission.
00026  *
00027  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
00028  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00029  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
00030  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00031  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
00032  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00033  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00034  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00035  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00036  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00037  *
00038  * Author: Logan Gunthorpe <logang@deltatee.com>
00039  *         Dirk Ziegelmeier <dziegel@gmx.de>
00040  *
00041  */
00042 
00043 /**
00044  * @defgroup tftp TFTP server
00045  * @ingroup apps
00046  *
00047  * This is simple TFTP server for the lwIP raw API.
00048  */
00049 
00050 #include "lwip/apps/tftp_server.h"
00051 
00052 #if LWIP_UDP
00053 
00054 #include "lwip/udp.h"
00055 #include "lwip/timeouts.h"
00056 #include "lwip/debug.h"
00057 
00058 #define TFTP_MAX_PAYLOAD_SIZE 512
00059 #define TFTP_HEADER_LENGTH    4
00060 
00061 #define TFTP_RRQ   1
00062 #define TFTP_WRQ   2
00063 #define TFTP_DATA  3
00064 #define TFTP_ACK   4
00065 #define TFTP_ERROR 5
00066 
00067 enum tftp_error {
00068   TFTP_ERROR_FILE_NOT_FOUND    = 1,
00069   TFTP_ERROR_ACCESS_VIOLATION  = 2,
00070   TFTP_ERROR_DISK_FULL         = 3,
00071   TFTP_ERROR_ILLEGAL_OPERATION = 4,
00072   TFTP_ERROR_UNKNOWN_TRFR_ID   = 5,
00073   TFTP_ERROR_FILE_EXISTS       = 6,
00074   TFTP_ERROR_NO_SUCH_USER      = 7
00075 };
00076 
00077 #include <string.h>
00078 
00079 struct tftp_state {
00080   const struct tftp_context *ctx;
00081   void *handle;
00082   struct pbuf *last_data;
00083   struct udp_pcb *upcb;
00084   ip_addr_t addr;
00085   u16_t port;
00086   int timer;
00087   int last_pkt;
00088   u16_t blknum;
00089   u8_t retries;
00090   u8_t mode_write;
00091 };
00092 
00093 static struct tftp_state tftp_state;
00094 
00095 static void tftp_tmr(void* arg);
00096 
00097 static void
00098 close_handle(void)
00099 {
00100   tftp_state.port = 0;
00101   ip_addr_set_any(0, &tftp_state.addr);
00102 
00103   if(tftp_state.last_data != NULL) {
00104     pbuf_free(tftp_state.last_data);
00105     tftp_state.last_data = NULL;
00106   }
00107 
00108   sys_untimeout(tftp_tmr, NULL);
00109   
00110   if (tftp_state.handle) {
00111     tftp_state.ctx->close(tftp_state.handle);
00112     tftp_state.handle = NULL;
00113     LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: closing\n"));
00114   }
00115 }
00116 
00117 static void
00118 send_error(const ip_addr_t *addr, u16_t port, enum tftp_error code, const char *str)
00119 {
00120   int str_length = strlen(str);
00121   struct pbuf* p;
00122   u16_t* payload;
00123   
00124   p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(TFTP_HEADER_LENGTH + str_length + 1), PBUF_RAM);
00125   if(p == NULL) {
00126     return;
00127   }
00128 
00129   payload = (u16_t*) p->payload;
00130   payload[0] = PP_HTONS(TFTP_ERROR);
00131   payload[1] = lwip_htons(code);
00132   MEMCPY(&payload[2], str, str_length + 1);
00133 
00134   udp_sendto(tftp_state.upcb, p, addr, port);
00135   pbuf_free(p);
00136 }
00137 
00138 static void
00139 send_ack(u16_t blknum)
00140 {
00141   struct pbuf* p;
00142   u16_t* payload;
00143   
00144   p = pbuf_alloc(PBUF_TRANSPORT, TFTP_HEADER_LENGTH, PBUF_RAM);
00145   if(p == NULL) {
00146     return;
00147   }
00148   payload = (u16_t*) p->payload;
00149   
00150   payload[0] = PP_HTONS(TFTP_ACK);
00151   payload[1] = lwip_htons(blknum);
00152   udp_sendto(tftp_state.upcb, p, &tftp_state.addr, tftp_state.port);
00153   pbuf_free(p);
00154 }
00155 
00156 static void
00157 resend_data(void)
00158 {
00159   struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, tftp_state.last_data->len, PBUF_RAM);
00160   if(p == NULL) {
00161     return;
00162   }
00163 
00164   if(pbuf_copy(p, tftp_state.last_data) != ERR_OK) {
00165     pbuf_free(p);
00166     return;
00167   }
00168     
00169   udp_sendto(tftp_state.upcb, p, &tftp_state.addr, tftp_state.port);
00170   pbuf_free(p);
00171 }
00172 
00173 static void
00174 send_data(void)
00175 {
00176   u16_t *payload;
00177   int ret;
00178 
00179   if(tftp_state.last_data != NULL) {
00180     pbuf_free(tftp_state.last_data);
00181   }
00182   
00183   tftp_state.last_data = pbuf_alloc(PBUF_TRANSPORT, TFTP_HEADER_LENGTH + TFTP_MAX_PAYLOAD_SIZE, PBUF_RAM);
00184   if(tftp_state.last_data == NULL) {
00185     return;
00186   }
00187 
00188   payload = (u16_t *) tftp_state.last_data->payload;
00189   payload[0] = PP_HTONS(TFTP_DATA);
00190   payload[1] = lwip_htons(tftp_state.blknum);
00191 
00192   ret = tftp_state.ctx->read(tftp_state.handle, &payload[2], TFTP_MAX_PAYLOAD_SIZE);
00193   if (ret < 0) {
00194     send_error(&tftp_state.addr, tftp_state.port, TFTP_ERROR_ACCESS_VIOLATION, "Error occured while reading the file.");
00195     close_handle();
00196     return;
00197   }
00198 
00199   pbuf_realloc(tftp_state.last_data, (u16_t)(TFTP_HEADER_LENGTH + ret));
00200   resend_data();
00201 }
00202 
00203 static void
00204 recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
00205 {
00206   u16_t *sbuf = (u16_t *) p->payload;
00207   int opcode;
00208 
00209   LWIP_UNUSED_ARG(arg);
00210   LWIP_UNUSED_ARG(upcb);
00211   
00212   if (((tftp_state.port != 0) && (port != tftp_state.port)) ||
00213       (!ip_addr_isany_val(tftp_state.addr) && !ip_addr_cmp(&tftp_state.addr, addr))) {
00214     send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
00215     pbuf_free(p);
00216     return;
00217   }
00218 
00219   opcode = sbuf[0];
00220 
00221   tftp_state.last_pkt = tftp_state.timer;
00222   tftp_state.retries = 0;
00223 
00224   switch (opcode) {
00225     case PP_HTONS(TFTP_RRQ): /* fall through */
00226     case PP_HTONS(TFTP_WRQ):
00227     {
00228       const char tftp_null = 0;
00229       char filename[TFTP_MAX_FILENAME_LEN];
00230       char mode[TFTP_MAX_MODE_LEN];
00231       u16_t filename_end_offset;
00232       u16_t mode_end_offset;
00233 
00234       if(tftp_state.handle != NULL) {
00235         send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
00236         break;
00237       }
00238       
00239       sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
00240 
00241       /* find \0 in pbuf -> end of filename string */
00242       filename_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), 2);
00243       if((u16_t)(filename_end_offset-2) > sizeof(filename)) {
00244         send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Filename too long/not NULL terminated");
00245         break;
00246       }
00247       pbuf_copy_partial(p, filename, filename_end_offset-2, 2);
00248 
00249       /* find \0 in pbuf -> end of mode string */
00250       mode_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), filename_end_offset+1);
00251       if((u16_t)(mode_end_offset-filename_end_offset) > sizeof(mode)) {
00252         send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Mode too long/not NULL terminated");
00253         break;
00254       }
00255       pbuf_copy_partial(p, mode, mode_end_offset-filename_end_offset, filename_end_offset+1);
00256  
00257       tftp_state.handle = tftp_state.ctx->open(filename, mode, opcode == PP_HTONS(TFTP_WRQ));
00258       tftp_state.blknum = 1;
00259 
00260       if (!tftp_state.handle) {
00261         send_error(addr, port, TFTP_ERROR_FILE_NOT_FOUND, "Unable to open requested file.");
00262         break;
00263       }
00264 
00265       LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: %s request from ", (opcode == PP_HTONS(TFTP_WRQ)) ? "write" : "read"));
00266       ip_addr_debug_print(TFTP_DEBUG | LWIP_DBG_STATE, addr);
00267       LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, (" for '%s' mode '%s'\n", filename, mode));
00268 
00269       ip_addr_copy(tftp_state.addr, *addr);
00270       tftp_state.port = port;
00271 
00272       if (opcode == PP_HTONS(TFTP_WRQ)) {
00273         tftp_state.mode_write = 1;
00274         send_ack(0);
00275       } else {
00276         tftp_state.mode_write = 0;
00277         send_data();
00278       }
00279 
00280       break;
00281     }
00282     
00283     case PP_HTONS(TFTP_DATA):
00284     {
00285       int ret;
00286       u16_t blknum;
00287       
00288       if (tftp_state.handle == NULL) {
00289         send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
00290         break;
00291       }
00292 
00293       if (tftp_state.mode_write != 1) {
00294         send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a write connection");
00295         break;
00296       }
00297 
00298       blknum = lwip_ntohs(sbuf[1]);
00299       pbuf_header(p, -TFTP_HEADER_LENGTH);
00300 
00301       ret = tftp_state.ctx->write(tftp_state.handle, p);
00302       if (ret < 0) {
00303         send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "error writing file");
00304         close_handle();
00305       } else {
00306         send_ack(blknum);
00307       }
00308 
00309       if (p->tot_len < TFTP_MAX_PAYLOAD_SIZE) {
00310         close_handle();
00311       }
00312       break;
00313     }
00314 
00315     case PP_HTONS(TFTP_ACK):
00316     {
00317       u16_t blknum;
00318       int lastpkt;
00319 
00320       if (tftp_state.handle == NULL) {
00321         send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
00322         break;
00323       }
00324 
00325       if (tftp_state.mode_write != 0) {
00326         send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a read connection");
00327         break;
00328       }
00329 
00330       blknum = lwip_ntohs(sbuf[1]);
00331       if (blknum != tftp_state.blknum) {
00332         send_error(addr, port, TFTP_ERROR_UNKNOWN_TRFR_ID, "Wrong block number");
00333         break;
00334       }
00335 
00336       lastpkt = 0;
00337 
00338       if (tftp_state.last_data != NULL) {
00339         lastpkt = tftp_state.last_data->tot_len != (TFTP_MAX_PAYLOAD_SIZE + TFTP_HEADER_LENGTH);
00340       }
00341 
00342       if (!lastpkt) {
00343         tftp_state.blknum++;
00344         send_data();
00345       } else {
00346         close_handle();
00347       }
00348 
00349       break;
00350     }
00351     
00352     default:
00353       send_error(addr, port, TFTP_ERROR_ILLEGAL_OPERATION, "Unknown operation");
00354       break;
00355   }
00356 
00357   pbuf_free(p);
00358 }
00359 
00360 static void
00361 tftp_tmr(void* arg)
00362 {
00363   LWIP_UNUSED_ARG(arg);
00364   
00365   tftp_state.timer++;
00366 
00367   if (tftp_state.handle == NULL) {
00368     return;
00369   }
00370 
00371   sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
00372 
00373   if ((tftp_state.timer - tftp_state.last_pkt) > (TFTP_TIMEOUT_MSECS / TFTP_TIMER_MSECS)) {
00374     if ((tftp_state.last_data != NULL) && (tftp_state.retries < TFTP_MAX_RETRIES)) {
00375       LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout, retrying\n"));
00376       resend_data();
00377       tftp_state.retries++;
00378     } else {
00379       LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout\n"));
00380       close_handle();
00381     }
00382   }
00383 }
00384 
00385 /** @ingroup tftp
00386  * Initialize TFTP server.
00387  * @param ctx TFTP callback struct
00388  */
00389 err_t 
00390 tftp_init(const struct tftp_context *ctx)
00391 {
00392   err_t ret;
00393 
00394   struct udp_pcb *pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
00395   if (pcb == NULL) {
00396     return ERR_MEM;
00397   }
00398 
00399   ret = udp_bind(pcb, IP_ANY_TYPE, TFTP_PORT);
00400   if (ret != ERR_OK) {
00401     udp_remove(pcb);
00402     return ret;
00403   }
00404 
00405   tftp_state.handle    = NULL;
00406   tftp_state.port      = 0;
00407   tftp_state.ctx       = ctx;
00408   tftp_state.timer     = 0;
00409   tftp_state.last_data = NULL;
00410   tftp_state.upcb      = pcb;
00411 
00412   udp_recv(pcb, recv, NULL);
00413 
00414   return ERR_OK;
00415 }
00416 
00417 #endif /* LWIP_UDP */