Skip to content
Snippets Groups Projects
tftp.c 40.4 KiB
Newer Older
  • Learn to ignore specific revisions
  •   state->blksize = TFTP_BLKSIZE_DEFAULT;
      state->requested_blksize = blksize;
    
      ((struct sockaddr *)&state->local_addr)->sa_family =
    
    Yang Tse's avatar
    Yang Tse committed
        (unsigned short)(conn->ip_addr->ai_family);
    
      if(!conn->bits.bound) {
        /* If not already bound, bind to any interface, random UDP port. If it is
         * reused or a custom local port was desired, this has already been done!
    
         * We once used the size of the local_addr struct as the third argument
         * for bind() to better work with IPv6 or whatever size the struct could
         * have, but we learned that at least Tru64, AIX and IRIX *requires* the
         * size of that argument to match the exact size of a 'sockaddr_in' struct
         * when running IPv4-only.
    
         *
         * Therefore we use the size from the address we connected to, which we
         * assume uses the same IP version and thus hopefully this works for both
         * IPv4 and IPv6...
         */
        rc = bind(state->sockfd, (struct sockaddr *)&state->local_addr,
                  conn->ip_addr->ai_addrlen);
        if(rc) {
    
    }
    
    /**********************************************************
     *
    
     *
     * The done callback
     *
     **********************************************************/
    
    static CURLcode tftp_done(struct connectdata *conn, CURLcode status,
    
                              bool premature)
    
      CURLcode result = CURLE_OK;
    
      tftp_state_data_t *state = (tftp_state_data_t *)conn->proto.tftpc;
    
    
      if(Curl_pgrsDone(conn))
        return CURLE_ABORTED_BY_CALLBACK;
    
      if(state)
    
        result = tftp_translate_code(state->error);
    
    
    /**********************************************************
     *
    
     *
     **********************************************************/
    
    static int tftp_getsock(struct connectdata *conn, curl_socket_t *socks,
    
                            int numsocks)
    
      socks[0] = conn->sock[FIRSTSOCKET];
    
      return GETSOCK_READSOCK(0);
    }
    
    /**********************************************************
     *
     * tftp_receive_packet
     *
     * Called once select fires and data is ready on the socket
     *
     **********************************************************/
    static CURLcode tftp_receive_packet(struct connectdata *conn)
    
    Yang Tse's avatar
     
    Yang Tse committed
      curl_socklen_t        fromlen;
    
      struct Curl_easy  *data = conn->data;
    
      tftp_state_data_t     *state = (tftp_state_data_t *)conn->proto.tftpc;
      struct SingleRequest  *k = &data->req;
    
      /* Receive the packet */
      fromlen = sizeof(fromaddr);
      state->rbytes = (int)recvfrom(state->sockfd,
    
                                    (void *)state->rpacket.data,
                                    state->blksize+4,
                                    0,
                                    (struct sockaddr *)&fromaddr,
                                    &fromlen);
    
      if(state->remote_addrlen==0) {
        memcpy(&state->remote_addr, &fromaddr, fromlen);
        state->remote_addrlen = fromlen;
      }
    
      /* Sanity check packet length */
      if(state->rbytes < 4) {
        failf(data, "Received too short packet");
        /* Not a timeout, but how best to handle it? */
        state->event = TFTP_EVENT_TIMEOUT;
      }
      else {
        /* The event is given by the TFTP packet time */
        state->event = (tftp_event_t)getrpacketevent(&state->rpacket);
    
        switch(state->event) {
        case TFTP_EVENT_DATA:
          /* Don't pass to the client empty or retransmitted packets */
          if(state->rbytes > 4 &&
    
             (NEXT_BLOCKNUM(state->block) == getrpacketblock(&state->rpacket))) {
    
            result = Curl_client_write(conn, CLIENTWRITE_BODY,
    
                                       (char *)state->rpacket.data+4,
                                       state->rbytes-4);
    
            if(result) {
              tftp_state_machine(state, TFTP_EVENT_ERROR);
              return result;
            }
            k->bytecount += state->rbytes-4;
            Curl_pgrsSetDownloadCounter(data, (curl_off_t) k->bytecount);
          }
          break;
        case TFTP_EVENT_ERROR:
          state->error = (tftp_error_t)getrpacketblock(&state->rpacket);
          infof(data, "%s\n", (const char *)state->rpacket.data+4);
          break;
        case TFTP_EVENT_ACK:
          break;
        case TFTP_EVENT_OACK:
          result = tftp_parse_option_ack(state,
    
                                         (const char *)state->rpacket.data+2,
                                         state->rbytes-2);
    
          if(result)
            return result;
          break;
        case TFTP_EVENT_RRQ:
        case TFTP_EVENT_WRQ:
        default:
          failf(data, "%s", "Internal error: Unexpected packet");
          break;
        }
    
        /* Update the progress meter */
        if(Curl_pgrsUpdate(conn)) {
          tftp_state_machine(state, TFTP_EVENT_ERROR);
          return CURLE_ABORTED_BY_CALLBACK;
        }
      }
      return result;
    }
    
    /**********************************************************
     *
     * tftp_state_timeout
     *
     * Check if timeouts have been reached
     *
     **********************************************************/
    static long tftp_state_timeout(struct connectdata *conn, tftp_event_t *event)
    {
      time_t                current;
      tftp_state_data_t     *state = (tftp_state_data_t *)conn->proto.tftpc;
    
    
        *event = TFTP_EVENT_NONE;
    
      time(&current);
      if(current > state->max_time) {
    
    Yang Tse's avatar
     
    Yang Tse committed
        DEBUGF(infof(conn->data, "timeout: %ld > %ld\n",
                     (long)current, (long)state->max_time));
    
        state->error = TFTP_ERR_TIMEOUT;
        state->state = TFTP_STATE_FIN;
    
        return 0;
    
      else if(current > state->rx_time+state->retry_time) {
        if(event)
    
          *event = TFTP_EVENT_TIMEOUT;
        time(&state->rx_time); /* update even though we received nothing */
      }
    
    
      /* there's a typecast below here since 'time_t' may in fact be larger than
         'long', but we estimate that a 'long' will still be able to hold number
         of seconds even if "only" 32 bit */
    
      return (long)(state->max_time - current);
    
    }
    
    /**********************************************************
     *
     * tftp_multi_statemach
     *
     * Handle single RX socket event and return
     *
     **********************************************************/
    static CURLcode tftp_multi_statemach(struct connectdata *conn, bool *done)
    {
      int                   rc;
      tftp_event_t          event;
      CURLcode              result = CURLE_OK;
    
      struct Curl_easy  *data = conn->data;
    
      tftp_state_data_t     *state = (tftp_state_data_t *)conn->proto.tftpc;
      long                  timeout_ms = tftp_state_timeout(conn, &event);
    
      *done = FALSE;
    
      if(timeout_ms <= 0) {
        failf(data, "TFTP response timeout");
        return CURLE_OPERATION_TIMEDOUT;
      }
    
      else if(event != TFTP_EVENT_NONE) {
    
        result = tftp_state_machine(state, event);
    
        *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE;
    
          Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
    
      }
      else {
        /* no timeouts to handle, check our socket */
        rc = Curl_socket_ready(state->sockfd, CURL_SOCKET_BAD, 0);
    
        if(rc == -1) {
          /* bail out */
          int error = SOCKERRNO;
          failf(data, "%s", Curl_strerror(conn, error));
          state->event = TFTP_EVENT_ERROR;
    
        else if(rc != 0) {
          result = tftp_receive_packet(conn);
    
          result = tftp_state_machine(state, state->event);
    
          *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE;
    
            Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
    
    
      return result;
    }
    
    /**********************************************************
     *
     * tftp_doing
     *
    
     * Called from multi.c while DOing
    
     *
     **********************************************************/
    static CURLcode tftp_doing(struct connectdata *conn, bool *dophase_done)
    {
      CURLcode result;
      result = tftp_multi_statemach(conn, dophase_done);
    
      if(*dophase_done) {
        DEBUGF(infof(conn->data, "DO phase is complete\n"));
      }
    
        /* The multi code doesn't have this logic for the DOING state so we
           provide it for TFTP since it may do the entire transfer in this
           state. */
        if(Curl_pgrsUpdate(conn))
          result = CURLE_ABORTED_BY_CALLBACK;
        else
          result = Curl_speedcheck(conn->data, Curl_tvnow());
      }
    
      return result;
    }
    
    /**********************************************************
     *
     * tftp_peform
     *
     * Entry point for transfer from tftp_do, sarts state mach
     *
     **********************************************************/
    static CURLcode tftp_perform(struct connectdata *conn, bool *dophase_done)
    {
      CURLcode              result = CURLE_OK;
      tftp_state_data_t     *state = (tftp_state_data_t *)conn->proto.tftpc;
    
      *dophase_done = FALSE;
    
      result = tftp_state_machine(state, TFTP_EVENT_INIT);
    
    
      if((state->state == TFTP_STATE_FIN) || result)
        return result;
    
      tftp_multi_statemach(conn, dophase_done);
    
    
      if(*dophase_done)
        DEBUGF(infof(conn->data, "DO phase is complete\n"));
    
      return result;
    }
    
    
    /**********************************************************
     *
     * tftp_do
     *
     * The do callback
     *
     * This callback initiates the TFTP transfer
     *
     **********************************************************/
    
    static CURLcode tftp_do(struct connectdata *conn, bool *done)
    {
    
      tftp_state_data_t *state;
      CURLcode result;
    
        result = tftp_connect(conn, done);
        if(result)
          return result;
    
      state = (tftp_state_data_t *)conn->proto.tftpc;
    
      if(!state)
        return CURLE_BAD_CALLING_ORDER;
    
      result = tftp_perform(conn, done);
    
      /* If tftp_perform() returned an error, use that for return code. If it
         was OK, see if tftp_translate_code() has an error. */
    
        /* If we have encountered an internal tftp error, translate it. */
    
        result = tftp_translate_code(state->error);
    
    static CURLcode tftp_setup_connection(struct connectdata * conn)
    
      struct Curl_easy *data = conn->data;
    
      char * type;
      char command;
    
      conn->socktype = SOCK_DGRAM;   /* UDP datagram based */
    
      /* TFTP URLs support an extension like ";mode=<typecode>" that
       * we'll try to get now! */
    
      type = strstr(data->state.path, ";mode=");
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      if(!type)
    
        type = strstr(conn->host.rawalloc, ";mode=");
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      if(type) {
    
        *type = 0;                   /* it was in the middle of the hostname */
    
        command = Curl_raw_toupper(type[6]);
    
    
        switch (command) {
        case 'A': /* ASCII mode */
        case 'N': /* NETASCII mode */
          data->set.prefer_ascii = TRUE;
          break;
    
        case 'O': /* octet mode */
        case 'I': /* binary mode */
        default:
          /* switch off ASCII */
          data->set.prefer_ascii = FALSE;
          break;
        }
      }
    
      return CURLE_OK;
    }