示例#1
0
END_TEST

START_TEST(tc_tftp_send_ack)
{
    example_session.socket = &example_socket;
#ifdef PICO_FAULTY
    /* send_ack must not segfault when out of memory */
    pico_set_mm_failure(1);
    tftp_send_ack(&example_session);
    fail_if(called_sendto > 0);
#endif
    expected_opcode = PICO_TFTP_ACK;
    tftp_send_ack(&example_session);
    fail_if(called_sendto < 1);

}
示例#2
0
文件: tftp_mtftp.c 项目: seveas/atftp
/*
 * Receive a file. This is implemented as a state machine using a while loop
 * and a switch statement. This follow pxe specification pages 32-34.
 */
int tftp_mtftp_receive_file(struct client_data *data)
{
     int state = S_SEND_REQ;    /* current state in the state machine */
     int timeout_state = state; /* what state should we go on when timeout */
     int result;
     int block_number = 0;
     int last_block_number = -1;/* block number of last block for multicast */
     int data_size;             /* size of data received */
     int sockfd = data->sockfd; /* just to simplify calls */
     int sock;
     struct sockaddr_storage sa; /* a copy of data.sa_peer */
     struct sockaddr_storage from;
     char from_str[SOCKADDR_PRINT_ADDR_LEN];
     struct tftphdr *tftphdr = (struct tftphdr *)data->data_buffer;
     FILE *fp = NULL;           /* the local file pointer */
     int number_of_timeout = 0;
     int timeout = 0;

     struct sockaddr_storage sa_mcast_group;
     int mcast_sockfd = 0;
     struct sockaddr_storage sa_mcast;
     union ip_mreq_storage mreq;
     struct addrinfo hints, *addrinfo;
     int err;

     int mode = LISTEN;
     unsigned int file_bitmap[NB_BLOCK];

     char string[MAXLEN];

     data->file_size = 0;
     tftp_cancel = 0;
     memset(&from, 0, sizeof(from));

     memset(&sa_mcast, 0, sizeof(struct sockaddr_storage));
     memset(&file_bitmap, 0, sizeof(file_bitmap));

     /* make sure the socket is not connected */
     sa.ss_family = AF_UNSPEC;
     connect(sockfd, (struct sockaddr *)&sa, sizeof(sa));

     /* copy sa_peer structure */
     memcpy(&sa, &data->sa_peer, sizeof(sa));

     /* check to see if conversion is requiered */
     if (strcasecmp(data->tftp_options[OPT_MODE].value, "netascii") == 0)
          fprintf(stderr, "netascii convertion ignored\n");

     /* make sure the data buffer is SEGSIZE + 4 bytes */
     if (data->data_buffer_size != (SEGSIZE + 4))
     {
          data->data_buffer = realloc(data->data_buffer, SEGSIZE + 4);
          tftphdr = (struct tftphdr *)data->data_buffer;
          if (data->data_buffer == NULL)
          {
               fprintf(stderr, "atftp: memory allocation failure.\n");
               exit(1);
          }
          data->data_buffer_size = SEGSIZE + 4;
     }

     /* open the file for writing */
     if ((fp = fopen(data->local_file, "w")) == NULL)
     {
          fprintf(stderr, "atftp: can't open %s for writing.\n",
                  data->local_file);
          return ERR;
     }
     
     /* Configure multicast stuff,  look up the host */
     /* if valid, update s_inn structure */
     memset(&hints, 0, sizeof(hints));
     hints.ai_socktype = SOCK_DGRAM;
     if (!getaddrinfo(data->mtftp_mcast_ip, NULL, &hints, &addrinfo) &&
         !sockaddr_set_addrinfo(&sa_mcast_group, addrinfo))
     {
          freeaddrinfo(addrinfo);
          if (!sockaddr_is_multicast(&sa_mcast_group))
	  {
	       fprintf(stderr,
		       "mtftp: bad multicast address %s\n",
		       data->mtftp_mcast_ip);
	       exit(1);
	  }
     } 
     else
     {
	  fprintf(stderr, "atftp: bad multicast address %s",
		  data->mtftp_mcast_ip);
	  exit(1);
     }
     /* we need to open a new socket for multicast */
     if ((mcast_sockfd = socket(AF_INET, SOCK_DGRAM, 0))<0)
     {
	  perror("atftp: socket");
	  exit(1);
     }                   
     memset(&sa_mcast, 0, sizeof(sa_mcast));
     sa_mcast.ss_family = sa_mcast_group.ss_family;
     sockaddr_set_port(&sa, data->mtftp_client_port);
                         
     if (bind(mcast_sockfd, (struct sockaddr *)&sa_mcast,
	      sizeof(sa_mcast)) < 0)
     {
	  perror("atftp: bind");
	  exit(1);
     }
              
     sockaddr_get_mreq(&sa_mcast_group, &mreq);
     if (sa_mcast_group.ss_family == AF_INET)
          err = setsockopt(mcast_sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, 
                           &mreq.v4, sizeof(mreq.v4));
     else
          err = setsockopt(mcast_sockfd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
                           &mreq.v6, sizeof(mreq.v6));
     if (err < 0)
     {
	  perror("atftp: setsockopt");
	  exit(1);
     }

     state = S_LISTEN;

     while (1)
     {
#ifdef DEBUG
          if (data->delay)
               usleep(data->delay*1000);
#endif
          if (tftp_cancel)
          {
               if (from.ss_family == 0)
                    state = S_ABORT;
               else
               {
                    if (mode == RECEIVE)
                    {
                         tftp_send_error(sockfd, &sa, EUNDEF, data->data_buffer,
                                         data->data_buffer_size);
                         if (data->trace)
                              fprintf(stderr,  "sent ERROR <code: %d, msg: %s>\n",
                                      EUNDEF, tftp_errmsg[EUNDEF]);
                    }
                    state = S_ABORT;
               }
               tftp_cancel = 0;
          }

          switch (state)
          {
	  case S_LISTEN:
               if (data->trace)
                    fprintf(stderr, "mtftp: listening for ongoing transfer on %s port %d\n",
                            data->mtftp_mcast_ip, data->mtftp_client_port);
               number_of_timeout = 0;
               mode = LISTEN;
               if (last_block_number > 0)
               {
                    timeout = data->mtftp_listen_delay - 
                         tftp_mtftp_missed_packet(file_bitmap, last_block_number, 1);
                    if (timeout < 0)
                         timeout = 0;
               }
               else
                    timeout = data->mtftp_listen_delay;
	       state = S_WAIT_PACKET;
	       timeout_state = S_OPEN;
	       break;
	  case S_OPEN:
               if (data->trace)
                    fprintf(stderr, "mtftp: opening new connection\n");
               mode = OPEN;
               block_number = 0;
               timeout = data->mtftp_timeout_delay;
	       state = S_SEND_REQ;
	       break;
          case S_RECEIVE:
               if (data->trace)
                    fprintf(stderr, "mtftp: connected, receiving\n");
               mode = RECEIVE;
               timeout = data->mtftp_timeout_delay;
	       state = S_SEND_ACK;
               break;
          case S_SEND_REQ:
               timeout_state = S_SEND_REQ;
               if (data->trace)
               {
                    opt_options_to_string(data->tftp_options, string, MAXLEN);
                    fprintf(stderr, "sent RRQ <file: %s, mode: %s <%s>>\n",
                            data->tftp_options[OPT_FILENAME].value,
                            data->tftp_options[OPT_MODE].value,
                            string);
               }
               /* send request packet */
               if (tftp_send_request(sockfd, &sa, RRQ, data->data_buffer,
                                     data->data_buffer_size,
                                     data->tftp_options) == ERR)
                    state = S_ABORT;
               else
                    state = S_WAIT_PACKET;

               sockaddr_set_port(&sa, 0); /* must be set to 0 before the fist call to
                                   tftp_get_packet, but it was set before the
                                   call to tftp_send_request with the server port */
               break;
          case S_SEND_ACK:
               timeout_state = S_SEND_ACK;
               
               /* walk the bitmap to find the next missing block */
               //prev_bitmap_hole =
               //tftp_find_bitmap_hole(prev_bitmap_hole, file_bitmap);
               //block_number = prev_bitmap_hole;

               if (data->trace)
                    fprintf(stderr, "sent ACK <block: %d>\n", block_number);
               tftp_send_ack(sockfd, &sa, block_number);
               /* if we just ACK the last block we are done */
               if (block_number == last_block_number)
                    state = S_END;
               else
                    state = S_WAIT_PACKET;
               break;
          case S_WAIT_PACKET:
               data_size = data->data_buffer_size;
               /* receive the data */
               result = tftp_get_packet(sockfd, mcast_sockfd, &sock, &sa, &from,
					NULL, timeout, &data_size,
					data->data_buffer);
               switch (result)
               {
               case GET_TIMEOUT:
                    number_of_timeout++;
                    if (mode == LISTEN)
                    {
                         fprintf(stderr, "mtftp: timeout while listening\n");
                         state = S_OPEN;
                    }
                    else
                    {
                         fprintf(stderr, "mtftp: timeout: retrying...\n");
                         if (number_of_timeout > NB_OF_RETRY)
                              state = S_ABORT;
                         else
                              state = timeout_state;
                    }
                    break;
               case GET_ERROR:
                    if (mode == LISTEN)
                    {
                         fprintf(stderr,
                                 "mtftp: unexpected error received from server, ignoring\n");
                         break;
                    }
                    /* Can only receive this error from unicast */
                    if (!sockaddr_equal_addr(&sa, &from))
                    {
                         fprintf(stderr, "mtftp: error packet discarded from <%s>.\n",
                                 sockaddr_print_addr(&from, from_str, sizeof(from_str)));
                         break;
                    }
                    /* packet is for us */
                    fprintf(stderr, "mtftp: error received from server");
                    fwrite(tftphdr->th_msg, 1, data_size - 4 - 1, stderr);
                    fprintf(stderr, ">\n");
                    state = S_ABORT;
                    break;
               case GET_DATA:
                    /* Specification state that server source IP must matches, but
                       port is not a requierement (anyway we may not know the source
                       port yet) */
                    if (sockaddr_equal_addr(&sa, &from))
                    {
                         if (mode != LISTEN)
                         {
                              if (sock == sockfd)
                              {
                                   /* This is a unicast packet from the server. This should
                                      happend for the first data block only, when current
                                      block number is 0 and when in OPEN mode */
                                   if ((block_number > 0) || (mode != OPEN))
                                        fprintf(stderr,
                                                "mtftp: unexpected unicast packet from <%s>,"
                                                " continuing\n",
                                                sockaddr_print_addr(&from, from_str, sizeof(from_str)));
                                   else
                                        mode = RECEIVE;
                              }
                              else
                              {
                                   /* We receive data on the multicast socket, it should
                                      happend for packets 1 and above */
                                   if (block_number == 0)
                                   {
                                        mode = LISTEN;
                                        fprintf(stderr, "mtftp: got multicast data packet,"
                                                " falling back to listen mode\n");
                                   }
                              }
                         }
                         else
                         {
                              /* We are in listenning mode, we expect data on multicast
                                 socket only */
                              if (sock == sockfd)
                              {
                                   fprintf(stderr,
                                           "mtftp: unexpected unicast packet from <%s>.\n",
                                           sockaddr_print_addr(&from, from_str, sizeof(from_str)));
                                   break;
                              }
                         }
                    }
                    else
                    {
                         fprintf(stderr, "mtftp: unexpected packet from <%s>\n",
                                 sockaddr_print_addr(&from, from_str, sizeof(from_str)));
                         break;
                    }
                    number_of_timeout = 0;
                    state = S_DATA_RECEIVED;
                    break;
               case GET_DISCARD:
                    /* consider discarded packet as timeout to make sure when don't lock up
                       when doing multicast transfer and routing is broken or when using wrong
                       mcast IP or port */
                    number_of_timeout++;
                    fprintf(stderr, "mtftp: packet discard <%s>.\n",
                            sockaddr_print_addr(&from, from_str, sizeof(from_str)));
                    if (number_of_timeout > NB_OF_RETRY)
                         state = S_ABORT;
                    break;
               case ERR:
                    fprintf(stderr, "mtftp: unknown error.\n");
                    state = S_ABORT;
                    break;
               default:
                    fprintf(stderr, "mtftp: abnormal return value %d.\n",
                            result);
               }
               break;
          case S_DATA_RECEIVED:
               block_number = ntohs(tftphdr->th_block);
               if (data->trace)
                    fprintf(stderr, "received DATA <block: %d, size: %d>\n",
                            ntohs(tftphdr->th_block), data_size - 4);
               fseek(fp, (block_number - 1) * (data->data_buffer_size - 4),
                     SEEK_SET);
               if (fwrite(tftphdr->th_data, 1, data_size - 4, fp) !=
                   (data_size - 4))
               {
                    
                    fprintf(stderr, "mtftp: error writing to file %s\n",
                            data->local_file);
		    if (mode == RECEIVE)
			 tftp_send_error(sockfd, &sa, ENOSPACE, data->data_buffer,
                                         data->data_buffer_size);
                    state = S_END;
                    break;
               }
               data->file_size += data_size; /* FIXME: not usefull */
               /* Record the block number of the last block. The last block
                  is the one with less data than the transfer block size */
               if (data_size < data->data_buffer_size)
                    last_block_number = block_number;
	       /* Mark the received block in the bitmap */
	       file_bitmap[(block_number - 1)/32]
		    |= (1 << ((block_number - 1) % 32));
	       /* if we are the master client we ack, else
		  we just wait for data */
	       if (mode == LISTEN)
               {
                    /* If we've not received all packets, continue listen. In the
                       case we've not seen the last packet yet, no choice but continuing
                       listen phase and eventually fall back to the open mode and download
                       the whole file again. If we've seen the last packet, we also continue
                       listen, but if we've got all the file we are done */
                    if (last_block_number < 0)
                         state = S_WAIT_PACKET;
                    else
                    {
                         if (tftp_mtftp_missed_packet(file_bitmap, last_block_number, 0))
                              state = S_WAIT_PACKET;
                         else
                         {
                              fprintf(stderr, "mtftp: got all packets\n");
                              state = S_END;
                         }
                    }
               }
               else
		    state = S_SEND_ACK;
               break;	       
	  case S_END:
	  case S_ABORT:
               /* close file */
               if (fp)
                    fclose(fp);
               /* drop multicast membership */
               if (sa_mcast_group.ss_family == AF_INET)
                    err = setsockopt(mcast_sockfd, IPPROTO_IP,
                                     IP_DROP_MEMBERSHIP, 
                                     &mreq.v4, sizeof(mreq.v4));
               else
                    err = setsockopt(mcast_sockfd, IPPROTO_IPV6,
                                     IPV6_DROP_MEMBERSHIP,
                                     &mreq.v6, sizeof(mreq.v6));
               if (err < 0)
               {
                    perror("setsockopt");
                    exit(1);
               }
               /* close socket */
               if (mcast_sockfd)
                    close(mcast_sockfd);
               /* return proper error code */
               if (state == S_END)
                    return OK;
               else
                    fprintf(stderr, "mtftp: aborting\n");
          default:
               return ERR;
          }
     }
}
示例#3
0
/*
 * Receive 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 ACK or OACK
 *     2) wait replay
 *          - if DATA packet, read it, send an acknoledge, goto 2
 *          - if ERROR abort
 *          - if TIMEOUT goto previous state
 */
int tftpd_receive_file(struct thread_data *data)
{
     int state = S_BEGIN;
     int timeout_state = state;
     int result;
     int block_number = 0;
     int data_size;
     int sockfd = data->sockfd;
     struct sockaddr_storage *sa = &data->client_info->client;
     struct sockaddr_storage from;
     char addr_str[SOCKADDR_PRINT_ADDR_LEN];
     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 all_blocks_received = 0; /* temporary kludge */
     int convert = 0;           /* if true, do netascii convertion */

     int prev_block_number = 0; /* needed to support netascii convertion */
     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;
     }

     /* Open the file for writing. */
     if ((fp = fopen(filename, "w")) == NULL)
     {
          /* Can't create the file. */
          logger(LOG_INFO, "Can't open %s for writing", filename);
          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;
     }

     /* tsize option */
     if (((result = opt_get_tsize(data->tftp_options)) > -1) && !convert)
     {
          opt_set_tsize(result, data->tftp_options);
          logger(LOG_DEBUG, "tsize option -> %d", result);
     }

     /* 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_DEBUG, "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_DEBUG, "blksize option -> %d", result);
     }

     /* that's it, we start receiving the file */
     while (1)
     {
          if (tftpd_cancel)
          {
               logger(LOG_DEBUG, "thread cancelled");
               tftp_send_error(sockfd, sa, EUNDEF, data->data_buffer, data->data_buffer_size);
               if (data->trace)
                    logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EUNDEF,
                           tftp_errmsg[EUNDEF]);
               state = S_ABORT;
          }

          switch (state)
          {
          case S_BEGIN:
               /* Did the client request RFC1350 options ?*/
               if (opt_support_options(data->tftp_options))
                    state = S_SEND_OACK;
               else
                    state = S_SEND_ACK;
               break;
          case S_SEND_ACK:
               timeout_state = state;
               tftp_send_ack(sockfd, sa, block_number);
               if (data->trace)
                    logger(LOG_DEBUG, "sent ACK <block: %d>", block_number);
               if (all_blocks_received)
                    state = S_END;
               else
                    state = S_WAIT_PACKET;
               break;
          case S_SEND_OACK:
               timeout_state = state;
               tftp_send_oack(sockfd, sa, data->tftp_options,
                              data->data_buffer, data->data_buffer_size);
               opt_options_to_string(data->tftp_options, string, MAXLEN);
               if (data->trace)
                    logger(LOG_DEBUG, "sent OACK <%s>", string);
               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(&data->client_info->client,
                                                    addr_str, sizeof(addr_str)));
                         state = S_END;
                    }
                    else
                    {
                         logger(LOG_WARNING, "timeout: retrying...");
                         state = timeout_state;
                    }
                    break;
               case GET_ERROR:
                    /*
                     * This does not work correctly if load balancers are
                     * employed on one side of a multihomed host. ie, the
                     * tftp RRQ goes through the load balancer and has the
                     * source ports changed while the multicast traffic
                     * bypasses the load balancers.
                     *
                     * **** It is not compliant with RFC1350 to skip this
                     * **** test since the port number is the TID. Use this
                     * **** only if you know what you're doing.
                     */
                    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");
                    }
                    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);
                    state = S_ABORT;
                    break;
               case GET_DATA:
                    /* Check that source port match */
                    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");
                    }
                    number_of_timeout = 0;
                    state = S_DATA_RECEIVED;
                    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);
                    state = S_ABORT;
               }
               break;
          case S_DATA_RECEIVED:
               /* We need to seek to the right place in the file */
               block_number = ntohs(tftphdr->th_block);
               if (data->trace)
                    logger(LOG_DEBUG, "received DATA <block: %d, size: %d>",
                           block_number, data_size - 4);

               if (tftp_file_write(fp, tftphdr->th_data, data->data_buffer_size - 4, block_number,
                                   data_size - 4, convert, &prev_block_number, &temp)
                   != data_size - 4)
               {
                    logger(LOG_ERR, "%s: %d: error writing to file %s",
                           __FILE__, __LINE__, filename);
                    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]);
                    state = S_ABORT;
                    break;
               }
               if (data_size < data->data_buffer_size)
                    all_blocks_received = 1;
               else
                    all_blocks_received = 0;
               state = S_SEND_ACK;
               break;
          case S_END:
               fclose(fp);
               return OK;
          case S_ABORT:
               fclose(fp);
               return ERR;
          default:
               fclose(fp);
               logger(LOG_ERR, "%s: %d: tftpd_file.c: huh?",
                      __FILE__, __LINE__);
               return ERR;
          }
     }
}
示例#4
0
文件: tftp.c 项目: nejsimon/tftp-labb
/*
  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;
}
int tftp_get(struct net_device *dev, const unsigned char *server_ipaddr,
	     const char *filename,
	     size_t *__filesize,
	     addr_t __buf, unsigned int buflen, const char *blksize)
{
	int ret;
	int flag = 0;
	struct net_packet pkt, rx_pkt;
	unsigned char send_pbuf[1024];
	int send_pbuflen;
	unsigned char *recv_pbuf;
	unsigned int recv_pbuflen;
	unsigned short port;

	unsigned short opecode;
	unsigned short block;
	unsigned char *rcv_data;
	unsigned int rcv_data_len;
	unsigned char *filebuf = (unsigned char *)__buf;
	unsigned int filesize = 0;
	int progress = 0;
	int req_block = 1;
	int octets;
	int wait_oack = 0;
	int retry;

	if (blksize && blksize[0]) {
		octets = strtol(blksize, NULL, 0);
		wait_oack = 1;
	} else {
		octets = FIXED_BLOCK_SIZE;
	}

	dev_info_r("Filename : %s\n", filename);
	send_pbuflen = create_read_packet(filename, MODE_BIN, send_pbuf,
					  1024, blksize);
	if (send_pbuflen == 0)
		return -H_EINVAL;

	memzero(&pkt, sizeof(struct net_packet));
	pkt.f4 = &send_pbuf;
	pkt.f4len = send_pbuflen;
	ret = udp_send(dev, server_ipaddr, TFTP_PORT_NO, 30000, &pkt);
	if (ret)
		return ret;

	if (wait_oack) {
		ret = udp_proc(dev, server_ipaddr, 30000, &port, &rx_pkt);
		if (ret)
			return ret;

		recv_pbuf = rx_pkt.f4;
		recv_pbuflen = rx_pkt.f4len;

		opecode = ntohs(*(unsigned short *)(recv_pbuf + 0));
		switch (opecode) {
		case TFTP_OP_OPTION:
			tftp_send_ack(dev, server_ipaddr, port, 0);
			break;

		case TFTP_OP_ERROR:
		{
			unsigned short errcode;
			unsigned char *errmsg;
			errcode = ntohs(*(unsigned short *)(recv_pbuf + 2));
			errmsg = recv_pbuf + 4;
			dev_err("[%d]: %s\n", errcode, errmsg);
			return -H_EIO;
		}
		default:
			dev_err("Could not receive OACK.\n");
			return -H_EIO;
		}
	}

	retry = TFTP_PROC_RETRY;
	do {
		if ((progress++ % 10) == 0)
			dev_info_r("."); /* progress */

		ret = udp_proc(dev, server_ipaddr, 30000, &port, &rx_pkt);
		if (ret) {
			if (ret == -H_EAGAIN) {
				if (--retry <= 0)
					return -H_ETIMEDOUT;
				tftp_send_ack(dev, server_ipaddr, port, req_block - 1);
				continue;
			}
			return ret;
		}

		recv_pbuf = rx_pkt.f4;
		recv_pbuflen = rx_pkt.f4len;

		opecode = ntohs(*(unsigned short *)(recv_pbuf + 0));
		switch (opecode) {
		case TFTP_OP_DATA:
			block = ntohs(*(unsigned short *)(recv_pbuf + 2));

			if (req_block != block) {
				dev_dbg("receive unknown block: %d (request: %d)\n",
					req_block, block);
				tftp_send_ack(dev, server_ipaddr, port, req_block - 1);
				continue;
			} else {
				req_block++;
			}

			rcv_data_len = recv_pbuflen - 4;
			rcv_data = recv_pbuf + 4;

			tftp_send_ack(dev, server_ipaddr, port, block);

			if (rcv_data_len < octets)
				flag = 1;

			if (rcv_data_len == 0)
				break;

			if (buflen - filesize < rcv_data_len) {
				dev_err("Out of memory\n");
				return -H_EOVERFLOW;
			}

			memcpy(&filebuf[filesize], rcv_data, rcv_data_len);

			filesize += rcv_data_len;
			break;

		case TFTP_OP_ERROR:
		{
			unsigned short errcode;
			unsigned char *errmsg;
			errcode = ntohs(*(unsigned short *)(recv_pbuf + 2));
			errmsg = recv_pbuf + 4;
			dev_err("[%d]: %s\n", errcode, errmsg);
			return -H_EIO;
		}

		default:
			break;
		}
		retry = TFTP_PROC_RETRY;
	} while (flag == 0);

	dev_info_r("\nFilesize : %d\n\n", filesize);

	*__filesize = filesize;

	return 0;
}
示例#6
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;
}