/*
Program sap_detect: listen to sap annonces on a multicast network.
SAP is the "Standard Announcement Protocol" for IP multicast streams.

Copyright (C) 2006  Eric Abouaf <neyric at via dot ecp dot fr>

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
     
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 

#include <stdio.h>       
#include <getopt.h>      

// Maximum Paquet size
#define MAXBUF 260

void usage()
{
  printf( "Usage: sap_detect [-h] [-p port] [-s server_ip] [-w wait] [-g group]\n");
  printf(" -h\t--help\t\tDisplay this help file\n" 
         " -p\t--port\t\tMulticast port to listen to\n"
         " -s\t--server\tOnly announces from the specified ip adress\n"
         " -w\t--wait\t\tNumber of sap announces to wait before quit\n"
         " -g\t--group\t\tMulticast adress of the SAP group\n" );
  exit(0);
};

int main(int argc, char *argv[]) 
{ 
  int i,j;
  int s, n, r; 
  struct sockaddr_in srv, cli; 
  struct ip_mreq mreq; 

  char buf[MAXBUF];               
  char sap_name[256];             

  // Default parameters
  int wait = 10000;
  int port = 9875;     
  unsigned char isset_server = 0;
  char groupe[16] = "224.2.127.254";  // Group SAP.MCAST.NET (specific to VIA)
  char ipaddr[16];                  

  int option;
  const char* const shortopts = "hpswg:";
  struct option longopts[] = {
                { "help",      0, NULL, 'h'},
                { "port",      1, NULL, 'p'},
                { "server",    1, NULL, 's'},
                { "wait",      1, NULL, 'w'},
                { "group",     1, NULL, 'g'},
                { NULL,        0, NULL,  0 } };

  do  
  {
     option = getopt_long( argc, argv, shortopts, longopts, NULL);
     switch (option)
     {
        case 'h': usage(); break;              
        case 'p': port = atoi(optarg); break;
        case 's':
            isset_server = 1;
            strcpy(ipaddr, optarg);
            break;
        case 'g': strcpy(groupe, optarg); break;
        case 'w': wait = atoi(optarg); break;
        case -1: break;
      };
  } while (option =! -1);


  // Socket
  bzero(&srv, sizeof(srv)); 
  srv.sin_family = AF_INET; 
  srv.sin_port = htons(port); 
  if (inet_aton(groupe, &srv.sin_addr) < 0) { 
    perror("inet_aton"); 
    return 1; 
  } 

  if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 
    perror("socket error\n"); 
    return 1; 
  }

  if (bind(s, (struct sockaddr *)&srv, sizeof(srv)) < 0) { 
    perror("bind error\n"); 
    return 1; 
  }

  if (inet_aton(groupe, &mreq.imr_multiaddr) < 0) { 
    perror("inet_aton error\n"); 
    return 1; 
  } 
  mreq.imr_interface.s_addr = htonl(INADDR_ANY); 

  if (setsockopt(s,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq)) 
      < 0) { 
    perror("setsockopt error\n"); 
    return 1; 
  }

  n = sizeof(cli); 

  while (isset_server == 0 || wait != 0) 
  { 

    if ((r = recvfrom(s, buf, MAXBUF, 0, (struct sockaddr *) 
         &cli, &n)) < 0) { 
      perror("recvfrom"); 
    } else { 
      buf[r] = 0;
 
      i = 0;
      // Look for "s="
      while( buf[i] != 's' || buf[i+1] != '=') i++;
      i+=2;     

      j = 0;
      // Get the annonce name
      while(buf[i] != 10 ) sap_name[j++] = buf[i++];
      sap_name[j] = 0;

      if(isset_server == 1)
      {
         if( strcmp( inet_ntoa(cli.sin_addr) ,ipaddr) == 0)
         {
            printf("%s\t%s\n", ipaddr, sap_name);    
            exit(0);
         }
         else wait--;
      }
      else
         fprintf(stdout, "%s\t%s\n", inet_ntoa(cli.sin_addr), sap_name);
    } 
  }


  printf("No SAP annonce from %s\n", ipaddr);
  return 0;
}