sendto creating a segmentation fault when sending a packet

673 views Asked by At

I'm trying to send a packet through UDP, however I'm getting a seg fault on line 198:

sendto(socketfd, buffer_str, total_len, 0, res->ai_addr, res->ai_addrlen);

And I'm not quite sure what is causing it. I've run the program through GDB, and none of the arguments seem to have anything wrong with them. the file I'm attempting to send is just a simple txt file which has the text "Lorem ipsum dolor sit amet" in it.

The first sendto on line 76 works absolutely fine, as does the recv from on line 78. When I tried replacing the sendto on 198 with the code from like 76, I got the same segfault.

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <arpa/inet.h> 
#include <netinet/in.h>
#include <netdb.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>

struct packet {
    unsigned int total_frag;
    unsigned int frag_no;
    unsigned int size;
    char* filename;
    char filedata[1000];
};

struct node {
    struct node* next;
    struct packet data;
};

struct list {
    struct node* head;
};

int main(int argc, char *argv[]) {
    printf("ftp <file name>\n");
    
    struct sockaddr_storage their_addr;
    socklen_t addr_size = sizeof(their_addr);
    struct addrinfo hints, *res;
    char* port = "5050";//argv[2];
    struct sockaddr_in *serverAddr;
    serverAddr = malloc(sizeof(struct sockaddr_in));
    memset(&hints, 0, sizeof(hints));
    memset(serverAddr, 0, sizeof(*serverAddr));
    serverAddr->sin_family = AF_INET;
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_addr = (struct sockaddr *) serverAddr;

    getaddrinfo("ug168.eecg.utoronto.ca"/*argv[1]*/, port, &hints, &res);
    int socketfd = socket(PF_INET, SOCK_DGRAM, 0);
    char fileName[100];
    scanf("%s", fileName);
    int fd = open(fileName, O_RDWR);
    if(fd == -1) {
        return 0;
    }

    struct timeval pre_time, post_time;
    gettimeofday(&pre_time, NULL);
    sendto(socketfd, (char*)"ftp", 3, 0, res->ai_addr, res->ai_addrlen);
    char buf[1000];
    int length = recvfrom(socketfd, buf, 1000, 0, (struct sockaddr *)&serverAddr, &addr_size);
    gettimeofday(&post_time, NULL);

    buf[length] = '\0';
    if(strcmp("yes", buf) == 0) {
        printf("A file transfer can start\n");
    }
    printf("seconds : %ld\nmicro seconds : %ld\n", post_time.tv_sec - pre_time.tv_sec, post_time.tv_usec - pre_time.tv_usec);

    FILE *file;
    file = fopen(fileName, "rb"); //rb = read as binary
    fseek(file, 0, SEEK_END);
    int fileLength = ftell(file);
    fseek(file, 0, SEEK_SET);

    char buffer[fileLength];
    fread(buffer, fileLength, 1, file);
    fclose(file);
    int frag_total = fileLength / 1000;
    if(fileLength % 1000 != 0) {
        frag_total++;
    }

    struct list packet_list;
    packet_list.head = malloc(sizeof(struct node));
    struct node* curr = packet_list.head;
    for(int i = 0; i < frag_total; i++) {
        curr->data.total_frag = frag_total;
        curr->data.frag_no = i + 1;
        if(i == frag_total - 1) {
            curr->data.size = fileLength % 1000;
        } else {
            curr->data.size = 1000;
        }
        curr->data.filename = malloc(sizeof(char) * (strlen(fileName) + 1));
        strcpy(curr->data.filename, fileName);
        if(i == frag_total - 1) {
            //copy data equal to remainder
            for(int j = 0; j < fileLength % 1000; j++) {
                curr->data.filedata[j] = buffer[1000 * i + j];
            }
        } else {
            //copy 1000 bytes
            for(int j = 0; j < 1000; j++) {
                curr->data.filedata[j] = buffer[1000 * i + j];
            }
        }
        if(i < frag_total - 1) {
            curr->next = malloc(sizeof(struct node));
            curr = curr->next;
        }
    }

    curr = packet_list.head;
    while(curr != NULL) {
        int j = curr->data.total_frag;
        int frag_total_len = 0;
        while(j != 0) {
            j /= 10;
            frag_total_len++;
        }
        j = curr->data.frag_no;
        int frag_no_len = 0;
        while(j != 0) {
            j /= 10;
            frag_no_len++;
        }
        j = curr->data.size;
        int size_len = 0;
        while(j != 0) {
            j /= 10;
            size_len++;
        }
        int name_len = strlen(curr->data.filename);

        int total_len = frag_total_len + frag_no_len + size_len + name_len + 4 + curr->data.size; //4 bc 4 colons

        char buffer_str[total_len];
        j = 0;

        char strbuf[total_len];

        sprintf(strbuf, "%d", curr->data.total_frag);
        for(int k = j; k < j + frag_total_len; k++) {
           buffer_str[k] = strbuf[k-j];
        }
        j += frag_total_len;
        buffer_str[j] = ':';
        j++;

        sprintf(strbuf, "%d", curr->data.frag_no);
        for(int k = j; k < j + frag_no_len; k++) {
           buffer_str[k] = strbuf[k-j];
        }
        j += frag_no_len;
        buffer_str[j] = ':';
        j++;

        sprintf(strbuf, "%d", curr->data.size);
        for(int k = j; k < j + size_len; k++) {
           buffer_str[k] = strbuf[k-j];
        }
        j += size_len;
        buffer_str[j] = ':';
        j++;

        for(int k = 0 ; k < name_len; k++, j++) {
            buffer_str[j] = curr->data.filename[k];
        }

        buffer_str[j] = ':';
        j++;
        for(int k = 0; k < curr->data.size; k++, j++) {
            buffer_str[j] = curr->data.filedata[k];
        }

        sendto(socketfd, buffer_str, total_len, 0, res->ai_addr, res->ai_addrlen);
        curr = curr->next;
    }
    return 0;
}

I'm not really sure what could be causing this

2

There are 2 answers

2
Armali On BEST ANSWER

The local variable res is overwritten by the call

int length = recvfrom(socketfd, buf, 1000, 0, (struct sockaddr *)&serverAddr, &addr_size);

this is because the address of the pointer serverAddr is passed instead of the value of the pointer:

int length = recvfrom(socketfd, buf, 1000, 0, (struct sockaddr *)serverAddr, &addr_size);

You'll probably see that the program now segfaults for another reason: The buffer handling fails for short files with less than two fragments, because curr->next is uninitialized.

7
Milag On

The original posted code omitted error checking for getaddrinfo(), which is returning an error code and left res unchanged; that led to an uninitialized pointer being dereferenced when calling sendto(); example revision:

int rv = getaddrinfo("ug168.eecg.utoronto.ca"/*argv[1]*/, port, &hints, &res);
if (rv) {
    fprintf(stderr, "getaddrinfo error, rv %d\n", rv);
    return (1);
}