| /*============================================================================= |
| 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 <boost/spirit/include/classic_core.hpp> |
| #include <boost/spirit/include/classic_push_back_actor.hpp> |
| #include <boost/spirit/include/classic_if.hpp> |
| #include <boost/spirit/include/classic_for.hpp> |
| #include <boost/spirit/include/phoenix1.hpp> |
| #include <iostream> |
| #include <string> |
| #include <vector> |
| #include <algorithm> |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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<ipv4_prefix_data> prefixes; |
| |
| ipv4_data() |
| : packet_len(0),header_len(0){} |
| |
| }; |
| |
| struct ipv4 : public grammar<ipv4> |
| { |
| template <typename ScannerT> |
| 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<ScannerT> packet, payload, ipv4_prefix; |
| rule<ScannerT> 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<int>(*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; |
| } |
| |