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