/* udp.cpp
 *
 * Copyright (C) DFS Deutsche Flugsicherung (2004, 2005). 
 * All Rights Reserved.
 *
 * UDP functions for IPv4
 * Multicast Doc: /usr/share/doc/howto/en/html/Multicast-HOWTO.html
 *
 * Version 0.2
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

 /* Socket io */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/errno.h>
#include <assert.h>

#include "udp.h"
#include "intercomd.h"

int UDP_recv_init(int port)
/* starte UDP LAN Empfang. Programm-Exit bei Fehler! */
/* return: Filedescriptor fuer Empfang */
/* in port: UDP Port Nummer */
{
  int fd;
  struct sockaddr_in sock;
  int local_flag;
  socklen_t local_flagsize;

  /* init UDP local1 */
  fd = socket(AF_INET, SOCK_DGRAM, 0);
  if (fd < 0) {
    fprintf(stderr, "if_recv_init(): socket() failed\n");
    exit(1);
  }

  memset((char *) &sock, 0, sizeof(sock));
  sock.sin_family = AF_INET;
  sock.sin_addr.s_addr = htonl(INADDR_ANY);
  sock.sin_port = htons(port);
  if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) < 0) {
    fprintf(stderr, "if_recv_init(): bind() failed\n");
    exit(2);
  }
  local_flagsize = sizeof(int);
  if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &local_flag,
      &local_flagsize) < 0) {
    fprintf(stderr, "if_recv_init(): getsockopt() failed\n");
    exit(3);
  }
  /* printf("SO_RCVBUF=%d\n", local_flag); */

  /* printf("init socket\n"); */
  return fd;
}

void UDP::send_init(char *foreign_name, int foreign_port, int local_fd_)
/* Starte Versand von UDP Meldungen auf LAN */
/* in foreign_name: IP-Adresse auf die gesendet wird */
/* in foreign_port: UDP-Port auf den gesendet wird */
{
  struct hostent *foreign_host;
  int local_flag;
  socklen_t local_flagsize;

  local_fd = local_fd_;

  /* turn on broadcast */
  local_flagsize = sizeof(int);
  if (getsockopt(local_fd, SOL_SOCKET, SO_BROADCAST,
      (char *) &local_flag, &local_flagsize) < 0) {
    fprintf(stderr, "udp_send_init(): getsockopt() failed\n");
    exit(3);
  }
  if (local_flag == 0) {
    local_flag = 1;
    setsockopt(local_fd, SOL_SOCKET, SO_BROADCAST, (char *) &local_flag,
      sizeof(int));
    local_flagsize = sizeof(int);
    if (getsockopt(local_fd, SOL_SOCKET, SO_BROADCAST,
        (char *) &local_flag, &local_flagsize) < 0) {
      fprintf(stderr, "udp_send_init() SO_BROADCAST failed\n");
      exit(3);
    }
  }

  /* init foreign part */
  memset((char *) &foreign_sock, 0, sizeof(foreign_sock));

  foreign_host = gethostbyname(foreign_name);
  if (foreign_host == NULL || foreign_host->h_length == 0) {
    fprintf(stderr, "udp_send_init(): gethostbyname() failed");
    exit(1);
  }
  memcpy(&foreign_sock.sin_addr.s_addr, foreign_host->h_addr_list[0],
    foreign_host->h_length);
  foreign_sock.sin_family = AF_INET;
  foreign_sock.sin_port = htons(foreign_port);


  in_addr_t to_ip = ntohl(foreign_sock.sin_addr.s_addr);
  char s[20];
  print_gui("c %s\n", iptoa(s, to_ip));
}

void UDP::send(char *buf, int bytes)
/* Sende UDP Meldung auf LAN */
/* in buf: Meldung */
/* in bytes: Laenge der Meldung */
{
  int len;

  len = sendto(local_fd,
    buf, bytes,
    0, (struct sockaddr *) &foreign_sock, sizeof(foreign_sock));
  if (len != bytes) {
    fprintf(stderr, "udp_send(): sendto() foreign failed ret=%d\n",
      len);
  }
}

void UDP::send_close()
{
  memset((char *) &foreign_sock, 0, sizeof(foreign_sock));
}

// IP to ASCII
char *iptoa(char *buf, in_addr_t ip)
{
  int i1 = ip >> 24;
  int i2 = (ip >> 16) & 0xFF;
  int i3 = (ip >> 8) & 0xFF;
  int i4 = ip & 0xFF;

  sprintf(buf, "%d.%d.%d.%d", i1, i2, i3, i4);

  return buf;
}

// ASCII to IP
in_addr_t atoip(char *buf)
{
  int i1, i2, i3, i4;

  sscanf(buf, "%d.%d.%d.%d", &i1, &i2, &i3, &i4);

  return i1 << 24 | i2 << 16 | i3 << 8 | i4;
}
