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