Search code examples
cmultithreadingsocketsforktftp

Issue With Timeout and Re-transmission in TFTP


I wrote a simple TFTP server that only handles read requests (RRQ) and it is working fine. The server is supposed to re-transmit the current data packet again, if no ACK is received within 5 seconds. The server should also re-transmit the packet three times before giving up. I tried to suspend the client in the middle of a transmission session to see if the server would re-transmit the data packet again and it didn't. The problem seems to be that the server doesn't continue in the while loop. I tried to test if it escapes the loop and it didn't. I really can't figure out why doesn't it iterates through the loop again.

Here's the code I've written so far...

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>

#define TIMEOUT 5000
#define RETRIES 3

void sendFile (char *Filename, char *mode, struct sockaddr_in client, int tid)
{
    struct timeval tv;
    tv.tv_sec = 5;
    char path[70] = "tmp/";
    char filebuf [1024];
    int count = 0, i;  // Number of data portions sent 
    unsigned char packetbuf[1024];
    char recvbuf[1024];
    socklen_t recv_size;

    int sock = socket(PF_INET, SOCK_DGRAM, 0);
    socklen_t optionslength = sizeof(tv);
    setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, optionslength);

    FILE *fp;
    char fullpath[200];
    strcpy(fullpath, path);
    strncat(fullpath, Filename, sizeof(fullpath) -1);
    fp = fopen(fullpath, "r");
    if (fp == NULL)
        perror("");

    memset(filebuf, 0, sizeof(filebuf));
    while (1)
    {   
        int acked = 0;
        int ssize = fread(filebuf, 1 , 512, fp);
        count++;        
        sprintf((char *) packetbuf, "%c%c%c%c", 0x00, 0x03, 0x00, 0x00);
        memcpy((char *) packetbuf + 4, filebuf, ssize);
        packetbuf[2] = (count & 0xFF00) >> 8;
        packetbuf[3] = (count & 0x00FF);

        int len = 4 + ssize;        

        memset(recvbuf, 0, 1024);
        printf("\nSending Packet #%d", count);
        sendto(sock, packetbuf, len, 0, (struct sockaddr *) &client, sizeof(client));

        for (i=0; i<3; i++)
        {
            int result = recvfrom(sock, recvbuf, 1024, 0, (struct sockaddr *) &client, &recv_size);

            if ((result == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
            {
                printf("\nRetransmitting Packet #%d",count);
                sendto(sock, packetbuf, len, 0, (struct sockaddr *) &client, sizeof(client));
            }

            else if (result == -1)
            {

            }

            else 
            {
                if (tid == ntohs(client.sin_port))
                {
                    printf("\nReceived Ack #%d",count);
                    acked++;
                    break;
                }

                else
                    continue;
            }
        }

        if (acked!=1)
        {
            puts("\nGave Up Transmission");
            break;
        }

        if (ssize != 512)
        {
            break;

        }
    }
}


int main()
{
    int udpSocket, nBytes, tid, pid, status;
    char buffer[1024], filename[200], mode[20], *bufindex, opcode;
    struct sockaddr_in serverAddr, client;
    struct sockaddr_storage serverStorage;
    socklen_t addr_size;

    udpSocket = socket(AF_INET, SOCK_DGRAM, 0);

    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(69);
    serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero); 

    bind(udpSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));
    pid = fork();

    while(1)
    {
        int client_len = sizeof(client);
        memset (buffer, 0, 1024);
        nBytes = 0;
        while (errno == EAGAIN || nBytes == 0)
        {
            waitpid(-1, &status, WNOHANG);
            nBytes = recvfrom(udpSocket,buffer,1024,0,(struct sockaddr *)&client, &client_len);

        }

        bufindex = buffer;
        bufindex++;

        // Record the client port...
        tid = ntohs(client.sin_port); 

        // Extracting the opcode from the packet...     
        opcode = *bufindex++;

        // Extracting the filename from the packet...
        strncpy(filename, bufindex, sizeof(filename)-1);

        bufindex += strlen(filename) + 1;

        // Extracting the mode from the packet...       
        strncpy(mode, bufindex, sizeof(mode)-1);

        // If we received an RRQ...
        if (opcode == 1)
        {
            puts("Received RRQ Packet");
            pid = fork();
            if (pid == 0)
            {
                sendFile(filename, mode, client, tid);
                exit(1);
            }
        }
    }   

    return 0;
}

Note: You can use the standard TFTP client that comes with linux to test the server.

Thanks in advance :)


Solution

  • Likely caused by this:

    struct timeval tv;
    tv.tv_sec = 5;
    

    tv is allocated on the stack. Stack memory is uninitialised and thus has random values in it. So you need to explicitly set tv.tv_usec to 0.