• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  *
3  * @file tftp_server.c
4  *
5  * @author   Logan Gunthorpe <logang@deltatee.com>
6  *           Dirk Ziegelmeier <dziegel@gmx.de>
7  *
8  * @brief    Trivial File Transfer Protocol (RFC 1350)
9  *
10  * Copyright (c) Deltatee Enterprises Ltd. 2013
11  * All rights reserved.
12  *
13  */
14 
15 /*
16  * Redistribution and use in source and binary forms, with or without
17  * modification,are permitted provided that the following conditions are met:
18  *
19  * 1. Redistributions of source code must retain the above copyright notice,
20  *    this list of conditions and the following disclaimer.
21  * 2. Redistributions in binary form must reproduce the above copyright notice,
22  *    this list of conditions and the following disclaimer in the documentation
23  *    and/or other materials provided with the distribution.
24  * 3. The name of the author may not be used to endorse or promote products
25  *    derived from this software without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
28  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
29  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
30  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
32  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37  *
38  * Author: Logan Gunthorpe <logang@deltatee.com>
39  *         Dirk Ziegelmeier <dziegel@gmx.de>
40  *
41  */
42 
43 /**
44  * @defgroup tftp TFTP server
45  * @ingroup apps
46  *
47  * This is simple TFTP server for the lwIP raw API.
48  */
49 
50 #include "lwip/apps/tftp_server.h"
51 
52 #if LWIP_UDP
53 
54 #include "lwip/udp.h"
55 #include "lwip/timeouts.h"
56 #include "lwip/debug.h"
57 
58 #define TFTP_MAX_PAYLOAD_SIZE 512
59 #define TFTP_HEADER_LENGTH    4
60 
61 #define TFTP_RRQ   1
62 #define TFTP_WRQ   2
63 #define TFTP_DATA  3
64 #define TFTP_ACK   4
65 #define TFTP_ERROR 5
66 
67 enum tftp_error {
68   TFTP_ERROR_FILE_NOT_FOUND    = 1,
69   TFTP_ERROR_ACCESS_VIOLATION  = 2,
70   TFTP_ERROR_DISK_FULL         = 3,
71   TFTP_ERROR_ILLEGAL_OPERATION = 4,
72   TFTP_ERROR_UNKNOWN_TRFR_ID   = 5,
73   TFTP_ERROR_FILE_EXISTS       = 6,
74   TFTP_ERROR_NO_SUCH_USER      = 7
75 };
76 
77 #include <string.h>
78 
79 struct tftp_state {
80   const struct tftp_context *ctx;
81   void *handle;
82   struct pbuf *last_data;
83   struct udp_pcb *upcb;
84   ip_addr_t addr;
85   u16_t port;
86   int timer;
87   int last_pkt;
88   u16_t blknum;
89   u8_t retries;
90   u8_t mode_write;
91 };
92 
93 static struct tftp_state tftp_state;
94 
95 static void tftp_tmr(void *arg);
96 
97 static void
close_handle(void)98 close_handle(void)
99 {
100   tftp_state.port = 0;
101   ip_addr_set_any(0, &tftp_state.addr);
102 
103   if (tftp_state.last_data != NULL) {
104     pbuf_free(tftp_state.last_data);
105     tftp_state.last_data = NULL;
106   }
107 
108   sys_untimeout(tftp_tmr, NULL);
109 
110   if (tftp_state.handle) {
111     tftp_state.ctx->close(tftp_state.handle);
112     tftp_state.handle = NULL;
113     LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: closing\n"));
114   }
115 }
116 
117 static void
send_error(const ip_addr_t * addr,u16_t port,enum tftp_error code,const char * str)118 send_error(const ip_addr_t *addr, u16_t port, enum tftp_error code, const char *str)
119 {
120   int str_length = strlen(str);
121   struct pbuf *p;
122   u16_t *payload;
123 
124   p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(TFTP_HEADER_LENGTH + str_length + 1), PBUF_RAM);
125   if (p == NULL) {
126     return;
127   }
128 
129   payload = (u16_t *) p->payload;
130   payload[0] = PP_HTONS(TFTP_ERROR);
131   payload[1] = lwip_htons(code);
132   MEMCPY(&payload[2], str, str_length + 1);
133 
134   udp_sendto(tftp_state.upcb, p, addr, port);
135   pbuf_free(p);
136 }
137 
138 static void
send_ack(u16_t blknum)139 send_ack(u16_t blknum)
140 {
141   struct pbuf *p;
142   u16_t *payload;
143 
144   p = pbuf_alloc(PBUF_TRANSPORT, TFTP_HEADER_LENGTH, PBUF_RAM);
145   if (p == NULL) {
146     return;
147   }
148   payload = (u16_t *) p->payload;
149 
150   payload[0] = PP_HTONS(TFTP_ACK);
151   payload[1] = lwip_htons(blknum);
152   udp_sendto(tftp_state.upcb, p, &tftp_state.addr, tftp_state.port);
153   pbuf_free(p);
154 }
155 
156 static void
resend_data(void)157 resend_data(void)
158 {
159   struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, tftp_state.last_data->len, PBUF_RAM);
160   if (p == NULL) {
161     return;
162   }
163 
164   if (pbuf_copy(p, tftp_state.last_data) != ERR_OK) {
165     pbuf_free(p);
166     return;
167   }
168 
169   udp_sendto(tftp_state.upcb, p, &tftp_state.addr, tftp_state.port);
170   pbuf_free(p);
171 }
172 
173 static void
send_data(void)174 send_data(void)
175 {
176   u16_t *payload;
177   int ret;
178 
179   if (tftp_state.last_data != NULL) {
180     pbuf_free(tftp_state.last_data);
181   }
182 
183   tftp_state.last_data = pbuf_alloc(PBUF_TRANSPORT, TFTP_HEADER_LENGTH + TFTP_MAX_PAYLOAD_SIZE, PBUF_RAM);
184   if (tftp_state.last_data == NULL) {
185     return;
186   }
187 
188   payload = (u16_t *) tftp_state.last_data->payload;
189   payload[0] = PP_HTONS(TFTP_DATA);
190   payload[1] = lwip_htons(tftp_state.blknum);
191 
192   ret = tftp_state.ctx->read(tftp_state.handle, &payload[2], TFTP_MAX_PAYLOAD_SIZE);
193   if (ret < 0) {
194     send_error(&tftp_state.addr, tftp_state.port, TFTP_ERROR_ACCESS_VIOLATION, "Error occured while reading the file.");
195     close_handle();
196     return;
197   }
198 
199   pbuf_realloc(tftp_state.last_data, (u16_t)(TFTP_HEADER_LENGTH + ret));
200   resend_data();
201 }
202 
203 static void
recv(void * arg,struct udp_pcb * upcb,struct pbuf * p,const ip_addr_t * addr,u16_t port)204 recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
205 {
206   u16_t *sbuf = (u16_t *) p->payload;
207   int opcode;
208 
209   LWIP_UNUSED_ARG(arg);
210   LWIP_UNUSED_ARG(upcb);
211 
212   if (((tftp_state.port != 0) && (port != tftp_state.port)) ||
213       (!ip_addr_isany_val(tftp_state.addr) && !ip_addr_cmp(&tftp_state.addr, addr))) {
214     send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
215     pbuf_free(p);
216     return;
217   }
218 
219   opcode = sbuf[0];
220 
221   tftp_state.last_pkt = tftp_state.timer;
222   tftp_state.retries = 0;
223 
224   switch (opcode) {
225     case PP_HTONS(TFTP_RRQ): /* fall through */
226     case PP_HTONS(TFTP_WRQ): {
227       const char tftp_null = 0;
228       char filename[TFTP_MAX_FILENAME_LEN + 1];
229       char mode[TFTP_MAX_MODE_LEN + 1];
230       u16_t filename_end_offset;
231       u16_t mode_end_offset;
232 
233       if (tftp_state.handle != NULL) {
234         send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
235         break;
236       }
237 
238       sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
239 
240       /* find \0 in pbuf -> end of filename string */
241       filename_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), 2);
242       if ((u16_t)(filename_end_offset - 1) > sizeof(filename)) {
243         send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Filename too long/not NULL terminated");
244         break;
245       }
246       pbuf_copy_partial(p, filename, filename_end_offset - 1, 2);
247 
248       /* find \0 in pbuf -> end of mode string */
249       mode_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), filename_end_offset + 1);
250       if ((u16_t)(mode_end_offset - filename_end_offset) > sizeof(mode)) {
251         send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Mode too long/not NULL terminated");
252         break;
253       }
254       pbuf_copy_partial(p, mode, mode_end_offset - filename_end_offset, filename_end_offset + 1);
255 
256       tftp_state.handle = tftp_state.ctx->open(filename, mode, opcode == PP_HTONS(TFTP_WRQ));
257       tftp_state.blknum = 1;
258 
259       if (!tftp_state.handle) {
260         send_error(addr, port, TFTP_ERROR_FILE_NOT_FOUND, "Unable to open requested file.");
261         break;
262       }
263 
264       LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: %s request from ", (opcode == PP_HTONS(TFTP_WRQ)) ? "write" : "read"));
265       ip_addr_debug_print(TFTP_DEBUG | LWIP_DBG_STATE, addr);
266       LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, (" for '%s' mode '%s'\n", filename, mode));
267 
268       ip_addr_copy(tftp_state.addr, *addr);
269       tftp_state.port = port;
270 
271       if (opcode == PP_HTONS(TFTP_WRQ)) {
272         tftp_state.mode_write = 1;
273         send_ack(0);
274       } else {
275         tftp_state.mode_write = 0;
276         send_data();
277       }
278 
279       break;
280     }
281 
282     case PP_HTONS(TFTP_DATA): {
283       int ret;
284       u16_t blknum;
285 
286       if (tftp_state.handle == NULL) {
287         send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
288         break;
289       }
290 
291       if (tftp_state.mode_write != 1) {
292         send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a write connection");
293         break;
294       }
295 
296       blknum = lwip_ntohs(sbuf[1]);
297       if (blknum == tftp_state.blknum) {
298         pbuf_remove_header(p, TFTP_HEADER_LENGTH);
299 
300         ret = tftp_state.ctx->write(tftp_state.handle, p);
301         if (ret < 0) {
302           send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "error writing file");
303           close_handle();
304         } else {
305           send_ack(blknum);
306         }
307 
308         if (p->tot_len < TFTP_MAX_PAYLOAD_SIZE) {
309           close_handle();
310         } else {
311           tftp_state.blknum++;
312         }
313       } else if ((u16_t)(blknum + 1) == tftp_state.blknum) {
314         /* retransmit of previous block, ack again (casting to u16_t to care for overflow) */
315         send_ack(blknum);
316       } else {
317         send_error(addr, port, TFTP_ERROR_UNKNOWN_TRFR_ID, "Wrong block number");
318       }
319       break;
320     }
321 
322     case PP_HTONS(TFTP_ACK): {
323       u16_t blknum;
324       int lastpkt;
325 
326       if (tftp_state.handle == NULL) {
327         send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
328         break;
329       }
330 
331       if (tftp_state.mode_write != 0) {
332         send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a read connection");
333         break;
334       }
335 
336       blknum = lwip_ntohs(sbuf[1]);
337       if (blknum != tftp_state.blknum) {
338         send_error(addr, port, TFTP_ERROR_UNKNOWN_TRFR_ID, "Wrong block number");
339         break;
340       }
341 
342       lastpkt = 0;
343 
344       if (tftp_state.last_data != NULL) {
345         lastpkt = tftp_state.last_data->tot_len != (TFTP_MAX_PAYLOAD_SIZE + TFTP_HEADER_LENGTH);
346       }
347 
348       if (!lastpkt) {
349         tftp_state.blknum++;
350         send_data();
351       } else {
352         close_handle();
353       }
354 
355       break;
356     }
357 
358     default:
359       send_error(addr, port, TFTP_ERROR_ILLEGAL_OPERATION, "Unknown operation");
360       break;
361   }
362 
363   pbuf_free(p);
364 }
365 
366 static void
tftp_tmr(void * arg)367 tftp_tmr(void *arg)
368 {
369   LWIP_UNUSED_ARG(arg);
370 
371   tftp_state.timer++;
372 
373   if (tftp_state.handle == NULL) {
374     return;
375   }
376 
377   sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
378 
379   if ((tftp_state.timer - tftp_state.last_pkt) > (TFTP_TIMEOUT_MSECS / TFTP_TIMER_MSECS)) {
380     if ((tftp_state.last_data != NULL) && (tftp_state.retries < TFTP_MAX_RETRIES)) {
381       LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout, retrying\n"));
382       resend_data();
383       tftp_state.retries++;
384     } else {
385       LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout\n"));
386       close_handle();
387     }
388   }
389 }
390 
391 /** @ingroup tftp
392  * Initialize TFTP server.
393  * @param ctx TFTP callback struct
394  */
395 err_t
tftp_init(const struct tftp_context * ctx)396 tftp_init(const struct tftp_context *ctx)
397 {
398   err_t ret;
399 
400   /* LWIP_ASSERT_CORE_LOCKED(); is checked by udp_new() */
401   struct udp_pcb *pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
402   if (pcb == NULL) {
403     return ERR_MEM;
404   }
405 
406   ret = udp_bind(pcb, IP_ANY_TYPE, TFTP_PORT);
407   if (ret != ERR_OK) {
408     udp_remove(pcb);
409     return ret;
410   }
411 
412   tftp_state.handle    = NULL;
413   tftp_state.port      = 0;
414   tftp_state.ctx       = ctx;
415   tftp_state.timer     = 0;
416   tftp_state.last_data = NULL;
417   tftp_state.upcb      = pcb;
418 
419   udp_recv(pcb, recv, NULL);
420 
421   return ERR_OK;
422 }
423 
424 /** @ingroup tftp
425  * Deinitialize ("turn off") TFTP server.
426  */
tftp_cleanup(void)427 void tftp_cleanup(void)
428 {
429   LWIP_ASSERT("Cleanup called on non-initialized TFTP", tftp_state.upcb != NULL);
430   udp_remove(tftp_state.upcb);
431   close_handle();
432   memset(&tftp_state, 0, sizeof(tftp_state));
433 }
434 
435 #endif /* LWIP_UDP */
436