//===========================================================================
//  @(#) $Name: cflowd-2-1-b1 $
//  @(#) $Id: flowdump.cc,v 1.13 1999/05/26 11:01:07 dwm Exp $
//===========================================================================
//  CAIDA Copyright Notice
//
//  By accessing this software, cflowd++, you are duly informed
//  of and agree to be bound by the conditions described below in this
//  notice:
//
//  This software product, cflowd++, is developed by Daniel W. McRobb, and
//  copyrighted(C) 1998 by the University of California, San Diego
//  (UCSD), with all rights reserved.  UCSD administers the CAIDA grant,
//  NCR-9711092, under which part of this code was developed.
//
//  There is no charge for cflowd++ software. You can redistribute it
//  and/or modify it under the terms of the GNU General Public License,
//  v.  2 dated June 1991 which is incorporated by reference herein.
//  cflowd++ is distributed WITHOUT ANY WARRANTY, IMPLIED OR EXPRESS, OF
//  MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE or that the use
//  of it will not infringe on any third party's intellectual property
//  rights.
//
//  You should have received a copy of the GNU GPL along with cflowd++.
//  Copies can also be obtained from:
//
//    http://www.gnu.org/copyleft/gpl.html
//
//  or by writing to:
//
//    University of California, San Diego
//
//    SDSC/CAIDA
//    9500 Gilman Dr., MS-0505
//    La Jolla, CA 92093 - 0505  USA
//
//  Or contact:
//
//    info@caida.org
//===========================================================================

extern "C" {
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
}

#include <string>
#include <fstream.h>

#include "CflowdRawFlow.hh"
#include "CflowdFlowFilter.hh"
#include "CflowdVersion.hh"

static const string         rcsid = "@(#) $Name: cflowd-2-1-b1 $ $Id: flowdump.cc,v 1.13 1999/05/26 11:01:07 dwm Exp $";
static const CflowdVersion  g_cflowdVersion = CflowdVersion(rcsid);

//-------------------------------------------------------------------------
//                      void Usage(const char *argv0)                      
//.........................................................................
//  
//-------------------------------------------------------------------------
void Usage(const char *argv0)
{
  cerr << "usage: " << argv0
       << " [-c] [-v] [-e filterExpression] [-o outFile] inFile(s)" << endl;
  cerr << "  filterExpression is evaluated for a match on each flow entry."
       << endl << endl;
  cerr << " Possible flow entry field operands:" << endl
       << "   router srcaddr dstaddr inputif outputif srcport dstport"
       << " pkts bytes" << endl
       << "   nexthop starttime endtime protocol tos srcas dstas"
       << " srcmask dstmask" << endl
       << "   tcpflags enginetype engineid all now"
       << endl << endl;
  cerr << " Possible operators:" << endl
       << "   <  <=  ==  !=  >=  >  &&  ||  &  |  !  *  /  +  -  %  (  )"
       << endl << endl;
  cerr << " Example:" << endl
       << "   flowdump -e '(protocol == 6) && (dstas == 2056) &&"
       << " ((tcpflags & 0x02) == 0x02))' 204.212.46.1.flows.1" << endl;
  
  return;
}

//-------------------------------------------------------------------------
//                     int main(int argc, char *argv[])                    
//.........................................................................
//  
//-------------------------------------------------------------------------
int main(int argc, char *argv[])
{
  int                fileNum;
  extern int         optind;
  extern char       *optarg;
  int                optChar;
  char              *filterExpression = NULL;
  ofstream          *outStream = (ofstream *)NULL;
  CflowdFlowFilter   flowFilter;
  CflowdRawFlow      rawFlow;
  bool               countOnly = false;
  uint64_t           totalFlowCount = 0;
  uint64_t           matchedFlowCount = 0;
  
  while ((optChar = getopt(argc,argv,"ce:o:v")) != EOF) {
    switch (optChar) {
      case 'c':
        countOnly = true;
        break;
      case 'e':
        filterExpression = (char *)strdup(optarg);
        break;
      case 'o':
        outStream = new ofstream(optarg);
        if (!outStream || !(*outStream)) {
          cerr << "failed to open output file '" << optarg << "': "
               << strerror(errno) << " {" << __FILE__ << ":"
               << __LINE__ << "}" << endl;
          exit(1);
        }
        break;
      case 'v':
        cerr << g_cflowdVersion.Name() << "  " << g_cflowdVersion.Id() << endl;
        exit(0);
        break;
      case '?':
      default:
        Usage(argv[0]);
        exit(1);
        break;
    }
  }

  if (! filterExpression) {
    filterExpression = (char *)strdup("all");
  }

  if (flowFilter.Compile(filterExpression) < 0) {
    Usage(argv[0]);
    exit(1);
  }
  
  if (optind == argc) {
    Usage(argv[0]);
    exit(1);
  }
  
  for (fileNum = optind; fileNum < argc; fileNum++) {
    istream  *flowStream = (ifstream *)0;
    bool flowStreamOnHeap = false;
    if (strcmp(argv[fileNum],"-") == 0) {
      flowStream = &cin;
    }
    else {
      flowStream = new ifstream(argv[fileNum]);
      flowStreamOnHeap = true;
    }
    
    if (!flowStream)
      continue;
    
    while (*flowStream) {
      rawFlow.Clear();
      rawFlow.Read(*flowStream);
      if (!(*flowStream) || rawFlow.Index() == 0) {
        break;
      }
      totalFlowCount++;
      if (flowFilter.Matches(&rawFlow)) {
        if (!countOnly) {
          if (outStream != ((ofstream *)NULL)) {
            rawFlow.Write(*outStream);
          }
          else {
            cout << rawFlow;
          }
        }
        else {
          matchedFlowCount++;
        }
      }
    }
    if (flowStreamOnHeap)
      delete(flowStream);
  }

  if (countOnly) {
    cout << "matched " << matchedFlowCount << " of " << totalFlowCount
         << " flows" << endl;
  }
  exit(0);
}
