Exemplo n.º 1
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;
}
Exemplo n.º 2
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;
}