#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "net/ipv4.h"
#include "net/socket.h"
#include "msg/msg.h"
#include "relay-ctrl.h"
#include "fork.h"
#include "iopoll.h"

const char program[] = "relay-ctrl-send";
const int msg_show_pid = 1;
static char packet[512];
static long packetlen;
static long tries;
static long timeout;
static unsigned short port;

struct remote 
{
  struct remote* next;
  int rcvd;
  unsigned short port;
  ipv4addr addr;
};

static struct remote* parse_remotes(char* str)
{
  struct remote* remotes;
  const char* remote;

  remotes = 0;
  remote = strtok(str, ",");
  while (remote != 0) {
    struct remote* nr;
    if ((nr = malloc(sizeof *nr)) == 0)
      die1(111, "Could not allocate remote structure.");
    nr->next = remotes;
    if (!ipv4_parse(remote, nr->addr, &remote))
      warn3("Could not parse IP '", remote, "'.");
    else {
      nr->rcvd = 0;
      nr->port = port;
      remotes = nr;
    }
    remote = strtok(0, ",");
  }
  return remotes;
}

static void send_one(struct remote* remote, int sock)
{
  for (; remote != 0; remote = remote->next)
    if (!remote->rcvd)
      if (socket_send4(sock, packet, packetlen,
		       remote->addr, remote->port) != packetlen)
	warn3("Sending IP to '", ipv4_format(remote->addr), "' failed.");
}

static int recv_wait(struct remote* remotes, int sock)
{
  iopoll_fd io;
  io.fd = sock;
  io.events = IOPOLL_READ;
  while (iopoll(&io, 1, timeout*1000) == 1) {
    ipv4addr addr;
    unsigned short port;
    char buf[1];
    int len;
    int done;
    if ((len = socket_recv4(sock, buf, 1, addr, &port)) == 1) {
      struct remote* r;
      for (done = 1, r = remotes; r != 0; r = r->next) {
	if (memcmp(r->addr, addr, 4) == 0)
	  r->rcvd = 1;
	if (!r->rcvd) done = 0;
      }
      if (done) return 1;
    }
  }
  return 0;
}

static void send_ip(char* str)
{
  int sock;
  struct remote* remotes;
  struct remote* r;
  
  if ((remotes = parse_remotes(str)) == 0) return;
  if ((sock = socket_udp()) == -1)
    die1(111, "Could not create UDP socket.");
  if (socket_bind4(sock, IPV4ADDR_ANY, 0) == -1)
    die1(111, "Could not bind UDP socket.");

  for (; tries > 0; --tries) {
    send_one(remotes, sock);
    if (recv_wait(remotes, sock)) break;
  }
  for (r = remotes; r != 0; r = r->next)
    if (!r->rcvd)
      warn3("Timed out waiting for response from '",
	    ipv4_format(r->addr), "'.");
}

int make_packet(const char* ip)
{
  if ((packetlen = strlen(ip)) > (long)sizeof packet) return 0;
  memcpy(packet, ip, packetlen);
  return 1;
}

int main(int argc, char* argv[])
{
  char* remotes;
  const char* tmp;

  if (argc < 2) die1(111, "usage: relay-ctrl-send program [args ...]");
  if ((tmp = getenv("TCPREMOTEIP")) == 0 || (tmp = validate_ip(tmp)) == 0)
    die1(111, "Must be run from tcp-env or tcpserver.");
  if (!make_packet(tmp))
    die1(111, "Failed to build packet data");
  strcpy(packet, tmp);
  packetlen = strlen(packet);
  
  port = 0;
  if ((tmp = getenv("RELAY_CTRL_PORT")) != 0) port = atoi(tmp);
  if (port <= 0) port = DEFAULT_PORT;

  tries = 0;
  if ((tmp = getenv("RELAY_CTRL_TRIES")) != 0) tries = atoi(tmp);
  if (tries <= 0) tries = 5;
  
  timeout = 0;
  if ((tmp = getenv("RELAY_CTRL_TIMEOUT")) != 0) timeout = atoi(tmp);
  if (timeout <= 0) timeout = 1;
  
  if ((remotes = getenv("RELAY_CTRL_REMOTES")) == 0)
    warn1("$RELAY_CTRL_REMOTES is not set.");
  else 
    if (is_authenticated()) {
      switch (fork()) {
      case -1:
	warn1("Could not fork.");
	break;
      case 0: /* child */
	send_ip(remotes);
	return 0;
      }
    }
  execvp(argv[1], argv+1);
  return 111;
}


syntax highlighted by Code2HTML, v. 0.9.1