tftp.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* 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 2002 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"
/*
* TFTP User Program -- Protocol Machines
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stddef.h>
#include <inttypes.h>
#include "tftpcommon.h"
#include "tftpprivate.h"
static char *blksize_str(void);
static char *timeout_str(void);
static char *tsize_str(void);
static int blksize_handler(char *);
static int timeout_handler(char *);
static int tsize_handler(char *);
static int add_options(char *, char *);
static int process_oack(tftpbuf *, int);
static void nak(int);
static void startclock(void);
static void stopclock(void);
static void printstats(char *, off_t);
static int makerequest(int, char *, struct tftphdr *, char *);
static struct options {
char *opt_name;
char *(*opt_str)(void);
int (*opt_handler)(char *);
} options[] = {
{ NULL }
};
static char optbuf[MAX_OPTVAL_LEN];
static int timeout;
static jmp_buf timeoutbuf;
/*ARGSUSED*/
static void
{
if (timeout >= maxtimeout) {
}
}
/*
* Send the requested file.
*/
void
{
struct sockaddr_in6 from;
int convert; /* true if doing nl->crlf conversion */
int errcode;
startclock(); /* start stat's clock */
if (tsize_set)
do {
if (block == 0) {
"tftp: Error: Write request packet too "
"big\n");
return;
}
size -= 4;
} else {
if (size < 0) {
break;
}
}
timeout = 0;
(void) setjmp(timeoutbuf);
if (trace)
if (n != size + 4) {
perror("tftp: sendto");
goto abort;
}
/* Can't read-ahead first block as OACK may change blocksize */
if (block != 0)
for (; ; ) {
do {
if (n < 0) {
perror("tftp: recvfrom");
goto abort;
}
if (trace)
/* should verify packet came from server */
goto abort;
}
if (errcode >= 0) {
(void) fputs("Rejected OACK\n",
stderr);
goto abort;
}
break;
}
break;
}
/*
* Never resend the current DATA packet on
* receipt of a duplicate ACK, doing so would
* cause the "Sorcerer's Apprentice Syndrome".
*/
}
}
cancel_alarm();
if (block > 0)
block++;
cancel_alarm();
stopclock();
if (amount > 0)
}
/*
* Receive a file.
*/
void
{
unsigned long amount = 0;
struct sockaddr_in6 from;
int convert; /* true if converting crlf -> lf */
int errcode;
startclock();
if (tsize_set)
tsize = 0;
"tftp: Error: Read request packet too big\n");
return;
}
do {
if (firsttrip) {
} else {
size = 4;
block++;
}
timeout = 0;
(void) setjmp(timeoutbuf);
if (trace)
(void) alarm(0);
perror("tftp: sendto");
goto abort;
}
goto abort;
}
for (; ; ) {
do {
if (n < 0) {
perror("tftp: recvfrom");
goto abort;
}
if (trace)
/* should verify client address */
goto abort;
}
if (errcode >= 0) {
cancel_alarm();
(void) fputs("Rejected OACK\n",
stderr);
return;
}
size = 4;
goto send_oack_ack;
}
int j;
break; /* have next packet */
}
/*
* On an error, try to synchronize
* both sides.
*/
j = synchnet(f);
if (j < 0) {
perror("tftp: recvfrom");
goto abort;
}
if ((j > 0) && trace) {
(void) printf("discarded %d packets\n",
j);
}
goto send_ack; /* resend ack */
}
}
}
cancel_alarm();
if (size < 0) {
goto abort;
}
cancel_alarm();
goto abort;
}
if (n == EOF) {
goto abort;
}
/* ok to ack, since user has seen err msg */
if (trace)
perror("tftp: sendto");
cancel_alarm();
stopclock();
if (amount > 0)
}
static int
{
int len;
/* Maximum size of a request packet is 512 bytes (RFC 2347) */
return (-1);
return (-1);
if (len == -1)
return (-1);
}
/*
* Return the blksize option value string to include in the request packet.
*/
static char *
blksize_str(void)
{
if (blksize == 0)
return (NULL);
return (optbuf);
}
/*
* Return the timeout option value string to include in the request packet.
*/
static char *
timeout_str(void)
{
if (srexmtval == 0)
return (NULL);
return (optbuf);
}
/*
* Return the tsize option value string to include in the request packet.
*/
static char *
tsize_str(void)
{
return (NULL);
return (optbuf);
}
/*
* Validate and action the blksize option value string from the OACK packet.
* Returns -1 on success or an error code on failure.
*/
static int
blksize_handler(char *optstr)
{
char *endp;
int value;
/* Make sure the option was requested */
if (blksize == 0)
return (EOPTNEG);
errno = 0;
*endp != '\0')
return (EOPTNEG);
return (-1);
}
/*
* Validate and action the timeout option value string from the OACK packet.
* Returns -1 on success or an error code on failure.
*/
static int
timeout_handler(char *optstr)
{
char *endp;
int value;
/* Make sure the option was requested */
if (srexmtval == 0)
return (EOPTNEG);
errno = 0;
return (EOPTNEG);
/*
* Nothing to set, client and server retransmission intervals are
* set separately in the client.
*/
return (-1);
}
/*
* Validate and action the tsize option value string from the OACK packet.
* Returns -1 on success or an error code on failure.
*/
static int
tsize_handler(char *optstr)
{
char *endp;
/* Make sure the option was requested */
return (EOPTNEG);
errno = 0;
return (EOPTNEG);
#if _FILE_OFFSET_BITS == 32
return (ENOSPACE);
#endif
/*
* Don't bother checking the tsize value we specified in a write
* request is echoed back in the OACK.
*/
if (tsize == 0)
return (-1);
}
/*
* Add TFTP options to a request packet.
*/
static int
{
int i;
+ 1;
return (-1);
return (-1);
}
}
}
/*
* Process OACK packet sent by server in response to options in the request
* packet. Returns -1 on success or an error code on failure.
*/
static int
{
int i, errcode;
return (EOPTNEG);
return (EOPTNEG);
break;
}
return (EOPTNEG);
if (errcode >= 0)
return (errcode);
}
return (-1);
}
/*
* Send a nak packet (error message).
* Error code passed in is one of the
* standard TFTP codes, or a UNIX errno
* offset by 100.
*/
static void
{
int length;
break;
}
if (trace)
perror("nak");
}
static void
{
static char *opcodes[] = \
{ "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" };
char *tpend;
else
switch (op) {
case RRQ:
case WRQ:
break;
}
break;
}
if (n > 0) {
(void) printf(", options: ");
}
(void) puts(">");
break;
case DATA:
break;
case ACK:
break;
case OACK:
(void) printf("<options: ");
(void) puts(">");
break;
case ERROR:
if (n > 0)
(void) puts(">");
break;
}
}
static void
startclock(void)
{
}
static void
stopclock(void)
{
}
static void
{
if (verbose)
else
(void) putchar('\n');
}