#if defined(__APPLE__)
#include <stdint.h>
#endif

#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#if defined(DMALLOC)
#include <dmalloc.h>
#endif

#include "scamper_addr.h"
#include "scamper_list.h"
#include "scamper_tlv.h"
#include "scamper_trace.h"
#include "scamper_ping.h"
#include "scamper_file.h"

static void usage()
{
  fprintf(stderr, "usage: warts-dump <file>\n");
  return;
}

static void dump_list_summary(scamper_list_t *list)
{
  if(list != NULL)
    {
      fprintf(stdout, " list id: %d", list->id);
      if(list->name != NULL)
	fprintf(stdout, ", name: %s", list->name);
      if(list->monitor != NULL)
	fprintf(stdout, ", monitor: %s", list->monitor);
      fprintf(stdout, "\n");
    }
  return;
}

static void dump_cycle_summary(scamper_cycle_t *cycle)
{
  if(cycle != NULL)
    {
      fprintf(stdout, " cycle id: %d\n", cycle->id);
    }
  return;
}

static void dump_start(struct timeval *start)
{
  time_t tt = start->tv_sec;
  char buf[32];
  memcpy(buf, ctime(&tt), 24); buf[24] = '\0';
  fprintf(stdout, " start: %s %06d\n", buf, (int)start->tv_usec);
  return;
}

static void dump_trace_hop(scamper_trace_hop_t *hop)
{
  scamper_tlv_t *tlv;
  char addr[256];

  fprintf(stdout, "hop %2d  %s\n",
	  hop->hop_probe_ttl,
	  scamper_addr_tostr(hop->hop_addr, addr, sizeof(addr)));

  fprintf(stdout, " attempt: %d, rtt: %d.%06d sec\n",
	  hop->hop_probe_id,
	  (int)hop->hop_rtt.tv_sec, (int)hop->hop_rtt.tv_usec);

  fprintf(stdout, " probe_size: %d", hop->hop_probe_size);
  if(hop->hop_flags & SCAMPER_TRACE_HOP_FLAG_REPLY_TTL)
    {
      fprintf(stdout, ", reply_ttl: %d", hop->hop_reply_ttl);
    }
  fprintf(stdout, "\n");

  if((hop->hop_flags & SCAMPER_TRACE_HOP_FLAG_TCP) == 0)
    {
      fprintf(stdout, " icmp type: %d, code: %d\n",
	      hop->hop_icmp_type, hop->hop_icmp_code);
    }
  else
    {
      fprintf(stdout, " tcp flags: 0x%02x", hop->hop_tcp_flags);
      if(hop->hop_tcp_flags != 0)
	{
	  fprintf(stdout, " (%s%s%s%s%s%s%s%s )",
		  (hop->hop_tcp_flags & 0x01) ? " fin" : "",
		  (hop->hop_tcp_flags & 0x02) ? " syn" : "",
		  (hop->hop_tcp_flags & 0x04) ? " rst" : "",
		  (hop->hop_tcp_flags & 0x08) ? " psh" : "",
		  (hop->hop_tcp_flags & 0x10) ? " ack" : "",
		  (hop->hop_tcp_flags & 0x20) ? " urg" : "",
		  (hop->hop_tcp_flags & 0x40) ? " ece" : "",
		  (hop->hop_tcp_flags & 0x80) ? " cwr" : "");
	}
      fprintf(stdout, "\n");
    }

  fprintf(stdout, " flags: 0x%02x", hop->hop_flags);
  if(hop->hop_flags != 0)
    {
      fprintf(stdout, " (");
      if(hop->hop_flags & SCAMPER_TRACE_HOP_FLAG_TS_SOCK_RX)
	fprintf(stdout, " sockrxts");
      if(hop->hop_flags & SCAMPER_TRACE_HOP_FLAG_TS_DL_TX)
	fprintf(stdout, " dltxts");
      if(hop->hop_flags & SCAMPER_TRACE_HOP_FLAG_TS_DL_RX)
	fprintf(stdout, " dlrxts");
      if(hop->hop_flags & SCAMPER_TRACE_HOP_FLAG_TS_TSC)
	fprintf(stdout, " tscrtt");
      if(hop->hop_flags & SCAMPER_TRACE_HOP_FLAG_REPLY_TTL)
	fprintf(stdout, " replyttl");
      fprintf(stdout, " )");
    }
  fprintf(stdout, "\n");

  if(hop->hop_tlvs != NULL)
    {
      for(tlv = hop->hop_tlvs; tlv != NULL; tlv = tlv->tlv_next)
	{
	  switch(tlv->tlv_type)
	    {
	    case SCAMPER_TRACE_HOP_TLV_REPLY_IPID:
	      fprintf(stdout, " ipid outer 0x%04x", tlv->tlv_val_16);
	      break;

	    case SCAMPER_TRACE_HOP_TLV_REPLY_IPTOS:
	      fprintf(stdout, " iptos outer 0x%02x", tlv->tlv_val_8);
	      break;

	    case SCAMPER_TRACE_HOP_TLV_NHMTU:
	      fprintf(stdout, " nhmtu %d", tlv->tlv_val_16);
	      break;

	    case SCAMPER_TRACE_HOP_TLV_INNER_IPTTL:
	      fprintf(stdout, " turn ttl %d", tlv->tlv_val_8);
	      break;

	    case SCAMPER_TRACE_HOP_TLV_INNER_IPLEN:
	      fprintf(stdout, " iplen inner %d", tlv->tlv_val_16);
	      break;
	    }
	}
      fprintf(stdout, "\n");
    }

  return;
}

static void dump_trace(scamper_trace_t *trace)
{
  scamper_trace_hop_t *hop;
  uint16_t i;
  char buf[256];

  scamper_addr_tostr(trace->src, buf, sizeof(buf));
  fprintf(stdout, "traceroute from %s to ", buf);
  scamper_addr_tostr(trace->dst, buf, sizeof(buf));
  fprintf(stdout, "%s\n", buf);

  dump_list_summary(trace->list);
  dump_cycle_summary(trace->cycle);
  dump_start(&trace->start);

  fprintf(stdout, " type: ");
  switch(trace->type)
    {
    case SCAMPER_TRACE_TYPE_ICMP_ECHO:
      fprintf(stdout, "icmp, echo id: %d", trace->sport);
      break;

    case SCAMPER_TRACE_TYPE_ICMP_ECHO_PARIS:
      fprintf(stdout, "icmp paris, echo id: %d", trace->sport);
      break;

    case SCAMPER_TRACE_TYPE_UDP:
      fprintf(stdout, "udp, sport: %d, base dport %d",
	      trace->sport, trace->dport);
      break;

    case SCAMPER_TRACE_TYPE_UDP_PARIS:
      fprintf(stdout, "udp paris, sport: %d, dport %d",
	      trace->sport, trace->dport);
      break;

    case SCAMPER_TRACE_TYPE_TCP:
      fprintf(stdout, "tcp, sport: %d, dport %d", trace->sport, trace->dport);
      break;

    default:
      fprintf(stdout, "%d", trace->type);
      break;
    }
  fprintf(stdout, "\n");

  fprintf(stdout, " attempts: %d, hoplimit: %d, loops: %d\n",
	  trace->attempts, trace->hoplimit, trace->loops);

  fprintf(stdout, " flags: 0x%02x", trace->flags);
  if(trace->flags != 0)
    {
      fprintf(stdout, " (");
      if(trace->flags & SCAMPER_TRACE_FLAG_ALLATTEMPTS)
	fprintf(stdout, " all_attempts");
      if(trace->flags & SCAMPER_TRACE_FLAG_PMTUD)
	fprintf(stdout, " pmtud");
      if(trace->flags & SCAMPER_TRACE_FLAG_DL)
	fprintf(stdout, " dltxts");
      fprintf(stdout, ")");
    }
  fprintf(stdout, "\n");

  fprintf(stdout, " stop reason: ");
  switch(trace->stop_reason)
    {
    case SCAMPER_TRACE_STOP_NONE:
      fprintf(stdout, "none");
      break;

    case SCAMPER_TRACE_STOP_COMPLETED:
      fprintf(stdout, "done");
      break;

    case SCAMPER_TRACE_STOP_UNREACH:
      fprintf(stdout, "icmp unreach %d", trace->stop_data);
      break;

    case SCAMPER_TRACE_STOP_ICMP:
      fprintf(stdout, "icmp type %d", trace->stop_data);
      break;

    case SCAMPER_TRACE_STOP_LOOP:
      fprintf(stdout, "loop %d", trace->stop_data);
      break;

    case SCAMPER_TRACE_STOP_DEAD:
      fprintf(stdout, "dead %d", trace->stop_data);
      break;

    case SCAMPER_TRACE_STOP_ERROR:
      fprintf(stdout, "sendto errno %d", trace->stop_data);
      break;

    case SCAMPER_TRACE_STOP_HOPLIMIT:
      fprintf(stdout, "hoplimit");
      break;

    default:
      fprintf(stdout, "reason 0x%02x data 0x%02x",
	      trace->stop_reason, trace->stop_data);
      break;
    }
  fprintf(stdout, "\n");

  for(i=0; i<trace->hop_count; i++)
    {
      for(hop = trace->hops[i]; hop != NULL; hop = hop->hop_next)
	{
	  dump_trace_hop(hop);
	}
    }

  if(trace->pmtud != NULL)
    {
      fprintf(stdout, "pmtud: ifmtu %d, pmtu %d\n",
	     trace->pmtud->ifmtu, trace->pmtud->pmtu);
      for(hop = trace->pmtud->hops; hop != NULL; hop = hop->hop_next)
	{
	  dump_trace_hop(hop);
	}
    }

  fprintf(stdout, "\n");

  scamper_trace_free(trace);

  return;
}

static void dump_ping_reply(scamper_ping_reply_t *reply)
{
  char addr[256];

  fprintf(stdout, "reply from %s, attempt: %d, rtt: %d.%06d sec\n",
	  scamper_addr_tostr(reply->addr, addr, sizeof(addr)),
	  reply->probe_id+1, (int)reply->rtt.tv_sec, (int)reply->rtt.tv_usec);

  fprintf(stdout, " size: %d", reply->reply_size);
  if(reply->flags & SCAMPER_PING_REPLY_FLAG_REPLY_TTL)
    {
      fprintf(stdout, ", ttl %d", reply->reply_ttl);
    }
  fprintf(stdout, "\n");

  fprintf(stdout, " icmp type: %d, code: %d\n",
	  reply->icmp_type, reply->icmp_code);

  return;
}

static void dump_ping(scamper_ping_t *ping)
{
  scamper_ping_reply_t *reply;
  char buf[256];
  int i;

  scamper_addr_tostr(ping->src, buf, sizeof(buf));
  fprintf(stdout, "ping from %s to ", buf);
  scamper_addr_tostr(ping->dst, buf, sizeof(buf));
  fprintf(stdout, "%s\n", buf);

  dump_list_summary(ping->list);
  dump_cycle_summary(ping->cycle);
  dump_start(&ping->start);

  fprintf(stdout, " probe count: %d, size: %d, wait: %d, ttl %d\n",
	  ping->probe_count,ping->probe_size,ping->probe_wait,ping->probe_ttl);

  fprintf(stdout, " probes sent: %d", ping->ping_sent);
  if(ping->reply_count > 0)
    {
      fprintf(stdout, ", replies requested: %d", ping->reply_count);
    }
  fprintf(stdout, "\n");

  /* dump pad bytes, if used */
  if(ping->pattern_len > 0 && ping->pattern_bytes != NULL)
    {
      fprintf(stdout, " pattern bytes (%d): ", ping->pattern_len);
      for(i=0; i<ping->pattern_len; i++)
	{
	  fprintf(stdout, "%02x", ping->pattern_bytes[i]);
	}
      fprintf(stdout, "\n");
    }

  fprintf(stdout, " stop reason: ");
  switch(ping->stop_reason)
    {
    case SCAMPER_PING_STOP_NONE:
      fprintf(stdout, "none"); break;

    case SCAMPER_PING_STOP_COMPLETED:
      fprintf(stdout, "done"); break;

    case SCAMPER_PING_STOP_ERROR:
      fprintf(stdout, "sendto errno %d", ping->stop_data); break;

    default:
      fprintf(stdout, "reason 0x%02x data 0x%02x",
	      ping->stop_reason, ping->stop_data);
      break;
    }
  fprintf(stdout, "\n");

  for(i=0; i<ping->ping_sent; i++)
    {
      for(reply = ping->ping_replies[i]; reply != NULL; reply = reply->next)
	{
	  dump_ping_reply(reply);
	}
    }

  fprintf(stdout, "\n");

  scamper_ping_free(ping);

  return;
}

static void dump_cycle(scamper_cycle_t *cycle, const char *type)
{
  time_t tt;
  char buf[32];

  if(strcmp(type, "start") == 0 || strcmp(type, "def") == 0)
    {
      tt = cycle->start_time;
    }
  else
    {
      tt = cycle->stop_time;
    }
  memcpy(buf, ctime(&tt), 24); buf[24] = '\0';

  printf("cycle %s, list %s %d, cycle %d, time %s\n",
	 type, cycle->list->name, cycle->list->id, cycle->id, buf);
  scamper_cycle_free(cycle);
  return;
}

static void dump_list(scamper_list_t *list)
{
  printf("list id %d, name %s", list->id, list->name);
  if(list->descr != NULL) printf(", descr \"%s\"", list->descr);
  printf("\n");
  scamper_list_free(list);
  return;
}

static void dump_addr(scamper_addr_t *addr)
{
  char buf[128];
  printf("addr %s\n", scamper_addr_tostr(addr, buf, sizeof(buf)));
  scamper_addr_free(addr);
  return;
}

int main(int argc, char *argv[])
{
  scamper_file_t        *file;
  scamper_file_filter_t *filter;
  uint16_t filter_types[] = {
    SCAMPER_FILE_OBJ_LIST,
    SCAMPER_FILE_OBJ_CYCLE_START,
    SCAMPER_FILE_OBJ_CYCLE_DEF,
    SCAMPER_FILE_OBJ_CYCLE_STOP,
    SCAMPER_FILE_OBJ_TRACE,
    SCAMPER_FILE_OBJ_PING,
  };
  uint16_t filter_cnt = sizeof(filter_types)/sizeof(uint16_t);
  void     *data;
  uint16_t  type;

#if defined(DMALLOC)
  free(malloc(1));
#endif

  if(argc == 1)
    {
      if((file = scamper_file_openfd(STDIN_FILENO, "-", 'r', "warts")) == NULL)
	{
	  usage();
	  fprintf(stderr, "could not use stdin\n");
	  return -1;
	}
    }
  else if(argc == 2)
    {
      if((file = scamper_file_open(argv[1], 'r', NULL)) == NULL)
	{
	  usage();
	  fprintf(stderr, "could not open %s\n", argv[1]);
	  return -1;
	}
    }
  else
    {
      usage();
      return -1;
    }

  if((filter = scamper_file_filter_alloc(filter_types, filter_cnt)) == NULL)
    {
      usage();
      fprintf(stderr, "could not alloc fitler\n");
      return -1;
    }

  while(scamper_file_read(file, filter, &type, &data) == 0)
    {
      /* hit eof */
      if(data == NULL)
	{
	  goto done;
	}

      switch(type)
	{
	case SCAMPER_FILE_OBJ_ADDR:
	  dump_addr(data);
	  break;

	case SCAMPER_FILE_OBJ_TRACE:
	  dump_trace(data);
	  break;

	case SCAMPER_FILE_OBJ_PING:
	  dump_ping(data);
	  break;

	case SCAMPER_FILE_OBJ_LIST:
	  dump_list(data);
	  break;

	case SCAMPER_FILE_OBJ_CYCLE_START:
	  dump_cycle(data, "start");
	  break;

	case SCAMPER_FILE_OBJ_CYCLE_STOP:
	  dump_cycle(data, "stop");
	  break;

	case SCAMPER_FILE_OBJ_CYCLE_DEF:
	  dump_cycle(data, "def");
	  break;
	}
    }

  scamper_file_filter_free(filter);
  scamper_file_close(file);
  fprintf(stderr, "error encountered\n");
  return -1;

 done:
  scamper_file_filter_free(filter);
  scamper_file_close(file);
  return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1