1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29
30 /*
31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
33 * All Rights Reserved
34 *
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
37 * contributors.
38 */
39
40 /*
41 * Simple minded read-ahead/write-behind subroutines for tftp user and
42 * server. Written originally with multiple buffers in mind, but current
43 * implementation has two buffer logic wired in.
44 *
45 * Todo: add some sort of final error check so when the write-buffer
46 * is finally flushed, the caller can detect if the disk filled up
47 * (or had an i/o error) and return a nak to the other side.
48 */
49
50 #include <sys/types.h>
51 #include <sys/socket.h>
52 #include <sys/ioctl.h>
53 #include <sys/filio.h>
54
55 #include <netinet/in.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <unistd.h>
59 #include <signal.h>
60 #include <errno.h>
61 #include <poll.h>
62 #include <string.h>
63
64 #include "tftpcommon.h"
65
66 struct errmsg errmsgs[] = {
67 { EUNDEF, "Undefined error code" },
68 { ENOTFOUND, "File not found" },
69 { EACCESS, "Access violation" },
70 { ENOSPACE, "Disk full or allocation exceeded" },
71 { EBADOP, "Illegal TFTP operation" },
72 { EBADID, "Unknown transfer ID" },
73 { EEXISTS, "File already exists" },
74 { ENOUSER, "No such user" },
75 { EOPTNEG, "Option negotiation error" },
76 { -1, NULL }
77 };
78
79 static struct bf {
80 int counter; /* size of data in buffer, or flag */
81 tftpbuf buf; /* room for data packet */
82 } bfs[2];
83
84 extern int blocksize; /* Number of data bytes in a DATA packet */
85 /* Values for bf.counter */
86 #define BF_ALLOC -3 /* alloc'd but not yet filled */
87 #define BF_FREE -2 /* free */
88 /* [-1 .. blocksize] = size of data in the data buffer */
89
90 static int nextone; /* index of next buffer to use */
91 static int current; /* index of buffer in use */
92
93 /* control flags for crlf conversions */
94 static int newline = 0; /* fillbuf: in middle of newline expansion */
95 static int prevchar = -1; /* putbuf: previous char (cr check) */
96
97 static struct tftphdr *rw_init(int);
98
w_init()99 struct tftphdr *w_init() { return (rw_init(0)); } /* write-behind */
r_init()100 struct tftphdr *r_init() { return (rw_init(1)); } /* read-ahead */
101
102 /*
103 * Init for either read-ahead or write-behind.
104 * x is zero for write-behind, one for read-head.
105 */
106 static struct tftphdr *
rw_init(int x)107 rw_init(int x)
108 {
109 newline = 0; /* init crlf flag */
110 prevchar = -1;
111 bfs[0].counter = BF_ALLOC; /* pass out the first buffer */
112 current = 0;
113 bfs[1].counter = BF_FREE;
114 nextone = x; /* ahead or behind? */
115 return (&bfs[0].buf.tb_hdr);
116 }
117
118
119 /*
120 * Have emptied current buffer by sending to net and getting ack.
121 * Free it and return next buffer filled with data.
122 */
123 int
readit(FILE * file,struct tftphdr ** dpp,int convert)124 readit(FILE *file, struct tftphdr **dpp, int convert)
125 {
126 struct bf *b;
127
128 bfs[current].counter = BF_FREE; /* free old one */
129 current = !current; /* "incr" current */
130
131 b = &bfs[current]; /* look at new buffer */
132 if (b->counter == BF_FREE) /* if it's empty */
133 read_ahead(file, convert); /* fill it */
134 *dpp = &b->buf.tb_hdr; /* set caller's ptr */
135 return (b->counter);
136 }
137
138 /*
139 * fill the input buffer, doing ascii conversions if requested
140 * conversions are lf -> cr,lf and cr -> cr, nul
141 */
142 void
read_ahead(FILE * file,int convert)143 read_ahead(FILE *file, int convert)
144 {
145 int i;
146 char *p;
147 int c;
148 struct bf *b;
149 struct tftphdr *dp;
150
151 b = &bfs[nextone]; /* look at "next" buffer */
152 if (b->counter != BF_FREE) /* nop if not free */
153 return;
154 nextone = !nextone; /* "incr" next buffer ptr */
155
156 dp = &b->buf.tb_hdr;
157
158 if (!convert) {
159 b->counter = fread(dp->th_data, sizeof (char), blocksize,
160 file);
161 if (ferror(file))
162 b->counter = -1;
163 return;
164 }
165
166 p = dp->th_data;
167 for (i = 0; i < blocksize; i++) {
168 if (newline) {
169 if (prevchar == '\n')
170 c = '\n'; /* lf to cr,lf */
171 else c = '\0'; /* cr to cr,nul */
172 newline = 0;
173 } else {
174 c = getc(file);
175 if (c == EOF) break;
176 if (c == '\n' || c == '\r') {
177 prevchar = c;
178 c = '\r';
179 newline = 1;
180 }
181 }
182 *p++ = c;
183 }
184 b->counter = (int)(p - dp->th_data);
185 }
186
187 /*
188 * Update count associated with the buffer, get new buffer
189 * from the queue. Calls write_behind only if next buffer not
190 * available.
191 */
192 int
writeit(FILE * file,struct tftphdr ** dpp,int ct,int convert)193 writeit(FILE *file, struct tftphdr **dpp, int ct, int convert)
194 {
195 bfs[current].counter = ct; /* set size of data to write */
196 current = !current; /* switch to other buffer */
197 if (bfs[current].counter != BF_FREE) /* if not free */
198 if (write_behind(file, convert) < 0) /* flush it */
199 ct = -1;
200 bfs[current].counter = BF_ALLOC; /* mark as alloc'd */
201 *dpp = &bfs[current].buf.tb_hdr;
202 return (ct); /* this is a lie of course */
203 }
204
205 /*
206 * Output a buffer to a file, converting from netascii if requested.
207 * CR,NUL -> CR and CR,LF => LF.
208 * Note spec is undefined if we get CR as last byte of file or a
209 * CR followed by anything else. In this case we leave it alone.
210 */
211 int
write_behind(FILE * file,int convert)212 write_behind(FILE *file, 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 = &b->buf.tb_hdr;
229 nextone = !nextone; /* incr for next time */
230 buf = dp->th_data;
231
232 if (count <= 0)
233 return (0); /* nak logic? */
234
235 if (!convert) {
236 size_t left = count;
237
238 while (left > 0) {
239 size_t written;
240
241 written = fwrite(buf, sizeof (char), left, file);
242 if (ferror(file)) {
243 /* Retry if we were interrupted by a signal. */
244 if (errno == EINTR)
245 continue;
246 return (-1);
247 }
248 if (written == 0)
249 return (-1);
250
251 left -= written;
252 buf += written;
253 }
254
255 return (count);
256 }
257
258 p = buf;
259 ct = count;
260 while (ct--) { /* loop over the buffer */
261 c = *p++; /* pick up a character */
262 if (prevchar == '\r') { /* if prev char was cr */
263 if (c == '\n') { /* if have cr,lf then just */
264 /* smash lf on top of the cr */
265 if (fseek(file, -1, SEEK_CUR) < 0)
266 return (-1);
267 } else {
268 if (c == '\0') {
269 /*
270 * If we have cr,nul then
271 * just skip over the putc.
272 */
273 prevchar = 0;
274 continue;
275 }
276 }
277 /* else just fall through and allow it */
278 }
279 if (putc(c, file) == EOF)
280 return (-1);
281 prevchar = c;
282 }
283 return (count);
284 }
285
286
287 /*
288 * When an error has occurred, it is possible that the two sides
289 * are out of synch. Ie: that what I think is the other side's
290 * response to packet N is really their response to packet N-1.
291 *
292 * So, to try to prevent that, we flush all the input queued up
293 * for us on the network connection on our host.
294 *
295 * We return the number of packets we flushed (mostly for reporting
296 * when trace is active) or -1 in case of an error.
297 */
298
299 int
synchnet(int socket)300 synchnet(int socket)
301 {
302 struct pollfd pfd;
303 int packets;
304
305 pfd.fd = socket;
306 pfd.events = POLLRDNORM;
307 for (packets = 0; ; packets++) {
308 char buf;
309 struct sockaddr_in6 from;
310 socklen_t fromlen;
311
312 if (poll(&pfd, 1, 0) <= 0)
313 break;
314
315 /*
316 * A one byte buffer is enough because recvfrom() will
317 * discard the remaining data of the packet.
318 */
319 fromlen = sizeof (from);
320 if (recvfrom(socket, &buf, sizeof (buf), 0,
321 (struct sockaddr *)&from, &fromlen) < 0)
322 return (-1);
323 }
324
325 return (packets);
326 }
327
328 /*
329 * Return a pointer to the next field in string s, or return NULL if no
330 * terminating NUL is found for the current field before end.
331 */
332 char *
next_field(const char * s,const char * end)333 next_field(const char *s, const char *end)
334 {
335 if (s < end) {
336 s = memchr(s, 0, end - s);
337 if (s != NULL)
338 return ((char *)s + 1);
339 }
340 return (NULL);
341 }
342
343 /*
344 * Print to stream options in the format option_name=option_value
345 */
346 void
print_options(FILE * stream,char * opts,int len)347 print_options(FILE *stream, char *opts, int len)
348 {
349 char *cp, *optname, *optval;
350 char *endopts = opts + len;
351 int first = 1;
352
353 /*
354 * Ignore null padding, appended by broken TFTP clients to
355 * requests which don't include options.
356 */
357 cp = opts;
358 while ((cp < endopts) && (*cp == '\0'))
359 cp++;
360 if (cp == endopts)
361 return;
362
363 while (opts < endopts) {
364 optname = opts;
365 if ((optval = next_field(optname, endopts)) == NULL) {
366 (void) putc('?', stream);
367 return;
368 }
369 if (first)
370 first = 0;
371 else
372 (void) putc(' ', stream);
373 (void) fputs(optname, stream);
374 if ((opts = next_field(optval, endopts)) == NULL) {
375 (void) putc('?', stream);
376 return;
377 }
378 (void) fprintf(stream, "=%s", optval);
379 }
380 }
381
382 /*
383 * Turn off the alarm timer and ensure any pending SIGALRM signal is ignored.
384 */
385 void
cancel_alarm(void)386 cancel_alarm(void)
387 {
388 (void) alarm(0);
389 (void) signal(SIGALRM, SIG_IGN);
390 (void) sigrelse(SIGALRM);
391 }
392