blob: bda04f380471efec305bac2db6565e348c2dd2a4 [file] [log] [blame]
Simon Kelley59546082012-01-06 20:02:04 +00001/* dnsmasq is Copyright (c) 2000-2012 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 Kelley8ef5ada2010-06-03 19:42:45 +010021static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, int special);
Simon Kelley832af0b2007-01-21 20:01:28 +000022static void free_transfer(struct tftp_transfer *transfer);
23static ssize_t tftp_err(int err, char *packet, char *mess, char *file);
Simon Kelley1b7ecd12007-02-05 14:57:57 +000024static ssize_t tftp_err_oops(char *packet, char *file);
Simon Kelley832af0b2007-01-21 20:01:28 +000025static ssize_t get_block(char *packet, struct tftp_transfer *transfer);
26static char *next(char **p, char *end);
Simon Kelley42fb8152012-04-20 17:15:01 +010027static void sanitise(char *buf);
Simon Kelley832af0b2007-01-21 20:01:28 +000028
29#define OP_RRQ 1
30#define OP_WRQ 2
31#define OP_DATA 3
32#define OP_ACK 4
33#define OP_ERR 5
34#define OP_OACK 6
35
36#define ERR_NOTDEF 0
37#define ERR_FNF 1
38#define ERR_PERM 2
39#define ERR_FULL 3
40#define ERR_ILL 4
41
Simon Kelley5aabfc72007-08-29 11:24:47 +010042void tftp_request(struct listener *listen, time_t now)
Simon Kelley832af0b2007-01-21 20:01:28 +000043{
44 ssize_t len;
45 char *packet = daemon->packet;
46 char *filename, *mode, *p, *end, *opt;
Simon Kelley28866e92011-02-14 20:19:14 +000047 union mysockaddr addr, peer;
Simon Kelley832af0b2007-01-21 20:01:28 +000048 struct msghdr msg;
Simon Kelley832af0b2007-01-21 20:01:28 +000049 struct iovec iov;
Simon Kelley1f15b812009-10-13 17:49:32 +010050 struct ifreq ifr;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010051 int is_err = 1, if_index = 0, mtu = 0, special = 0;
52#ifdef HAVE_DHCP
Simon Kelley832af0b2007-01-21 20:01:28 +000053 struct iname *tmp;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010054#endif
Simon Kelley5aabfc72007-08-29 11:24:47 +010055 struct tftp_transfer *transfer;
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;
64 struct interface_list *ir;
65
Simon Kelley832af0b2007-01-21 20:01:28 +000066 union {
67 struct cmsghdr align; /* this ensures alignment */
Simon Kelley28866e92011-02-14 20:19:14 +000068#ifdef HAVE_IPV6
69 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
70#endif
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)
74 char control[CMSG_SPACE(sizeof(unsigned int))];
Simon Kelley7622fc02009-06-04 20:32:05 +010075#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
Simon Kelley832af0b2007-01-21 20:01:28 +000076 char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
77#endif
78 } control_u;
79
80 msg.msg_controllen = sizeof(control_u);
81 msg.msg_control = control_u.control;
82 msg.msg_flags = 0;
83 msg.msg_name = &peer;
84 msg.msg_namelen = sizeof(peer);
85 msg.msg_iov = &iov;
86 msg.msg_iovlen = 1;
87
88 iov.iov_base = packet;
89 iov.iov_len = daemon->packet_buff_sz;
90
91 /* we overwrote the buffer... */
92 daemon->srv_save = NULL;
93
94 if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
95 return;
96
Simon Kelley28866e92011-02-14 20:19:14 +000097 if (option_bool(OPT_NOWILD))
Simon Kelley1f15b812009-10-13 17:49:32 +010098 {
Simon Kelley52d4abf2012-03-21 21:39:48 +000099 if (listen->iface)
100 {
101 addr = listen->iface->addr;
102 mtu = listen->iface->mtu;
103 name = listen->iface->name;
104 }
105 else
106 {
107 /* we're listening on an address that doesn't appear on an interface,
108 ask the kernel what the socket is bound to */
109 socklen_t tcp_len = sizeof(union mysockaddr);
110 if (getsockname(listen->tftpfd, (struct sockaddr *)&addr, &tcp_len) == -1)
111 return;
112 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100113 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000114 else
115 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100116 struct cmsghdr *cmptr;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100117 int check;
118 struct interface_list *ir;
119
Simon Kelley28866e92011-02-14 20:19:14 +0000120 if (msg.msg_controllen < sizeof(struct cmsghdr))
121 return;
122
123 addr.sa.sa_family = listen->family;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000124
Simon Kelley832af0b2007-01-21 20:01:28 +0000125#if defined(HAVE_LINUX_NETWORK)
Simon Kelley28866e92011-02-14 20:19:14 +0000126 if (listen->family == AF_INET)
127 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000128 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
Simon Kelley28866e92011-02-14 20:19:14 +0000129 {
130 union {
131 unsigned char *c;
132 struct in_pktinfo *p;
133 } p;
134 p.c = CMSG_DATA(cmptr);
135 addr.in.sin_addr = p.p->ipi_spec_dst;
136 if_index = p.p->ipi_ifindex;
137 }
138
139#elif defined(HAVE_SOLARIS_NETWORK)
140 if (listen->family == AF_INET)
141 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000142 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100143 union {
144 unsigned char *c;
Simon Kelley28866e92011-02-14 20:19:14 +0000145 struct in_addr *a;
146 unsigned int *i;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100147 } p;
148 p.c = CMSG_DATA(cmptr);
Simon Kelley28866e92011-02-14 20:19:14 +0000149 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
150 addr.in.sin_addr = *(p.a);
151 else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
152 if_index = *(p.i);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000153 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000154
Simon Kelley832af0b2007-01-21 20:01:28 +0000155#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
Simon Kelley28866e92011-02-14 20:19:14 +0000156 if (listen->family == AF_INET)
157 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
158 {
159 union {
160 unsigned char *c;
161 struct in_addr *a;
162 struct sockaddr_dl *s;
163 } p;
164 p.c = CMSG_DATA(cmptr);
165 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
166 addr.in.sin_addr = *(p.a);
167 else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
168 if_index = p.s->sdl_index;
169 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100170
Simon Kelley824af852008-02-12 20:43:05 +0000171#endif
Simon Kelley28866e92011-02-14 20:19:14 +0000172
173#ifdef HAVE_IPV6
174 if (listen->family == AF_INET6)
175 {
176 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000177 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
Simon Kelley28866e92011-02-14 20:19:14 +0000178 {
179 union {
180 unsigned char *c;
181 struct in6_pktinfo *p;
182 } p;
183 p.c = CMSG_DATA(cmptr);
184
185 addr.in6.sin6_addr = p.p->ipi6_addr;
186 if_index = p.p->ipi6_ifindex;
187 }
188 }
189#endif
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 Kelley28866e92011-02-14 20:19:14 +0000195
196#ifdef HAVE_IPV6
197 if (listen->family == AF_INET6)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000198 check = iface_check(AF_INET6, (struct all_addr *)&addr.in6.sin6_addr, name);
Simon Kelley28866e92011-02-14 20:19:14 +0000199 else
200#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000201 check = iface_check(AF_INET, (struct all_addr *)&addr.in.sin_addr, name);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100202
203 /* wierd TFTP service override */
204 for (ir = daemon->tftp_interfaces; ir; ir = ir->next)
205 if (strcmp(ir->interface, name) == 0)
206 break;
207
208 if (!ir)
209 {
210 if (!daemon->tftp_unlimited || !check)
211 return;
212
213#ifdef HAVE_DHCP
214 /* allowed interfaces are the same as for DHCP */
215 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
216 if (tmp->name && (strcmp(tmp->name, name) == 0))
217 return;
218#endif
219 }
220
Simon Kelley316e2732010-01-22 20:16:09 +0000221 strncpy(ifr.ifr_name, name, IF_NAMESIZE);
Simon Kelley1f15b812009-10-13 17:49:32 +0100222 if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
223 mtu = ifr.ifr_mtu;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000224 }
225
Simon Kelley52d4abf2012-03-21 21:39:48 +0000226 if (name)
227 {
228 /* check for per-interface prefix */
229 for (pref = daemon->if_prefix; pref; pref = pref->next)
230 if (strcmp(pref->interface, name) == 0)
231 prefix = pref->prefix;
232
233 /* wierd TFTP interfaces disable special options. */
234 for (ir = daemon->tftp_interfaces; ir; ir = ir->next)
235 if (strcmp(ir->interface, name) == 0)
236 special = 1;
237 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100238
Simon Kelley28866e92011-02-14 20:19:14 +0000239 if (listen->family == AF_INET)
Simon Kelley7de060b2011-08-26 17:24:52 +0100240 {
241 addr.in.sin_port = htons(port);
242#ifdef HAVE_SOCKADDR_SA_LEN
243 addr.in.sin_len = sizeof(addr.in);
244#endif
245 }
Simon Kelley28866e92011-02-14 20:19:14 +0000246#ifdef HAVE_IPV6
247 else
248 {
249 addr.in6.sin6_port = htons(port);
250 addr.in6.sin6_flowinfo = 0;
Simon Kelley7de060b2011-08-26 17:24:52 +0100251 addr.in6.sin6_scope_id = 0;
252#ifdef HAVE_SOCKADDR_SA_LEN
253 addr.in6.sin6_len = sizeof(addr.in6);
254#endif
Simon Kelley28866e92011-02-14 20:19:14 +0000255 }
256#endif
257
Simon Kelley5aabfc72007-08-29 11:24:47 +0100258 if (!(transfer = whine_malloc(sizeof(struct tftp_transfer))))
Simon Kelley832af0b2007-01-21 20:01:28 +0000259 return;
260
Simon Kelley28866e92011-02-14 20:19:14 +0000261 if ((transfer->sockfd = socket(listen->family, SOCK_DGRAM, 0)) == -1)
Simon Kelley832af0b2007-01-21 20:01:28 +0000262 {
263 free(transfer);
264 return;
265 }
266
267 transfer->peer = peer;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100268 transfer->timeout = now + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +0000269 transfer->backoff = 1;
270 transfer->block = 1;
271 transfer->blocksize = 512;
Simon Kelley9e038942008-05-30 20:06:34 +0100272 transfer->offset = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000273 transfer->file = NULL;
274 transfer->opt_blocksize = transfer->opt_transize = 0;
Simon Kelley9e038942008-05-30 20:06:34 +0100275 transfer->netascii = transfer->carrylf = 0;
Simon Kelley28866e92011-02-14 20:19:14 +0000276
Simon Kelleyc72daea2012-01-05 21:33:27 +0000277 prettyprint_addr(&peer, daemon->addrbuff);
Simon Kelley28866e92011-02-14 20:19:14 +0000278
Simon Kelley824af852008-02-12 20:43:05 +0000279 /* if we have a nailed-down range, iterate until we find a free one. */
280 while (1)
Simon Kelley832af0b2007-01-21 20:01:28 +0000281 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100282 if (bind(transfer->sockfd, &addr.sa, sa_len(&addr)) == -1 ||
Simon Kelley824af852008-02-12 20:43:05 +0000283#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000284 setsockopt(transfer->sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 ||
Simon Kelley824af852008-02-12 20:43:05 +0000285#endif
286 !fix_fd(transfer->sockfd))
287 {
288 if (errno == EADDRINUSE && daemon->start_tftp_port != 0)
289 {
290 if (++port <= daemon->end_tftp_port)
291 {
Simon Kelley28866e92011-02-14 20:19:14 +0000292 if (listen->family == AF_INET)
293 addr.in.sin_port = htons(port);
294#ifdef HAVE_IPV6
295 else
296 addr.in6.sin6_port = htons(port);
297#endif
Simon Kelley824af852008-02-12 20:43:05 +0000298 continue;
299 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100300 my_syslog(MS_TFTP | LOG_ERR, _("unable to get free port for TFTP"));
Simon Kelley824af852008-02-12 20:43:05 +0000301 }
302 free_transfer(transfer);
303 return;
304 }
305 break;
Simon Kelley832af0b2007-01-21 20:01:28 +0000306 }
Simon Kelley824af852008-02-12 20:43:05 +0000307
Simon Kelley832af0b2007-01-21 20:01:28 +0000308 p = packet + 2;
309 end = packet + len;
310
311 if (ntohs(*((unsigned short *)packet)) != OP_RRQ ||
312 !(filename = next(&p, end)) ||
313 !(mode = next(&p, end)) ||
Simon Kelley9e038942008-05-30 20:06:34 +0100314 (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000315 len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff);
Simon Kelley832af0b2007-01-21 20:01:28 +0000316 else
317 {
Simon Kelley9e038942008-05-30 20:06:34 +0100318 if (strcasecmp(mode, "netascii") == 0)
319 transfer->netascii = 1;
320
Simon Kelley832af0b2007-01-21 20:01:28 +0000321 while ((opt = next(&p, end)))
322 {
Simon Kelley77e94da2009-08-31 17:32:17 +0100323 if (strcasecmp(opt, "blksize") == 0)
Simon Kelley832af0b2007-01-21 20:01:28 +0000324 {
Simon Kelley77e94da2009-08-31 17:32:17 +0100325 if ((opt = next(&p, end)) &&
Simon Kelley28866e92011-02-14 20:19:14 +0000326 (special || !option_bool(OPT_TFTP_NOBLOCK)))
Simon Kelley77e94da2009-08-31 17:32:17 +0100327 {
328 transfer->blocksize = atoi(opt);
329 if (transfer->blocksize < 1)
330 transfer->blocksize = 1;
331 if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4)
332 transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4;
Simon Kelley1f15b812009-10-13 17:49:32 +0100333 /* 32 bytes for IP, UDP and TFTP headers */
334 if (mtu != 0 && transfer->blocksize > (unsigned)mtu - 32)
335 transfer->blocksize = (unsigned)mtu - 32;
Simon Kelley77e94da2009-08-31 17:32:17 +0100336 transfer->opt_blocksize = 1;
337 transfer->block = 0;
338 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000339 }
Simon Kelley77e94da2009-08-31 17:32:17 +0100340 else if (strcasecmp(opt, "tsize") == 0 && next(&p, end) && !transfer->netascii)
Simon Kelley832af0b2007-01-21 20:01:28 +0000341 {
342 transfer->opt_transize = 1;
343 transfer->block = 0;
344 }
345 }
346
Simon Kelley1f15b812009-10-13 17:49:32 +0100347 /* cope with backslashes from windows boxen. */
Simon Kelley61ce6002012-04-20 21:28:49 +0100348 for (p = filename; *p; p++)
349 if (*p == '\\')
350 *p = '/';
351 else if (option_bool(OPT_TFTP_LC))
352 *p = tolower(*p);
353
Simon Kelleyf2621c72007-04-29 19:47:21 +0100354 strcpy(daemon->namebuff, "/");
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100355 if (prefix)
Simon Kelley832af0b2007-01-21 20:01:28 +0000356 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100357 if (prefix[0] == '/')
Simon Kelleyf2621c72007-04-29 19:47:21 +0100358 daemon->namebuff[0] = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100359 strncat(daemon->namebuff, prefix, (MAXDNAME-1) - strlen(daemon->namebuff));
360 if (prefix[strlen(prefix)-1] != '/')
Simon Kelley77e94da2009-08-31 17:32:17 +0100361 strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelley5aabfc72007-08-29 11:24:47 +0100362
Simon Kelley28866e92011-02-14 20:19:14 +0000363 if (!special && option_bool(OPT_TFTP_APREF))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100364 {
365 size_t oldlen = strlen(daemon->namebuff);
366 struct stat statbuf;
367
Simon Kelleyc72daea2012-01-05 21:33:27 +0000368 strncat(daemon->namebuff, daemon->addrbuff, (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelley77e94da2009-08-31 17:32:17 +0100369 strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelley5aabfc72007-08-29 11:24:47 +0100370
371 /* remove unique-directory if it doesn't exist */
372 if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
373 daemon->namebuff[oldlen] = 0;
374 }
375
Simon Kelleyf2621c72007-04-29 19:47:21 +0100376 /* Absolute pathnames OK if they match prefix */
377 if (filename[0] == '/')
378 {
379 if (strstr(filename, daemon->namebuff) == filename)
380 daemon->namebuff[0] = 0;
381 else
382 filename++;
383 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000384 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100385 else if (filename[0] == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +0000386 daemon->namebuff[0] = 0;
Simon Kelley77e94da2009-08-31 17:32:17 +0100387 strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelley832af0b2007-01-21 20:01:28 +0000388
Simon Kelley5aabfc72007-08-29 11:24:47 +0100389 /* check permissions and open file */
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100390 if ((transfer->file = check_tftp_fileperm(&len, prefix, special)))
Simon Kelley832af0b2007-01-21 20:01:28 +0000391 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000392 if ((len = get_block(packet, transfer)) == -1)
393 len = tftp_err_oops(packet, daemon->namebuff);
394 else
395 is_err = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000396 }
397 }
398
399 while (sendto(transfer->sockfd, packet, len, 0,
400 (struct sockaddr *)&peer, sizeof(peer)) == -1 && errno == EINTR);
401
402 if (is_err)
403 free_transfer(transfer);
404 else
405 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000406 transfer->next = daemon->tftp_trans;
407 daemon->tftp_trans = transfer;
408 }
409}
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000410
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100411static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, int special)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000412{
413 char *packet = daemon->packet, *namebuff = daemon->namebuff;
414 struct tftp_file *file;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100415 struct tftp_transfer *t;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000416 uid_t uid = geteuid();
417 struct stat statbuf;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100418 int fd = -1;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000419
420 /* trick to ban moving out of the subtree */
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100421 if (prefix && strstr(namebuff, "/../"))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100422 goto perm;
423
424 if ((fd = open(namebuff, O_RDONLY)) == -1)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000425 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100426 if (errno == ENOENT)
427 {
428 *len = tftp_err(ERR_FNF, packet, _("file %s not found"), namebuff);
429 return NULL;
430 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000431 else if (errno == EACCES)
432 goto perm;
433 else
434 goto oops;
435 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100436
437 /* stat the file descriptor to avoid stat->open races */
438 if (fstat(fd, &statbuf) == -1)
439 goto oops;
440
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000441 /* running as root, must be world-readable */
442 if (uid == 0)
443 {
444 if (!(statbuf.st_mode & S_IROTH))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100445 goto perm;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000446 }
447 /* in secure mode, must be owned by user running dnsmasq */
Simon Kelley28866e92011-02-14 20:19:14 +0000448 else if (!special && option_bool(OPT_TFTP_SECURE) && uid != statbuf.st_uid)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100449 goto perm;
450
451 /* If we're doing many tranfers from the same file, only
452 open it once this saves lots of file descriptors
453 when mass-booting a big cluster, for instance.
454 Be conservative and only share when inode and name match
455 this keeps error messages sane. */
456 for (t = daemon->tftp_trans; t; t = t->next)
457 if (t->file->dev == statbuf.st_dev &&
458 t->file->inode == statbuf.st_ino &&
459 strcmp(t->file->filename, namebuff) == 0)
460 {
461 close(fd);
462 t->file->refcount++;
463 return t->file;
464 }
465
466 if (!(file = whine_malloc(sizeof(struct tftp_file) + strlen(namebuff) + 1)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000467 {
468 errno = ENOMEM;
469 goto oops;
470 }
471
Simon Kelley5aabfc72007-08-29 11:24:47 +0100472 file->fd = fd;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000473 file->size = statbuf.st_size;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100474 file->dev = statbuf.st_dev;
475 file->inode = statbuf.st_ino;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000476 file->refcount = 1;
477 strcpy(file->filename, namebuff);
478 return file;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100479
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000480 perm:
Simon Kelley5aabfc72007-08-29 11:24:47 +0100481 errno = EACCES;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000482 *len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100483 if (fd != -1)
484 close(fd);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000485 return NULL;
486
Simon Kelley5aabfc72007-08-29 11:24:47 +0100487 oops:
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000488 *len = tftp_err_oops(packet, namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100489 if (fd != -1)
490 close(fd);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000491 return NULL;
492}
493
Simon Kelley5aabfc72007-08-29 11:24:47 +0100494void check_tftp_listeners(fd_set *rset, time_t now)
Simon Kelley832af0b2007-01-21 20:01:28 +0000495{
496 struct tftp_transfer *transfer, *tmp, **up;
497 ssize_t len;
498
499 struct ack {
500 unsigned short op, block;
501 } *mess = (struct ack *)daemon->packet;
502
503 /* Check for activity on any existing transfers */
504 for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
505 {
506 tmp = transfer->next;
507
508 if (FD_ISSET(transfer->sockfd, rset))
509 {
510 /* we overwrote the buffer... */
511 daemon->srv_save = NULL;
Simon Kelley28866e92011-02-14 20:19:14 +0000512
Simon Kelleyc72daea2012-01-05 21:33:27 +0000513 prettyprint_addr(&transfer->peer, daemon->addrbuff);
Simon Kelley832af0b2007-01-21 20:01:28 +0000514
515 if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack))
516 {
517 if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block)
518 {
519 /* Got ack, ensure we take the (re)transmit path */
520 transfer->timeout = now;
521 transfer->backoff = 0;
Simon Kelley9e038942008-05-30 20:06:34 +0100522 if (transfer->block++ != 0)
523 transfer->offset += transfer->blocksize - transfer->expansion;
Simon Kelley832af0b2007-01-21 20:01:28 +0000524 }
525 else if (ntohs(mess->op) == OP_ERR)
526 {
527 char *p = daemon->packet + sizeof(struct ack);
528 char *end = daemon->packet + len;
529 char *err = next(&p, end);
Simon Kelley28866e92011-02-14 20:19:14 +0000530
Simon Kelley832af0b2007-01-21 20:01:28 +0000531 /* Sanitise error message */
532 if (!err)
533 err = "";
534 else
Simon Kelley7a14dfe2012-04-20 20:50:42 +0100535 sanitise(err);
Simon Kelley28866e92011-02-14 20:19:14 +0000536
Simon Kelley316e2732010-01-22 20:16:09 +0000537 my_syslog(MS_TFTP | LOG_ERR, _("error %d %s received from %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100538 (int)ntohs(mess->block), err,
Simon Kelleyc72daea2012-01-05 21:33:27 +0000539 daemon->addrbuff);
Simon Kelley832af0b2007-01-21 20:01:28 +0000540
541 /* Got err, ensure we take abort */
542 transfer->timeout = now;
543 transfer->backoff = 100;
544 }
545 }
546 }
547
548 if (difftime(now, transfer->timeout) >= 0.0)
549 {
550 int endcon = 0;
551
552 /* timeout, retransmit */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100553 transfer->timeout += 1 + (1<<transfer->backoff);
Simon Kelley832af0b2007-01-21 20:01:28 +0000554
555 /* we overwrote the buffer... */
556 daemon->srv_save = NULL;
557
558 if ((len = get_block(daemon->packet, transfer)) == -1)
559 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000560 len = tftp_err_oops(daemon->packet, transfer->file->filename);
Simon Kelley832af0b2007-01-21 20:01:28 +0000561 endcon = 1;
562 }
Simon Kelley42fb8152012-04-20 17:15:01 +0100563 /* don't complain about timeout when we're awaiting the last
564 ACK, some clients never send it */
565 else if (++transfer->backoff > 5 && len != 0)
Simon Kelley832af0b2007-01-21 20:01:28 +0000566 {
Simon Kelley42fb8152012-04-20 17:15:01 +0100567 endcon = 1;
568 len = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000569 }
Simon Kelley42fb8152012-04-20 17:15:01 +0100570
Simon Kelley832af0b2007-01-21 20:01:28 +0000571 if (len != 0)
572 while(sendto(transfer->sockfd, daemon->packet, len, 0,
573 (struct sockaddr *)&transfer->peer, sizeof(transfer->peer)) == -1 && errno == EINTR);
574
575 if (endcon || len == 0)
576 {
Simon Kelley42fb8152012-04-20 17:15:01 +0100577 strcpy(daemon->namebuff, transfer->file->filename);
578 sanitise(daemon->namebuff);
579 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 +0000580 /* unlink */
581 *up = tmp;
Simon Kelleya9530962012-03-20 22:07:35 +0000582 /* put on queue to be sent to script and deleted */
583 transfer->next = daemon->tftp_done_trans;
584 daemon->tftp_done_trans = transfer;
Simon Kelley832af0b2007-01-21 20:01:28 +0000585 continue;
586 }
587 }
588
589 up = &transfer->next;
Simon Kelley28866e92011-02-14 20:19:14 +0000590 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000591}
592
593static void free_transfer(struct tftp_transfer *transfer)
594{
595 close(transfer->sockfd);
596 if (transfer->file && (--transfer->file->refcount) == 0)
597 {
598 close(transfer->file->fd);
599 free(transfer->file);
600 }
601 free(transfer);
602}
603
604static char *next(char **p, char *end)
605{
606 char *ret = *p;
607 size_t len;
608
609 if (*(end-1) != 0 ||
610 *p == end ||
611 (len = strlen(ret)) == 0)
612 return NULL;
613
614 *p += len + 1;
615 return ret;
616}
617
Simon Kelley42fb8152012-04-20 17:15:01 +0100618static void sanitise(char *buf)
619{
Simon Kelley7a14dfe2012-04-20 20:50:42 +0100620 unsigned char *q, *r;
621 for (q = r = (unsigned char *)buf; *r; r++)
622 if (isprint(*r))
623 *(q++) = *r;
624 *q = 0;
Simon Kelley42fb8152012-04-20 17:15:01 +0100625
Simon Kelley7a14dfe2012-04-20 20:50:42 +0100626}
Simon Kelley42fb8152012-04-20 17:15:01 +0100627
Simon Kelley832af0b2007-01-21 20:01:28 +0000628static ssize_t tftp_err(int err, char *packet, char *message, char *file)
629{
630 struct errmess {
631 unsigned short op, err;
632 char message[];
633 } *mess = (struct errmess *)packet;
634 ssize_t ret = 4;
635 char *errstr = strerror(errno);
Simon Kelley42fb8152012-04-20 17:15:01 +0100636
637 sanitise(file);
638
Simon Kelley832af0b2007-01-21 20:01:28 +0000639 mess->op = htons(OP_ERR);
640 mess->err = htons(err);
641 ret += (snprintf(mess->message, 500, message, file, errstr) + 1);
Simon Kelley316e2732010-01-22 20:16:09 +0000642 my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message);
Simon Kelley832af0b2007-01-21 20:01:28 +0000643
644 return ret;
645}
646
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000647static ssize_t tftp_err_oops(char *packet, char *file)
648{
Simon Kelley42fb8152012-04-20 17:15:01 +0100649 /* May have >1 refs to file, so potentially mangle a copy of the name */
650 strcpy(daemon->namebuff, file);
651 return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000652}
653
Simon Kelley832af0b2007-01-21 20:01:28 +0000654/* return -1 for error, zero for done. */
655static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
656{
657 if (transfer->block == 0)
658 {
659 /* send OACK */
660 char *p;
661 struct oackmess {
662 unsigned short op;
663 char data[];
664 } *mess = (struct oackmess *)packet;
665
666 p = mess->data;
667 mess->op = htons(OP_OACK);
668 if (transfer->opt_blocksize)
669 {
670 p += (sprintf(p, "blksize") + 1);
671 p += (sprintf(p, "%d", transfer->blocksize) + 1);
672 }
673 if (transfer->opt_transize)
674 {
675 p += (sprintf(p,"tsize") + 1);
676 p += (sprintf(p, "%u", (unsigned int)transfer->file->size) + 1);
677 }
678
679 return p - packet;
680 }
681 else
682 {
683 /* send data packet */
684 struct datamess {
685 unsigned short op, block;
686 unsigned char data[];
687 } *mess = (struct datamess *)packet;
688
Simon Kelley9e038942008-05-30 20:06:34 +0100689 size_t size = transfer->file->size - transfer->offset;
Simon Kelley832af0b2007-01-21 20:01:28 +0000690
Simon Kelley9e038942008-05-30 20:06:34 +0100691 if (transfer->offset > transfer->file->size)
Simon Kelley832af0b2007-01-21 20:01:28 +0000692 return 0; /* finished */
693
694 if (size > transfer->blocksize)
695 size = transfer->blocksize;
696
Simon Kelley832af0b2007-01-21 20:01:28 +0000697 mess->op = htons(OP_DATA);
698 mess->block = htons((unsigned short)(transfer->block));
699
Simon Kelley9e038942008-05-30 20:06:34 +0100700 if (lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1 ||
701 !read_write(transfer->file->fd, mess->data, size, 1))
Simon Kelley832af0b2007-01-21 20:01:28 +0000702 return -1;
Simon Kelley9e038942008-05-30 20:06:34 +0100703
704 transfer->expansion = 0;
705
706 /* Map '\n' to CR-LF in netascii mode */
707 if (transfer->netascii)
708 {
709 size_t i;
710 int newcarrylf;
711
712 for (i = 0, newcarrylf = 0; i < size; i++)
713 if (mess->data[i] == '\n' && ( i != 0 || !transfer->carrylf))
714 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100715 transfer->expansion++;
716
717 if (size != transfer->blocksize)
Simon Kelley9e038942008-05-30 20:06:34 +0100718 size++; /* room in this block */
Simon Kelley7de060b2011-08-26 17:24:52 +0100719 else if (i == size - 1)
720 newcarrylf = 1; /* don't expand LF again if it moves to the next block */
721
Simon Kelley9e038942008-05-30 20:06:34 +0100722 /* make space and insert CR */
723 memmove(&mess->data[i+1], &mess->data[i], size - (i + 1));
724 mess->data[i] = '\r';
725
726 i++;
727 }
728 transfer->carrylf = newcarrylf;
729
730 }
731
732 return size + 4;
Simon Kelley832af0b2007-01-21 20:01:28 +0000733 }
734}
735
Simon Kelleya9530962012-03-20 22:07:35 +0000736
737int do_tftp_script_run(void)
738{
739 struct tftp_transfer *transfer;
740
741 if ((transfer = daemon->tftp_done_trans))
742 {
743 daemon->tftp_done_trans = transfer->next;
744#ifdef HAVE_SCRIPT
745 queue_tftp(transfer->file->size, transfer->file->filename, &transfer->peer);
746#endif
747 free_transfer(transfer);
748 return 1;
749 }
750
751 return 0;
752}
Simon Kelley832af0b2007-01-21 20:01:28 +0000753#endif