http_connect.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:62k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /*
  2. http_connect
  3. Copyright July 5, 2001, The University of North Carolina at Chapel Hill
  4. Redistribution and use in source and binary forms, with or without
  5. modification, are permitted provided that the following conditions are met:
  6.   1. Redistributions of source code must retain the above copyright notice,
  7.      this list of conditions and the following disclaimer.
  8.   2. Redistributions in binary form must reproduce the above copyright
  9.      notice, this list of conditions and the following disclaimer in the
  10.      documentation and/or other materials provided with the distribution.
  11.   3. The name of the author may not be used to endorse or promote products
  12.      derived from this software without specific prior written permission.
  13. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
  14. WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  15. MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
  16. EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  17. SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  18. PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  19. OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  20. WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  21. OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  22. ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  23. Contact person:
  24.     Frank D. Smith, University of North Carolina at Chapel Hill
  25.         email: smithfd@cs.unc.edu
  26. phone: 919-962-1884
  27. fax:   919-962-1799
  28. */
  29. /* 
  30.    This program performs an analysis of tcpdump output (the ASCII print
  31.    lines that tcpdump generates to stdout) and produces a summary of the
  32.    TCP connections used for HTTP.  It assumes that the tcpdump has been
  33.    filtered for packets that are from TCP source port 80 and that
  34.    the result has been sorted so that packets are in ascending time
  35.    order within each TCP connection.  The script given below will properly
  36.    prepare the input for this program given a tcpdump binary file that 
  37.    may contain more than just HTTP packets (the file extensions are
  38.    just examples, the program does not make any assumptions about input
  39.    file names).  Note that this filtering produces a UNI-DIRECTIONAL 
  40.    trace containing only those TCP segments sent from the server to the
  41.    client.
  42.    The output is a file with summary records for each connection (basically
  43.    the data data summarized into response and request lengths).  There is
  44.    also a <file name>.log file with summary counts.
  45.    To get usage information, invoke the program with the -h switch.
  46.    WARNING! THIS PROGRAM DEPENDS ON THE FORMAT OF THE OUTPUT OF TCPDUMP
  47.    AS PRINTED ON STDOUT (ASCII).  IT HAS BEEN TESTED ONLY ON THE 
  48.    FREEBSD VERSION OF TCPDUMP CORRESPONDING TO RELEASES UP THROUGH 4.1
  49.    KNOWN BUG: Does not process tcpdump output for ECN flags correctly.
  50. #! /bin/sh
  51. tcpdump -n -tt -r $1 tcp src port 80 > $1.http-srv
  52. sort -s -o $1.http-srv-sort +1 -2 +3 -4 +0 -1 -T /tmp $1.http-srv
  53. */
  54. #include <stdlib.h>
  55. #include <stdio.h>
  56. #include <math.h>
  57. #include <sys/time.h>
  58. #define min(a,b) ((a) <= (b) ? (a) : (b))
  59. #define max(a,b) ((a) >= (b) ? (a) : (b))
  60. int report_ACK_err = 1;
  61. void Usage(char *s)
  62. {
  63.   fprintf (stderr,"nUsage: %sn", s);
  64.   fprintf (stderr,"    [-w file_name] (name for output file)n");
  65.   fprintf (stderr,"    [-r file_name] (name for input file)n");
  66.   fprintf (stderr,"If either -w or -r is omitted, stdout(stdin) is usedn");
  67.   fprintf (stderr,"n");
  68.   exit(-1);
  69. }
  70.   FILE *dumpFP, *outFP;
  71.   FILE *logFP;
  72.   struct timeval recvTime;
  73.   struct timeval lastTime = {0,0};
  74.   char ts[20]; /* ASCII timestamp in tcpdump output */
  75.                /* max. ssssssssss.mmmmmm characters + EOL */
  76.   char sh[25]; /* ASCII source host.port in tcpdump output */
  77.                /* max. hhh.hhh.hhh.hhh.ppppp + EOL */
  78.   char gt[3];  /* ">" symbol in tcpdump output */
  79.   char lt[3];  /* "<" symbol in tcpdump output */
  80.   char dh[25]; /* ASCII destination host.port in tcpdump output */
  81.                /* max. hhh.hhh.hhh.hhh.ppppp + EOL */
  82.   char fl[5];  /* ASCII TCP flags field in tcpdump output */
  83.   char p1[50]; /* ASCII first field after flags in tcpdump output */
  84.   char p2[50]; /* ASCII second field after flags */
  85.   char p3[50]; /* ASCII third field after flags */
  86. /* These are read from the tcpdump records */
  87.   unsigned long begin_seq, end_seq, seq_bytes, new_ack;
  88.   unsigned long current_synseq;
  89.   int has_seq, has_ack;
  90. /* In TCP ACKs should be monotonically increasing so the length of the current
  91.    request in a sequence can be computed as the difference between the
  92.    ACK value marking the end of the previous request and the ACK value
  93.    marking the end of the current request.  Unfortunately, out-of-order
  94.    segments can cause ACKs to appear as if they "go backward".  For segments
  95.    without data (ACK_ONLY), simply ignoring the "backward" ACK is fine. 
  96.    In many cases where there is data along with a "backward" ACK,
  97.    this is the result of (un-necessary) retransmission of segments
  98.    containing data and those cases are handled properly.  In other cases,
  99.    the results can be incorrect for HTTP 1.1 connections.  Each case of
  100.    suspected ACK mis-ordering that might lead to erroneous request or
  101.    response lengths is noted for off-line investigation.
  102.    */
  103.   unsigned long current_request_end, last_request_end;
  104. /* In TCP segments the sequence numbers we see may NOT be monotonically
  105.    increasing (retransmission, out-of-order).  Instead we record the 
  106.    largest value seen at any point.  Then the length of the current
  107.    response in a sequence can be computed as the difference between the
  108.    (largest) sequence at the end of the previous response and the
  109.    (largest) sequence at the end of the current response.  */
  110.   unsigned long current_response_end, last_response_end;
  111. /* Various counters for logging connection summary information */
  112.   int syn_count = 0; /* connections beginning with SYN in trace */
  113.   int req_count = 0; /* number of identified requests */
  114.   int rsp_count = 0; /* number of identified responses */
  115.   int fin_count = 0; /* connections ending with FIN in trace */
  116.   int rst_count = 0; /* connections ending with Reset (no FIN) */
  117.   int trm_count = 0; /* connections ending with no Reset or FIN */
  118.   int err_count = 0; /* connections with at least one suspected error */
  119.   int act_req_count = 0; /* connections beginning in a request */
  120.   int act_rsp_count = 0; /* connections beginning in a response */
  121.   int pending_fin_count = 0; /* partial with only FIN(s) */
  122.   int pending_rst_count = 0; /* partial with only Reset(s) */
  123.   int pending_ack_count = 0; /* partial with only ACK(s) */
  124.   int pending_oth_count = 0; /* partial -- others */
  125.   int pending_cmb_count = 0; /* partials with FIN, Reset, ACK combinations */
  126. /* Various counters for events within a single connection */
  127.   /* the following apply only when state == PENDING; see pending_xxx_counts
  128.      above for explanations */
  129.   int have_pending_acks = 0;
  130.   int have_pending_fins = 0;
  131.   int have_pending_rsts = 0;
  132.   int have_pending_othr = 0;
  133.   /* the following apply only when state != PENDING */
  134.   int have_ACK_error = 0;   /* seen "backwards" ACK */
  135.   int have_value_error = 0; /* seen suspect ACK or sequence # */
  136.   int have_FINdata_error = 0; /* seen data after FIN */
  137.   enum states {PENDING, SYN_SENT, FIN_SENT, RESET, IN_REQUEST, IN_RESPONSE};
  138.   enum states connection_state = PENDING;
  139.   enum states last_state = PENDING;
  140.   enum inputs {SYN, FIN, RST, ACK_ONLY, DATA_ACK};
  141.   enum inputs input_type;
  142.   char current_src[25] = "";
  143.   char src_host[25];
  144.   char src_port[10];
  145.   char current_dst[25] = "";
  146.   char dst_host[25];
  147.   char dst_port[10];
  148. /* A request is considered to start at the timestamp on the tcpdump record
  149.    containing the first advance in the ACK field (a) following the connection
  150.    establishment or (b) while the connection is sending response data. */
  151.   char start_request_time[20];
  152. /* A response is considered to start at the timestamp on the tcpdump record
  153.    containing the first advance in the sequence number field following a
  154.    period when the ACK has been advanced (receiving request data).  */
  155.   char start_response_time[20];
  156. /* A response is considered to end at the timestamp on the tcpdump record 
  157.    that LAST advanced the data sequence number before the response ends 
  158.    (a new request starts, a FIN is sent, etc.).  Since we would have to
  159.    look ahead in the trace to be sure the current record has the LAST 
  160.    advance, we go ahead and assume it is the last and record its 
  161.    timestamp as response_end_time.  This way, when we know the sequence
  162.    number will not advance more for this response from processsing some 
  163.    subsequent record in the trace (FIN or ACK advance), the last timestamp
  164.    has already been saved.  Similarly, a request is considered to end at
  165.    the timestamp on the last record of the request. */
  166.   
  167.   char response_end_time[20];
  168.   char request_end_time[20];
  169.   char FIN_sent_time[20];   
  170.   char RST_sent_time[20];
  171.   char last_connection_time[20];
  172.   char input_name[256] = "";
  173.   char output_name[256] = "";
  174.   char log_name[256] = "";
  175.   char new_line[500];  /* assumes no tcpdump output line is longer */
  176.   long elapsed;
  177.   int new_address = 0;
  178.   int rc = 0;
  179. void error_line(char *s);  
  180. void error_state(char *s);
  181. int parse_dump_record(void);
  182. void init_connection(void);
  183. void init_active(void);
  184. void check_tuple_reuse(void);
  185. int check_ACK_advance(unsigned long old_ack);
  186. void begin_REQ(void);
  187. void more_REQ(void);
  188. void begin_RSP(void);
  189. void more_RSP(void);
  190. void log_REQ(void);
  191. void log_RSP(void);
  192. void log_SYN(void);
  193. void log_END(char *how);
  194. void log_ACT(char *how);
  195. void log_nosyn(void);
  196. void log_connection(void);
  197. void log_log(void);
  198. long elapsed_ms(char *end, char *start);
  199. void get_host_port(char *adr, char *host, char *port);
  200. int  get_sequence(char *p, unsigned long *begin, unsigned long *end,
  201.                            unsigned long *bytes);
  202. void main (int argc, char* argv[])
  203. {
  204.   int i;
  205.   /* Parse the command line */
  206.   i = 1;
  207.   while (i < argc) {
  208.     if (strcmp (argv[i], "-r") == 0) {
  209.       /* -r flag is followed by name of file to read */
  210.       if (++i >= argc) Usage (argv[0]);
  211.       strcpy (input_name, argv[i]);
  212.     }
  213.     else if (strcmp (argv[i], "-w") == 0) {
  214.       /* -w flag is followed by the name of file to write */
  215.       if (++i >= argc) Usage (argv[0]);
  216.       strcpy (output_name, argv[i]);
  217.     }
  218.     else 
  219.       Usage (argv[0]);
  220.     i++;
  221.   }
  222.   /* Open files */
  223.   /* Note: program is written to also be used as a filter */
  224.   if (strcmp(output_name, "") == 0) 
  225.     /* if no explicit output file named with -w, use stdout */
  226.      outFP = stdout;
  227.   else 
  228.      {
  229.       if ((outFP = fopen (output_name, "w")) == NULL) {
  230.           fprintf (stderr, "error opening %sn", output_name);
  231.           exit (-1);
  232.           }
  233.      }
  234.   if (strcmp(input_name, "") == 0)
  235.     /* if no explicit input file named with -r, use stdin */
  236.      dumpFP = stdin;
  237.   else 
  238.      {
  239.       if ((dumpFP = fopen (input_name, "r")) == NULL) {
  240.           fprintf (stderr, "error opening %sn", input_name);
  241.           exit (-1);
  242.          }
  243.      }
  244.   
  245.   strcpy(log_name, output_name);
  246.   strcat(log_name, ".log");
  247.   if ((logFP = fopen (log_name, "w")) == NULL) {
  248.       fprintf (stderr, "error opening %sn", log_name);
  249.       exit (-1);
  250.      }
  251.   /* begin main loop; once through loop for each line in the tcpdump */
  252.   /* a <continue> anywhere in the loop (usually after an error case)
  253.      implies beginning of processing a new tcpdump record */
  254.   while (!feof (dumpFP)) {
  255. /* printf("State is %d, last_state is %dn", connection_state, last_state); */
  256.     /* Get and parse line of tcpdump file */
  257.     fgets (new_line, sizeof(new_line), dumpFP);
  258.     /* get line pieces; this works because there are always 8 or more 
  259.        fields separated by white space in tcpdump ASCII-format lines */
  260. /*    sscanf (new_line, "%s %s %s %s %s %s %s %s %s",
  261.                       &ts, &lt, &sh, &gt, &dh, &fl, &p1, &p2, &p3);
  262. */
  263.     sscanf (new_line, "%s %s %s %s %s %s %s %s",
  264.                       &ts, &sh, &gt, &dh, &fl, &p1, &p2, &p3);
  265.    /* If any part of the connection tuple (source host.port,destination
  266.       host.port) differs from the current values, there are no more 
  267.       tcpdump records for that connection so treat as end of connection.
  268.       The action taken at the end of a connection depends on the current
  269.       state of processing in that connection */  
  270.    if ((strcmp(current_src, sh) != 0) ||  /* new source host/port */
  271.        (strcmp(current_dst, dh) != 0))    /* new dest. host/port */
  272.        {
  273.         log_connection(); 
  274.         /* begin processing this record as being from a potential new
  275.            TCP connection so initialize connection's state */
  276.         strcpy(current_src, sh);  /* new connection tuple */
  277.         strcpy(current_dst, dh);  /* host and port for src & dest */
  278.         have_pending_acks = 0;
  279.         have_pending_fins = 0;
  280.         have_pending_rsts = 0;
  281.         have_pending_othr = 0;
  282.         current_synseq = 0;
  283.         connection_state = PENDING;  /* unconnected pending a SYN */
  284.         last_state = PENDING;
  285.         new_address = 1;        /* true only for very first record
  286.                                    from a different TCP connection -- 
  287.                                    avoids multiple error messages */
  288.        }
  289.    
  290.    /* break dump record into essential data fields; initializes the
  291.       following variables: begin_seq, end_seq, seq_bytes, new_ack,
  292.                            has_ack, has_seq, input_type.
  293.       Also checks for suspect sequence and ACK values.
  294.    */
  295.    if ((rc = parse_dump_record()) < 0) 
  296.        continue;
  297.    /* processing records from a TCP connections is based on a notion of
  298.       the current "state" of the connection.  The defined states are
  299.         PENDING := record is from different connection than before but
  300.                    a beginning SYN has not yet been identified.
  301.         SYN_SENT:= have identified SYN sent from source port 80 (server)
  302.         FIN_SENT:= have identified a FIN sent from source port 80 (server)
  303.                    This terminates any data from server.
  304.         RESET   := have identified a Reset sent from source port 80 (server)
  305.                    This means that the server should not accept any more
  306.                    client data.
  307.         IN_REQUEST := processing ACKs sent from source port 80 (server)
  308.                       in response to data (request) from the client.  In
  309.                       this state, need to identify end of client data (request)
  310.                       and start of server data (response).
  311.         IN_RESPONSE := processing data sequence #s from source port 80
  312.                        In this state, need to identify the end of server data
  313.                        (response) and, possibly the beginning of a new request.
  314.       Whenever the state changes, the prior state is also noted.
  315.       Fundamental to all of this is the notion that a server cannot  
  316.       possibly be sending data in response to a request unless that
  317.       data is accompanied or preceeded by an advance in the ACK sequence
  318.       indicating receipt of the request data.  Similarly, we assume that
  319.       any new data sent by a server that follows or is accompanied by an
  320.       advance in the ACK sequence number is a response to the request 
  321.       that caused the ACK sequence to advance.  Put another way, response
  322.       data (sequence # advance) marks the end of a request and ACK 
  323.       advance marks the end of a response.  Of course other events such
  324.       as FIN or Reset can mark ends also.  The use of ACK advance to mark
  325.       the end of a response assumes that HTTP/1.1 browsers don't overlap 
  326.       requests on a single TCP connection even if they may "batch" requests, 
  327.       i.e., a new request will not be generated until the response has 
  328.       been received. If requests and responses are batched but not 
  329.       overlapped this will understate the number of objects requested 
  330.       and overstate request and response sizes.
  331.       This use of advancing sequence numbers (ACK or data) to mark
  332.       requests and responses is disturbed by segment reorderings in 
  333.       the network.  In some cases, such as reordering of only data segments
  334.       in a response, there is no problem since only the highest value seen
  335.       is used.  Reordering of ACKs (especially with data) presents real
  336.       problems since boundaries between requests and responses are missed
  337.       which can result in overstating request and response sizes.  For this
  338.       reason, all such cases of ACK misordering are logged and reported.
  339.    */
  340.    switch (connection_state)
  341.      /* a <break> anywhere ends processing of the current tcpdump record 
  342.         by ending the switch (which continues the main read loop) */
  343.       {
  344.        case PENDING:
  345.  {
  346.           switch (input_type)
  347.              {
  348.      case FIN:
  349.         {
  350.  /* Ignore random FIN before something useful */ 
  351.                  have_pending_fins++;
  352.                  break;
  353.                 }
  354.      case SYN:
  355.         {
  356.   /* normal connection start, initialize connection state */
  357.                  init_connection();
  358.                  break;
  359. }
  360.      case RST:
  361.                 {
  362.   /* Ignore random Reset before something useful */
  363.                  have_pending_rsts++;
  364.                  break;
  365.                 }
  366.              /* The trace may start after a connection is established and we
  367.                 do not see a SYN.  Determine if the connection is most likely
  368.                 in a request or in a response and log its status.  As before,
  369.                 a request is indicated if the ACK sequence (after the first
  370.                 one in the trace) is advancing and a response is indicated if
  371.                 the data sequence number is advancing. */
  372.              case ACK_ONLY:
  373.         {
  374.                   /* ignore initial ACK (has absolute value, not relative) */
  375.                  if (new_address == 1)
  376.    {
  377.                     new_address = 0;
  378.                     have_pending_acks++;
  379.                    }
  380.                  else
  381.     {
  382.      /* If ACK advances, the connection is in a request */
  383.                      if ((new_ack > 2) &&
  384.                          (new_ack < 16384))
  385.         {
  386.                          log_ACT("REQ");
  387.                          last_request_end = 1;
  388.                          current_request_end = new_ack;
  389.                          last_response_end = 1;
  390.                          current_response_end = 1;
  391.                        /* record the request start time as beginning at the 
  392.                           tcpdump time stamp on this record */
  393.                           strcpy(start_request_time, ts);
  394.                           strcpy(request_end_time, ts);
  395.                           last_state = connection_state;
  396.                           connection_state = IN_REQUEST;
  397.  
  398.                           init_active();  /* initialize connection state */
  399.  }
  400.                      else /* ignore ACKs that are not advancing */
  401.                         have_pending_acks++;
  402.     }
  403.                  break;
  404.                 }
  405.      case DATA_ACK:          
  406.         {
  407.  /* If data sequence advances, connection is in response */
  408.                  if ((seq_bytes > 1) &&
  409.                      (seq_bytes < 65535))
  410.     {
  411.                      log_ACT("RSP");
  412.                      /* assume tcpdump relative addressing and initialize */
  413.                      last_request_end = 1;    
  414.                      current_request_end = 1;
  415.                      last_response_end = 0;     
  416.                      current_response_end = seq_bytes;
  417.                      /* record timestamp of tcpdump record as current
  418.                         value of both start and end times of response
  419.                         (in case no later end time is found) */
  420.                      strcpy(start_response_time, ts); 
  421.                      strcpy(response_end_time, ts);
  422.                      last_state = connection_state;
  423.                      connection_state = IN_RESPONSE;
  424.  
  425.                      init_active();
  426.                     }
  427.                  else /* ignore data lengths of 0 or 1 */
  428.                     have_pending_othr++;
  429.                  break;
  430.                 }
  431.      default:
  432.                 break;
  433.              }  /* end switch on input_type */
  434.           break;
  435.  } /* end case PENDING */
  436.        case SYN_SENT:
  437.          {
  438.          /* Treat this case as the establishment of a connection.  Usually
  439.             the first activity on the connection will be request data from
  440.             the client, but some servers appear to "pre-send" data (maybe
  441.             the headers) speculatively before ACKing any client data. */
  442.           switch (input_type)
  443.              {
  444.      case FIN:
  445.               /* A FIN marks the end of either or both request and response */
  446.         {
  447.                  if ((has_ack == 1) &&
  448.                      (new_ack > (current_request_end + 1)))
  449.                                /* ignore possible ACK of FIN */
  450.            { /* this record had advanced the ACK sequence so
  451.                         save current request data and log it (since
  452.                         the FIN ends the connection it also ends the 
  453.                         request). */
  454.                
  455.                      begin_REQ();
  456.                      log_REQ();
  457.                     }
  458.                  /* If the data sequence number advances, then there was
  459.                     response data.  Save the current response info. and
  460.                     log it (since the FIN ends the connection, it also
  461.                     ends the response). */
  462.                  if ((has_seq == 1) &&
  463.                      (end_seq > current_response_end))  
  464.     {
  465.                      begin_RSP();
  466.                      log_RSP();
  467.                     }
  468.                  last_state = SYN_SENT;
  469.                  connection_state = FIN_SENT;
  470.                  /* record timestamp of first FIN seen on connection */
  471.                  if (strcmp(FIN_sent_time, "") == 0)
  472.                     strcpy(FIN_sent_time, ts);
  473.                  break;
  474.                 }
  475.      case SYN:
  476.         {
  477.   /* In some cases the same host/port pairs are reused in a
  478.                      single trace; check for plausible reuse */
  479.                  check_tuple_reuse();
  480.                  break;
  481. }
  482.      case RST:
  483.                 {
  484.                  connection_state = RESET;
  485.                  last_state = SYN_SENT;
  486.                  /* record timestamp of first Reset on the connection */
  487.                  if (strcmp(RST_sent_time, "") == 0)
  488.                     strcpy(RST_sent_time, ts);
  489.                  break;
  490.                 }
  491.              case ACK_ONLY:
  492.         {
  493.                  /* since there is no data sequence # present in the record
  494.                     and, therefore, the server is not sending data so
  495.                     all the client's data may not have arrived.  Note the
  496.                     current extent of ACKed client data and change state */
  497.                  if (new_ack > (current_request_end + 1))
  498.                     begin_REQ();
  499.                  break;
  500.                 }
  501.      case DATA_ACK:          
  502.         {
  503.                 /* check for presence of data sequence # in the common cases
  504.                    in SYN_SENT state, data presence indicates that 
  505.                    request data has been received and server is sending data
  506.                    that should be a response */
  507.                  if (new_ack > (current_request_end + 1)) 
  508.              {/* this record had advanced the ACK sequence so
  509.                      save current request info. and change state */
  510.                      begin_REQ();
  511.                      /* the server's data sequence may not have advanced;
  512.                         but if it has, treat it as the start of a response
  513.                         and the end of the client (request) data */
  514.                      if (end_seq > current_response_end)
  515.         {
  516.                           /* start of response ends the request, log
  517.                           it and change state to look for end of response */  
  518.                          log_REQ();
  519.                          begin_RSP();
  520.         }
  521.     }
  522.                  else 
  523.    /* some servers appear to send response data immediately
  524.                       on completing the TCP connection without receiving 
  525.                       any request data */
  526.                     if ((end_seq > last_response_end) && 
  527.                         (seq_bytes > 0))
  528.                        begin_RSP();
  529.                 break;
  530. }
  531.      default:
  532.                 break;
  533.      } /* end switch on input type */
  534.           break;
  535.  } /* end case SYN_SENT */
  536.        case FIN_SENT:
  537.   {
  538.            switch (input_type)
  539.               {
  540.        case SYN:
  541.           {
  542.   /* In some cases the same host/port pairs are reused in a
  543.                      single trace; check for plausible reuse */
  544.                    check_tuple_reuse();
  545.                    break;
  546.   }
  547.         case FIN:  /* ignore multiple FINs */
  548.                    break;
  549.                 case RST:  /* ignore reset after FIN */
  550.                    break;
  551.         case ACK_ONLY:
  552.                     /* If there is an ACK (only) coming after a FIN is sent, it
  553.                       is ether the normal one in the 4-way termination or it
  554.                       is to ACK more request data which will be ignored (there
  555.                       can be no response).  
  556.                       In either case, it is ignored here. */
  557.                    break;
  558.         case DATA_ACK:
  559.   {
  560.               /* All others may have sequence number fields.  Treat them as
  561.                  errors if they advance the sequence number after a FIN.  
  562.                  The usual case here is just retransmissions (including 
  563.                  retransmission of the FIN) which should not advance it
  564.                  because the highest sequence number possible was on the FIN */
  565.                    if ((end_seq > (current_response_end + 2)) &&
  566.                        (have_FINdata_error == 0))
  567.       {
  568.                        error_state("new data in FIN_SENT state");
  569.                        have_FINdata_error = 1;
  570.                       }
  571.                    break;
  572.   }
  573.         default:
  574.                    break;
  575.       } /* end switch on input type */
  576.             break; 
  577.   } /* end case FIN_SENT */
  578.        case RESET:
  579.    {
  580.     /* A Reset nominally means an abnormal close of the connection 
  581.                with any queued data discarded before transmitting it.  We
  582.                observe, however, that under some (unknown) circumstances
  583.                segments that advance the data sequence number continue in 
  584.                the trace after the Reset.  If these are part of a response, 
  585.                it makes sense to count them in the response size since 
  586.                the server obviously sent them and the network has to handle 
  587.                them.  This also means that a FIN following the Reset may be
  588.                a valid indication of the end of a response. Just ignore 
  589.                anything else unexpected (e.g., Reset) */
  590.             switch (input_type)
  591.                {
  592.         case RST:
  593.                    break;
  594.         case SYN:
  595.           {
  596.   /* In some cases the same host/port pairs are reused in a
  597.                      single trace; check for plausible reuse */
  598.                    check_tuple_reuse();
  599.                    break;
  600.   }
  601.         case ACK_ONLY:
  602.                /* an ACK might advance possibly indicating a request.  However,
  603.                   since the connection is Reset and there should be no new
  604.                   response, just ignore it */
  605.                     break;
  606.         case FIN:
  607.   {
  608.             /* Only if the state is IN_RESPONSE do we try to continue looking
  609.                for the end of the response.  FIN is treated as the true end
  610.                of a response. */
  611.                    if (last_state == IN_RESPONSE)
  612.                {/* ends the current response, ignore any ACK */
  613.                        if ((has_seq == 1) &&
  614.                            (end_seq > current_response_end)) 
  615.                           more_RSP();
  616.                        log_RSP();
  617.   
  618.                        last_state = RESET;
  619.                        connection_state = FIN_SENT;
  620.                        /* record timestamp of first FIN on connection */
  621.                        if (strcmp(FIN_sent_time, "") == 0)
  622.                           strcpy(FIN_sent_time, ts);
  623.       }
  624.                     break;
  625.    }
  626.        case DATA_ACK:
  627.                  {
  628.                 /* segments with data may advance the data sequence number as 
  629.                    more parts of the response.  They may also advance the ACK
  630.                    which normally would indicate a new request.  However,
  631.                    after a Reset, it probably will be ignored by the 
  632.                    server so it is also ignored here except to mark the end
  633.                    of the response. */
  634.                    if (last_state == IN_RESPONSE)
  635.       {
  636.                        if (new_ack > (last_request_end + 1)) /* new request */
  637.                           { /* implies end of current response */
  638.                            log_RSP(); 
  639.                            last_state = RESET; /* will ignore all else */
  640.                            break; 
  641.                           }
  642.  
  643.                      /* As long as the data sequence # advances, continue to
  644.                         save info about the current response. */
  645.                        if ((end_seq > current_response_end) &&
  646.                            (seq_bytes > 0))
  647.                           more_RSP();
  648.                       }
  649.                   break;
  650.  }
  651.                default:
  652.                   break;
  653.        } /* end switch on input type */
  654.             break;
  655.            } /* end case RESET */
  656.        case IN_RESPONSE:
  657.    {
  658.             /* In this state, look for events that will indicate the end of
  659.                the response data (ACK advances for request, FIN, Reset).  If
  660.                the event is ACK advance, this also initiates the start of a
  661.                new request.  */
  662.             switch (input_type)
  663.                {
  664.         case FIN:
  665.   {         
  666.                    /* FIN is complicated since the segment that carries it may
  667.                     also advance the ACK (new request), advance the data
  668.                     sequence # and end the connection.  If the ACK advances,
  669.                     any sequence # advance is for the new request; otherwise
  670.                     a sequence # advance extends and completes the current 
  671.                     response. */
  672.                    if (has_ack == 1) 
  673.       {
  674.        if ((rc = check_ACK_advance(current_request_end)) < 0)
  675.                           break;
  676.                       }
  677.    /* The ACK advance amount has to be greater than 1 to be
  678.                      considered a real new request.  This is primarily to
  679.                     filter out just an ACK for a FIN from the client.  Note
  680.                     the ACK must advance so duplicate ACKs are ignored. */
  681.                    if ((has_ack == 1) &&
  682.                        (new_ack > (current_request_end + 1)))
  683.                      { /* implies end of current response, begin request 
  684.                            Log the info. for the current response. */
  685.                        log_RSP();  /*end of previous response */
  686.                        /* on FIN, assume end of request also since server is
  687.                           closing the connection. */
  688.                        begin_REQ();
  689.                        log_REQ(); 
  690.                        /* if the data sequence # also advances, this segment
  691.                           (only -- because of the FIN) carries the last part
  692.                           of the response */
  693.  
  694.                        if ((has_seq == 1) &&
  695.                            (end_seq > last_response_end)) 
  696.                           {/* response begins and ends in the FIN segment */
  697.                            begin_RSP();
  698.                            log_RSP();  /* also end of any response */
  699.                           }
  700.                        /* Note that the null "else" here is the case that the
  701.                           data sequence # does not advance (there is no 
  702.                           response to the request) -- probably an HTTP/1.1
  703.                           broswer attempting multiple requests on a TCP 
  704.                           connection where the server doesn't play nice. */
  705.                       }
  706.                    else 
  707.                       /* the ACK did not advance so no request; if the data
  708.                        sequence # advances, it extends the current response */
  709.       {
  710.                        if ((has_seq == 1) &&
  711.                            (end_seq > current_response_end)) 
  712.                           more_RSP();
  713.                        log_RSP();    /* FIN always implies end of response */
  714.                       }
  715.                    last_state = IN_RESPONSE;
  716.                    connection_state = FIN_SENT;
  717.                    if (strcmp(FIN_sent_time, "") == 0)
  718.                       strcpy(FIN_sent_time, ts);
  719.                    break;
  720.   }
  721.                case RST:
  722.                  /* Look for a Reset and only change state to RESET to
  723.                    continue processing.  This is explained in comments in 
  724.                    processing for the RESET state when the last_state is
  725.                    IN_RESPONSE. */
  726.            {/* ignore any advance in seq# on Reset */
  727.                   /* it may not be wise to trust ACK on RST */
  728.                   last_state = IN_RESPONSE;
  729.                   connection_state = RESET;   
  730.                   if (strcmp(RST_sent_time, "") == 0)
  731.                      strcpy(RST_sent_time, ts);
  732.                   break;
  733.                  }
  734.                case SYN:
  735.            {
  736.   /* In some cases the same host/port pairs are reused in a
  737.                      single trace; check for plausible reuse */
  738.                   check_tuple_reuse();
  739.                   break;
  740.  }
  741.                case ACK_ONLY:
  742.             /* Begin checking for cases where the ACK may advance
  743.                     and really indicate that a new request starts. */
  744.                  {
  745.     /* The ACK advance amount has to be greater than 1 to be
  746.                      considered a real new request.  This is primarily to
  747.                      filter out just an ACK for a FIN from the client.  Note
  748.                      the ACK must advance so duplicate or OOO
  749.                      ACKs are ignored. */
  750.                   if (new_ack > (last_request_end + 1)) 
  751.      { /* implies end of current response, begin request.
  752.                          Log the info. for the current response and change
  753.                          states. */
  754.                       log_RSP();
  755.                     /* since there is no data sequence # present in the record
  756.                     and, therefore, the server is not sending data so
  757.                     all the client's data may not have arrived.  Note the
  758.                     current extent of ACKed client data and start time */
  759.                       begin_REQ();
  760.                      }
  761.                   break;
  762.                  }
  763.                case DATA_ACK:
  764.             /* cases with data and ACKs have two important sub-cases: (a) 
  765.                ACK sequence number advances -- implies the start of a
  766.                new request and the end of the current response; if the
  767.                data sequence number also advances, it is assumed that
  768.                all the request data has been received and the first of
  769.                the response data is contained in the segment. (b) the
  770.                ACK sequence number does not advance so any advance in
  771.                the data sequence number is more of the current response. */
  772.        {
  773. if ((rc = check_ACK_advance(last_request_end)) < 0)
  774.                    break;
  775.  /* The ACK advance amount has to be greater than 1 to be
  776.                     considered a real new request.  This is primarily to
  777.                     filter out just an ACK for a FIN from the client.  Note
  778.                     the ACK must advance so duplicate ACKs are ignored. */
  779.                 if (new_ack > (last_request_end + 1)) 
  780.            { /* implies end of current response, begin request 
  781.                          Log the info. for the current response. */
  782.                     log_RSP();  
  783.                     begin_REQ();
  784.                     /* If there is also new data (sequence # advances), then
  785.                        the request is considered completed and a new response
  786.                        has started but not necessarily completed. */
  787.                     if ((end_seq > current_response_end) &&
  788.                         (seq_bytes > 0))
  789.        {/* Log info. about this request */
  790.                         log_REQ();
  791.                         begin_RSP();
  792.        }
  793.    }
  794.                 else
  795.                     /* the ACK did not advance so no request; if the data
  796.                        sequence # advances, it extends the current response */
  797.                     if (end_seq > current_response_end) 
  798.                        more_RSP();
  799.                 break;
  800.                }
  801.      default:
  802.                 break;
  803.     } /* end switch on input type */
  804.            break;
  805.   }  /* end case IN_RESPONSE */
  806.        case IN_REQUEST:
  807.    {
  808.     /* In this state check for events that indicate the end of the
  809.                request data (advance of data sequence #, FIN, Reset).  An
  810.                advance in the data sequence # also marks the beginning of
  811.                the response to the request */
  812.             switch (input_type)
  813.                {
  814.         case FIN:
  815.             /* FIN is somewhat less complicated here since the segment that
  816.                implies that the server will send no more data.  Treat this
  817.                as effectively ending the request.  If the ACK advances, it
  818.                is considered as part of the request and if the data 
  819.                sequence # advances, it is the response data (all in this
  820.                segment). */
  821.         { /* FIN can ACK more of current request */
  822.   /* The ACK advance amount has to be greater than 1 to be
  823.                     considered as extending the request.  This is primarily 
  824.                     to filter out just an ACK for a FIN from the client.  Note
  825.                     the ACK must advance so duplicate ACKs are ignored. */
  826.                  if ((has_ack == 1) &&
  827.                      (new_ack > (current_request_end + 1)))
  828.                     more_REQ();
  829.                  else
  830.                     if (has_ack == 1)
  831.        {
  832.         if ((rc = check_ACK_advance(current_request_end)) < 0)
  833.                            break;
  834.                        }
  835.                  log_REQ();  /* on FIN, assume end of request */
  836.                 /* the server's data sequence may not have advanced;
  837.                    but if it has, treat it as the complete response */
  838.                  if ((has_seq == 1) &&
  839.                      (end_seq > last_response_end))  
  840.                     {
  841.                      begin_RSP();
  842.                      log_RSP();  /* also end of any response */
  843.                     }
  844.                  last_state = IN_REQUEST;
  845.                  connection_state = FIN_SENT;
  846.                  if (strcmp(FIN_sent_time, "") == 0)
  847.                     strcpy(FIN_sent_time, ts);
  848.                  break;
  849. }
  850.      case RST:
  851.                {
  852.                 /* Treat a Reset as ending the request with no response */
  853.         /* ignore any advance in ack on Reset */
  854. /* treat as ending any request */
  855.                 log_REQ();                
  856.                 last_state = IN_REQUEST;
  857.                 connection_state = RESET;
  858.                 if (strcmp(RST_sent_time, "") == 0)
  859.                    strcpy(RST_sent_time, ts);
  860.                 break;
  861.        }
  862.      case SYN:
  863.         {
  864.  /* In some cases the same host/port pairs are reused in a
  865.                     single trace; check for plausible reuse */
  866.                  check_tuple_reuse();
  867.                  break;
  868. }
  869.      case ACK_ONLY:
  870.         {
  871.  /* The ACK advance amount has to be greater than 1 to be
  872.                     considered as extending the requesst.  This is primarily 
  873.                     to filter out just an ACK for a FIN from the client.  Note
  874.                     the ACK must advance so duplicate or OOO 
  875.                     ACKs are ignored. */
  876.                  if (new_ack > (current_request_end + 1))
  877.                     more_REQ();
  878.                  break;
  879.                 }
  880.      case DATA_ACK:
  881.                {
  882.                 /* cases with data and ACKs have two important sub-cases: 
  883.                   (a) ACK sequence number advances which extends the amount
  884.                   of request data, and (b) the data sequence # advances
  885.                   which ends the request and is the beginning of the 
  886.                   response to that request. */
  887. /* The ACK advance amount has to be greater than 1 to be
  888.                    considered as extending the request.  This is primarily 
  889.                    to filter out just an ACK for a FIN from the client.  Note
  890.                    the ACK must advance so duplicate ACKs are ignored. */
  891.                 if (new_ack > (current_request_end + 1))
  892.                    more_REQ();
  893.                 else
  894.     if ((rc = check_ACK_advance(current_request_end)) < 0)
  895.                        break;
  896.                 /* the server's data sequence may not have advanced;
  897.                    but if it has, treat it as the start of a response
  898.                    and the end of the request data */
  899.                 if ((end_seq > last_response_end) && 
  900.                     (seq_bytes > 0))
  901.                    {
  902.     /* record info. for current request and change state 
  903.                        to look for the end of the response*/
  904.                     log_REQ();
  905.                     begin_RSP();
  906.                    }
  907.                 break;
  908.                }
  909.      default:
  910.                 break;
  911.     } /* end switch on input type */ 
  912.           break;
  913.  }  /* end case IN_REQUEST */
  914.        default:
  915.        break;
  916.       } /* end switch on connection state */
  917.    /* save the last known time for the connection from current record */
  918.    strcpy(last_connection_time, ts); 
  919.  }  /* end main loop */  
  920.   log_log();
  921.   close (dumpFP);
  922.   close (outFP);
  923.   close (logFP);
  924. } /* end main() */
  925. /* Initialize connection state when a new connection is recognized by
  926.    the presence of a SYN. */ 
  927. void init_connection(void)
  928. {
  929.  log_SYN();                /* log start of connection */
  930.  connection_state = SYN_SENT;  /* new connection state */
  931.  /* save SYN sequence number for duplicate detection */
  932.  if (has_seq == 1)
  933.      current_synseq = begin_seq;
  934.  else
  935.      error_line ("SYN without valid sequence #");
  936.  /* assume tcpdump relative addressing and initialize */
  937.  /* note that initializing to 1 instead of 0 adjusts for ACK
  938.     being the next expected sequence number */
  939.  last_request_end = 1;       /* need "last" and "current" */
  940.  last_response_end = 1;      /* values since there may be */
  941.  current_response_end = 1;   /* > 1 request per connection */
  942.  current_request_end = 1;
  943.  strcpy(FIN_sent_time, "");
  944.  strcpy(RST_sent_time, "");
  945.  strcpy(last_connection_time, "");
  946.  have_ACK_error = 0;
  947.  have_value_error = 0;
  948.  have_FINdata_error = 0;
  949. }
  950. /* Initialize connection state when a new connection is recognized by a
  951.    change in the host/port 4-tuple, there is no SYN but request or 
  952.    response activity can be determined.  Request or response state is
  953.    initialized in the main program when the type of activity is determined */ 
  954. void init_active(void)
  955. {
  956.  strcpy(FIN_sent_time, "");
  957.  strcpy(RST_sent_time, "");
  958.  strcpy(last_connection_time, "");
  959.  have_ACK_error = 0;
  960.  have_value_error = 0;
  961.  have_FINdata_error = 0;
  962. }
  963. /* Check for some indications of possible errors 
  964.    such as the ACK appearing to move back -- may indicate
  965.    out-of-order or something worse. */
  966. int check_ACK_advance(unsigned long old_ack)
  967. {
  968.  
  969.  if ((new_ack < old_ack) &&
  970.       report_ACK_err)
  971.     {
  972.      if (have_ACK_error == 0)
  973.         {
  974.          error_state("ACK error -- backward");
  975.          have_ACK_error = 1;
  976.         }
  977.      return(-1);
  978.     }
  979.  else
  980.     return (0);
  981. }
  982. /* Check to see if reuse of a connection in a trace is reasonable */
  983. void check_tuple_reuse(void)
  984. {
  985.  /* ignore duplicate SYNs (have same SYN sequence number) */
  986.  if ((has_seq == 1) &&
  987.      (current_synseq != begin_seq))
  988.     {
  989.       /* if a non-duplicate SYN comes before any other activity,
  990.          treat it as terminating the incomplete connection and starting
  991.          another */
  992.      
  993.      if ((connection_state == SYN_SENT) ||
  994.          ((connection_state == RESET) && (last_state == SYN_SENT)))
  995.         {
  996.          log_END("TRM");
  997.          init_connection();
  998.          return;
  999.         }
  1000.     /* It is quite possible to have a valid new
  1001.     connection that reuses the same source and destination
  1002.     IP.port address 4-tuple.  Treat this as valid if 
  1003.     there has been a FIN from the server or at least
  1004.     2*MSL has passed since seeing any previous packet
  1005.     from this connection (in case we missed the FIN).  If there
  1006.     was a FIN from the server, allow for the case it was an
  1007.     active close by the server (normal for HTTP 1.0), the 
  1008.     stack is a BSD derivative, and SO_REUSEADDR is is use
  1009.     (see Stevens vol. 1, pp 245) */ 
  1010.      if (connection_state == FIN_SENT)
  1011.         elapsed = 60001;  /* force beyond 2MSL test */
  1012.      else        
  1013.         elapsed = elapsed_ms(ts, last_connection_time);
  1014.      if (elapsed < 60000)  /* MSL of 30 seconds, minimum */
  1015.         error_state("Non-duplicate SYN in connection");
  1016.      else
  1017. {
  1018.  /* perform all actions associated with the end of
  1019.             one connection and the beginning of the next 
  1020.             (see state PENDING) */
  1021.          switch (connection_state)
  1022.     {
  1023.     case FIN_SENT:
  1024.                  log_END("FIN");
  1025.                  break;
  1026.             case RESET:
  1027.                  if (last_state == IN_RESPONSE)
  1028.                     log_RSP();
  1029.                  log_END("RST");
  1030.                  break;
  1031.             case IN_RESPONSE:
  1032.                  log_RSP();
  1033.                  log_END("TRM");
  1034.                  break;
  1035.             case IN_REQUEST:
  1036.                  log_REQ();
  1037.                  log_END("TRM");
  1038.                  break;
  1039.             default:
  1040.                  break;
  1041.     }
  1042.          init_connection();
  1043.         }
  1044.     }
  1045. }
  1046. /* Record information about the beginning of a new request */
  1047. void begin_REQ(void)
  1048. {
  1049.  current_request_end = new_ack;
  1050.  /* record the request start time as beginning at the 
  1051.     tcpdump time stamp on this record */
  1052.  strcpy(start_request_time, ts);
  1053.  strcpy(request_end_time, ts);
  1054.  last_state = connection_state;
  1055.  connection_state = IN_REQUEST;
  1056. }
  1057. /* Record information about an in-progress request */
  1058. void more_REQ(void)
  1059. {
  1060.  current_request_end = new_ack;
  1061.  strcpy(request_end_time, ts);
  1062. }
  1063. /* Record information about the beginning of a new response */
  1064. void begin_RSP(void)
  1065. {
  1066.  current_response_end = end_seq;
  1067.  /* record timestamp of tcpdump record as current
  1068.  value of both start and end times of response
  1069.  (in case no later end time is found) */
  1070.  strcpy(start_response_time, ts); 
  1071.  strcpy(response_end_time, ts);
  1072.  last_state = connection_state;
  1073.  connection_state = IN_RESPONSE;
  1074. }
  1075. /* Record information about an in-progress response */
  1076. void more_RSP(void)
  1077. {
  1078.  current_response_end = end_seq;
  1079.  /* save the new (potential) response end time */
  1080.  strcpy(response_end_time, ts);
  1081. }
  1082. /* Breaks dump record into essential data fields; initializes the
  1083.    following variables: begin_seq, end_seq, seq_bytes, new_ack,
  1084.                         has_ack, has_seq, input_type.
  1085.    Also checks for suspect sequence and ACK values.
  1086. */
  1087. int parse_dump_record()
  1088. {
  1089.     begin_seq = end_seq = seq_bytes = new_ack = 0;
  1090.     has_ack = has_seq = 0;
  1091.    /* The following flag combinations are flagged because they (a) make no
  1092.       real sense for SYNs and (b) should be very rare. */
  1093.     if ((strcmp(fl, "SFRP") == 0) ||
  1094. (strcmp(fl, "SFR") == 0) ||
  1095. (strcmp(fl, "SFP") == 0) ||
  1096. (strcmp(fl, "SF") == 0) ||
  1097. (strcmp(fl, "SRP") == 0) ||
  1098. (strcmp(fl, "SR") == 0))
  1099.        {
  1100.  /* If out of PENDING state (initial SYN recognized), just note
  1101.             the error, ignore it and continue.  */
  1102.         if (connection_state != PENDING)
  1103.             error_line ("SYN in combination with F or R");
  1104.         return(-1);
  1105.        }
  1106.     /* In tcpdump format, the fields coming after flags are, in order,
  1107.        data-seqno (format "bbb:eee(ccc)") and ack (format "ack xxx").
  1108.        Both the data-seqno and ack fields may not be present if they
  1109.        do not have valid information.  If the data-seqno field is not
  1110.        present, the first field after the flag is the string "ack";
  1111.        if both are not present the field after the flag is the 
  1112.        string "win".  Also check to see if tcpdump used absolute instead
  1113.        of relative values */
  1114.     if (strcmp(p1, "ack") == 0)  /* ack and no data sequence no. */
  1115.        {
  1116.         has_seq = 0;
  1117.         has_ack = 1; 
  1118.         new_ack = strtoul(p2, (char **)NULL, 10);
  1119.        }
  1120.     else
  1121.        {
  1122.         if (strcmp(p1, "win") == 0) /* no ack, no data */
  1123.    {
  1124.             has_ack = 0;
  1125.             has_seq = 0;
  1126.            }
  1127.         else 
  1128.    {
  1129.             /* assume it is a valid sequence number field */
  1130.     /* parse sequence field in header */
  1131.     /* sequence field format is
  1132.                <begin_seq #>:<end_seq #>(<seq_bytes count>) */
  1133.             if ((rc = get_sequence(p1, &begin_seq, &end_seq, &seq_bytes)) < 0)
  1134.        {
  1135.                 error_line ("invalid sequence # field");
  1136.                 return (-1);
  1137.                }
  1138.             has_seq = 1;
  1139.             /* check the field following the data sequence # for an ACK */
  1140.             if (strcmp(p2, "ack") == 0)  
  1141.                {
  1142.                 has_ack = 1; 
  1143.                 new_ack = strtoul(p3, (char **)NULL, 10);
  1144.                }
  1145.             else
  1146.                has_ack = 0;
  1147.            }
  1148.        }
  1149.    /* classify flag combinations into equivalence classes for later steps */
  1150.       if ((strcmp(fl, "F") == 0) || 
  1151.           (strcmp(fl, "FP") == 0) ||
  1152.           (strcmp(fl, "FR") == 0) ||
  1153.           (strcmp(fl, "FRP") == 0))
  1154.          input_type = FIN;
  1155.       else
  1156.  {
  1157.           if ((strcmp(fl, "R") == 0) ||
  1158.               (strcmp(fl, "RP") == 0))
  1159.              input_type = RST;
  1160.           else
  1161.      {
  1162.               if ((strcmp(fl, "S") == 0) || 
  1163.                   (strcmp(fl, "SP") == 0))
  1164.                  input_type = SYN;
  1165.               else
  1166.                 {
  1167.                  if ((has_ack == 1) &&
  1168.                      (has_seq == 0))
  1169.                     input_type = ACK_ONLY;
  1170.                  else
  1171.                     if ((has_seq == 1) &&
  1172.                         (has_ack == 1))
  1173.                        input_type = DATA_ACK;
  1174.                     else
  1175.        {   
  1176.                         error_line("Unexpected Data/ACK combination");
  1177.                         return (-1);
  1178.                        }
  1179. }
  1180.      }
  1181.  }
  1182.       if ((connection_state == IN_RESPONSE) ||
  1183.           (connection_state == IN_REQUEST) ||
  1184.           (connection_state == SYN_SENT) ||
  1185.           ((connection_state == RESET) && (last_state == IN_RESPONSE)))
  1186.  {   
  1187.    /* allow for gaps of up to one normal TCP window */
  1188.           if (((input_type == FIN) ||
  1189.                (input_type == DATA_ACK)) &&
  1190.                (end_seq > (current_response_end + 65535)))
  1191.      {
  1192.               if (have_value_error == 0)
  1193.  {
  1194.                   error_line ("suspect sequence # value");
  1195.                   have_value_error = 1;
  1196.                  }
  1197.               return (-1);
  1198.              }
  1199.           /* ACK is required for each 2 segments with space for gaps */
  1200.           if (((input_type == FIN) ||
  1201.                (input_type == ACK_ONLY) ||
  1202.                (input_type == DATA_ACK)) &&
  1203.                (new_ack > (current_request_end + 16384)))
  1204.      {
  1205.               if (have_value_error == 0)
  1206.  {
  1207.                   error_line ("suspect ACK value");
  1208.                   have_value_error = 1;
  1209.                  }
  1210.               return (-1);
  1211.              }
  1212.          }
  1213.  return(0);
  1214. }
  1215. /* Output data associated with ending a connection */
  1216. void log_connection(void)
  1217. {
  1218. /* if no more tcpdump records found while processing an http
  1219.    request, log (perhaps incomplete) client request */  
  1220.    if (connection_state == IN_REQUEST)
  1221.       log_REQ();
  1222.    else
  1223.       {
  1224.        /* if no more records found while processing an http 
  1225.           response, log (perhaps incomplete) response information */
  1226.        if ((connection_state == IN_RESPONSE) ||
  1227.            ((connection_state == RESET) && last_state == IN_RESPONSE))
  1228.   { /* don't log if just ACKed 1 (assume  FIN) */
  1229.            if (current_response_end > (last_response_end + 1))
  1230.                log_RSP();
  1231.           }
  1232.       }
  1233.    /* make log entry indicating type of connection termination;
  1234.       entry for connection is made only if a valid start (SYN) was
  1235.       previously recognized */ 
  1236.    if (connection_state != PENDING)  /* saw SYN */
  1237.       {
  1238.        if (connection_state == FIN_SENT) 
  1239.            log_END("FIN");
  1240.        else
  1241.   {
  1242.            if (connection_state == RESET) 
  1243.                log_END("RST");
  1244.            else
  1245.                log_END("TRM");
  1246.   }
  1247.       }
  1248.    else
  1249.       {
  1250.        if (((have_pending_fins > 0) +
  1251.             (have_pending_rsts > 0) +
  1252.             (have_pending_othr > 0) +
  1253.             (have_pending_acks > 0)) > 1)
  1254.           pending_cmb_count++;
  1255.        else
  1256.   {
  1257.            pending_fin_count += (have_pending_fins > 0);
  1258.            pending_rst_count += (have_pending_rsts > 0);
  1259.            pending_ack_count += (have_pending_acks > 0);
  1260.            pending_oth_count += (have_pending_othr > 0);
  1261.           }
  1262.       }
  1263. }
  1264. void log_log(void)
  1265. {
  1266.  fprintf(logFP, "Input tcpdump file: %s n", input_name);
  1267.  fprintf(logFP, "Output connection file: %s n", output_name);
  1268.  fprintf(logFP, "   SYNs     %8d n", syn_count);
  1269.  fprintf(logFP, "   REQs     %8d n", req_count);
  1270.  fprintf(logFP, "   ACT-REQs %8d n", act_req_count);
  1271.  fprintf(logFP, "   RSPs     %8d n", rsp_count);
  1272.  fprintf(logFP, "   ACT-RSPs %8d n", act_rsp_count);
  1273.  fprintf(logFP, "   FINs     %8d n", fin_count);
  1274.  fprintf(logFP, "   RSTs     %8d n", rst_count);
  1275.  fprintf(logFP, "   TRMs     %8d n", trm_count);
  1276.  fprintf(logFP, "   ERRs     %8d n", err_count);
  1277.  fprintf(logFP, "Partial Connections:n");
  1278.  fprintf(logFP, " FIN only   %8d n", pending_fin_count);
  1279.  fprintf(logFP, " RST only   %8d n", pending_rst_count);
  1280.  fprintf(logFP, " ACK only   %8d n", pending_ack_count);
  1281.  fprintf(logFP, " Combos     %8d n", pending_cmb_count);
  1282.  fprintf(logFP, " Other      %8d n", pending_oth_count);
  1283. }
  1284. /* A set of event-specific data logging functions.  A critical part of
  1285.    the logging functions for Requests and Responses is to save the 
  1286.    "current" value of the sequence number (ACK or data) that marks the
  1287.    end of it as the "last" value.  This is done to tell when the 
  1288.    sequence number advances again for multiple request/response pairs
  1289.    in a connection and to allow computing its size as (current - last). */
  1290. void log_REQ(void)
  1291. {
  1292. /* parse sourse host/port */
  1293.   get_host_port(current_src, src_host, src_port);
  1294. /* parse destination host/port */
  1295.   get_host_port(current_dst, dst_host, dst_port);
  1296.   /* for requests we log the request start time  -- the tcpdump 
  1297.      timestamp on the first record associated with a request -- 
  1298.      along with the TCP connection information and the size of the 
  1299.      request data */
  1300.   fprintf(outFP, "%s %-15s %5s > %-15s %4s: REQ %12d  %sn", 
  1301.                                     start_request_time,  
  1302.                                     dst_host, dst_port, src_host, src_port,  
  1303.                                     current_request_end - last_request_end,
  1304.                                     request_end_time);
  1305.   /* IMPORTANT */
  1306.   last_request_end = current_request_end;
  1307.   req_count++;
  1308. }
  1309. void log_RSP(void)
  1310. {
  1311. /* parse sourse host/port */
  1312.   get_host_port(current_src, src_host, src_port);
  1313. /* parse destination host/port */
  1314.   get_host_port(current_dst, dst_host, dst_port);
  1315.   /* for responses we log the response end time  -- the tcpdump 
  1316.      timestamp on the last record associated with a response -- 
  1317.      along with the TCP connection information, the size of the 
  1318.      response data, and the response start time -- the tcpdump
  1319.      timestamp on the first record associated with the response. */
  1320.   fprintf(outFP, "%s %-15s %5s > %-15s %4s: RSP %12d  %sn", 
  1321.                                    response_end_time, 
  1322.                                    dst_host, dst_port, src_host, src_port,  
  1323.                                    current_response_end - last_response_end,
  1324.                                    start_response_time);
  1325. #ifdef FOO
  1326.   fprintf(outFP, "%s %-15s %5s > %-15s %4s RSP %d %sn", start_response_time, 
  1327.                                    src_host, src_port, dst_host, dst_port, 
  1328.                                    current_response_end - last_response_end,
  1329.                                    response_end_time);
  1330.   fprintf(outFP, "%s %s > %s RSP %dn", start_response_time, current_src, 
  1331.                                    current_dst, 
  1332.                                    current_response_end - last_response_end);
  1333. #endif
  1334.   /* IMPORTANT */
  1335.   last_response_end = current_response_end;
  1336.   rsp_count++;
  1337. }
  1338. void log_SYN(void)
  1339. {
  1340. /* parse sourse host/port */
  1341.   get_host_port(current_src, src_host, src_port);
  1342. /* parse destination host/port */
  1343.   get_host_port(current_dst, dst_host, dst_port);
  1344.   fprintf(outFP, "%s %-15s %5s > %-15s %4s: SYNn", ts, 
  1345.                                      dst_host, dst_port, src_host, src_port);  
  1346.   syn_count++;
  1347. }
  1348. void log_END(char *how)
  1349. {
  1350.   char logical_end_time[20];
  1351.   
  1352. /* parse sourse host/port */
  1353.   get_host_port(current_src, src_host, src_port);
  1354. /* parse destination host/port */
  1355.   get_host_port(current_dst, dst_host, dst_port);
  1356.   if (strcmp(how, "FIN") == 0)
  1357.      {
  1358.       fin_count++;
  1359.       strcpy(logical_end_time, FIN_sent_time);
  1360.      }
  1361.   else
  1362.     {
  1363.      if (strcmp(how, "RST") == 0)
  1364.         {
  1365.          rst_count++;
  1366.          strcpy(logical_end_time, RST_sent_time);
  1367.         }
  1368.      else
  1369.         if (strcmp(how, "TRM") == 0)
  1370.    {
  1371.             trm_count++;
  1372.             strcpy(logical_end_time, last_connection_time);
  1373.            }
  1374.     }
  1375.   /* for termination of a connection we record the tcpdump timestamp of
  1376.      the last record of any kind associated with that conneciton along
  1377.      with the TCP connection 4-tuple and the way the connection ended
  1378.      (FIN, Reset, or just no more records in the trace). */
  1379.   fprintf(outFP, "%s %-15s %5s > %-15s %4s: %s               %sn", 
  1380.                                     last_connection_time, 
  1381.                                     dst_host, dst_port, src_host, src_port,  
  1382.                                     how, logical_end_time);
  1383. }
  1384. void log_ACT(char *how)
  1385. {
  1386. /* parse sourse host/port */
  1387.   get_host_port(current_src, src_host, src_port);
  1388. /* parse destination host/port */
  1389.   get_host_port(current_dst, dst_host, dst_port);
  1390.   /* for activity on a SYN-less connection we record the tcpdump timestamp
  1391.      of the first record of activiy associated with that conneciton along
  1392.      with the TCP connection 4-tuple and the way the connection started
  1393.      (Request or Response). */
  1394.   fprintf(outFP, "%s %-15s %5s > %-15s %4s: ACT-%sn", ts, 
  1395.                                     dst_host, dst_port, src_host, src_port,  
  1396.                                     how);
  1397.   if (strcmp(how, "REQ") == 0)
  1398.      act_req_count++;
  1399.   else
  1400.      if (strcmp(how, "RSP") == 0)
  1401.         act_rsp_count++;
  1402. }
  1403. void error_line(char * s)
  1404. {
  1405. /* parse sourse host/port */
  1406.   get_host_port(sh, src_host, src_port);
  1407. /* parse destination host/port */
  1408.   get_host_port(dh, dst_host, dst_port);
  1409.   fprintf(outFP, "%s %-15s %5s > %-15s %4s: ERR: %sn", ts, 
  1410.                                    dst_host, dst_port, src_host, src_port, s);
  1411.   err_count++;
  1412. }
  1413. void error_state(char * s)
  1414. {
  1415. /* parse sourse host/port */
  1416.   get_host_port(sh, src_host, src_port);
  1417. /* parse destination host/port */
  1418.   get_host_port(dh, dst_host, dst_port);
  1419.   fprintf(outFP, "%s %-15s %5s > %-15s %4s: ERR: %sn", ts, 
  1420.                                    dst_host, dst_port, src_host, src_port, s);
  1421.   err_count++;
  1422. }
  1423. void get_host_port(char *adr, char *host, char *port)
  1424. {
  1425.  char *fp;
  1426.  char *fpx;
  1427.  char adr_field[50];
  1428.  strcpy(adr_field, adr);
  1429.  /* break string at '.' separating host and port fields (last in string) */
  1430.  fp = (char *)rindex(adr_field, '.');
  1431.  *fp = '';   /* replace '.' with string terminator */
  1432.  strcpy(host, adr_field); /* copies host name up to terminator */ 
  1433.  fp++;  /* move pointer past terminator to 1st char in port field */
  1434.  fpx = (char *)index(fp, ':');   /* see if we have the ':' after a dst port */
  1435.  if (fpx != NULL)
  1436.      *fpx = '';  /* if so, replace with string terminator */
  1437.  strcpy(port, fp); 
  1438. }
  1439. int get_sequence(char *p, unsigned long *begin, unsigned long *end,
  1440.                            unsigned long *bytes)
  1441. {
  1442.  char seq_field[50];
  1443.  char *cursor = seq_field;
  1444.  char *fp;
  1445.  strcpy (seq_field, p);
  1446.  fp = (char *)strsep(&cursor, ":" );
  1447.  if ((cursor == (char *)NULL) ||
  1448.      (fp == (char *)NULL))
  1449.     return (-1);
  1450.  else
  1451.     *begin = strtoul(fp, (char **)NULL, 10);
  1452.  fp = (char *)strsep(&cursor, "(" );
  1453.  if ((cursor == (char *)NULL) ||
  1454.      (fp == (char *)NULL))
  1455.     return (-1);
  1456.  else
  1457.     *end = strtoul(fp, (char **)NULL, 10);
  1458.  fp = (char *)strsep(&cursor, ")" );
  1459.  if ((cursor == (char *)NULL) ||
  1460.      (fp == (char *)NULL))
  1461.     return (-1);
  1462.  else
  1463.     *bytes = strtoul(fp, (char **)NULL, 10);
  1464.  return(0);
  1465. }
  1466. /*--------------------------------------------------------------*/ 
  1467. /* subtract two timevals (t1 - t0) with result in tdiff         */
  1468. /* tdiff, t1 and t0 are all pointers to struct timeval          */
  1469. /*--------------------------------------------------------------*/ 
  1470. static void
  1471. tvsub(tdiff, t1, t0)
  1472. struct timeval *tdiff, *t1, *t0;
  1473. {
  1474.         tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
  1475.         tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
  1476.         if (tdiff->tv_usec < 0)
  1477.            {
  1478.             tdiff->tv_sec--;
  1479.             tdiff->tv_usec += 1000000;
  1480.            }
  1481. }
  1482. /*--------------------------------------------------------------*/ 
  1483. /* compute the elapsed time in milliseconds to end_time         */
  1484. /* from some past time given by start_time (both formatted timevals) */
  1485. /*--------------------------------------------------------------*/ 
  1486. long elapsed_ms(char *end, char *start)
  1487. {
  1488.  struct timeval delta, end_time, start_time;
  1489.  long elapsed_time;
  1490.  char end_tmp[20];
  1491.  char start_tmp[20];
  1492.  char *cursor;
  1493.  char *cp;
  1494.  strcpy(end_tmp, end);
  1495.  cursor = end_tmp;
  1496.  cp = (char *)strsep(&cursor, "." );
  1497.  end_time.tv_sec = atoi(end_tmp);
  1498.  end_time.tv_usec = atoi(cursor);
  1499.  strcpy(start_tmp, start);
  1500.  cursor = start_tmp;
  1501.  cp = (char *)strsep(&cursor, "." );
  1502.  start_time.tv_sec = atoi(start_tmp);
  1503.  start_time.tv_usec = atoi(cursor);
  1504.  tvsub(&delta, &end_time, &start_time);
  1505.  /* express as milliseconds */
  1506.  elapsed_time = (delta.tv_sec * 1000) + (delta.tv_usec/1000);
  1507.  return (elapsed_time);
  1508. }