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