/*
 *   MediaMVP Server Library
 *
 *   (C) 2003 Dominic Morris
 *
 *   $Id: udp.c,v 1.7 2004/03/31 08:50:00 dom Exp $
 *   $Date: 2004/03/31 08:50:00 $
 *
 *
 *   Handles the initial UDP phase
 *
 *   Incoming protocol to 16881 - packet length is 52

4   32:  0001
2   16:  babe
2   16:  fafe
6   48:  mac address
2   16:  0000
4   32:  client address
2   16:  port to call back on
30   30:  00
==52


   Reply:

4   32:  0001
2   16:  fafe
2   16:  babe
8   64:  0000 0000 0000 0000
4   32:  client ip
2   16:  port to make tcp connection from
2   16:  0000
4   32:  server ip
2   16:  gui port (-100!)
2   16:  0000
4   32:  server ip
2   16:  control port
6   48:  0000 0000 0000
4   32:  server ip
2   16:  unknown port - 16886
===52
   Client the connects to 6337
*/


#include "libmvp_internal.h"

/* This is the breakdown of the UDP message */

typedef struct {
    uint32_t  sequence;
    uint32_t  id1;
    uint32_t  id2;
    uint8_t   mac[6];
    uint8_t   pad[2];
    uint32_t  client_addr;
    uint16_t  client_port;
    uint8_t   pad2[2];
    uint32_t  guiserv_addr;
    uint16_t  guiserv_port;
    uint8_t   pad3[2];
    uint32_t  conserv_addr;
    uint16_t  conserv_port;
    uint8_t   pad4[6];
    uint32_t  serv_addr;
    uint16_t  serv_port;
} udpprot_t;




static void          udp_read(int fd, short event, void *arg);
static void          parse_udp(udpprot_t *prot,unsigned char *buf, int len);
static void          serialise_udp(udpprot_t *prot, unsigned char *buf, int len);

static int           c_udpport      = 16881;
static struct event  listen_event;
static int           listen_sock;



int udp_init()
{

    if ( (listen_sock = udp_listen(NULL,c_udpport) ) == -1 ) {
        return -1;
    }


    event_set(&listen_event,listen_sock,EV_READ,udp_read,&listen_event);

    /* Add the event with no timeout */
    event_add(&listen_event,NULL);

    Dprintf(DEBUG,"MediaMVP director service listening on UDP port %d\n",c_udpport);

    return 0;
}

void udp_exit()
{
    event_del(&listen_event);
    close(listen_sock);
}


static void udp_read(int fd, short event, void *arg)
{
    struct event  *ev = (struct event *)arg;
    char           mac_addr[10];
    char           msg[60];
    char           buf[500];
    int            len;
    char          *desthost;
    int            destport;
    uint32_t       desthostip;
    udpprot_t      prot;
    struct in_addr in;

    /* Reschedule event */
    event_add(ev,NULL);

    len = read(fd,buf,sizeof(buf)-1);

    // dump_hex(buf,len);

    if ( len == 52 ) {
        parse_udp(&prot,(unsigned char*)buf,len);

        if ( prot.id1 != 0xbabe || prot.id2 != 0xfafe ) {
            Dprintf(DEBUG,"id1 = %04x id2=%04x\n",prot.id1,prot.id2);
            return;
        }

        prot.id1 = 0xfafe;
        prot.id2 = 0xbabe;
        memcpy(mac_addr,&prot.mac,8);
        memset(&prot.mac,0,8);
        destport = prot.client_port;
        prot.client_port = 2048;

        /* FIXME Here to support multiple clients */
        prot.guiserv_addr = ntohl(initparams->main_interfaceip);
        if ( initparams->dongle_version < 21307 ) {
            prot.guiserv_port = initparams->gui_port - 100;
        } else {
            prot.guiserv_port = initparams->gui_port;
        }
        prot.conserv_addr = ntohl(initparams->main_interfaceip);
        prot.conserv_port = initparams->stream_port;
        prot.serv_addr = ntohl(initparams->main_interfaceip);  /* No idea what this is.. */
        prot.serv_port = 16886;

        serialise_udp(&prot,(unsigned char*)msg,52);
        desthostip = ntohl(prot.client_addr);
        memcpy(&in,&desthostip,4);
        desthost = inet_ntoa(in);

        if ( dongle_authorise(mac_addr,desthostip) ) {
            Dprintf(DEBUG,"Replying to directory query from host %s\n",desthost);
            udp_send(msg,52,desthost,destport);
        } else {
            Dprintf(DEBUG,"Dongle at %s is not authorised to connect\n",desthost);
        }
    }
}


static void parse_udp(udpprot_t *prot,unsigned char *buf, int len)
{
    unsigned char  *ptr = buf;

    BUF_TO_INT32(prot->sequence,ptr);
    BUF_TO_INT16(prot->id1,ptr);
    BUF_TO_INT16(prot->id2,ptr);
    memcpy(&prot->mac,ptr,6);
    ptr += 6;
    ptr += 2;  /* Skip pad */
    BUF_TO_INT32(prot->client_addr,ptr);
    BUF_TO_INT16(prot->client_port,ptr);
    ptr += 2;  /* Skip pad */
    BUF_TO_INT32(prot->guiserv_addr,ptr);
    BUF_TO_INT16(prot->guiserv_port,ptr);
    ptr += 2;  /* Skip pad */
    BUF_TO_INT32(prot->conserv_addr,ptr);
    BUF_TO_INT16(prot->conserv_port,ptr);
    ptr += 6;  /* Skip pad */
    BUF_TO_INT32(prot->serv_addr,ptr);
    BUF_TO_INT16(prot->serv_port,ptr);
}

static void serialise_udp(udpprot_t *prot, unsigned char *buf, int len)
{
    unsigned char *ptr = buf;
    memset(buf,0,len);

    INT32_TO_BUF(prot->sequence,ptr);
    INT16_TO_BUF(prot->id1,ptr);
    INT16_TO_BUF(prot->id2,ptr);
    memcpy(&prot->mac,ptr,6);
    ptr += 6;
    ptr += 2;  /* Skip pad */
    INT32_TO_BUF(prot->client_addr,ptr);
    INT16_TO_BUF(prot->client_port,ptr);
    ptr += 2;  /* Skip pad */
    INT32_TO_BUF(prot->guiserv_addr,ptr);
    INT16_TO_BUF(prot->guiserv_port,ptr);
    ptr += 2;  /* Skip pad */
    INT32_TO_BUF(prot->conserv_addr,ptr);
    INT16_TO_BUF(prot->conserv_port,ptr);
    ptr += 6;  /* Skip pad */
    INT32_TO_BUF(prot->serv_addr,ptr);
    INT16_TO_BUF(prot->serv_port,ptr);
}



/*
 * Local Variables:
 *  indent-tabs-mode:nil
 *  require-final-newline:t
 *  c-basic-offset: 4
 * End:
 */
