blob: b18c846724e7b79a9a41cb7a85fae0fdab80aeb9 [file] [log] [blame]
Simon Kelley5aabfc72007-08-29 11:24:47 +01001/* dnsmasq is Copyright (c) 2000-2007 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
13 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;
48 struct cmsghdr *cmptr;
49 struct iovec iov;
50 struct ifreq ifr;
51 int is_err = 1, if_index = 0;
52 struct iname *tmp;
Simon Kelley5aabfc72007-08-29 11:24:47 +010053 struct tftp_transfer *transfer;
Simon Kelley824af852008-02-12 20:43:05 +000054 int port = daemon->start_tftp_port; /* may be zero to use ephemeral port */
55#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
56 int mtu = IP_PMTUDISC_DONT;
57#endif
58
Simon Kelley832af0b2007-01-21 20:01:28 +000059 union {
60 struct cmsghdr align; /* this ensures alignment */
Simon Kelley824af852008-02-12 20:43:05 +000061#if defined(HAVE_LINUX_NETWORK)
Simon Kelley832af0b2007-01-21 20:01:28 +000062 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
Simon Kelley824af852008-02-12 20:43:05 +000063#elif defined(HAVE_SOLARIS_NETWORK)
64 char control[CMSG_SPACE(sizeof(unsigned int))];
Simon Kelley832af0b2007-01-21 20:01:28 +000065#else
66 char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
67#endif
68 } control_u;
69
70 msg.msg_controllen = sizeof(control_u);
71 msg.msg_control = control_u.control;
72 msg.msg_flags = 0;
73 msg.msg_name = &peer;
74 msg.msg_namelen = sizeof(peer);
75 msg.msg_iov = &iov;
76 msg.msg_iovlen = 1;
77
78 iov.iov_base = packet;
79 iov.iov_len = daemon->packet_buff_sz;
80
81 /* we overwrote the buffer... */
82 daemon->srv_save = NULL;
83
84 if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
85 return;
86
Simon Kelley1b7ecd12007-02-05 14:57:57 +000087 if (daemon->options & OPT_NOWILD)
88 addr = listen->iface->addr.in;
89 else
90 {
91 addr.sin_addr.s_addr = 0;
92
Simon Kelley832af0b2007-01-21 20:01:28 +000093#if defined(HAVE_LINUX_NETWORK)
Simon Kelley1b7ecd12007-02-05 14:57:57 +000094 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
95 if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
96 {
97 addr.sin_addr = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst;
98 if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
99 }
100 if (!(ifr.ifr_ifindex = if_index) ||
101 ioctl(listen->tftpfd, SIOCGIFNAME, &ifr) == -1)
102 return;
103
Simon Kelley832af0b2007-01-21 20:01:28 +0000104#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000105 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
106 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
107 addr.sin_addr = *((struct in_addr *)CMSG_DATA(cmptr));
108 else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
Simon Kelley824af852008-02-12 20:43:05 +0000109#ifdef HAVE_SOLARIS_NETWORK
110 if_index = *((unsigned int *)CMSG_DATA(cmptr));
111#else
112 if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
113#endif
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000114
115 if (if_index == 0 || !if_indextoname(if_index, ifr.ifr_name))
116 return;
117
Simon Kelley832af0b2007-01-21 20:01:28 +0000118#endif
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000119
120 if (addr.sin_addr.s_addr == 0)
121 return;
122
Simon Kelley5aabfc72007-08-29 11:24:47 +0100123 if (!iface_check(AF_INET, (struct all_addr *)&addr.sin_addr,
Simon Kelleyf2621c72007-04-29 19:47:21 +0100124 &ifr, &if_index))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000125 return;
126
127 /* allowed interfaces are the same as for DHCP */
128 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
129 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
130 return;
131
132 }
133
Simon Kelley824af852008-02-12 20:43:05 +0000134 addr.sin_port = htons(port);
Simon Kelley832af0b2007-01-21 20:01:28 +0000135 addr.sin_family = AF_INET;
136#ifdef HAVE_SOCKADDR_SA_LEN
137 addr.sin_len = sizeof(addr);
138#endif
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000139
Simon Kelley5aabfc72007-08-29 11:24:47 +0100140 if (!(transfer = whine_malloc(sizeof(struct tftp_transfer))))
Simon Kelley832af0b2007-01-21 20:01:28 +0000141 return;
142
143 if ((transfer->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
144 {
145 free(transfer);
146 return;
147 }
148
149 transfer->peer = peer;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100150 transfer->timeout = now + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +0000151 transfer->backoff = 1;
152 transfer->block = 1;
153 transfer->blocksize = 512;
Simon Kelley9e038942008-05-30 20:06:34 +0100154 transfer->offset = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000155 transfer->file = NULL;
156 transfer->opt_blocksize = transfer->opt_transize = 0;
Simon Kelley9e038942008-05-30 20:06:34 +0100157 transfer->netascii = transfer->carrylf = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000158
Simon Kelley824af852008-02-12 20:43:05 +0000159 /* if we have a nailed-down range, iterate until we find a free one. */
160 while (1)
Simon Kelley832af0b2007-01-21 20:01:28 +0000161 {
Simon Kelley824af852008-02-12 20:43:05 +0000162 if (bind(transfer->sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1 ||
163#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
164 setsockopt(transfer->sockfd, SOL_IP, IP_MTU_DISCOVER, &mtu, sizeof(mtu)) == -1 ||
165#endif
166 !fix_fd(transfer->sockfd))
167 {
168 if (errno == EADDRINUSE && daemon->start_tftp_port != 0)
169 {
170 if (++port <= daemon->end_tftp_port)
171 {
172 addr.sin_port = htons(port);
173 continue;
174 }
175 my_syslog(LOG_ERR, _("unable to get free port for TFTP"));
176 }
177 free_transfer(transfer);
178 return;
179 }
180 break;
Simon Kelley832af0b2007-01-21 20:01:28 +0000181 }
Simon Kelley824af852008-02-12 20:43:05 +0000182
Simon Kelley832af0b2007-01-21 20:01:28 +0000183 p = packet + 2;
184 end = packet + len;
185
186 if (ntohs(*((unsigned short *)packet)) != OP_RRQ ||
187 !(filename = next(&p, end)) ||
188 !(mode = next(&p, end)) ||
Simon Kelley9e038942008-05-30 20:06:34 +0100189 (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
Simon Kelley832af0b2007-01-21 20:01:28 +0000190 len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), inet_ntoa(peer.sin_addr));
191 else
192 {
Simon Kelley9e038942008-05-30 20:06:34 +0100193 if (strcasecmp(mode, "netascii") == 0)
194 transfer->netascii = 1;
195
Simon Kelley832af0b2007-01-21 20:01:28 +0000196 while ((opt = next(&p, end)))
197 {
198 if (strcasecmp(opt, "blksize") == 0 &&
Simon Kelley6b010842007-02-12 20:32:07 +0000199 (opt = next(&p, end)) &&
200 !(daemon->options & OPT_TFTP_NOBLOCK))
Simon Kelley832af0b2007-01-21 20:01:28 +0000201 {
202 transfer->blocksize = atoi(opt);
203 if (transfer->blocksize < 1)
204 transfer->blocksize = 1;
205 if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4)
206 transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4;
207 transfer->opt_blocksize = 1;
208 transfer->block = 0;
209 }
210
Simon Kelley9e038942008-05-30 20:06:34 +0100211 if (strcasecmp(opt, "tsize") == 0 && next(&p, end) && !transfer->netascii)
Simon Kelley832af0b2007-01-21 20:01:28 +0000212 {
213 transfer->opt_transize = 1;
214 transfer->block = 0;
215 }
216 }
217
Simon Kelleyf2621c72007-04-29 19:47:21 +0100218 strcpy(daemon->namebuff, "/");
Simon Kelley832af0b2007-01-21 20:01:28 +0000219 if (daemon->tftp_prefix)
220 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100221 if (daemon->tftp_prefix[0] == '/')
222 daemon->namebuff[0] = 0;
223 strncat(daemon->namebuff, daemon->tftp_prefix, MAXDNAME);
224 if (daemon->tftp_prefix[strlen(daemon->tftp_prefix)-1] != '/')
Simon Kelley832af0b2007-01-21 20:01:28 +0000225 strncat(daemon->namebuff, "/", MAXDNAME);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100226
227 if (daemon->options & OPT_TFTP_APREF)
228 {
229 size_t oldlen = strlen(daemon->namebuff);
230 struct stat statbuf;
231
232 strncat(daemon->namebuff, inet_ntoa(peer.sin_addr), MAXDNAME);
233 strncat(daemon->namebuff, "/", MAXDNAME);
234
235 /* remove unique-directory if it doesn't exist */
236 if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
237 daemon->namebuff[oldlen] = 0;
238 }
239
Simon Kelleyf2621c72007-04-29 19:47:21 +0100240 /* Absolute pathnames OK if they match prefix */
241 if (filename[0] == '/')
242 {
243 if (strstr(filename, daemon->namebuff) == filename)
244 daemon->namebuff[0] = 0;
245 else
246 filename++;
247 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000248 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100249 else if (filename[0] == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +0000250 daemon->namebuff[0] = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000251 strncat(daemon->namebuff, filename, MAXDNAME);
252 daemon->namebuff[MAXDNAME-1] = 0;
253
Simon Kelley5aabfc72007-08-29 11:24:47 +0100254 /* check permissions and open file */
255 if ((transfer->file = check_tftp_fileperm(&len)))
Simon Kelley832af0b2007-01-21 20:01:28 +0000256 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000257 if ((len = get_block(packet, transfer)) == -1)
258 len = tftp_err_oops(packet, daemon->namebuff);
259 else
260 is_err = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000261 }
262 }
263
264 while (sendto(transfer->sockfd, packet, len, 0,
265 (struct sockaddr *)&peer, sizeof(peer)) == -1 && errno == EINTR);
266
267 if (is_err)
268 free_transfer(transfer);
269 else
270 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100271 my_syslog(LOG_INFO, _("TFTP sent %s to %s"), daemon->namebuff, inet_ntoa(peer.sin_addr));
Simon Kelley832af0b2007-01-21 20:01:28 +0000272 transfer->next = daemon->tftp_trans;
273 daemon->tftp_trans = transfer;
274 }
275}
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000276
Simon Kelley5aabfc72007-08-29 11:24:47 +0100277static struct tftp_file *check_tftp_fileperm(ssize_t *len)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000278{
279 char *packet = daemon->packet, *namebuff = daemon->namebuff;
280 struct tftp_file *file;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100281 struct tftp_transfer *t;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000282 uid_t uid = geteuid();
283 struct stat statbuf;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100284 int fd = -1;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000285
286 /* trick to ban moving out of the subtree */
287 if (daemon->tftp_prefix && strstr(namebuff, "/../"))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100288 goto perm;
289
290 if ((fd = open(namebuff, O_RDONLY)) == -1)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000291 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100292 if (errno == ENOENT)
293 {
294 *len = tftp_err(ERR_FNF, packet, _("file %s not found"), namebuff);
295 return NULL;
296 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000297 else if (errno == EACCES)
298 goto perm;
299 else
300 goto oops;
301 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100302
303 /* stat the file descriptor to avoid stat->open races */
304 if (fstat(fd, &statbuf) == -1)
305 goto oops;
306
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000307 /* running as root, must be world-readable */
308 if (uid == 0)
309 {
310 if (!(statbuf.st_mode & S_IROTH))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100311 goto perm;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000312 }
313 /* in secure mode, must be owned by user running dnsmasq */
314 else if ((daemon->options & OPT_TFTP_SECURE) && uid != statbuf.st_uid)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100315 goto perm;
316
317 /* If we're doing many tranfers from the same file, only
318 open it once this saves lots of file descriptors
319 when mass-booting a big cluster, for instance.
320 Be conservative and only share when inode and name match
321 this keeps error messages sane. */
322 for (t = daemon->tftp_trans; t; t = t->next)
323 if (t->file->dev == statbuf.st_dev &&
324 t->file->inode == statbuf.st_ino &&
325 strcmp(t->file->filename, namebuff) == 0)
326 {
327 close(fd);
328 t->file->refcount++;
329 return t->file;
330 }
331
332 if (!(file = whine_malloc(sizeof(struct tftp_file) + strlen(namebuff) + 1)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000333 {
334 errno = ENOMEM;
335 goto oops;
336 }
337
Simon Kelley5aabfc72007-08-29 11:24:47 +0100338 file->fd = fd;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000339 file->size = statbuf.st_size;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100340 file->dev = statbuf.st_dev;
341 file->inode = statbuf.st_ino;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000342 file->refcount = 1;
343 strcpy(file->filename, namebuff);
344 return file;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100345
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000346 perm:
Simon Kelley5aabfc72007-08-29 11:24:47 +0100347 errno = EACCES;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000348 *len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100349 if (fd != -1)
350 close(fd);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000351 return NULL;
352
Simon Kelley5aabfc72007-08-29 11:24:47 +0100353 oops:
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000354 *len = tftp_err_oops(packet, namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100355 if (fd != -1)
356 close(fd);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000357 return NULL;
358}
359
Simon Kelley5aabfc72007-08-29 11:24:47 +0100360void check_tftp_listeners(fd_set *rset, time_t now)
Simon Kelley832af0b2007-01-21 20:01:28 +0000361{
362 struct tftp_transfer *transfer, *tmp, **up;
363 ssize_t len;
364
365 struct ack {
366 unsigned short op, block;
367 } *mess = (struct ack *)daemon->packet;
368
369 /* Check for activity on any existing transfers */
370 for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
371 {
372 tmp = transfer->next;
373
374 if (FD_ISSET(transfer->sockfd, rset))
375 {
376 /* we overwrote the buffer... */
377 daemon->srv_save = NULL;
378
379 if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack))
380 {
381 if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block)
382 {
383 /* Got ack, ensure we take the (re)transmit path */
384 transfer->timeout = now;
385 transfer->backoff = 0;
Simon Kelley9e038942008-05-30 20:06:34 +0100386 if (transfer->block++ != 0)
387 transfer->offset += transfer->blocksize - transfer->expansion;
Simon Kelley832af0b2007-01-21 20:01:28 +0000388 }
389 else if (ntohs(mess->op) == OP_ERR)
390 {
391 char *p = daemon->packet + sizeof(struct ack);
392 char *end = daemon->packet + len;
393 char *err = next(&p, end);
394 /* Sanitise error message */
395 if (!err)
396 err = "";
397 else
398 {
399 char *q, *r;
400 for (q = r = err; *r; r++)
Simon Kelley824af852008-02-12 20:43:05 +0000401 if (isprint((int)*r))
Simon Kelley832af0b2007-01-21 20:01:28 +0000402 *(q++) = *r;
403 *q = 0;
404 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100405 my_syslog(LOG_ERR, _("TFTP error %d %s received from %s"),
406 (int)ntohs(mess->block), err,
407 inet_ntoa(transfer->peer.sin_addr));
Simon Kelley832af0b2007-01-21 20:01:28 +0000408
409 /* Got err, ensure we take abort */
410 transfer->timeout = now;
411 transfer->backoff = 100;
412 }
413 }
414 }
415
416 if (difftime(now, transfer->timeout) >= 0.0)
417 {
418 int endcon = 0;
419
420 /* timeout, retransmit */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100421 transfer->timeout += 1 + (1<<transfer->backoff);
Simon Kelley832af0b2007-01-21 20:01:28 +0000422
423 /* we overwrote the buffer... */
424 daemon->srv_save = NULL;
425
426 if ((len = get_block(daemon->packet, transfer)) == -1)
427 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000428 len = tftp_err_oops(daemon->packet, transfer->file->filename);
Simon Kelley832af0b2007-01-21 20:01:28 +0000429 endcon = 1;
430 }
431 else if (++transfer->backoff > 5)
432 {
433 /* don't complain about timeout when we're awaiting the last
434 ACK, some clients never send it */
435 if (len != 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100436 my_syslog(LOG_ERR, _("TFTP failed sending %s to %s"),
437 transfer->file->filename, inet_ntoa(transfer->peer.sin_addr));
Simon Kelley832af0b2007-01-21 20:01:28 +0000438 len = 0;
439 }
440
441 if (len != 0)
442 while(sendto(transfer->sockfd, daemon->packet, len, 0,
443 (struct sockaddr *)&transfer->peer, sizeof(transfer->peer)) == -1 && errno == EINTR);
444
445 if (endcon || len == 0)
446 {
447 /* unlink */
448 *up = tmp;
449 free_transfer(transfer);
450 continue;
451 }
452 }
453
454 up = &transfer->next;
455 }
456}
457
458static void free_transfer(struct tftp_transfer *transfer)
459{
460 close(transfer->sockfd);
461 if (transfer->file && (--transfer->file->refcount) == 0)
462 {
463 close(transfer->file->fd);
464 free(transfer->file);
465 }
466 free(transfer);
467}
468
469static char *next(char **p, char *end)
470{
471 char *ret = *p;
472 size_t len;
473
474 if (*(end-1) != 0 ||
475 *p == end ||
476 (len = strlen(ret)) == 0)
477 return NULL;
478
479 *p += len + 1;
480 return ret;
481}
482
483static ssize_t tftp_err(int err, char *packet, char *message, char *file)
484{
485 struct errmess {
486 unsigned short op, err;
487 char message[];
488 } *mess = (struct errmess *)packet;
489 ssize_t ret = 4;
490 char *errstr = strerror(errno);
491
492 mess->op = htons(OP_ERR);
493 mess->err = htons(err);
494 ret += (snprintf(mess->message, 500, message, file, errstr) + 1);
495 if (err != ERR_FNF)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100496 my_syslog(LOG_ERR, "TFTP %s", mess->message);
Simon Kelley832af0b2007-01-21 20:01:28 +0000497
498 return ret;
499}
500
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000501static ssize_t tftp_err_oops(char *packet, char *file)
502{
503 return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), file);
504}
505
Simon Kelley832af0b2007-01-21 20:01:28 +0000506/* return -1 for error, zero for done. */
507static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
508{
509 if (transfer->block == 0)
510 {
511 /* send OACK */
512 char *p;
513 struct oackmess {
514 unsigned short op;
515 char data[];
516 } *mess = (struct oackmess *)packet;
517
518 p = mess->data;
519 mess->op = htons(OP_OACK);
520 if (transfer->opt_blocksize)
521 {
522 p += (sprintf(p, "blksize") + 1);
523 p += (sprintf(p, "%d", transfer->blocksize) + 1);
524 }
525 if (transfer->opt_transize)
526 {
527 p += (sprintf(p,"tsize") + 1);
528 p += (sprintf(p, "%u", (unsigned int)transfer->file->size) + 1);
529 }
530
531 return p - packet;
532 }
533 else
534 {
535 /* send data packet */
536 struct datamess {
537 unsigned short op, block;
538 unsigned char data[];
539 } *mess = (struct datamess *)packet;
540
Simon Kelley9e038942008-05-30 20:06:34 +0100541 size_t size = transfer->file->size - transfer->offset;
Simon Kelley832af0b2007-01-21 20:01:28 +0000542
Simon Kelley9e038942008-05-30 20:06:34 +0100543 if (transfer->offset > transfer->file->size)
Simon Kelley832af0b2007-01-21 20:01:28 +0000544 return 0; /* finished */
545
546 if (size > transfer->blocksize)
547 size = transfer->blocksize;
548
Simon Kelley832af0b2007-01-21 20:01:28 +0000549 mess->op = htons(OP_DATA);
550 mess->block = htons((unsigned short)(transfer->block));
551
Simon Kelley9e038942008-05-30 20:06:34 +0100552 if (lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1 ||
553 !read_write(transfer->file->fd, mess->data, size, 1))
Simon Kelley832af0b2007-01-21 20:01:28 +0000554 return -1;
Simon Kelley9e038942008-05-30 20:06:34 +0100555
556 transfer->expansion = 0;
557
558 /* Map '\n' to CR-LF in netascii mode */
559 if (transfer->netascii)
560 {
561 size_t i;
562 int newcarrylf;
563
564 for (i = 0, newcarrylf = 0; i < size; i++)
565 if (mess->data[i] == '\n' && ( i != 0 || !transfer->carrylf))
566 {
567 if (size == transfer->blocksize)
568 {
569 transfer->expansion++;
570 if (i == size - 1)
571 newcarrylf = 1; /* don't expand LF again if it moves to the next block */
572 }
573 else
574 size++; /* room in this block */
575
576 /* make space and insert CR */
577 memmove(&mess->data[i+1], &mess->data[i], size - (i + 1));
578 mess->data[i] = '\r';
579
580 i++;
581 }
582 transfer->carrylf = newcarrylf;
583
584 }
585
586 return size + 4;
Simon Kelley832af0b2007-01-21 20:01:28 +0000587 }
588}
589
590#endif