/*
 *
 * Copyright (c) 2011-2016 The University of Waikato, Hamilton, New Zealand.
 * All rights reserved.
 *
 * This file is part of libprotoident.
 *
 * This code has been developed by the University of Waikato WAND
 * research group. For further information please see http://www.wand.net.nz/
 *
 * libprotoident is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * libprotoident is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 */

#include <string.h>

#ifdef __APPLE__
#include <libkern/OSByteOrder.h>
#define bswap32 OSSwapInt32
#endif

#ifdef __FreeBSD__
#include <sys/endian.h>
#endif

#ifdef __linux__
#include <byteswap.h>
#define bswap32 __bswap_32
#endif

#include "libprotoident.h"
#include "proto_common.h"

bool match_str_either(lpi_data_t *data, const char *string) {

        if (MATCHSTR(data->payload[0], string))
                return true;
        if (MATCHSTR(data->payload[1], string))
                return true;
        return false;
}

bool match_str_both(lpi_data_t *data, const char *string1,
        const char *string2) {

        if (MATCHSTR(data->payload[0], string1) &&
                MATCHSTR(data->payload[1], string2))
                return true;
        if (MATCHSTR(data->payload[1], string1) &&
                MATCHSTR(data->payload[0], string2))
                return true;
        return false;
}

bool match_chars_either(lpi_data_t *data, char a, char b, char c,
        char d) {

        if (MATCH(data->payload[0], a, b, c, d))
                return true;
        if (MATCH(data->payload[1], a, b, c, d))
                return true;
        return false;
}

bool match_payload_length(uint32_t payload, uint32_t payload_len) {

        uint32_t header = 0;

        header = ntohl(payload);

        /* See if the length in the (presumed) header matches the
         * length of the rest of the packet minus the header itself (4 bytes).
         *
         * Watch out for the case of a 4 byte packet containing just 
         * 00 00 00 00! */
        if (payload_len > 4 && header == payload_len - 4)
                return true;

        return false;
}

bool match_ip_address_both(lpi_data_t *data) {

	uint8_t matches = 0;

	if (data->ips[0] == 0 || data->ips[1] == 0)
		return false;
	
	if (data->payload_len[0] == 0)
		matches += 1;
	else if (data->payload[0] == data->ips[0])
		matches += 1;
	else if (data->payload[0] == data->ips[1])
		matches += 1;
		
	if (data->payload_len[1] == 0)
		matches += 1;
	else if (data->payload[1] == data->ips[0])
		matches += 1;
	else if (data->payload[1] == data->ips[1])
		matches += 1;
	 
	if (matches == 2)
		return true;
	else
		return false;
	
}

/* Multiple protocols use HTTP-style requests */
bool match_http_request(uint32_t payload, uint32_t len) {

        /* HTTP requests - some of these are MS-specific extensions */
        if (len == 0)
                return true;

        if (MATCHSTR(payload, "GET ")) return true;
        if (len == 1 && MATCH(payload, 'G', 0x00, 0x00, 0x00))
                return true;
        if (len == 2 && MATCH(payload, 'G', 'E', 0x00, 0x00))
                return true;
        if (len == 3 && MATCH(payload, 'G', 'E', 'T', 0x00))
                return true;

        if (MATCHSTR(payload, "POST")) return true;
        if (MATCHSTR(payload, "HEAD")) return true;
        if (MATCHSTR(payload, "PUT ")) return true;
        if (MATCHSTR(payload, "DELE")) return true;
        if (MATCHSTR(payload, "auth")) return true;

        /* SVN? */
        if (MATCHSTR(payload, "REPO")) return true;

        /* Webdav */
        if (MATCHSTR(payload, "LOCK")) return true;
        if (MATCHSTR(payload, "UNLO")) return true;
        if (MATCHSTR(payload, "OPTI")) return true;
        if (MATCHSTR(payload, "PROP")) return true;
        if (MATCHSTR(payload, "MKCO")) return true;
        if (MATCHSTR(payload, "POLL")) return true;
        if (MATCHSTR(payload, "SEAR")) return true;

        /* Ntrip - some differential GPS system using modified HTTP */
        if (MATCHSTR(payload, "SOUR")) return true;


        return false;

}

/* File headers are not specific to any particular protocol */
bool match_file_header(uint32_t payload) {

        /* RIFF is a meta-format for storing AVI and WAV files */
        if (MATCHSTR(payload, "RIFF"))
                return true;

        /* MZ is a .exe file */
        if (MATCH(payload, 'M', 'Z', ANY, 0x00))
                return true;

        /* Ogg files */
        if (MATCHSTR(payload, "OggS"))
                return true;

        /* ZIP files */
        if (MATCH(payload, 'P', 'K', 0x03, 0x04))
                return true;

        /* MPEG files */
        if (MATCH(payload, 0x00, 0x00, 0x01, 0xba))
                return true;

        /* RAR files */
        if (MATCHSTR(payload, "Rar!"))
                return true;

        /* EBML */
        if (MATCH(payload, 0x1a, 0x45, 0xdf, 0xa3))
                return true;

        /* JPG */
        if (MATCH(payload, 0xff, 0xd8, ANY, ANY))
                return true;

        /* GIF */
        if (MATCHSTR(payload, "GIF8"))
                return true;

        /* I'm also going to include PHP scripts in here */
        if (MATCH(payload, 0x3c, 0x3f, 0x70, 0x68))
                return true;

        /* Unix scripts */
        if (MATCH(payload, 0x23, 0x21, 0x2f, 0x62))
                return true;

        /* PDFs */
        if (MATCHSTR(payload, "%PDF"))
                return true;

        /* PNG */
        if (MATCH(payload, 0x89, 'P', 'N', 'G'))
                return true;

        /* HTML */
        if (MATCHSTR(payload, "<htm"))
                return true;
        if (MATCH(payload, 0x0a, '<', '!', 'D'))
                return true;

        /* 7zip */
        if (MATCH(payload, 0x37, 0x7a, 0xbc, 0xaf))
                return true;

        /* gzip  - may need to replace last two bytes with ANY */
        if (MATCH(payload, 0x1f, 0x8b, 0x08, ANY))
                return true;

        /* XML */
        if (MATCHSTR(payload, "<!DO"))
                return true;

        /* FLAC */
        if (MATCHSTR(payload, "fLaC"))
                return true;

        /* MP3 */
        if (MATCH(payload, 'I', 'D', '3', 0x03))
                return true;
	if (MATCHSTR(payload, "\xff\xfb\x90\xc0"))
		return true;

        /* RPM */
        if (MATCH(payload, 0xed, 0xab, 0xee, 0xdb))
                return true;

        /* Wz Patch */
        if (MATCHSTR(payload, "WzPa"))
                return true;

        /* Flash Video */
        if (MATCH(payload, 'F', 'L', 'V', 0x01))
                return true;

        /* .BKF (Microsoft Tape Format) */
        if (MATCHSTR(payload, "TAPE"))
                return true;

        /* MS Office Doc file - this is unpleasantly geeky */
        if (MATCH(payload, 0xd0, 0xcf, 0x11, 0xe0))
                return true;

        /* ASP */
        if (MATCH(payload, 0x3c, 0x25, 0x40, 0x20))
                return true;

        /* WMS file */
        if (MATCH(payload, 0x3c, 0x21, 0x2d, 0x2d))
                return true;

	/* ar archive, typically .deb files */
	if (MATCHSTR(payload, "!<ar"))
		return true;

	/* Raw XML */
	if (MATCHSTR(payload, "<?xm"))
		return true;
	if (MATCHSTR(payload, "<iq "))
		return true;

	/* SPF */
	if (MATCHSTR(payload, "SPFI"))
		return true;

	/* ABIF - Applied Biosystems */
	if (MATCHSTR(payload, "ABIF"))
		return true;

	/* bzip2 - other digits are also possible instead of 9 */
	if (MATCH(payload, 'B', 'Z', 'h', '9'))
		return true;

        /* xz compression format */
        if (MATCH(payload, 0xfd, '7', 'z', 'X'))
                return true;

        /* Microsoft Cabinet Files */
        if (MATCH(payload, 'M', 'S', 'C', 'F'))
                return true;

        /* M4A -- be wary of false positives? */
        if (MATCH(payload, 0x00, 0x00, 0x00, 0x20))
                return true;

        /* TIFF */
        if (MATCH(payload, 0x49, 0x49, 0x2a, 0x00))
                return true;

        /* LZMA */
        if (MATCH(payload, 0x5d, 0x00, 0x00, 0x80))
                return true;

        /* Source engine BSP file */
        if (MATCH(payload, 'V', 'B', 'S', 'P'))
                return true;

        /* TTF */
        if (MATCH(payload, 0x00, 0x01, 0x00, 0x00))
                return true;
        if (MATCH(payload, 'O', 'T', 'T', 'O'))
                return true;

        /* WOF2 TTCF */
        if (MATCH(payload, 't', 't', 'c', 'f'))
                return true;

        /* RIR delegation files... */
        if (MATCH(payload, '2', '.', '3', '|'))
                return true;

        /* REBASE -- restriction enzyme database
         * A bit niche, but might be fairly common at universities? */
        if (MATCH(payload, 0x20, 0x0a, 'R', 'E'))
                return true;

        /* Old coralreef trace files! */
        if (MATCHSTR(payload, "\xff\xff\x44\x00"))
                return true;

        /* I'm pretty sure the following are files of some type or another.
         * They crop up pretty often in our test data sets, so I'm going to
         * put them in here.
         *
         * Hopefully one day we will find out what they really are */

        if (MATCH(payload, '<', 'c', 'f', ANY))
                return true;
        if (MATCH(payload, '<', 'C', 'F', ANY))
                return true;
        if (MATCHSTR(payload, ".tem"))
                return true;
        if (MATCHSTR(payload, ".ite"))
                return true;
        if (MATCHSTR(payload, ".lef"))
                return true;

        return false;

}

bool valid_http_port(lpi_data_t *data) {
        /* Must be on a known HTTP port - designed to filter 
         * out P2P protos that use HTTP.
         *
         * XXX If this doesn't work well, get rid of it!
        */
        if (data->server_port == 80 || data->client_port == 80)
                return true;
        if (data->server_port == 8080 || data->client_port == 8080)
                return true;
        if (data->server_port == 8081 || data->client_port == 8081)
                return true;

        /* If port 443 responds, we want it to be counted as genuine
         * HTTP, rather than a bad port scenario */
        if (data->server_port == 443 || data->client_port == 443) {
                if (data->payload_len[0] > 0 && data->payload_len[1] > 0)
                        return true;
        }

        return false;

}

/* 16 03 00 X is an SSLv3 handshake */
static inline bool match_ssl3_handshake(uint32_t payload, uint32_t len) {

        if (len == 0)
                return true;
        if (len == 1 && MATCH(payload, 0x16, 0x00, 0x00, 0x00))
                return true;
        if (MATCH(payload, 0x16, 0x03, 0x00, ANY))
                return true;
        return false;
}

/* 16 03 01 X is an TLS handshake */
static inline bool match_tls_handshake(uint32_t payload, uint32_t len) {

        if (len == 0)
                return true;
        if (len == 1 && MATCH(payload, 0x16, 0x00, 0x00, 0x00))
                return true;
        if (MATCH(payload, 0x16, 0x03, 0x01, ANY))
                return true;
        if (MATCH(payload, 0x16, 0x03, 0x02, ANY))
                return true;
        if (MATCH(payload, 0x16, 0x03, 0x03, ANY))
                return true;
        return false;
}

/* SSLv2 handshake - the ANY byte in the 0x80 payload is actually the length 
 * of the payload - 2. 
 *
 * XXX This isn't always true - consecutive packets may be merged it seems :(
 */
static inline bool match_ssl2_handshake(uint32_t payload, uint32_t len) {

        if (MATCH(payload, 0x80, ANY, 0x01, 0x03))
                return true;
        if (MATCH(payload, 0x81, ANY, 0x01, 0x03))
                return true;

        return false;
}

static inline bool match_tls_alert(uint32_t payload, uint32_t len) {
        if (MATCH(payload, 0x15, 0x03, 0x01, ANY))
                return true;
        if (MATCH(payload, 0x15, 0x03, 0x02, ANY))
                return true;
        if (MATCH(payload, 0x15, 0x03, 0x03, ANY))
                return true;

	/* Alerts are also possible under SSL 3.0 */
        if (MATCH(payload, 0x15, 0x03, 0x00, ANY))
                return true;
        return false;
}

static inline bool match_tls_change(uint32_t payload, uint32_t len) {
        if (MATCH(payload, 0x14, 0x03, 0x01, ANY))
                return true;
        if (MATCH(payload, 0x14, 0x03, 0x02, ANY))
                return true;
        if (MATCH(payload, 0x14, 0x03, 0x03, ANY))
                return true;
        return false;

}

static inline bool match_tls_content(uint32_t payload, uint32_t len) {
        if (MATCH(payload, 0x17, 0x03, 0x01, ANY))
                return true;
        if (MATCH(payload, 0x17, 0x03, 0x02, ANY))
                return true;
        if (MATCH(payload, 0x17, 0x03, 0x03, ANY))
                return true;
        return false;
}

bool match_ssl(lpi_data_t *data) {


        if (match_ssl3_handshake(data->payload[0], data->payload_len[0]) &&
                        match_ssl3_handshake(data->payload[1], data->payload_len[1]))
                return true;

        if (match_tls_handshake(data->payload[0], data->payload_len[0]) &&
                        match_tls_handshake(data->payload[1], data->payload_len[1]))
                return true;

        if (match_ssl3_handshake(data->payload[0], data->payload_len[0]) &&
                        match_tls_handshake(data->payload[1], data->payload_len[1]))
                return true;

        if (match_tls_handshake(data->payload[0], data->payload_len[0]) &&
                        match_ssl3_handshake(data->payload[1], data->payload_len[1]))
                return true;
        /* Seems we can sometimes skip the full handshake and start on the data
         * right away (as indicated by 0x17) - for now, I've only done this for TLS */
        if (match_tls_handshake(data->payload[0], data->payload_len[0]) &&
                        match_tls_content(data->payload[1], data->payload_len[1]))
                return true;
        if (match_tls_handshake(data->payload[1], data->payload_len[1]) &&
                        match_tls_content(data->payload[0], data->payload_len[0]))
                return true;
        /* Need to check for TLS alerts (errors) too */
        if (match_tls_handshake(data->payload[0], data->payload_len[0]) &&
                        match_tls_alert(data->payload[1], data->payload_len[1]))
                return true;
        if (match_tls_handshake(data->payload[1], data->payload_len[1]) &&
                        match_tls_alert(data->payload[0], data->payload_len[0]))
                return true;
        if (match_ssl3_handshake(data->payload[0], data->payload_len[0]) &&
                        match_tls_alert(data->payload[1], data->payload_len[1]))
                return true;
        if (match_ssl3_handshake(data->payload[1], data->payload_len[1]) &&
                        match_tls_alert(data->payload[0], data->payload_len[0]))
                return true;

        /* Need to check for cipher changes too */
        if (match_tls_handshake(data->payload[0], data->payload_len[0]) &&
                        match_tls_change(data->payload[1], data->payload_len[1]))
                return true;
        if (match_tls_handshake(data->payload[1], data->payload_len[1]) &&
                        match_tls_change(data->payload[0], data->payload_len[0]))
                return true;


        /* Some HTTPS servers respond with unencrypted content, presumably
         * when somebody invalid attempts a connection */
        if (match_tls_handshake(data->payload[0], data->payload_len[0]) &&
                        MATCHSTR(data->payload[1], "<!DO") &&
			data->payload_len[0] != 0)
                return true;
        if (match_tls_handshake(data->payload[1], data->payload_len[1]) &&
                        MATCHSTR(data->payload[0], "<!DO") &&
			data->payload_len[1] != 0)
                return true;


        /* Allow TLS content in both directions -- could be multi-path TCP?
         * Or some form of picking up where a previous connection left off?
         */
        if (match_tls_content(data->payload[0], data->payload_len[0]) &&
                        match_tls_content(data->payload[1], data->payload_len[1]))
                return true;
        if (match_tls_content(data->payload[1], data->payload_len[1]) &&
                        match_tls_content(data->payload[0], data->payload_len[0]))
                return true;
        

        if ((match_tls_handshake(data->payload[0], data->payload_len[0]) ||
                        match_ssl3_handshake(data->payload[0], data->payload_len[0])) &&
                        match_ssl2_handshake(data->payload[1], data->payload_len[1]))
                return true;

        if ((match_tls_handshake(data->payload[1], data->payload_len[1]) ||
                        match_ssl3_handshake(data->payload[1], data->payload_len[1])) &&
                        match_ssl2_handshake(data->payload[0], data->payload_len[0]))
                return true;

        if (data->payload_len[0] == 0 && match_ssl2_handshake(data->payload[1], data->payload_len[1]))
                return true;
        if (data->payload_len[1] == 0 && match_ssl2_handshake(data->payload[0], data->payload_len[0]))
                return true;

        return false;
}

static bool dns_req(uint32_t payload) {

        /* The flags / rcode on requests are usually all zero.
         *
         * Exceptions: CD and RD may be set 
         *
         * Remember BYTE ORDER!
         */

	payload = htonl(payload);

	if ((payload & 0x0000ffff) == 0x00000000)
		return true;
	/* Check for CD */
	if ((payload & 0x0000ffff) == 0x00000010)
		return true;
	/* Check for RD */
	if ((payload & 0x0000ffff) == 0x00000100)
		return true;


        return false;

}

static bool dns_backscatter(uint32_t payload) {

	/* Let's see if we can identify unsolicited DNS responses */

	/* Last byte seems to be always 0x00 - third is either 0x84 or 0x85 */

	payload = htonl(payload);

	if ((payload & 0x0000ffff) == 0x00008500)
		return true;
	if ((payload & 0x0000ffff) == 0x00008580)
		return true;
	if ((payload & 0x0000ffff) == 0x00008400)
		return true;
	if ((payload & 0x0000ffff) == 0x00008480)
		return true;
	if ((payload & 0x0000ffff) == 0x00008483)
		return true;
	if ((payload & 0x0000ffff) == 0x00008403)
		return true;
	if ((payload & 0x0000ffff) == 0x00008000)
		return true;

	return false;
}

bool match_dns(lpi_data_t *data) {

        if (data->payload_len[0] == 0 || data->payload_len[1] == 0) {

                /* No response, so we have a bit of a hard time - however,
                 * most requests have a pretty standard set of flags.
                 *
                 * We'll also use the port here to help out */
                if (data->server_port != 53 && data->client_port != 53)
                        return false;
                if (data->payload_len[0] > 12 && dns_req(data->payload[0]))
                        return true;
                if (data->payload_len[1] > 12 && dns_req(data->payload[1]))
                        return true;
                if (data->payload_len[0] > 12 && 
				dns_backscatter(data->payload[0]))
                        return true;
                if (data->payload_len[1] > 12 && 
				dns_backscatter(data->payload[1]))
                        return true;

                return false;
        }

        if (((htonl(data->payload[0])) & 0xffff7800) != 
			((htonl(data->payload[1])) & 0xffff7800))
                return false;

        if ((htonl(data->payload[0]) & 0x00008000) == 
		(htonl(data->payload[1]) & 0x00008000))
                return false;

        return true;

}

bool match_tds_request(uint32_t payload, uint32_t len) {

        uint32_t stated_len = 0;

        stated_len = (ntohl(payload) & 0xffff);
        if (stated_len != len)
                return false;

        if (MATCH(payload, 0x12, 0x01, ANY, ANY))
                return true;
        if (MATCH(payload, 0x10, 0x01, ANY, ANY))
                return true;

        return false;

}


bool match_8000_payload(uint32_t payload, uint32_t len) {

        if (len == 0)
                return true;

        if (MATCH(payload, 0x3b, 0x00, 0x00, 0x00)) {
                return true;
        }
        if (MATCH(payload, 0x3c, 0x00, 0x00, 0x00)) {
                return true;
        }
        if (MATCH(payload, 0x3d, 0x00, 0x00, 0x00)) {
                return true;
        }
        if (MATCH(payload, 0x3e, 0x00, 0x00, 0x00)) {
                return true;
        }

        return false;
}

bool match_emule(lpi_data_t *data) {

        /* Check that payload begins with e3 or c5 in both directions before 
         * classifying as eMule */
        /* (I noticed that most emule(probably) flows began with "e3 xx 00 00" 
         * or "c5 xx 00 00", perhaps is worth looking into... Although I 
         * couldn't find anything about emule packets) */

        if (data->payload_len[0] < 4 && data->payload_len[1] < 4)
                return false;

        if (MATCH(data->payload[0], 0xe3, ANY, 0x00, 0x00) &&
            MATCH(data->payload[1], 0xe3, ANY, 0x00, 0x00))
                return true;

        if (MATCH(data->payload[0], 0xe3, ANY, 0x00, 0x00) &&
            MATCH(data->payload[1], 0xc5, ANY, 0x00, 0x00))
                return true;

        /* XXX I haven't seen any obviously legit emule that starts with c5
         * in both directions */
        /*
        if (MATCH(data->payload[0], 0xc5, ANY, ANY, ANY) &&
            MATCH(data->payload[1], 0xc5, ANY, ANY, ANY))
                return true;
        */

        if (MATCH(data->payload[0], 0xc5, ANY, 0x00, 0x00) &&
            MATCH(data->payload[1], 0xe3, ANY, 0x00, 0x00))
                return true;

        if (MATCH(data->payload[0], 0xe3, ANY, 0x00, 0x00) &&
                data->payload_len[1] == 0)
                return true;

        if (MATCH(data->payload[1], 0xe3, ANY, 0x00, 0x00) &&
                data->payload_len[0] == 0)
                return true;



        return false;
}

static inline bool match_kaspersky_ke(uint32_t payload, uint32_t len) {
        if (len == 0)
                return true;
        if (MATCH(payload, 'K', 'E', 0x00, 0x00))
                return true;
        if (MATCH(payload, 'K', 'E', 0x00, 0x02))
                return true;
        if (MATCH(payload, 'K', 'E', 0x00, 0x07))
                return true;
        return false;
}

static inline bool match_kaspersky_ks(uint32_t payload, uint32_t len) {
        if (len == 0)
                return true;
        if (MATCH(payload, 'K', 'S', 0x00, 0x00))
                return true;
        return false;
}

bool match_kaspersky(lpi_data_t *data) {

	/* Traffic is either on TCP port 443 or UDP port 2001.
	 *
	 * One of the endpoints is always in either a Kaspersky range or
	 * an old PSInet range */

	if (match_str_both(data, "PI\x00\x00", "PI\x00\x00")) {
		if (data->payload_len[0] == 2 && data->payload_len[1] == 2)
			return true;
	}
        if (match_kaspersky_ke(data->payload[0], data->payload_len[0])) {
                if (match_kaspersky_ke(data->payload[1], data->payload_len[1]))
                        return true;
        }
        if (match_kaspersky_ks(data->payload[0], data->payload_len[0])) {
                if (match_kaspersky_ks(data->payload[1], data->payload_len[1]))
                        return true;
        }
	return false;
}

bool match_youku_payload(uint32_t pload, uint32_t len) {

	if (len == 0)
                return true;
        if (MATCH(pload, 0x4b, 0x55, 0x00, 0x01))
                return true;
        if (MATCH(pload, 0x4b, 0x55, 0x00, 0x02))
                return true;
        if (MATCH(pload, 0x4b, 0x55, 0x00, 0x03))
                return true;
        if (MATCH(pload, 0x4b, 0x55, 0x00, 0x04))
                return true;
        return false;

}

bool match_tpkt(uint32_t payload, uint32_t len) {
        uint32_t stated_len = 0;

        /*
         * TPKT header is 03 00 + 2 bytes of length (including the TPKT header)
         */

        if (!MATCH(payload, 0x03, 0x00, ANY, ANY))
                return false;

        stated_len = ntohl(payload) & 0xffff;
        if (stated_len != len)
                return false;
        return true;
}

bool match_qqlive_payload(uint32_t payload, uint32_t len) {

        uint32_t swap;

        /* This appears to have a 3 byte header. First byte is always 0xfe.
         * Second and third bytes are the length (minus the 3 byte header).
         */

        if (len == 0)
                return true;

        swap = htonl(payload);
        swap = (swap & 0xffff00) >> 8;

        if (ntohs(swap) != len - 3)
                return false;

	/* Interestingly, the third and fourth byte always match */
        swap = htonl(payload);
        if ((swap & 0xff) != ((swap & 0xff00) >> 8))
                return false;

        if (MATCH(payload, 0xfe, ANY, ANY, ANY))
                return true;
        return false;

}

bool match_yy_payload(uint32_t payload, uint32_t len) {

        /* The first four bytes are a length field, but using the
         * wrong byte order...
         */

        if (!MATCH(payload, ANY, ANY, 0x00, 0x00))
                return false;

#if BYTE_ORDER == BIG_ENDIAN
        if (bswap32(payload) == len)
                return true;
#else
        if (payload == len)
                return true;
#endif
        return false;
}


/* Byte swapping functions for various inttypes */
uint64_t byteswap64(uint64_t num)
{
        return (byteswap32((num&0xFFFFFFFF00000000ULL)>>32))
              |((uint64_t)byteswap32(num&0x00000000FFFFFFFFULL)<<32);
}

uint32_t byteswap32(uint32_t num)
{
        return ((num&0x000000FFU)<<24)
                | ((num&0x0000FF00U)<<8)
                | ((num&0x00FF0000U)>>8)
                | ((num&0xFF000000U)>>24);
}

uint16_t byteswap16(uint16_t num)
{
        return ((num<<8)&0xFF00)|((num>>8)&0x00FF);
}

