luminousmen
luminousmen

Reputation: 2169

How can I send received packet using raw sockets in C?

I've wrote some code, which is not working. I need to send incoming packets to another host(2.2.2.1). This code receiving packet but can't send it. What I'm doing wrong? I'm receiving packet, than copy packet to another array and than trying to send it using sendto() function.

#include <unistd.h> 
#include <arpa/inet.h>
#include <stdio.h> 
#include <string.h> 
#include <sys/socket.h> 
#include <netinet/ip.h> 
#include <netinet/tcp.h>   

#define PCKT_LEN 8192

int main(){
    int s, s1;
    char buffer[PCKT_LEN];
    struct sockaddr saddr;
    struct sockaddr_in daddr;
    memset(buffer, 0, PCKT_LEN);

    s = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
    if(s < 0){
        printf("socket() error");
        return -1;
    }
    int one = 1;
    const int *val = &one;
    if(setsockopt (s, IPPROTO_IP, IP_HDRINCL, val, sizeof(one))<0){
        printf("setsock");
        return -1;
    }
    s1 = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
    if(s1 < 0){
        printf("sock2 error");
        return -1;
    }
    if(setsockopt (s1, IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0){
        printf("setsock");
        return -1;
    }
    int saddr_size = sizeof(saddr);
    int daddr_size = sizeof(daddr); 
    int header_size = sizeof(struct iphdr) + sizeof(struct tcphdr);
    unsigned int count;

    daddr.sin_family = AF_INET;
    daddr.sin_port = htons(1234);
    daddr.sin_addr.s_addr = inet_addr ("2.2.2.1");

    char *datagram, *data;
    datagram = (char*)calloc(4096, sizeof(char));
    struct iphdr *iph;
    struct tcphdr *tcph;
    int counter = 0;
    while(counter != 3){
        counter++;
        if(recvfrom(s, buffer, PCKT_LEN , 0, &saddr, &saddr_size) < 0){
            printf("recvfrom() error");
            return -1;
        }
        else{
            int i = header_size;
            int packet_len = ntohs(iph->tot_len);           
            memset(datagram, 0 ,4096);
            iph = (struct iphdr*) buffer;
            tcph = (struct tcphdr*) (buffer + sizeof(struct ip));
            data = buffer + sizeof(struct iphdr) + sizeof(struct tcphdr);

            i = 0;
            for(; i < packet_len-sizeof(struct iphdr)-sizeof(struct tcphdr); i++)
                printf("%c", data[i]);
                 printf("\n");

            memcpy(datagram, iph, sizeof(struct iphdr));
            memcpy(datagram+sizeof(struct iphdr), tcph , sizeof(struct tcphdr));
            memcpy(datagram+sizeof(struct iphdr)+sizeof(struct tcphdr), data,packet_len-sizeof(struct iphdr)-sizeof(struct tcphdr));

            iph->daddr = daddr.sin_addr.s_addr;

            if (sendto (s, datagram, packet_len, 0, &daddr, &daddr_size) < 0){
                printf("sendto() error");
                return -1;
            }
        }
    }
    close(s);
    close(s1);
    free(datagram);
    return 0;
}

Upvotes: 1

Views: 420

Answers (1)

Iharob Al Asimi
Iharob Al Asimi

Reputation: 53006

  1. You didn't include stdlib.h and hence calloc() is implicitly declared so the compiler assumes it returns int, and that will cause many problems.

    Also, don't use calloc() unless you really want to initialize data to 0's, because if you have a problem with data initialization, then nothing bad will happen, and you will never know. Use malloc() unless you are going to memset(pointer, 0, size); later, which isn't bad either, in my case, I always use malloc().

  2. You didn't include unistd.h either so you have a missing declaration for close() too. This one is not causing problems, but just because conicidentially it returns int.

  3. Your sendto() call is also wrong, the last argument should be of type socklen_t and you passed a int *, so fix it like this

    sendto (s, datagram, packet_len, 0, (struct sockaddr *)&daddr, daddr_size) < 0
    /*                                                            ^ no & here */
    
  4. To avoid some of the warnings that will appear when you ask the compiler to warn you you will need to declare these to variables as socklen_t

    socklen_t saddr_size = sizeof(saddr);
    socklen_t daddr_size = sizeof(daddr);
    

    and also you need the cast that you see above (struct sockaddr *)&daddr.

  5. The most important problem is that you try to access iph without initializing it

    size_t packet_len = ntohs(iph->tot_len);
    

    this should go after iph = (struct iphdr *)buffer;.

    The cause for this, is precisely that your code is so messy that it's hard to notice that.

You can avoid all this by using compiler warnings, like this

gcc -Wall -Wextra -Werror sourcefile.c -o executable

Note: organize your code better, it's very hard to follow, and that's not good if someone else has to review it, like I did just now, also for you it's not good, because when you look at it months from now, you will be very angry at the person who wrote that mess.

Upvotes: 1

Related Questions