Skip to content
Snippets Groups Projects
tftp.c 40.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • 
      Curl_pgrsStartNow(conn->data);
    
      *done = TRUE;
      code = CURLE_OK;
      return(code);
    }
    
    /**********************************************************
     *
    
     *
     * The done callback
     *
     **********************************************************/
    
    static CURLcode tftp_done(struct connectdata *conn, CURLcode status,
    
      CURLcode code = CURLE_OK;
      tftp_state_data_t *state = (tftp_state_data_t *)conn->proto.tftpc;
    
    
      /* If we have encountered an error */
      code = tftp_translate_code(state->error);
    
    
    /**********************************************************
     *
    
     *
     **********************************************************/
    
    static int tftp_getsock(struct connectdata *conn, curl_socket_t *socks,
                           int numsocks)
    {
      if(!numsocks)
        return GETSOCK_BLANK;
    
      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;
    
      CURLcode              result = CURLE_OK;
      struct SessionHandle  *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 &&
              ((state->block+1) == 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;
    
      if (event)
        *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 */
        return(state->max_time-current);
      }
      else {
        return(state->max_time-current);
      }
    }
    
    /**********************************************************
     *
     * tftp_easy_statemach
     *
     * Handle easy request until completion
     *
     **********************************************************/
    static CURLcode tftp_easy_statemach(struct connectdata *conn)
    {
      int                   rc;
      int                   check_time = 0;
      CURLcode              result = CURLE_OK;
      struct SessionHandle  *data = conn->data;
      tftp_state_data_t     *state = (tftp_state_data_t *)conn->proto.tftpc;
    
      for(;
          (state->state != TFTP_STATE_FIN) && (result == CURLE_OK);
          result=tftp_state_machine(state, state->event) ) {
    
    
        /* Wait until ready to read or timeout occurs */
    
        rc=Curl_socket_ready(state->sockfd, CURL_SOCKET_BAD,
    
          failf(data, "%s", Curl_strerror(conn, error));
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        else if(rc==0) {
    
    
          /* Force a look at transfer timeouts */
          check_time = 0;
    
        }
        else {
    
        }
    
        /* Check for transfer timeout every 10 blocks, or after timeout */
        if(check_time%10==0) {
    
          /* ignore the event here as Curl_socket_ready() handles
           * retransmission timeouts inside the easy state mach */
          tftp_state_timeout(conn, NULL);
    
      result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
    
    /**********************************************************
     *
     * 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 SessionHandle  *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);
        if(result != CURLE_OK)
          return(result);
        *done = (bool)(state->state == TFTP_STATE_FIN);
        if(*done)
          /* Tell curl we're done */
          result = 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);
          if(result != CURLE_OK)
            return(result);
          result = tftp_state_machine(state, state->event);
          if(result != CURLE_OK)
            return(result);
          *done = (bool)(state->state == TFTP_STATE_FIN);
          if(*done)
            /* Tell curl we're done */
            result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
        }
        /* if rc == 0, then select() timed out */
    
    
      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"));
      }
      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 != CURLE_OK)
        return(result);
    
      if(conn->data->state.used_interface == Curl_if_multi)
        tftp_multi_statemach(conn, dophase_done);
      else {
        result = tftp_easy_statemach(conn);
        *dophase_done = TRUE; /* with the easy interface we are done here */
      }
    
      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              code;
    
      *done = FALSE;
    
      /*
        Since connections can be re-used between SessionHandles, this might be a
        connection already existing but on a fresh SessionHandle struct so we must
        make sure we have a good 'struct TFTP' to play with. For new connections,
        the struct TFTP is allocated and setup in the tftp_connect() function.
      */
      Curl_reset_reqproto(conn);
    
      if(!conn->proto.tftpc) {
        code = tftp_connect(conn, done);
        if(code)
          return code;
      }
      state = (tftp_state_data_t *)conn->proto.tftpc;
    
      code = tftp_perform(conn, done);
    
      /* If we have encountered an error */
      code = tftp_translate_code(state->error);
    
    
    static CURLcode tftp_setup_connection(struct connectdata * conn)
    
    {
      struct SessionHandle *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;
    }