Ethereal-dev: Re: [Ethereal-dev] Reassembly & tvb_find_guint8()

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Alex Kirk <alex@xxxxxxxxxxxx>
Date: Thu, 3 Feb 2005 15:41:19 -0500
Quoting Guy Harris <gharris@xxxxxxxxx>:

> Alex Kirk wrote:
> 
> > The situation is this: my PDU begins with a 4-byte field that gives me its
> > length. Following the instructions in README.developer, I've been able to
> get a
> > tvb whose size, as shown by tvb_reported_length_remaining(), is correct.
> During
> > dissection, I have a function that searches for a record terminator with
> > tvb_get_guint8() (since these long packets should only ever be a series of
> > records with a specific terminator). Using either -1 or the remaining
> length of
> > the packet (given by, again, tvb_reported_length_remaining(tvb,
> > current_offset)), however, tvb_get_guint8() always fails to find record
> > terminators that are beyond the current packet's boundary in the tvb. For
> > example, if the packet ends at 1440 bytes, but tvb has a reported length of
> 2880
> > with a terminator at 1450, tvb_get_guint8() never finds that terminator.
> 
> OK, so what's a "packet" and what's a "record" here?  Is a PDU a 
> "packet" or a "record", or none of the above, e.g. with a "packet" and a 
> "record" being the same thing, with multiple "packets"/"records" in a PDU?
>
> It sounds from "since these long packets should only ever be a series of 
> records with a specific terminator" as if a PDU is a "packet", where the 
> packet boundaries are determined by the 4-byte length field, and the 
> "records" are delimited by terminator bytes.
> 
> If so, then, if "the packet ends at 1440 bytes", but "tvb has a reported 
> length of 2880", does that mean that the 4-byte length field had a value 
> of 1440 or 2880?
> 
> If it's 1440, then the PDU/packet is 1440 bytes long, so it shouldn't be 
> surprising that "tvb_get_guint8()" doesn't find a terminator at 1450. 
> It is, however, surprising that the tvb has a reported length of 2880, 
> as the tvbuff should have the PDU/packet length as the reported length.
> 
> If it's 2880, then if "the packet ends at 1440 bytes", does that mean 
> that the "packet" is a record with a 2880-byte PDU, rather than being 
> the PDU?
> 
> > Is this behavior to be expected? If so, then obviuosly my approach of
> treating
> > the PDU like one big packet and continuing to add branches to the
> dissection
> > tree irrespective of packet boundaries is incorrect, and I need some
> guidance as
> > to how to dissect data that comes in the 2nd -> Nth packets of a PDU
> 
> OK, so *that* sounds as if a "packet" is a "record", rather than a PDU. 
>   If you have a 2880-byte PDU with the first 1440 bytes being the first 
> packet, and a terminator at an offset of 1450, I presume the extra 10 
> bytes are some sort of trailer not considered part of the packet.


A "packet" is a straight TCP packet; a "record" is a chunk of data within that
packet. Since I appear to have been confusing in my explanation, I'm attaching a
sample PCAP: packet #5 is the start of the PDU, and packet #6 is a continuation
(as well as the end of the PDU). The 4-byte length field, at the start of packet
#5 in this case is 2420; as you'll be able to see, there are exactly that many
bytes of data following in the PDU (i.e. in the remainder of packet #5's payload
and in the payload of packet #6, just for clarity's sake). After some header
data, the first "record" starts at offset 0x5A and goes through offset 0x89
(0xFFFFFFFF is the terminator; my search routine actually scans for 0xFF, and
then checks the next three bytes one at a time to see if we've got the
terminator). All of the remaining records are identical.

> If there is a terminator byte with a particular value at an offset of 
> 1450 from the beginning of the tvbuff, then a call
> 
> 	terminator_offset = tvb_find_guint8(tvb, 0, {value});
> 
> should find that terminator byte, as should any other call with the 
> offset argument being <= 1450.  (Obviously, a call with an offset > 1450 
> won't find it.)  Is that not happening?

Uh, for starters, tvb_find_guint8() takes 4 parameters: the tvb, the starting
offset, how far into the tvb to search (with -1 meaning "search through the rest
of the packet"), and the byte you're looking for (see comment starting at line
355 of epan/tvbuff.h). 

But assuming that's just a typo or a minor mental slip on your part, I'll answer
the question. I'm calling:

next_terminator = tvb_find_guint8(tvb, offset, -1, 255);

I had inserted a bunch of fprintf() calls in my function, to watch where
terminators were being found, the size of tvbuff, etc. The output should be
pretty self-explanatory:

...
In find_record_terminator, size of tvb: 2424, tvb remaining, 1056, offset: 1368
Next terminator: 1412
Success: record ends at 1416
In find_record_terminator, size of tvb: 2424, tvb remaining, 1008, offset: 1416
Next terminator: -1

Since I know that there's *at least* one more terminator byte in my tvbuff, that
shouldn't be happening. I suspect that the problem is because there are no more
terminators left in packet #5, which should end at offset 1448 of tvbuff.

Just for reference, here's the code of my terminator-lookup function:

/* Find the next record terminator */
int
find_record_terminator(tvbuff_t *tvb, int offset, guint32 sizeof_tvb) {
    gint next_terminator;
    int  c = 0;
    guint8 next_byte;
    FILE *fp;

    fp = fopen("/tmp/ethereal", "a+");
    fprintf(fp, "In find_record_terminator, size of tvb: %d, tvb remaining, %d,
offset: %d\n", tvb_reported_length_remaining(tvb, 0),
tvb_reported_length_remaining(tvb, offset), offset);
    next_terminator = tvb_find_guint8(tvb, offset, -1, 255);
    fprintf(fp, "Next terminator: %d\n", next_terminator);
    if (next_terminator > 0) {
        /* We found an 0xFF, check the next 3 bytes */
        offset = next_terminator;
        while ((c < 3) && ((next_byte = tvb_get_guint8(tvb, offset)) == 255)) {
            c++;
            offset++;
        }
        if (c == 3) {
            /* We've obviously seen all 4 0xFF's, return offset to end of record */
            fprintf(fp, "Success: record ends at %d\n", (offset + 1));
            fclose(fp);
            return (offset + 1);
        }
        else {
            /*
             * One of our bytes wasn't right; no field terminator found.
             * Call ourself again to see if we can find a terminator later on
             * in the packet; infinite loops are prevented because, if we
             * hit the end of the packet, our first if above will fail.
             */
            fprintf(fp, "Calling ourself...\n");
            fclose(fp);
            find_record_terminator(tvb, offset, sizeof_tvb);
        }
    }
    else {
        /* No 0xFF's before packet end, return -1 for failure */
        fprintf(fp, "No more terminators, aww...\n");
        fclose(fp);
        return -1;
    }
    /* We should never, ever get here; make compiler warning go away */
    fprintf(fp, "Utter insanity!\n");
    fclose(fp);
    return -1;
}

Hopefully this will clarify what I'm trying to do. Please let me know if there
is still any confusion, and I'll be happy to elaborate.

Oh, and once we get this settled, perhaps it'd be a good idea to discuss (on- or
off-list) what the hell a PDU *actually* is, and how all of this is supposed to
work, so I can write some better documentation. :-P

Alex Kirk

Attachment: wins-fake-long-rep-msg.pcap
Description: Binary data