zeek/src/analyzer/protocol/rpc/RPC.h
Jon Siwek 2b3c2bd394 Fix reassembly of data w/ sizes beyond 32-bit capacities (BIT-348).
The main change is that reassembly code (e.g. for TCP) now uses
int64/uint64 (signedness is situational) data types in place of int
types in order to support delivering data to analyzers that pass 2GB
thresholds.  There's also changes in logic that accompany the change in
data types, e.g. to fix TCP sequence space arithmetic inconsistencies.

Another significant change is in the Analyzer API: the *Packet and
*Undelivered methods now use a uint64 in place of an int for the
relative sequence space offset parameter.
2014-04-09 13:03:24 -05:00

249 lines
6.6 KiB
C++

// See the file "COPYING" in the main distribution directory for copyright.
#ifndef ANALYZER_PROTOCOL_RPC_RPC_H
#define ANALYZER_PROTOCOL_RPC_RPC_H
#include "analyzer/protocol/tcp/TCP.h"
#include "analyzer/protocol/udp/UDP.h"
namespace analyzer { namespace rpc {
enum {
RPC_CALL = 0,
RPC_REPLY = 1,
};
enum {
RPC_MSG_ACCEPTED = 0,
RPC_MSG_DENIED = 1,
};
enum {
RPC_SUCCESS = 0,
RPC_PROG_UNAVAIL = 1,
RPC_PROG_MISMATCH = 2,
RPC_PROC_UNAVAIL = 3,
RPC_GARBAGE_ARGS = 4,
RPC_SYSTEM_ERR = 5,
};
enum {
RPC_MISMATCH = 0,
RPC_AUTH_ERROR = 1,
};
enum {
RPC_AUTH_BADCRED = 1,
RPC_AUTH_REJECTEDCRED = 2,
RPC_AUTH_BADVERF = 3,
RPC_AUTH_REJECTEDVERF = 4,
RPC_AUTH_TOOWEAK = 5,
};
enum {
RPC_AUTH_NULL = 0,
RPC_AUTH_UNIX = 1,
RPC_AUTH_SHORT = 2,
RPC_AUTH_DES = 3,
};
class RPC_CallInfo {
public:
RPC_CallInfo(uint32 xid, const u_char*& buf, int& n, double start_time,
double last_time, int rpc_len);
~RPC_CallInfo();
void AddVal(Val* arg_v) { Unref(v); v = arg_v; }
Val* RequestVal() const { return v; }
Val* TakeRequestVal() { Val* rv = v; v = 0; return rv; }
int CompareRexmit(const u_char* buf, int n) const;
uint32 Program() const { return prog; }
uint32 Version() const { return vers; }
uint32 Proc() const { return proc; }
double StartTime() const { return start_time; }
void SetStartTime(double t) { start_time = t; }
double LastTime() const { return last_time; }
void SetLastTime(double t) { last_time = t; }
int CallLen() const { return call_n; }
int RPCLen() const { return rpc_len; }
int HeaderLen() const { return header_len; }
uint32 XID() const { return xid; }
void SetValidCall() { valid_call = true; }
bool IsValidCall() const { return valid_call; }
protected:
uint32 xid, rpc_version, prog, vers, proc;
uint32 cred_flavor, verf_flavor;
u_char* call_buf; // copy of original call buffer
double start_time;
double last_time;
int rpc_len; // size of the full RPC call, incl. xid and msg_type
int call_n; // size of call buf
int header_len; // size of data before the arguments
bool valid_call; // whether call was well-formed
Val* v; // single (perhaps compound) value corresponding to call
};
declare(PDict,RPC_CallInfo);
class RPC_Interpreter {
public:
RPC_Interpreter(analyzer::Analyzer* analyzer);
virtual ~RPC_Interpreter();
// Delivers the given RPC. Returns true if "len" bytes were
// enough, false otherwise. "is_orig" is true if the data is
// from the originator of the connection.
int DeliverRPC(const u_char* data, int len, int caplen, int is_orig, double start_time, double last_time);
void Timeout();
protected:
virtual int RPC_BuildCall(RPC_CallInfo* c, const u_char*& buf, int& n) = 0;
virtual int RPC_BuildReply(RPC_CallInfo* c, BifEnum::rpc_status success,
const u_char*& buf, int& n, double start_time, double last_time,
int reply_len) = 0;
void Event_RPC_Dialogue(RPC_CallInfo* c, BifEnum::rpc_status status, int reply_len);
void Event_RPC_Call(RPC_CallInfo* c);
void Event_RPC_Reply(uint32_t xid, BifEnum::rpc_status status, int reply_len);
void Weird(const char* name);
PDict(RPC_CallInfo) calls;
analyzer::Analyzer* analyzer;
};
/* A simple buffer for reassembling the fragments that RPC-over-TCP
* uses. Only needed by RPC_Contents.
* However, RPC messages can be quite large. As a first step, we only
* extract and analyzer the first part of an RPC message and skip
* over the rest.
*
* We specify:
* maxsize: the number of bytes we want to copy into the buffer to analyze.
* expected: the total number of bytes in the RPC message. Can be
* quite large. We will be "skipping over" expected-maxsize bytes.
*
* We can extend "expected" (by calling AddToExpected()), but maxsize is
* fixed.
*
* TODO: grow buffer dynamically
*/
class RPC_Reasm_Buffer {
public:
RPC_Reasm_Buffer() {
maxsize = expected = 0;
fill = processed = 0;
buf = 0;
};
~RPC_Reasm_Buffer() { if (buf) delete [] buf; }
void Init(int64_t arg_maxsize, int64_t arg_expected);
const u_char *GetBuf() { return buf; } // Pointer to the buffer
int64_t GetFill() { return fill; } // Number of bytes in buf
int64_t GetSkipped() { return processed-fill; } // How many bytes did we skipped?
int64_t GetExpected() { return expected; } // How many bytes are we expecting?
int64_t GetProcessed() { return processed; } // How many bytes are we expecting?
// Expand expected by delta bytes. Returns false if the number of
// expected bytes exceeds maxsize (which means that we will truncate
// the message).
bool AddToExpected(int64_t delta)
{ expected += delta; return ! (expected > maxsize); }
// Consume a chunk of input data (pointed to by data, up len in
// size). data and len will be adjusted accordingly. Returns true if
// "expected" bytes have been processed, i.e., returns true when we
// don't expect any more data.
bool ConsumeChunk(const u_char*& data, int& len);
protected:
int64_t fill; // how many bytes we currently have in the buffer
int64_t maxsize; // maximum buffer size we want to allocate
int64_t processed; // number of bytes we have processed so far
int64_t expected; // number of input bytes we expect
u_char *buf;
};
/* Support Analyzer for reassembling RPC-over-TCP messages */
class Contents_RPC : public tcp::TCP_SupportAnalyzer {
public:
Contents_RPC(Connection* conn, bool orig, RPC_Interpreter* interp);
virtual ~Contents_RPC();
protected:
typedef enum {
WAIT_FOR_MESSAGE,
WAIT_FOR_MARKER,
WAIT_FOR_DATA,
WAIT_FOR_LAST_DATA,
} state_t;
typedef enum {
NEED_RESYNC,
RESYNC_WAIT_FOR_MSG_START,
RESYNC_WAIT_FOR_FULL_MSG,
RESYNC_HAD_FULL_MSG,
INSYNC,
RESYNC_INIT,
} resync_state_t;
virtual void Init();
virtual bool CheckResync(int& len, const u_char*& data, bool orig);
virtual void DeliverStream(int len, const u_char* data, bool orig);
virtual void Undelivered(uint64 seq, int len, bool orig);
virtual void NeedResync() {
resync_state = NEED_RESYNC;
resync_toskip = 0;
state = WAIT_FOR_MESSAGE;
}
RPC_Interpreter* interp;
RPC_Reasm_Buffer marker_buf; // reassembles the 32bit RPC-over-TCP marker
RPC_Reasm_Buffer msg_buf; // reassembles RPC messages
state_t state;
double start_time;
double last_time;
resync_state_t resync_state;
int resync_toskip;
};
class RPC_Analyzer : public tcp::TCP_ApplicationAnalyzer {
public:
RPC_Analyzer(const char* name, Connection* conn,
RPC_Interpreter* arg_interp);
virtual ~RPC_Analyzer();
virtual void Done();
protected:
virtual void DeliverPacket(int len, const u_char* data, bool orig,
uint64 seq, const IP_Hdr* ip, int caplen);
void ExpireTimer(double t);
RPC_Interpreter* interp;
Contents_RPC* orig_rpc;
Contents_RPC* resp_rpc;
};
} } // namespace analyzer::*
#endif