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
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.
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]
23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
40 #pragma ident "%Z%%M% %I% %E% SMI"
43 * Simple minded read-ahead/write-behind subroutines for tftp user and
44 * server. Written originally with multiple buffers in mind, but current
45 * implementation has two buffer logic wired in.
47 * Todo: add some sort of final error check so when the write-buffer
48 * is finally flushed, the caller can detect if the disk filled up
49 * (or had an i/o error) and return a nak to the other side.
52 #include <sys/types.h>
53 #include <sys/socket.h>
54 #include <sys/ioctl.h>
55 #include <sys/filio.h>
57 #include <netinet/in.h>
66 #include "tftpcommon.h"
68 struct errmsg errmsgs
[] = {
69 { EUNDEF
, "Undefined error code" },
70 { ENOTFOUND
, "File not found" },
71 { EACCESS
, "Access violation" },
72 { ENOSPACE
, "Disk full or allocation exceeded" },
73 { EBADOP
, "Illegal TFTP operation" },
74 { EBADID
, "Unknown transfer ID" },
75 { EEXISTS
, "File already exists" },
76 { ENOUSER
, "No such user" },
77 { EOPTNEG
, "Option negotiation error" },
82 int counter
; /* size of data in buffer, or flag */
83 tftpbuf buf
; /* room for data packet */
86 extern int blocksize
; /* Number of data bytes in a DATA packet */
87 /* Values for bf.counter */
88 #define BF_ALLOC -3 /* alloc'd but not yet filled */
89 #define BF_FREE -2 /* free */
90 /* [-1 .. blocksize] = size of data in the data buffer */
92 static int nextone
; /* index of next buffer to use */
93 static int current
; /* index of buffer in use */
95 /* control flags for crlf conversions */
96 static int newline
= 0; /* fillbuf: in middle of newline expansion */
97 static int prevchar
= -1; /* putbuf: previous char (cr check) */
99 static struct tftphdr
*rw_init(int);
101 struct tftphdr
*w_init() { return (rw_init(0)); } /* write-behind */
102 struct tftphdr
*r_init() { return (rw_init(1)); } /* read-ahead */
105 * Init for either read-ahead or write-behind.
106 * x is zero for write-behind, one for read-head.
108 static struct tftphdr
*
111 newline
= 0; /* init crlf flag */
113 bfs
[0].counter
= BF_ALLOC
; /* pass out the first buffer */
115 bfs
[1].counter
= BF_FREE
;
116 nextone
= x
; /* ahead or behind? */
117 return (&bfs
[0].buf
.tb_hdr
);
122 * Have emptied current buffer by sending to net and getting ack.
123 * Free it and return next buffer filled with data.
126 readit(FILE *file
, struct tftphdr
**dpp
, int convert
)
130 bfs
[current
].counter
= BF_FREE
; /* free old one */
131 current
= !current
; /* "incr" current */
133 b
= &bfs
[current
]; /* look at new buffer */
134 if (b
->counter
== BF_FREE
) /* if it's empty */
135 read_ahead(file
, convert
); /* fill it */
136 *dpp
= &b
->buf
.tb_hdr
; /* set caller's ptr */
141 * fill the input buffer, doing ascii conversions if requested
142 * conversions are lf -> cr,lf and cr -> cr, nul
145 read_ahead(FILE *file
, int convert
)
153 b
= &bfs
[nextone
]; /* look at "next" buffer */
154 if (b
->counter
!= BF_FREE
) /* nop if not free */
156 nextone
= !nextone
; /* "incr" next buffer ptr */
161 b
->counter
= fread(dp
->th_data
, sizeof (char), blocksize
,
169 for (i
= 0; i
< blocksize
; i
++) {
171 if (prevchar
== '\n')
172 c
= '\n'; /* lf to cr,lf */
173 else c
= '\0'; /* cr to cr,nul */
178 if (c
== '\n' || c
== '\r') {
186 b
->counter
= (int)(p
- dp
->th_data
);
190 * Update count associated with the buffer, get new buffer
191 * from the queue. Calls write_behind only if next buffer not
195 writeit(FILE *file
, struct tftphdr
**dpp
, int ct
, int convert
)
197 bfs
[current
].counter
= ct
; /* set size of data to write */
198 current
= !current
; /* switch to other buffer */
199 if (bfs
[current
].counter
!= BF_FREE
) /* if not free */
200 if (write_behind(file
, convert
) < 0) /* flush it */
202 bfs
[current
].counter
= BF_ALLOC
; /* mark as alloc'd */
203 *dpp
= &bfs
[current
].buf
.tb_hdr
;
204 return (ct
); /* this is a lie of course */
208 * Output a buffer to a file, converting from netascii if requested.
209 * CR,NUL -> CR and CR,LF => LF.
210 * Note spec is undefined if we get CR as last byte of file or a
211 * CR followed by anything else. In this case we leave it alone.
214 write_behind(FILE *file
, int convert
)
220 int c
; /* current character */
225 if (b
->counter
< -1) /* anything to flush? */
226 return (0); /* just nop if nothing to do */
228 count
= b
->counter
; /* remember byte count */
229 b
->counter
= BF_FREE
; /* reset flag */
231 nextone
= !nextone
; /* incr for next time */
235 return (0); /* nak logic? */
243 written
= fwrite(buf
, sizeof (char), left
, file
);
245 /* Retry if we were interrupted by a signal. */
262 while (ct
--) { /* loop over the buffer */
263 c
= *p
++; /* pick up a character */
264 if (prevchar
== '\r') { /* if prev char was cr */
265 if (c
== '\n') { /* if have cr,lf then just */
266 /* smash lf on top of the cr */
267 if (fseek(file
, -1, SEEK_CUR
) < 0)
272 * If we have cr,nul then
273 * just skip over the putc.
279 /* else just fall through and allow it */
281 if (putc(c
, file
) == EOF
)
290 * When an error has occurred, it is possible that the two sides
291 * are out of synch. Ie: that what I think is the other side's
292 * response to packet N is really their response to packet N-1.
294 * So, to try to prevent that, we flush all the input queued up
295 * for us on the network connection on our host.
297 * We return the number of packets we flushed (mostly for reporting
298 * when trace is active) or -1 in case of an error.
308 pfd
.events
= POLLRDNORM
;
309 for (packets
= 0; ; packets
++) {
311 struct sockaddr_in6 from
;
314 if (poll(&pfd
, 1, 0) <= 0)
318 * A one byte buffer is enough because recvfrom() will
319 * discard the remaining data of the packet.
321 fromlen
= sizeof (from
);
322 if (recvfrom(socket
, &buf
, sizeof (buf
), 0,
323 (struct sockaddr
*)&from
, &fromlen
) < 0)
331 * Return a pointer to the next field in string s, or return NULL if no
332 * terminating NUL is found for the current field before end.
335 next_field(const char *s
, const char *end
)
338 s
= memchr(s
, 0, end
- s
);
340 return ((char *)s
+ 1);
346 * Print to stream options in the format option_name=option_value
349 print_options(FILE *stream
, char *opts
, int len
)
351 char *cp
, *optname
, *optval
;
352 char *endopts
= opts
+ len
;
356 * Ignore null padding, appended by broken TFTP clients to
357 * requests which don't include options.
360 while ((cp
< endopts
) && (*cp
== '\0'))
365 while (opts
< endopts
) {
367 if ((optval
= next_field(optname
, endopts
)) == NULL
) {
368 (void) putc('?', stream
);
374 (void) putc(' ', stream
);
375 (void) fputs(optname
, stream
);
376 if ((opts
= next_field(optval
, endopts
)) == NULL
) {
377 (void) putc('?', stream
);
380 (void) fprintf(stream
, "=%s", optval
);
385 * Turn off the alarm timer and ensure any pending SIGALRM signal is ignored.
391 (void) signal(SIGALRM
, SIG_IGN
);
392 (void) sigrelse(SIGALRM
);