blob: 4421cf9379ba5c5c05bed90438f344890b23b9aa [file] [log] [blame]
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001/* dnsmasq is Copyright (c) 2000-2024 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);
Tarun Kundu12e3b2e2024-08-15 16:16:53 -070022static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *client);
Simon Kelley832af0b2007-01-21 20:01:28 +000023static void free_transfer(struct tftp_transfer *transfer);
Tarun Kundu12e3b2e2024-08-15 16:16:53 -070024static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *arg2);
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
Simon Kelleydfb1f7c2021-03-30 21:21:09 +010042#define ERR_TID 5
Simon Kelley832af0b2007-01-21 20:01:28 +000043
Simon Kelley5aabfc72007-08-29 11:24:47 +010044void tftp_request(struct listener *listen, time_t now)
Simon Kelley832af0b2007-01-21 20:01:28 +000045{
46 ssize_t len;
47 char *packet = daemon->packet;
48 char *filename, *mode, *p, *end, *opt;
Simon Kelley28866e92011-02-14 20:19:14 +000049 union mysockaddr addr, peer;
Simon Kelley832af0b2007-01-21 20:01:28 +000050 struct msghdr msg;
Simon Kelley832af0b2007-01-21 20:01:28 +000051 struct iovec iov;
Simon Kelley1f15b812009-10-13 17:49:32 +010052 struct ifreq ifr;
Simon Kelley8bc4cec2012-07-03 21:04:11 +010053 int is_err = 1, if_index = 0, mtu = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +000054 struct iname *tmp;
Simon Kelley66f62652020-01-05 16:21:24 +000055 struct tftp_transfer *transfer = NULL, **up;
Simon Kelley824af852008-02-12 20:43:05 +000056 int port = daemon->start_tftp_port; /* may be zero to use ephemeral port */
57#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
Simon Kelley1f15b812009-10-13 17:49:32 +010058 int mtuflag = IP_PMTUDISC_DONT;
Simon Kelley824af852008-02-12 20:43:05 +000059#endif
Simon Kelley8ef5ada2010-06-03 19:42:45 +010060 char namebuff[IF_NAMESIZE];
Simon Kelley52d4abf2012-03-21 21:39:48 +000061 char *name = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010062 char *prefix = daemon->tftp_prefix;
63 struct tftp_prefix *pref;
Simon Kelleycc921df2019-01-02 22:48:59 +000064 union all_addr addra;
Petr Menšík1c1b9252019-07-15 17:16:44 +020065 int family = listen->addr.sa.sa_family;
Simon Kelley2329bef2013-12-03 13:41:16 +000066 /* Can always get recvd interface for IPv6 */
Petr Menšík1c1b9252019-07-15 17:16:44 +020067 int check_dest = !option_bool(OPT_NOWILD) || family == AF_INET6;
Simon Kelley832af0b2007-01-21 20:01:28 +000068 union {
69 struct cmsghdr align; /* this ensures alignment */
Simon Kelley28866e92011-02-14 20:19:14 +000070 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
Simon Kelley824af852008-02-12 20:43:05 +000071#if defined(HAVE_LINUX_NETWORK)
Simon Kelley832af0b2007-01-21 20:01:28 +000072 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
Simon Kelley824af852008-02-12 20:43:05 +000073#elif defined(HAVE_SOLARIS_NETWORK)
Simon Kelley936bd822019-10-12 23:29:59 +010074 char control[CMSG_SPACE(sizeof(struct in_addr)) +
75 CMSG_SPACE(sizeof(unsigned int))];
Simon Kelley7622fc02009-06-04 20:32:05 +010076#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
Simon Kelley936bd822019-10-12 23:29:59 +010077 char control[CMSG_SPACE(sizeof(struct in_addr)) +
78 CMSG_SPACE(sizeof(struct sockaddr_dl))];
Simon Kelley832af0b2007-01-21 20:01:28 +000079#endif
80 } control_u;
81
82 msg.msg_controllen = sizeof(control_u);
83 msg.msg_control = control_u.control;
84 msg.msg_flags = 0;
85 msg.msg_name = &peer;
86 msg.msg_namelen = sizeof(peer);
87 msg.msg_iov = &iov;
88 msg.msg_iovlen = 1;
89
90 iov.iov_base = packet;
91 iov.iov_len = daemon->packet_buff_sz;
92
93 /* we overwrote the buffer... */
94 daemon->srv_save = NULL;
95
96 if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
97 return;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -070098
99#ifdef HAVE_DUMPFILE
100 dump_packet_udp(DUMP_TFTP, (void *)packet, len, (union mysockaddr *)&peer, NULL, listen->tftpfd);
101#endif
Simon Kelley74d4fcd2021-03-15 21:59:51 +0000102
Simon Kelley2329bef2013-12-03 13:41:16 +0000103 /* Can always get recvd interface for IPv6 */
104 if (!check_dest)
Simon Kelley1f15b812009-10-13 17:49:32 +0100105 {
Simon Kelley52d4abf2012-03-21 21:39:48 +0000106 if (listen->iface)
107 {
108 addr = listen->iface->addr;
Simon Kelley52d4abf2012-03-21 21:39:48 +0000109 name = listen->iface->name;
Simon Kelleybec366b2016-02-24 22:03:26 +0000110 mtu = listen->iface->mtu;
111 if (daemon->tftp_mtu != 0 && daemon->tftp_mtu < mtu)
112 mtu = daemon->tftp_mtu;
Simon Kelley52d4abf2012-03-21 21:39:48 +0000113 }
114 else
115 {
116 /* we're listening on an address that doesn't appear on an interface,
117 ask the kernel what the socket is bound to */
118 socklen_t tcp_len = sizeof(union mysockaddr);
119 if (getsockname(listen->tftpfd, (struct sockaddr *)&addr, &tcp_len) == -1)
120 return;
121 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100122 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000123 else
124 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100125 struct cmsghdr *cmptr;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100126
Simon Kelley28866e92011-02-14 20:19:14 +0000127 if (msg.msg_controllen < sizeof(struct cmsghdr))
128 return;
129
Petr Menšík1c1b9252019-07-15 17:16:44 +0200130 addr.sa.sa_family = family;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000131
Simon Kelley832af0b2007-01-21 20:01:28 +0000132#if defined(HAVE_LINUX_NETWORK)
Petr Menšík1c1b9252019-07-15 17:16:44 +0200133 if (family == AF_INET)
Simon Kelley28866e92011-02-14 20:19:14 +0000134 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000135 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
Simon Kelley28866e92011-02-14 20:19:14 +0000136 {
137 union {
138 unsigned char *c;
139 struct in_pktinfo *p;
140 } p;
141 p.c = CMSG_DATA(cmptr);
142 addr.in.sin_addr = p.p->ipi_spec_dst;
143 if_index = p.p->ipi_ifindex;
144 }
145
146#elif defined(HAVE_SOLARIS_NETWORK)
Petr Menšík1c1b9252019-07-15 17:16:44 +0200147 if (family == AF_INET)
Simon Kelley28866e92011-02-14 20:19:14 +0000148 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000149 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100150 union {
151 unsigned char *c;
Simon Kelley28866e92011-02-14 20:19:14 +0000152 struct in_addr *a;
153 unsigned int *i;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100154 } p;
155 p.c = CMSG_DATA(cmptr);
Simon Kelley28866e92011-02-14 20:19:14 +0000156 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
157 addr.in.sin_addr = *(p.a);
158 else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
159 if_index = *(p.i);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000160 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000161
Simon Kelley832af0b2007-01-21 20:01:28 +0000162#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
Petr Menšík1c1b9252019-07-15 17:16:44 +0200163 if (family == AF_INET)
Simon Kelley28866e92011-02-14 20:19:14 +0000164 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
165 {
166 union {
167 unsigned char *c;
168 struct in_addr *a;
169 struct sockaddr_dl *s;
170 } p;
171 p.c = CMSG_DATA(cmptr);
172 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
173 addr.in.sin_addr = *(p.a);
174 else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
175 if_index = p.s->sdl_index;
176 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100177
Simon Kelley824af852008-02-12 20:43:05 +0000178#endif
Simon Kelley28866e92011-02-14 20:19:14 +0000179
Petr Menšík1c1b9252019-07-15 17:16:44 +0200180 if (family == AF_INET6)
Simon Kelley28866e92011-02-14 20:19:14 +0000181 {
182 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000183 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
Simon Kelley28866e92011-02-14 20:19:14 +0000184 {
185 union {
186 unsigned char *c;
187 struct in6_pktinfo *p;
188 } p;
189 p.c = CMSG_DATA(cmptr);
190
191 addr.in6.sin6_addr = p.p->ipi6_addr;
192 if_index = p.p->ipi6_ifindex;
193 }
194 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000195
Simon Kelley28866e92011-02-14 20:19:14 +0000196 if (!indextoname(listen->tftpfd, if_index, namebuff))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000197 return;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100198
199 name = namebuff;
Simon Kelleye25db1f2013-01-29 22:10:26 +0000200
Simon Kelleycc921df2019-01-02 22:48:59 +0000201 addra.addr4 = addr.in.sin_addr;
Simon Kelley28866e92011-02-14 20:19:14 +0000202
Petr Menšík1c1b9252019-07-15 17:16:44 +0200203 if (family == AF_INET6)
Simon Kelleycc921df2019-01-02 22:48:59 +0000204 addra.addr6 = addr.in6.sin6_addr;
Simon Kelleye25db1f2013-01-29 22:10:26 +0000205
Simon Kelley2937f8a2013-07-29 19:49:07 +0100206 if (daemon->tftp_interfaces)
Simon Kelley3169daa2012-08-13 17:39:57 +0100207 {
Simon Kelley2937f8a2013-07-29 19:49:07 +0100208 /* dedicated tftp interface list */
209 for (tmp = daemon->tftp_interfaces; tmp; tmp = tmp->next)
210 if (tmp->name && wildcard_match(tmp->name, name))
211 break;
212
213 if (!tmp)
Simon Kelley3169daa2012-08-13 17:39:57 +0100214 return;
215 }
Simon Kelley2937f8a2013-07-29 19:49:07 +0100216 else
217 {
218 /* Do the same as DHCP */
Petr Menšík1c1b9252019-07-15 17:16:44 +0200219 if (!iface_check(family, &addra, name, NULL))
Simon Kelley2937f8a2013-07-29 19:49:07 +0100220 {
221 if (!option_bool(OPT_CLEVERBIND))
222 enumerate_interfaces(0);
Petr Menšík1c1b9252019-07-15 17:16:44 +0200223 if (!loopback_exception(listen->tftpfd, family, &addra, name) &&
224 !label_exception(if_index, family, &addra))
Simon Kelley2937f8a2013-07-29 19:49:07 +0100225 return;
226 }
227
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100228#ifdef HAVE_DHCP
Simon Kelley2937f8a2013-07-29 19:49:07 +0100229 /* allowed interfaces are the same as for DHCP */
230 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700231 if (tmp->name && (tmp->flags & INAME_4) && (tmp->flags & INAME_6) &&
232 wildcard_match(tmp->name, name))
Simon Kelley2937f8a2013-07-29 19:49:07 +0100233 return;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100234#endif
Simon Kelley2937f8a2013-07-29 19:49:07 +0100235 }
236
Petr Menšík47b45b22018-08-15 18:17:00 +0200237 safe_strncpy(ifr.ifr_name, name, IF_NAMESIZE);
Simon Kelley1f15b812009-10-13 17:49:32 +0100238 if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
Simon Kelleybec366b2016-02-24 22:03:26 +0000239 {
240 mtu = ifr.ifr_mtu;
241 if (daemon->tftp_mtu != 0 && daemon->tftp_mtu < mtu)
242 mtu = daemon->tftp_mtu;
243 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000244 }
Stefan Tomanek7aa970e2015-04-01 17:55:07 +0100245
Simon Kelleybec366b2016-02-24 22:03:26 +0000246 /* Failed to get interface mtu - can use configured value. */
247 if (mtu == 0)
248 mtu = daemon->tftp_mtu;
249
Simon Kelley66f62652020-01-05 16:21:24 +0000250 /* data transfer via server listening socket */
251 if (option_bool(OPT_SINGLE_PORT))
Simon Kelley2ac4cf02020-01-06 23:39:33 +0000252 {
253 int tftp_cnt;
254
255 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 +0000256 {
Simon Kelley2ac4cf02020-01-06 23:39:33 +0000257 tftp_cnt++;
258
259 if (sockaddr_isequal(&peer, &transfer->peer))
Simon Kelley66f62652020-01-05 16:21:24 +0000260 {
Simon Kelley2ac4cf02020-01-06 23:39:33 +0000261 if (ntohs(*((unsigned short *)packet)) == OP_RRQ)
262 {
263 /* Handle repeated RRQ or abandoned transfer from same host and port
264 by unlinking and reusing the struct transfer. */
265 *up = transfer->next;
266 break;
267 }
268 else
269 {
270 handle_tftp(now, transfer, len);
271 return;
272 }
Simon Kelley66f62652020-01-05 16:21:24 +0000273 }
274 }
Simon Kelley2ac4cf02020-01-06 23:39:33 +0000275
276 /* Enforce simultaneous transfer limit. In non-single-port mode
277 this is doene by not listening on the server socket when
278 too many transfers are in progress. */
279 if (!transfer && tftp_cnt >= daemon->tftp_max)
280 return;
281 }
282
Simon Kelley52d4abf2012-03-21 21:39:48 +0000283 if (name)
284 {
285 /* check for per-interface prefix */
286 for (pref = daemon->if_prefix; pref; pref = pref->next)
287 if (strcmp(pref->interface, name) == 0)
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100288 prefix = pref->prefix;
Simon Kelley52d4abf2012-03-21 21:39:48 +0000289 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100290
Petr Menšík1c1b9252019-07-15 17:16:44 +0200291 if (family == AF_INET)
Simon Kelley7de060b2011-08-26 17:24:52 +0100292 {
293 addr.in.sin_port = htons(port);
294#ifdef HAVE_SOCKADDR_SA_LEN
295 addr.in.sin_len = sizeof(addr.in);
296#endif
297 }
Simon Kelley28866e92011-02-14 20:19:14 +0000298 else
299 {
300 addr.in6.sin6_port = htons(port);
301 addr.in6.sin6_flowinfo = 0;
Simon Kelley7de060b2011-08-26 17:24:52 +0100302 addr.in6.sin6_scope_id = 0;
303#ifdef HAVE_SOCKADDR_SA_LEN
304 addr.in6.sin6_len = sizeof(addr.in6);
305#endif
Simon Kelley28866e92011-02-14 20:19:14 +0000306 }
Simon Kelley28866e92011-02-14 20:19:14 +0000307
Simon Kelley66f62652020-01-05 16:21:24 +0000308 /* May reuse struct transfer from abandoned transfer in single port mode. */
309 if (!transfer && !(transfer = whine_malloc(sizeof(struct tftp_transfer))))
Simon Kelley832af0b2007-01-21 20:01:28 +0000310 return;
311
Simon Kelley66f62652020-01-05 16:21:24 +0000312 if (option_bool(OPT_SINGLE_PORT))
313 transfer->sockfd = listen->tftpfd;
Petr Menšík1c1b9252019-07-15 17:16:44 +0200314 else if ((transfer->sockfd = socket(family, SOCK_DGRAM, 0)) == -1)
Simon Kelley832af0b2007-01-21 20:01:28 +0000315 {
316 free(transfer);
317 return;
318 }
319
320 transfer->peer = peer;
Simon Kelley66f62652020-01-05 16:21:24 +0000321 transfer->source = addra;
322 transfer->if_index = if_index;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100323 transfer->timeout = now + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +0000324 transfer->backoff = 1;
325 transfer->block = 1;
326 transfer->blocksize = 512;
Simon Kelley9e038942008-05-30 20:06:34 +0100327 transfer->offset = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000328 transfer->file = NULL;
329 transfer->opt_blocksize = transfer->opt_transize = 0;
Simon Kelley9e038942008-05-30 20:06:34 +0100330 transfer->netascii = transfer->carrylf = 0;
Simon Kelley28866e92011-02-14 20:19:14 +0000331
Petr Mensik51cdd1a2019-07-04 20:28:08 +0200332 (void)prettyprint_addr(&peer, daemon->addrbuff);
Simon Kelley28866e92011-02-14 20:19:14 +0000333
Simon Kelley824af852008-02-12 20:43:05 +0000334 /* if we have a nailed-down range, iterate until we find a free one. */
Simon Kelley66f62652020-01-05 16:21:24 +0000335 while (!option_bool(OPT_SINGLE_PORT))
Simon Kelley832af0b2007-01-21 20:01:28 +0000336 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100337 if (bind(transfer->sockfd, &addr.sa, sa_len(&addr)) == -1 ||
Simon Kelley824af852008-02-12 20:43:05 +0000338#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000339 setsockopt(transfer->sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 ||
Simon Kelley824af852008-02-12 20:43:05 +0000340#endif
341 !fix_fd(transfer->sockfd))
342 {
343 if (errno == EADDRINUSE && daemon->start_tftp_port != 0)
344 {
345 if (++port <= daemon->end_tftp_port)
346 {
Petr Menšík1c1b9252019-07-15 17:16:44 +0200347 if (family == AF_INET)
Simon Kelley28866e92011-02-14 20:19:14 +0000348 addr.in.sin_port = htons(port);
Simon Kelley28866e92011-02-14 20:19:14 +0000349 else
Simon Kelleyee875042018-10-23 22:10:17 +0100350 addr.in6.sin6_port = htons(port);
351
Simon Kelley824af852008-02-12 20:43:05 +0000352 continue;
353 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100354 my_syslog(MS_TFTP | LOG_ERR, _("unable to get free port for TFTP"));
Simon Kelley824af852008-02-12 20:43:05 +0000355 }
356 free_transfer(transfer);
357 return;
358 }
359 break;
Simon Kelley832af0b2007-01-21 20:01:28 +0000360 }
Simon Kelley824af852008-02-12 20:43:05 +0000361
Simon Kelley832af0b2007-01-21 20:01:28 +0000362 p = packet + 2;
363 end = packet + len;
Simon Kelley66f62652020-01-05 16:21:24 +0000364
Simon Kelley832af0b2007-01-21 20:01:28 +0000365 if (ntohs(*((unsigned short *)packet)) != OP_RRQ ||
366 !(filename = next(&p, end)) ||
367 !(mode = next(&p, end)) ||
Simon Kelley9e038942008-05-30 20:06:34 +0100368 (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
Simon Kelley6a69ab52012-04-24 14:42:26 +0100369 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700370 len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff, NULL);
Simon Kelley6a69ab52012-04-24 14:42:26 +0100371 is_err = 1;
372 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000373 else
374 {
Simon Kelley9e038942008-05-30 20:06:34 +0100375 if (strcasecmp(mode, "netascii") == 0)
376 transfer->netascii = 1;
377
Simon Kelley832af0b2007-01-21 20:01:28 +0000378 while ((opt = next(&p, end)))
379 {
Simon Kelley77e94da2009-08-31 17:32:17 +0100380 if (strcasecmp(opt, "blksize") == 0)
Simon Kelley832af0b2007-01-21 20:01:28 +0000381 {
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100382 if ((opt = next(&p, end)) && !option_bool(OPT_TFTP_NOBLOCK))
Simon Kelley77e94da2009-08-31 17:32:17 +0100383 {
Simon Kelleyd1377fa2016-03-04 21:32:21 +0000384 /* 32 bytes for IP, UDP and TFTP headers, 52 bytes for IPv6 */
Petr Menšík1c1b9252019-07-15 17:16:44 +0200385 int overhead = (family == AF_INET) ? 32 : 52;
Simon Kelley77e94da2009-08-31 17:32:17 +0100386 transfer->blocksize = atoi(opt);
387 if (transfer->blocksize < 1)
388 transfer->blocksize = 1;
389 if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4)
390 transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4;
Simon Kelleyd1377fa2016-03-04 21:32:21 +0000391 if (mtu != 0 && transfer->blocksize > (unsigned)mtu - overhead)
392 transfer->blocksize = (unsigned)mtu - overhead;
Simon Kelley77e94da2009-08-31 17:32:17 +0100393 transfer->opt_blocksize = 1;
394 transfer->block = 0;
395 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000396 }
Simon Kelley77e94da2009-08-31 17:32:17 +0100397 else if (strcasecmp(opt, "tsize") == 0 && next(&p, end) && !transfer->netascii)
Simon Kelley832af0b2007-01-21 20:01:28 +0000398 {
399 transfer->opt_transize = 1;
400 transfer->block = 0;
401 }
402 }
403
Simon Kelley1f15b812009-10-13 17:49:32 +0100404 /* cope with backslashes from windows boxen. */
Simon Kelley61ce6002012-04-20 21:28:49 +0100405 for (p = filename; *p; p++)
406 if (*p == '\\')
407 *p = '/';
408 else if (option_bool(OPT_TFTP_LC))
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700409 *p = tolower((unsigned char)*p);
Simon Kelley61ce6002012-04-20 21:28:49 +0100410
Simon Kelleyf2621c72007-04-29 19:47:21 +0100411 strcpy(daemon->namebuff, "/");
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100412 if (prefix)
Simon Kelley832af0b2007-01-21 20:01:28 +0000413 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100414 if (prefix[0] == '/')
Simon Kelleyf2621c72007-04-29 19:47:21 +0100415 daemon->namebuff[0] = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100416 strncat(daemon->namebuff, prefix, (MAXDNAME-1) - strlen(daemon->namebuff));
417 if (prefix[strlen(prefix)-1] != '/')
Simon Kelley77e94da2009-08-31 17:32:17 +0100418 strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelley5aabfc72007-08-29 11:24:47 +0100419
Floris Bos60704f52017-04-09 22:22:49 +0100420 if (option_bool(OPT_TFTP_APREF_IP))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100421 {
422 size_t oldlen = strlen(daemon->namebuff);
423 struct stat statbuf;
424
Simon Kelleyc72daea2012-01-05 21:33:27 +0000425 strncat(daemon->namebuff, daemon->addrbuff, (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelley77e94da2009-08-31 17:32:17 +0100426 strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelley5aabfc72007-08-29 11:24:47 +0100427
428 /* remove unique-directory if it doesn't exist */
429 if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
430 daemon->namebuff[oldlen] = 0;
431 }
Floris Bos503c6092017-04-09 23:07:13 +0100432
Floris Bos60704f52017-04-09 22:22:49 +0100433 if (option_bool(OPT_TFTP_APREF_MAC))
434 {
435 unsigned char *macaddr = NULL;
436 unsigned char macbuf[DHCP_CHADDR_MAX];
Floris Bos503c6092017-04-09 23:07:13 +0100437
Floris Bos60704f52017-04-09 22:22:49 +0100438#ifdef HAVE_DHCP
439 if (daemon->dhcp && peer.sa.sa_family == AF_INET)
440 {
441 /* Check if the client IP is in our lease database */
442 struct dhcp_lease *lease = lease_find_by_addr(peer.in.sin_addr);
443 if (lease && lease->hwaddr_type == ARPHRD_ETHER && lease->hwaddr_len == ETHER_ADDR_LEN)
444 macaddr = lease->hwaddr;
445 }
446#endif
Floris Bos503c6092017-04-09 23:07:13 +0100447
Floris Bos60704f52017-04-09 22:22:49 +0100448 /* If no luck, try to find in ARP table. This only works if client is in same (V)LAN */
449 if (!macaddr && find_mac(&peer, macbuf, 1, now) > 0)
Floris Bos503c6092017-04-09 23:07:13 +0100450 macaddr = macbuf;
451
Floris Bos60704f52017-04-09 22:22:49 +0100452 if (macaddr)
453 {
454 size_t oldlen = strlen(daemon->namebuff);
455 struct stat statbuf;
456
457 snprintf(daemon->namebuff + oldlen, (MAXDNAME-1) - oldlen, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x/",
458 macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]);
Floris Bos503c6092017-04-09 23:07:13 +0100459
Floris Bos60704f52017-04-09 22:22:49 +0100460 /* remove unique-directory if it doesn't exist */
461 if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
462 daemon->namebuff[oldlen] = 0;
463 }
464 }
Floris Bos503c6092017-04-09 23:07:13 +0100465
Simon Kelleyf2621c72007-04-29 19:47:21 +0100466 /* Absolute pathnames OK if they match prefix */
467 if (filename[0] == '/')
468 {
469 if (strstr(filename, daemon->namebuff) == filename)
470 daemon->namebuff[0] = 0;
471 else
472 filename++;
473 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000474 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100475 else if (filename[0] == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +0000476 daemon->namebuff[0] = 0;
Simon Kelley77e94da2009-08-31 17:32:17 +0100477 strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
Floris Bos503c6092017-04-09 23:07:13 +0100478
Simon Kelley5aabfc72007-08-29 11:24:47 +0100479 /* check permissions and open file */
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700480 if ((transfer->file = check_tftp_fileperm(&len, prefix, daemon->addrbuff)))
Simon Kelley832af0b2007-01-21 20:01:28 +0000481 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000482 if ((len = get_block(packet, transfer)) == -1)
483 len = tftp_err_oops(packet, daemon->namebuff);
484 else
485 is_err = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000486 }
487 }
Simon Kelley66f62652020-01-05 16:21:24 +0000488
489 send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), packet, len, &peer, &addra, if_index);
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700490
491#ifdef HAVE_DUMPFILE
492 dump_packet_udp(DUMP_TFTP, (void *)packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd);
493#endif
Simon Kelley832af0b2007-01-21 20:01:28 +0000494
495 if (is_err)
496 free_transfer(transfer);
497 else
498 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000499 transfer->next = daemon->tftp_trans;
500 daemon->tftp_trans = transfer;
501 }
502}
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000503
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700504static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *client)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000505{
506 char *packet = daemon->packet, *namebuff = daemon->namebuff;
507 struct tftp_file *file;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100508 struct tftp_transfer *t;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000509 uid_t uid = geteuid();
510 struct stat statbuf;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100511 int fd = -1;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000512
513 /* trick to ban moving out of the subtree */
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100514 if (prefix && strstr(namebuff, "/../"))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100515 goto perm;
516
517 if ((fd = open(namebuff, O_RDONLY)) == -1)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000518 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100519 if (errno == ENOENT)
520 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700521 *len = tftp_err(ERR_FNF, packet, _("file %s not found for %s"), namebuff, client);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100522 return NULL;
523 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000524 else if (errno == EACCES)
525 goto perm;
526 else
527 goto oops;
528 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100529
530 /* stat the file descriptor to avoid stat->open races */
531 if (fstat(fd, &statbuf) == -1)
532 goto oops;
533
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000534 /* running as root, must be world-readable */
535 if (uid == 0)
536 {
537 if (!(statbuf.st_mode & S_IROTH))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100538 goto perm;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000539 }
540 /* in secure mode, must be owned by user running dnsmasq */
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100541 else if (option_bool(OPT_TFTP_SECURE) && uid != statbuf.st_uid)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100542 goto perm;
543
Josh Soref730c6742017-02-06 16:14:04 +0000544 /* If we're doing many transfers from the same file, only
Simon Kelley5aabfc72007-08-29 11:24:47 +0100545 open it once this saves lots of file descriptors
546 when mass-booting a big cluster, for instance.
547 Be conservative and only share when inode and name match
548 this keeps error messages sane. */
549 for (t = daemon->tftp_trans; t; t = t->next)
550 if (t->file->dev == statbuf.st_dev &&
551 t->file->inode == statbuf.st_ino &&
552 strcmp(t->file->filename, namebuff) == 0)
553 {
554 close(fd);
555 t->file->refcount++;
556 return t->file;
557 }
558
559 if (!(file = whine_malloc(sizeof(struct tftp_file) + strlen(namebuff) + 1)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000560 {
561 errno = ENOMEM;
562 goto oops;
563 }
564
Simon Kelley5aabfc72007-08-29 11:24:47 +0100565 file->fd = fd;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000566 file->size = statbuf.st_size;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100567 file->dev = statbuf.st_dev;
568 file->inode = statbuf.st_ino;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000569 file->refcount = 1;
570 strcpy(file->filename, namebuff);
571 return file;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100572
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000573 perm:
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700574 *len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff, strerror(EACCES));
Simon Kelley5aabfc72007-08-29 11:24:47 +0100575 if (fd != -1)
576 close(fd);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000577 return NULL;
578
Simon Kelley5aabfc72007-08-29 11:24:47 +0100579 oops:
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000580 *len = tftp_err_oops(packet, namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100581 if (fd != -1)
582 close(fd);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000583 return NULL;
584}
585
Simon Kelleyb842bc92015-07-12 21:09:11 +0100586void check_tftp_listeners(time_t now)
Simon Kelley832af0b2007-01-21 20:01:28 +0000587{
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700588 struct listener *listener;
Simon Kelley832af0b2007-01-21 20:01:28 +0000589 struct tftp_transfer *transfer, *tmp, **up;
Simon Kelley832af0b2007-01-21 20:01:28 +0000590
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700591 for (listener = daemon->listeners; listener; listener = listener->next)
592 if (listener->tftpfd != -1 && poll_check(listener->tftpfd, POLLIN))
593 tftp_request(listener, now);
594
Simon Kelley66f62652020-01-05 16:21:24 +0000595 /* In single port mode, all packets come via port 69 and tftp_request() */
596 if (!option_bool(OPT_SINGLE_PORT))
597 for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next)
Simon Kelleyb842bc92015-07-12 21:09:11 +0100598 if (poll_check(transfer->sockfd, POLLIN))
Simon Kelley832af0b2007-01-21 20:01:28 +0000599 {
Simon Kelleydfb1f7c2021-03-30 21:21:09 +0100600 union mysockaddr peer;
601 socklen_t addr_len = sizeof(union mysockaddr);
602 ssize_t len;
603
Simon Kelley832af0b2007-01-21 20:01:28 +0000604 /* we overwrote the buffer... */
605 daemon->srv_save = NULL;
Simon Kelleydfb1f7c2021-03-30 21:21:09 +0100606
607 if ((len = recvfrom(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0, &peer.sa, &addr_len)) > 0)
608 {
609 if (sockaddr_isequal(&peer, &transfer->peer))
610 handle_tftp(now, transfer, len);
611 else
612 {
613 /* Wrong source address. See rfc1350 para 4. */
614 prettyprint_addr(&peer, daemon->addrbuff);
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700615 len = tftp_err(ERR_TID, daemon->packet, _("ignoring packet from %s (TID mismatch)"), daemon->addrbuff, NULL);
616 while(retry_send(sendto(transfer->sockfd, daemon->packet, len, 0, &peer.sa, sa_len(&peer))));
617
618#ifdef HAVE_DUMPFILE
619 dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd);
620#endif
Simon Kelleydfb1f7c2021-03-30 21:21:09 +0100621 }
622 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000623 }
Simon Kelleydfb1f7c2021-03-30 21:21:09 +0100624
Simon Kelley66f62652020-01-05 16:21:24 +0000625 for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
626 {
627 tmp = transfer->next;
Simon Kelley832af0b2007-01-21 20:01:28 +0000628
629 if (difftime(now, transfer->timeout) >= 0.0)
630 {
631 int endcon = 0;
Simon Kelley66f62652020-01-05 16:21:24 +0000632 ssize_t len;
Simon Kelley832af0b2007-01-21 20:01:28 +0000633
634 /* timeout, retransmit */
Simon Kelley2ac4cf02020-01-06 23:39:33 +0000635 transfer->timeout += 1 + (1<<(transfer->backoff/2));
Simon Kelley832af0b2007-01-21 20:01:28 +0000636
637 /* we overwrote the buffer... */
638 daemon->srv_save = NULL;
Simon Kelley74d4fcd2021-03-15 21:59:51 +0000639
Simon Kelleyc7a44c42020-01-07 20:30:16 +0000640 if ((len = get_block(daemon->packet, transfer)) == -1)
Simon Kelley832af0b2007-01-21 20:01:28 +0000641 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000642 len = tftp_err_oops(daemon->packet, transfer->file->filename);
Simon Kelley832af0b2007-01-21 20:01:28 +0000643 endcon = 1;
644 }
Simon Kelley2ac4cf02020-01-06 23:39:33 +0000645 else if (++transfer->backoff > 7)
Simon Kelley832af0b2007-01-21 20:01:28 +0000646 {
Simon Kelley2ac4cf02020-01-06 23:39:33 +0000647 /* don't complain about timeout when we're awaiting the last
648 ACK, some clients never send it */
Simon Kelley980b14f2020-03-05 18:01:48 +0000649 if ((unsigned)len == transfer->blocksize + 4)
Simon Kelley2ac4cf02020-01-06 23:39:33 +0000650 endcon = 1;
Simon Kelley42fb8152012-04-20 17:15:01 +0100651 len = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000652 }
Simon Kelley42fb8152012-04-20 17:15:01 +0100653
Simon Kelley832af0b2007-01-21 20:01:28 +0000654 if (len != 0)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700655 {
656 send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), daemon->packet, len,
657 &transfer->peer, &transfer->source, transfer->if_index);
658#ifdef HAVE_DUMPFILE
659 dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&transfer->peer, transfer->sockfd);
660#endif
661 }
662
Simon Kelley832af0b2007-01-21 20:01:28 +0000663 if (endcon || len == 0)
664 {
Simon Kelley42fb8152012-04-20 17:15:01 +0100665 strcpy(daemon->namebuff, transfer->file->filename);
666 sanitise(daemon->namebuff);
Petr Mensik51cdd1a2019-07-04 20:28:08 +0200667 (void)prettyprint_addr(&transfer->peer, daemon->addrbuff);
Simon Kelley42fb8152012-04-20 17:15:01 +0100668 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 +0000669 /* unlink */
670 *up = tmp;
Simon Kelley231d0612012-04-27 13:50:45 +0100671 if (endcon)
672 free_transfer(transfer);
673 else
674 {
675 /* put on queue to be sent to script and deleted */
676 transfer->next = daemon->tftp_done_trans;
677 daemon->tftp_done_trans = transfer;
678 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000679 continue;
680 }
681 }
682
683 up = &transfer->next;
Simon Kelley28866e92011-02-14 20:19:14 +0000684 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000685}
Simon Kelley66f62652020-01-05 16:21:24 +0000686
687/* packet in daemon->packet as this is called. */
688static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
689{
690 struct ack {
691 unsigned short op, block;
692 } *mess = (struct ack *)daemon->packet;
693
694 if (len >= (ssize_t)sizeof(struct ack))
695 {
696 if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block)
697 {
698 /* Got ack, ensure we take the (re)transmit path */
699 transfer->timeout = now;
700 transfer->backoff = 0;
701 if (transfer->block++ != 0)
702 transfer->offset += transfer->blocksize - transfer->expansion;
703 }
704 else if (ntohs(mess->op) == OP_ERR)
705 {
706 char *p = daemon->packet + sizeof(struct ack);
707 char *end = daemon->packet + len;
708 char *err = next(&p, end);
709
Petr Mensik51cdd1a2019-07-04 20:28:08 +0200710 (void)prettyprint_addr(&transfer->peer, daemon->addrbuff);
Simon Kelley66f62652020-01-05 16:21:24 +0000711
712 /* Sanitise error message */
713 if (!err)
714 err = "";
715 else
716 sanitise(err);
717
718 my_syslog(MS_TFTP | LOG_ERR, _("error %d %s received from %s"),
719 (int)ntohs(mess->block), err,
720 daemon->addrbuff);
721
722 /* Got err, ensure we take abort */
723 transfer->timeout = now;
724 transfer->backoff = 100;
725 }
726 }
727}
Simon Kelley832af0b2007-01-21 20:01:28 +0000728
729static void free_transfer(struct tftp_transfer *transfer)
730{
Simon Kelley66f62652020-01-05 16:21:24 +0000731 if (!option_bool(OPT_SINGLE_PORT))
732 close(transfer->sockfd);
733
Simon Kelley832af0b2007-01-21 20:01:28 +0000734 if (transfer->file && (--transfer->file->refcount) == 0)
735 {
736 close(transfer->file->fd);
737 free(transfer->file);
738 }
Simon Kelley66f62652020-01-05 16:21:24 +0000739
Simon Kelley832af0b2007-01-21 20:01:28 +0000740 free(transfer);
741}
742
743static char *next(char **p, char *end)
744{
745 char *ret = *p;
746 size_t len;
747
748 if (*(end-1) != 0 ||
749 *p == end ||
750 (len = strlen(ret)) == 0)
751 return NULL;
752
753 *p += len + 1;
754 return ret;
755}
756
Simon Kelley42fb8152012-04-20 17:15:01 +0100757static void sanitise(char *buf)
758{
Simon Kelley7a14dfe2012-04-20 20:50:42 +0100759 unsigned char *q, *r;
760 for (q = r = (unsigned char *)buf; *r; r++)
Simon Kelley11263a42012-04-27 14:00:55 +0100761 if (isprint((int)*r))
Simon Kelley7a14dfe2012-04-20 20:50:42 +0100762 *(q++) = *r;
763 *q = 0;
Simon Kelley42fb8152012-04-20 17:15:01 +0100764
Simon Kelley7a14dfe2012-04-20 20:50:42 +0100765}
Simon Kelley42fb8152012-04-20 17:15:01 +0100766
Simon Kelley294d36d2016-07-06 21:30:25 +0100767#define MAXMESSAGE 500 /* limit to make packet < 512 bytes and definitely smaller than buffer */
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700768static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *arg2)
Simon Kelley832af0b2007-01-21 20:01:28 +0000769{
770 struct errmess {
771 unsigned short op, err;
772 char message[];
773 } *mess = (struct errmess *)packet;
Simon Kelley294d36d2016-07-06 21:30:25 +0100774 ssize_t len, ret = 4;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700775
Simon Kelleyfa785732016-07-22 20:56:01 +0100776 memset(packet, 0, daemon->packet_buff_sz);
Simon Kelleydfb1f7c2021-03-30 21:21:09 +0100777 if (file)
778 sanitise(file);
Simon Kelleyfa785732016-07-22 20:56:01 +0100779
Simon Kelley832af0b2007-01-21 20:01:28 +0000780 mess->op = htons(OP_ERR);
781 mess->err = htons(err);
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700782 len = snprintf(mess->message, MAXMESSAGE, message, file, arg2);
Simon Kelley294d36d2016-07-06 21:30:25 +0100783 ret += (len < MAXMESSAGE) ? len + 1 : MAXMESSAGE; /* include terminating zero */
784
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700785 if (err != ERR_FNF || !option_bool(OPT_QUIET_TFTP))
786 my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message);
Simon Kelley832af0b2007-01-21 20:01:28 +0000787
788 return ret;
789}
790
Petr Menšík484bd752021-03-17 14:40:04 +0000791static ssize_t tftp_err_oops(char *packet, const char *file)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000792{
Simon Kelley42fb8152012-04-20 17:15:01 +0100793 /* May have >1 refs to file, so potentially mangle a copy of the name */
Petr Menšík484bd752021-03-17 14:40:04 +0000794 if (file != daemon->namebuff)
795 strcpy(daemon->namebuff, file);
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700796 return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff, strerror(errno));
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000797}
798
Simon Kelley832af0b2007-01-21 20:01:28 +0000799/* return -1 for error, zero for done. */
800static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
801{
Simon Kelleyfa785732016-07-22 20:56:01 +0100802 memset(packet, 0, daemon->packet_buff_sz);
803
Simon Kelley832af0b2007-01-21 20:01:28 +0000804 if (transfer->block == 0)
805 {
806 /* send OACK */
807 char *p;
808 struct oackmess {
809 unsigned short op;
810 char data[];
811 } *mess = (struct oackmess *)packet;
812
813 p = mess->data;
814 mess->op = htons(OP_OACK);
815 if (transfer->opt_blocksize)
816 {
817 p += (sprintf(p, "blksize") + 1);
Rosen Penevcbd29e52017-06-27 22:29:51 +0100818 p += (sprintf(p, "%u", transfer->blocksize) + 1);
Simon Kelley832af0b2007-01-21 20:01:28 +0000819 }
820 if (transfer->opt_transize)
821 {
822 p += (sprintf(p,"tsize") + 1);
823 p += (sprintf(p, "%u", (unsigned int)transfer->file->size) + 1);
824 }
825
826 return p - packet;
827 }
828 else
829 {
830 /* send data packet */
831 struct datamess {
832 unsigned short op, block;
833 unsigned char data[];
834 } *mess = (struct datamess *)packet;
835
Simon Kelley9e038942008-05-30 20:06:34 +0100836 size_t size = transfer->file->size - transfer->offset;
Simon Kelley832af0b2007-01-21 20:01:28 +0000837
Simon Kelley9e038942008-05-30 20:06:34 +0100838 if (transfer->offset > transfer->file->size)
Simon Kelley832af0b2007-01-21 20:01:28 +0000839 return 0; /* finished */
840
841 if (size > transfer->blocksize)
842 size = transfer->blocksize;
843
Simon Kelley832af0b2007-01-21 20:01:28 +0000844 mess->op = htons(OP_DATA);
845 mess->block = htons((unsigned short)(transfer->block));
846
Simon Kelley9e038942008-05-30 20:06:34 +0100847 if (lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1 ||
848 !read_write(transfer->file->fd, mess->data, size, 1))
Simon Kelley832af0b2007-01-21 20:01:28 +0000849 return -1;
Simon Kelley9e038942008-05-30 20:06:34 +0100850
851 transfer->expansion = 0;
852
853 /* Map '\n' to CR-LF in netascii mode */
854 if (transfer->netascii)
855 {
856 size_t i;
857 int newcarrylf;
858
859 for (i = 0, newcarrylf = 0; i < size; i++)
860 if (mess->data[i] == '\n' && ( i != 0 || !transfer->carrylf))
861 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100862 transfer->expansion++;
863
864 if (size != transfer->blocksize)
Simon Kelley9e038942008-05-30 20:06:34 +0100865 size++; /* room in this block */
Simon Kelley7de060b2011-08-26 17:24:52 +0100866 else if (i == size - 1)
867 newcarrylf = 1; /* don't expand LF again if it moves to the next block */
868
Simon Kelley9e038942008-05-30 20:06:34 +0100869 /* make space and insert CR */
870 memmove(&mess->data[i+1], &mess->data[i], size - (i + 1));
871 mess->data[i] = '\r';
872
873 i++;
874 }
875 transfer->carrylf = newcarrylf;
876
877 }
878
879 return size + 4;
Simon Kelley832af0b2007-01-21 20:01:28 +0000880 }
881}
882
Simon Kelleya9530962012-03-20 22:07:35 +0000883
884int do_tftp_script_run(void)
885{
886 struct tftp_transfer *transfer;
887
888 if ((transfer = daemon->tftp_done_trans))
889 {
890 daemon->tftp_done_trans = transfer->next;
891#ifdef HAVE_SCRIPT
892 queue_tftp(transfer->file->size, transfer->file->filename, &transfer->peer);
893#endif
894 free_transfer(transfer);
895 return 1;
896 }
897
898 return 0;
899}
Simon Kelley832af0b2007-01-21 20:01:28 +0000900#endif