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