mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00

These are non-functional changes. * accounting * activation * actual * added * addresult * aggregable * aligned * alternatively * ambiguous * analysis * analyzer * anticlimactic * apparently * application * appropriate * arithmetic * assignment * assigns * associated * authentication * authoritative * barrier * boundary * broccoli * buffering * caching * called * canonicalized * capturing * certificates * ciphersuite * columns * communication * comparison * comparisons * compilation * component * concatenating * concatenation * connection * convenience * correctly * corresponding * could * counting * data * declared * decryption * defining * dependent * deprecated * detached * dictionary * directional * directly * directory * discarding * disconnecting * distinguishes * documentation * elsewhere * emitted * empty * endianness * endpoint * enumerator * essentially * evaluated * everything * exactly * execute * explicit * expressions * facilitates * fiddling * filesystem * flag * flagged * for * fragments * guarantee * guaranteed * happen * happening * hemisphere * identifier * identifies * identify * implementation * implemented * implementing * including * inconsistency * indeterminate * indices * individual * information * initial * initialization * initialize * initialized * initializes * instantiate * instantiated * instantiates * interface * internal * interpreted * interpreter * into * it * iterators * length * likely * log * longer * mainly * mark * maximum * message * minimum * module * must * name * namespace * necessary * nonexistent * not * notifications * notifier * number * objects * occurred * operations * original * otherwise * output * overridden * override * overriding * overwriting * ownership * parameters * particular * payload * persistent * potential * precision * preexisting * preservation * preserved * primarily * probably * procedure * proceed * process * processed * processes * processing * propagate * propagated * prototype * provides * publishing * purposes * queue * reached * reason * reassem * reassemble * reassembler * recommend * record * reduction * reference * regularly * representation * request * reserved * retrieve * returning * separate * should * shouldn't * significant * signing * simplified * simultaneously * single * somebody * sources * specific * specification * specified * specifies * specify * statement * subdirectories * succeeded * successful * successfully * supplied * synchronization * tag * temporarily * terminating * that * the * transmitted * true * truncated * try * understand * unescaped * unforwarding * unknown * unknowndata * unspecified * update * usually * which * wildcard Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
303 lines
9.1 KiB
C++
303 lines
9.1 KiB
C++
// See the file "COPYING" in the main distribution directory for copyright.
|
|
|
|
#pragma once
|
|
|
|
#include "zeek/IPAddr.h"
|
|
#include "zeek/analyzer/protocol/http/events.bif.h"
|
|
#include "zeek/analyzer/protocol/mime/MIME.h"
|
|
#include "zeek/analyzer/protocol/pia/PIA.h"
|
|
#include "zeek/analyzer/protocol/tcp/ContentLine.h"
|
|
#include "zeek/analyzer/protocol/tcp/TCP.h"
|
|
#include "zeek/analyzer/protocol/zip/ZIP.h"
|
|
#include "zeek/binpac_zeek.h"
|
|
|
|
namespace zeek::analyzer::http
|
|
{
|
|
|
|
enum CHUNKED_TRANSFER_STATE
|
|
{
|
|
NON_CHUNKED_TRANSFER,
|
|
BEFORE_CHUNK,
|
|
EXPECT_CHUNK_SIZE,
|
|
EXPECT_CHUNK_DATA,
|
|
EXPECT_CHUNK_DATA_CRLF,
|
|
EXPECT_CHUNK_TRAILER,
|
|
EXPECT_NOTHING,
|
|
};
|
|
|
|
class HTTP_Entity;
|
|
class HTTP_Message;
|
|
class HTTP_Analyzer;
|
|
|
|
class HTTP_Entity final : public analyzer::mime::MIME_Entity
|
|
{
|
|
public:
|
|
HTTP_Entity(HTTP_Message* msg, analyzer::mime::MIME_Entity* parent_entity, int expect_body);
|
|
~HTTP_Entity() override
|
|
{
|
|
if ( zip )
|
|
{
|
|
zip->Done();
|
|
delete zip;
|
|
}
|
|
}
|
|
|
|
void EndOfData() override;
|
|
void Deliver(int len, const char* data, bool trailing_CRLF) override;
|
|
bool Undelivered(int64_t len);
|
|
int64_t BodyLength() const { return body_length; }
|
|
int64_t HeaderLength() const { return header_length; }
|
|
void SkipBody() { deliver_body = 0; }
|
|
const string& FileID() const { return precomputed_file_id; }
|
|
|
|
protected:
|
|
class UncompressedOutput;
|
|
friend class UncompressedOutput;
|
|
|
|
HTTP_Message* http_message;
|
|
int chunked_transfer_state;
|
|
int64_t content_length;
|
|
int64_t range_length;
|
|
int64_t expect_data_length;
|
|
int expect_body;
|
|
int64_t body_length;
|
|
int64_t header_length;
|
|
enum
|
|
{
|
|
IDENTITY,
|
|
GZIP,
|
|
COMPRESS,
|
|
DEFLATE
|
|
} encoding;
|
|
analyzer::zip::ZIP_Analyzer* zip;
|
|
bool deliver_body;
|
|
bool is_partial_content;
|
|
uint64_t offset;
|
|
int64_t instance_length; // total length indicated by content-range
|
|
bool send_size; // whether to send size indication to FAF
|
|
std::string precomputed_file_id;
|
|
|
|
analyzer::mime::MIME_Entity* NewChildEntity() override
|
|
{
|
|
return new HTTP_Entity(http_message, this, 1);
|
|
}
|
|
|
|
void DeliverBody(int len, const char* data, bool trailing_CRLF);
|
|
void DeliverBodyClear(int len, const char* data, bool trailing_CRLF);
|
|
|
|
void SubmitData(int len, const char* buf) override;
|
|
|
|
void SetPlainDelivery(int64_t length);
|
|
|
|
void SubmitHeader(analyzer::mime::MIME_Header* h) override;
|
|
void SubmitAllHeaders() override;
|
|
};
|
|
|
|
enum
|
|
{
|
|
HTTP_BODY_NOT_EXPECTED,
|
|
HTTP_BODY_EXPECTED,
|
|
HTTP_BODY_MAYBE,
|
|
};
|
|
|
|
// Finishing HTTP Messages:
|
|
//
|
|
// HTTP_Entity::SubmitAllHeaders -> EndOfData (no body)
|
|
// HTTP_Entity::Deliver -> EndOfData (end of body)
|
|
// HTTP_Analyzer::Done -> {Request,Reply}Made (connection terminated)
|
|
// {Request,Reply}Made -> HTTP_Message::Done
|
|
// HTTP_Message::Done -> MIME_Message::Done, EndOfData, HTTP_MessageDone
|
|
// MIME_Entity::EndOfData -> Message::EndEntity
|
|
// HTTP_Message::EndEntity -> Message::Done
|
|
// HTTP_MessageDone -> {Request,Reply}Made
|
|
|
|
class HTTP_Message final : public analyzer::mime::MIME_Message
|
|
{
|
|
friend class HTTP_Entity;
|
|
|
|
public:
|
|
HTTP_Message(HTTP_Analyzer* analyzer, analyzer::tcp::ContentLine_Analyzer* cl, bool is_orig,
|
|
int expect_body, int64_t init_header_length);
|
|
~HTTP_Message() override;
|
|
void Done(bool interrupted, const char* msg);
|
|
void Done() override { Done(false, "message ends normally"); }
|
|
|
|
bool Undelivered(int64_t len);
|
|
|
|
void BeginEntity(analyzer::mime::MIME_Entity* /* entity */) override;
|
|
void EndEntity(analyzer::mime::MIME_Entity* entity) override;
|
|
void SubmitHeader(analyzer::mime::MIME_Header* h) override;
|
|
void SubmitAllHeaders(analyzer::mime::MIME_HeaderList& /* hlist */) override;
|
|
void SubmitData(int len, const char* buf) override;
|
|
bool RequestBuffer(int* plen, char** pbuf) override;
|
|
void SubmitAllData();
|
|
void SubmitEvent(int event_type, const char* detail) override;
|
|
|
|
void SubmitTrailingHeaders(analyzer::mime::MIME_HeaderList& /* hlist */);
|
|
void SetPlainDelivery(int64_t length);
|
|
void SetDeliverySize(int64_t length);
|
|
void SkipEntityData();
|
|
|
|
HTTP_Analyzer* MyHTTP_Analyzer() const { return (HTTP_Analyzer*)analyzer; }
|
|
|
|
void Weird(const char* msg);
|
|
bool IsOrig() { return is_orig; }
|
|
|
|
protected:
|
|
HTTP_Analyzer* analyzer;
|
|
analyzer::tcp::ContentLine_Analyzer* content_line;
|
|
bool is_orig;
|
|
|
|
char* entity_data_buffer;
|
|
|
|
double start_time;
|
|
|
|
int64_t body_length; // total length of entity bodies
|
|
int64_t header_length; // total length of headers, including the request/reply line
|
|
|
|
// Total length of content gaps that are "successfully" skipped.
|
|
// Note: this might NOT include all content gaps!
|
|
int64_t content_gap_length;
|
|
|
|
HTTP_Entity* current_entity;
|
|
|
|
RecordValPtr BuildMessageStat(bool interrupted, const char* msg);
|
|
};
|
|
|
|
class HTTP_Analyzer final : public analyzer::tcp::TCP_ApplicationAnalyzer
|
|
{
|
|
public:
|
|
HTTP_Analyzer(Connection* conn);
|
|
|
|
void HTTP_Header(bool is_orig, analyzer::mime::MIME_Header* h);
|
|
void HTTP_EntityData(bool is_orig, String* entity_data);
|
|
void HTTP_MessageDone(bool is_orig, HTTP_Message* message);
|
|
void HTTP_Event(const char* category, const char* detail);
|
|
void HTTP_Event(const char* category, StringValPtr detail);
|
|
|
|
void SkipEntityData(bool is_orig);
|
|
|
|
bool IsConnectionClose() { return connection_close; }
|
|
int HTTP_ReplyCode() const { return reply_code; };
|
|
|
|
// Overridden from Analyzer.
|
|
void Done() override;
|
|
void DeliverStream(int len, const u_char* data, bool orig) override;
|
|
void Undelivered(uint64_t seq, int len, bool orig) override;
|
|
|
|
// Overridden from analyzer::tcp::TCP_ApplicationAnalyzer
|
|
void EndpointEOF(bool is_orig) override;
|
|
void ConnectionFinished(bool half_finished) override;
|
|
void ConnectionReset() override;
|
|
void PacketWithRST() override;
|
|
|
|
struct HTTP_VersionNumber
|
|
{
|
|
uint8_t major = 0;
|
|
uint8_t minor = 0;
|
|
|
|
bool operator==(const HTTP_VersionNumber& other) const
|
|
{
|
|
return minor == other.minor && major == other.major;
|
|
}
|
|
|
|
bool operator!=(const HTTP_VersionNumber& other) const { return ! operator==(other); }
|
|
|
|
double ToDouble() const { return major + minor * 0.1; }
|
|
};
|
|
|
|
double GetRequestVersion() { return request_version.ToDouble(); };
|
|
double GetReplyVersion() { return reply_version.ToDouble(); };
|
|
HTTP_VersionNumber GetRequestVersionNumber() { return request_version; };
|
|
HTTP_VersionNumber GetReplyVersionNumber() { return reply_version; };
|
|
int GetRequestOngoing() { return request_ongoing; };
|
|
int GetReplyOngoing() { return reply_ongoing; };
|
|
|
|
static analyzer::Analyzer* Instantiate(Connection* conn) { return new HTTP_Analyzer(conn); }
|
|
|
|
static bool Available()
|
|
{
|
|
return (http_request || http_reply || http_header || http_all_headers ||
|
|
http_begin_entity || http_end_entity || http_content_type || http_entity_data ||
|
|
http_message_done || http_event || http_stats);
|
|
}
|
|
|
|
protected:
|
|
void GenStats();
|
|
|
|
int HTTP_RequestLine(const char* line, const char* end_of_line);
|
|
int HTTP_ReplyLine(const char* line, const char* end_of_line);
|
|
|
|
void InitHTTPMessage(analyzer::tcp::ContentLine_Analyzer* cl, HTTP_Message*& message,
|
|
bool is_orig, int expect_body, int64_t init_header_length);
|
|
|
|
const char* PrefixMatch(const char* line, const char* end_of_line, const char* prefix);
|
|
const char* PrefixWordMatch(const char* line, const char* end_of_line, const char* prefix);
|
|
|
|
bool ParseRequest(const char* line, const char* end_of_line);
|
|
HTTP_VersionNumber HTTP_Version(int len, const char* data);
|
|
|
|
void SetVersion(HTTP_VersionNumber* version, HTTP_VersionNumber new_version);
|
|
|
|
bool RequestExpected() const { return num_requests == 0 || keep_alive; }
|
|
|
|
void HTTP_Request();
|
|
void HTTP_Reply();
|
|
|
|
void RequestMade(bool interrupted, const char* msg);
|
|
void ReplyMade(bool interrupted, const char* msg);
|
|
void RequestClash(Val* clash_val);
|
|
|
|
const String* UnansweredRequestMethod();
|
|
|
|
int HTTP_ReplyCode(const char* code_str);
|
|
int ExpectReplyMessageBody();
|
|
|
|
StringValPtr TruncateURI(const StringValPtr& uri);
|
|
|
|
int request_state, reply_state;
|
|
int num_requests, num_replies;
|
|
int num_request_lines, num_reply_lines;
|
|
HTTP_VersionNumber request_version, reply_version;
|
|
int keep_alive;
|
|
int connection_close;
|
|
int request_ongoing, reply_ongoing;
|
|
|
|
bool connect_request;
|
|
analyzer::pia::PIA_TCP* pia;
|
|
// set to true after a connection was upgraded
|
|
bool upgraded;
|
|
// set to true when encountering an "connection" header in a reply.
|
|
bool upgrade_connection;
|
|
// set to the protocol string when encountering an "upgrade" header
|
|
// in a reply.
|
|
std::string upgrade_protocol;
|
|
|
|
StringValPtr request_method;
|
|
|
|
// request_URI is in the original form (may contain '%<hex><hex>'
|
|
// sequences).
|
|
StringValPtr request_URI;
|
|
|
|
// unescaped_URI does not contain escaped sequences.
|
|
StringValPtr unescaped_URI;
|
|
|
|
std::queue<StringValPtr> unanswered_requests;
|
|
|
|
int reply_code;
|
|
StringValPtr reply_reason_phrase;
|
|
|
|
analyzer::tcp::ContentLine_Analyzer* content_line_orig;
|
|
analyzer::tcp::ContentLine_Analyzer* content_line_resp;
|
|
|
|
HTTP_Message* request_message;
|
|
HTTP_Message* reply_message;
|
|
};
|
|
|
|
extern bool is_reserved_URI_char(unsigned char ch);
|
|
extern bool is_unreserved_URI_char(unsigned char ch);
|
|
extern void escape_URI_char(unsigned char ch, unsigned char*& p);
|
|
extern String* unescape_URI(const u_char* line, const u_char* line_end,
|
|
analyzer::Analyzer* analyzer);
|
|
|
|
} // namespace zeek::analyzer::http
|