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$");
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 <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					fseek(file, -1, SEEK_END);
82					convbuffer[n++] = '\n';
83				} else {
84					/* This shouldn't happen */
85					tftp_log(LOG_ERR,
86					    "Received LF as first character");
87					abort();
88				}
89			} else
90				convbuffer[n-1] = '\n';
91			gotcr = 0;
92			continue;
93		}
94
95		/* Everything else just accept as is */
96		convbuffer[n++] = buffer[i];
97		gotcr = (buffer[i] == '\r');
98		continue;
99	}
100
101	return fwrite(convbuffer, 1, n, file);
102}
103
104static size_t
105convert_to_net(char *buffer, size_t count, int init)
106{
107	size_t i;
108	static size_t n = 0, in = 0;
109	static int newline = 0;
110
111	if (init) {
112		newline = 0;
113		n = 0;
114		in = 0;
115		return 0 ;
116	}
117
118	/*
119	 * Convert all LF to CR,LF and all CR to CR,NUL
120	 */
121	i = 0;
122
123	if (newline) {
124		buffer[i++] = newline;
125		newline = 0;
126	}
127
128	while (i < count) {
129		if (n == in) {
130			/* When done we're done */
131			if (feof(file)) break;
132
133			/* Otherwise read another bunch */
134			in = fread(convbuffer, 1, count, file);
135			if (in == 0) break;
136			n = 0;
137		}
138
139		/* CR -> CR,NULL */
140		if (convbuffer[n] == '\r') {
141			buffer[i++] = '\r';
142			buffer[i++] = '\0';
143			n++;
144			continue;
145		}
146
147		/* LF -> CR,LF */
148		if (convbuffer[n] == '\n') {
149			buffer[i++] = '\r';
150			buffer[i++] = '\n';
151			n++;
152			continue;
153		}
154
155		buffer[i++] = convbuffer[n++];
156	}
157
158	if (i > count) {
159		/*
160		 * Whoops... that isn't alllowed (but it will happen
161		 * when there is a CR or LF at the end of the buffer)
162		 */
163		newline = buffer[i-1];
164	}
165
166	if (i < count) {
167		/* We are done! */
168		return i;
169	} else
170		return count;
171
172}
173
174int
175write_init(int fd, FILE *f, const char *mode)
176{
177
178	if (f == NULL) {
179		file = fdopen(fd, "w");
180		if (file == NULL) {
181			int en = errno;
182			tftp_log(LOG_ERR, "fdopen() failed: %s",
183			    strerror(errno));
184			return en;
185		}
186	} else
187		file = f;
188	convert = !strcmp(mode, "netascii");
189	return 0;
190}
191
192size_t
193write_file(char *buffer, int count)
194{
195
196	if (convert == 0)
197		return fwrite(buffer, 1, count, file);
198
199	return convert_from_net(buffer, count);
200}
201
202int
203write_close(void)
204{
205
206	if (fclose(file) != 0) {
207		tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
208		return 1;
209	}
210	return 0;
211}
212
213int
214read_init(int fd, FILE *f, const char *mode)
215{
216
217	convert_to_net(NULL, 0, 1);
218	if (f == NULL) {
219		file = fdopen(fd, "r");
220		if (file == NULL) {
221			int en = errno;
222			tftp_log(LOG_ERR, "fdopen() failed: %s",
223			    strerror(errno));
224			return en;
225		}
226	} else
227		file = f;
228	convert = !strcmp(mode, "netascii");
229	return 0;
230}
231
232size_t
233read_file(char *buffer, int count)
234{
235
236	if (convert == 0)
237		return fread(buffer, 1, count, file);
238
239	return convert_to_net(buffer, count, 0);
240}
241
242int
243read_close(void)
244{
245
246	if (fclose(file) != 0) {
247		tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
248		return 1;
249	}
250	return 0;
251}
252
253
254/* When an error has occurred, it is possible that the two sides
255 * are out of synch.  Ie: that what I think is the other side's
256 * response to packet N is really their response to packet N-1.
257 *
258 * So, to try to prevent that, we flush all the input queued up
259 * for us on the network connection on our host.
260 *
261 * We return the number of packets we flushed (mostly for reporting
262 * when trace is active).
263 */
264
265int
266synchnet(int peer)			/* socket to flush */
267{
268	int i, j = 0;
269	char rbuf[MAXPKTSIZE];
270	struct sockaddr_storage from;
271	socklen_t fromlen;
272
273	while (1) {
274		(void) ioctl(peer, FIONREAD, &i);
275		if (i) {
276			j++;
277			fromlen = sizeof from;
278			(void) recvfrom(peer, rbuf, sizeof (rbuf), 0,
279				(struct sockaddr *)&from, &fromlen);
280		} else {
281			return(j);
282		}
283	}
284}
285