#include #include #include #include #include #include #include namespace asio= boost::asio; namespace ip= asio::ip; using Alepha::IOStreams::Str; template< typename= Alepha::Capabilities< Alepha::IOStreams::Streamable > > struct Allocation_core; using Allocation= Allocation_core<>; struct IP_Address : ip::network_v6 { using ip::network_v6::network_v6; IP_Address( const network_v6 &n ) : ip::network_v6( n ) {} friend std::istream & operator >> ( std::istream &is, IP_Address &a ) { std::string s; is >> s; std::cerr << "Network string: " << s << std::endl; if( not s.contains( '/' ) ) { throw std::runtime_error{ "No subnet mask detected. Did you forget a `/`?" }; } a= ip::make_network_v6( s ); std::cerr << "Parsed into: `" << a << std::endl; return is; } }; template< typename > struct Allocation_core { IP_Address network; std::string owner; std::string name; std::string purpose; std::string country; std::string region; std::string city; std::string postal; std::vector< Allocation > subAllocations() const; std::vector< Allocation > available() const; }; std::vector< Allocation > spaces; template<> inline std::vector< Allocation > Allocation::subAllocations() const { std::vector< Allocation > rv; for( const auto &alloc: spaces ) { if( alloc.network.is_subnet_of( network ) ) rv.push_back( alloc ); } return rv; } struct Command { std::string name; std::string help; std::function< void ( std::vector< std::string > ) > run; }; std::map< std::string, Command > commands; void add_command( Command command ) { commands.insert( { command.name, command } ); } int main() try { spaces.push_back( { ip::make_network_v6( "2000::/3" ), "IANA", "Global Routing - v6", "The initial global routing table for v6" } ); spaces.push_back( { ip::make_network_v6( "2600::/8" ), "ARIN", "ARIN v6 RIR Allocation", "Large ARIN block." } ); spaces.push_back( { ip::make_network_v6( "2602:f6a8::/36" ), "IMP-NET", "Imperial Network", "Main Imperial Network Allocation." } ); add_command ( { "help", "List all commands", []( ... ) { for( const auto &[ name, command ]: commands ) { std::cout << name << " - " << command.help << std::endl; } } } ); add_command ( { "quit", "Exits the IP Address Manager", []( ... ) { ::exit( EXIT_SUCCESS ); } } ); add_command ( { "describe", "Describe a network range", []( const std::vector< std::string > &args ) { if( args.empty() ) throw std::runtime_error{ "Requires one argument." }; if( args.size() != 1 ) throw std::runtime_error{ "Too many arguments." }; const auto &network= boost::lexical_cast< IP_Address >( args.at( 0 ) ); std::cout << "Parsed: `" << network << "` -- it has a " << network.prefix_length() << " bit mask." << std::endl; const Allocation *p= nullptr; std::optional< IP_Address > found; auto better= [&]( const auto &where ) { return not found.has_value() or where.is_subnet_of( found.value() ); }; for( const auto &space: spaces ) { const auto &[ where, owner, name, purpose, country, state, city, postal ]= space; std::cerr << "Checking " << network << " against: " << where; if( network == where ) { std::cerr << " -- Exact match!" << std::endl; found= where; p= &space; break; } if( network.is_subnet_of( where ) and better( where ) ) { std::cerr << " -- Matched." << std::endl; found= where; p = &space; } else std::cerr << " -- No match." << std::endl; } std::cout << network << " was best matched by " << found.value() << std::endl; const auto &[ _, owner, name, purpose, country, state, city, postal ]= *p; std::cout << network << " is owned by " << owner << std::endl; std::cout << "It is called `" << name << "`" << std::endl; std::cout << "It is used for '" << purpose << "'" << std::endl; } } ); add_command ( { "subnets", "Describe a network range's subnets", []( const std::vector< std::string > &args ) { if( args.empty() ) throw std::runtime_error{ "Requires one argument." }; if( args.size() != 1 ) throw std::runtime_error{ "Too many arguments." }; const auto &network= boost::lexical_cast< IP_Address >( args.at( 0 ) ); std::cout << "Parsed: `" << network << "` -- it has a " << network.prefix_length() << " bit mask." << std::endl; const Allocation *p= nullptr; std::optional< IP_Address > found; auto better= [&]( const auto &where ) { return not found.has_value() or where.is_subnet_of( found.value() ); }; for( const auto &space: spaces ) { const auto &[ where, owner, name, purpose, country, state, city, postal ]= space; if( network.is_subnet_of( where ) and better( where ) ) { found= where; p = &space; } } std::cout << network << " was best matched by " << found.value() << std::endl; const auto &[ _, owner, name, purpose, country, state, city, postal ]= *p; std::cout << network << " is owned by " << owner << std::endl; std::cout << "It is called `" << name << "`" << std::endl; std::cout << "It is used for '" << purpose << "'" << std::endl; for( const auto &sub: p->subAllocations() ) { std::cout << sub.network << ": " << owner << std::endl; } } } ); while( not std::cin.eof() ) try { std::string line; getline( std::cin, line ); const auto parsed= Alepha::split( line, ' ' ); const auto &command= parsed.at( 0 ); auto args= parsed; args.erase( begin( args ) ); if( command.empty() ) continue; if( not commands.contains( command ) ) { throw std::runtime_error{ Str << "No such command `" << command << '`' }; } commands.at( command ).run( args ); } catch( const std::exception &e ) { std::cerr << "ERROR: " << e.what() << std::endl; } return EXIT_SUCCESS; } catch( const std::exception &ex ) { std::cerr << "FATAL ERROR: " << ex.what() << std::endl; return EXIT_FAILURE; }