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