📄 tftp.c
字号:
/*
* Boot-ROM-Code to load (any file or) an operating system across
* a TCP/IP network.
*
* Module: tftp.c
* Purpose: Get a file with TFTP protocol
* Entries: tftp_boot_load, tftp_set_server, tftp_set_boot_fname
*
**************************************************************************
*
* Copyright (C) 1995,1996,1997 Gero Kuhlmann <gero@gkminix.han.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Major changes for Watt-32 by G. Vanem <giva@bgnett.no> 1999
*
* This client adheres to RFC-1350 (TFTP v2), but supports only
* reading from a remote host.
*
* !!to-do: Support ETFTP (RFC-1986)
*/
#include "socket.h"
#include <netdb.h>
#include <arpa/tftp.h>
#include "udp_dom.h"
#include "tftp.h"
#if defined(USE_TFTP)
#if defined(USE_DEBUG)
#define TRACE(x) (*_printf) x
#else
#define TRACE(x) ((void)0)
#endif
/*
* Error codes private to this file:
*/
#define ERR_INV 1 /* invalid packet size */
#define ERR_ERR 2 /* error packet received */
#define ERR_OP 3 /* invalid opcode */
#define ERR_BLOCK 4 /* invalid block number */
#define ERR_TIMEOUT 5 /* timeout while receiving */
#define ERR_UNKNOWN 6 /* unknown error */
/*
* Various definitions:
*/
#define TFTP_RETRY 5 /* Maximum number of retries */
#define TFTP_TIMEOUT 8 /* 8 seconds timeout */
#define TFTP_HEADSIZE 4 /* th_opcode/th_block size */
#define TFTP_PORT_LOW 1024 /* lowest legal local port */
#define OCTET_STR "octet" /* name for 8-bit raw format */
#define NETASCII_STR "netascii" /* name for netascii format */
#define MAIL_STR "mail" /* name for mail format */
/*
* Public data
*/
int (*tftp_writer) (const void*, size_t) = NULL;
int (*tftp_terminator) (void) = NULL;
extern const char *icmp_type_str [ICMP_MAXTYPE+1];
/*
* Local variables
*/
static struct tftphdr *inbuf; /* TFTP input buffer */
static struct tftphdr *outbuf; /* TFTP output buffer */
static sock_type *sock; /* Socket for UDP recv/xmit */
static int currblock; /* Current data block */
static int blocksize; /* Server's block size */
static int ibuflen; /* Size of data in input buffer */
static int isopen; /* TRUE if connection is open */
static int tftp_errno = 0;
static DWORD tftp_server = 0;
static int tftp_timeout = TFTP_TIMEOUT;
static int tftp_retry = TFTP_RETRY;
static int tftp_lport = 0;
static char tftp_server_name[MAX_HOSTLEN] = "";
static char tftp_boot_fname [MAX_PATHLEN] = "";
static char tftp_xfer_mode [MAX_STRING] = OCTET_STR;
/*
* Send a tftp request packet
*/
static void send_req (char request, const char *fname)
{
char *cp, *mode;
int len;
int fnamlen = strlen (fname);
/* The output buffer is setup with the request code, the file name,
* and the name of the data format.
*/
memset (outbuf, 0, sizeof(*outbuf));
outbuf->th_opcode = intel16 (request);
len = SEGSIZE - sizeof(outbuf->th_opcode) - strlen(tftp_xfer_mode) - 1;
cp = (char*)&outbuf->th_stuff;
for ( ; *fname && len > 0 && fnamlen > 0; len--, fnamlen--)
*cp++ = *fname++;
*cp++ = '\0';
for (mode = tftp_xfer_mode; *mode; )
*cp++ = *mode++;
*cp++ = '\0';
/* Finally send the request
*/
len = (int) (cp - (char*)outbuf);
sock_fastwrite (sock, (BYTE*)outbuf, len);
}
/*
* Send a tftp acknowledge packet
*/
static void send_ack (int block)
{
struct tftphdr ack;
ack.th_opcode = intel16 (ACK);
ack.th_block = intel16 ((WORD)block);
sock_fastwrite (sock, (BYTE*)&ack, TFTP_HEADSIZE);
}
#if defined(USE_DEBUG)
/*
* Return error string for 'th_code'
*/
static const char *tftp_strerror (int code)
{
static const char *err_tab[] = {
"EUNDEF",
"ENOTFOUND",
"EACCESS",
"ENOSPACE",
"EBADOP",
"EBADID",
"EEXISTS",
"ENOUSER"
};
if (code < 0 || code > ENOUSER)
return ("?");
return (err_tab[code]);
}
#endif
/*
* Watch out for "ICMP port unreachable".
*/
static void udp_callback (udp_Socket *s, int icmp_type)
{
if (s == (udp_Socket*)sock && s->ip_type == UDP_PROTO &&
icmp_type == ICMP_UNREACH)
{
/* In lack of a better way, pretend we got a FIN.
* This causes sock_wait_input() below to break it's loop.
*/
s->locflags |= LF_GOT_FIN;
s->err_msg = icmp_type_str [ICMP_UNREACH];
}
}
/*
* Receive a TFTP data packet
*/
static int recv_packet (int block)
{
int len, status = 0;
/* Use a callback since first block sent might cause a "ICMP
* port unreachable" to be sent back. Note that the normal mechanism
* of detecting ICMP errors (through _udp_cancel) doesn't work since
* we did set 'sock->udp.hisaddr = 0'.
*/
if (block == 1)
sock->udp.sol_callb = (sol_upcall) udp_callback;
else sock->udp.sol_callb = NULL;
/* Read packet with timeout
*/
sock_wait_input (sock, tftp_timeout, NULL, &status);
len = sock_fastread (sock, (BYTE*)inbuf, TFTP_HEADSIZE+SEGSIZE);
/* Check that the packet has a correct length
*/
len -= TFTP_HEADSIZE;
if (len < 0 || len > SEGSIZE)
{
TRACE (("tftp: Invalid packet, len = %d\r\n"));
tftp_errno = ERR_INV;
return (-1);
}
/* Check if we got an error packet
*/
if (intel16(inbuf->th_opcode) == ERROR)
{
#if defined(USE_DEBUG)
int code = intel16 (inbuf->th_code);
const char *str = tftp_strerror (code);
TRACE (("tftp: Error: %s (%d): %.*s\r\n",
str, code, SEGSIZE, inbuf->th_data));
#endif
tftp_errno = ERR_ERR;
return (-1);
}
/* Check if we got a valid data packet at all
*/
if (intel16(inbuf->th_opcode) != DATA)
{
TRACE (("tftp: Invalid opcode %d\r\n", intel16(inbuf->th_opcode)));
tftp_errno = ERR_OP;
return (-1);
}
/* Check if the block number of the data packet is correct
*/
if (intel16(inbuf->th_block) != block)
{
TRACE (("tftp: Block %d != %d\r\n", intel16(inbuf->th_block), block));
tftp_errno = ERR_BLOCK;
return (-1);
}
tftp_errno = 0;
return (len);
sock_err:
if (status == 0)
{
TRACE (("tftp: Timeout\n"));
tftp_errno = ERR_TIMEOUT;
return (-1);
}
/* most likely "Port unreachable"
*/
TRACE (("tftp: %s\n", sockerr(&sock->tcp)));
tftp_errno = ERR_UNKNOWN;
return (-1);
}
/*
* Open a TFTP connection on a random local port (our transaction ID).
* Send the request, wait for first data block and send the first ACK.
*/
static int tftp_open (DWORD server, const char *fname)
{
int retry;
WORD port = 69;
#if defined(USE_BSD_FUNC)
struct servent *sp = getservbyname ("tftp", "udp");
if (sp)
port = intel16 ((WORD)sp->s_port);
#endif
currblock = 0;
blocksize = 0;
for (retry = 0; retry < tftp_retry; retry++)
{
WORD our_tid; /* our transaction ID (local port) */
if (tftp_lport && tftp_lport < TFTP_PORT_LOW)
outsnl (_LANG("tftp: Illegal local port."));
if (tftp_lport >= TFTP_PORT_LOW)
our_tid = tftp_lport;
else our_tid = Random (TFTP_PORT_LOW, USHRT_MAX);
/* Try to open a TFTP connection to the server
*/
if (!udp_open(&sock->udp, our_tid, server, port, NULL))
{
STAT (ipstats.ips_noroute++);
TRACE (("tftp: ARP timeout\r\n"));
return (0);
}
/* Send the file request block, and then wait for the first data
* block. If there is no response to the query, retry it with
* another transaction ID (local port), so that all old packets get
* discarded automatically.
*/
send_req (RRQ, fname);
/* This hack makes it work because the response is sent back on
* a source-port different from port (69); i.e. the server TID
* uses a random port. Force the response packet to match a passive
* socket in udp_handler().
*/
sock->udp.hisaddr = 0;
ibuflen = recv_packet (1);
if (ibuflen >= 0)
{
blocksize = ibuflen;
TRACE (("tftp: blocksize = %d\n", blocksize));
isopen = TRUE;
send_ack (1);
return (1);
}
/* If an error (except timeout) occurred, retries are useless
*/
if (tftp_errno == ERR_ERR || tftp_errno == ERR_UNKNOWN)
break;
}
return (0);
}
/*
* Close the TFTP connection
*/
static void tftp_close (void)
{
if (tftp_terminator)
(*tftp_terminator)();
if (debug_on)
outs ("\n");
if (sock)
{
sock_close (sock);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -