blob: ce7b56df8fba44c5b6e8a352eb06ef309d7514a6 [file] [log] [blame]
Simon Kelleyc8e8f5c2021-01-24 21:59:37 +00001/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
Simon Kelley832af0b2007-01-21 20:01:28 +00002
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
Simon Kelley824af852008-02-12 20:43:05 +00005 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
Simon Kelley832af0b2007-01-21 20:01:28 +00008 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
Simon Kelley824af852008-02-12 20:43:05 +000012
Simon Kelley73a08a22009-02-05 20:28:08 +000013 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
Simon Kelley832af0b2007-01-21 20:01:28 +000015*/
16
17#include "dnsmasq.h"
18
19#ifdef HAVE_TFTP
20
Simon Kelley66f62652020-01-05 16:21:24 +000021static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len);
Simon Kelley8bc4cec2012-07-03 21:04:11 +010022static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix);
Simon Kelley832af0b2007-01-21 20:01:28 +000023static void free_transfer(struct tftp_transfer *transfer);
Rosen Penev50a28412017-06-27 22:27:02 +010024static ssize_t tftp_err(int err, char *packet, char *message, char *file);
Petr Menšík484bd752021-03-17 14:40:04 +000025static ssize_t tftp_err_oops(char *packet, const char *file);
Simon Kelley832af0b2007-01-21 20:01:28 +000026static ssize_t get_block(char *packet, struct tftp_transfer *transfer);
27static char *next(char **p, char *end);
Simon Kelley42fb8152012-04-20 17:15:01 +010028static void sanitise(char *buf);
Simon Kelley832af0b2007-01-21 20:01:28 +000029
30#define OP_RRQ 1
31#define OP_WRQ 2
32#define OP_DATA 3
33#define OP_ACK 4
34#define OP_ERR 5
35#define OP_OACK 6
36
37#define ERR_NOTDEF 0
38#define ERR_FNF 1
39#define ERR_PERM 2
40#define ERR_FULL 3
41#define ERR_ILL 4
42
Simon Kelley5aabfc72007-08-29 11:24:47 +010043void tftp_request(struct listener *listen, time_t now)
Simon Kelley832af0b2007-01-21 20:01:28 +000044{
45 ssize_t len;
46 char *packet = daemon->packet;
47 char *filename, *mode, *p, *end, *opt;
Simon Kelley28866e92011-02-14 20:19:14 +000048 union mysockaddr addr, peer;
Simon Kelley832af0b2007-01-21 20:01:28 +000049 struct msghdr msg;
Simon Kelley832af0b2007-01-21 20:01:28 +000050 struct iovec iov;
Simon Kelley1f15b812009-10-13 17:49:32 +010051 struct ifreq ifr;
Simon Kelley8bc4cec2012-07-03 21:04:11 +010052 int is_err = 1, if_index = 0, mtu = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +000053 struct iname *tmp;
Simon Kelley66f62652020-01-05 16:21:24 +000054 struct tftp_transfer *transfer = NULL, **up;
Simon Kelley824af852008-02-12 20:43:05 +000055 int port = daemon->start_tftp_port; /* may be zero to use ephemeral port */
56#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
Simon Kelley1f15b812009-10-13 17:49:32 +010057 int mtuflag = IP_PMTUDISC_DONT;
Simon Kelley824af852008-02-12 20:43:05 +000058#endif
Simon Kelley8ef5ada2010-06-03 19:42:45 +010059 char namebuff[IF_NAMESIZE];
Simon Kelley52d4abf2012-03-21 21:39:48 +000060 char *name = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010061 char *prefix = daemon->tftp_prefix;
62 struct tftp_prefix *pref;
Simon Kelleycc921df2019-01-02 22:48:59 +000063 union all_addr addra;
Petr Menšík1c1b9252019-07-15 17:16:44 +020064 int family = listen->addr.sa.sa_family;
Simon Kelley2329bef2013-12-03 13:41:16 +000065 /* Can always get recvd interface for IPv6 */
Petr Menšík1c1b9252019-07-15 17:16:44 +020066 int check_dest = !option_bool(OPT_NOWILD) || family == AF_INET6;
Simon Kelley832af0b2007-01-21 20:01:28 +000067 union {
68 struct cmsghdr align; /* this ensures alignment */
Simon Kelley28866e92011-02-14 20:19:14 +000069 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
Simon Kelley824af852008-02-12 20:43:05 +000070#if defined(HAVE_LINUX_NETWORK)
Simon Kelley832af0b2007-01-21 20:01:28 +000071 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
Simon Kelley824af852008-02-12 20:43:05 +000072#elif defined(HAVE_SOLARIS_NETWORK)
Simon Kelley936bd822019-10-12 23:29:59 +010073 char control[CMSG_SPACE(sizeof(struct in_addr)) +
74 CMSG_SPACE(sizeof(unsigned int))];
Simon Kelley7622fc02009-06-04 20:32:05 +010075#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
Simon Kelley936bd822019-10-12 23:29:59 +010076 char control[CMSG_SPACE(sizeof(struct in_addr)) +
77 CMSG_SPACE(sizeof(struct sockaddr_dl))];
Simon Kelley832af0b2007-01-21 20:01:28 +000078#endif
79 } control_u;
80
81 msg.msg_controllen = sizeof(control_u);
82 msg.msg_control = control_u.control;
83 msg.msg_flags = 0;
84 msg.msg_name = &peer;
85 msg.msg_namelen = sizeof(peer);
86 msg.msg_iov = &iov;
87 msg.msg_iovlen = 1;
88
89 iov.iov_base = packet;
90 iov.iov_len = daemon->packet_buff_sz;
91
92 /* we overwrote the buffer... */
93 daemon->srv_save = NULL;
94
95 if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
96 return;
Simon Kelley2329bef2013-12-03 13:41:16 +000097
98 /* Can always get recvd interface for IPv6 */
99 if (!check_dest)
Simon Kelley1f15b812009-10-13 17:49:32 +0100100 {
Simon Kelley52d4abf2012-03-21 21:39:48 +0000101 if (listen->iface)
102 {
103 addr = listen->iface->addr;
Simon Kelley52d4abf2012-03-21 21:39:48 +0000104 name = listen->iface->name;
Simon Kelleybec366b2016-02-24 22:03:26 +0000105 mtu = listen->iface->mtu;
106 if (daemon->tftp_mtu != 0 && daemon->tftp_mtu < mtu)
107 mtu = daemon->tftp_mtu;
Simon Kelley52d4abf2012-03-21 21:39:48 +0000108 }
109 else
110 {
111 /* we're listening on an address that doesn't appear on an interface,
112 ask the kernel what the socket is bound to */
113 socklen_t tcp_len = sizeof(union mysockaddr);
114 if (getsockname(listen->tftpfd, (struct sockaddr *)&addr, &tcp_len) == -1)
115 return;
116 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100117 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000118 else
119 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100120 struct cmsghdr *cmptr;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100121
Simon Kelley28866e92011-02-14 20:19:14 +0000122 if (msg.msg_controllen < sizeof(struct cmsghdr))
123 return;
124
Petr Menšík1c1b9252019-07-15 17:16:44 +0200125 addr.sa.sa_family = family;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000126
Simon Kelley832af0b2007-01-21 20:01:28 +0000127#if defined(HAVE_LINUX_NETWORK)
Petr Menšík1c1b9252019-07-15 17:16:44 +0200128 if (family == AF_INET)
Simon Kelley28866e92011-02-14 20:19:14 +0000129 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000130 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
Simon Kelley28866e92011-02-14 20:19:14 +0000131 {
132 union {
133 unsigned char *c;
134 struct in_pktinfo *p;
135 } p;
136 p.c = CMSG_DATA(cmptr);
137 addr.in.sin_addr = p.p->ipi_spec_dst;
138 if_index = p.p->ipi_ifindex;
139 }
140
141#elif defined(HAVE_SOLARIS_NETWORK)
Petr Menšík1c1b9252019-07-15 17:16:44 +0200142 if (family == AF_INET)
Simon Kelley28866e92011-02-14 20:19:14 +0000143 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000144 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100145 union {
146 unsigned char *c;
Simon Kelley28866e92011-02-14 20:19:14 +0000147 struct in_addr *a;
148 unsigned int *i;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100149 } p;
150 p.c = CMSG_DATA(cmptr);
Simon Kelley28866e92011-02-14 20:19:14 +0000151 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
152 addr.in.sin_addr = *(p.a);
153 else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
154 if_index = *(p.i);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000155 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000156
Simon Kelley832af0b2007-01-21 20:01:28 +0000157#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
Petr Menšík1c1b9252019-07-15 17:16:44 +0200158 if (family == AF_INET)
Simon Kelley28866e92011-02-14 20:19:14 +0000159 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
160 {
161 union {
162 unsigned char *c;
163 struct in_addr *a;
164 struct sockaddr_dl *s;
165 } p;
166 p.c = CMSG_DATA(cmptr);
167 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
168 addr.in.sin_addr = *(p.a);
169 else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
170 if_index = p.s->sdl_index;
171 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100172
Simon Kelley824af852008-02-12 20:43:05 +0000173#endif
Simon Kelley28866e92011-02-14 20:19:14 +0000174
Petr Menšík1c1b9252019-07-15 17:16:44 +0200175 if (family == AF_INET6)
Simon Kelley28866e92011-02-14 20:19:14 +0000176 {
177 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000178 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
Simon Kelley28866e92011-02-14 20:19:14 +0000179 {
180 union {
181 unsigned char *c;
182 struct in6_pktinfo *p;
183 } p;
184 p.c = CMSG_DATA(cmptr);
185
186 addr.in6.sin6_addr = p.p->ipi6_addr;
187 if_index = p.p->ipi6_ifindex;
188 }
189 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000190
Simon Kelley28866e92011-02-14 20:19:14 +0000191 if (!indextoname(listen->tftpfd, if_index, namebuff))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000192 return;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100193
194 name = namebuff;
Simon Kelleye25db1f2013-01-29 22:10:26 +0000195
Simon Kelleycc921df2019-01-02 22:48:59 +0000196 addra.addr4 = addr.in.sin_addr;
Simon Kelley28866e92011-02-14 20:19:14 +0000197
Petr Menšík1c1b9252019-07-15 17:16:44 +0200198 if (family == AF_INET6)
Simon Kelleycc921df2019-01-02 22:48:59 +0000199 addra.addr6 = addr.in6.sin6_addr;
Simon Kelleye25db1f2013-01-29 22:10:26 +0000200
Simon Kelley2937f8a2013-07-29 19:49:07 +0100201 if (daemon->tftp_interfaces)
Simon Kelley3169daa2012-08-13 17:39:57 +0100202 {
Simon Kelley2937f8a2013-07-29 19:49:07 +0100203 /* dedicated tftp interface list */
204 for (tmp = daemon->tftp_interfaces; tmp; tmp = tmp->next)
205 if (tmp->name && wildcard_match(tmp->name, name))
206 break;
207
208 if (!tmp)
Simon Kelley3169daa2012-08-13 17:39:57 +0100209 return;
210 }
Simon Kelley2937f8a2013-07-29 19:49:07 +0100211 else
212 {
213 /* Do the same as DHCP */
Petr Menšík1c1b9252019-07-15 17:16:44 +0200214 if (!iface_check(family, &addra, name, NULL))
Simon Kelley2937f8a2013-07-29 19:49:07 +0100215 {
216 if (!option_bool(OPT_CLEVERBIND))
217 enumerate_interfaces(0);
Petr Menšík1c1b9252019-07-15 17:16:44 +0200218 if (!loopback_exception(listen->tftpfd, family, &addra, name) &&
219 !label_exception(if_index, family, &addra))
Simon Kelley2937f8a2013-07-29 19:49:07 +0100220 return;
221 }
222
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100223#ifdef HAVE_DHCP
Simon Kelley2937f8a2013-07-29 19:49:07 +0100224 /* allowed interfaces are the same as for DHCP */
225 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
226 if (tmp->name && wildcard_match(tmp->name, name))
227 return;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100228#endif
Simon Kelley2937f8a2013-07-29 19:49:07 +0100229 }
230
Petr Menšík47b45b22018-08-15 18:17:00 +0200231 safe_strncpy(ifr.ifr_name, name, IF_NAMESIZE);
Simon Kelley1f15b812009-10-13 17:49:32 +0100232 if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
Simon Kelleybec366b2016-02-24 22:03:26 +0000233 {
234 mtu = ifr.ifr_mtu;
235 if (daemon->tftp_mtu != 0 && daemon->tftp_mtu < mtu)
236 mtu = daemon->tftp_mtu;
237 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000238 }
Stefan Tomanek7aa970e2015-04-01 17:55:07 +0100239
Simon Kelleybec366b2016-02-24 22:03:26 +0000240 /* Failed to get interface mtu - can use configured value. */
241 if (mtu == 0)
242 mtu = daemon->tftp_mtu;
243
Simon Kelley66f62652020-01-05 16:21:24 +0000244 /* data transfer via server listening socket */
245 if (option_bool(OPT_SINGLE_PORT))
Simon Kelley2ac4cf02020-01-06 23:39:33 +0000246 {
247 int tftp_cnt;
248
249 for (tftp_cnt = 0, transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; up = &transfer->next, transfer = transfer->next)
Simon Kelley66f62652020-01-05 16:21:24 +0000250 {
Simon Kelley2ac4cf02020-01-06 23:39:33 +0000251 tftp_cnt++;
252
253 if (sockaddr_isequal(&peer, &transfer->peer))
Simon Kelley66f62652020-01-05 16:21:24 +0000254 {
Simon Kelley2ac4cf02020-01-06 23:39:33 +0000255 if (ntohs(*((unsigned short *)packet)) == OP_RRQ)
256 {
257 /* Handle repeated RRQ or abandoned transfer from same host and port
258 by unlinking and reusing the struct transfer. */
259 *up = transfer->next;
260 break;
261 }
262 else
263 {
264 handle_tftp(now, transfer, len);
265 return;
266 }
Simon Kelley66f62652020-01-05 16:21:24 +0000267 }
268 }
Simon Kelley2ac4cf02020-01-06 23:39:33 +0000269
270 /* Enforce simultaneous transfer limit. In non-single-port mode
271 this is doene by not listening on the server socket when
272 too many transfers are in progress. */
273 if (!transfer && tftp_cnt >= daemon->tftp_max)
274 return;
275 }
276
Simon Kelley52d4abf2012-03-21 21:39:48 +0000277 if (name)
278 {
279 /* check for per-interface prefix */
280 for (pref = daemon->if_prefix; pref; pref = pref->next)
281 if (strcmp(pref->interface, name) == 0)
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100282 prefix = pref->prefix;
Simon Kelley52d4abf2012-03-21 21:39:48 +0000283 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100284
Petr Menšík1c1b9252019-07-15 17:16:44 +0200285 if (family == AF_INET)
Simon Kelley7de060b2011-08-26 17:24:52 +0100286 {
287 addr.in.sin_port = htons(port);
288#ifdef HAVE_SOCKADDR_SA_LEN
289 addr.in.sin_len = sizeof(addr.in);
290#endif
291 }
Simon Kelley28866e92011-02-14 20:19:14 +0000292 else
293 {
294 addr.in6.sin6_port = htons(port);
295 addr.in6.sin6_flowinfo = 0;
Simon Kelley7de060b2011-08-26 17:24:52 +0100296 addr.in6.sin6_scope_id = 0;
297#ifdef HAVE_SOCKADDR_SA_LEN
298 addr.in6.sin6_len = sizeof(addr.in6);
299#endif
Simon Kelley28866e92011-02-14 20:19:14 +0000300 }
Simon Kelley28866e92011-02-14 20:19:14 +0000301
Simon Kelley66f62652020-01-05 16:21:24 +0000302 /* May reuse struct transfer from abandoned transfer in single port mode. */
303 if (!transfer && !(transfer = whine_malloc(sizeof(struct tftp_transfer))))
Simon Kelley832af0b2007-01-21 20:01:28 +0000304 return;
305
Simon Kelley66f62652020-01-05 16:21:24 +0000306 if (option_bool(OPT_SINGLE_PORT))
307 transfer->sockfd = listen->tftpfd;
Petr Menšík1c1b9252019-07-15 17:16:44 +0200308 else if ((transfer->sockfd = socket(family, SOCK_DGRAM, 0)) == -1)
Simon Kelley832af0b2007-01-21 20:01:28 +0000309 {
310 free(transfer);
311 return;
312 }
313
314 transfer->peer = peer;
Simon Kelley66f62652020-01-05 16:21:24 +0000315 transfer->source = addra;
316 transfer->if_index = if_index;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100317 transfer->timeout = now + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +0000318 transfer->backoff = 1;
319 transfer->block = 1;
320 transfer->blocksize = 512;
Simon Kelley9e038942008-05-30 20:06:34 +0100321 transfer->offset = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000322 transfer->file = NULL;
323 transfer->opt_blocksize = transfer->opt_transize = 0;
Simon Kelley9e038942008-05-30 20:06:34 +0100324 transfer->netascii = transfer->carrylf = 0;
Simon Kelley28866e92011-02-14 20:19:14 +0000325
Petr Mensik51cdd1a2019-07-04 20:28:08 +0200326 (void)prettyprint_addr(&peer, daemon->addrbuff);
Simon Kelley28866e92011-02-14 20:19:14 +0000327
Simon Kelley824af852008-02-12 20:43:05 +0000328 /* if we have a nailed-down range, iterate until we find a free one. */
Simon Kelley66f62652020-01-05 16:21:24 +0000329 while (!option_bool(OPT_SINGLE_PORT))
Simon Kelley832af0b2007-01-21 20:01:28 +0000330 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100331 if (bind(transfer->sockfd, &addr.sa, sa_len(&addr)) == -1 ||
Simon Kelley824af852008-02-12 20:43:05 +0000332#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000333 setsockopt(transfer->sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 ||
Simon Kelley824af852008-02-12 20:43:05 +0000334#endif
335 !fix_fd(transfer->sockfd))
336 {
337 if (errno == EADDRINUSE && daemon->start_tftp_port != 0)
338 {
339 if (++port <= daemon->end_tftp_port)
340 {
Petr Menšík1c1b9252019-07-15 17:16:44 +0200341 if (family == AF_INET)
Simon Kelley28866e92011-02-14 20:19:14 +0000342 addr.in.sin_port = htons(port);
Simon Kelley28866e92011-02-14 20:19:14 +0000343 else
Simon Kelleyee875042018-10-23 22:10:17 +0100344 addr.in6.sin6_port = htons(port);
345
Simon Kelley824af852008-02-12 20:43:05 +0000346 continue;
347 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100348 my_syslog(MS_TFTP | LOG_ERR, _("unable to get free port for TFTP"));
Simon Kelley824af852008-02-12 20:43:05 +0000349 }
350 free_transfer(transfer);
351 return;
352 }
353 break;
Simon Kelley832af0b2007-01-21 20:01:28 +0000354 }
Simon Kelley824af852008-02-12 20:43:05 +0000355
Simon Kelley832af0b2007-01-21 20:01:28 +0000356 p = packet + 2;
357 end = packet + len;
Simon Kelley66f62652020-01-05 16:21:24 +0000358
Simon Kelley832af0b2007-01-21 20:01:28 +0000359 if (ntohs(*((unsigned short *)packet)) != OP_RRQ ||
360 !(filename = next(&p, end)) ||
361 !(mode = next(&p, end)) ||
Simon Kelley9e038942008-05-30 20:06:34 +0100362 (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
Simon Kelley6a69ab52012-04-24 14:42:26 +0100363 {
364 len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff);
365 is_err = 1;
366 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000367 else
368 {
Simon Kelley9e038942008-05-30 20:06:34 +0100369 if (strcasecmp(mode, "netascii") == 0)
370 transfer->netascii = 1;
371
Simon Kelley832af0b2007-01-21 20:01:28 +0000372 while ((opt = next(&p, end)))
373 {
Simon Kelley77e94da2009-08-31 17:32:17 +0100374 if (strcasecmp(opt, "blksize") == 0)
Simon Kelley832af0b2007-01-21 20:01:28 +0000375 {
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100376 if ((opt = next(&p, end)) && !option_bool(OPT_TFTP_NOBLOCK))
Simon Kelley77e94da2009-08-31 17:32:17 +0100377 {
Simon Kelleyd1377fa2016-03-04 21:32:21 +0000378 /* 32 bytes for IP, UDP and TFTP headers, 52 bytes for IPv6 */
Petr Menšík1c1b9252019-07-15 17:16:44 +0200379 int overhead = (family == AF_INET) ? 32 : 52;
Simon Kelley77e94da2009-08-31 17:32:17 +0100380 transfer->blocksize = atoi(opt);
381 if (transfer->blocksize < 1)
382 transfer->blocksize = 1;
383 if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4)
384 transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4;
Simon Kelleyd1377fa2016-03-04 21:32:21 +0000385 if (mtu != 0 && transfer->blocksize > (unsigned)mtu - overhead)
386 transfer->blocksize = (unsigned)mtu - overhead;
Simon Kelley77e94da2009-08-31 17:32:17 +0100387 transfer->opt_blocksize = 1;
388 transfer->block = 0;
389 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000390 }
Simon Kelley77e94da2009-08-31 17:32:17 +0100391 else if (strcasecmp(opt, "tsize") == 0 && next(&p, end) && !transfer->netascii)
Simon Kelley832af0b2007-01-21 20:01:28 +0000392 {
393 transfer->opt_transize = 1;
394 transfer->block = 0;
395 }
396 }
397
Simon Kelley1f15b812009-10-13 17:49:32 +0100398 /* cope with backslashes from windows boxen. */
Simon Kelley61ce6002012-04-20 21:28:49 +0100399 for (p = filename; *p; p++)
400 if (*p == '\\')
401 *p = '/';
402 else if (option_bool(OPT_TFTP_LC))
403 *p = tolower(*p);
404
Simon Kelleyf2621c72007-04-29 19:47:21 +0100405 strcpy(daemon->namebuff, "/");
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100406 if (prefix)
Simon Kelley832af0b2007-01-21 20:01:28 +0000407 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100408 if (prefix[0] == '/')
Simon Kelleyf2621c72007-04-29 19:47:21 +0100409 daemon->namebuff[0] = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100410 strncat(daemon->namebuff, prefix, (MAXDNAME-1) - strlen(daemon->namebuff));
411 if (prefix[strlen(prefix)-1] != '/')
Simon Kelley77e94da2009-08-31 17:32:17 +0100412 strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelley5aabfc72007-08-29 11:24:47 +0100413
Floris Bos60704f52017-04-09 22:22:49 +0100414 if (option_bool(OPT_TFTP_APREF_IP))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100415 {
416 size_t oldlen = strlen(daemon->namebuff);
417 struct stat statbuf;
418
Simon Kelleyc72daea2012-01-05 21:33:27 +0000419 strncat(daemon->namebuff, daemon->addrbuff, (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelley77e94da2009-08-31 17:32:17 +0100420 strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelley5aabfc72007-08-29 11:24:47 +0100421
422 /* remove unique-directory if it doesn't exist */
423 if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
424 daemon->namebuff[oldlen] = 0;
425 }
Floris Bos503c6092017-04-09 23:07:13 +0100426
Floris Bos60704f52017-04-09 22:22:49 +0100427 if (option_bool(OPT_TFTP_APREF_MAC))
428 {
429 unsigned char *macaddr = NULL;
430 unsigned char macbuf[DHCP_CHADDR_MAX];
Floris Bos503c6092017-04-09 23:07:13 +0100431
Floris Bos60704f52017-04-09 22:22:49 +0100432#ifdef HAVE_DHCP
433 if (daemon->dhcp && peer.sa.sa_family == AF_INET)
434 {
435 /* Check if the client IP is in our lease database */
436 struct dhcp_lease *lease = lease_find_by_addr(peer.in.sin_addr);
437 if (lease && lease->hwaddr_type == ARPHRD_ETHER && lease->hwaddr_len == ETHER_ADDR_LEN)
438 macaddr = lease->hwaddr;
439 }
440#endif
Floris Bos503c6092017-04-09 23:07:13 +0100441
Floris Bos60704f52017-04-09 22:22:49 +0100442 /* If no luck, try to find in ARP table. This only works if client is in same (V)LAN */
443 if (!macaddr && find_mac(&peer, macbuf, 1, now) > 0)
Floris Bos503c6092017-04-09 23:07:13 +0100444 macaddr = macbuf;
445
Floris Bos60704f52017-04-09 22:22:49 +0100446 if (macaddr)
447 {
448 size_t oldlen = strlen(daemon->namebuff);
449 struct stat statbuf;
450
451 snprintf(daemon->namebuff + oldlen, (MAXDNAME-1) - oldlen, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x/",
452 macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]);
Floris Bos503c6092017-04-09 23:07:13 +0100453
Floris Bos60704f52017-04-09 22:22:49 +0100454 /* remove unique-directory if it doesn't exist */
455 if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
456 daemon->namebuff[oldlen] = 0;
457 }
458 }
Floris Bos503c6092017-04-09 23:07:13 +0100459
Simon Kelleyf2621c72007-04-29 19:47:21 +0100460 /* Absolute pathnames OK if they match prefix */
461 if (filename[0] == '/')
462 {
463 if (strstr(filename, daemon->namebuff) == filename)
464 daemon->namebuff[0] = 0;
465 else
466 filename++;
467 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000468 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100469 else if (filename[0] == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +0000470 daemon->namebuff[0] = 0;
Simon Kelley77e94da2009-08-31 17:32:17 +0100471 strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
Floris Bos503c6092017-04-09 23:07:13 +0100472
Simon Kelley5aabfc72007-08-29 11:24:47 +0100473 /* check permissions and open file */
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100474 if ((transfer->file = check_tftp_fileperm(&len, prefix)))
Simon Kelley832af0b2007-01-21 20:01:28 +0000475 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000476 if ((len = get_block(packet, transfer)) == -1)
477 len = tftp_err_oops(packet, daemon->namebuff);
478 else
479 is_err = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000480 }
481 }
Simon Kelley66f62652020-01-05 16:21:24 +0000482
483 send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), packet, len, &peer, &addra, if_index);
Simon Kelley832af0b2007-01-21 20:01:28 +0000484
485 if (is_err)
486 free_transfer(transfer);
487 else
488 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000489 transfer->next = daemon->tftp_trans;
490 daemon->tftp_trans = transfer;
491 }
492}
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000493
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100494static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000495{
496 char *packet = daemon->packet, *namebuff = daemon->namebuff;
497 struct tftp_file *file;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100498 struct tftp_transfer *t;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000499 uid_t uid = geteuid();
500 struct stat statbuf;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100501 int fd = -1;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000502
503 /* trick to ban moving out of the subtree */
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100504 if (prefix && strstr(namebuff, "/../"))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100505 goto perm;
506
507 if ((fd = open(namebuff, O_RDONLY)) == -1)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000508 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100509 if (errno == ENOENT)
510 {
511 *len = tftp_err(ERR_FNF, packet, _("file %s not found"), namebuff);
512 return NULL;
513 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000514 else if (errno == EACCES)
515 goto perm;
516 else
517 goto oops;
518 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100519
520 /* stat the file descriptor to avoid stat->open races */
521 if (fstat(fd, &statbuf) == -1)
522 goto oops;
523
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000524 /* running as root, must be world-readable */
525 if (uid == 0)
526 {
527 if (!(statbuf.st_mode & S_IROTH))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100528 goto perm;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000529 }
530 /* in secure mode, must be owned by user running dnsmasq */
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100531 else if (option_bool(OPT_TFTP_SECURE) && uid != statbuf.st_uid)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100532 goto perm;
533
Josh Soref730c6742017-02-06 16:14:04 +0000534 /* If we're doing many transfers from the same file, only
Simon Kelley5aabfc72007-08-29 11:24:47 +0100535 open it once this saves lots of file descriptors
536 when mass-booting a big cluster, for instance.
537 Be conservative and only share when inode and name match
538 this keeps error messages sane. */
539 for (t = daemon->tftp_trans; t; t = t->next)
540 if (t->file->dev == statbuf.st_dev &&
541 t->file->inode == statbuf.st_ino &&
542 strcmp(t->file->filename, namebuff) == 0)
543 {
544 close(fd);
545 t->file->refcount++;
546 return t->file;
547 }
548
549 if (!(file = whine_malloc(sizeof(struct tftp_file) + strlen(namebuff) + 1)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000550 {
551 errno = ENOMEM;
552 goto oops;
553 }
554
Simon Kelley5aabfc72007-08-29 11:24:47 +0100555 file->fd = fd;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000556 file->size = statbuf.st_size;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100557 file->dev = statbuf.st_dev;
558 file->inode = statbuf.st_ino;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000559 file->refcount = 1;
560 strcpy(file->filename, namebuff);
561 return file;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100562
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000563 perm:
Simon Kelley5aabfc72007-08-29 11:24:47 +0100564 errno = EACCES;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000565 *len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100566 if (fd != -1)
567 close(fd);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000568 return NULL;
569
Simon Kelley5aabfc72007-08-29 11:24:47 +0100570 oops:
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000571 *len = tftp_err_oops(packet, namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100572 if (fd != -1)
573 close(fd);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000574 return NULL;
575}
576
Simon Kelleyb842bc92015-07-12 21:09:11 +0100577void check_tftp_listeners(time_t now)
Simon Kelley832af0b2007-01-21 20:01:28 +0000578{
579 struct tftp_transfer *transfer, *tmp, **up;
Simon Kelley832af0b2007-01-21 20:01:28 +0000580
Simon Kelley66f62652020-01-05 16:21:24 +0000581 /* In single port mode, all packets come via port 69 and tftp_request() */
582 if (!option_bool(OPT_SINGLE_PORT))
583 for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next)
Simon Kelleyb842bc92015-07-12 21:09:11 +0100584 if (poll_check(transfer->sockfd, POLLIN))
Simon Kelley832af0b2007-01-21 20:01:28 +0000585 {
586 /* we overwrote the buffer... */
587 daemon->srv_save = NULL;
Simon Kelley66f62652020-01-05 16:21:24 +0000588 handle_tftp(now, transfer, recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0));
Simon Kelley832af0b2007-01-21 20:01:28 +0000589 }
Simon Kelley66f62652020-01-05 16:21:24 +0000590
591 for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
592 {
593 tmp = transfer->next;
Simon Kelley832af0b2007-01-21 20:01:28 +0000594
595 if (difftime(now, transfer->timeout) >= 0.0)
596 {
597 int endcon = 0;
Simon Kelley66f62652020-01-05 16:21:24 +0000598 ssize_t len;
Simon Kelley832af0b2007-01-21 20:01:28 +0000599
600 /* timeout, retransmit */
Simon Kelley2ac4cf02020-01-06 23:39:33 +0000601 transfer->timeout += 1 + (1<<(transfer->backoff/2));
Simon Kelley832af0b2007-01-21 20:01:28 +0000602
603 /* we overwrote the buffer... */
604 daemon->srv_save = NULL;
605
Simon Kelleyc7a44c42020-01-07 20:30:16 +0000606 if ((len = get_block(daemon->packet, transfer)) == -1)
Simon Kelley832af0b2007-01-21 20:01:28 +0000607 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000608 len = tftp_err_oops(daemon->packet, transfer->file->filename);
Simon Kelley832af0b2007-01-21 20:01:28 +0000609 endcon = 1;
610 }
Simon Kelley2ac4cf02020-01-06 23:39:33 +0000611 else if (++transfer->backoff > 7)
Simon Kelley832af0b2007-01-21 20:01:28 +0000612 {
Simon Kelley2ac4cf02020-01-06 23:39:33 +0000613 /* don't complain about timeout when we're awaiting the last
614 ACK, some clients never send it */
Simon Kelley980b14f2020-03-05 18:01:48 +0000615 if ((unsigned)len == transfer->blocksize + 4)
Simon Kelley2ac4cf02020-01-06 23:39:33 +0000616 endcon = 1;
Simon Kelley42fb8152012-04-20 17:15:01 +0100617 len = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000618 }
Simon Kelley42fb8152012-04-20 17:15:01 +0100619
Simon Kelley832af0b2007-01-21 20:01:28 +0000620 if (len != 0)
Simon Kelley66f62652020-01-05 16:21:24 +0000621 send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), daemon->packet, len,
622 &transfer->peer, &transfer->source, transfer->if_index);
623
Simon Kelley832af0b2007-01-21 20:01:28 +0000624 if (endcon || len == 0)
625 {
Simon Kelley42fb8152012-04-20 17:15:01 +0100626 strcpy(daemon->namebuff, transfer->file->filename);
627 sanitise(daemon->namebuff);
Petr Mensik51cdd1a2019-07-04 20:28:08 +0200628 (void)prettyprint_addr(&transfer->peer, daemon->addrbuff);
Simon Kelley42fb8152012-04-20 17:15:01 +0100629 my_syslog(MS_TFTP | LOG_INFO, endcon ? _("failed sending %s to %s") : _("sent %s to %s"), daemon->namebuff, daemon->addrbuff);
Simon Kelley832af0b2007-01-21 20:01:28 +0000630 /* unlink */
631 *up = tmp;
Simon Kelley231d0612012-04-27 13:50:45 +0100632 if (endcon)
633 free_transfer(transfer);
634 else
635 {
636 /* put on queue to be sent to script and deleted */
637 transfer->next = daemon->tftp_done_trans;
638 daemon->tftp_done_trans = transfer;
639 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000640 continue;
641 }
642 }
643
644 up = &transfer->next;
Simon Kelley28866e92011-02-14 20:19:14 +0000645 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000646}
Simon Kelley66f62652020-01-05 16:21:24 +0000647
648/* packet in daemon->packet as this is called. */
649static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
650{
651 struct ack {
652 unsigned short op, block;
653 } *mess = (struct ack *)daemon->packet;
654
655 if (len >= (ssize_t)sizeof(struct ack))
656 {
657 if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block)
658 {
659 /* Got ack, ensure we take the (re)transmit path */
660 transfer->timeout = now;
661 transfer->backoff = 0;
662 if (transfer->block++ != 0)
663 transfer->offset += transfer->blocksize - transfer->expansion;
664 }
665 else if (ntohs(mess->op) == OP_ERR)
666 {
667 char *p = daemon->packet + sizeof(struct ack);
668 char *end = daemon->packet + len;
669 char *err = next(&p, end);
670
Petr Mensik51cdd1a2019-07-04 20:28:08 +0200671 (void)prettyprint_addr(&transfer->peer, daemon->addrbuff);
Simon Kelley66f62652020-01-05 16:21:24 +0000672
673 /* Sanitise error message */
674 if (!err)
675 err = "";
676 else
677 sanitise(err);
678
679 my_syslog(MS_TFTP | LOG_ERR, _("error %d %s received from %s"),
680 (int)ntohs(mess->block), err,
681 daemon->addrbuff);
682
683 /* Got err, ensure we take abort */
684 transfer->timeout = now;
685 transfer->backoff = 100;
686 }
687 }
688}
Simon Kelley832af0b2007-01-21 20:01:28 +0000689
690static void free_transfer(struct tftp_transfer *transfer)
691{
Simon Kelley66f62652020-01-05 16:21:24 +0000692 if (!option_bool(OPT_SINGLE_PORT))
693 close(transfer->sockfd);
694
Simon Kelley832af0b2007-01-21 20:01:28 +0000695 if (transfer->file && (--transfer->file->refcount) == 0)
696 {
697 close(transfer->file->fd);
698 free(transfer->file);
699 }
Simon Kelley66f62652020-01-05 16:21:24 +0000700
Simon Kelley832af0b2007-01-21 20:01:28 +0000701 free(transfer);
702}
703
704static char *next(char **p, char *end)
705{
706 char *ret = *p;
707 size_t len;
708
709 if (*(end-1) != 0 ||
710 *p == end ||
711 (len = strlen(ret)) == 0)
712 return NULL;
713
714 *p += len + 1;
715 return ret;
716}
717
Simon Kelley42fb8152012-04-20 17:15:01 +0100718static void sanitise(char *buf)
719{
Simon Kelley7a14dfe2012-04-20 20:50:42 +0100720 unsigned char *q, *r;
721 for (q = r = (unsigned char *)buf; *r; r++)
Simon Kelley11263a42012-04-27 14:00:55 +0100722 if (isprint((int)*r))
Simon Kelley7a14dfe2012-04-20 20:50:42 +0100723 *(q++) = *r;
724 *q = 0;
Simon Kelley42fb8152012-04-20 17:15:01 +0100725
Simon Kelley7a14dfe2012-04-20 20:50:42 +0100726}
Simon Kelley42fb8152012-04-20 17:15:01 +0100727
Simon Kelley294d36d2016-07-06 21:30:25 +0100728#define MAXMESSAGE 500 /* limit to make packet < 512 bytes and definitely smaller than buffer */
Simon Kelley832af0b2007-01-21 20:01:28 +0000729static ssize_t tftp_err(int err, char *packet, char *message, char *file)
730{
731 struct errmess {
732 unsigned short op, err;
733 char message[];
734 } *mess = (struct errmess *)packet;
Simon Kelley294d36d2016-07-06 21:30:25 +0100735 ssize_t len, ret = 4;
Simon Kelley832af0b2007-01-21 20:01:28 +0000736 char *errstr = strerror(errno);
Simon Kelley42fb8152012-04-20 17:15:01 +0100737
Simon Kelleyfa785732016-07-22 20:56:01 +0100738 memset(packet, 0, daemon->packet_buff_sz);
Simon Kelleyc7a44c42020-01-07 20:30:16 +0000739 sanitise(file);
Simon Kelleyfa785732016-07-22 20:56:01 +0100740
Simon Kelley832af0b2007-01-21 20:01:28 +0000741 mess->op = htons(OP_ERR);
742 mess->err = htons(err);
Simon Kelley294d36d2016-07-06 21:30:25 +0100743 len = snprintf(mess->message, MAXMESSAGE, message, file, errstr);
744 ret += (len < MAXMESSAGE) ? len + 1 : MAXMESSAGE; /* include terminating zero */
745
Simon Kelley316e2732010-01-22 20:16:09 +0000746 my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message);
Simon Kelley832af0b2007-01-21 20:01:28 +0000747
748 return ret;
749}
750
Petr Menšík484bd752021-03-17 14:40:04 +0000751static ssize_t tftp_err_oops(char *packet, const char *file)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000752{
Simon Kelley42fb8152012-04-20 17:15:01 +0100753 /* May have >1 refs to file, so potentially mangle a copy of the name */
Petr Menšík484bd752021-03-17 14:40:04 +0000754 if (file != daemon->namebuff)
755 strcpy(daemon->namebuff, file);
Simon Kelley42fb8152012-04-20 17:15:01 +0100756 return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000757}
758
Simon Kelley832af0b2007-01-21 20:01:28 +0000759/* return -1 for error, zero for done. */
760static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
761{
Simon Kelleyfa785732016-07-22 20:56:01 +0100762 memset(packet, 0, daemon->packet_buff_sz);
763
Simon Kelley832af0b2007-01-21 20:01:28 +0000764 if (transfer->block == 0)
765 {
766 /* send OACK */
767 char *p;
768 struct oackmess {
769 unsigned short op;
770 char data[];
771 } *mess = (struct oackmess *)packet;
772
773 p = mess->data;
774 mess->op = htons(OP_OACK);
775 if (transfer->opt_blocksize)
776 {
777 p += (sprintf(p, "blksize") + 1);
Rosen Penevcbd29e52017-06-27 22:29:51 +0100778 p += (sprintf(p, "%u", transfer->blocksize) + 1);
Simon Kelley832af0b2007-01-21 20:01:28 +0000779 }
780 if (transfer->opt_transize)
781 {
782 p += (sprintf(p,"tsize") + 1);
783 p += (sprintf(p, "%u", (unsigned int)transfer->file->size) + 1);
784 }
785
786 return p - packet;
787 }
788 else
789 {
790 /* send data packet */
791 struct datamess {
792 unsigned short op, block;
793 unsigned char data[];
794 } *mess = (struct datamess *)packet;
795
Simon Kelley9e038942008-05-30 20:06:34 +0100796 size_t size = transfer->file->size - transfer->offset;
Simon Kelley832af0b2007-01-21 20:01:28 +0000797
Simon Kelley9e038942008-05-30 20:06:34 +0100798 if (transfer->offset > transfer->file->size)
Simon Kelley832af0b2007-01-21 20:01:28 +0000799 return 0; /* finished */
800
801 if (size > transfer->blocksize)
802 size = transfer->blocksize;
803
Simon Kelley832af0b2007-01-21 20:01:28 +0000804 mess->op = htons(OP_DATA);
805 mess->block = htons((unsigned short)(transfer->block));
806
Simon Kelley9e038942008-05-30 20:06:34 +0100807 if (lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1 ||
808 !read_write(transfer->file->fd, mess->data, size, 1))
Simon Kelley832af0b2007-01-21 20:01:28 +0000809 return -1;
Simon Kelley9e038942008-05-30 20:06:34 +0100810
811 transfer->expansion = 0;
812
813 /* Map '\n' to CR-LF in netascii mode */
814 if (transfer->netascii)
815 {
816 size_t i;
817 int newcarrylf;
818
819 for (i = 0, newcarrylf = 0; i < size; i++)
820 if (mess->data[i] == '\n' && ( i != 0 || !transfer->carrylf))
821 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100822 transfer->expansion++;
823
824 if (size != transfer->blocksize)
Simon Kelley9e038942008-05-30 20:06:34 +0100825 size++; /* room in this block */
Simon Kelley7de060b2011-08-26 17:24:52 +0100826 else if (i == size - 1)
827 newcarrylf = 1; /* don't expand LF again if it moves to the next block */
828
Simon Kelley9e038942008-05-30 20:06:34 +0100829 /* make space and insert CR */
830 memmove(&mess->data[i+1], &mess->data[i], size - (i + 1));
831 mess->data[i] = '\r';
832
833 i++;
834 }
835 transfer->carrylf = newcarrylf;
836
837 }
838
839 return size + 4;
Simon Kelley832af0b2007-01-21 20:01:28 +0000840 }
841}
842
Simon Kelleya9530962012-03-20 22:07:35 +0000843
844int do_tftp_script_run(void)
845{
846 struct tftp_transfer *transfer;
847
848 if ((transfer = daemon->tftp_done_trans))
849 {
850 daemon->tftp_done_trans = transfer->next;
851#ifdef HAVE_SCRIPT
852 queue_tftp(transfer->file->size, transfer->file->filename, &transfer->peer);
853#endif
854 free_transfer(transfer);
855 return 1;
856 }
857
858 return 0;
859}
Simon Kelley832af0b2007-01-21 20:01:28 +0000860#endif