tftp-file.c revision 207614
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: head/libexec/tftpd/tftp-file.c 207614 2010-05-04 13:07:40Z imp $");
28
29#include <sys/types.h>
30#include <sys/stat.h>
31
32#include <netinet/in.h>
33#include <arpa/tftp.h>
34
35#include <errno.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <syslog.h>
40#include <unistd.h>
41
42#include "tftp-file.h"
43#include "tftp-utils.h"
44
45static FILE	*file;
46static int	convert;
47
48static char	convbuffer[66000];
49static int	gotcr = 0;
50
51static size_t
52convert_from_net(char *buffer, size_t count)
53{
54	size_t i, n;
55
56	/*
57	 * Convert all CR/LF to LF and all CR,NUL to CR
58	 */
59
60	n = 0;
61	for (i = 0; i < count; i++) {
62
63		if (gotcr == 0) {
64			convbuffer[n++] = buffer[i];
65			gotcr = (buffer[i] == '\r');
66			continue;
67		}
68
69		/* CR, NULL -> CR */
70		if (buffer[i] == '\0') {
71			gotcr = 0;
72			continue;
73		}
74
75		/* CR, LF -> LF */
76		if (buffer[i] == '\n') {
77			if (n == 0) {
78				if (ftell(file) != 0) {
79					fseek(file, -1, SEEK_END);
80					convbuffer[n++] = '\n';
81				} else {
82					/* This shouldn't happen */
83					tftp_log(LOG_ERR,
84					    "Received LF as first character");
85					abort();
86				}
87			} else
88				convbuffer[n-1] = '\n';
89			gotcr = 0;
90			continue;
91		}
92
93		/* Everything else just accept as is */
94		convbuffer[n++] = buffer[i];
95		gotcr = (buffer[i] == '\r');
96		continue;
97	}
98
99	return fwrite(convbuffer, 1, n, file);
100}
101
102static size_t
103convert_to_net(char *buffer, size_t count, int init)
104{
105	size_t i;
106	static size_t n = 0, read = 0;
107	static int newline = 0;
108
109	if (init) {
110		newline = 0;
111		n = 0;
112		read = 0;
113		return 0 ;
114	}
115
116	/*
117	 * Convert all LF to CR,LF and all CR to CR,NUL
118	 */
119	i = 0;
120
121	if (newline) {
122		buffer[i++] = newline;
123		newline = 0;
124	}
125
126	while (i < count) {
127		if (n == read) {
128			/* When done we're done */
129			if (feof(file)) break;
130
131			/* Otherwise read another bunch */
132			read = fread(convbuffer, 1, count, file);
133			if (read == 0) break;
134			n = 0;
135		}
136
137		/* CR -> CR,NULL */
138		if (convbuffer[n] == '\r') {
139			buffer[i++] = '\r';
140			buffer[i++] = '\0';
141			n++;
142			continue;
143		}
144
145		/* LF -> CR,LF */
146		if (convbuffer[n] == '\n') {
147			buffer[i++] = '\r';
148			buffer[i++] = '\n';
149			n++;
150			continue;
151		}
152
153		buffer[i++] = convbuffer[n++];
154	}
155
156	if (i > count) {
157		/*
158		 * Whoops... that isn't alllowed (but it will happen
159		 * when there is a CR or LF at the end of the buffer)
160		 */
161		newline = buffer[i-1];
162	}
163
164	if (i < count) {
165		/* We are done! */
166		return i;
167	} else
168		return count;
169
170}
171
172int
173write_init(int fd, FILE *f, const char *mode)
174{
175
176	if (f == NULL) {
177		file = fdopen(fd, "w");
178		if (file == NULL) {
179			int en = errno;
180			tftp_log(LOG_ERR, "fdopen() failed: %s",
181			    strerror(errno));
182			return en;
183		}
184	} else
185		file = f;
186	convert = !strcmp(mode, "netascii");
187	return 0;
188}
189
190size_t
191write_file(char *buffer, int count)
192{
193
194	if (convert == 0)
195		return fwrite(buffer, 1, count, file);
196
197	return convert_from_net(buffer, count);
198}
199
200int
201write_close(void)
202{
203
204	if (fclose(file) != 0) {
205		tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
206		return 1;
207	}
208	return 0;
209}
210
211int
212read_init(int fd, FILE *f, const char *mode)
213{
214
215	convert_to_net(NULL, 0, 1);
216	if (f == NULL) {
217		file = fdopen(fd, "r");
218		if (file == NULL) {
219			int en = errno;
220			tftp_log(LOG_ERR, "fdopen() failed: %s",
221			    strerror(errno));
222			return en;
223		}
224	} else
225		file = f;
226	convert = !strcmp(mode, "netascii");
227	return 0;
228}
229
230size_t
231read_file(char *buffer, int count)
232{
233
234	if (convert == 0)
235		return fread(buffer, 1, count, file);
236
237	return convert_to_net(buffer, count, 0);
238}
239
240int
241read_close(void)
242{
243
244	if (fclose(file) != 0) {
245		tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
246		return 1;
247	}
248	return 0;
249}
250
251
252int
253synchnet(int peer)
254{
255
256	return 0;
257}
258