Ejemplo n.º 1
0
int tftpget(const char *remote, const char *local, in_addr_t addr, bool binary)
{
    struct sockaddr_in server;  /* The address of the TFTP server */
    struct sockaddr_in from;    /* The address the last UDP message recv'd from */
    uint8_t *packet;            /* Allocated memory to hold one packet */
    uint16_t blockno = 0;       /* The current transfer block number */
    uint16_t opcode;            /* Received opcode */
    uint16_t rblockno;          /* Received block number */
    int len;                    /* Generic length */
    int sd;                     /* Socket descriptor for socket I/O */
    int fd;                     /* File descriptor for file I/O */
    int retry;                  /* Retry counter */
    int nbytesrecvd = 0;        /* The number of bytes received in the packet */
    int ndatabytes;             /* The number of data bytes received */
    int result = ERROR;         /* Assume failure */
    int ret;                    /* Generic return status */

    /* Allocate the buffer to used for socket/disk I/O */

    packet = (uint8_t*)zalloc(TFTP_IOBUFSIZE);
    if (!packet)
    {
        ndbg("packet memory allocation failure\n");
        set_errno(ENOMEM);
        goto errout;
    }

    /* Open the file for writing */

    fd = open(local, O_WRONLY|O_CREAT|O_TRUNC, 0666);
    if (fd < 0)
    {
        ndbg("open failed: %d\n", errno);
        goto errout_with_packet;
    }

    /* Initialize a UDP socket and setup the server addresss */

    sd = tftp_sockinit(&server, addr);
    if (sd < 0)
    {
        goto errout_with_fd;
    }

    /* Then enter the transfer loop.  Loop until the entire file has
     * been received or until an error occurs.
     */

    do
    {
        /* Increment the TFTP block number for the next transfer */

        blockno++;

        /* Send the next block if the file within a loop.  We will
         * retry up to TFTP_RETRIES times before giving up on the
         * transfer.
         */

        for (retry = 0; retry < TFTP_RETRIES; retry++)
        {
            /* Send the read request using the well-known port number before
             * receiving the first block.  Each retry of the first block will
             * re-send the request.
             */

            if (blockno == 1)
            {
                len             = tftp_mkreqpacket(packet, TFTP_RRQ, remote, binary);
                server.sin_port = HTONS(CONFIG_NETUTILS_TFTP_PORT);
                ret             = tftp_sendto(sd, packet, len, &server);
                if (ret != len)
                {
                    goto errout_with_sd;
                }

                /* Subsequent sendto will use the port number selected by the TFTP
                 * server in the DATA packet.  Setting the server port to zero
                 * here indicates that we have not yet received the server port number.
                 */

                server.sin_port = 0;
            }

            /* Get the next packet from the server */

            nbytesrecvd = tftp_recvfrom(sd, packet, TFTP_IOBUFSIZE, &from);

            /* Check if anything valid was received */

            if (nbytesrecvd >= 0)
            {
                /* Verify the sender address and port number */

                if (server.sin_addr.s_addr != from.sin_addr.s_addr)
                {
                    nvdbg("Invalid address in DATA\n");
                    retry--;
                    continue;
                }

                if (server.sin_port && server.sin_port != from.sin_port)
                {
                    nvdbg("Invalid port in DATA\n");
                    len = tftp_mkerrpacket(packet, TFTP_ERR_UNKID, TFTP_ERRST_UNKID);
                    ret = tftp_sendto(sd, packet, len, &from);
                    retry--;
                    continue;
                }

                /* Parse the incoming DATA packet */

                if (nbytesrecvd < TFTP_DATAHEADERSIZE)
                {
                    /* Packet is not big enough to be parsed */

                    nvdbg("Tiny data packet ignored\n");
                    continue;
                }

                if (tftp_parsedatapacket(packet, &opcode, &rblockno) != OK ||
                        blockno != rblockno)
                {
                    /* Opcode is not TFTP_DATA or the block number is unexpected */

                    nvdbg("Parse failure\n");
                    if (opcode > TFTP_MAXRFC1350)
                    {
                        len = tftp_mkerrpacket(packet, TFTP_ERR_ILLEGALOP, TFTP_ERRST_ILLEGALOP);
                        ret = tftp_sendto(sd, packet, len, &from);
                    }
                    continue;
                }

                /* Replace the server port to the one in the good data response */

                if (!server.sin_port)
                {
                    server.sin_port = from.sin_port;
                }

                /* Then break out of the loop */

                break;
            }
        }

        /* Did we exhaust all of the retries? */

        if (retry == TFTP_RETRIES)
        {
            nvdbg("Retry limit exceeded\n");
            goto errout_with_sd;
        }

        /* Write the received data chunk to the file */

        ndatabytes = nbytesrecvd - TFTP_DATAHEADERSIZE;
        tftp_dumpbuffer("Recvd DATA", packet + TFTP_DATAHEADERSIZE, ndatabytes);
        if (tftp_write(fd, packet + TFTP_DATAHEADERSIZE, ndatabytes) < 0)
        {
            goto errout_with_sd;
        }

        /* Send the acknowledgment */

        len = tftp_mkackpacket(packet, blockno);
        ret = tftp_sendto(sd, packet, len, &server);
        if (ret != len)
        {
            goto errout_with_sd;
        }
        nvdbg("ACK blockno %d\n", blockno);
    }
    while (ndatabytes >= TFTP_DATASIZE);

    /* Return success */

    result = OK;

errout_with_sd:
    close(sd);
errout_with_fd:
    close(fd);
errout_with_packet:
    free(packet);
errout:
    return result;
}
Ejemplo n.º 2
0
static int tftp_rcvack(int sd, uint8_t *packet, struct sockaddr_in *server,
                       uint16_t *port, uint16_t *blockno)
{
  struct sockaddr_in from;     /* The address the last UDP message recv'd from */
  ssize_t nbytes;              /* The number of bytes received. */
  uint16_t opcode;             /* The received opcode */
  uint16_t rblockno;           /* The received block number */
  int packetlen;               /* Packet length */
  int retry;                   /* Retry counter */

  /* Try up to TFTP_RETRIES times */

  for (retry = 0; retry < TFTP_RETRIES; retry++)
    {
      /* Try for until a valid ACK is received or some error occurs */

      for (;;)
        {
          /* Receive the next UDP packet from the server */

          nbytes = tftp_recvfrom(sd, packet, TFTP_IOBUFSIZE, &from);
          if (nbytes < TFTP_ACKHEADERSIZE)
            {
              /* Failed to receive a good packet */

              if (nbytes == 0)
                {
                  ndbg("Connection lost: %d bytes\n", nbytes);
                }
              else if (nbytes > 0)
                {
                  ndbg("Short packet: %d bytes\n", nbytes);
                }
              else
                {
                  ndbg("Recveid failure\n");
                }

              /* Break out to bump up the retry count */

              break;
            }
          else
            {
               /* Get the port being used by the server if that has not yet been established */

               if (!*port)
                 {
                   *port            = from.sin_port;
                   server->sin_port = from.sin_port;
                 }

               /* Verify that the packet was received from the correct host and port */

               if (server->sin_addr.s_addr != from.sin_addr.s_addr)
                 {
                   nvdbg("Invalid address in DATA\n");
                   continue;
                 }

              if (*port != server->sin_port)
                {
                  nvdbg("Invalid port in DATA\n");
                  packetlen = tftp_mkerrpacket(packet, TFTP_ERR_UNKID, TFTP_ERRST_UNKID);
                  (void)tftp_sendto(sd, packet, packetlen, server);
                  continue;
                }

              /* Parse the error message */

               opcode   = (uint16_t)packet[0] << 8 | (uint16_t)packet[1];
               rblockno = (uint16_t)packet[2] << 8 | (uint16_t)packet[3];

              /* Verify that the message that we received is an ACK for the
               * expected block number.
               */

               if (opcode != TFTP_ACK)
                 {
                   nvdbg("Bad opcode\n");
#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_NET)
                  if (opcode == TFTP_ERR)
                    {
                      (void)tftp_parseerrpacket(packet);
                    }
                  else
#endif
                  if (opcode > TFTP_MAXRFC1350)
                    {
                      packetlen = tftp_mkerrpacket(packet, TFTP_ERR_ILLEGALOP, TFTP_ERRST_ILLEGALOP);
                      (void)tftp_sendto(sd, packet, packetlen, server);
                    }

                  /* Break out an bump up the retry count */

                  break;
                }

              /* Success! */

              nvdbg("Received ACK for block %d\n", rblockno);
              *blockno = rblockno;
              return OK;
            }
        }
    }

  /* We have tried TFTP_RETRIES times */

  ndbg("Timeout, Waiting for ACK\n");
  return ERROR; /* Will never get here */
}