⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tftp.c

📁 开放源码的编译器open watcom 1.6.0版的源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/*
 * 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 + -