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