// Multicast Client/Server
// written for LINUX
// Version 0.0.1
//
// Change: IP_MULTICAST_LOOP : Enable / Disable loopback for outgoing messages
//         to disable sending packets to loopback, set value of variable "one"
           to 0 (line 69)
//
// Compile : gcc -o cs cs.c
//
// This code has NOT been tested
//

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

#define MAXBUFSIZE 65536 // Max UDP Packet size is 64 Kbyte


int sender(int sock)
{
  int status, socklen;
  char buffer[MAXBUFSIZE];
  struct sockaddr_in saddr;

  // set destination multicast address
  saddr.sin_family = PF_INET;
  saddr.sin_addr.s_addr = inet_addr("239.2.11.90");
  saddr.sin_port = htons(4096);

  // put some data in buffer
  strcpy(buffer, "Hello world CS\n");

  socklen = sizeof(struct sockaddr_in);
  // receive packet from socket
  status = sendto(sock, buffer, strlen(buffer), 0,
                   (struct sockaddr *)&saddr, socklen);
  printf("Sending packet\n");
  return 0;
}

int receiver(int sock)
{
  int status;
  unsigned socklen;
  char buffer[MAXBUFSIZE];
  struct sockaddr_in saddr;

  // receive packet from socket
  printf("Receiving packet\n");
  status = recvfrom(sock, buffer, MAXBUFSIZE, 0, 
                    (struct sockaddr *)&saddr, &socklen);

  return 0;
}

int main()
{
  int sock, status;
  struct sockaddr_in saddr;
  struct in_addr iaddr;
  unsigned char ttl = 3;
  unsigned char one = 1;
  struct ip_mreq imreq;

  // set content of struct saddr and imreq to zero
  memset(&saddr, 0, sizeof(struct sockaddr_in));
  memset(&iaddr, 0, sizeof(struct in_addr));

  // open a UDP socket
  sock = socket(PF_INET, SOCK_DGRAM, 0);
  if ( sock < 0 )
    perror("Error creating socket"), exit(0);

  saddr.sin_family = PF_INET;
  saddr.sin_port = htons(4096); // Use the first free port
  saddr.sin_addr.s_addr = htonl(INADDR_ANY); // bind socket to any interface
  status = bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in));

  if ( status < 0 )
    perror("Error binding socket to interface"), exit(0);

  iaddr.s_addr = INADDR_ANY; // use DEFAULT interface
  // iaddr.s_addr = inet_addr("148.187.160.39"); // use DEFAULT interface

  // Set the outgoing interface to DEFAULT
  setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &iaddr,
             sizeof(struct in_addr));

  // Set multicast packet TTL to 3; default TTL is 1
  setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
             sizeof(unsigned char));

  // send multicast traffic to myself too
  status = setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP,
                      &one, sizeof(unsigned char));

  status = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, 
             (const void *)&imreq, sizeof(struct ip_mreq));

   imreq.imr_multiaddr.s_addr = inet_addr("239.2.11.90");
   imreq.imr_interface.s_addr = INADDR_ANY; // use DEFAULT interface

   // JOIN multicast group on default interface
   status = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, 
              (const void *)&imreq, sizeof(struct ip_mreq));

  sender(sock);
  sender(sock);
  sender(sock);
  receiver(sock);
  receiver(sock);
  receiver(sock);
  receiver(sock);
  sender(sock);

  // shutdown socket
  shutdown(sock, 2);
  // close socket
  close(sock);

  return 0;
}

