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

Side by Side Diff: app/inetsrv/tftp.c

Issue 1346853002: [app][inetsrv] add tftp server. (Closed) Base URL: https://github.com/travisg/lk.git@master
Patch Set: 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 | « app/inetsrv/tftp.h ('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 <stdlib.h>
27 #include <string.h>
28 #include <list.h>
29 #include <compiler.h>
30 #include <endian.h>
31 #include <lib/minip.h>
32 #include <lib/cksum.h>
33 #include <platform.h>
34
35 #include "tftp.h"
36
37 #define TFTP_BUFSIZE 512
cpu_(ooo_6.6-7.5) 2015/09/16 18:04:11 remove
38
39 // TFTP Opcodes:
40 #define TFTP_OPCODE_RRQ 1UL
41 #define TFTP_OPCODE_WRQ 2UL
42 #define TFTP_OPCODE_DATA 3UL
43 #define TFTP_OPCODE_ACK 4UL
44 #define TFTP_OPCODE_ERROR 5UL
45
46 // TFTP Errors:
47 #define TFTP_ERROR_UNDEF 0UL
48 #define TFTP_ERROR_NOT_FOUND 1UL
49 #define TFTP_ERROR_ACCESS 2UL
50 #define TFTP_ERROR_FULL 3UL
51 #define TFTP_ERROR_ILLEGAL_OP 4UL
52 #define TFTP_ERROR_UNKNOWN_XFER 4UL
53 #define TFTP_ERROR_EXISTS 6UL
54 #define TFTP_ERROR_NO_SUCH_USER 7UL
55
56 #define TFTP_PORT 69
57
58 #define RD_U16(ptr) \
59 (uint16_t)(((uint16_t)*((uint8_t*)(ptr)+1)<<8)|(uint16_t)*(uint8_t*)(ptr))
60
61 static struct list_node tftp_list = LIST_INITIAL_VALUE(tftp_list);
62
63 // Represents tftp jobs in progress or possible.
cpu_(ooo_6.6-7.5) 2015/09/16 18:04:11 elaborate, confusing
64 typedef struct {
65 struct list_node list;
66 // Registration info.
67 const char* file_name;
68 tftp_callback_t callback;
69 void *arg;
70 // Current job info.
71 udp_socket_t* socket;
72 uint32_t src_addr;
73 uint16_t src_port;
74 uint16_t pkt_count;
75 } tftp_job_t;
76
77 uint16_t next_port = 2224;
78
79 static void send_ack(udp_socket_t* socket, uint16_t count)
80 {
81 status_t st;
82 uint16_t ack[] = {htons(TFTP_OPCODE_ACK), htons(count)};
83 st = udp_send(ack, sizeof(ack), socket);
84 if (st < 0)
85 TRACEF("send_ack failed: %d\n", st);
cja 2015/09/16 21:19:41 LTRACEF for code you're checking in.
cja 2015/09/16 21:19:41 nit: inconsistent use of single line conditionals
cpu_(ooo_6.6-7.5) 2015/09/17 01:22:30 Acknowledged.
cpu_(ooo_6.6-7.5) 2015/09/17 01:22:31 Acknowledged.
86 }
87
88 static void end_transfer(tftp_job_t* job)
89 {
90 udp_close(job->socket);
91 job->socket = NULL;
92 job->callback(NULL, 0UL, job->arg);
93 }
94
95 static void send_error(udp_socket_t* socket, uint16_t code)
96 {
cpu_(ooo_6.6-7.5) 2015/09/16 18:04:11 move send error next to send ack
97 status_t st;
98 uint16_t ncode = htons(code);
99 uint16_t err[] = {htons(TFTP_OPCODE_ERROR), ncode,
100 0x7245, 0x2072, 0x3030 + ncode, 0 };
101 st = udp_send(err, sizeof(err), socket);
cpu_(ooo_6.6-7.5) 2015/09/16 18:04:11 xplain magic numbers
102 if (st < 0)
103 TRACEF("send_err failed: %d\n", st);
cja 2015/09/16 21:19:41 LTRACEF for code you're checking in.
cpu_(ooo_6.6-7.5) 2015/09/17 01:22:31 Acknowledged.
104 }
105
106 static void udp_wrq_callback(void *data, size_t len,
107 uint32_t srcaddr, uint16_t srcport,
108 void *arg)
109 {
cja 2015/09/16 21:19:41 What if data is NULL? It's dereferenced below with
cpu_(ooo_6.6-7.5) 2015/09/17 01:22:31 it will come with len == 0 ?
cja 2015/09/17 18:11:56 Perhaps it's a personal preference, but I'd still
110 char* data_c = data;
cpu_(ooo_6.6-7.5) 2015/09/16 18:04:11 in comment do the layout of the packet
111 tftp_job_t* job = arg;
cja 2015/09/16 21:19:41 arg also needs a NULL check.
112 job->pkt_count++;
113
114 if (len < 4) {
115 // Not to spec. Ignore.
116 return;
117 }
118
119 if (!job->socket) {
120 // It is possible to have the client sent another packet
121 // after we called end_transfer().
122 return;
123 }
124
125 if ((srcaddr != job->src_addr) || (srcport != job->src_port)) {
126 TRACEF("invalid source\n");
cja 2015/09/16 21:19:41 LTRACEF for code you're checking in.
cpu_(ooo_6.6-7.5) 2015/09/17 01:22:30 Acknowledged.
127 send_error(job->socket, TFTP_ERROR_UNKNOWN_XFER);
128 end_transfer(job);
cpu_(ooo_6.6-7.5) 2015/09/16 18:04:11 not sure spec requires to abort job.
129 return;
130 }
131
132 if (RD_U16(data_c) != htons(TFTP_OPCODE_DATA)) {
133 TRACEF("invalid opcode\n");
cja 2015/09/16 21:19:41 LTRACEF for code you're checking in.
cpu_(ooo_6.6-7.5) 2015/09/17 01:22:31 Acknowledged.
134 send_error(job->socket, TFTP_ERROR_ILLEGAL_OP);
135 end_transfer(job);
136 return;
137 }
138
139 send_ack(job->socket, job->pkt_count);
140
141 if (job->callback(&data_c[4], len, job->arg) < 0) {
cpu_(ooo_6.6-7.5) 2015/09/16 18:04:11 bug: len - 4
142 // The client wants to abort.
143 send_error(job->socket, TFTP_ERROR_FULL);
144 end_transfer(job);
145 }
146
147 if (len != 516) {
cja 2015/09/16 21:19:41 What does 516 mean?
cpu_(ooo_6.6-7.5) 2015/09/17 01:22:31 good point, added a comment.
148 end_transfer(job);
149 }
150 }
151
152 static tftp_job_t* get_job_by_name(const char* file_name)
153 {
cja 2015/09/16 21:19:41 Need NULL check for file_name.
cpu_(ooo_6.6-7.5) 2015/09/17 01:22:31 Acknowledged.
154 tftp_job_t *entry;
155 list_for_every_entry(&tftp_list, entry, tftp_job_t, list) {
156 if (strcmp(entry->file_name, file_name) == 0) {
157 return entry;
158 }
159 }
160 return NULL;
161 }
162
163 static void udp_svc_callback(void *data, size_t len,
164 uint32_t srcaddr, uint16_t srcport,
165 void *arg)
166 {
167 status_t st;
168 uint16_t opcode;
169 udp_socket_t* socket;
170 tftp_job_t* job;
171
172 st = udp_open(srcaddr, next_port, srcport, &socket);
173 if (st < 0) {
174 TRACEF("error opening send socket %d\n", st);
cja 2015/09/16 21:19:41 LTRACEF for code you're checking in.
cpu_(ooo_6.6-7.5) 2015/09/17 01:22:30 Acknowledged.
175 return;
176 }
177
178 opcode = ntohs(RD_U16(data));
179
180 if (opcode != TFTP_OPCODE_WRQ)
181 {
182 // Operation not suported.
183 TRACEF("op not supported, opcode: %d\n", opcode);
cja 2015/09/16 21:19:41 LTRACEF for code you're checking in.
cpu_(ooo_6.6-7.5) 2015/09/17 01:22:30 Acknowledged.
184 send_error(socket, TFTP_ERROR_ACCESS);
185 udp_close(socket);
186 return;
187 }
188
cpu_(ooo_6.6-7.5) 2015/09/16 18:04:11 put in the comment the packet layout.
189 // Look for a client that can hadle the file. TODO: |data|
190 // needs to be null terminated. A malicious client can crash us.
191 job = get_job_by_name(((char*)data) + 2);
192
193 if (!job) {
194 // Nobody claims to handle that file.
195 TRACEF("no client registered for file\n");
196 send_error(socket, TFTP_ERROR_UNKNOWN_XFER);
197 udp_close(socket);
198 return;
199 }
200
201 if (job->socket) {
202 // There is already an ongoing job.
203 // TODO: garbage collect the existing one if too long since the
204 // last packet was processed.
cja 2015/09/16 21:19:41 is this todo a "nice to have" or required for oper
cpu_(ooo_6.6-7.5) 2015/09/17 01:22:31 It is is a very nice to have, but it works as-is t
205 TRACEF("existing job in progress\n");
cja 2015/09/16 21:19:41 LTRACEF for code you're checking in.
cpu_(ooo_6.6-7.5) 2015/09/17 01:22:31 Acknowledged.
206 send_error(socket, TFTP_ERROR_EXISTS);
207 udp_close(socket);
208 return;
209 }
210
211 TRACEF("write op accepted, port %d\n", srcport);
cja 2015/09/16 21:19:41 LTRACEF for code you're checking in.
212 // Request accepted. The rest of the transfer happens between
213 // next_port <----> srcport via |udp_wrq_callback|.
cpu_(ooo_6.6-7.5) 2015/09/16 18:04:11 remove || and put ()
214
215 job->socket = socket;
216 job->src_addr = srcaddr;
217 job->src_port = srcport;
218 job->pkt_count = 0UL;
219
220 st = udp_listen(next_port, &udp_wrq_callback, job);
221 if (st < 0) {
222 TRACEF("error listening on port\n");
cja 2015/09/16 21:19:41 LTRACEF for code you're checking in.
223 return;
224 }
225
226 send_ack(socket, 0UL);
227 next_port++;
228 }
229
230 int tftp_set_write_client(const char* file_name, tftp_callback_t cb, void* arg)
231 {
cja 2015/09/16 21:19:41 Arg check.
cpu_(ooo_6.6-7.5) 2015/09/17 01:22:30 Acknowledged.
232 tftp_job_t *job;
233
234 list_for_every_entry(&tftp_list, job, tftp_job_t, list) {
235 if (strcmp(file_name, job->file_name) == 0) {
236 // TODO: un-registration.
cja 2015/09/16 21:19:41 Important?
cpu_(ooo_6.6-7.5) 2015/09/17 01:22:30 In the next CL. I need to make sure I write the ot
237 return -1;
238 }
239 }
240
241 if ((job = malloc(sizeof(tftp_job_t))) == NULL) {
242 return -1;
243 }
244
245 memset(job, 0, sizeof(tftp_job_t));
246 job->file_name = file_name;
247 job->callback = cb;
248 job->arg = arg;
249
250 list_add_tail(&tftp_list, &job->list);
251 return 0;
252 }
253
254 static unsigned long test_crc = 0UL;
255
256 int test_tftp_client(void* data, size_t len, void* arg) {
257 if (!data) {
258 TRACEF("--test transfer done-- crc32 = %lu\n", test_crc);
259 test_crc = 0UL;
260 }
261
262 test_crc = crc32(test_crc, data,len);
263 return 0;
264 }
265
266 int tftp_server_init(void *arg)
267 {
268 tftp_set_write_client("tftp_test.txt", &test_tftp_client, NULL);
cpu_(ooo_6.6-7.5) 2015/09/16 18:04:11 268 is test code, it can be removed.
269
270 status_t st = udp_listen(TFTP_PORT, &udp_svc_callback, 0);
271 return st;
272 }
273
OLDNEW
« no previous file with comments | « app/inetsrv/tftp.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698