/* $Id: rtp.c,v 1.12 2003/11/29 11:16:55 aleidinger Exp $ */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#ifndef __GNUC__
# if HAVE_ALLOCA_H
#  include <alloca.h>
# else
#  ifdef _AIX
#pragma alloca
#  else
#   ifndef alloca /* predefined by HP cc +Olibcalls */
char *alloca ();
#   endif
#  endif
# endif
#endif

#include <stdio.h>

#ifdef STDC_HEADERS
# include <stdlib.h>
# include <string.h>
#else
# ifndef HAVE_STRCHR
#  define strchr index
#  define strrchr rindex
# endif
char *strchr (), *strrchr ();
# ifndef HAVE_MEMCPY
#  define memcpy(d, s, n) bcopy ((s), (d), (n))
#  define memmove(d, s, n) bcopy ((s), (d), (n))
# endif
#endif

#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#ifdef WITH_DMALLOC
#include <dmalloc.h>
#endif

struct rtpbits {
    int     sequence:16;     /* sequence number: random */
    int     pt:7;            /* payload type: 14 for MPEG audio */
    int     m:1;             /* marker: 0 */
    int     cc:4;            /* number of CSRC identifiers: 0 */
    int     x:1;             /* number of extension headers: 0 */
    int     p:1;             /* is there padding appended: 0 */
    int     v:2;             /* version: 2 */
};

struct rtpheader {           /* in network byte order */
    struct rtpbits b;
    int     timestamp;       /* start: random */
    int     ssrc;            /* random */
    int     iAudioHeader;    /* =0?! */
};

void
initrtp (struct rtpheader *foo)
{
    foo->b.v        =  2;
    foo->b.p        =  0;
    foo->b.x        =  0;
    foo->b.cc       =  0;
    foo->b.m        =  0;
    foo->b.pt       = 14;     /* MPEG Audio */
#ifdef FEFE
    foo->b.sequence = 42;
    foo->timestamp  =  0;
#else
    foo->b.sequence = rand () & 65535;
    foo->timestamp  = rand ();
#endif
    foo->ssrc       = rand ();
    foo->iAudioHeader = 0;
}

int
sendrtp (int fd, struct sockaddr_in *sSockAddr, struct rtpheader *foo,
         const void *data, int len)
{
    char   *buf = alloca (len + sizeof (struct rtpheader));
    int    *cast = (int *) foo;
    int    *outcast = (int *) buf;
    outcast[0] = htonl (cast[0]);
    outcast[1] = htonl (cast[1]);
    outcast[2] = htonl (cast[2]);
    outcast[3] = htonl (cast[3]);
    memmove (buf + sizeof (struct rtpheader), data, len);
    return sendto (fd, buf, len + sizeof (*foo), 0,
                   (struct sockaddr *) sSockAddr, sizeof (*sSockAddr));
/*  return write(fd,buf,len+sizeof(*foo))==len+sizeof(*foo); */
}

/* create a sender socket. */
int
makesocket (char *szAddr, unsigned short port, unsigned char TTL,
            struct sockaddr_in *sSockAddr)
{
    int     iRet, iLoop = 1;
    struct sockaddr_in sin;
    unsigned char    cTtl = TTL;
    char    cLoop = 0;
    unsigned int tempaddr;

    int     iSocket = socket (AF_INET, SOCK_DGRAM, 0);
    if (iSocket < 0) {
        fprintf (stderr, "socket() failed.\n");
        exit (1);
    }

    tempaddr = inet_addr (szAddr);
    sSockAddr->sin_family = sin.sin_family = AF_INET;
    sSockAddr->sin_port = sin.sin_port = htons (port);
    sSockAddr->sin_addr.s_addr = tempaddr;

    iRet = setsockopt (iSocket, SOL_SOCKET, SO_REUSEADDR, &iLoop, sizeof (int));
    if (iRet < 0) {
        fprintf (stderr, "setsockopt SO_REUSEADDR failed\n");
        exit (1);
    }

    if ((ntohl (tempaddr) >> 28) == 0xe) {
        /* only set multicast parameters for multicast destination IPs */
        iRet =
            setsockopt (iSocket, IPPROTO_IP, IP_MULTICAST_TTL, &cTtl,
                        sizeof (char));
        if (iRet < 0) {
            fprintf (stderr,
                     "setsockopt IP_MULTICAST_TTL failed.  multicast in kernel?\n");
            exit (1);
        }

        cLoop = 1;      /* !? */
        iRet = setsockopt (iSocket, IPPROTO_IP, IP_MULTICAST_LOOP,
                           &cLoop, sizeof (char));
        if (iRet < 0) {
            fprintf (stderr,
                     "setsockopt IP_MULTICAST_LOOP failed.  multicast in kernel?\n");
            exit (1);
        }
    }

    return iSocket;
}




#if 0
/* */
/* code contributed by Anonymous source.  Supposed to be much better */
/* then original code, but only seems to run on windows with MSVC.   */
/* and I cannot test it */
/* */
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

struct rtpbits {
  int sequence:16;	/* sequence number: random */
  int pt:7;	/* payload type: 14 for MPEG audio */
  int m:1;	/* marker: 0 */
  int cc:4;	/* number of CSRC identifiers: 0 */
  int x:1;	/* number of extension headers: 0 */
  int p:1;	/* is there padding appended: 0 */
  int v:2;	/* version: 2 */
};

struct rtpheader {	/* in network byte order */
  struct rtpbits b;
  int timestamp;	/* start: random */
  int ssrc;		/* random */
  int iAudioHeader;	/* =0?! */
};

void
rtp_initialization
(
    struct rtpheader *foo
)
{
  foo->b.v=2;
  foo->b.p=0;
  foo->b.x=0;
  foo->b.cc=0;
  foo->b.m=0;
  foo->b.pt=14;		/* MPEG Audio */
#ifdef FEFE
  foo->b.sequence=42;
  foo->timestamp=0;
#else
  foo->b.sequence=rand() & 65535;
  foo->timestamp=rand();
#endif
  foo->ssrc=rand();
  foo->iAudioHeader=0;
}

int
rtp_send
(
    SOCKET s,
    struct rtpheader *foo,
    void *data,
    int len
)
{
    char *buffer=malloc(len+sizeof(struct rtpheader));
    int *cast=(int *)foo;
    int *outcast=(int *)buffer;
    int count, size ;

    outcast[0]=htonl(cast[0]);
    outcast[1]=htonl(cast[1]);
    outcast[2]=htonl(cast[2]);
    outcast[3]=htonl(cast[3]);
    memmove (buffer+sizeof(struct rtpheader),data,len);
/*    return sendto (fd,buf,len+sizeof(*foo),0,(struct sockaddr *)sSockAddr,sizeof(*sSockAddr)); */
/*  return write(fd,buf,len+sizeof(*foo))==len+sizeof(*foo); */
    size = len + sizeof (*foo) ;
    count = send (s, buffer, size, 0) ;
    free (buffer) ;

    return count != size ;
}

/* create a sender socket. */
int
rtp_socket
(
    SOCKET *ps,
    char *address,
    unsigned short port,
    int TTL
)
{
/*    int iRet ; */
    int iLoop = 1 ;
/*    struct  sockaddr_in sin ; */
    char    cTTL = (char)TTL ;
    char    cLoop=0 ;
/*    unsigned int tempaddr ; */
    BOOL True = TRUE ;
    INT error ;
    char *c = "" ;
    UINT ip ;
    PHOSTENT host ;
    SOCKET s ;
    SOCKADDR_IN source, dest ;
#if 0
    int s = socket (AF_INET, SOCK_DGRAM, 0) ;
    if (s < 0)
    {
        fprintf(stderr,"socket() failed.\n");
        exit(1);
    }

    tempaddr=inet_addr(address);
    sSockAddr->sin_family = sin.sin_family = AF_INET;
    sSockAddr->sin_port = sin.sin_port = htons(port);
    sSockAddr->sin_addr.s_addr = tempaddr;

    iRet = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *) &iLoop, 
sizeof(int));
    if (iRet < 0)
    {
        fprintf(stderr,"setsockopt SO_REUSEADDR failed\n");
        exit(1);
    }

    if ((ntohl(tempaddr) >> 28) == 0xe)
    {
        /* only set multicast parameters for multicast destination IPs */
        iRet = setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &cTTL, 
sizeof(char));
        if (iRet < 0) {
            fprintf(stderr,"setsockopt IP_MULTICAST_TTL failed.    multicast in kernel?\n");
            exit(1);
        }

        cLoop = 1;	/* !? */
        iRet = setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP,
		            &cLoop, sizeof(char));
        if (iRet < 0)
        {
            fprintf(stderr,"setsockopt IP_MULTICAST_LOOP failed.    multicast in kernel?\n");
            exit(1);
        }
    }
#endif
    source.sin_family = AF_INET ;
    source.sin_addr.s_addr = htonl (INADDR_ANY) ;
    source.sin_port = htons (0) ;

    dest.sin_family = AF_INET;
    dest.sin_addr.s_addr = inet_addr (address) ;

    if (!strcmp (address, "255.255.255.255"))
    {
    }
    else if (dest.sin_addr.s_addr == INADDR_NONE)
    {
        host = gethostbyname (address) ;

        if (host)
        {
            dest.sin_addr = *(PIN_ADDR) host->h_addr ;
        }
        else
        {
	        printf ("Unknown host %s\r\n", address) ;
            return 1 ;
        }
    }

    dest.sin_port = htons ((u_short) port) ;

    ip = ntohl (dest.sin_addr.s_addr) ;

    if (IN_CLASSA(ip)) c = "class A" ;
    if (IN_CLASSB(ip)) c = "class B" ;
    if (IN_CLASSC(ip)) c = "class C" ;
    if (IN_CLASSD(ip)) c = "class D" ;
    if (ip == INADDR_LOOPBACK) c = "loopback" ;
    if (ip == INADDR_BROADCAST) c = "broadcast" ;

    s = socket (AF_INET, SOCK_DGRAM, PF_UNSPEC) ;

    if (s == INVALID_SOCKET)
    {
        error = WSAGetLastError () ;
        printf ("socket () error %d\r\n", error) ;
        return error ;
    }

    error = setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (const char *) &True, 
sizeof (BOOL)) ;

    error = bind (s, (struct sockaddr *) &source, sizeof (source)) ;

    if (error == SOCKET_ERROR)
    {
        error = WSAGetLastError () ;
        printf ("bind () error %d\r\n", error) ;
        closesocket (s) ;
        return error ;
    }

    if (ip == INADDR_BROADCAST)
    {
        printf ("broadcast %s:%u %s\r\n", inet_ntoa (dest.sin_addr), ntohs 
(dest.sin_port), c) ;

        error = setsockopt (s, SOL_SOCKET, SO_BROADCAST, (const char *) 
&True, sizeof (BOOL)) ;

        if (error == SOCKET_ERROR)
        {
            error = WSAGetLastError () ;
            printf ("setsockopt (%u, SOL_SOCKET, SO_BROADCAST, ...) error %d\r\n", s, error) ;
            closesocket (s) ;
            return error ;
        }
    }

    if (IN_CLASSD(ip))
    {
        printf ("multicast %s:%u %s\r\n", inet_ntoa (dest.sin_addr), ntohs (dest.sin_port), c) ;

/*        error = setsockopt (s, IPPROTO_IP, IP_MULTICAST_TTL, (const char *) &TTL, sizeof (int)) ; */
        error = setsockopt (s, IPPROTO_IP, 3, (const char *) &TTL, sizeof (int)) ;

        if (error == SOCKET_ERROR)
        {
            error = WSAGetLastError () ;
            printf ("setsockopt (%u, IPPROTO_IP, IP_MULTICAST_TTL, ...) error %d\r\n", s, error) ;
            closesocket (s) ;
            return error ;
        }
    }

    error = connect (s, (PSOCKADDR) &dest, sizeof (SOCKADDR_IN)) ;

    if (error == SOCKET_ERROR)
    {
        printf ("connect: error %d\n", WSAGetLastError ()) ;
        closesocket (s) ;
        return error ;
    }

    *ps = s ;

    return 0 ;
}



#endif

