tftp-file.c revision 339059
1/*
2 * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26#include <sys/cdefs.h>
27__FBSDID("$FreeBSD: stable/10/libexec/tftpd/tftp-file.c 339059 2018-10-01 16:08:27Z asomers $");
28
29#include <sys/types.h>
30#include <sys/ioctl.h>
31#include <sys/socket.h>
32#include <sys/stat.h>
33
34#include <netinet/in.h>
35#include <arpa/tftp.h>
36
37#include <assert.h>
38#include <errno.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <syslog.h>
43#include <unistd.h>
44
45#include "tftp-file.h"
46#include "tftp-utils.h"
47
48static FILE	*file;
49static int	convert;
50
51static char	convbuffer[66000];
52static int	gotcr = 0;
53
54static size_t
55convert_from_net(char *buffer, size_t count)
56{
57	size_t i, n;
58
59	/*
60	 * Convert all CR/LF to LF and all CR,NUL to CR
61	 */
62
63	n = 0;
64	for (i = 0; i < count; i++) {
65
66		if (gotcr == 0) {
67			convbuffer[n++] = buffer[i];
68			gotcr = (buffer[i] == '\r');
69			continue;
70		}
71
72		/* CR, NULL -> CR */
73		if (buffer[i] == '\0') {
74			gotcr = 0;
75			continue;
76		}
77
78		/* CR, LF -> LF */
79		if (buffer[i] == '\n') {
80			if (n == 0) {
81				if (ftell(file) != 0) {
82					int r = fseek(file, -1, SEEK_END);
83					assert(r == 0);
84					convbuffer[n++] = '\n';
85				} else {
86					/* This shouldn't happen */
87					tftp_log(LOG_ERR,
88					    "Received LF as first character");
89					abort();
90				}
91			} else
92				convbuffer[n-1] = '\n';
93			gotcr = 0;
94			continue;
95		}
96
97		/* Everything else just accept as is */
98		convbuffer[n++] = buffer[i];
99		gotcr = (buffer[i] == '\r');
100		continue;
101	}
102
103	return fwrite(convbuffer, 1, n, file);
104}
105
106static size_t
107convert_to_net(char *buffer, size_t count, int init)
108{
109	size_t i;
110	static size_t n = 0, in = 0;
111	static int newline = 0;
112
113	if (init) {
114		newline = 0;
115		n = 0;
116		in = 0;
117		return 0 ;
118	}
119
120	/*
121	 * Convert all LF to CR,LF and all CR to CR,NUL
122	 */
123	i = 0;
124
125	if (newline) {
126		buffer[i++] = newline;
127		newline = 0;
128	}
129
130	while (i < count) {
131		if (n == in) {
132			/* When done we're done */
133			if (feof(file)) break;
134
135			/* Otherwise read another bunch */
136			in = fread(convbuffer, 1, count, file);
137			if (in == 0) break;
138			n = 0;
139		}
140
141		/* CR -> CR,NULL */
142		if (convbuffer[n] == '\r') {
143			buffer[i++] = '\r';
144			buffer[i++] = '\0';
145			n++;
146			continue;
147		}
148
149		/* LF -> CR,LF */
150		if (convbuffer[n] == '\n') {
151			buffer[i++] = '\r';
152			buffer[i++] = '\n';
153			n++;
154			continue;
155		}
156
157		buffer[i++] = convbuffer[n++];
158	}
159
160	if (i > count) {
161		/*
162		 * Whoops... that isn't alllowed (but it will happen
163		 * when there is a CR or LF at the end of the buffer)
164		 */
165		newline = buffer[i-1];
166	}
167
168	if (i < count) {
169		/* We are done! */
170		return i;
171	} else
172		return count;
173
174}
175
176int
177write_init(int fd, FILE *f, const char *mode)
178{
179
180	if (f == NULL) {
181		file = fdopen(fd, "w");
182		if (file == NULL) {
183			int en = errno;
184			tftp_log(LOG_ERR, "fdopen() failed: %s",
185			    strerror(errno));
186			return en;
187		}
188	} else
189		file = f;
190	convert = !strcmp(mode, "netascii");
191	return 0;
192}
193
194size_t
195write_file(char *buffer, int count)
196{
197
198	if (convert == 0)
199		return fwrite(buffer, 1, count, file);
200
201	return convert_from_net(buffer, count);
202}
203
204int
205write_close(void)
206{
207
208	if (fclose(file) != 0) {
209		tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
210		return 1;
211	}
212	return 0;
213}
214
215int
216read_init(int fd, FILE *f, const char *mode)
217{
218
219	convert_to_net(NULL, 0, 1);
220	if (f == NULL) {
221		file = fdopen(fd, "r");
222		if (file == NULL) {
223			int en = errno;
224			tftp_log(LOG_ERR, "fdopen() failed: %s",
225			    strerror(errno));
226			return en;
227		}
228	} else
229		file = f;
230	convert = !strcmp(mode, "netascii");
231	return 0;
232}
233
234size_t
235read_file(char *buffer, int count)
236{
237
238	if (convert == 0)
239		return fread(buffer, 1, count, file);
240
241	return convert_to_net(buffer, count, 0);
242}
243
244int
245read_close(void)
246{
247
248	if (fclose(file) != 0) {
249		tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
250		return 1;
251	}
252	return 0;
253}
254
255
256/* When an error has occurred, it is possible that the two sides
257 * are out of synch.  Ie: that what I think is the other side's
258 * response to packet N is really their response to packet N-1.
259 *
260 * So, to try to prevent that, we flush all the input queued up
261 * for us on the network connection on our host.
262 *
263 * We return the number of packets we flushed (mostly for reporting
264 * when trace is active).
265 */
266
267int
268synchnet(int peer)			/* socket to flush */
269{
270	int i, j = 0;
271	char rbuf[MAXPKTSIZE];
272	struct sockaddr_storage from;
273	socklen_t fromlen;
274
275	while (1) {
276		(void) ioctl(peer, FIONREAD, &i);
277		if (i) {
278			j++;
279			fromlen = sizeof from;
280			(void) recvfrom(peer, rbuf, sizeof (rbuf), 0,
281				(struct sockaddr *)&from, &fromlen);
282		} else {
283			return(j);
284		}
285	}
286}
287