END_TEST

START_TEST(tc_tftp_send_data)
{
    example_session.state = 0;
    example_session.socket = &example_socket;
    called_sendto = 0;
    expected_opcode = PICO_TFTP_DATA;
    tftp_send_data(&example_session, (const uint8_t*)"buffer", strlen("buffer"));
    fail_if(called_sendto < 1);
    fail_if(example_session.state != PICO_TFTP_STATE_TX_LAST);
}
Exemple #2
0
static void tftp_handle_ack(PNATState pData, struct tftp_t *tp, int pktlen)
{
    int s;

    s = tftp_session_find(pData, tp);
    if (s < 0)
        return;

    if (tftp_send_data(pData, &tftp_sessions[s],
                       RT_N2H_U16(tp->x.tp_data.tp_block_nr) + 1, tp) < 0)
    {
        /* XXX */
    }
}
Exemple #3
0
static void tftp_handle_ack(Slirp *slirp, struct tftp_t *tp, int pktlen)
{
  int s;

  s = tftp_session_find(slirp, tp);

  if (s < 0) {
    return;
  }

  if (tftp_send_data(&slirp->tftp_sessions[s],
		     ntohs(tp->x.tp_data.tp_block_nr) + 1,
		     tp) < 0) {
    return;
  }
}
Exemple #4
0
/*
 * Send a file. It is implemented as a state machine using a while loop
 * and a switch statement. Function flow is as follow:
 *  - sanity check
 *  - check client's request
 *  - enter state machine
 *
 *     1) send a DATA or OACK
 *     2) wait replay
 *          - if ACK, goto 3
 *          - if ERROR abort
 *          - if TIMEOUT goto previous state
 *     3) send data, goto 2
 */
int tftpd_send_file(struct thread_data *data)
{
     int state = S_BEGIN;
     int timeout_state = state;
     int result;
     int block_number = 0;
     int last_block = -1;
     int data_size;
     struct sockaddr_storage *sa = &data->client_info->client;
     struct sockaddr_storage from;
     char addr_str[SOCKADDR_PRINT_ADDR_LEN];
     int sockfd = data->sockfd;
     struct tftphdr *tftphdr = (struct tftphdr *)data->data_buffer;
     FILE *fp;
     char filename[MAXLEN];
     char string[MAXLEN];
     int timeout = data->timeout;
     int number_of_timeout = 0;
     int mcast_switch = data->mcast_switch_client;
     struct stat file_stat;
     int convert = 0;           /* if true, do netascii conversion */
     struct thread_data *thread = NULL; /* used when looking for a multicast
                                           thread */
     int multicast = 0;         /* set to 1 if multicast */

     struct client_info *client_info = data->client_info;
     struct client_info *client_old = NULL;
     struct tftp_opt options[OPT_NUMBER];

     int prev_block_number = 0; /* needed to support netascii convertion */
     int prev_file_pos = 0;
     int temp = 0;

     /* look for mode option */
     if (strcasecmp(data->tftp_options[OPT_MODE].value, "netascii") == 0)
     {
          convert = 1;
          logger(LOG_DEBUG, "will do netascii convertion");
     }

     /* file name verification */
     Strncpy(filename, data->tftp_options[OPT_FILENAME].value,
             MAXLEN);
     if (tftpd_rules_check(filename) != OK)
     {
          tftp_send_error(sockfd, sa, EACCESS, data->data_buffer, data->data_buffer_size);
          if (data->trace)
               logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EACCESS,
                      tftp_errmsg[EACCESS]);
          return ERR;
     }

     /* verify that the requested file exist */
     fp = fopen(filename, "r");

#ifdef HAVE_PCRE
     if (fp == NULL)
     {
          /* Verify if this file have a working subsitution */
          if (pcre_top != NULL)
          {
               if (tftpd_pcre_sub(pcre_top, string, MAXLEN,
                                  data->tftp_options[OPT_FILENAME].value) < 0)
               {
                    logger(LOG_DEBUG, "PCRE failed to match");
               }
               else
               {
                    logger(LOG_INFO, "PCRE mapped %s -> %s", 
                           data->tftp_options[OPT_FILENAME].value, string);
                    Strncpy(filename, string, MAXLEN);
                    /* recheck those rules */
                    if (tftpd_rules_check(filename) != OK)
                    {
                         tftp_send_error(sockfd, sa, EACCESS, data->data_buffer,
                                         data->data_buffer_size);
                         if (data->trace)
                              logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EACCESS,
                                     tftp_errmsg[EACCESS]);
                         return ERR;
                    }
                    /* write back the new file name to the option structure */
                    opt_set_options(data->tftp_options, "filename", filename);
                    /* try to open this new file */
                    fp = fopen(filename, "r");
               }
          }
     }
#endif
     if (fp == NULL)
     {
          tftp_send_error(sockfd, sa, ENOTFOUND, data->data_buffer, data->data_buffer_size);
          logger(LOG_INFO, "File %s not found", filename);
          if (data->trace)
               logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", ENOTFOUND,
                      tftp_errmsg[ENOTFOUND]);
          return ERR;
     }

     /* To return the size of the file with tsize argument */
     fstat(fileno(fp), &file_stat);

     /* tsize option */
     if ((opt_get_tsize(data->tftp_options) > -1) && !convert)
     {
          opt_set_tsize(file_stat.st_size, data->tftp_options);
          logger(LOG_INFO, "tsize option -> %d", file_stat.st_size);
     }

     /* timeout option */
     if ((result = opt_get_timeout(data->tftp_options)) > -1)
     {
          if ((result < 1) || (result > 255))
          {
               tftp_send_error(sockfd, sa, EOPTNEG, data->data_buffer, data->data_buffer_size);
               if (data->trace)
                    logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EOPTNEG,
                           tftp_errmsg[EOPTNEG]);
               fclose(fp);
               return ERR;
          }
          timeout = result;
          opt_set_timeout(timeout, data->tftp_options);
          logger(LOG_INFO, "timeout option -> %d", timeout);
     }

     /* blksize options */
     if ((result = opt_get_blksize(data->tftp_options)) > -1)
     {
          if ((result < 8) || (result > 65464))
          {
               tftp_send_error(sockfd, sa, EOPTNEG, data->data_buffer, data->data_buffer_size);
               if (data->trace)
                    logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EOPTNEG,
                           tftp_errmsg[EOPTNEG]);
               fclose(fp);
               return ERR;
          }

          data->data_buffer_size = result + 4;
          data->data_buffer = realloc(data->data_buffer, data->data_buffer_size);

          if (data->data_buffer == NULL)
          {
               logger(LOG_ERR, "memory allocation failure");
               fclose(fp);
               return ERR;
          }
          tftphdr = (struct tftphdr *)data->data_buffer;

          if (data->data_buffer == NULL)
          {
               tftp_send_error(sockfd, sa, ENOSPACE, data->data_buffer, data->data_buffer_size);
               if (data->trace)
                    logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", ENOSPACE,
                           tftp_errmsg[ENOSPACE]);
               fclose(fp);
               return ERR;
          }
          opt_set_blksize(result, data->tftp_options);
          logger(LOG_INFO, "blksize option -> %d", result);
     }

     /* Verify that the file can be sent in 2^16 block of BLKSIZE octets */
     if ((file_stat.st_size / (data->data_buffer_size - 4)) > 65535)
     {
          tftp_send_error(sockfd, sa, EUNDEF, data->data_buffer, data->data_buffer_size);
          logger(LOG_NOTICE, "Requested file to big, increase BLKSIZE");
          if (data->trace)
               logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EUNDEF,
                      tftp_errmsg[EUNDEF]);
          fclose(fp);
          return ERR;
     }

     /* multicast option */
     if (data->tftp_options[OPT_MULTICAST].specified &&
         data->tftp_options[OPT_MULTICAST].enabled && !convert)
     {
          /*
           * Find a server with the same options to give up the client.
           */
          logger(LOG_DEBUG, "Searching a server thread to give up this client");
          result = tftpd_list_find_multicast_server_and_add(&thread, data, data->client_info);
          if ( result > 0)
          {
               /* add this client to its list of client */
               if (result == 1)
                    logger(LOG_DEBUG, "Added client %p to thread %p", data->client_info,
                           thread);
               else
                    logger(LOG_DEBUG, "Client (%p) is already in list of thread %p",
                           data->client_info, thread);

               /* NULL our own pointer so we don't free memory */
               if (result == 1)
                    data->client_info = NULL;

               /* Look at needed information to oack that client */
               opt_set_multicast(data->tftp_options, thread->mc_addr,
                                 thread->mc_port, 0);
               logger(LOG_INFO, "multicast option -> %s,%d,%d",
                      thread->mc_addr, thread->mc_port, 0);

               /* Send an OACK to that client. There is a possible race condition
                  here where the new server thread OACK this client before us. This should
                  not be a problem: the client thread will receive a second OACK and fall
                  back to non master mode. Then the server will timeout and either resend
                  OACK or continu with the next client */
               opt_options_to_string(data->tftp_options, string, MAXLEN);
               if (data->trace)
                    logger(LOG_DEBUG, "sent OACK <%s>", string);
               tftp_send_oack(thread->sockfd, sa, data->tftp_options,
                              data->data_buffer, data->data_buffer_size);

               /* We are done */
               logger(LOG_INFO, "Client transfered to %p", thread);
               fclose(fp);
               return OK;
          }
          else
          {
               struct addrinfo hints, *result;

               /* configure socket, get an IP address */
               if (tftpd_mcast_get_tid(&data->mc_addr, &data->mc_port) != OK)
               {
                    logger(LOG_ERR, "No multicast address/port available");
                    fclose(fp);
                    return ERR;
               }
               logger(LOG_DEBUG, "mcast_addr: %s, mcast_port: %d",
                      data->mc_addr, data->mc_port);

               /* convert address */
               memset(&hints, 0, sizeof(hints));
               hints.ai_socktype = SOCK_DGRAM;
               hints.ai_flags = AI_NUMERICHOST;
               if (getaddrinfo(data->mc_addr, NULL, &hints, &result) ||
                   sockaddr_set_addrinfo(&data->sa_mcast, result))
               {
                    logger(LOG_ERR, "bad address %s\n",data->mc_addr);
                    fclose(fp);
                    return ERR;
               }
               freeaddrinfo(result);
               sockaddr_set_port(&data->sa_mcast, data->mc_port);

               /* verify address is multicast */
               if (!sockaddr_is_multicast(&data->sa_mcast))
               {
                    logger(LOG_ERR, "bad multicast address %s\n",
                           sockaddr_print_addr(&data->sa_mcast,
                                               addr_str, sizeof(addr_str)));
                    fclose(fp);
                    return ERR;
               }

               /* initialise multicast address structure */
               sockaddr_get_mreq(&data->sa_mcast, &data->mcastaddr);
               if (data->sa_mcast.ss_family == AF_INET)
                    setsockopt(data->sockfd, IPPROTO_IP, IP_MULTICAST_TTL,
                               &data->mcast_ttl, sizeof(data->mcast_ttl));
               else
                    setsockopt(data->sockfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
                               &data->mcast_ttl, sizeof(data->mcast_ttl));
               
               /* set options data for OACK */
               opt_set_multicast(data->tftp_options, data->mc_addr,
                                 data->mc_port, 1);
               logger(LOG_INFO, "multicast option -> %s,%d,%d", data->mc_addr,
                      data->mc_port, 1);
            
               /* the socket must be unconnected for multicast */
               sa->ss_family = AF_UNSPEC;
               connect(sockfd, (struct sockaddr *)sa, sizeof(*sa));

               /* set multicast flag */
               multicast = 1;
               /* Now ready to receive new clients */
               tftpd_clientlist_ready(data);
          }
     }

     /* copy options to local structure, used when falling back a client to slave */
     memcpy(options, data->tftp_options, sizeof(options));
     opt_set_multicast(options, data->mc_addr, data->mc_port, 0);

     /* That's it, ready to send the file */
     while (1)
     {
          if (tftpd_cancel)
          {
               /* Send error to all client */
               logger(LOG_DEBUG, "thread cancelled");
               do
               {
                    tftpd_clientlist_done(data, client_info, NULL);
                    tftp_send_error(sockfd, &client_info->client,
                                    EUNDEF, data->data_buffer, data->data_buffer_size);
                    if (data->trace)
                    {
                         logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s> to %s", EUNDEF,
                                tftp_errmsg[EUNDEF],
                                sockaddr_print_addr(&client_info->client,
                                                    addr_str, sizeof(addr_str)));
                    }
               } while (tftpd_clientlist_next(data, &client_info) == 1);
               state = S_ABORT;
          }

          switch (state)
          {
          case S_BEGIN:
               if (opt_support_options(data->tftp_options))
                    state = S_SEND_OACK;
               else
                    state = S_SEND_DATA;
               break;
          case S_SEND_OACK:
               timeout_state = state;
               opt_options_to_string(data->tftp_options, string, MAXLEN);
               if (data->trace)
                    logger(LOG_DEBUG, "sent OACK <%s>", string);
               tftp_send_oack(sockfd, sa, data->tftp_options,
                              data->data_buffer, data->data_buffer_size);
               state = S_WAIT_PACKET;
               break;
          case S_SEND_DATA:
               timeout_state = state;

               data_size = tftp_file_read(fp, tftphdr->th_data, data->data_buffer_size - 4, block_number,
                                          convert, &prev_block_number, &prev_file_pos, &temp);
               data_size += 4;  /* need to consider tftp header */

               /* record the last block number */
               if (feof(fp))
                    last_block = block_number;

               if (multicast)
               {
                    tftp_send_data(sockfd, &data->sa_mcast,
                                   block_number + 1, data_size,
                                   data->data_buffer);
               }
               else
               {
                    tftp_send_data(sockfd, sa, block_number + 1,
                                   data_size, data->data_buffer);
               }
               if (data->trace)
                    logger(LOG_DEBUG, "sent DATA <block: %d, size %d>",
                           block_number + 1, data_size - 4);
               state = S_WAIT_PACKET;
               break;
          case S_WAIT_PACKET:
               data_size = data->data_buffer_size;
               result = tftp_get_packet(sockfd, -1, NULL, sa, &from, NULL,
                                        timeout, &data_size, data->data_buffer);
               switch (result)
               {
               case GET_TIMEOUT:
                    number_of_timeout++;
                    
                    if (number_of_timeout > NB_OF_RETRY)
                    {
                         logger(LOG_INFO, "client (%s) not responding",
                                sockaddr_print_addr(&client_info->client,
                                                    addr_str, sizeof(addr_str)));
                         state = S_END;
                    }
                    else
                    {
                         /* The client failed to ACK our last packet. Send an
                            OACK with mc=0 to it, fetch the next client in the
                            list and continu with this new one */
                         if (multicast && mcast_switch)
                         {
                              client_old = client_info;

                              tftpd_clientlist_next(data, &client_info);

                              if (client_info && (client_info != client_old))
                              {
                                   /* Send an OACK to the old client remove is
                                      master client status */
                                   opt_options_to_string(options,
                                                         string, MAXLEN);
                                   if (data->trace)
                                        logger(LOG_DEBUG, "sent OACK <%s>", string);
                                   tftp_send_oack(sockfd, sa, options,
                                                  data->data_buffer, data->data_buffer_size);

                                   /* Proceed normally with the next client,
                                      going to OACK state */
                                   logger(LOG_INFO,
                                          "Serving next client: %s:%d",
                                          sockaddr_print_addr(
                                               &client_info->client,
                                               addr_str, sizeof(addr_str)),
                                          sockaddr_get_port(
                                               &client_info->client));
                                   sa = &client_info->client;
                                   state = S_SEND_OACK;
                                   break;
                              }
                              else if (client_info == NULL)
                              {
                                   /* we got a big problem if this happend */
                                   logger(LOG_ERR,
                                          "%s: %d: abnormal condition",
                                          __FILE__, __LINE__);
                                   state = S_ABORT;
                                   break;
                              }
                         }
                         logger(LOG_WARNING, "timeout: retrying...");
                         state = timeout_state;
                    }
                    break;
               case GET_ACK:
                    /* handle case where packet come from un unexpected client */
                    if (multicast)
                    {
                         if (!sockaddr_equal(sa, &from))
                         {
                              /* We got an ACK from a client that is not the master client.
                               * If this is an ACK for the last block, mark this client as
                               * done
                               */
                              if ((last_block != -1) && (block_number > last_block))
                              {
                                   if (tftpd_clientlist_done(data, NULL, &from) == 1)
                                        logger(LOG_DEBUG, "client done <%s>",
                                               sockaddr_print_addr(
                                                    &from, addr_str,
                                                    sizeof(addr_str)));
                                   else
                                        logger(LOG_WARNING, "packet discarded <%s:%d>",
                                               sockaddr_print_addr(
                                                    &from, addr_str,
                                                    sizeof(addr_str)),
                                               sockaddr_get_port(&from));
                              }
                              else
                                   /* If not, send and OACK with mc=0 to shut it up. */
                              {
                                   opt_options_to_string(options,
                                                         string, MAXLEN);
                                   if (data->trace)
                                        logger(LOG_DEBUG, "sent OACK <%s>", string);
                                   tftp_send_oack(sockfd, &from, options,
                                                  data->data_buffer, data->data_buffer_size);
                              }
                              break;
                              
                         }
                    }
                    else
                    {
                         /* check that the packet is from the current client */
                         if (sockaddr_get_port(sa) != sockaddr_get_port(&from))
                         {
                              if (data->checkport)
                              {
                                   logger(LOG_WARNING, "packet discarded <%s:%d>",
                                          sockaddr_print_addr(&from, addr_str,
                                                              sizeof(addr_str)),
                                          sockaddr_get_port(&from));
                                   break;
                              }
                              else
                                   logger(LOG_WARNING,
                                          "source port mismatch, check bypassed");
                         }
                    }
                    /* The ACK is from the current client */
                    number_of_timeout = 0;
                    block_number = ntohs(tftphdr->th_block);
                    if (data->trace)
                         logger(LOG_DEBUG, "received ACK <block: %d>",
                                block_number);
                    if ((last_block != -1) && (block_number > last_block))
                    {
                         state = S_END;
                         break;
                    }
                    state = S_SEND_DATA;
                    break;
               case GET_ERROR:
                    /* handle case where packet come from un unexpected client */
                    if (multicast)
                    {
                         /* if packet is not from the current master client */
                         if (!sockaddr_equal(sa, &from))
                         {
                              /* mark this client done */
                              if (tftpd_clientlist_done(data, NULL, &from) == 1)
                              {
                                   if (data->trace)
                                        logger(LOG_DEBUG, "client sent ERROR, mark as done <%s>",
                                               sockaddr_print_addr(
                                                    &from, addr_str,
                                                    sizeof(addr_str)));
                              }
                              else
                                   logger(LOG_WARNING, "packet discarded <%s>",
                                          sockaddr_print_addr(&from, addr_str,
                                                              sizeof(addr_str)));
                              /* current state is unchanged */
                              break;
                         }
                    }
                    else
                    {
                         /* check that the packet is from the current client */
                         if (sockaddr_get_port(sa) != sockaddr_get_port(&from))
                         {
                              if (data->checkport)
                              {
                                   logger(LOG_WARNING, "packet discarded <%s>",
                                          sockaddr_print_addr(&from, addr_str,
                                                              sizeof(addr_str)));
                                   break;
                              }
                              else
                                   logger(LOG_WARNING,
                                          "source port mismatch, check bypassed");
                         }
                    }
                    /* Got an ERROR from the current master client */
                    Strncpy(string, tftphdr->th_msg,
                            (((data_size - 4) > MAXLEN) ? MAXLEN :
                             (data_size - 4)));
                    if (data->trace)
                         logger(LOG_DEBUG, "received ERROR <code: %d, msg: %s>",
                                ntohs(tftphdr->th_code), string);
                    if (multicast)
                    {
                         logger(LOG_DEBUG, "Marking client as done");
                         state = S_END;
                    }
                    else
                         state = S_ABORT;
                    break;
               case GET_DISCARD:
                    /* FIXME: should we increment number_of_timeout */
                    logger(LOG_WARNING, "packet discarded <%s>",
                           sockaddr_print_addr(&from, addr_str,
                                               sizeof(addr_str)));
                    break;
               case ERR:
                    logger(LOG_ERR, "%s: %d: recvfrom: %s",
                           __FILE__, __LINE__, strerror(errno));
                    state = S_ABORT;
                    break;
               default:
                    logger(LOG_ERR, "%s: %d: abnormal return value %d",
                           __FILE__, __LINE__, result);
               }
               break;
          case S_END:
               if (multicast)
               {
                    logger(LOG_DEBUG, "End of multicast transfer");
                    /* mark the current client done */
                    tftpd_clientlist_done(data, client_info, NULL);
                    /* Look if there is another client to serve. We lock list of
                       client to make sure no other thread try to add clients in
                       our back */
                    if (tftpd_clientlist_next(data, &client_info) == 1)
                    {
                         logger(LOG_INFO,
                                "Serving next client: %s:%d",
                                sockaddr_print_addr(&client_info->client,
                                                    addr_str, sizeof(addr_str)),
                                sockaddr_get_port(&client_info->client));
                         /* client is a new client structure */
                         sa =  &client_info->client;
                         /* nedd to send an oack to that client */
                         state = S_SEND_OACK;                
                         fseek(fp, 0, SEEK_SET);
                    }
                    else
                    {
                         logger(LOG_INFO, "No more client, end of tranfers");
                         fclose(fp);
                         return OK;
                    }
               }
               else
               {
                    logger(LOG_DEBUG, "End of transfer");
                    fclose(fp);
                    return OK;
               }
               break;
          case S_ABORT:
               logger(LOG_DEBUG, "Aborting transfer");
               fclose(fp);
               return ERR;
          default:
               fclose(fp);
               logger(LOG_ERR, "%s: %d: abnormal condition",
                      __FILE__, __LINE__);
               return ERR;
          }
     }
}
Exemple #5
0
static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen)
{
  struct tftp_session *spt;
  int s, k;
  size_t prefix_len;
  char *req_fname;

  s = tftp_session_allocate(slirp, tp);

  if (s < 0) {
    return;
  }

  spt = &slirp->tftp_sessions[s];

  /* unspecifed prefix means service disabled */
  if (!slirp->tftp_prefix) {
      tftp_send_error(spt, 2, "Access violation", tp);
      return;
  }

  /* skip header fields */
  k = 0;
  pktlen -= ((uint8_t *)&tp->x.tp_buf[0] - (uint8_t *)tp);

  /* prepend tftp_prefix */
  prefix_len = strlen(slirp->tftp_prefix);
  spt->filename = qemu_malloc(prefix_len + TFTP_FILENAME_MAX + 2);
  memcpy(spt->filename, slirp->tftp_prefix, prefix_len);
  spt->filename[prefix_len] = '/';

  /* get name */
  req_fname = spt->filename + prefix_len + 1;

  while (1) {
    if (k >= TFTP_FILENAME_MAX || k >= pktlen) {
      tftp_send_error(spt, 2, "Access violation", tp);
      return;
    }
    req_fname[k] = (char)tp->x.tp_buf[k];
    if (req_fname[k++] == '\0') {
      break;
    }
  }

  /* check mode */
  if ((pktlen - k) < 6) {
    tftp_send_error(spt, 2, "Access violation", tp);
    return;
  }

  if (memcmp(&tp->x.tp_buf[k], "octet\0", 6) != 0) {
      tftp_send_error(spt, 4, "Unsupported transfer mode", tp);
      return;
  }

  k += 6; /* skipping octet */

  /* do sanity checks on the filename */
  if (!strncmp(req_fname, "../", 3) ||
      req_fname[strlen(req_fname) - 1] == '/' ||
      strstr(req_fname, "/../")) {
      tftp_send_error(spt, 2, "Access violation", tp);
      return;
  }

  /* check if the file exists */
  if (tftp_read_data(spt, 0, NULL, 0) < 0) {
      tftp_send_error(spt, 1, "File not found", tp);
      return;
  }

  if (tp->x.tp_buf[pktlen - 1] != 0) {
      tftp_send_error(spt, 2, "Access violation", tp);
      return;
  }

  while (k < pktlen) {
      const char *key, *value;

      key = (const char *)&tp->x.tp_buf[k];
      k += strlen(key) + 1;

      if (k >= pktlen) {
	  tftp_send_error(spt, 2, "Access violation", tp);
	  return;
      }

      value = (const char *)&tp->x.tp_buf[k];
      k += strlen(value) + 1;

      if (strcmp(key, "tsize") == 0) {
	  int tsize = atoi(value);
	  struct stat stat_p;

	  if (tsize == 0) {
	      if (stat(spt->filename, &stat_p) == 0)
		  tsize = stat_p.st_size;
	      else {
		  tftp_send_error(spt, 1, "File not found", tp);
		  return;
	      }
	  }

	  tftp_send_oack(spt, "tsize", tsize, tp);
      }
  }

  tftp_send_data(spt, 1, tp);
}
static void tftp_handle_rrq(struct tftp_t *tp, int pktlen)
{
  struct tftp_session *spt;
  int s, k, n;
  u_int8_t *src, *dst;

  s = tftp_session_allocate(tp);

  if (s < 0) {
    return;
  }

  spt = &tftp_sessions[s];

  src = tp->x.tp_buf;
  dst = spt->filename;
  n = pktlen - ((uint8_t *)&tp->x.tp_buf[0] - (uint8_t *)tp);

  /* get name */

  for (k = 0; k < n; k++) {
    if (k < TFTP_FILENAME_MAX) {
      dst[k] = src[k];
    }
    else {
      return;
    }

    if (src[k] == '\0') {
      break;
    }
  }

  if (k >= n) {
    return;
  }

  k++;

  /* check mode */
  if ((n - k) < 6) {
    return;
  }

  if (memcmp(&src[k], "octet\0", 6) != 0) {
      tftp_send_error(spt, 4, "Unsupported transfer mode", tp);
      return;
  }

  k += 6; /* skipping octet */

  /* do sanity checks on the filename */

  if ((spt->filename[0] != '/')
      || (spt->filename[strlen((char *)spt->filename) - 1] == '/')
      ||  strstr((char *)spt->filename, "/../")) {
      tftp_send_error(spt, 2, "Access violation", tp);
      return;
  }

  /* only allow exported prefixes */

  if (!tftp_prefix) {
      tftp_send_error(spt, 2, "Access violation", tp);
      return;
  }

  /* check if the file exists */

  if (tftp_read_data(spt, 0, spt->filename, 0) < 0) {
      tftp_send_error(spt, 1, "File not found", tp);
      return;
  }

  if (src[n - 1] != 0) {
      tftp_send_error(spt, 2, "Access violation", tp);
      return;
  }

  while (k < n) {
      const char *key, *value;

      key = (char *)src + k;
      k += strlen(key) + 1;

      if (k >= n) {
	  tftp_send_error(spt, 2, "Access violation", tp);
	  return;
      }

      value = (char *)src + k;
      k += strlen(value) + 1;

      if (strcmp(key, "tsize") == 0) {
	  int tsize = atoi(value);
	  struct stat stat_p;

	  if (tsize == 0 && tftp_prefix) {
	      char buffer[1024];
	      int len;

	      len = snprintf(buffer, sizeof(buffer), "%s/%s",
			     tftp_prefix, spt->filename);

	      if (stat(buffer, &stat_p) == 0)
		  tsize = stat_p.st_size;
	      else {
		  tftp_send_error(spt, 1, "File not found", tp);
		  return;
	      }
	  }

	  tftp_send_oack(spt, "tsize", tsize, tp);
      }
  }

  tftp_send_data(spt, 1, tp);
}
Exemple #7
0
/*
  Transfer a file to or from the server.

 */
int tftp_transfer(struct tftp_conn *tc)
{
    int retval = 0;
    int len;
    int reclen;
    int totlen = 0;
    int terminate = 0;

    struct timeval timeout;


    /* Sanity check */
    if (!tc)
        return -1;


    fd_set sfd;
    char recbuf[MSGBUF_SIZE];

    FD_ZERO(&sfd);
    FD_SET(tc->sock, &sfd);



    len = BLOCK_SIZE + TFTP_DATA_HDR_LEN;
    reclen = 0;

    /* After the connection request we should start receiving data
     * immediately */

    /* Set a timeout for resending data. */

    timeout.tv_sec = TFTP_TIMEOUT;
    timeout.tv_usec = 0;

    /* Check if we are putting a file or getting a file and send
     * the corresponding request. */

    if (tc->type == TFTP_TYPE_GET) {
        /* Send read request */
        if(tftp_send_rrq(tc) < 0)
            fprintf(stderr,"FAIL TO SEND RRQ\n");

    } else if (tc->type == TFTP_TYPE_PUT) {
        /* Send write request */
        if(tftp_send_wrq(tc) < 0)
            fprintf(stderr,"FAIL TO SEND WRQ\n");

    } else {
        return -1;
    }


    /*
      Put or get the file, block by block, in a loop.
     */
    do {
        /* 1. Wait for something from the server (using
         * 'select'). If a timeout occurs, resend last block
         * or ack depending on whether we are in put or get
         * mode. */

        /* ... */
        printf("Waiting for response... \n");

        FD_ZERO(&sfd);
        FD_SET(tc->sock, &sfd);

        switch (select(tc->sock + 1, &sfd, NULL, NULL, &timeout)) {
        case (-1):
            fprintf(stderr, "\nselect()\n");
            break;
        case (0):
            /* Timeout, reinit the counter and do a resend.
             * Nested switch-case statemens are awesome! */

            printf("**** TIMEOUT *****\n");
            timeout.tv_sec = TFTP_TIMEOUT;
            timeout.tv_usec = 0;

            switch (ntohs(((u_int16_t*) tc->msgbuf)[0])) {
            case OPCODE_RRQ:
                tftp_send_rrq(tc);
                continue;
            case OPCODE_WRQ:
                tftp_send_wrq(tc);
                continue;
            case OPCODE_DATA:
                tftp_send_data(tc, -len);
                continue;
            case OPCODE_ACK:
                tftp_send_ack(tc);
                continue;
            case OPCODE_ERR:
                //TODO: Vilka error-medelanden ska skickas om, om några?
                continue;
            default:
                fprintf(stderr, "\nThis shouldn't happend\n");
                goto out;
                //continue;
            }
            break;
        default:
            //TODO: Använda recvfrom() istället och kolla efter felaktig source port.

            /* Save the recieved bytes in 'rec_len' so we
             * can check if we should terminate the transfer */

            printf("GOT SOMETHING!!!!\n");
            reclen = recvfrom(tc->sock, recbuf, MSGBUF_SIZE,0, (struct sockaddr *)  &tc->peer_addr, &tc->addrlen);
            print_message((struct tftp_msg *)recbuf, 1);
            //printf("%d\n", ntohs(((u_int16_t*) recbuf)[0]));
            break;
        }

        /* 2. Check the message type and take the necessary
         * action. */
        switch (ntohs(((u_int16_t*) recbuf)[0])) {
        case OPCODE_DATA:

            /* Received data block, send ack */
            //TODO: Skriv datan till en fil
            printf("Received data\n");
            tc->blocknr++;
            if (tc->type == TFTP_TYPE_PUT) {
                fprintf(stderr, "\nExpected ack, got data\n");
                goto out;
            }
            printf("We expect block number %d\n", tc->blocknr);
            printf("We got block number %d\n",ntohs(((u_int16_t*) recbuf)[1]));

            if (ntohs(((u_int16_t*) recbuf)[1]) != tc->blocknr) {
                fprintf(stderr, "\nGot unexpected data block§ nr\n");
                goto out;
            }


            /* If we are getting and recieved a data package with
             * a block of < 512, we want to terminate the loop
             * after getting sending an ack */
            if (reclen < (TFTP_DATA_HDR_LEN + BLOCK_SIZE))
                terminate = 1;

            tftp_send_ack(tc); //TODO: Kolla returvärdet

            int i = 0;

            int hnllen = sizeof(HOST_NEWLINE_STYLE) - 1;
            int nanllen = sizeof(NETASCII_NEWLINE_STYLE) - 1;

            if (!strcmp(tc->mode, MODE_NETASCII)) {
                do {

                    if (!strncmp(&recbuf[TFTP_DATA_HDR_LEN + i], NETASCII_NEWLINE_STYLE, nanllen)) {
                        fwrite(HOST_NEWLINE_STYLE, 1, hnllen, tc->fp);

                        i += nanllen;

                    } else {

                        fwrite(&recbuf[TFTP_DATA_HDR_LEN + i], 1, 1,tc->fp);
                        i++;

                    }

                } while(i < reclen - TFTP_DATA_HDR_LEN);

            } else {
                fwrite(&recbuf[TFTP_DATA_HDR_LEN],1,reclen - TFTP_DATA_HDR_LEN,tc->fp);
            }

            break;
        case OPCODE_ACK:
            printf("Received ACK, send next block\n");

            if (tc->type == TFTP_TYPE_GET) {
                fprintf(stderr, "\nExpected data, got ack\n");
                goto out;
            }

            /* If we are putting and sent a data package with
             * a block of < 512 bytes last time, we want to
             * terminate the loop after getting the final ack */
            if (len < (TFTP_DATA_HDR_LEN + BLOCK_SIZE)) {
                terminate = 1;
                printf("We're done sending, let's terminate\n");
            } else {
                /* Save the numer of sent bytes in 'len' in case
                * it's < 512 and the package has to be resent. */
                len = tftp_send_data(tc, BLOCK_SIZE);
                printf("We sent a packet of length %d\n",len);
            }

            break;
        case OPCODE_ERR:
            printf("The transfer was terminated with an error ");
            printf("and the pitiful excuse given by the server was: ");
            printf("%s\n", ((struct tftp_err*) &recbuf)->errmsg);
            break;
        default:
            fprintf(stderr, "\nUnknown message type\n");
            goto out;

        }

        totlen += (len + reclen);

    } while (!terminate);


    if (tc->type == TFTP_TYPE_GET && terminate) {
        /* The loop terminated succesfully but the last ack might
         * have been lost. Wait for a possible duplicate data
         * package and in that case send the ack one more time */
        timeout.tv_sec = TFTP_TIMEOUT;
        timeout.tv_usec = 0;

        if (select(1, &sfd, NULL, NULL, &timeout) > 0) {
            read(tc->sock, tc->msgbuf, MSGBUF_SIZE);
            if (ntohs(((u_int16_t *) tc->msgbuf)[0]) == OPCODE_DATA
                && ntohs(((u_int16_t*) tc->msgbuf)[1]) == tc->blocknr) {
                tftp_send_ack(tc);
            }
        }
    }



    printf("\nTotal data bytes sent/received: %d.\n", totlen);
out:
    fclose(tc->fp);
    return retval;
}
Exemple #8
0
static void tftp_handle_rrq(struct tftp_t *tp, int pktlen)
{
  struct tftp_session *spt;
  int s, k, n;
  u_int8_t *src, *dst;

  s = tftp_session_allocate(tp);

  if (s < 0) {
    return;
  }

  spt = &tftp_sessions[s];

  src = tp->x.tp_buf;
  dst = (u_int8_t *)spt->filename;
  n = pktlen - ((uint8_t *)&tp->x.tp_buf[0] - (uint8_t *)tp);

  /* get name */

  for (k = 0; k < n; k++) {
    if (k < TFTP_FILENAME_MAX) {
      dst[k] = src[k];
    }
    else {
      return;
    }
    
    if (src[k] == '\0') {
      break;
    }
  }
      
  if (k >= n) {
    return;
  }
  
  k++;
  
  /* check mode */
  if ((n - k) < 6) {
    return;
  }
  
  if (memcmp(&src[k], "octet\0", 6) != 0) {
      tftp_send_error(spt, 4, "Unsupported transfer mode", tp);
      return;
  }

  /* do sanity checks on the filename */

  if ((spt->filename[0] != '/')
      || (spt->filename[strlen(spt->filename) - 1] == '/')
      ||  strstr(spt->filename, "/../")) {
      tftp_send_error(spt, 2, "Access violation", tp);
      return;
  }

  /* only allow exported prefixes */

  if (!tftp_prefix
      || (strncmp(spt->filename, tftp_prefix, strlen(tftp_prefix)) != 0)) {
      tftp_send_error(spt, 2, "Access violation", tp);
      return;
  }

  /* check if the file exists */
  
  if (tftp_read_data(spt, 0, (u_int8_t *)spt->filename, 0) < 0) {
      tftp_send_error(spt, 1, "File not found", tp);
      return;
  }

  tftp_send_data(spt, 1, tp);
}
Exemple #9
0
/*
  Transfer a file to or from the server.

*/
int tftp_transfer(struct tftp_conn *tc)
{
  int retval = 0;
  int len;
  int totlen = 0;
  struct timeval timeout;

  int loopend = 1;
  int final_pkt = 0;
  int first_rrq = 1;
  int first_wrq = 1;
  /* Sanity check */
  if (!tc)
    return -1;

  len = 0;

  /* ===ADDED (NOT ACCORDING TO INSTRUCTIONS)=== */
  long int file_size;
  fseek(tc->fp, 0L, SEEK_END);
  file_size = ftell(tc->fp);
  fseek(tc->fp, 0L, SEEK_SET);
  /* ===END OF ADDED=== */

  /* After the connection request we should start receiving data
   * immediately */

  /* Set a timeout for resending data. */

  timeout.tv_sec = TFTP_TIMEOUT;
  timeout.tv_usec = 0;

  /* Check if we are putting a file or getting a file and send
   * the corresponding request. */

  /* ===ADDED=== */
  if (tc->type == TFTP_TYPE_GET) {
    retval = tftp_send_rrq(tc);
  } else if (tc->type == TFTP_TYPE_PUT) {
    retval = tftp_send_wrq(tc);
  }

  if (retval == -1) {
    return retval;
  } else {
    totlen += retval;
  }
  /* ===END OF ADDED=== */

  /*
    Put or get the file, block by block, in a loop.
  */

  int all_done = 0;
  do {
    /* 1. Wait for something from the server (using
     * 'select'). If a timeout occurs, resend last block
     * or ack depending on whether we are in put or get
     * mode. */
    /* ===ADDED=== */
    fd_set readfds;
    FD_ZERO(&readfds);
    FD_SET(tc->sock, &readfds);
    select(tc->sock + 1, &readfds, NULL, NULL, &timeout);
    if (FD_ISSET(tc->sock, &readfds)) {
      retval = recvfrom(tc->sock, tc->msgbuf, MSGBUF_SIZE, 0,
			(struct sockaddr *) &tc->peer_addr, &tc->addrlen);
    } else { // Timeout
      if (tc->type == TFTP_TYPE_PUT) {
	if (first_wrq == 1) {
	  retval = tftp_send_wrq(tc);
	} else {
	  retval = tftp_send_data(tc, -len);
	}
        continue;
      } else if (tc->type == TFTP_TYPE_GET) {
	if (first_rrq == 1) {
	  retval = tftp_send_rrq(tc);
	} else if(final_pkt == 1) {
	  break;
	} else {
	  retval = tftp_send_ack(tc);
	}
	continue;
      }
    }
    /* ===END OF ADDED=== */

    /* 2. Check the message type and take the necessary
     * action. */

    /* ===ADDED/CHANGED=== */
    u_int16_t received_opcode = htons(*((u_int16_t*)tc->msgbuf));

    u_int16_t received_blocknr;
    struct tftp_data *data;
    struct tftp_ack *ack_recv;
    struct tftp_err *err_recv;

    switch (received_opcode) {
    case OPCODE_DATA:
      /* Received data block, send ack */
      data = (struct tftp_data *) tc->msgbuf;
      totlen += retval;
      len = retval - TFTP_DATA_HDR_LEN;
      received_blocknr = ntohs(data->blocknr);
      if((tc->blocknr+1) == received_blocknr){
	fwrite(data->data, 1, len, tc->fp);
	if(len < BLOCK_SIZE) {
	  final_pkt = 1;
	}
	tc->blocknr++;
      }
      retval = tftp_send_ack(tc);
      first_rrq = 0;
      break;
    case OPCODE_ACK:
      /* Received ACK, send next block */
      ack_recv = (struct tftp_ack *) tc->msgbuf;
      received_blocknr = ntohs(ack_recv->blocknr);
      if(received_blocknr == tc->blocknr){
	if(all_done){
	  loopend = 0;
	} else {
	  tc->blocknr++;
	  long int file_pos = ftell(tc->fp);
	  if (file_size - file_pos < BLOCK_SIZE) {
	    retval = tftp_send_data(tc, file_size - file_pos);
	    all_done = 1;
	  } else {
	    retval = tftp_send_data(tc, BLOCK_SIZE);
	  }
	}
      } else {
	retval = tftp_send_data(tc, -len);
      }
      len = retval;
      first_wrq = 0;
      break;
    case OPCODE_ERR:
      err_recv = (struct tftp_err *) tc->msgbuf;
      printf("ERROR CODE %d: %s\n", ntohs(err_recv->errcode), err_recv->errmsg);

      goto out;
    /* ===END OF ADDED/CHANGED=== */
    default:
      fprintf(stderr, "\nUnknown message type\n");
      goto out;

    }

    /* ===ADDED=== */
      totlen += retval;
    /* ===END OF ADDED=== */

    /* ===CHANGED=== */
  } while (loopend /* 3. Loop until file is finished */);
  /* ===END OF CHANGED=== */
  printf("\nTotal data bytes sent/received: %d.\n", totlen);
 out:
  return retval;
}