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
67enum 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
79struct 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
93static struct tftp_state tftp_state;
94
95static void tftp_tmr(void* arg);
96
97static void
98close_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
117static void
118send_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
138static void
139send_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
156static void
157resend_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
173static void
174send_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
203static void
204recv(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    {
228      const char tftp_null = 0;
229      char filename[TFTP_MAX_FILENAME_LEN];
230      char mode[TFTP_MAX_MODE_LEN];
231      u16_t filename_end_offset;
232      u16_t mode_end_offset;
233
234      if(tftp_state.handle != NULL) {
235        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
236        break;
237      }
238
239      sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
240
241      /* find \0 in pbuf -> end of filename string */
242      filename_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), 2);
243      if((u16_t)(filename_end_offset-2) > sizeof(filename)) {
244        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Filename too long/not NULL terminated");
245        break;
246      }
247      pbuf_copy_partial(p, filename, filename_end_offset-2, 2);
248
249      /* find \0 in pbuf -> end of mode string */
250      mode_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), filename_end_offset+1);
251      if((u16_t)(mode_end_offset-filename_end_offset) > sizeof(mode)) {
252        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Mode too long/not NULL terminated");
253        break;
254      }
255      pbuf_copy_partial(p, mode, mode_end_offset-filename_end_offset, filename_end_offset+1);
256
257      tftp_state.handle = tftp_state.ctx->open(filename, mode, opcode == PP_HTONS(TFTP_WRQ));
258      tftp_state.blknum = 1;
259
260      if (!tftp_state.handle) {
261        send_error(addr, port, TFTP_ERROR_FILE_NOT_FOUND, "Unable to open requested file.");
262        break;
263      }
264
265      LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: %s request from ", (opcode == PP_HTONS(TFTP_WRQ)) ? "write" : "read"));
266      ip_addr_debug_print(TFTP_DEBUG | LWIP_DBG_STATE, addr);
267      LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, (" for '%s' mode '%s'\n", filename, mode));
268
269      ip_addr_copy(tftp_state.addr, *addr);
270      tftp_state.port = port;
271
272      if (opcode == PP_HTONS(TFTP_WRQ)) {
273        tftp_state.mode_write = 1;
274        send_ack(0);
275      } else {
276        tftp_state.mode_write = 0;
277        send_data();
278      }
279
280      break;
281    }
282
283    case PP_HTONS(TFTP_DATA):
284    {
285      int ret;
286      u16_t blknum;
287
288      if (tftp_state.handle == NULL) {
289        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
290        break;
291      }
292
293      if (tftp_state.mode_write != 1) {
294        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a write connection");
295        break;
296      }
297
298      blknum = lwip_ntohs(sbuf[1]);
299      pbuf_header(p, -TFTP_HEADER_LENGTH);
300
301      ret = tftp_state.ctx->write(tftp_state.handle, p);
302      if (ret < 0) {
303        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "error writing file");
304        close_handle();
305      } else {
306        send_ack(blknum);
307      }
308
309      if (p->tot_len < TFTP_MAX_PAYLOAD_SIZE) {
310        close_handle();
311      }
312      break;
313    }
314
315    case PP_HTONS(TFTP_ACK):
316    {
317      u16_t blknum;
318      int lastpkt;
319
320      if (tftp_state.handle == NULL) {
321        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
322        break;
323      }
324
325      if (tftp_state.mode_write != 0) {
326        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a read connection");
327        break;
328      }
329
330      blknum = lwip_ntohs(sbuf[1]);
331      if (blknum != tftp_state.blknum) {
332        send_error(addr, port, TFTP_ERROR_UNKNOWN_TRFR_ID, "Wrong block number");
333        break;
334      }
335
336      lastpkt = 0;
337
338      if (tftp_state.last_data != NULL) {
339        lastpkt = tftp_state.last_data->tot_len != (TFTP_MAX_PAYLOAD_SIZE + TFTP_HEADER_LENGTH);
340      }
341
342      if (!lastpkt) {
343        tftp_state.blknum++;
344        send_data();
345      } else {
346        close_handle();
347      }
348
349      break;
350    }
351
352    default:
353      send_error(addr, port, TFTP_ERROR_ILLEGAL_OPERATION, "Unknown operation");
354      break;
355  }
356
357  pbuf_free(p);
358}
359
360static void
361tftp_tmr(void* arg)
362{
363  LWIP_UNUSED_ARG(arg);
364
365  tftp_state.timer++;
366
367  if (tftp_state.handle == NULL) {
368    return;
369  }
370
371  sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
372
373  if ((tftp_state.timer - tftp_state.last_pkt) > (TFTP_TIMEOUT_MSECS / TFTP_TIMER_MSECS)) {
374    if ((tftp_state.last_data != NULL) && (tftp_state.retries < TFTP_MAX_RETRIES)) {
375      LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout, retrying\n"));
376      resend_data();
377      tftp_state.retries++;
378    } else {
379      LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout\n"));
380      close_handle();
381    }
382  }
383}
384
385/** @ingroup tftp
386 * Initialize TFTP server.
387 * @param ctx TFTP callback struct
388 */
389err_t
390tftp_init(const struct tftp_context *ctx)
391{
392  err_t ret;
393
394  struct udp_pcb *pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
395  if (pcb == NULL) {
396    return ERR_MEM;
397  }
398
399  ret = udp_bind(pcb, IP_ANY_TYPE, TFTP_PORT);
400  if (ret != ERR_OK) {
401    udp_remove(pcb);
402    return ret;
403  }
404
405  tftp_state.handle    = NULL;
406  tftp_state.port      = 0;
407  tftp_state.ctx       = ctx;
408  tftp_state.timer     = 0;
409  tftp_state.last_data = NULL;
410  tftp_state.upcb      = pcb;
411
412  udp_recv(pcb, recv, NULL);
413
414  return ERR_OK;
415}
416
417#endif /* LWIP_UDP */
418