566 lines
17 KiB
C
566 lines
17 KiB
C
|
/*
|
||
|
* Copyright (c) 1990 The Regents of the University of California.
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* Redistribution and use in source and binary forms, with or without
|
||
|
* modification, are permitted provided that: (1) source code distributions
|
||
|
* retain the above copyright notice and this paragraph in its entirety, (2)
|
||
|
* distributions including binary code include the above copyright notice and
|
||
|
* this paragraph in its entirety in the documentation or other materials
|
||
|
* provided with the distribution, and (3) all advertising materials mentioning
|
||
|
* features or use of this software display the following acknowledgement:
|
||
|
* ``This product includes software developed by the University of California,
|
||
|
* Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
|
||
|
* the University nor the names of its contributors may be used to endorse
|
||
|
* or promote products derived from this software without specific prior
|
||
|
* written permission.
|
||
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
|
||
|
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
||
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||
|
*
|
||
|
* $Id: search.c,v 1.1 1993/11/14 21:21:02 deraadt Exp $
|
||
|
*/
|
||
|
#ifndef lint
|
||
|
static char rcsid[] =
|
||
|
"@(#) Header: search.c,v 1.3 92/05/01 15:14:45 vern Exp (LBL)";
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* search.c - supports fast searching through tcpdump files for timestamps
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/time.h>
|
||
|
|
||
|
#include "interface.h"
|
||
|
#include "savefile.h"
|
||
|
|
||
|
|
||
|
/* Maximum number of seconds that we can conceive of a dump file spanning. */
|
||
|
#define MAX_REASONABLE_FILE_SPAN (3600*24*366) /* one year */
|
||
|
|
||
|
/* Maximum packet length we ever expect to see. */
|
||
|
#define MAX_REASONABLE_PACKET_LENGTH 65535
|
||
|
|
||
|
/* Size of a packet header in bytes; easier than typing the sizeof() all
|
||
|
* the time ...
|
||
|
*/
|
||
|
#define PACKET_HDR_LEN (sizeof( struct packet_header ))
|
||
|
|
||
|
/* The maximum size of a packet, including its header. */
|
||
|
#define MAX_PACKET_SIZE (PACKET_HDR_LEN + snaplen)
|
||
|
|
||
|
/* Number of contiguous bytes from a dumpfile in which there's guaranteed
|
||
|
* to be enough information to find a "definite" header if one exists
|
||
|
* therein. This takes 3 full packets - the first to be just misaligned
|
||
|
* (one byte short of a full packet), missing its timestamp; the second
|
||
|
* to have the legitimate timestamp; and the third to provide confirmation
|
||
|
* that the second is legit, making it a "definite" header. We could
|
||
|
* scrimp a bit here since not the entire third packet is required, but
|
||
|
* it doesn't seem worth it
|
||
|
*/
|
||
|
#define MAX_BYTES_FOR_DEFINITE_HEADER (3 * MAX_PACKET_SIZE)
|
||
|
|
||
|
/* Maximum number of seconds that might reasonably separate two headers. */
|
||
|
#define MAX_REASONABLE_HDR_SEPARATION (3600 * 24 * 7) /* one week */
|
||
|
|
||
|
/* When searching a file for a packet, if we think we're within this many
|
||
|
* bytes of the packet we just search linearly. Since linear searches are
|
||
|
* probably much faster than random ones (random ones require searching for
|
||
|
* the beginning of the packet, which may be unaligned in memory), we make
|
||
|
* this value pretty hefty.
|
||
|
*/
|
||
|
#define STRAIGHT_SCAN_THRESHOLD (100 * MAX_PACKET_SIZE)
|
||
|
|
||
|
/* Extracts a long integer from a possibly unaligned buffer containing
|
||
|
* unsigned characters.
|
||
|
*/
|
||
|
#define EXTRACT_LONG(buf) (buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3])
|
||
|
|
||
|
|
||
|
/* Given a header and an acceptable first and last time stamp, returns non-zero
|
||
|
* if the header looks reasonable and zero otherwise.
|
||
|
*/
|
||
|
static int reasonable_header( hdr, first_time, last_time )
|
||
|
struct packet_header *hdr;
|
||
|
long first_time, last_time;
|
||
|
{
|
||
|
if ( last_time == 0 )
|
||
|
last_time = first_time + MAX_REASONABLE_FILE_SPAN;
|
||
|
|
||
|
return hdr->ts.tv_sec >= first_time &&
|
||
|
hdr->ts.tv_sec <= last_time &&
|
||
|
hdr->len > 0 &&
|
||
|
hdr->len <= MAX_REASONABLE_PACKET_LENGTH &&
|
||
|
hdr->caplen > 0 &&
|
||
|
hdr->caplen <= MAX_REASONABLE_PACKET_LENGTH;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Given a buffer, extracts a (properly aligned) packet header from it. */
|
||
|
|
||
|
static void extract_header( buf, hdr )
|
||
|
u_char *buf;
|
||
|
struct packet_header *hdr;
|
||
|
{
|
||
|
hdr->ts.tv_sec = EXTRACT_LONG(buf);
|
||
|
buf += sizeof( long );
|
||
|
hdr->ts.tv_usec = EXTRACT_LONG(buf);
|
||
|
buf += sizeof( long );
|
||
|
hdr->len = EXTRACT_LONG(buf);
|
||
|
buf += sizeof( long );
|
||
|
hdr->caplen = EXTRACT_LONG(buf);
|
||
|
|
||
|
if ( sf_swapped )
|
||
|
{
|
||
|
hdr->ts.tv_sec = SWAPLONG(hdr->ts.tv_sec);
|
||
|
hdr->ts.tv_usec = SWAPLONG(hdr->ts.tv_usec);
|
||
|
hdr->len = SWAPLONG(hdr->len);
|
||
|
hdr->caplen = SWAPLONG(hdr->caplen);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Search a buffer to locate the first header within it. Return values
|
||
|
* are HEADER_NONE, HEADER_CLASH, HEADER_PERHAPS, and HEADER_DEFINITELY.
|
||
|
* The first indicates that no evidence of a header was found; the second
|
||
|
* that two or more possible headers were found, neither more convincing
|
||
|
* than the other(s); the third that exactly one "possible" header was
|
||
|
* found; and the fourth that exactly one "definite" header was found.
|
||
|
*
|
||
|
* Headers are detected by looking for positions in the buffer which have
|
||
|
* reasonable timestamps and lengths. If there is enough room in the buffer
|
||
|
* for another header to follow a candidate header, a check is made for
|
||
|
* that following header. If it is present then the header is *definite*
|
||
|
* (unless another "perhaps" or "definite" header is found); if not, then
|
||
|
* the header is discarded. If there is not enough room in the buffer for
|
||
|
* another header then the candidate is *perhaps* (unless another header
|
||
|
* is subsequently found). A "tie" between a "definite" header and a
|
||
|
* "perhaps" header is resolved in favor of the definite header. Any
|
||
|
* other tie leads to HEADER_CLASH.
|
||
|
*
|
||
|
* The buffer position of the header is returned in hdrpos_addr and
|
||
|
* for convenience the corresponding header in return_hdr.
|
||
|
*
|
||
|
* first_time is the earliest possible acceptable timestamp in the
|
||
|
* header. last_time, if non-zero, is the last such timestamp. If
|
||
|
* zero, then up to MAX_REASONABLE_FILE_SPAN seconds after first_time
|
||
|
* is acceptable.
|
||
|
*/
|
||
|
|
||
|
#define HEADER_NONE 0
|
||
|
#define HEADER_CLASH 1
|
||
|
#define HEADER_PERHAPS 2
|
||
|
#define HEADER_DEFINITELY 3
|
||
|
|
||
|
static int find_header( buf, buf_len, first_time, last_time,
|
||
|
hdrpos_addr, return_hdr )
|
||
|
u_char *buf;
|
||
|
unsigned buf_len;
|
||
|
long first_time, last_time;
|
||
|
u_char **hdrpos_addr;
|
||
|
struct packet_header *return_hdr;
|
||
|
{
|
||
|
u_char *bufptr, *bufend, *last_pos_to_try;
|
||
|
struct packet_header hdr, hdr2;
|
||
|
int status = HEADER_NONE;
|
||
|
int saw_PERHAPS_clash = 0;
|
||
|
|
||
|
/* Initially, try each buffer position to see whether it looks like
|
||
|
* a valid packet header. We may later restrict the positions we look
|
||
|
* at to avoid seeing a sequence of legitimate headers as conflicting
|
||
|
* with one another.
|
||
|
*/
|
||
|
bufend = buf + buf_len;
|
||
|
last_pos_to_try = bufend - PACKET_HDR_LEN;
|
||
|
|
||
|
for ( bufptr = buf; bufptr < last_pos_to_try; ++bufptr )
|
||
|
{
|
||
|
extract_header( bufptr, &hdr );
|
||
|
|
||
|
if ( reasonable_header( &hdr, first_time, last_time ) )
|
||
|
{
|
||
|
u_char *next_header = bufptr + PACKET_HDR_LEN + hdr.caplen;
|
||
|
|
||
|
if ( next_header + PACKET_HDR_LEN < bufend )
|
||
|
{ /* check for another good header */
|
||
|
extract_header( next_header, &hdr2 );
|
||
|
|
||
|
if ( reasonable_header( &hdr2, hdr.ts.tv_sec,
|
||
|
hdr.ts.tv_sec + MAX_REASONABLE_HDR_SEPARATION ) )
|
||
|
{ /* a confirmed header */
|
||
|
switch ( status )
|
||
|
{
|
||
|
case HEADER_NONE:
|
||
|
case HEADER_PERHAPS:
|
||
|
status = HEADER_DEFINITELY;
|
||
|
*hdrpos_addr = bufptr;
|
||
|
*return_hdr = hdr;
|
||
|
|
||
|
/* Make sure we don't demote this "definite"
|
||
|
* to a "clash" if we stumble across its
|
||
|
* successor.
|
||
|
*/
|
||
|
last_pos_to_try = next_header - PACKET_HDR_LEN;
|
||
|
break;
|
||
|
|
||
|
case HEADER_DEFINITELY:
|
||
|
return HEADER_CLASH;
|
||
|
|
||
|
default:
|
||
|
error( "bad status in find_header()" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* ... else the header is bogus - we've verified that it's
|
||
|
* not followed by a reasonable header.
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
else
|
||
|
{ /* can't check for another good header */
|
||
|
switch ( status )
|
||
|
{
|
||
|
case HEADER_NONE:
|
||
|
status = HEADER_PERHAPS;
|
||
|
*hdrpos_addr = bufptr;
|
||
|
*return_hdr = hdr;
|
||
|
break;
|
||
|
|
||
|
case HEADER_PERHAPS:
|
||
|
/* We don't immediately turn this into a
|
||
|
* clash because perhaps we'll later see a
|
||
|
* "definite" which will save us ...
|
||
|
*/
|
||
|
saw_PERHAPS_clash = 1;
|
||
|
break;
|
||
|
|
||
|
case HEADER_DEFINITELY:
|
||
|
/* Keep the definite in preference to this one. */
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
error( "bad status in find_header()" );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( status == HEADER_PERHAPS && saw_PERHAPS_clash )
|
||
|
status = HEADER_CLASH;
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Positions the sf_readfile stream such that the next sf_read() will
|
||
|
* read the final full packet in the file. Returns non-zero if
|
||
|
* successful, zero if unsuccessful. If successful, returns the
|
||
|
* timestamp of the last packet in last_timestamp.
|
||
|
*
|
||
|
* Note that this routine is a special case of sf_find_packet(). In
|
||
|
* order to use sf_find_packet(), one first must use this routine in
|
||
|
* order to give sf_find_packet() an upper bound on the timestamps
|
||
|
* present in the dump file.
|
||
|
*/
|
||
|
int sf_find_end( first_timestamp, last_timestamp )
|
||
|
struct timeval *first_timestamp;
|
||
|
struct timeval *last_timestamp;
|
||
|
{
|
||
|
long first_time = first_timestamp->tv_sec;
|
||
|
unsigned num_bytes;
|
||
|
u_char *buf, *bufpos, *bufend;
|
||
|
u_char *hdrpos;
|
||
|
struct packet_header hdr, successor_hdr;
|
||
|
int status;
|
||
|
|
||
|
/* Allow enough room for at least two full (untruncated) packets,
|
||
|
* perhaps followed by a truncated packet, so we have a shot at
|
||
|
* finding a "definite" header and following its chain to the
|
||
|
* end of the file.
|
||
|
*/
|
||
|
num_bytes = MAX_BYTES_FOR_DEFINITE_HEADER;
|
||
|
if ( fseek( sf_readfile, (long) -num_bytes, 2 ) < 0 )
|
||
|
return 0;
|
||
|
|
||
|
buf = (u_char *)malloc((unsigned) num_bytes);
|
||
|
if ( ! buf )
|
||
|
return 0;
|
||
|
|
||
|
status = 0;
|
||
|
bufpos = buf;
|
||
|
bufend = buf + num_bytes;
|
||
|
|
||
|
if ( fread( (char *) bufpos, num_bytes, 1, sf_readfile ) != 1 )
|
||
|
goto done;
|
||
|
|
||
|
if ( find_header( bufpos, num_bytes, first_time, 0L, &hdrpos, &hdr ) !=
|
||
|
HEADER_DEFINITELY )
|
||
|
goto done;
|
||
|
|
||
|
/* Okay, we have a definite header in our hands. Follow its
|
||
|
* chain till we find the last valid packet in the file ...
|
||
|
*/
|
||
|
for ( ; ; )
|
||
|
{
|
||
|
/* move to the next header position */
|
||
|
bufpos = hdrpos + PACKET_HDR_LEN + hdr.caplen;
|
||
|
|
||
|
/* bufpos now points to a candidate packet, which if valid
|
||
|
* should replace the current packet pointed to by hdrpos as
|
||
|
* the last valid packet ...
|
||
|
*/
|
||
|
if ( bufpos >= bufend - PACKET_HDR_LEN )
|
||
|
/* not enough room for another header */
|
||
|
break;
|
||
|
|
||
|
extract_header( bufpos, &successor_hdr );
|
||
|
|
||
|
first_time = hdr.ts.tv_sec;
|
||
|
if ( ! reasonable_header( &successor_hdr, first_time, 0L ) )
|
||
|
/* this bodes ill - it means bufpos is perhaps a
|
||
|
* bogus packet header after all ...
|
||
|
*/
|
||
|
break;
|
||
|
|
||
|
/* Note that the following test is for whether the next
|
||
|
* packet starts at a position > bufend, *not* for a
|
||
|
* position >= bufend. If this is the last packet in the
|
||
|
* file and there isn't a subsequent partial packet, then
|
||
|
* we expect the first buffer position beyond this packet
|
||
|
* to be just beyond the end of the buffer, i.e., at bufend
|
||
|
* itself.
|
||
|
*/
|
||
|
if ( bufpos + PACKET_HDR_LEN + successor_hdr.caplen > bufend )
|
||
|
/* the packet is truncated */
|
||
|
break;
|
||
|
|
||
|
/* Accept this packet as fully legit. */
|
||
|
hdrpos = bufpos;
|
||
|
hdr = successor_hdr;
|
||
|
}
|
||
|
|
||
|
/* Success! Last valid packet is at hdrpos. */
|
||
|
*last_timestamp = hdr.ts;
|
||
|
status = 1;
|
||
|
|
||
|
/* Seek so that the next read will start at last valid packet. */
|
||
|
if ( fseek( sf_readfile, (long) -(bufend - hdrpos), 2 ) < 0 )
|
||
|
error( "final fseek() failed in sf_find_end()" );
|
||
|
|
||
|
done:
|
||
|
free( (char *) buf );
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Takes two timeval's and returns the difference, tv2 - tv1, as a double. */
|
||
|
|
||
|
static double timeval_diff( tv1, tv2 )
|
||
|
struct timeval *tv1, *tv2;
|
||
|
{
|
||
|
double result = (tv2->tv_sec - tv1->tv_sec);
|
||
|
result += (tv2->tv_usec - tv1->tv_usec) / 1000000.0;
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Returns true if timestamp t1 is chronologically less than timestamp t2. */
|
||
|
|
||
|
int sf_timestamp_less_than( t1, t2 )
|
||
|
struct timeval *t1, *t2;
|
||
|
{
|
||
|
return t1->tv_sec < t2->tv_sec ||
|
||
|
(t1->tv_sec == t2->tv_sec &&
|
||
|
t1->tv_usec < t2->tv_usec);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Given two timestamps on either side of desired_time and their positions,
|
||
|
* returns the interpolated position of the desired_time packet. Returns a
|
||
|
* negative value if the desired_time is outside the given range.
|
||
|
*/
|
||
|
|
||
|
static
|
||
|
long interpolated_position( min_time, min_pos, max_time, max_pos, desired_time )
|
||
|
struct timeval *min_time, *max_time, *desired_time;
|
||
|
long min_pos, max_pos;
|
||
|
{
|
||
|
double full_span = timeval_diff( max_time, min_time );
|
||
|
double desired_span = timeval_diff( desired_time, min_time );
|
||
|
long full_span_pos = max_pos - min_pos;
|
||
|
double fractional_offset = desired_span / full_span;
|
||
|
|
||
|
if ( fractional_offset < 0.0 || fractional_offset > 1.0 )
|
||
|
return -1;
|
||
|
|
||
|
return min_pos + (long) (fractional_offset * (double) full_span_pos);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Reads packets linearly until one with a time >= the given desired time
|
||
|
* is found; positions the dump file so that the next read will start
|
||
|
* at the given packet. Returns non-zero on success, 0 if an EOF was
|
||
|
* first encountered.
|
||
|
*/
|
||
|
|
||
|
static int read_up_to( desired_time )
|
||
|
struct timeval *desired_time;
|
||
|
{
|
||
|
int status = 1;
|
||
|
struct packet_header hdr;
|
||
|
u_char *buf;
|
||
|
long pos;
|
||
|
|
||
|
buf = (u_char *) malloc( (unsigned) snaplen );
|
||
|
|
||
|
for ( ; ; )
|
||
|
{
|
||
|
struct timeval *timestamp;
|
||
|
|
||
|
pos = ftell( sf_readfile );
|
||
|
status = sf_next_packet( &hdr, buf, snaplen );
|
||
|
|
||
|
if ( status )
|
||
|
{
|
||
|
if ( status == SFERR_EOF )
|
||
|
{
|
||
|
status = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
error( "bad status %d in read_up_to()", status );
|
||
|
}
|
||
|
|
||
|
timestamp = &hdr.ts;
|
||
|
|
||
|
if ( ! sf_timestamp_less_than( timestamp, desired_time ) )
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( fseek( sf_readfile, pos, 0 ) < 0 )
|
||
|
error( "fseek() failed in read_up_to()" );
|
||
|
|
||
|
free( (char *) buf );
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Positions the sf_readfile stream so that the next sf_read() will
|
||
|
* return the first packet with a time greater than or equal to
|
||
|
* desired_time. desired_time must be greater than min_time and less
|
||
|
* than max_time, which should correspond to actual packets in the
|
||
|
* file. min_pos is the file position (byte offset) corresponding to
|
||
|
* the min_time packet and max_pos is the same for the max_time packet.
|
||
|
*
|
||
|
* Returns non-zero on success, 0 if the given position is beyond max_pos.
|
||
|
*
|
||
|
* NOTE: when calling this routine, the sf_readfile stream *must* be
|
||
|
* already aligned so that the next call to sf_next_packet() will yield
|
||
|
* a valid packet.
|
||
|
*/
|
||
|
|
||
|
int sf_find_packet( min_time, min_pos, max_time, max_pos, desired_time )
|
||
|
struct timeval *min_time, *max_time;
|
||
|
long min_pos, max_pos;
|
||
|
struct timeval *desired_time;
|
||
|
{
|
||
|
int status = 1;
|
||
|
struct timeval min_time_copy, max_time_copy;
|
||
|
unsigned num_bytes = MAX_BYTES_FOR_DEFINITE_HEADER;
|
||
|
int num_bytes_read;
|
||
|
long desired_pos, present_pos;
|
||
|
u_char *buf, *hdrpos;
|
||
|
struct packet_header hdr;
|
||
|
|
||
|
buf = (u_char *) malloc( num_bytes );
|
||
|
if ( ! buf )
|
||
|
error( "malloc() failured in sf_find_packet()" );
|
||
|
|
||
|
min_time_copy = *min_time;
|
||
|
min_time = &min_time_copy;
|
||
|
|
||
|
max_time_copy = *max_time;
|
||
|
max_time = &max_time_copy;
|
||
|
|
||
|
for ( ; ; ) /* loop until positioned correctly */
|
||
|
{
|
||
|
desired_pos =
|
||
|
interpolated_position( min_time, min_pos,
|
||
|
max_time, max_pos,
|
||
|
desired_time );
|
||
|
|
||
|
if ( desired_pos < 0 )
|
||
|
{
|
||
|
status = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
present_pos = ftell( sf_readfile );
|
||
|
|
||
|
if ( present_pos <= desired_pos &&
|
||
|
desired_pos - present_pos < STRAIGHT_SCAN_THRESHOLD )
|
||
|
{ /* we're close enough to just blindly read ahead */
|
||
|
status = read_up_to( desired_time );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Undershoot the target a little bit - it's much easier to
|
||
|
* then scan straight forward than to try to read backwards ...
|
||
|
*/
|
||
|
desired_pos -= STRAIGHT_SCAN_THRESHOLD / 2;
|
||
|
if ( desired_pos < min_pos )
|
||
|
desired_pos = min_pos;
|
||
|
|
||
|
if ( fseek( sf_readfile, desired_pos, 0 ) < 0 )
|
||
|
error( "fseek() failed in sf_find_packet()" );
|
||
|
|
||
|
num_bytes_read =
|
||
|
fread( (char *) buf, 1, num_bytes, sf_readfile );
|
||
|
|
||
|
if ( num_bytes_read == 0 )
|
||
|
/* This shouldn't ever happen because we try to
|
||
|
* undershoot, unless the dump file has only a
|
||
|
* couple packets in it ...
|
||
|
*/
|
||
|
error( "fread() failed in sf_find_packet()" );
|
||
|
|
||
|
if ( find_header( buf, num_bytes, min_time->tv_sec,
|
||
|
max_time->tv_sec, &hdrpos, &hdr ) !=
|
||
|
HEADER_DEFINITELY )
|
||
|
error( "can't find header at position %ld in dump file",
|
||
|
desired_pos );
|
||
|
|
||
|
/* Correct desired_pos to reflect beginning of packet. */
|
||
|
desired_pos += (hdrpos - buf);
|
||
|
|
||
|
/* Seek to the beginning of the header. */
|
||
|
if ( fseek( sf_readfile, desired_pos, 0 ) < 0 )
|
||
|
error( "fseek() failed in sf_find_packet()" );
|
||
|
|
||
|
if ( sf_timestamp_less_than( &hdr.ts, desired_time ) )
|
||
|
{ /* too early in the file */
|
||
|
*min_time = hdr.ts;
|
||
|
min_pos = desired_pos;
|
||
|
}
|
||
|
|
||
|
else if ( sf_timestamp_less_than( desired_time, &hdr.ts ) )
|
||
|
{ /* too late in the file */
|
||
|
*max_time = hdr.ts;
|
||
|
max_pos = desired_pos;
|
||
|
}
|
||
|
|
||
|
else
|
||
|
/* got it! */
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
free( (char *) buf );
|
||
|
|
||
|
return status;
|
||
|
}
|