blob: c1ddb19b24d54d6391cc8eb0bd272ab767d0bfb6 [file] [log] [blame]
Simon Kelley73a08a22009-02-05 20:28:08 +00001/* dnsmasq is Copyright (c) 2000-2009 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 Kelley5aabfc72007-08-29 11:24:47 +010021static struct tftp_file *check_tftp_fileperm(ssize_t *len);
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);
27
28#define OP_RRQ 1
29#define OP_WRQ 2
30#define OP_DATA 3
31#define OP_ACK 4
32#define OP_ERR 5
33#define OP_OACK 6
34
35#define ERR_NOTDEF 0
36#define ERR_FNF 1
37#define ERR_PERM 2
38#define ERR_FULL 3
39#define ERR_ILL 4
40
Simon Kelley5aabfc72007-08-29 11:24:47 +010041void tftp_request(struct listener *listen, time_t now)
Simon Kelley832af0b2007-01-21 20:01:28 +000042{
43 ssize_t len;
44 char *packet = daemon->packet;
45 char *filename, *mode, *p, *end, *opt;
Simon Kelley832af0b2007-01-21 20:01:28 +000046 struct sockaddr_in addr, peer;
47 struct msghdr msg;
Simon Kelley832af0b2007-01-21 20:01:28 +000048 struct iovec iov;
Simon Kelley1f15b812009-10-13 17:49:32 +010049 struct ifreq ifr;
50 int is_err = 1, if_index = 0, mtu = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +000051 struct iname *tmp;
Simon Kelley5aabfc72007-08-29 11:24:47 +010052 struct tftp_transfer *transfer;
Simon Kelley824af852008-02-12 20:43:05 +000053 int port = daemon->start_tftp_port; /* may be zero to use ephemeral port */
54#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
Simon Kelley1f15b812009-10-13 17:49:32 +010055 int mtuflag = IP_PMTUDISC_DONT;
Simon Kelley824af852008-02-12 20:43:05 +000056#endif
57
Simon Kelley832af0b2007-01-21 20:01:28 +000058 union {
59 struct cmsghdr align; /* this ensures alignment */
Simon Kelley824af852008-02-12 20:43:05 +000060#if defined(HAVE_LINUX_NETWORK)
Simon Kelley832af0b2007-01-21 20:01:28 +000061 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
Simon Kelley824af852008-02-12 20:43:05 +000062#elif defined(HAVE_SOLARIS_NETWORK)
63 char control[CMSG_SPACE(sizeof(unsigned int))];
Simon Kelley7622fc02009-06-04 20:32:05 +010064#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
Simon Kelley832af0b2007-01-21 20:01:28 +000065 char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
66#endif
67 } control_u;
68
69 msg.msg_controllen = sizeof(control_u);
70 msg.msg_control = control_u.control;
71 msg.msg_flags = 0;
72 msg.msg_name = &peer;
73 msg.msg_namelen = sizeof(peer);
74 msg.msg_iov = &iov;
75 msg.msg_iovlen = 1;
76
77 iov.iov_base = packet;
78 iov.iov_len = daemon->packet_buff_sz;
79
80 /* we overwrote the buffer... */
81 daemon->srv_save = NULL;
82
83 if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
84 return;
85
Simon Kelley1b7ecd12007-02-05 14:57:57 +000086 if (daemon->options & OPT_NOWILD)
Simon Kelley1f15b812009-10-13 17:49:32 +010087 {
88 addr = listen->iface->addr.in;
89 mtu = listen->iface->mtu;
90 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +000091 else
92 {
Simon Kelley7622fc02009-06-04 20:32:05 +010093 char name[IF_NAMESIZE];
94 struct cmsghdr *cmptr;
95
Simon Kelley1b7ecd12007-02-05 14:57:57 +000096 addr.sin_addr.s_addr = 0;
97
Simon Kelley832af0b2007-01-21 20:01:28 +000098#if defined(HAVE_LINUX_NETWORK)
Simon Kelley1b7ecd12007-02-05 14:57:57 +000099 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
100 if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
101 {
102 addr.sin_addr = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst;
103 if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
104 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000105
Simon Kelley7622fc02009-06-04 20:32:05 +0100106#elif defined(HAVE_SOLARIS_NETWORK)
107 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
108 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
109 addr.sin_addr = *((struct in_addr *)CMSG_DATA(cmptr));
110 else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
111 if_index = *((unsigned int *)CMSG_DATA(cmptr));
112
113
Simon Kelley832af0b2007-01-21 20:01:28 +0000114#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000115 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
116 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
117 addr.sin_addr = *((struct in_addr *)CMSG_DATA(cmptr));
118 else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
Simon Kelley7622fc02009-06-04 20:32:05 +0100119 if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
120
Simon Kelley824af852008-02-12 20:43:05 +0000121#endif
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000122
Simon Kelley7622fc02009-06-04 20:32:05 +0100123 if (!indextoname(listen->tftpfd, if_index, name) ||
124 addr.sin_addr.s_addr == 0 ||
125 !iface_check(AF_INET, (struct all_addr *)&addr.sin_addr, name, &if_index))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000126 return;
127
128 /* allowed interfaces are the same as for DHCP */
129 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
Simon Kelley7622fc02009-06-04 20:32:05 +0100130 if (tmp->name && (strcmp(tmp->name, name) == 0))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000131 return;
Simon Kelley1f15b812009-10-13 17:49:32 +0100132
133 strncpy(name, ifr.ifr_name, IF_NAMESIZE);
134 if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
135 mtu = ifr.ifr_mtu;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000136 }
137
Simon Kelley824af852008-02-12 20:43:05 +0000138 addr.sin_port = htons(port);
Simon Kelley832af0b2007-01-21 20:01:28 +0000139 addr.sin_family = AF_INET;
140#ifdef HAVE_SOCKADDR_SA_LEN
141 addr.sin_len = sizeof(addr);
142#endif
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000143
Simon Kelley5aabfc72007-08-29 11:24:47 +0100144 if (!(transfer = whine_malloc(sizeof(struct tftp_transfer))))
Simon Kelley832af0b2007-01-21 20:01:28 +0000145 return;
146
147 if ((transfer->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
148 {
149 free(transfer);
150 return;
151 }
152
153 transfer->peer = peer;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100154 transfer->timeout = now + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +0000155 transfer->backoff = 1;
156 transfer->block = 1;
157 transfer->blocksize = 512;
Simon Kelley9e038942008-05-30 20:06:34 +0100158 transfer->offset = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000159 transfer->file = NULL;
160 transfer->opt_blocksize = transfer->opt_transize = 0;
Simon Kelley9e038942008-05-30 20:06:34 +0100161 transfer->netascii = transfer->carrylf = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000162
Simon Kelley824af852008-02-12 20:43:05 +0000163 /* if we have a nailed-down range, iterate until we find a free one. */
164 while (1)
Simon Kelley832af0b2007-01-21 20:01:28 +0000165 {
Simon Kelley824af852008-02-12 20:43:05 +0000166 if (bind(transfer->sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1 ||
167#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
Simon Kelley1f15b812009-10-13 17:49:32 +0100168 setsockopt(transfer->sockfd, SOL_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 ||
Simon Kelley824af852008-02-12 20:43:05 +0000169#endif
170 !fix_fd(transfer->sockfd))
171 {
172 if (errno == EADDRINUSE && daemon->start_tftp_port != 0)
173 {
174 if (++port <= daemon->end_tftp_port)
175 {
176 addr.sin_port = htons(port);
177 continue;
178 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100179 my_syslog(MS_TFTP | LOG_ERR, _("unable to get free port for TFTP"));
Simon Kelley824af852008-02-12 20:43:05 +0000180 }
181 free_transfer(transfer);
182 return;
183 }
184 break;
Simon Kelley832af0b2007-01-21 20:01:28 +0000185 }
Simon Kelley824af852008-02-12 20:43:05 +0000186
Simon Kelley832af0b2007-01-21 20:01:28 +0000187 p = packet + 2;
188 end = packet + len;
189
190 if (ntohs(*((unsigned short *)packet)) != OP_RRQ ||
191 !(filename = next(&p, end)) ||
192 !(mode = next(&p, end)) ||
Simon Kelley9e038942008-05-30 20:06:34 +0100193 (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
Simon Kelley832af0b2007-01-21 20:01:28 +0000194 len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), inet_ntoa(peer.sin_addr));
195 else
196 {
Simon Kelley9e038942008-05-30 20:06:34 +0100197 if (strcasecmp(mode, "netascii") == 0)
198 transfer->netascii = 1;
199
Simon Kelley832af0b2007-01-21 20:01:28 +0000200 while ((opt = next(&p, end)))
201 {
Simon Kelley77e94da2009-08-31 17:32:17 +0100202 if (strcasecmp(opt, "blksize") == 0)
Simon Kelley832af0b2007-01-21 20:01:28 +0000203 {
Simon Kelley77e94da2009-08-31 17:32:17 +0100204 if ((opt = next(&p, end)) &&
205 !(daemon->options & OPT_TFTP_NOBLOCK))
206 {
207 transfer->blocksize = atoi(opt);
208 if (transfer->blocksize < 1)
209 transfer->blocksize = 1;
210 if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4)
211 transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4;
Simon Kelley1f15b812009-10-13 17:49:32 +0100212 /* 32 bytes for IP, UDP and TFTP headers */
213 if (mtu != 0 && transfer->blocksize > (unsigned)mtu - 32)
214 transfer->blocksize = (unsigned)mtu - 32;
Simon Kelley77e94da2009-08-31 17:32:17 +0100215 transfer->opt_blocksize = 1;
216 transfer->block = 0;
217 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000218 }
Simon Kelley77e94da2009-08-31 17:32:17 +0100219 else if (strcasecmp(opt, "tsize") == 0 && next(&p, end) && !transfer->netascii)
Simon Kelley832af0b2007-01-21 20:01:28 +0000220 {
221 transfer->opt_transize = 1;
222 transfer->block = 0;
223 }
224 }
225
Simon Kelley1f15b812009-10-13 17:49:32 +0100226 /* cope with backslashes from windows boxen. */
227 while ((p = strchr(filename, '\\')))
228 *p = '/';
229
Simon Kelleyf2621c72007-04-29 19:47:21 +0100230 strcpy(daemon->namebuff, "/");
Simon Kelley832af0b2007-01-21 20:01:28 +0000231 if (daemon->tftp_prefix)
232 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100233 if (daemon->tftp_prefix[0] == '/')
234 daemon->namebuff[0] = 0;
Simon Kelley77e94da2009-08-31 17:32:17 +0100235 strncat(daemon->namebuff, daemon->tftp_prefix, (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100236 if (daemon->tftp_prefix[strlen(daemon->tftp_prefix)-1] != '/')
Simon Kelley77e94da2009-08-31 17:32:17 +0100237 strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelley5aabfc72007-08-29 11:24:47 +0100238
239 if (daemon->options & OPT_TFTP_APREF)
240 {
241 size_t oldlen = strlen(daemon->namebuff);
242 struct stat statbuf;
243
Simon Kelley77e94da2009-08-31 17:32:17 +0100244 strncat(daemon->namebuff, inet_ntoa(peer.sin_addr), (MAXDNAME-1) - strlen(daemon->namebuff));
245 strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelley5aabfc72007-08-29 11:24:47 +0100246
247 /* remove unique-directory if it doesn't exist */
248 if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
249 daemon->namebuff[oldlen] = 0;
250 }
251
Simon Kelleyf2621c72007-04-29 19:47:21 +0100252 /* Absolute pathnames OK if they match prefix */
253 if (filename[0] == '/')
254 {
255 if (strstr(filename, daemon->namebuff) == filename)
256 daemon->namebuff[0] = 0;
257 else
258 filename++;
259 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000260 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100261 else if (filename[0] == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +0000262 daemon->namebuff[0] = 0;
Simon Kelley77e94da2009-08-31 17:32:17 +0100263 strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelley832af0b2007-01-21 20:01:28 +0000264
Simon Kelley5aabfc72007-08-29 11:24:47 +0100265 /* check permissions and open file */
266 if ((transfer->file = check_tftp_fileperm(&len)))
Simon Kelley832af0b2007-01-21 20:01:28 +0000267 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000268 if ((len = get_block(packet, transfer)) == -1)
269 len = tftp_err_oops(packet, daemon->namebuff);
270 else
271 is_err = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000272 }
273 }
274
275 while (sendto(transfer->sockfd, packet, len, 0,
276 (struct sockaddr *)&peer, sizeof(peer)) == -1 && errno == EINTR);
277
278 if (is_err)
279 free_transfer(transfer);
280 else
281 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100282 my_syslog(MS_TFTP | LOG_INFO, _("TFTP sent %s to %s"), daemon->namebuff, inet_ntoa(peer.sin_addr));
Simon Kelley832af0b2007-01-21 20:01:28 +0000283 transfer->next = daemon->tftp_trans;
284 daemon->tftp_trans = transfer;
285 }
286}
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000287
Simon Kelley5aabfc72007-08-29 11:24:47 +0100288static struct tftp_file *check_tftp_fileperm(ssize_t *len)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000289{
290 char *packet = daemon->packet, *namebuff = daemon->namebuff;
291 struct tftp_file *file;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100292 struct tftp_transfer *t;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000293 uid_t uid = geteuid();
294 struct stat statbuf;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100295 int fd = -1;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000296
297 /* trick to ban moving out of the subtree */
298 if (daemon->tftp_prefix && strstr(namebuff, "/../"))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100299 goto perm;
300
301 if ((fd = open(namebuff, O_RDONLY)) == -1)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000302 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100303 if (errno == ENOENT)
304 {
305 *len = tftp_err(ERR_FNF, packet, _("file %s not found"), namebuff);
306 return NULL;
307 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000308 else if (errno == EACCES)
309 goto perm;
310 else
311 goto oops;
312 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100313
314 /* stat the file descriptor to avoid stat->open races */
315 if (fstat(fd, &statbuf) == -1)
316 goto oops;
317
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000318 /* running as root, must be world-readable */
319 if (uid == 0)
320 {
321 if (!(statbuf.st_mode & S_IROTH))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100322 goto perm;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000323 }
324 /* in secure mode, must be owned by user running dnsmasq */
325 else if ((daemon->options & OPT_TFTP_SECURE) && uid != statbuf.st_uid)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100326 goto perm;
327
328 /* If we're doing many tranfers from the same file, only
329 open it once this saves lots of file descriptors
330 when mass-booting a big cluster, for instance.
331 Be conservative and only share when inode and name match
332 this keeps error messages sane. */
333 for (t = daemon->tftp_trans; t; t = t->next)
334 if (t->file->dev == statbuf.st_dev &&
335 t->file->inode == statbuf.st_ino &&
336 strcmp(t->file->filename, namebuff) == 0)
337 {
338 close(fd);
339 t->file->refcount++;
340 return t->file;
341 }
342
343 if (!(file = whine_malloc(sizeof(struct tftp_file) + strlen(namebuff) + 1)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000344 {
345 errno = ENOMEM;
346 goto oops;
347 }
348
Simon Kelley5aabfc72007-08-29 11:24:47 +0100349 file->fd = fd;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000350 file->size = statbuf.st_size;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100351 file->dev = statbuf.st_dev;
352 file->inode = statbuf.st_ino;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000353 file->refcount = 1;
354 strcpy(file->filename, namebuff);
355 return file;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100356
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000357 perm:
Simon Kelley5aabfc72007-08-29 11:24:47 +0100358 errno = EACCES;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000359 *len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100360 if (fd != -1)
361 close(fd);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000362 return NULL;
363
Simon Kelley5aabfc72007-08-29 11:24:47 +0100364 oops:
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000365 *len = tftp_err_oops(packet, namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100366 if (fd != -1)
367 close(fd);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000368 return NULL;
369}
370
Simon Kelley5aabfc72007-08-29 11:24:47 +0100371void check_tftp_listeners(fd_set *rset, time_t now)
Simon Kelley832af0b2007-01-21 20:01:28 +0000372{
373 struct tftp_transfer *transfer, *tmp, **up;
374 ssize_t len;
375
376 struct ack {
377 unsigned short op, block;
378 } *mess = (struct ack *)daemon->packet;
379
380 /* Check for activity on any existing transfers */
381 for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
382 {
383 tmp = transfer->next;
384
385 if (FD_ISSET(transfer->sockfd, rset))
386 {
387 /* we overwrote the buffer... */
388 daemon->srv_save = NULL;
389
390 if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack))
391 {
392 if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block)
393 {
394 /* Got ack, ensure we take the (re)transmit path */
395 transfer->timeout = now;
396 transfer->backoff = 0;
Simon Kelley9e038942008-05-30 20:06:34 +0100397 if (transfer->block++ != 0)
398 transfer->offset += transfer->blocksize - transfer->expansion;
Simon Kelley832af0b2007-01-21 20:01:28 +0000399 }
400 else if (ntohs(mess->op) == OP_ERR)
401 {
402 char *p = daemon->packet + sizeof(struct ack);
403 char *end = daemon->packet + len;
404 char *err = next(&p, end);
405 /* Sanitise error message */
406 if (!err)
407 err = "";
408 else
409 {
410 char *q, *r;
411 for (q = r = err; *r; r++)
Simon Kelley824af852008-02-12 20:43:05 +0000412 if (isprint((int)*r))
Simon Kelley832af0b2007-01-21 20:01:28 +0000413 *(q++) = *r;
414 *q = 0;
415 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100416 my_syslog(MS_TFTP | LOG_ERR, _("TFTP error %d %s received from %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100417 (int)ntohs(mess->block), err,
418 inet_ntoa(transfer->peer.sin_addr));
Simon Kelley832af0b2007-01-21 20:01:28 +0000419
420 /* Got err, ensure we take abort */
421 transfer->timeout = now;
422 transfer->backoff = 100;
423 }
424 }
425 }
426
427 if (difftime(now, transfer->timeout) >= 0.0)
428 {
429 int endcon = 0;
430
431 /* timeout, retransmit */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100432 transfer->timeout += 1 + (1<<transfer->backoff);
Simon Kelley832af0b2007-01-21 20:01:28 +0000433
434 /* we overwrote the buffer... */
435 daemon->srv_save = NULL;
436
437 if ((len = get_block(daemon->packet, transfer)) == -1)
438 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000439 len = tftp_err_oops(daemon->packet, transfer->file->filename);
Simon Kelley832af0b2007-01-21 20:01:28 +0000440 endcon = 1;
441 }
442 else if (++transfer->backoff > 5)
443 {
444 /* don't complain about timeout when we're awaiting the last
445 ACK, some clients never send it */
446 if (len != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +0100447 my_syslog(MS_TFTP | LOG_ERR, _("TFTP failed sending %s to %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100448 transfer->file->filename, inet_ntoa(transfer->peer.sin_addr));
Simon Kelley832af0b2007-01-21 20:01:28 +0000449 len = 0;
450 }
451
452 if (len != 0)
453 while(sendto(transfer->sockfd, daemon->packet, len, 0,
454 (struct sockaddr *)&transfer->peer, sizeof(transfer->peer)) == -1 && errno == EINTR);
455
456 if (endcon || len == 0)
457 {
458 /* unlink */
459 *up = tmp;
460 free_transfer(transfer);
461 continue;
462 }
463 }
464
465 up = &transfer->next;
466 }
467}
468
469static void free_transfer(struct tftp_transfer *transfer)
470{
471 close(transfer->sockfd);
472 if (transfer->file && (--transfer->file->refcount) == 0)
473 {
474 close(transfer->file->fd);
475 free(transfer->file);
476 }
477 free(transfer);
478}
479
480static char *next(char **p, char *end)
481{
482 char *ret = *p;
483 size_t len;
484
485 if (*(end-1) != 0 ||
486 *p == end ||
487 (len = strlen(ret)) == 0)
488 return NULL;
489
490 *p += len + 1;
491 return ret;
492}
493
494static ssize_t tftp_err(int err, char *packet, char *message, char *file)
495{
496 struct errmess {
497 unsigned short op, err;
498 char message[];
499 } *mess = (struct errmess *)packet;
500 ssize_t ret = 4;
501 char *errstr = strerror(errno);
502
503 mess->op = htons(OP_ERR);
504 mess->err = htons(err);
505 ret += (snprintf(mess->message, 500, message, file, errstr) + 1);
Simon Kelley03a97b62009-06-10 20:55:49 +0100506 my_syslog(MS_TFTP | LOG_ERR, "TFTP %s", mess->message);
Simon Kelley832af0b2007-01-21 20:01:28 +0000507
508 return ret;
509}
510
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000511static ssize_t tftp_err_oops(char *packet, char *file)
512{
513 return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), file);
514}
515
Simon Kelley832af0b2007-01-21 20:01:28 +0000516/* return -1 for error, zero for done. */
517static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
518{
519 if (transfer->block == 0)
520 {
521 /* send OACK */
522 char *p;
523 struct oackmess {
524 unsigned short op;
525 char data[];
526 } *mess = (struct oackmess *)packet;
527
528 p = mess->data;
529 mess->op = htons(OP_OACK);
530 if (transfer->opt_blocksize)
531 {
532 p += (sprintf(p, "blksize") + 1);
533 p += (sprintf(p, "%d", transfer->blocksize) + 1);
534 }
535 if (transfer->opt_transize)
536 {
537 p += (sprintf(p,"tsize") + 1);
538 p += (sprintf(p, "%u", (unsigned int)transfer->file->size) + 1);
539 }
540
541 return p - packet;
542 }
543 else
544 {
545 /* send data packet */
546 struct datamess {
547 unsigned short op, block;
548 unsigned char data[];
549 } *mess = (struct datamess *)packet;
550
Simon Kelley9e038942008-05-30 20:06:34 +0100551 size_t size = transfer->file->size - transfer->offset;
Simon Kelley832af0b2007-01-21 20:01:28 +0000552
Simon Kelley9e038942008-05-30 20:06:34 +0100553 if (transfer->offset > transfer->file->size)
Simon Kelley832af0b2007-01-21 20:01:28 +0000554 return 0; /* finished */
555
556 if (size > transfer->blocksize)
557 size = transfer->blocksize;
558
Simon Kelley832af0b2007-01-21 20:01:28 +0000559 mess->op = htons(OP_DATA);
560 mess->block = htons((unsigned short)(transfer->block));
561
Simon Kelley9e038942008-05-30 20:06:34 +0100562 if (lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1 ||
563 !read_write(transfer->file->fd, mess->data, size, 1))
Simon Kelley832af0b2007-01-21 20:01:28 +0000564 return -1;
Simon Kelley9e038942008-05-30 20:06:34 +0100565
566 transfer->expansion = 0;
567
568 /* Map '\n' to CR-LF in netascii mode */
569 if (transfer->netascii)
570 {
571 size_t i;
572 int newcarrylf;
573
574 for (i = 0, newcarrylf = 0; i < size; i++)
575 if (mess->data[i] == '\n' && ( i != 0 || !transfer->carrylf))
576 {
577 if (size == transfer->blocksize)
578 {
579 transfer->expansion++;
580 if (i == size - 1)
581 newcarrylf = 1; /* don't expand LF again if it moves to the next block */
582 }
583 else
584 size++; /* room in this block */
585
586 /* make space and insert CR */
587 memmove(&mess->data[i+1], &mess->data[i], size - (i + 1));
588 mess->data[i] = '\r';
589
590 i++;
591 }
592 transfer->carrylf = newcarrylf;
593
594 }
595
596 return size + 4;
Simon Kelley832af0b2007-01-21 20:01:28 +0000597 }
598}
599
600#endif