blob: 1a79c45cfb6034e1746f2f0e2f48f10f65e60203 [file] [log] [blame]
bellardc7f74642004-08-24 21:57:12 +00001/*
2 * tftp.c - a simple, read-only tftp server for qemu
ths5fafdf22007-09-16 21:08:06 +00003 *
bellardc7f74642004-08-24 21:57:12 +00004 * Copyright (c) 2004 Magnus Damm <damm@opensource.se>
ths5fafdf22007-09-16 21:08:06 +00005 *
bellardc7f74642004-08-24 21:57:12 +00006 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24
25#include <slirp.h>
Jan Kiszka0d62c4c2009-06-24 14:42:29 +020026#include "qemu-common.h"
bellardc7f74642004-08-24 21:57:12 +000027
Jan Kiszka460fec62009-06-24 14:42:31 +020028static inline int tftp_session_in_use(struct tftp_session *spt)
29{
30 return (spt->slirp != NULL);
31}
ths3b46e622007-09-17 08:09:54 +000032
Jan Kiszka460fec62009-06-24 14:42:31 +020033static inline void tftp_session_update(struct tftp_session *spt)
bellardc7f74642004-08-24 21:57:12 +000034{
bellarda3504c82004-08-25 20:55:44 +000035 spt->timestamp = curtime;
bellardc7f74642004-08-24 21:57:12 +000036}
37
38static void tftp_session_terminate(struct tftp_session *spt)
39{
Hervé Poussineau78be0562012-09-10 20:52:25 +020040 if (spt->fd >= 0) {
41 close(spt->fd);
42 spt->fd = -1;
43 }
Anthony Liguori7267c092011-08-20 22:09:37 -050044 g_free(spt->filename);
Jan Kiszka460fec62009-06-24 14:42:31 +020045 spt->slirp = NULL;
bellardc7f74642004-08-24 21:57:12 +000046}
47
Jan Kiszka460fec62009-06-24 14:42:31 +020048static int tftp_session_allocate(Slirp *slirp, struct tftp_t *tp)
bellardc7f74642004-08-24 21:57:12 +000049{
50 struct tftp_session *spt;
bellardc7f74642004-08-24 21:57:12 +000051 int k;
52
bellardc7f74642004-08-24 21:57:12 +000053 for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
Jan Kiszka460fec62009-06-24 14:42:31 +020054 spt = &slirp->tftp_sessions[k];
bellardc7f74642004-08-24 21:57:12 +000055
Jan Kiszka460fec62009-06-24 14:42:31 +020056 if (!tftp_session_in_use(spt))
bellarda3504c82004-08-25 20:55:44 +000057 goto found;
bellardc7f74642004-08-24 21:57:12 +000058
59 /* sessions time out after 5 inactive seconds */
Jan Kiszka93679642009-06-24 14:42:30 +020060 if ((int)(curtime - spt->timestamp) > 5000) {
Hervé Poussineau78be0562012-09-10 20:52:25 +020061 tftp_session_terminate(spt);
bellarda3504c82004-08-25 20:55:44 +000062 goto found;
Jan Kiszka93679642009-06-24 14:42:30 +020063 }
bellardc7f74642004-08-24 21:57:12 +000064 }
65
66 return -1;
67
68 found:
69 memset(spt, 0, sizeof(*spt));
70 memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip));
Hervé Poussineau78be0562012-09-10 20:52:25 +020071 spt->fd = -1;
bellardc7f74642004-08-24 21:57:12 +000072 spt->client_port = tp->udp.uh_sport;
Jan Kiszka460fec62009-06-24 14:42:31 +020073 spt->slirp = slirp;
bellardc7f74642004-08-24 21:57:12 +000074
75 tftp_session_update(spt);
76
77 return k;
78}
79
Jan Kiszka460fec62009-06-24 14:42:31 +020080static int tftp_session_find(Slirp *slirp, struct tftp_t *tp)
bellardc7f74642004-08-24 21:57:12 +000081{
82 struct tftp_session *spt;
83 int k;
84
85 for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
Jan Kiszka460fec62009-06-24 14:42:31 +020086 spt = &slirp->tftp_sessions[k];
bellardc7f74642004-08-24 21:57:12 +000087
Jan Kiszka460fec62009-06-24 14:42:31 +020088 if (tftp_session_in_use(spt)) {
bellardc7f74642004-08-24 21:57:12 +000089 if (!memcmp(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip))) {
90 if (spt->client_port == tp->udp.uh_sport) {
91 return k;
92 }
93 }
94 }
95 }
96
97 return -1;
98}
99
Hervé Poussineau4aa401f2012-09-13 12:39:36 +0200100static int tftp_read_data(struct tftp_session *spt, uint32_t block_nr,
Stefan Weilb6dce922010-07-22 22:15:23 +0200101 uint8_t *buf, int len)
bellardc7f74642004-08-24 21:57:12 +0000102{
Hervé Poussineau78be0562012-09-10 20:52:25 +0200103 int bytes_read = 0;
bellardc7f74642004-08-24 21:57:12 +0000104
Hervé Poussineau78be0562012-09-10 20:52:25 +0200105 if (spt->fd < 0) {
106 spt->fd = open(spt->filename, O_RDONLY | O_BINARY);
107 }
bellardc7f74642004-08-24 21:57:12 +0000108
Hervé Poussineau78be0562012-09-10 20:52:25 +0200109 if (spt->fd < 0) {
110 return -1;
111 }
bellardc7f74642004-08-24 21:57:12 +0000112
Hervé Poussineau78be0562012-09-10 20:52:25 +0200113 if (len) {
114 lseek(spt->fd, block_nr * 512, SEEK_SET);
bellardc7f74642004-08-24 21:57:12 +0000115
Hervé Poussineau78be0562012-09-10 20:52:25 +0200116 bytes_read = read(spt->fd, buf, len);
117 }
bellardc7f74642004-08-24 21:57:12 +0000118
Hervé Poussineau78be0562012-09-10 20:52:25 +0200119 return bytes_read;
bellardc7f74642004-08-24 21:57:12 +0000120}
121
ths5fafdf22007-09-16 21:08:06 +0000122static int tftp_send_oack(struct tftp_session *spt,
Hervé Poussineau95b1ad72012-09-13 07:55:01 +0200123 const char *keys[], uint32_t values[], int nb,
ths1f697db2007-02-20 00:07:50 +0000124 struct tftp_t *recv_tp)
125{
126 struct sockaddr_in saddr, daddr;
127 struct mbuf *m;
128 struct tftp_t *tp;
Hervé Poussineau95b1ad72012-09-13 07:55:01 +0200129 int i, n = 0;
ths1f697db2007-02-20 00:07:50 +0000130
Jan Kiszka460fec62009-06-24 14:42:31 +0200131 m = m_get(spt->slirp);
ths1f697db2007-02-20 00:07:50 +0000132
133 if (!m)
134 return -1;
135
136 memset(m->m_data, 0, m->m_size);
137
blueswir19634d902007-10-26 19:01:16 +0000138 m->m_data += IF_MAXLINKHDR;
ths1f697db2007-02-20 00:07:50 +0000139 tp = (void *)m->m_data;
140 m->m_data += sizeof(struct udpiphdr);
ths3b46e622007-09-17 08:09:54 +0000141
ths1f697db2007-02-20 00:07:50 +0000142 tp->tp_op = htons(TFTP_OACK);
Hervé Poussineau95b1ad72012-09-13 07:55:01 +0200143 for (i = 0; i < nb; i++) {
144 n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%s",
145 keys[i]) + 1;
146 n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%u",
147 values[i]) + 1;
148 }
ths1f697db2007-02-20 00:07:50 +0000149
150 saddr.sin_addr = recv_tp->ip.ip_dst;
151 saddr.sin_port = recv_tp->udp.uh_dport;
ths3b46e622007-09-17 08:09:54 +0000152
ths1f697db2007-02-20 00:07:50 +0000153 daddr.sin_addr = spt->client_ip;
154 daddr.sin_port = spt->client_port;
155
ths5fafdf22007-09-16 21:08:06 +0000156 m->m_len = sizeof(struct tftp_t) - 514 + n -
ths1f697db2007-02-20 00:07:50 +0000157 sizeof(struct ip) - sizeof(struct udphdr);
158 udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
159
160 return 0;
161}
162
Jan Kiszkaef2d54d2009-06-24 14:42:30 +0200163static void tftp_send_error(struct tftp_session *spt,
Stefan Weilb6dce922010-07-22 22:15:23 +0200164 uint16_t errorcode, const char *msg,
Jan Kiszkaef2d54d2009-06-24 14:42:30 +0200165 struct tftp_t *recv_tp)
bellardc7f74642004-08-24 21:57:12 +0000166{
167 struct sockaddr_in saddr, daddr;
168 struct mbuf *m;
169 struct tftp_t *tp;
bellardc7f74642004-08-24 21:57:12 +0000170
Jan Kiszka460fec62009-06-24 14:42:31 +0200171 m = m_get(spt->slirp);
bellardc7f74642004-08-24 21:57:12 +0000172
173 if (!m) {
Jan Kiszkaef2d54d2009-06-24 14:42:30 +0200174 goto out;
bellardc7f74642004-08-24 21:57:12 +0000175 }
176
177 memset(m->m_data, 0, m->m_size);
178
blueswir19634d902007-10-26 19:01:16 +0000179 m->m_data += IF_MAXLINKHDR;
bellardc7f74642004-08-24 21:57:12 +0000180 tp = (void *)m->m_data;
181 m->m_data += sizeof(struct udpiphdr);
ths3b46e622007-09-17 08:09:54 +0000182
bellardc7f74642004-08-24 21:57:12 +0000183 tp->tp_op = htons(TFTP_ERROR);
184 tp->x.tp_error.tp_error_code = htons(errorcode);
blueswir1b55266b2008-09-20 08:07:15 +0000185 pstrcpy((char *)tp->x.tp_error.tp_msg, sizeof(tp->x.tp_error.tp_msg), msg);
bellardc7f74642004-08-24 21:57:12 +0000186
187 saddr.sin_addr = recv_tp->ip.ip_dst;
188 saddr.sin_port = recv_tp->udp.uh_dport;
189
190 daddr.sin_addr = spt->client_ip;
191 daddr.sin_port = spt->client_port;
192
ths5fafdf22007-09-16 21:08:06 +0000193 m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) -
bellardc7f74642004-08-24 21:57:12 +0000194 sizeof(struct ip) - sizeof(struct udphdr);
195
196 udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
197
Jan Kiszkaef2d54d2009-06-24 14:42:30 +0200198out:
bellardc7f74642004-08-24 21:57:12 +0000199 tftp_session_terminate(spt);
bellardc7f74642004-08-24 21:57:12 +0000200}
201
Jan Kiszkaeb7faf02012-09-13 12:44:27 +0200202static void tftp_send_next_block(struct tftp_session *spt,
203 struct tftp_t *recv_tp)
bellardc7f74642004-08-24 21:57:12 +0000204{
205 struct sockaddr_in saddr, daddr;
206 struct mbuf *m;
207 struct tftp_t *tp;
208 int nobytes;
209
Jan Kiszka460fec62009-06-24 14:42:31 +0200210 m = m_get(spt->slirp);
bellardc7f74642004-08-24 21:57:12 +0000211
212 if (!m) {
Jan Kiszkaeb7faf02012-09-13 12:44:27 +0200213 return;
bellardc7f74642004-08-24 21:57:12 +0000214 }
215
216 memset(m->m_data, 0, m->m_size);
217
blueswir19634d902007-10-26 19:01:16 +0000218 m->m_data += IF_MAXLINKHDR;
bellardc7f74642004-08-24 21:57:12 +0000219 tp = (void *)m->m_data;
220 m->m_data += sizeof(struct udpiphdr);
ths3b46e622007-09-17 08:09:54 +0000221
bellardc7f74642004-08-24 21:57:12 +0000222 tp->tp_op = htons(TFTP_DATA);
Hervé Poussineau4aa401f2012-09-13 12:39:36 +0200223 tp->x.tp_data.tp_block_nr = htons((spt->block_nr + 1) & 0xffff);
bellardc7f74642004-08-24 21:57:12 +0000224
225 saddr.sin_addr = recv_tp->ip.ip_dst;
226 saddr.sin_port = recv_tp->udp.uh_dport;
227
228 daddr.sin_addr = spt->client_ip;
229 daddr.sin_port = spt->client_port;
230
Hervé Poussineau4aa401f2012-09-13 12:39:36 +0200231 nobytes = tftp_read_data(spt, spt->block_nr, tp->x.tp_data.tp_buf, 512);
bellardc7f74642004-08-24 21:57:12 +0000232
233 if (nobytes < 0) {
234 m_free(m);
235
236 /* send "file not found" error back */
237
238 tftp_send_error(spt, 1, "File not found", tp);
239
Jan Kiszkaeb7faf02012-09-13 12:44:27 +0200240 return;
bellardc7f74642004-08-24 21:57:12 +0000241 }
242
ths5fafdf22007-09-16 21:08:06 +0000243 m->m_len = sizeof(struct tftp_t) - (512 - nobytes) -
bellardc7f74642004-08-24 21:57:12 +0000244 sizeof(struct ip) - sizeof(struct udphdr);
245
246 udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
247
248 if (nobytes == 512) {
249 tftp_session_update(spt);
250 }
251 else {
252 tftp_session_terminate(spt);
253 }
254
Hervé Poussineau4aa401f2012-09-13 12:39:36 +0200255 spt->block_nr++;
bellardc7f74642004-08-24 21:57:12 +0000256}
257
Jan Kiszka460fec62009-06-24 14:42:31 +0200258static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen)
bellardc7f74642004-08-24 21:57:12 +0000259{
260 struct tftp_session *spt;
Jan Kiszka20c24bf2009-06-24 14:42:30 +0200261 int s, k;
Jan Kiszka93679642009-06-24 14:42:30 +0200262 size_t prefix_len;
Jan Kiszka20c24bf2009-06-24 14:42:30 +0200263 char *req_fname;
Hervé Poussineau95b1ad72012-09-13 07:55:01 +0200264 const char *option_name[2];
265 uint32_t option_value[2];
266 int nb_options = 0;
bellardc7f74642004-08-24 21:57:12 +0000267
Thomas Horstenbfe4e172010-01-07 17:01:28 +0000268 /* check if a session already exists and if so terminate it */
269 s = tftp_session_find(slirp, tp);
270 if (s >= 0) {
271 tftp_session_terminate(&slirp->tftp_sessions[s]);
272 }
273
Jan Kiszka460fec62009-06-24 14:42:31 +0200274 s = tftp_session_allocate(slirp, tp);
bellardc7f74642004-08-24 21:57:12 +0000275
276 if (s < 0) {
277 return;
278 }
279
Jan Kiszka460fec62009-06-24 14:42:31 +0200280 spt = &slirp->tftp_sessions[s];
bellardc7f74642004-08-24 21:57:12 +0000281
Jan Kiszkaf8e3cbd2009-06-24 14:42:29 +0200282 /* unspecifed prefix means service disabled */
Jan Kiszka460fec62009-06-24 14:42:31 +0200283 if (!slirp->tftp_prefix) {
Jan Kiszkaf8e3cbd2009-06-24 14:42:29 +0200284 tftp_send_error(spt, 2, "Access violation", tp);
285 return;
286 }
287
Jan Kiszka20c24bf2009-06-24 14:42:30 +0200288 /* skip header fields */
289 k = 0;
Stefan Weil89d2d3af2011-02-23 19:40:14 +0100290 pktlen -= offsetof(struct tftp_t, x.tp_buf);
bellardc7f74642004-08-24 21:57:12 +0000291
Jan Kiszka93679642009-06-24 14:42:30 +0200292 /* prepend tftp_prefix */
Jan Kiszka460fec62009-06-24 14:42:31 +0200293 prefix_len = strlen(slirp->tftp_prefix);
Anthony Liguori7267c092011-08-20 22:09:37 -0500294 spt->filename = g_malloc(prefix_len + TFTP_FILENAME_MAX + 2);
Jan Kiszka460fec62009-06-24 14:42:31 +0200295 memcpy(spt->filename, slirp->tftp_prefix, prefix_len);
Jan Kiszka74efd612009-06-29 08:47:30 +0200296 spt->filename[prefix_len] = '/';
Jan Kiszka93679642009-06-24 14:42:30 +0200297
bellardc7f74642004-08-24 21:57:12 +0000298 /* get name */
Jan Kiszka74efd612009-06-29 08:47:30 +0200299 req_fname = spt->filename + prefix_len + 1;
bellardc7f74642004-08-24 21:57:12 +0000300
Jan Kiszka20c24bf2009-06-24 14:42:30 +0200301 while (1) {
302 if (k >= TFTP_FILENAME_MAX || k >= pktlen) {
303 tftp_send_error(spt, 2, "Access violation", tp);
bellardc7f74642004-08-24 21:57:12 +0000304 return;
305 }
Stefan Weil89d2d3af2011-02-23 19:40:14 +0100306 req_fname[k] = tp->x.tp_buf[k];
Jan Kiszka20c24bf2009-06-24 14:42:30 +0200307 if (req_fname[k++] == '\0') {
bellardc7f74642004-08-24 21:57:12 +0000308 break;
309 }
310 }
ths3b46e622007-09-17 08:09:54 +0000311
bellardc7f74642004-08-24 21:57:12 +0000312 /* check mode */
Jan Kiszka20c24bf2009-06-24 14:42:30 +0200313 if ((pktlen - k) < 6) {
314 tftp_send_error(spt, 2, "Access violation", tp);
bellardc7f74642004-08-24 21:57:12 +0000315 return;
316 }
ths3b46e622007-09-17 08:09:54 +0000317
Stefan Weil89d2d3af2011-02-23 19:40:14 +0100318 if (strcasecmp(&tp->x.tp_buf[k], "octet") != 0) {
bellardc7f74642004-08-24 21:57:12 +0000319 tftp_send_error(spt, 4, "Unsupported transfer mode", tp);
320 return;
321 }
322
ths1f697db2007-02-20 00:07:50 +0000323 k += 6; /* skipping octet */
324
bellardc7f74642004-08-24 21:57:12 +0000325 /* do sanity checks on the filename */
Jan Kiszka74efd612009-06-29 08:47:30 +0200326 if (!strncmp(req_fname, "../", 3) ||
327 req_fname[strlen(req_fname) - 1] == '/' ||
Jan Kiszka93679642009-06-24 14:42:30 +0200328 strstr(req_fname, "/../")) {
bellardc7f74642004-08-24 21:57:12 +0000329 tftp_send_error(spt, 2, "Access violation", tp);
330 return;
331 }
332
bellardc7f74642004-08-24 21:57:12 +0000333 /* check if the file exists */
Jan Kiszka20c24bf2009-06-24 14:42:30 +0200334 if (tftp_read_data(spt, 0, NULL, 0) < 0) {
bellardc7f74642004-08-24 21:57:12 +0000335 tftp_send_error(spt, 1, "File not found", tp);
336 return;
337 }
338
Jan Kiszka20c24bf2009-06-24 14:42:30 +0200339 if (tp->x.tp_buf[pktlen - 1] != 0) {
ths1f697db2007-02-20 00:07:50 +0000340 tftp_send_error(spt, 2, "Access violation", tp);
341 return;
342 }
343
Hervé Poussineau95b1ad72012-09-13 07:55:01 +0200344 while (k < pktlen && nb_options < ARRAY_SIZE(option_name)) {
ths1f697db2007-02-20 00:07:50 +0000345 const char *key, *value;
346
Stefan Weil89d2d3af2011-02-23 19:40:14 +0100347 key = &tp->x.tp_buf[k];
ths1f697db2007-02-20 00:07:50 +0000348 k += strlen(key) + 1;
349
Jan Kiszka20c24bf2009-06-24 14:42:30 +0200350 if (k >= pktlen) {
ths1f697db2007-02-20 00:07:50 +0000351 tftp_send_error(spt, 2, "Access violation", tp);
352 return;
353 }
354
Stefan Weil89d2d3af2011-02-23 19:40:14 +0100355 value = &tp->x.tp_buf[k];
ths1f697db2007-02-20 00:07:50 +0000356 k += strlen(value) + 1;
357
Sergei Gavrikovfacf1a62011-01-12 15:57:18 +0200358 if (strcasecmp(key, "tsize") == 0) {
ths1f697db2007-02-20 00:07:50 +0000359 int tsize = atoi(value);
360 struct stat stat_p;
361
Jan Kiszkaf8e3cbd2009-06-24 14:42:29 +0200362 if (tsize == 0) {
Jan Kiszka93679642009-06-24 14:42:30 +0200363 if (stat(spt->filename, &stat_p) == 0)
ths1f697db2007-02-20 00:07:50 +0000364 tsize = stat_p.st_size;
365 else {
366 tftp_send_error(spt, 1, "File not found", tp);
367 return;
368 }
369 }
370
Hervé Poussineau95b1ad72012-09-13 07:55:01 +0200371 option_name[nb_options] = "tsize";
372 option_value[nb_options] = tsize;
373 nb_options++;
374 } else if (strcasecmp(key, "blksize") == 0) {
375 int blksize = atoi(value);
376
377 /* If blksize option is bigger than what we will
378 * emit, accept the option with our packet size.
379 * Otherwise, simply do as we didn't see the option.
380 */
381 if (blksize >= 512) {
382 option_name[nb_options] = "blksize";
383 option_value[nb_options] = 512;
384 nb_options++;
385 }
ths1f697db2007-02-20 00:07:50 +0000386 }
387 }
388
Hervé Poussineau95b1ad72012-09-13 07:55:01 +0200389 if (nb_options > 0) {
390 assert(nb_options <= ARRAY_SIZE(option_name));
391 tftp_send_oack(spt, option_name, option_value, nb_options, tp);
392 return;
393 }
394
Hervé Poussineau4aa401f2012-09-13 12:39:36 +0200395 spt->block_nr = 0;
396 tftp_send_next_block(spt, tp);
bellardc7f74642004-08-24 21:57:12 +0000397}
398
Jan Kiszka460fec62009-06-24 14:42:31 +0200399static void tftp_handle_ack(Slirp *slirp, struct tftp_t *tp, int pktlen)
bellardc7f74642004-08-24 21:57:12 +0000400{
401 int s;
402
Jan Kiszka460fec62009-06-24 14:42:31 +0200403 s = tftp_session_find(slirp, tp);
bellardc7f74642004-08-24 21:57:12 +0000404
405 if (s < 0) {
406 return;
407 }
408
Jan Kiszkaeb7faf02012-09-13 12:44:27 +0200409 tftp_send_next_block(&slirp->tftp_sessions[s], tp);
bellardc7f74642004-08-24 21:57:12 +0000410}
411
Thomas Horstenbfe4e172010-01-07 17:01:28 +0000412static void tftp_handle_error(Slirp *slirp, struct tftp_t *tp, int pktlen)
413{
414 int s;
415
416 s = tftp_session_find(slirp, tp);
417
418 if (s < 0) {
419 return;
420 }
421
422 tftp_session_terminate(&slirp->tftp_sessions[s]);
423}
424
bellardc7f74642004-08-24 21:57:12 +0000425void tftp_input(struct mbuf *m)
426{
427 struct tftp_t *tp = (struct tftp_t *)m->m_data;
428
429 switch(ntohs(tp->tp_op)) {
430 case TFTP_RRQ:
Jan Kiszka460fec62009-06-24 14:42:31 +0200431 tftp_handle_rrq(m->slirp, tp, m->m_len);
bellardc7f74642004-08-24 21:57:12 +0000432 break;
433
434 case TFTP_ACK:
Jan Kiszka460fec62009-06-24 14:42:31 +0200435 tftp_handle_ack(m->slirp, tp, m->m_len);
bellardc7f74642004-08-24 21:57:12 +0000436 break;
Thomas Horstenbfe4e172010-01-07 17:01:28 +0000437
438 case TFTP_ERROR:
439 tftp_handle_error(m->slirp, tp, m->m_len);
440 break;
bellardc7f74642004-08-24 21:57:12 +0000441 }
442}