1207614Simp/*
2207614Simp * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
3207614Simp *
4207614Simp * Redistribution and use in source and binary forms, with or without
5207614Simp * modification, are permitted provided that the following conditions
6207614Simp * are met:
7207614Simp * 1. Redistributions of source code must retain the above copyright
8207614Simp *    notice, this list of conditions and the following disclaimer.
9207614Simp * 2. Redistributions in binary form must reproduce the above copyright
10207614Simp *    notice, this list of conditions and the following disclaimer in the
11207614Simp *    documentation and/or other materials provided with the distribution.
12207614Simp *
13207614Simp * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14207614Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15207614Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16207614Simp * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17207614Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18207614Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19207614Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20207614Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21207614Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22207614Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23207614Simp * SUCH DAMAGE.
24207614Simp */
25207614Simp
26207614Simp#include <sys/cdefs.h>
27207614Simp__FBSDID("$FreeBSD: stable/11/libexec/tftpd/tftp-file.c 339057 2018-10-01 16:04:07Z asomers $");
28207614Simp
29207614Simp#include <sys/types.h>
30223487Srodrigc#include <sys/ioctl.h>
31223487Srodrigc#include <sys/socket.h>
32207614Simp#include <sys/stat.h>
33207614Simp
34207614Simp#include <netinet/in.h>
35207614Simp#include <arpa/tftp.h>
36207614Simp
37339051Sasomers#include <assert.h>
38207614Simp#include <errno.h>
39207614Simp#include <stdio.h>
40207614Simp#include <stdlib.h>
41207614Simp#include <string.h>
42207614Simp#include <syslog.h>
43207614Simp#include <unistd.h>
44207614Simp
45207614Simp#include "tftp-file.h"
46207614Simp#include "tftp-utils.h"
47207614Simp
48207614Simpstatic FILE	*file;
49207614Simpstatic int	convert;
50207614Simp
51207614Simpstatic char	convbuffer[66000];
52207614Simpstatic int	gotcr = 0;
53207614Simp
54207614Simpstatic size_t
55207614Simpconvert_from_net(char *buffer, size_t count)
56207614Simp{
57207614Simp	size_t i, n;
58207614Simp
59207614Simp	/*
60207614Simp	 * Convert all CR/LF to LF and all CR,NUL to CR
61207614Simp	 */
62207614Simp
63207614Simp	n = 0;
64207614Simp	for (i = 0; i < count; i++) {
65207614Simp
66207614Simp		if (gotcr == 0) {
67207614Simp			convbuffer[n++] = buffer[i];
68207614Simp			gotcr = (buffer[i] == '\r');
69207614Simp			continue;
70207614Simp		}
71207614Simp
72207614Simp		/* CR, NULL -> CR */
73207614Simp		if (buffer[i] == '\0') {
74207614Simp			gotcr = 0;
75207614Simp			continue;
76207614Simp		}
77207614Simp
78207614Simp		/* CR, LF -> LF */
79207614Simp		if (buffer[i] == '\n') {
80207614Simp			if (n == 0) {
81207614Simp				if (ftell(file) != 0) {
82339051Sasomers					int r = fseek(file, -1, SEEK_END);
83339051Sasomers					assert(r == 0);
84207614Simp					convbuffer[n++] = '\n';
85207614Simp				} else {
86207614Simp					/* This shouldn't happen */
87207614Simp					tftp_log(LOG_ERR,
88207614Simp					    "Received LF as first character");
89207614Simp					abort();
90207614Simp				}
91207614Simp			} else
92207614Simp				convbuffer[n-1] = '\n';
93207614Simp			gotcr = 0;
94207614Simp			continue;
95207614Simp		}
96207614Simp
97207614Simp		/* Everything else just accept as is */
98207614Simp		convbuffer[n++] = buffer[i];
99207614Simp		gotcr = (buffer[i] == '\r');
100207614Simp		continue;
101207614Simp	}
102207614Simp
103207614Simp	return fwrite(convbuffer, 1, n, file);
104207614Simp}
105207614Simp
106207614Simpstatic size_t
107207614Simpconvert_to_net(char *buffer, size_t count, int init)
108207614Simp{
109207614Simp	size_t i;
110213099Smarius	static size_t n = 0, in = 0;
111339057Sasomers	static int newline = -1;
112207614Simp
113207614Simp	if (init) {
114339057Sasomers		newline = -1;
115207614Simp		n = 0;
116213099Smarius		in = 0;
117207614Simp		return 0 ;
118207614Simp	}
119207614Simp
120207614Simp	/*
121207614Simp	 * Convert all LF to CR,LF and all CR to CR,NUL
122207614Simp	 */
123207614Simp	i = 0;
124207614Simp
125339057Sasomers	if (newline != -1) {
126207614Simp		buffer[i++] = newline;
127339057Sasomers		newline = -1;
128207614Simp	}
129207614Simp
130207614Simp	while (i < count) {
131213099Smarius		if (n == in) {
132207614Simp			/* When done we're done */
133207614Simp			if (feof(file)) break;
134207614Simp
135207614Simp			/* Otherwise read another bunch */
136213099Smarius			in = fread(convbuffer, 1, count, file);
137213099Smarius			if (in == 0) break;
138207614Simp			n = 0;
139207614Simp		}
140207614Simp
141207614Simp		/* CR -> CR,NULL */
142207614Simp		if (convbuffer[n] == '\r') {
143207614Simp			buffer[i++] = '\r';
144207614Simp			buffer[i++] = '\0';
145207614Simp			n++;
146207614Simp			continue;
147207614Simp		}
148207614Simp
149207614Simp		/* LF -> CR,LF */
150207614Simp		if (convbuffer[n] == '\n') {
151207614Simp			buffer[i++] = '\r';
152207614Simp			buffer[i++] = '\n';
153207614Simp			n++;
154207614Simp			continue;
155207614Simp		}
156207614Simp
157207614Simp		buffer[i++] = convbuffer[n++];
158207614Simp	}
159207614Simp
160207614Simp	if (i > count) {
161207614Simp		/*
162339057Sasomers		 * Whoops... that isn't allowed (but it will happen
163207614Simp		 * when there is a CR or LF at the end of the buffer)
164207614Simp		 */
165207614Simp		newline = buffer[i-1];
166207614Simp	}
167207614Simp
168207614Simp	if (i < count) {
169207614Simp		/* We are done! */
170207614Simp		return i;
171207614Simp	} else
172207614Simp		return count;
173207614Simp
174207614Simp}
175207614Simp
176207614Simpint
177207614Simpwrite_init(int fd, FILE *f, const char *mode)
178207614Simp{
179207614Simp
180207614Simp	if (f == NULL) {
181207614Simp		file = fdopen(fd, "w");
182207614Simp		if (file == NULL) {
183207614Simp			int en = errno;
184207614Simp			tftp_log(LOG_ERR, "fdopen() failed: %s",
185207614Simp			    strerror(errno));
186207614Simp			return en;
187207614Simp		}
188207614Simp	} else
189207614Simp		file = f;
190207614Simp	convert = !strcmp(mode, "netascii");
191207614Simp	return 0;
192207614Simp}
193207614Simp
194207614Simpsize_t
195207614Simpwrite_file(char *buffer, int count)
196207614Simp{
197207614Simp
198207614Simp	if (convert == 0)
199207614Simp		return fwrite(buffer, 1, count, file);
200207614Simp
201207614Simp	return convert_from_net(buffer, count);
202207614Simp}
203207614Simp
204207614Simpint
205207614Simpwrite_close(void)
206207614Simp{
207207614Simp
208207614Simp	if (fclose(file) != 0) {
209207614Simp		tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
210207614Simp		return 1;
211207614Simp	}
212207614Simp	return 0;
213207614Simp}
214207614Simp
215207614Simpint
216207614Simpread_init(int fd, FILE *f, const char *mode)
217207614Simp{
218207614Simp
219207614Simp	convert_to_net(NULL, 0, 1);
220207614Simp	if (f == NULL) {
221207614Simp		file = fdopen(fd, "r");
222207614Simp		if (file == NULL) {
223207614Simp			int en = errno;
224207614Simp			tftp_log(LOG_ERR, "fdopen() failed: %s",
225207614Simp			    strerror(errno));
226207614Simp			return en;
227207614Simp		}
228207614Simp	} else
229207614Simp		file = f;
230207614Simp	convert = !strcmp(mode, "netascii");
231207614Simp	return 0;
232207614Simp}
233207614Simp
234207614Simpsize_t
235207614Simpread_file(char *buffer, int count)
236207614Simp{
237207614Simp
238207614Simp	if (convert == 0)
239207614Simp		return fread(buffer, 1, count, file);
240207614Simp
241207614Simp	return convert_to_net(buffer, count, 0);
242207614Simp}
243207614Simp
244207614Simpint
245207614Simpread_close(void)
246207614Simp{
247207614Simp
248207614Simp	if (fclose(file) != 0) {
249207614Simp		tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
250207614Simp		return 1;
251207614Simp	}
252207614Simp	return 0;
253207614Simp}
254207614Simp
255207614Simp
256223487Srodrigc/* When an error has occurred, it is possible that the two sides
257223487Srodrigc * are out of synch.  Ie: that what I think is the other side's
258223487Srodrigc * response to packet N is really their response to packet N-1.
259223487Srodrigc *
260223487Srodrigc * So, to try to prevent that, we flush all the input queued up
261223487Srodrigc * for us on the network connection on our host.
262223487Srodrigc *
263223487Srodrigc * We return the number of packets we flushed (mostly for reporting
264223487Srodrigc * when trace is active).
265223487Srodrigc */
266223487Srodrigc
267207614Simpint
268223487Srodrigcsynchnet(int peer)			/* socket to flush */
269207614Simp{
270223487Srodrigc	int i, j = 0;
271223487Srodrigc	char rbuf[MAXPKTSIZE];
272223487Srodrigc	struct sockaddr_storage from;
273223487Srodrigc	socklen_t fromlen;
274207614Simp
275223487Srodrigc	while (1) {
276223487Srodrigc		(void) ioctl(peer, FIONREAD, &i);
277223487Srodrigc		if (i) {
278223487Srodrigc			j++;
279223487Srodrigc			fromlen = sizeof from;
280223487Srodrigc			(void) recvfrom(peer, rbuf, sizeof (rbuf), 0,
281223487Srodrigc				(struct sockaddr *)&from, &fromlen);
282223487Srodrigc		} else {
283223487Srodrigc			return(j);
284223487Srodrigc		}
285223487Srodrigc	}
286207614Simp}
287