blob: 82bbccc1f420a56c3c5e5d8a800b0773e434e650 [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 Kelley8bc4cec2012-07-03 21:04:11 +010021static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix);
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 Kelley8bc4cec2012-07-03 21:04:11 +010051 int is_err = 1, if_index = 0, mtu = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010052#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;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010064
Simon Kelley832af0b2007-01-21 20:01:28 +000065 union {
66 struct cmsghdr align; /* this ensures alignment */
Simon Kelley28866e92011-02-14 20:19:14 +000067#ifdef HAVE_IPV6
68 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
69#endif
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)
73 char control[CMSG_SPACE(sizeof(unsigned int))];
Simon Kelley7622fc02009-06-04 20:32:05 +010074#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
Simon Kelley832af0b2007-01-21 20:01:28 +000075 char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
76#endif
77 } control_u;
78
79 msg.msg_controllen = sizeof(control_u);
80 msg.msg_control = control_u.control;
81 msg.msg_flags = 0;
82 msg.msg_name = &peer;
83 msg.msg_namelen = sizeof(peer);
84 msg.msg_iov = &iov;
85 msg.msg_iovlen = 1;
86
87 iov.iov_base = packet;
88 iov.iov_len = daemon->packet_buff_sz;
89
90 /* we overwrote the buffer... */
91 daemon->srv_save = NULL;
92
93 if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
94 return;
95
Simon Kelley28866e92011-02-14 20:19:14 +000096 if (option_bool(OPT_NOWILD))
Simon Kelley1f15b812009-10-13 17:49:32 +010097 {
Simon Kelley52d4abf2012-03-21 21:39:48 +000098 if (listen->iface)
99 {
100 addr = listen->iface->addr;
101 mtu = listen->iface->mtu;
102 name = listen->iface->name;
103 }
104 else
105 {
106 /* we're listening on an address that doesn't appear on an interface,
107 ask the kernel what the socket is bound to */
108 socklen_t tcp_len = sizeof(union mysockaddr);
109 if (getsockname(listen->tftpfd, (struct sockaddr *)&addr, &tcp_len) == -1)
110 return;
111 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100112 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000113 else
114 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100115 struct cmsghdr *cmptr;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100116
Simon Kelley28866e92011-02-14 20:19:14 +0000117 if (msg.msg_controllen < sizeof(struct cmsghdr))
118 return;
119
120 addr.sa.sa_family = listen->family;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000121
Simon Kelley832af0b2007-01-21 20:01:28 +0000122#if defined(HAVE_LINUX_NETWORK)
Simon Kelley28866e92011-02-14 20:19:14 +0000123 if (listen->family == AF_INET)
124 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000125 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
Simon Kelley28866e92011-02-14 20:19:14 +0000126 {
127 union {
128 unsigned char *c;
129 struct in_pktinfo *p;
130 } p;
131 p.c = CMSG_DATA(cmptr);
132 addr.in.sin_addr = p.p->ipi_spec_dst;
133 if_index = p.p->ipi_ifindex;
134 }
135
136#elif defined(HAVE_SOLARIS_NETWORK)
137 if (listen->family == AF_INET)
138 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000139 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100140 union {
141 unsigned char *c;
Simon Kelley28866e92011-02-14 20:19:14 +0000142 struct in_addr *a;
143 unsigned int *i;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100144 } p;
145 p.c = CMSG_DATA(cmptr);
Simon Kelley28866e92011-02-14 20:19:14 +0000146 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
147 addr.in.sin_addr = *(p.a);
148 else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
149 if_index = *(p.i);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000150 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000151
Simon Kelley832af0b2007-01-21 20:01:28 +0000152#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
Simon Kelley28866e92011-02-14 20:19:14 +0000153 if (listen->family == AF_INET)
154 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
155 {
156 union {
157 unsigned char *c;
158 struct in_addr *a;
159 struct sockaddr_dl *s;
160 } p;
161 p.c = CMSG_DATA(cmptr);
162 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
163 addr.in.sin_addr = *(p.a);
164 else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
165 if_index = p.s->sdl_index;
166 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100167
Simon Kelley824af852008-02-12 20:43:05 +0000168#endif
Simon Kelley28866e92011-02-14 20:19:14 +0000169
170#ifdef HAVE_IPV6
171 if (listen->family == AF_INET6)
172 {
173 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000174 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
Simon Kelley28866e92011-02-14 20:19:14 +0000175 {
176 union {
177 unsigned char *c;
178 struct in6_pktinfo *p;
179 } p;
180 p.c = CMSG_DATA(cmptr);
181
182 addr.in6.sin6_addr = p.p->ipi6_addr;
183 if_index = p.p->ipi6_ifindex;
184 }
185 }
186#endif
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000187
Simon Kelley28866e92011-02-14 20:19:14 +0000188 if (!indextoname(listen->tftpfd, if_index, namebuff))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000189 return;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100190
191 name = namebuff;
Simon Kelley28866e92011-02-14 20:19:14 +0000192
193#ifdef HAVE_IPV6
194 if (listen->family == AF_INET6)
Simon Kelley3169daa2012-08-13 17:39:57 +0100195 {
196 if (!iface_check(AF_INET6, (struct all_addr *)&addr.in6.sin6_addr, name))
197 return;
198 }
Simon Kelley28866e92011-02-14 20:19:14 +0000199 else
200#endif
Simon Kelley3169daa2012-08-13 17:39:57 +0100201 if (!iface_check(AF_INET, (struct all_addr *)&addr.in.sin_addr, name))
202 return;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100203
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100204#ifdef HAVE_DHCP
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100205 /* allowed interfaces are the same as for DHCP */
206 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
207 if (tmp->name && (strcmp(tmp->name, name) == 0))
208 return;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100209#endif
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100210
Simon Kelley316e2732010-01-22 20:16:09 +0000211 strncpy(ifr.ifr_name, name, IF_NAMESIZE);
Simon Kelley1f15b812009-10-13 17:49:32 +0100212 if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
213 mtu = ifr.ifr_mtu;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000214 }
215
Simon Kelley52d4abf2012-03-21 21:39:48 +0000216 if (name)
217 {
218 /* check for per-interface prefix */
219 for (pref = daemon->if_prefix; pref; pref = pref->next)
220 if (strcmp(pref->interface, name) == 0)
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100221 prefix = pref->prefix;
Simon Kelley52d4abf2012-03-21 21:39:48 +0000222 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100223
Simon Kelley28866e92011-02-14 20:19:14 +0000224 if (listen->family == AF_INET)
Simon Kelley7de060b2011-08-26 17:24:52 +0100225 {
226 addr.in.sin_port = htons(port);
227#ifdef HAVE_SOCKADDR_SA_LEN
228 addr.in.sin_len = sizeof(addr.in);
229#endif
230 }
Simon Kelley28866e92011-02-14 20:19:14 +0000231#ifdef HAVE_IPV6
232 else
233 {
234 addr.in6.sin6_port = htons(port);
235 addr.in6.sin6_flowinfo = 0;
Simon Kelley7de060b2011-08-26 17:24:52 +0100236 addr.in6.sin6_scope_id = 0;
237#ifdef HAVE_SOCKADDR_SA_LEN
238 addr.in6.sin6_len = sizeof(addr.in6);
239#endif
Simon Kelley28866e92011-02-14 20:19:14 +0000240 }
241#endif
242
Simon Kelley5aabfc72007-08-29 11:24:47 +0100243 if (!(transfer = whine_malloc(sizeof(struct tftp_transfer))))
Simon Kelley832af0b2007-01-21 20:01:28 +0000244 return;
245
Simon Kelley28866e92011-02-14 20:19:14 +0000246 if ((transfer->sockfd = socket(listen->family, SOCK_DGRAM, 0)) == -1)
Simon Kelley832af0b2007-01-21 20:01:28 +0000247 {
248 free(transfer);
249 return;
250 }
251
252 transfer->peer = peer;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100253 transfer->timeout = now + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +0000254 transfer->backoff = 1;
255 transfer->block = 1;
256 transfer->blocksize = 512;
Simon Kelley9e038942008-05-30 20:06:34 +0100257 transfer->offset = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000258 transfer->file = NULL;
259 transfer->opt_blocksize = transfer->opt_transize = 0;
Simon Kelley9e038942008-05-30 20:06:34 +0100260 transfer->netascii = transfer->carrylf = 0;
Simon Kelley28866e92011-02-14 20:19:14 +0000261
Simon Kelleyc72daea2012-01-05 21:33:27 +0000262 prettyprint_addr(&peer, daemon->addrbuff);
Simon Kelley28866e92011-02-14 20:19:14 +0000263
Simon Kelley824af852008-02-12 20:43:05 +0000264 /* if we have a nailed-down range, iterate until we find a free one. */
265 while (1)
Simon Kelley832af0b2007-01-21 20:01:28 +0000266 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100267 if (bind(transfer->sockfd, &addr.sa, sa_len(&addr)) == -1 ||
Simon Kelley824af852008-02-12 20:43:05 +0000268#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000269 setsockopt(transfer->sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 ||
Simon Kelley824af852008-02-12 20:43:05 +0000270#endif
271 !fix_fd(transfer->sockfd))
272 {
273 if (errno == EADDRINUSE && daemon->start_tftp_port != 0)
274 {
275 if (++port <= daemon->end_tftp_port)
276 {
Simon Kelley28866e92011-02-14 20:19:14 +0000277 if (listen->family == AF_INET)
278 addr.in.sin_port = htons(port);
279#ifdef HAVE_IPV6
280 else
281 addr.in6.sin6_port = htons(port);
282#endif
Simon Kelley824af852008-02-12 20:43:05 +0000283 continue;
284 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100285 my_syslog(MS_TFTP | LOG_ERR, _("unable to get free port for TFTP"));
Simon Kelley824af852008-02-12 20:43:05 +0000286 }
287 free_transfer(transfer);
288 return;
289 }
290 break;
Simon Kelley832af0b2007-01-21 20:01:28 +0000291 }
Simon Kelley824af852008-02-12 20:43:05 +0000292
Simon Kelley832af0b2007-01-21 20:01:28 +0000293 p = packet + 2;
294 end = packet + len;
295
296 if (ntohs(*((unsigned short *)packet)) != OP_RRQ ||
297 !(filename = next(&p, end)) ||
298 !(mode = next(&p, end)) ||
Simon Kelley9e038942008-05-30 20:06:34 +0100299 (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
Simon Kelley6a69ab52012-04-24 14:42:26 +0100300 {
301 len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff);
302 is_err = 1;
303 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000304 else
305 {
Simon Kelley9e038942008-05-30 20:06:34 +0100306 if (strcasecmp(mode, "netascii") == 0)
307 transfer->netascii = 1;
308
Simon Kelley832af0b2007-01-21 20:01:28 +0000309 while ((opt = next(&p, end)))
310 {
Simon Kelley77e94da2009-08-31 17:32:17 +0100311 if (strcasecmp(opt, "blksize") == 0)
Simon Kelley832af0b2007-01-21 20:01:28 +0000312 {
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100313 if ((opt = next(&p, end)) && !option_bool(OPT_TFTP_NOBLOCK))
Simon Kelley77e94da2009-08-31 17:32:17 +0100314 {
315 transfer->blocksize = atoi(opt);
316 if (transfer->blocksize < 1)
317 transfer->blocksize = 1;
318 if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4)
319 transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4;
Simon Kelley1f15b812009-10-13 17:49:32 +0100320 /* 32 bytes for IP, UDP and TFTP headers */
321 if (mtu != 0 && transfer->blocksize > (unsigned)mtu - 32)
322 transfer->blocksize = (unsigned)mtu - 32;
Simon Kelley77e94da2009-08-31 17:32:17 +0100323 transfer->opt_blocksize = 1;
324 transfer->block = 0;
325 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000326 }
Simon Kelley77e94da2009-08-31 17:32:17 +0100327 else if (strcasecmp(opt, "tsize") == 0 && next(&p, end) && !transfer->netascii)
Simon Kelley832af0b2007-01-21 20:01:28 +0000328 {
329 transfer->opt_transize = 1;
330 transfer->block = 0;
331 }
332 }
333
Simon Kelley1f15b812009-10-13 17:49:32 +0100334 /* cope with backslashes from windows boxen. */
Simon Kelley61ce6002012-04-20 21:28:49 +0100335 for (p = filename; *p; p++)
336 if (*p == '\\')
337 *p = '/';
338 else if (option_bool(OPT_TFTP_LC))
339 *p = tolower(*p);
340
Simon Kelleyf2621c72007-04-29 19:47:21 +0100341 strcpy(daemon->namebuff, "/");
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100342 if (prefix)
Simon Kelley832af0b2007-01-21 20:01:28 +0000343 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100344 if (prefix[0] == '/')
Simon Kelleyf2621c72007-04-29 19:47:21 +0100345 daemon->namebuff[0] = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100346 strncat(daemon->namebuff, prefix, (MAXDNAME-1) - strlen(daemon->namebuff));
347 if (prefix[strlen(prefix)-1] != '/')
Simon Kelley77e94da2009-08-31 17:32:17 +0100348 strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelley5aabfc72007-08-29 11:24:47 +0100349
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100350 if (option_bool(OPT_TFTP_APREF))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100351 {
352 size_t oldlen = strlen(daemon->namebuff);
353 struct stat statbuf;
354
Simon Kelleyc72daea2012-01-05 21:33:27 +0000355 strncat(daemon->namebuff, daemon->addrbuff, (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelley77e94da2009-08-31 17:32:17 +0100356 strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelley5aabfc72007-08-29 11:24:47 +0100357
358 /* remove unique-directory if it doesn't exist */
359 if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
360 daemon->namebuff[oldlen] = 0;
361 }
362
Simon Kelleyf2621c72007-04-29 19:47:21 +0100363 /* Absolute pathnames OK if they match prefix */
364 if (filename[0] == '/')
365 {
366 if (strstr(filename, daemon->namebuff) == filename)
367 daemon->namebuff[0] = 0;
368 else
369 filename++;
370 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000371 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100372 else if (filename[0] == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +0000373 daemon->namebuff[0] = 0;
Simon Kelley77e94da2009-08-31 17:32:17 +0100374 strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelley832af0b2007-01-21 20:01:28 +0000375
Simon Kelley5aabfc72007-08-29 11:24:47 +0100376 /* check permissions and open file */
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100377 if ((transfer->file = check_tftp_fileperm(&len, prefix)))
Simon Kelley832af0b2007-01-21 20:01:28 +0000378 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000379 if ((len = get_block(packet, transfer)) == -1)
380 len = tftp_err_oops(packet, daemon->namebuff);
381 else
382 is_err = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000383 }
384 }
385
386 while (sendto(transfer->sockfd, packet, len, 0,
Simon Kelley04363602012-04-27 10:11:51 +0100387 (struct sockaddr *)&peer, sa_len(&peer)) == -1 && errno == EINTR);
Simon Kelley832af0b2007-01-21 20:01:28 +0000388
389 if (is_err)
390 free_transfer(transfer);
391 else
392 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000393 transfer->next = daemon->tftp_trans;
394 daemon->tftp_trans = transfer;
395 }
396}
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000397
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100398static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000399{
400 char *packet = daemon->packet, *namebuff = daemon->namebuff;
401 struct tftp_file *file;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100402 struct tftp_transfer *t;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000403 uid_t uid = geteuid();
404 struct stat statbuf;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100405 int fd = -1;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000406
407 /* trick to ban moving out of the subtree */
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100408 if (prefix && strstr(namebuff, "/../"))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100409 goto perm;
410
411 if ((fd = open(namebuff, O_RDONLY)) == -1)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000412 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100413 if (errno == ENOENT)
414 {
415 *len = tftp_err(ERR_FNF, packet, _("file %s not found"), namebuff);
416 return NULL;
417 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000418 else if (errno == EACCES)
419 goto perm;
420 else
421 goto oops;
422 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100423
424 /* stat the file descriptor to avoid stat->open races */
425 if (fstat(fd, &statbuf) == -1)
426 goto oops;
427
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000428 /* running as root, must be world-readable */
429 if (uid == 0)
430 {
431 if (!(statbuf.st_mode & S_IROTH))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100432 goto perm;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000433 }
434 /* in secure mode, must be owned by user running dnsmasq */
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100435 else if (option_bool(OPT_TFTP_SECURE) && uid != statbuf.st_uid)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100436 goto perm;
437
438 /* If we're doing many tranfers from the same file, only
439 open it once this saves lots of file descriptors
440 when mass-booting a big cluster, for instance.
441 Be conservative and only share when inode and name match
442 this keeps error messages sane. */
443 for (t = daemon->tftp_trans; t; t = t->next)
444 if (t->file->dev == statbuf.st_dev &&
445 t->file->inode == statbuf.st_ino &&
446 strcmp(t->file->filename, namebuff) == 0)
447 {
448 close(fd);
449 t->file->refcount++;
450 return t->file;
451 }
452
453 if (!(file = whine_malloc(sizeof(struct tftp_file) + strlen(namebuff) + 1)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000454 {
455 errno = ENOMEM;
456 goto oops;
457 }
458
Simon Kelley5aabfc72007-08-29 11:24:47 +0100459 file->fd = fd;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000460 file->size = statbuf.st_size;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100461 file->dev = statbuf.st_dev;
462 file->inode = statbuf.st_ino;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000463 file->refcount = 1;
464 strcpy(file->filename, namebuff);
465 return file;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100466
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000467 perm:
Simon Kelley5aabfc72007-08-29 11:24:47 +0100468 errno = EACCES;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000469 *len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100470 if (fd != -1)
471 close(fd);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000472 return NULL;
473
Simon Kelley5aabfc72007-08-29 11:24:47 +0100474 oops:
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000475 *len = tftp_err_oops(packet, namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100476 if (fd != -1)
477 close(fd);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000478 return NULL;
479}
480
Simon Kelley5aabfc72007-08-29 11:24:47 +0100481void check_tftp_listeners(fd_set *rset, time_t now)
Simon Kelley832af0b2007-01-21 20:01:28 +0000482{
483 struct tftp_transfer *transfer, *tmp, **up;
484 ssize_t len;
485
486 struct ack {
487 unsigned short op, block;
488 } *mess = (struct ack *)daemon->packet;
489
490 /* Check for activity on any existing transfers */
491 for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
492 {
493 tmp = transfer->next;
494
Simon Kelley6a69ab52012-04-24 14:42:26 +0100495 prettyprint_addr(&transfer->peer, daemon->addrbuff);
496
Simon Kelley832af0b2007-01-21 20:01:28 +0000497 if (FD_ISSET(transfer->sockfd, rset))
498 {
499 /* we overwrote the buffer... */
500 daemon->srv_save = NULL;
501
502 if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack))
503 {
504 if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block)
505 {
506 /* Got ack, ensure we take the (re)transmit path */
507 transfer->timeout = now;
508 transfer->backoff = 0;
Simon Kelley9e038942008-05-30 20:06:34 +0100509 if (transfer->block++ != 0)
510 transfer->offset += transfer->blocksize - transfer->expansion;
Simon Kelley832af0b2007-01-21 20:01:28 +0000511 }
512 else if (ntohs(mess->op) == OP_ERR)
513 {
514 char *p = daemon->packet + sizeof(struct ack);
515 char *end = daemon->packet + len;
516 char *err = next(&p, end);
Simon Kelley28866e92011-02-14 20:19:14 +0000517
Simon Kelley832af0b2007-01-21 20:01:28 +0000518 /* Sanitise error message */
519 if (!err)
520 err = "";
521 else
Simon Kelley7a14dfe2012-04-20 20:50:42 +0100522 sanitise(err);
Simon Kelley6a69ab52012-04-24 14:42:26 +0100523
Simon Kelley316e2732010-01-22 20:16:09 +0000524 my_syslog(MS_TFTP | LOG_ERR, _("error %d %s received from %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100525 (int)ntohs(mess->block), err,
Simon Kelleyc72daea2012-01-05 21:33:27 +0000526 daemon->addrbuff);
Simon Kelley832af0b2007-01-21 20:01:28 +0000527
528 /* Got err, ensure we take abort */
529 transfer->timeout = now;
530 transfer->backoff = 100;
531 }
532 }
533 }
534
535 if (difftime(now, transfer->timeout) >= 0.0)
536 {
537 int endcon = 0;
538
539 /* timeout, retransmit */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100540 transfer->timeout += 1 + (1<<transfer->backoff);
Simon Kelley832af0b2007-01-21 20:01:28 +0000541
542 /* we overwrote the buffer... */
543 daemon->srv_save = NULL;
544
545 if ((len = get_block(daemon->packet, transfer)) == -1)
546 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000547 len = tftp_err_oops(daemon->packet, transfer->file->filename);
Simon Kelley832af0b2007-01-21 20:01:28 +0000548 endcon = 1;
549 }
Simon Kelley42fb8152012-04-20 17:15:01 +0100550 /* don't complain about timeout when we're awaiting the last
551 ACK, some clients never send it */
552 else if (++transfer->backoff > 5 && len != 0)
Simon Kelley832af0b2007-01-21 20:01:28 +0000553 {
Simon Kelley42fb8152012-04-20 17:15:01 +0100554 endcon = 1;
555 len = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000556 }
Simon Kelley42fb8152012-04-20 17:15:01 +0100557
Simon Kelley832af0b2007-01-21 20:01:28 +0000558 if (len != 0)
559 while(sendto(transfer->sockfd, daemon->packet, len, 0,
Simon Kelley04363602012-04-27 10:11:51 +0100560 (struct sockaddr *)&transfer->peer, sa_len(&transfer->peer)) == -1 && errno == EINTR);
Simon Kelley832af0b2007-01-21 20:01:28 +0000561
562 if (endcon || len == 0)
563 {
Simon Kelley42fb8152012-04-20 17:15:01 +0100564 strcpy(daemon->namebuff, transfer->file->filename);
565 sanitise(daemon->namebuff);
566 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 +0000567 /* unlink */
568 *up = tmp;
Simon Kelley231d0612012-04-27 13:50:45 +0100569 if (endcon)
570 free_transfer(transfer);
571 else
572 {
573 /* put on queue to be sent to script and deleted */
574 transfer->next = daemon->tftp_done_trans;
575 daemon->tftp_done_trans = transfer;
576 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000577 continue;
578 }
579 }
580
581 up = &transfer->next;
Simon Kelley28866e92011-02-14 20:19:14 +0000582 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000583}
584
585static void free_transfer(struct tftp_transfer *transfer)
586{
587 close(transfer->sockfd);
588 if (transfer->file && (--transfer->file->refcount) == 0)
589 {
590 close(transfer->file->fd);
591 free(transfer->file);
592 }
593 free(transfer);
594}
595
596static char *next(char **p, char *end)
597{
598 char *ret = *p;
599 size_t len;
600
601 if (*(end-1) != 0 ||
602 *p == end ||
603 (len = strlen(ret)) == 0)
604 return NULL;
605
606 *p += len + 1;
607 return ret;
608}
609
Simon Kelley42fb8152012-04-20 17:15:01 +0100610static void sanitise(char *buf)
611{
Simon Kelley7a14dfe2012-04-20 20:50:42 +0100612 unsigned char *q, *r;
613 for (q = r = (unsigned char *)buf; *r; r++)
Simon Kelley11263a42012-04-27 14:00:55 +0100614 if (isprint((int)*r))
Simon Kelley7a14dfe2012-04-20 20:50:42 +0100615 *(q++) = *r;
616 *q = 0;
Simon Kelley42fb8152012-04-20 17:15:01 +0100617
Simon Kelley7a14dfe2012-04-20 20:50:42 +0100618}
Simon Kelley42fb8152012-04-20 17:15:01 +0100619
Simon Kelley832af0b2007-01-21 20:01:28 +0000620static ssize_t tftp_err(int err, char *packet, char *message, char *file)
621{
622 struct errmess {
623 unsigned short op, err;
624 char message[];
625 } *mess = (struct errmess *)packet;
626 ssize_t ret = 4;
627 char *errstr = strerror(errno);
Simon Kelley42fb8152012-04-20 17:15:01 +0100628
629 sanitise(file);
630
Simon Kelley832af0b2007-01-21 20:01:28 +0000631 mess->op = htons(OP_ERR);
632 mess->err = htons(err);
633 ret += (snprintf(mess->message, 500, message, file, errstr) + 1);
Simon Kelley316e2732010-01-22 20:16:09 +0000634 my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message);
Simon Kelley832af0b2007-01-21 20:01:28 +0000635
636 return ret;
637}
638
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000639static ssize_t tftp_err_oops(char *packet, char *file)
640{
Simon Kelley42fb8152012-04-20 17:15:01 +0100641 /* May have >1 refs to file, so potentially mangle a copy of the name */
642 strcpy(daemon->namebuff, file);
643 return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000644}
645
Simon Kelley832af0b2007-01-21 20:01:28 +0000646/* return -1 for error, zero for done. */
647static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
648{
649 if (transfer->block == 0)
650 {
651 /* send OACK */
652 char *p;
653 struct oackmess {
654 unsigned short op;
655 char data[];
656 } *mess = (struct oackmess *)packet;
657
658 p = mess->data;
659 mess->op = htons(OP_OACK);
660 if (transfer->opt_blocksize)
661 {
662 p += (sprintf(p, "blksize") + 1);
663 p += (sprintf(p, "%d", transfer->blocksize) + 1);
664 }
665 if (transfer->opt_transize)
666 {
667 p += (sprintf(p,"tsize") + 1);
668 p += (sprintf(p, "%u", (unsigned int)transfer->file->size) + 1);
669 }
670
671 return p - packet;
672 }
673 else
674 {
675 /* send data packet */
676 struct datamess {
677 unsigned short op, block;
678 unsigned char data[];
679 } *mess = (struct datamess *)packet;
680
Simon Kelley9e038942008-05-30 20:06:34 +0100681 size_t size = transfer->file->size - transfer->offset;
Simon Kelley832af0b2007-01-21 20:01:28 +0000682
Simon Kelley9e038942008-05-30 20:06:34 +0100683 if (transfer->offset > transfer->file->size)
Simon Kelley832af0b2007-01-21 20:01:28 +0000684 return 0; /* finished */
685
686 if (size > transfer->blocksize)
687 size = transfer->blocksize;
688
Simon Kelley832af0b2007-01-21 20:01:28 +0000689 mess->op = htons(OP_DATA);
690 mess->block = htons((unsigned short)(transfer->block));
691
Simon Kelley9e038942008-05-30 20:06:34 +0100692 if (lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1 ||
693 !read_write(transfer->file->fd, mess->data, size, 1))
Simon Kelley832af0b2007-01-21 20:01:28 +0000694 return -1;
Simon Kelley9e038942008-05-30 20:06:34 +0100695
696 transfer->expansion = 0;
697
698 /* Map '\n' to CR-LF in netascii mode */
699 if (transfer->netascii)
700 {
701 size_t i;
702 int newcarrylf;
703
704 for (i = 0, newcarrylf = 0; i < size; i++)
705 if (mess->data[i] == '\n' && ( i != 0 || !transfer->carrylf))
706 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100707 transfer->expansion++;
708
709 if (size != transfer->blocksize)
Simon Kelley9e038942008-05-30 20:06:34 +0100710 size++; /* room in this block */
Simon Kelley7de060b2011-08-26 17:24:52 +0100711 else if (i == size - 1)
712 newcarrylf = 1; /* don't expand LF again if it moves to the next block */
713
Simon Kelley9e038942008-05-30 20:06:34 +0100714 /* make space and insert CR */
715 memmove(&mess->data[i+1], &mess->data[i], size - (i + 1));
716 mess->data[i] = '\r';
717
718 i++;
719 }
720 transfer->carrylf = newcarrylf;
721
722 }
723
724 return size + 4;
Simon Kelley832af0b2007-01-21 20:01:28 +0000725 }
726}
727
Simon Kelleya9530962012-03-20 22:07:35 +0000728
729int do_tftp_script_run(void)
730{
731 struct tftp_transfer *transfer;
732
733 if ((transfer = daemon->tftp_done_trans))
734 {
735 daemon->tftp_done_trans = transfer->next;
736#ifdef HAVE_SCRIPT
737 queue_tftp(transfer->file->size, transfer->file->filename, &transfer->peer);
738#endif
739 free_transfer(transfer);
740 return 1;
741 }
742
743 return 0;
744}
Simon Kelley832af0b2007-01-21 20:01:28 +0000745#endif