Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(307)

Side by Side Diff: lib/tftp/tftp.c

Issue 1346853002: [app][inetsrv] add tftp server. (Closed) Base URL: https://github.com/travisg/lk.git@master
Patch Set: review changes Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/tftp/rules.mk ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * Copyright (c) 2015 Carlos Pizano-Uribe <cpu@chromium.org>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 #include <err.h>
25 #include <trace.h>
26 #include <assert.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <list.h>
30 #include <compiler.h>
31 #include <endian.h>
32 #include <lib/minip.h>
33 #include <lib/cksum.h>
34 #include <platform.h>
35
36 #include <lib/tftp.h>
37
38 #define LOCAL_TRACE 0
39
40 // TFTP Opcodes:
41 #define TFTP_OPCODE_RRQ 1UL
42 #define TFTP_OPCODE_WRQ 2UL
43 #define TFTP_OPCODE_DATA 3UL
44 #define TFTP_OPCODE_ACK 4UL
45 #define TFTP_OPCODE_ERROR 5UL
46
47 // TFTP Errors:
48 #define TFTP_ERROR_UNDEF 0UL
49 #define TFTP_ERROR_NOT_FOUND 1UL
50 #define TFTP_ERROR_ACCESS 2UL
51 #define TFTP_ERROR_FULL 3UL
52 #define TFTP_ERROR_ILLEGAL_OP 4UL
53 #define TFTP_ERROR_UNKNOWN_XFER 4UL
54 #define TFTP_ERROR_EXISTS 6UL
55 #define TFTP_ERROR_NO_SUCH_USER 7UL
56
57 #define TFTP_PORT 69
58
59 #define RD_U16(ptr) \
60 (uint16_t)(((uint16_t)*((uint8_t*)(ptr)+1)<<8)|(uint16_t)*(uint8_t*)(ptr))
61
62 static struct list_node tftp_list = LIST_INITIAL_VALUE(tftp_list);
63
64 // Represents tftp jobs and clients of them. If |socket| is not null the
65 // job is in progress and members below it are valid.
66 typedef struct {
67 struct list_node list;
68 // Registration info.
69 const char* file_name;
70 tftp_callback_t callback;
71 void *arg;
72 // Current job info.
73 udp_socket_t* socket;
74 uint32_t src_addr;
75 uint16_t src_port;
76 uint16_t pkt_count;
77 } tftp_job_t;
78
79 uint16_t next_port = 2224;
80
81 static void send_ack(udp_socket_t* socket, uint16_t count)
82 {
83 // Packet is [4][count].
84 status_t st;
85 uint16_t ack[] = {htons(TFTP_OPCODE_ACK), htons(count)};
86 st = udp_send(ack, sizeof(ack), socket);
87 if (st < 0) {
88 LTRACEF("send_ack failed: %d\n", st);
89 }
90 }
91
92 static void send_error(udp_socket_t* socket, uint16_t code)
93 {
94 // Packet is [5][error code][error in ascii-string][0].
95 status_t st;
96 uint16_t ncode = htons(code);
97 uint16_t err[] = {htons(TFTP_OPCODE_ERROR), ncode,
98 0x7245, 0x2072, 0x3030 + ncode, 0 };
99 st = udp_send(err, sizeof(err), socket);
100 if (st < 0) {
101 LTRACEF("send_err failed: %d\n", st);
102 }
103 }
104
105 static void end_transfer(tftp_job_t* job)
106 {
107 udp_close(job->socket);
108 job->socket = NULL;
109 job->src_addr = 0UL;
110 job->callback(NULL, 0UL, job->arg);
111 }
112
113 static void udp_wrq_callback(void *data, size_t len,
114 uint32_t srcaddr, uint16_t srcport,
115 void *arg)
116 {
117 // Packet is [3][count][data]. All packets but the last have 512
118 // bytes of data, including zero data.
119 char* data_c = data;
120 tftp_job_t* job = arg;
121 job->pkt_count++;
122
123 if (len < 4) {
124 // Not to spec. Ignore.
125 return;
126 }
127
128 if (!job->socket) {
129 // It is possible to have the client sent another packet
130 // after we called end_transfer().
131 return;
132 }
133
134 if ((srcaddr != job->src_addr) || (srcport != job->src_port)) {
135 LTRACEF("invalid source\n");
136 send_error(job->socket, TFTP_ERROR_UNKNOWN_XFER);
137 end_transfer(job);
138 return;
139 }
140
141 if (RD_U16(data_c) != htons(TFTP_OPCODE_DATA)) {
142 LTRACEF("invalid opcode\n");
143 send_error(job->socket, TFTP_ERROR_ILLEGAL_OP);
144 end_transfer(job);
145 return;
146 }
147
148 send_ack(job->socket, job->pkt_count);
149
150 if (job->callback(&data_c[4], len - 4, job->arg) < 0) {
151 // The client wants to abort.
152 send_error(job->socket, TFTP_ERROR_FULL);
153 end_transfer(job);
154 }
155
156 // 512 bytes payload plus 4 of fixed header. The last packet.
157 // has always less than 512 bytes of payload.
158 if (len != 516) {
159 end_transfer(job);
160 }
161 }
162
163 static tftp_job_t* get_job_by_name(const char* file_name)
164 {
165 DEBUG_ASSERT(file_name);
166 tftp_job_t *entry;
167 list_for_every_entry(&tftp_list, entry, tftp_job_t, list) {
168 if (strcmp(entry->file_name, file_name) == 0) {
169 return entry;
170 }
171 }
172 return NULL;
173 }
174
175 static void udp_svc_callback(void *data, size_t len,
176 uint32_t srcaddr, uint16_t srcport,
177 void *arg)
178 {
179 status_t st;
180 uint16_t opcode;
181 udp_socket_t* socket;
182 tftp_job_t* job;
183
184 st = udp_open(srcaddr, next_port, srcport, &socket);
185 if (st < 0) {
186 LTRACEF("error opening send socket %d\n", st);
187 return;
188 }
189
190 opcode = ntohs(RD_U16(data));
191
192 if (opcode != TFTP_OPCODE_WRQ)
193 {
194 // Operation not suported.
195 LTRACEF("op not supported, opcode: %d\n", opcode);
196 send_error(socket, TFTP_ERROR_ACCESS);
197 udp_close(socket);
198 return;
199 }
200
201 // Look for a client that can hadle the file. TODO: |data|
202 // needs to be null terminated. A malicious client can crash us.
203 job = get_job_by_name(((char*)data) + 2);
204
205 if (!job) {
206 // Nobody claims to handle that file.
207 LTRACEF("no client registered for file\n");
208 send_error(socket, TFTP_ERROR_UNKNOWN_XFER);
209 udp_close(socket);
210 return;
211 }
212
213 if (job->socket) {
214 // There is already an ongoing job.
215 // TODO: garbage collect the existing one if too long since the
216 // last packet was processed.
217 LTRACEF("existing job in progress\n");
218 send_error(socket, TFTP_ERROR_EXISTS);
219 udp_close(socket);
220 return;
221 }
222
223 LTRACEF("write op accepted, port %d\n", srcport);
224 // Request accepted. The rest of the transfer happens between
225 // next_port <----> srcport via udp_wrq_callback().
226
227 job->socket = socket;
228 job->src_addr = srcaddr;
229 job->src_port = srcport;
230 job->pkt_count = 0UL;
231
232 st = udp_listen(next_port, &udp_wrq_callback, job);
233 if (st < 0) {
234 LTRACEF("error listening on port\n");
235 return;
236 }
237
238 send_ack(socket, 0UL);
239 next_port++;
240 }
241
242 int tftp_set_write_client(const char* file_name, tftp_callback_t cb, void* arg)
243 {
244 DEBUG_ASSERT(file_name);
245 DEBUG_ASSERT(cb);
246
247 tftp_job_t *job;
248
249 list_for_every_entry(&tftp_list, job, tftp_job_t, list) {
250 if (strcmp(file_name, job->file_name) == 0) {
251 // TODO: un-registration.
252 return -1;
253 }
254 }
255
256 if ((job = malloc(sizeof(tftp_job_t))) == NULL) {
257 return -1;
258 }
259
260 memset(job, 0, sizeof(tftp_job_t));
261 job->file_name = file_name;
262 job->callback = cb;
263 job->arg = arg;
264
265 list_add_tail(&tftp_list, &job->list);
266 return 0;
267 }
268
269 static unsigned long test_crc = 0UL;
270
271 int test_tftp_client(void* data, size_t len, void* arg) {
272 if (!data) {
273 printf("--test transfer done-- crc32 = %lu\n", test_crc);
274 test_crc = 0UL;
275 }
276
277 test_crc = crc32(test_crc, data,len);
278 return 0;
279 }
280
281 int tftp_server_init(void *arg)
282 {
283 tftp_set_write_client("tftp_test.txt", &test_tftp_client, NULL);
284
285 status_t st = udp_listen(TFTP_PORT, &udp_svc_callback, 0);
286 return st;
287 }
288
OLDNEW
« no previous file with comments | « lib/tftp/rules.mk ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698