/*============================================================================= Copyright (c) 2002-2003 Joel de Guzman http://spirit.sourceforge.net/ Use, modification and distribution is subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) =============================================================================*/ #include #include #include #include #include #include #include #include #include /////////////////////////////////////////////////////////////////////////////// // // Sample parser for binary data. This sample highlights the use of dynamic // parsing where the result of actions direct the actual parsing behavior. // We shall demonstrate 1) the use of phoenix to implement lambda (unnamed) // functions, 2) dynamic looping using for_p, 3) the push_back_a actor for // stuffing data into a vector, and 4) the if_p parser for choosing parser // branches based on semantic conditions. // // << Sample idea by Florian Weimer >> // // For simplicity, we shall use bytes as atoms (and not 16-bit quantities // in big-endian format or something similar, which would be more realistic) // and PASCAL strings. // // A packet is the literal octet with value 255, followed by a variable // octet N (denoting the total length of the packet), followed by N-2 octets // (the payload). The payload contains a variable-length header, followed // by zero or more elements. // // The header contains a single PASCAL string. // // An element is a PASCAL string (alternative: an element is an octet M, // followed by [M/8] bytes, i.e. the necessary number of bytes to store M // bits). // // (This data structure is inspired by the format of a BGP UPDATE message.) // // Packet layout: // // .-------------------. // | 0xff | ^ // +-------------------+ | // | packet length | | // +-------------------+ | number of bytes indicated by packet length // : : | // : payload : | // | | v // `-------------------' // // Payload layout: // // .-------------------. // | header length | // +-------------------+ // | header octets | ^ // : : | number of octets given by header length // : : | // : : v // +-------------------+ // | IPv4 prefix | ^ // : : | IPv4 prefixes have variable length (see // +-------------------+ | below). The number of prefixes is // | IPv4 prefix | | determined by the packet length. // : : | // +-------------------+ | // : : | // : : v // // // IPv4 prefix layout comes in five variants, depending on the first // octet: // // .-------------------. // | 0x00 | single octet, corresponds to 0.0.0.0/0 // `-------------------' // // .-------------------. // | 0x01 to 0x08 | two octets, prefix lengths up to /8. // +-------------------+ // | MSB of network | // `-------------------' // // .-------------------. // | 0x09 to 0x10 | three octets, prefix lengths up to /16. // +-------------------+ // | MSB of network | // +-------------------+ // | next octet | // `-------------------' // // .-------------------. // | 0x11 to 0x18 | four octets, prefix lengths up to /24. // +-------------------+ // | MSB of network | // +-------------------+ // | next octet | // +-------------------+ // | next octet | // `-------------------' // // .-------------------. // | 0x19 to 0x20 | five octets, prefix lengths up to /32. // +-------------------+ // | MSB of network | // +-------------------+ // | next octet | // +-------------------+ // | next octet | // +-------------------+ // | LSB of network | // `-------------------' // /////////////////////////////////////////////////////////////////////////////// using namespace std; using namespace BOOST_SPIRIT_CLASSIC_NS; using namespace phoenix; struct ipv4_prefix_data { char prefix_len, n0, n1, n2, n3; ipv4_prefix_data() : prefix_len(0),n0(0),n1(0),n2(0),n3(0) {} }; struct ipv4_data { char packet_len, header_len; std::string header; std::vector prefixes; ipv4_data() : packet_len(0),header_len(0){} }; struct ipv4 : public grammar { template struct definition { definition(ipv4 const& self) { packet = '\xff' >> anychar_p[var(self.data.packet_len) = arg1] >> payload ; payload = anychar_p[var(self.data.header_len) = arg1] >> for_p(var(i) = 0, var(i) < var(self.data.header_len), ++var(i)) [ anychar_p[var(self.data.header) += arg1] ] >> *ipv4_prefix ; ipv4_prefix = anychar_p [ var(temp.prefix_len) = arg1, var(temp.n0) = 0, var(temp.n1) = 0, var(temp.n2) = 0, var(temp.n3) = 0 ] >> if_p(var(temp.prefix_len) > 0x00) [ anychar_p[var(temp.n0) = arg1] >> if_p(var(temp.prefix_len) > 0x08) [ anychar_p[var(temp.n1) = arg1] >> if_p(var(temp.prefix_len) > 0x10) [ anychar_p[var(temp.n2) = arg1] >> if_p(var(temp.prefix_len) > 0x18) [ anychar_p[var(temp.n3) = arg1] ] ] ] ] [ push_back_a(self.data.prefixes, temp) ] ; } int i; ipv4_prefix_data temp; rule packet, payload, ipv4_prefix; rule const& start() const { return packet; } }; ipv4(ipv4_data& data) : data(data) {} ipv4_data& data; }; //////////////////////////////////////////////////////////////////////////// // // Main program // //////////////////////////////////////////////////////////////////////////// int as_byte(char n) { if (n < 0) return n + 256; return n; } void print_prefix(ipv4_prefix_data const& prefix) { cout << "prefix length = " << as_byte(prefix.prefix_len) << endl; cout << "n0 = " << as_byte(prefix.n0) << endl; cout << "n1 = " << as_byte(prefix.n1) << endl; cout << "n2 = " << as_byte(prefix.n2) << endl; cout << "n3 = " << as_byte(prefix.n3) << endl; } void parse_ipv4(char const* str, unsigned len) { ipv4_data data; ipv4 g(data); parse_info<> info = parse(str, str+len, g); if (info.full) { cout << "-------------------------\n"; cout << "Parsing succeeded\n"; cout << "packet length = " << as_byte(data.packet_len) << endl; cout << "header length = " << as_byte(data.header_len) << endl; cout << "header = " << data.header << endl; for_each(data.prefixes.begin(), data.prefixes.end(), print_prefix); cout << "-------------------------\n"; } else { cout << "Parsing failed\n"; cout << "stopped at:"; for (char const* s = info.stop; s != str+len; ++s) cout << static_cast(*s) << endl; } } // Test inputs: // The string in the header is "empty", the prefix list is empty. char const i1[8] = { 0xff,0x08,0x05, 'e','m','p','t','y' }; // The string in the header is "default route", the prefix list // has just one element, 0.0.0.0/0. char const i2[17] = { 0xff,0x11,0x0d, 'd','e','f','a','u','l','t',' ', 'r','o','u','t','e', 0x00 }; // The string in the header is "private address space", the prefix list // has the elements 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16. char const i3[32] = { 0xff,0x20,0x15, 'p','r','i','v','a','t','e',' ', 'a','d','d','r','e','s','s',' ', 's','p','a','c','e', 0x08,0x0a, 0x0c,0xac,0x10, 0x10,0xc0,0xa8 }; int main() { parse_ipv4(i1, sizeof(i1)); parse_ipv4(i2, sizeof(i2)); parse_ipv4(i3, sizeof(i3)); return 0; }