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