/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* University Copyright- Copyright (c) 1982, 1986, 1988
* The Regents of the University of California
* All Rights Reserved
*
* University Acknowledgment- Portions of this document are derived from
* software developed by the University of California, Berkeley, and its
* contributors.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Simple minded read-ahead/write-behind subroutines for tftp user and
* server. Written originally with multiple buffers in mind, but current
* implementation has two buffer logic wired in.
*
* Todo: add some sort of final error check so when the write-buffer
* is finally flushed, the caller can detect if the disk filled up
* (or had an i/o error) and return a nak to the other side.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <poll.h>
#include <string.h>
#include "tftpcommon.h"
{ EUNDEF, "Undefined error code" },
{ ENOTFOUND, "File not found" },
{ EACCESS, "Access violation" },
{ ENOSPACE, "Disk full or allocation exceeded" },
{ EBADOP, "Illegal TFTP operation" },
{ EBADID, "Unknown transfer ID" },
{ EEXISTS, "File already exists" },
{ ENOUSER, "No such user" },
{ EOPTNEG, "Option negotiation error" },
{ -1, NULL }
};
static struct bf {
extern int blocksize; /* Number of data bytes in a DATA packet */
/* Values for bf.counter */
/* [-1 .. blocksize] = size of data in the data buffer */
/* control flags for crlf conversions */
/*
* Init for either read-ahead or write-behind.
* x is zero for write-behind, one for read-head.
*/
static struct tftphdr *
rw_init(int x)
{
newline = 0; /* init crlf flag */
prevchar = -1;
current = 0;
nextone = x; /* ahead or behind? */
}
/*
* Have emptied current buffer by sending to net and getting ack.
* Free it and return next buffer filled with data.
*/
int
{
struct bf *b;
return (b->counter);
}
/*
* fill the input buffer, doing ascii conversions if requested
* conversions are lf -> cr,lf and cr -> cr, nul
*/
void
{
int i;
char *p;
int c;
struct bf *b;
return;
if (!convert) {
file);
b->counter = -1;
return;
}
for (i = 0; i < blocksize; i++) {
if (newline) {
if (prevchar == '\n')
c = '\n'; /* lf to cr,lf */
else c = '\0'; /* cr to cr,nul */
newline = 0;
} else {
if (c == EOF) break;
if (c == '\n' || c == '\r') {
prevchar = c;
c = '\r';
newline = 1;
}
}
*p++ = c;
}
}
/*
* Update count associated with the buffer, get new buffer
* from the queue. Calls write_behind only if next buffer not
* available.
*/
int
{
ct = -1;
return (ct); /* this is a lie of course */
}
/*
* Output a buffer to a file, converting from netascii if requested.
* CR,NUL -> CR and CR,LF => LF.
* Note spec is undefined if we get CR as last byte of file or a
* CR followed by anything else. In this case we leave it alone.
*/
int
{
char *buf;
int count;
int ct;
char *p;
int c; /* current character */
struct bf *b;
return (0); /* just nop if nothing to do */
if (count <= 0)
return (0); /* nak logic? */
if (!convert) {
while (left > 0) {
/* Retry if we were interrupted by a signal. */
continue;
return (-1);
}
if (written == 0)
return (-1);
}
return (count);
}
p = buf;
while (ct--) { /* loop over the buffer */
c = *p++; /* pick up a character */
if (c == '\n') { /* if have cr,lf then just */
/* smash lf on top of the cr */
return (-1);
} else {
if (c == '\0') {
/*
* If we have cr,nul then
* just skip over the putc.
*/
prevchar = 0;
continue;
}
}
/* else just fall through and allow it */
}
return (-1);
prevchar = c;
}
return (count);
}
/*
* When an error has occurred, it is possible that the two sides
* are out of synch. Ie: that what I think is the other side's
* response to packet N is really their response to packet N-1.
*
* So, to try to prevent that, we flush all the input queued up
* for us on the network connection on our host.
*
* We return the number of packets we flushed (mostly for reporting
* when trace is active) or -1 in case of an error.
*/
int
{
int packets;
char buf;
break;
/*
* A one byte buffer is enough because recvfrom() will
* discard the remaining data of the packet.
*/
return (-1);
}
return (packets);
}
/*
* Return a pointer to the next field in string s, or return NULL if no
* terminating NUL is found for the current field before end.
*/
char *
{
if (s < end) {
if (s != NULL)
return ((char *)s + 1);
}
return (NULL);
}
/*
* Print to stream options in the format option_name=option_value
*/
void
{
/*
* Ignore null padding, appended by broken TFTP clients to
* requests which don't include options.
*/
cp++;
return;
return;
}
if (first)
first = 0;
else
return;
}
}
}
/*
* Turn off the alarm timer and ensure any pending SIGALRM signal is ignored.
*/
void
cancel_alarm(void)
{
(void) alarm(0);
}