#include "common.h"

int udp_open_tunnel_send() {
int on = 1, s;

  if((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
		ERROR("socket");

   if(setsockopt(s,IPPROTO_IP,IP_HDRINCL,(char *)&on,sizeof(on)) < 0)
		ERROR("setsockopt");

return s;
}

int udp_open_tunnel_recv() {
int s;

	if((s = socket(AF_INET, SOCK_PACKET, htons(ETH_P_IP))) < 0)
		ERROR("socket input");

return s;
}

void udp_send_tunnel(int fd, unsigned char *buffer, int length, struct sockaddr_in saddr, struct sockaddr_in daddr, struct struct_conf conf) {
struct iphdr      *iphdr;
struct udphdr     *udphdr;
unsigned char     packet[MAX_PACKET];
int               i; 
unsigned char     *c;
struct pseudohdr {
        struct in_addr saddr;
        struct in_addr daddr;
        u_char zero;
        u_char protocol;
        u_short length;
} *pseudoheader;
 
/* Clean packet */
  memset(packet, 0, MAX_PACKET);

/* Define pointers */
	iphdr = (struct iphdr *)packet;
	udphdr = (struct udphdr *)(packet+sizeof(struct iphdr));
	pseudoheader = (struct pseudohdr *)((unsigned char *)udphdr-sizeof(struct pseudohdr));
  
/* UDP packet */
	udphdr->source =             htons(conf.sport);
	udphdr->dest =               htons(conf.dport);
   memcpy((unsigned char *)udphdr+sizeof(struct udphdr), buffer, length);

	pseudoheader->saddr.s_addr = saddr.sin_addr.s_addr;
	pseudoheader->daddr.s_addr = daddr.sin_addr.s_addr;
	pseudoheader->protocol =     IPPROTO_UDP;
	udphdr->len = pseudoheader->length = htons(sizeof(struct udphdr)+length);
	udphdr->check=cksum((u_short *)pseudoheader,sizeof(struct pseudohdr)+sizeof(struct udphdr)+length);

  /* IP packet */
  memset((char *)iphdr, 0, sizeof(struct iphdr));
  iphdr->ihl =                   5;
  iphdr->version =               4;
  iphdr->ttl =                   IPDEFTTL;
  iphdr->id =                    htons(conf.id);
  iphdr->protocol =              IPPROTO_UDP;
  iphdr->saddr =                 saddr.sin_addr.s_addr;
  iphdr->daddr =                 daddr.sin_addr.s_addr;
  iphdr->frag_off =              htons(IP_DF);

  length += (sizeof(struct iphdr)+sizeof(struct udphdr));

  if(sendto(fd, packet, length, 0x0, (struct sockaddr *)&daddr, sizeof(struct sockaddr) ) != length)
    ERROR( "sendto" );
}

int udp_recv_tunnel(int fd, unsigned char *buffer, struct sockaddr_in *saddr, struct sockaddr_in *daddr, struct struct_conf conf) {
unsigned char packet[MAX_PACKET];
struct sockaddr     from;
struct iphdr        *iphdr;
struct udphdr       *udphdr;
int                 fromlen, length;

	fromlen = sizeof(from);
	if( recvfrom(fd, packet, MAX_PACKET, 0x0, (struct sockaddr *)&from, &fromlen ) <= 0 ) 
		ERROR("recvfrom");

   iphdr = (struct iphdr *)(packet+conf.ethhdrlen);
	udphdr = (struct udphdr *)(packet+conf.ethhdrlen+sizeof(struct iphdr));

	if(iphdr->protocol == IPPROTO_UDP && htons(iphdr->id) == conf.id && 
      udphdr->source == htons(conf.sport) && udphdr->dest == htons(conf.dport) ) {
			length = htons(iphdr->tot_len)-sizeof(struct iphdr)-sizeof(struct udphdr);
			memcpy(buffer, packet+conf.ethhdrlen+sizeof(struct iphdr)+sizeof(struct udphdr), length); 
         saddr->sin_addr.s_addr = iphdr->saddr;
         daddr->sin_addr.s_addr = iphdr->daddr;

	return(length);
	}

return(0);
}
