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$");
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
37207614Simp#include <errno.h>
38207614Simp#include <stdio.h>
39207614Simp#include <stdlib.h>
40207614Simp#include <string.h>
41207614Simp#include <syslog.h>
42207614Simp#include <unistd.h>
43207614Simp
44207614Simp#include "tftp-file.h"
45207614Simp#include "tftp-utils.h"
46207614Simp
47207614Simpstatic FILE	*file;
48207614Simpstatic int	convert;
49207614Simp
50207614Simpstatic char	convbuffer[66000];
51207614Simpstatic int	gotcr = 0;
52207614Simp
53207614Simpstatic size_t
54207614Simpconvert_from_net(char *buffer, size_t count)
55207614Simp{
56207614Simp	size_t i, n;
57207614Simp
58207614Simp	/*
59207614Simp	 * Convert all CR/LF to LF and all CR,NUL to CR
60207614Simp	 */
61207614Simp
62207614Simp	n = 0;
63207614Simp	for (i = 0; i < count; i++) {
64207614Simp
65207614Simp		if (gotcr == 0) {
66207614Simp			convbuffer[n++] = buffer[i];
67207614Simp			gotcr = (buffer[i] == '\r');
68207614Simp			continue;
69207614Simp		}
70207614Simp
71207614Simp		/* CR, NULL -> CR */
72207614Simp		if (buffer[i] == '\0') {
73207614Simp			gotcr = 0;
74207614Simp			continue;
75207614Simp		}
76207614Simp
77207614Simp		/* CR, LF -> LF */
78207614Simp		if (buffer[i] == '\n') {
79207614Simp			if (n == 0) {
80207614Simp				if (ftell(file) != 0) {
81207614Simp					fseek(file, -1, SEEK_END);
82207614Simp					convbuffer[n++] = '\n';
83207614Simp				} else {
84207614Simp					/* This shouldn't happen */
85207614Simp					tftp_log(LOG_ERR,
86207614Simp					    "Received LF as first character");
87207614Simp					abort();
88207614Simp				}
89207614Simp			} else
90207614Simp				convbuffer[n-1] = '\n';
91207614Simp			gotcr = 0;
92207614Simp			continue;
93207614Simp		}
94207614Simp
95207614Simp		/* Everything else just accept as is */
96207614Simp		convbuffer[n++] = buffer[i];
97207614Simp		gotcr = (buffer[i] == '\r');
98207614Simp		continue;
99207614Simp	}
100207614Simp
101207614Simp	return fwrite(convbuffer, 1, n, file);
102207614Simp}
103207614Simp
104207614Simpstatic size_t
105207614Simpconvert_to_net(char *buffer, size_t count, int init)
106207614Simp{
107207614Simp	size_t i;
108213099Smarius	static size_t n = 0, in = 0;
109207614Simp	static int newline = 0;
110207614Simp
111207614Simp	if (init) {
112207614Simp		newline = 0;
113207614Simp		n = 0;
114213099Smarius		in = 0;
115207614Simp		return 0 ;
116207614Simp	}
117207614Simp
118207614Simp	/*
119207614Simp	 * Convert all LF to CR,LF and all CR to CR,NUL
120207614Simp	 */
121207614Simp	i = 0;
122207614Simp
123207614Simp	if (newline) {
124207614Simp		buffer[i++] = newline;
125207614Simp		newline = 0;
126207614Simp	}
127207614Simp
128207614Simp	while (i < count) {
129213099Smarius		if (n == in) {
130207614Simp			/* When done we're done */
131207614Simp			if (feof(file)) break;
132207614Simp
133207614Simp			/* Otherwise read another bunch */
134213099Smarius			in = fread(convbuffer, 1, count, file);
135213099Smarius			if (in == 0) break;
136207614Simp			n = 0;
137207614Simp		}
138207614Simp
139207614Simp		/* CR -> CR,NULL */
140207614Simp		if (convbuffer[n] == '\r') {
141207614Simp			buffer[i++] = '\r';
142207614Simp			buffer[i++] = '\0';
143207614Simp			n++;
144207614Simp			continue;
145207614Simp		}
146207614Simp
147207614Simp		/* LF -> CR,LF */
148207614Simp		if (convbuffer[n] == '\n') {
149207614Simp			buffer[i++] = '\r';
150207614Simp			buffer[i++] = '\n';
151207614Simp			n++;
152207614Simp			continue;
153207614Simp		}
154207614Simp
155207614Simp		buffer[i++] = convbuffer[n++];
156207614Simp	}
157207614Simp
158207614Simp	if (i > count) {
159207614Simp		/*
160207614Simp		 * Whoops... that isn't alllowed (but it will happen
161207614Simp		 * when there is a CR or LF at the end of the buffer)
162207614Simp		 */
163207614Simp		newline = buffer[i-1];
164207614Simp	}
165207614Simp
166207614Simp	if (i < count) {
167207614Simp		/* We are done! */
168207614Simp		return i;
169207614Simp	} else
170207614Simp		return count;
171207614Simp
172207614Simp}
173207614Simp
174207614Simpint
175207614Simpwrite_init(int fd, FILE *f, const char *mode)
176207614Simp{
177207614Simp
178207614Simp	if (f == NULL) {
179207614Simp		file = fdopen(fd, "w");
180207614Simp		if (file == NULL) {
181207614Simp			int en = errno;
182207614Simp			tftp_log(LOG_ERR, "fdopen() failed: %s",
183207614Simp			    strerror(errno));
184207614Simp			return en;
185207614Simp		}
186207614Simp	} else
187207614Simp		file = f;
188207614Simp	convert = !strcmp(mode, "netascii");
189207614Simp	return 0;
190207614Simp}
191207614Simp
192207614Simpsize_t
193207614Simpwrite_file(char *buffer, int count)
194207614Simp{
195207614Simp
196207614Simp	if (convert == 0)
197207614Simp		return fwrite(buffer, 1, count, file);
198207614Simp
199207614Simp	return convert_from_net(buffer, count);
200207614Simp}
201207614Simp
202207614Simpint
203207614Simpwrite_close(void)
204207614Simp{
205207614Simp
206207614Simp	if (fclose(file) != 0) {
207207614Simp		tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
208207614Simp		return 1;
209207614Simp	}
210207614Simp	return 0;
211207614Simp}
212207614Simp
213207614Simpint
214207614Simpread_init(int fd, FILE *f, const char *mode)
215207614Simp{
216207614Simp
217207614Simp	convert_to_net(NULL, 0, 1);
218207614Simp	if (f == NULL) {
219207614Simp		file = fdopen(fd, "r");
220207614Simp		if (file == NULL) {
221207614Simp			int en = errno;
222207614Simp			tftp_log(LOG_ERR, "fdopen() failed: %s",
223207614Simp			    strerror(errno));
224207614Simp			return en;
225207614Simp		}
226207614Simp	} else
227207614Simp		file = f;
228207614Simp	convert = !strcmp(mode, "netascii");
229207614Simp	return 0;
230207614Simp}
231207614Simp
232207614Simpsize_t
233207614Simpread_file(char *buffer, int count)
234207614Simp{
235207614Simp
236207614Simp	if (convert == 0)
237207614Simp		return fread(buffer, 1, count, file);
238207614Simp
239207614Simp	return convert_to_net(buffer, count, 0);
240207614Simp}
241207614Simp
242207614Simpint
243207614Simpread_close(void)
244207614Simp{
245207614Simp
246207614Simp	if (fclose(file) != 0) {
247207614Simp		tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
248207614Simp		return 1;
249207614Simp	}
250207614Simp	return 0;
251207614Simp}
252207614Simp
253207614Simp
254223487Srodrigc/* When an error has occurred, it is possible that the two sides
255223487Srodrigc * are out of synch.  Ie: that what I think is the other side's
256223487Srodrigc * response to packet N is really their response to packet N-1.
257223487Srodrigc *
258223487Srodrigc * So, to try to prevent that, we flush all the input queued up
259223487Srodrigc * for us on the network connection on our host.
260223487Srodrigc *
261223487Srodrigc * We return the number of packets we flushed (mostly for reporting
262223487Srodrigc * when trace is active).
263223487Srodrigc */
264223487Srodrigc
265207614Simpint
266223487Srodrigcsynchnet(int peer)			/* socket to flush */
267207614Simp{
268223487Srodrigc	int i, j = 0;
269223487Srodrigc	char rbuf[MAXPKTSIZE];
270223487Srodrigc	struct sockaddr_storage from;
271223487Srodrigc	socklen_t fromlen;
272207614Simp
273223487Srodrigc	while (1) {
274223487Srodrigc		(void) ioctl(peer, FIONREAD, &i);
275223487Srodrigc		if (i) {
276223487Srodrigc			j++;
277223487Srodrigc			fromlen = sizeof from;
278223487Srodrigc			(void) recvfrom(peer, rbuf, sizeof (rbuf), 0,
279223487Srodrigc				(struct sockaddr *)&from, &fromlen);
280223487Srodrigc		} else {
281223487Srodrigc			return(j);
282223487Srodrigc		}
283223487Srodrigc	}
284207614Simp}
285