/*
 * ipnameinfo.{cc,hh} -- element stores IP name-to-number mappings
 * Eddie Kohler
 *
 * Copyright (c) 2005 Regents of the University of California
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, subject to the conditions
 * listed in the Click LICENSE file. These conditions include: you must
 * preserve this copyright notice, and you cannot mention the copyright
 * holders in advertising related to the Software without their permission.
 * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
 * notice is a summary of the Click LICENSE file; the license in that file is
 * legally binding.
 */

#include <click/config.h>
#include "ipnameinfo.hh"
#include <click/nameinfo.hh>
#include <click/confparse.hh>
#include <clicknet/ip.h>
#include <clicknet/icmp.h>
#include <clicknet/tcp.h>
#include <clicknet/udp.h>
#if CLICK_USERLEVEL && HAVE_NETDB_H
# include <netdb.h>
#endif
CLICK_DECLS

static const StaticNameDB::Entry ip_protos[] = {
    { "icmp", IP_PROTO_ICMP },
    { "igmp", IP_PROTO_IGMP },
    { "ipip", IP_PROTO_IPIP },
    { "payload", IP_PROTO_PAYLOAD },
    { "tcp", IP_PROTO_TCP },
    { "tcpudp", IP_PROTO_TCP_OR_UDP },
    { "transp", IP_PROTO_TRANSP },
    { "udp", IP_PROTO_UDP }
};

static const StaticNameDB::Entry icmp_types[] = {
    { "echo", ICMP_ECHO },
    { "echo-reply", ICMP_ECHOREPLY },
    { "inforeq", ICMP_IREQ },
    { "inforeq-reply", ICMP_IREQREPLY },
    { "maskreq", ICMP_MASKREQ },
    { "maskreq-reply", ICMP_MASKREQREPLY },
    { "parameterproblem", ICMP_PARAMPROB },
    { "redirect", ICMP_REDIRECT },
    { "routeradvert", ICMP_ROUTERADVERT },
    { "routersolicit", ICMP_ROUTERSOLICIT },
    { "sourcequench", ICMP_SOURCEQUENCH },
    { "timeexceeded", ICMP_TIMXCEED },
    { "timestamp", ICMP_TSTAMP },
    { "timestamp-reply", ICMP_TSTAMPREPLY },
    { "unreachable", ICMP_UNREACH },
};

static const StaticNameDB::Entry icmp_unreach_codes[] = {
    { "filterprohibited", ICMP_UNREACH_FILTER_PROHIB },
    { "host", ICMP_UNREACH_HOST },
    { "hostprecedence", ICMP_UNREACH_HOST_PRECEDENCE },
    { "hostprohibited", ICMP_UNREACH_HOST_PROHIB },
    { "hostunknown", ICMP_UNREACH_HOST_UNKNOWN },
    { "isolated", ICMP_UNREACH_ISOLATED },
    { "needfrag", ICMP_UNREACH_NEEDFRAG },
    { "net", ICMP_UNREACH_NET },
    { "netprohibited", ICMP_UNREACH_NET_PROHIB },
    { "netunknown", ICMP_UNREACH_NET_UNKNOWN },
    { "port", ICMP_UNREACH_PORT },
    { "precedencecutoff", ICMP_UNREACH_PRECEDENCE_CUTOFF },
    { "protocol", ICMP_UNREACH_PROTOCOL },
    { "srcroutefail", ICMP_UNREACH_SRCFAIL },
    { "toshost", ICMP_UNREACH_TOSHOST },
    { "tosnet", ICMP_UNREACH_TOSNET }
};

static const StaticNameDB::Entry icmp_redirect_codes[] = {
    { "host", ICMP_REDIRECT_HOST },
    { "net", ICMP_REDIRECT_NET },
    { "toshost", ICMP_REDIRECT_TOSHOST },
    { "tosnet", ICMP_REDIRECT_TOSNET }
};

static const StaticNameDB::Entry icmp_timxceed_codes[] = {
    { "reassembly", ICMP_TIMXCEED_REASSEMBLY },
    { "transit", ICMP_TIMXCEED_TRANSIT }
};

static const StaticNameDB::Entry icmp_paramprob_codes[] = {
    { "erroratptr", ICMP_PARAMPROB_ERRATPTR },
    { "length", ICMP_PARAMPROB_LENGTH },
    { "missingopt", ICMP_PARAMPROB_OPTABSENT }
};

static const StaticNameDB::Entry known_ports[] = {
    { "auth", 113 },
    { "bootpc", 68 },
    { "bootps", 67 },
    { "chargen", 19 },
    { "daytime", 13 },
    { "discard", 9 },
    { "dns", 53 },
    { "domain", 53 },
    { "echo", 7 },
    { "finger", 79 },
    { "ftp", 21 },
    { "ftp-data", 20 },
    { "https", 443 },
    { "imap3", 220 },
    { "imaps", 993 },
    { "irc", 194 },
    { "netbios-dgm", 138 },
    { "netbios-ns", 137 },
    { "netbios-ssn", 139 },
    { "nntp", 119 },
    { "ntp", 123 },
    { "pop3", 110 },
    { "pop3s", 995 },
    { "rip", 520 },
    { "route", 520 },
    { "smtp", 25 },
    { "snmp", 161 },
    { "snmp-trap", 162 },
    { "ssh", 22 },
    { "sunrpc", 111 },
    { "telnet", 23 },
    { "tftp", 69 },
    { "www", 80 }
};

namespace {

#if CLICK_USERLEVEL && HAVE_NETDB_H
class ServicesNameDB : public NameDB { public:
    ServicesNameDB(uint32_t type)	: NameDB(type, String(), 4) { }
    bool query(const String &name, void *value, int vsize);
};
bool
ServicesNameDB::query(const String &name, void *value, int vsize)
{
    assert(vsize == 4);

    // Check common case: integer
    int32_t crap;
    if (cp_integer(name, &crap))
	return false;

    if (type() == NameInfo::T_IP_PROTO) {
	if (const struct protoent *proto = getprotobyname(name.c_str())) {
	    *reinterpret_cast<uint32_t*>(value) = proto->p_proto;
	    return true;
	}
    }

    if (type() >= NameInfo::T_IP_PORT && type() < NameInfo::T_IP_PORT + 256) {
	int proto = type() - NameInfo::T_IP_PORT;
	const char *proto_name;
	if (proto == IP_PROTO_TCP)
	    proto_name = "tcp";
	else if (proto == IP_PROTO_UDP)
	    proto_name = "udp";
	else if (const struct protoent *pe = getprotobynumber(proto))
	    proto_name = pe->p_name;
	else
	    return false;
	if (const struct servent *srv = getservbyname(name.c_str(), proto_name)) {
	    *reinterpret_cast<uint32_t*>(value) = ntohs(srv->s_port);
	    return true;
	}
    }

    return false;
}
#endif

}


static NameDB *dbs[11];

void
IPNameInfo::static_initialize()
{
#if CLICK_USERLEVEL && HAVE_NETDB_H
    dbs[0] = new ServicesNameDB(NameInfo::T_IP_PROTO);
#endif
    dbs[1] = new StaticNameDB(NameInfo::T_IP_PROTO, String(), ip_protos, sizeof(ip_protos) / sizeof(ip_protos[0]));
    dbs[2] = new StaticNameDB(NameInfo::T_ICMP_TYPE, String(), icmp_types, sizeof(icmp_types) / sizeof(icmp_types[0]));
    dbs[3] = new StaticNameDB(NameInfo::T_ICMP_CODE + ICMP_UNREACH, String(), icmp_unreach_codes, sizeof(icmp_unreach_codes) / sizeof(icmp_unreach_codes[0]));
    dbs[4] = new StaticNameDB(NameInfo::T_ICMP_CODE + ICMP_REDIRECT, String(), icmp_redirect_codes, sizeof(icmp_redirect_codes) / sizeof(icmp_redirect_codes[0]));
    dbs[5] = new StaticNameDB(NameInfo::T_ICMP_CODE + ICMP_TIMXCEED, String(), icmp_timxceed_codes, sizeof(icmp_timxceed_codes) / sizeof(icmp_timxceed_codes[0]));
    dbs[6] = new StaticNameDB(NameInfo::T_ICMP_CODE + ICMP_PARAMPROB, String(), icmp_paramprob_codes, sizeof(icmp_paramprob_codes) / sizeof(icmp_paramprob_codes[0]));
#if CLICK_USERLEVEL && HAVE_NETDB_H
    dbs[7] = new ServicesNameDB(NameInfo::T_TCP_PORT);
    dbs[8] = new ServicesNameDB(NameInfo::T_UDP_PORT);
#endif
    dbs[9] = new StaticNameDB(NameInfo::T_TCP_PORT, String(), known_ports, sizeof(known_ports) / sizeof(known_ports[0]));
    dbs[10] = new StaticNameDB(NameInfo::T_UDP_PORT, String(), known_ports, sizeof(known_ports) / sizeof(known_ports[0]));
    for (int i = 0; i < 11; i++)
	if (dbs[i])
	    NameInfo::installdb(dbs[i], 0);
}

void
IPNameInfo::static_cleanup()
{
    for (int i = 0; i < 11; i++)
	if (dbs[i]) {
	    NameInfo::removedb(dbs[i]);
	    delete dbs[i];
	    dbs[i] = 0;
	}
}

CLICK_ENDDECLS
EXPORT_ELEMENT(IPNameInfo)


syntax highlighted by Code2HTML, v. 0.9.1