xref: /openbsd/usr.bin/tftp/tftpsubs.c (revision e6c7c102)
1 /*	$OpenBSD: tftpsubs.c,v 1.16 2024/04/23 13:34:51 jsg Exp $	*/
2 /*	$NetBSD: tftpsubs.c,v 1.3 1994/12/08 09:51:31 jtc Exp $	*/
3 
4 /*
5  * Copyright (c) 1983, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 /*
34  * Simple minded read-ahead/write-behind subroutines for tftp user and
35  * server.  Written originally with multiple buffers in mind, but current
36  * implementation has two buffer logic wired in.
37  *
38  * Todo:  add some sort of final error check so when the write-buffer
39  * is finally flushed, the caller can detect if the disk filled up
40  * (or had an i/o error) and return a nak to the other side.
41  *
42  *			Jim Guyton 10/85
43  */
44 
45 #include <sys/types.h>
46 #include <sys/socket.h>
47 #include <sys/ioctl.h>
48 
49 #include <netinet/in.h>
50 #include <arpa/tftp.h>
51 
52 #include <stdio.h>
53 #include <unistd.h>
54 
55 #include "tftpsubs.h"
56 
57 					/* values for bf.counter */
58 #define BF_ALLOC	-3		/* alloc'd but not yet filled */
59 #define BF_FREE		-2		/* free */
60 /* [-1 .. SEGSIZE] = size of data in the data buffer */
61 
62 static struct tftphdr	*rw_init(int);
63 
64 struct bf {
65 	int	counter;		/* size of data in buffer, or flag */
66 	char	buf[SEGSIZE_MAX + 4];	/* room for data packet */
67 } bfs[2];
68 
69 static int	nextone;	/* index of next buffer to use */
70 static int	current;	/* index of buffer in use */
71 				/* control flags for crlf conversions */
72 int		newline = 0;	/* fillbuf: in middle of newline expansion */
73 int		prevchar = -1;	/* putbuf: previous char (cr check) */
74 
75 struct tftphdr *
w_init(void)76 w_init(void)
77 {
78 	return (rw_init(0));	/* write-behind */
79 }
80 
81 struct tftphdr *
r_init(void)82 r_init(void)
83 {
84 	return (rw_init(1));	/* read-ahead */
85 }
86 
87 /*
88  * Init for either read-ahead or write-behind.
89  * Zero for write-behind, one for read-head.
90  */
91 static struct tftphdr *
rw_init(int x)92 rw_init(int x)
93 {
94 	newline = 0;			/* init crlf flag */
95 	prevchar = -1;
96 	bfs[0].counter = BF_ALLOC;	/* pass out the first buffer */
97 	current = 0;
98 	bfs[1].counter = BF_FREE;
99 	nextone = x;			/* ahead or behind? */
100 
101 	return ((struct tftphdr *)bfs[0].buf);
102 }
103 
104 /*
105  * Have emptied current buffer by sending to net and getting ack.
106  * Free it and return next buffer filled with data.
107  */
108 int
readit(FILE * file,struct tftphdr ** dpp,int convert,int segment_size)109 readit(FILE *file, struct tftphdr **dpp, int convert, int segment_size)
110 {
111 	struct bf	*b;
112 
113 	bfs[current].counter = BF_FREE;		/* free old one */
114 	current = !current;			/* "incr" current */
115 
116 	b = &bfs[current];			/* look at new buffer */
117 	if (b->counter == BF_FREE)		/* if it's empty */
118 		read_ahead(file, convert, segment_size);	/* fill it */
119 	/* assert(b->counter != BF_FREE); */	/* check */
120 	*dpp = (struct tftphdr *)b->buf;	/* set caller's ptr */
121 
122 	return (b->counter);
123 }
124 
125 /*
126  * Fill the input buffer, doing ascii conversions if requested.
127  * Conversions are lf -> cr, lf and cr -> cr, nul.
128  */
129 void
read_ahead(FILE * file,int convert,int segment_size)130 read_ahead(FILE *file, int convert, int segment_size)
131 {
132 	int		 i;
133 	char		*p;
134 	int		 c;
135 	struct bf	*b;
136 	struct tftphdr	*dp;
137 
138 	b = &bfs[nextone];			/* look at "next" buffer */
139 	if (b->counter != BF_FREE)		/* nop if not free */
140 		return;
141 	nextone = !nextone;			/* "incr" next buffer ptr */
142 
143 	dp = (struct tftphdr *)b->buf;
144 
145 	if (convert == 0) {
146 		b->counter = read(fileno(file), dp->th_data, segment_size);
147 		return;
148 	}
149 
150 	p = dp->th_data;
151 	for (i = 0; i < segment_size; i++) {
152 		if (newline) {
153 			if (prevchar == '\n')
154 				c = '\n';	/* lf to cr, lf */
155 			else
156 				c = '\0';	/* cr to cr, nul */
157 			newline = 0;
158 		} else {
159 			c = getc(file);
160 			if (c == EOF)
161 				break;
162 			if (c == '\n' || c == '\r') {
163 				prevchar = c;
164 				c = '\r';
165 				newline = 1;
166 			}
167 		}
168 		*p++ = c;
169 	}
170 	b->counter = (int)(p - dp->th_data);
171 }
172 
173 /*
174  * Update count associated with the buffer, get new buffer
175  * from the queue.  Calls write_behind only if next buffer not
176  * available.
177  */
178 int
writeit(FILE * file,struct tftphdr ** dpp,int ct,int convert)179 writeit(FILE *file, struct tftphdr **dpp, int ct, int convert)
180 {
181 	bfs[current].counter = ct;		/* set size of data to write */
182 	current = !current;			/* switch to other buffer */
183 	if (bfs[current].counter != BF_FREE)	/* if not free */
184 		/* flush it */
185 		(void)write_behind(file, convert);
186 	bfs[current].counter = BF_ALLOC;	/* mark as alloc'd */
187 	*dpp =  (struct tftphdr *)bfs[current].buf;
188 
189 	return (ct);				/* this is a lie of course */
190 }
191 
192 /*
193  * Output a buffer to a file, converting from netascii if requested.
194  * CR, NUL -> CR and CR, LF -> LF.
195  * Note spec is undefined if we get CR as last byte of file or a
196  * CR followed by anything else.  In this case we leave it alone.
197  */
198 int
write_behind(FILE * file,int convert)199 write_behind(FILE *file, int convert)
200 {
201 	char		*buf;
202 	int		 count;
203 	int		 ct;
204 	char		*p;
205 	int		 c; /* current character */
206 	struct bf	*b;
207 	struct tftphdr	*dp;
208 
209 	b = &bfs[nextone];
210 	if (b->counter < -1)		/* anything to flush? */
211 		return (0);		/* just nop if nothing to do */
212 
213 	count = b->counter;		/* remember byte count */
214 	b->counter = BF_FREE;		/* reset flag */
215 	dp = (struct tftphdr *)b->buf;
216 	nextone = !nextone;		/* incr for next time */
217 	buf = dp->th_data;
218 
219 	if (count <= 0)			/* nak logic? */
220 		return (-1);
221 
222 	if (convert == 0)
223 		return (write(fileno(file), buf, count));
224 
225 	p = buf;
226 	ct = count;
227 	while (ct--) {				/* loop over the buffer */
228 		c = *p++;			/* pick up a character */
229 		if (prevchar == '\r') {		/* if prev char was cr */
230 			if (c == '\n')		/* if have cr,lf then just */
231 				/* smash lf on top of the cr */
232 				fseek(file, -1, SEEK_CUR);
233 			else if (c == '\0')	/* if have cr,nul then */
234 				goto skipit;	/* just skip over the putc */
235 			/* FALLTHROUGH */
236 		}
237 		putc(c, file);
238 skipit:
239 		prevchar = c;
240 	}
241 
242 	return (count);
243 }
244 
245 /*
246  * When an error has occurred, it is possible that the two sides
247  * are out of synch.  Ie: that what I think is the other side's
248  * response to packet N is really their response to packet N-1.
249  *
250  * So, to try to prevent that, we flush all the input queued up
251  * for us on the network connection on our host.
252  *
253  * We return the number of packets we flushed (mostly for reporting
254  * when trace is active).
255  */
256 int
synchnet(int f)257 synchnet(int f)
258 {
259 	int			i, j = 0;
260 	char			rbuf[SEGSIZE_MIN];
261 	struct sockaddr_storage	from;
262 	socklen_t		fromlen;
263 
264 	for (;;) {
265 		(void)ioctl(f, FIONREAD, &i);
266 		if (i) {
267 			j++;
268 			fromlen = sizeof(from);
269 			(void)recvfrom(f, rbuf, sizeof(rbuf), 0,
270 			    (struct sockaddr *)&from, &fromlen);
271 		} else
272 			return (j);
273 	}
274 }
275