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/opt.h"
51 #include "lwip/udp.h"
52 #include "lwip/timeouts.h"
53 #include "lwip/debug.h"
54 #include "lwip/apps/tftp.h"
55 
56 #include <string.h>
57 #include <stdio.h>
58 
59 #ifdef WITH_LWIP_TFTP_FS
60 #include <fcntl.h>
61 #include "aos/vfs.h"
62 /* file description for tftp mount to file system. thread safe is not consider
63    as currently only one session is allowed for tftp */
64 static int fd_tftp_fs = -1;
65 #endif
66 
67 
68 struct tftp_state {
69   const tftp_context_t *ctx;
70   void *handle;
71   struct pbuf *last_data;
72   struct udp_pcb *upcb;
73   ip_addr_t addr;
74   u16_t port;
75   int timer;
76   int last_pkt;
77   u16_t blknum;
78   u8_t retries;
79   u8_t mode_write;
80 };
81 
82 static struct tftp_state tftp_state;
83 
84 static void tftp_tmr(void* arg);
85 void tftp_send_error(struct udp_pcb *pcb, const ip_addr_t *addr, u16_t port,
86                     tftp_error_t code, const char *str);
87 void tftp_send_ack(struct udp_pcb *pcb, const ip_addr_t *addr, u16_t port, u16_t blknum);
88 
89 static void
close_handle(void)90 close_handle(void)
91 {
92   tftp_state.port = 0;
93   ip_addr_set_any(0, &tftp_state.addr);
94 
95   if(tftp_state.last_data != NULL) {
96     pbuf_free(tftp_state.last_data);
97     tftp_state.last_data = NULL;
98   }
99 
100   sys_untimeout(tftp_tmr, NULL);
101 
102   if (tftp_state.handle) {
103     tftp_state.ctx->close(tftp_state.handle);
104     tftp_state.handle = NULL;
105     LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp server: closing\n"));
106   }
107 }
108 
109 static void
resend_data(void)110 resend_data(void)
111 {
112   struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, tftp_state.last_data->len, PBUF_RAM);
113   if(p == NULL) {
114     return;
115   }
116 
117   if(pbuf_copy(p, tftp_state.last_data) != ERR_OK) {
118     pbuf_free(p);
119     return;
120   }
121 
122   udp_sendto(tftp_state.upcb, p, &tftp_state.addr, tftp_state.port);
123   pbuf_free(p);
124 }
125 
126 static void
send_data(void)127 send_data(void)
128 {
129   u16_t *payload;
130   int ret;
131 
132   if(tftp_state.last_data != NULL) {
133     pbuf_free(tftp_state.last_data);
134   }
135 
136   tftp_state.last_data = pbuf_alloc(PBUF_TRANSPORT, TFTP_HEADER_LENGTH + TFTP_MAX_PAYLOAD_SIZE, PBUF_RAM);
137   if(tftp_state.last_data == NULL) {
138     return;
139   }
140 
141   payload = (u16_t *) tftp_state.last_data->payload;
142   payload[0] = PP_HTONS(TFTP_DATA);
143   payload[1] = lwip_htons(tftp_state.blknum);
144 
145   ret = tftp_state.ctx->read(tftp_state.handle, &payload[2], TFTP_MAX_PAYLOAD_SIZE);
146   if (ret < 0) {
147     tftp_send_error(tftp_state.upcb, &tftp_state.addr, tftp_state.port, TFTP_ERROR_ACCESS_VIOLATION, "Error occured while reading the file.");
148     close_handle();
149     return;
150   }
151 
152   pbuf_realloc(tftp_state.last_data, (u16_t)(TFTP_HEADER_LENGTH + ret));
153   resend_data();
154 }
155 
156 static void
recv(void * arg,struct udp_pcb * upcb,struct pbuf * p,const ip_addr_t * addr,u16_t port)157 recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
158 {
159   u16_t *sbuf = (u16_t *) p->payload;
160   int opcode;
161 
162   LWIP_UNUSED_ARG(arg);
163   LWIP_UNUSED_ARG(upcb);
164 
165   if (((tftp_state.port != 0) && (port != tftp_state.port)) ||
166       (!ip_addr_isany_val(tftp_state.addr) && !ip_addr_cmp(&tftp_state.addr, addr))) {
167     tftp_send_error(tftp_state.upcb, addr, port,
168             TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
169     pbuf_free(p);
170     return;
171   }
172 
173   opcode = sbuf[0];
174 
175   tftp_state.last_pkt = tftp_state.timer;
176   tftp_state.retries = 0;
177 
178   switch (opcode) {
179     case PP_HTONS(TFTP_RRQ): /* fall through */
180     case PP_HTONS(TFTP_WRQ):
181     {
182       const char tftp_null = 0;
183       char filename[TFTP_MAX_FILENAME_LEN] = {0};
184       char mode[TFTP_MAX_MODE_LEN];
185       u16_t filename_end_offset;
186       u16_t mode_end_offset;
187 
188       if(tftp_state.handle != NULL) {
189         tftp_send_error(tftp_state.upcb, addr, port,
190                 TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
191         break;
192       }
193 
194       sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
195 
196       /* find \0 in pbuf -> end of filename string */
197       filename_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), 2);
198       if((u16_t)(filename_end_offset-2) > sizeof(filename)) {
199         tftp_send_error(tftp_state.upcb, addr, port,
200                 TFTP_ERROR_ACCESS_VIOLATION, "Filename too long/not NULL terminated");
201         break;
202       }
203       pbuf_copy_partial(p, filename, filename_end_offset-2, 2);
204 
205       /* find \0 in pbuf -> end of mode string */
206       mode_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), filename_end_offset+1);
207       if((u16_t)(mode_end_offset-filename_end_offset) > sizeof(mode)) {
208         tftp_send_error(tftp_state.upcb, addr, port,
209                 TFTP_ERROR_ACCESS_VIOLATION, "Mode too long/not NULL terminated");
210         break;
211       }
212       pbuf_copy_partial(p, mode, mode_end_offset-filename_end_offset, filename_end_offset+1);
213 
214       tftp_state.handle = tftp_state.ctx->open(filename, mode, opcode == PP_HTONS(TFTP_WRQ));
215       tftp_state.blknum = 1;
216 
217       if (!tftp_state.handle) {
218         tftp_send_error(tftp_state.upcb, addr, port,
219                 TFTP_ERROR_FILE_NOT_FOUND, "Unable to open requested file.");
220         break;
221       }
222 
223       LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: %s request from ",
224                   (opcode == PP_HTONS(TFTP_WRQ)) ? "write" : "read"));
225       ip_addr_debug_print(TFTP_DEBUG | LWIP_DBG_STATE, addr);
226       LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, (" for '%s' mode '%s'\n", filename, mode));
227 
228       ip_addr_copy(tftp_state.addr, *addr);
229       tftp_state.port = port;
230 
231       if (opcode == PP_HTONS(TFTP_WRQ)) {
232         tftp_state.mode_write = 1;
233         tftp_send_ack(tftp_state.upcb, &tftp_state.addr, tftp_state.port, 0);
234       } else {
235         tftp_state.mode_write = 0;
236         send_data();
237       }
238 
239       break;
240     }
241 
242     case PP_HTONS(TFTP_DATA):
243     {
244       int ret;
245       u16_t blknum;
246 
247       if (tftp_state.handle == NULL) {
248         tftp_send_error(tftp_state.upcb, addr, port,
249                 TFTP_ERROR_ACCESS_VIOLATION, "No connection");
250         break;
251       }
252 
253       if (tftp_state.mode_write != 1) {
254         tftp_send_error(tftp_state.upcb, addr, port,
255                 TFTP_ERROR_ACCESS_VIOLATION, "Not a write connection");
256         break;
257       }
258 
259       blknum = lwip_ntohs(sbuf[1]);
260       pbuf_header(p, -TFTP_HEADER_LENGTH);
261 
262       ret = tftp_state.ctx->write(tftp_state.handle, p);
263       if (ret < 0) {
264         tftp_send_error(tftp_state.upcb, addr, port,
265                 TFTP_ERROR_ACCESS_VIOLATION, "error writing file");
266         close_handle();
267       } else {
268         tftp_send_ack(tftp_state.upcb, &tftp_state.addr, tftp_state.port, blknum);
269       }
270 
271       if (p->tot_len < TFTP_MAX_PAYLOAD_SIZE) {
272         close_handle();
273       }
274       break;
275     }
276 
277     case PP_HTONS(TFTP_ACK):
278     {
279       u16_t blknum;
280       int lastpkt;
281 
282       if (tftp_state.handle == NULL) {
283         tftp_send_error(tftp_state.upcb, addr, port,
284                 TFTP_ERROR_ACCESS_VIOLATION, "No connection");
285         break;
286       }
287 
288       if (tftp_state.mode_write != 0) {
289         tftp_send_error(tftp_state.upcb, addr, port,
290                 TFTP_ERROR_ACCESS_VIOLATION, "Not a read connection");
291         break;
292       }
293 
294       blknum = lwip_ntohs(sbuf[1]);
295       if (blknum != tftp_state.blknum) {
296         tftp_send_error(tftp_state.upcb, addr, port,
297                 TFTP_ERROR_UNKNOWN_TRFR_ID, "Wrong block number");
298         break;
299       }
300 
301       lastpkt = 0;
302 
303       if (tftp_state.last_data != NULL) {
304         lastpkt = tftp_state.last_data->tot_len != (TFTP_MAX_PAYLOAD_SIZE + TFTP_HEADER_LENGTH);
305       }
306 
307       if (!lastpkt) {
308         tftp_state.blknum++;
309         send_data();
310       } else {
311         close_handle();
312       }
313 
314       break;
315     }
316 
317     default:
318       tftp_send_error(tftp_state.upcb, addr, port,
319               TFTP_ERROR_ILLEGAL_OPERATION, "Unknown operation");
320       break;
321   }
322 
323   pbuf_free(p);
324 }
325 
326 static void
tftp_tmr(void * arg)327 tftp_tmr(void* arg)
328 {
329   LWIP_UNUSED_ARG(arg);
330 
331   tftp_state.timer++;
332 
333   if (tftp_state.handle == NULL) {
334     return;
335   }
336 
337   sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
338 
339   if ((tftp_state.timer - tftp_state.last_pkt) > (TFTP_TIMEOUT_MSECS / TFTP_TIMER_MSECS)) {
340     if ((tftp_state.last_data != NULL) && (tftp_state.retries < TFTP_MAX_RETRIES)) {
341       LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout, retrying\n"));
342       resend_data();
343       tftp_state.retries++;
344     } else {
345       LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout\n"));
346       close_handle();
347     }
348   }
349 }
350 
tftp_fopen(const char * fname,const char * mode,u8_t write)351 static void* tftp_fopen(const char* fname, const char* mode, u8_t write)
352 {
353 #ifdef WITH_LWIP_TFTP_FS
354     /* mount to the file system if both comp vfs and spiffs exit */
355     /* omit mode of BIN or ASCII */
356     if(write){
357         fd_tftp_fs = aos_open(fname, O_RDWR);
358     }else{
359         fd_tftp_fs = aos_open(fname, O_RDONLY);
360     }
361     if (fd_tftp_fs >= 0) {
362         printf(">>>open fd %d\n", fd_tftp_fs);
363         return &fd_tftp_fs;
364     } else {
365         LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp fs failed: %d",fd_tftp_fs));
366         fd_tftp_fs = -1;
367         return NULL;
368     }
369 #else
370     FILE *fp = NULL;
371 
372     if (strncmp(mode, "netascii", 8) == 0) {
373         fp = fopen(fname, write == 0 ? "r" : "w");
374     } else if (strncmp(mode, "octet", 5) == 0) {
375         fp = fopen(fname, write == 0 ? "rb" : "wb");
376     }
377     return (void*)fp;
378 #endif
379 }
380 
tftp_fclose(void * handle)381 static void tftp_fclose(void* handle)
382 {
383 #ifdef WITH_LWIP_TFTP_FS
384     aos_close(*((int*)handle));
385 #else
386     fclose((FILE*)handle);
387 #endif
388 }
389 
tftp_fread(void * handle,void * buf,int bytes)390 static int tftp_fread(void* handle, void* buf, int bytes)
391 {
392 #ifdef WITH_LWIP_TFTP_FS
393     return aos_read(*((int*)handle), buf, bytes);
394 #else
395     size_t readbytes;
396     readbytes = fread(buf, 1, (size_t)bytes, (FILE*)handle);
397     return (int)readbytes;
398 #endif
399 }
400 
tftp_fwrite(void * handle,struct pbuf * p)401 static int tftp_fwrite(void* handle, struct pbuf* p)
402 {
403     char buff[512];
404     size_t writebytes = -1;
405     pbuf_copy_partial(p, buff, p->tot_len, 0);
406 #ifdef WITH_LWIP_TFTP_FS
407     writebytes = aos_write(*((int*)handle), buff, p->tot_len);
408 #else
409     writebytes = fwrite(buff, 1, p->tot_len, (FILE *)handle);
410 #endif
411     return (int)writebytes;
412 }
413 
414 const tftp_context_t server_ctx = {
415     .open = tftp_fopen,
416     .close = tftp_fclose,
417     .read = tftp_fread,
418     .write = tftp_fwrite
419 };
420 
421 /** @ingroup tftp
422  * Initialize TFTP server.
423  * @param ctx TFTP callback struct
424  */
425 err_t
tftp_server_start(void)426 tftp_server_start(void)
427 {
428   err_t ret;
429 
430   if (tftp_state.upcb != NULL)
431       return ERR_OK;
432 
433   struct udp_pcb *pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
434   if (pcb == NULL) {
435     return ERR_MEM;
436   }
437 
438   ret = udp_bind(pcb, IP_ANY_TYPE, TFTP_PORT);
439   if (ret != ERR_OK) {
440     udp_remove(pcb);
441     return ret;
442   }
443 
444   tftp_state.handle    = NULL;
445   tftp_state.port      = 0;
446   tftp_state.ctx       = &server_ctx;
447   tftp_state.timer     = 0;
448   tftp_state.last_data = NULL;
449   tftp_state.upcb      = pcb;
450   udp_recv(pcb, recv, NULL);
451 
452   return ERR_OK;
453 }
454 
455 void
tftp_server_stop(void)456 tftp_server_stop(void)
457 {
458     if (tftp_state.handle)
459         close_handle();
460 
461     if (tftp_state.upcb != NULL) {
462         udp_remove(tftp_state.upcb);
463         memset(&tftp_state, 0, sizeof(tftp_state));
464     }
465 }
466