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