// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)

package org.xbill.DNS;

import java.net.*;
import java.io.*;
import java.util.*;

/**
 * Well Known Services - Lists services offered by this host.
 *
 * @author Brian Wellington
 */

public class WKSRecord extends Record {

    private static final long serialVersionUID = -9104259763909119805L;

    public static class Protocol {
        /**
         * IP protocol identifiers.  This is basically copied out of RFC 1010.
         */

        private Protocol() {
        }

        /**
         * Internet Control Message
         */
        public static final int ICMP = 1;

        /**
         * Internet Group Management
         */
        public static final int IGMP = 2;

        /**
         * Gateway-to-Gateway
         */
        public static final int GGP = 3;

        /**
         * Stream
         */
        public static final int ST = 5;

        /**
         * Transmission Control
         */
        public static final int TCP = 6;

        /**
         * UCL
         */
        public static final int UCL = 7;

        /**
         * Exterior Gateway Protocol
         */
        public static final int EGP = 8;

        /**
         * any private interior gateway
         */
        public static final int IGP = 9;

        /**
         * BBN RCC Monitoring
         */
        public static final int BBN_RCC_MON = 10;

        /**
         * Network Voice Protocol
         */
        public static final int NVP_II = 11;

        /**
         * PUP
         */
        public static final int PUP = 12;

        /**
         * ARGUS
         */
        public static final int ARGUS = 13;

        /**
         * EMCON
         */
        public static final int EMCON = 14;

        /**
         * Cross Net Debugger
         */
        public static final int XNET = 15;

        /**
         * Chaos
         */
        public static final int CHAOS = 16;

        /**
         * User Datagram
         */
        public static final int UDP = 17;

        /**
         * Multiplexing
         */
        public static final int MUX = 18;

        /**
         * DCN Measurement Subsystems
         */
        public static final int DCN_MEAS = 19;

        /**
         * Host Monitoring
         */
        public static final int HMP = 20;

        /**
         * Packet Radio Measurement
         */
        public static final int PRM = 21;

        /**
         * XEROX NS IDP
         */
        public static final int XNS_IDP = 22;

        /**
         * Trunk-1
         */
        public static final int TRUNK_1 = 23;

        /**
         * Trunk-2
         */
        public static final int TRUNK_2 = 24;

        /**
         * Leaf-1
         */
        public static final int LEAF_1 = 25;

        /**
         * Leaf-2
         */
        public static final int LEAF_2 = 26;

        /**
         * Reliable Data Protocol
         */
        public static final int RDP = 27;

        /**
         * Internet Reliable Transaction
         */
        public static final int IRTP = 28;

        /**
         * ISO Transport Protocol Class 4
         */
        public static final int ISO_TP4 = 29;

        /**
         * Bulk Data Transfer Protocol
         */
        public static final int NETBLT = 30;

        /**
         * MFE Network Services Protocol
         */
        public static final int MFE_NSP = 31;

        /**
         * MERIT Internodal Protocol
         */
        public static final int MERIT_INP = 32;

        /**
         * Sequential Exchange Protocol
         */
        public static final int SEP = 33;

        /**
         * CFTP
         */
        public static final int CFTP = 62;

        /**
         * SATNET and Backroom EXPAK
         */
        public static final int SAT_EXPAK = 64;

        /**
         * MIT Subnet Support
         */
        public static final int MIT_SUBNET = 65;

        /**
         * MIT Remote Virtual Disk Protocol
         */
        public static final int RVD = 66;

        /**
         * Internet Pluribus Packet Core
         */
        public static final int IPPC = 67;

        /**
         * SATNET Monitoring
         */
        public static final int SAT_MON = 69;

        /**
         * Internet Packet Core Utility
         */
        public static final int IPCV = 71;

        /**
         * Backroom SATNET Monitoring
         */
        public static final int BR_SAT_MON = 76;

        /**
         * WIDEBAND Monitoring
         */
        public static final int WB_MON = 78;

        /**
         * WIDEBAND EXPAK
         */
        public static final int WB_EXPAK = 79;

        private static Mnemonic protocols = new Mnemonic("IP protocol",
                Mnemonic.CASE_LOWER);

        static {
            protocols.setMaximum(0xFF);
            protocols.setNumericAllowed(true);

            protocols.add(ICMP, "icmp");
            protocols.add(IGMP, "igmp");
            protocols.add(GGP, "ggp");
            protocols.add(ST, "st");
            protocols.add(TCP, "tcp");
            protocols.add(UCL, "ucl");
            protocols.add(EGP, "egp");
            protocols.add(IGP, "igp");
            protocols.add(BBN_RCC_MON, "bbn-rcc-mon");
            protocols.add(NVP_II, "nvp-ii");
            protocols.add(PUP, "pup");
            protocols.add(ARGUS, "argus");
            protocols.add(EMCON, "emcon");
            protocols.add(XNET, "xnet");
            protocols.add(CHAOS, "chaos");
            protocols.add(UDP, "udp");
            protocols.add(MUX, "mux");
            protocols.add(DCN_MEAS, "dcn-meas");
            protocols.add(HMP, "hmp");
            protocols.add(PRM, "prm");
            protocols.add(XNS_IDP, "xns-idp");
            protocols.add(TRUNK_1, "trunk-1");
            protocols.add(TRUNK_2, "trunk-2");
            protocols.add(LEAF_1, "leaf-1");
            protocols.add(LEAF_2, "leaf-2");
            protocols.add(RDP, "rdp");
            protocols.add(IRTP, "irtp");
            protocols.add(ISO_TP4, "iso-tp4");
            protocols.add(NETBLT, "netblt");
            protocols.add(MFE_NSP, "mfe-nsp");
            protocols.add(MERIT_INP, "merit-inp");
            protocols.add(SEP, "sep");
            protocols.add(CFTP, "cftp");
            protocols.add(SAT_EXPAK, "sat-expak");
            protocols.add(MIT_SUBNET, "mit-subnet");
            protocols.add(RVD, "rvd");
            protocols.add(IPPC, "ippc");
            protocols.add(SAT_MON, "sat-mon");
            protocols.add(IPCV, "ipcv");
            protocols.add(BR_SAT_MON, "br-sat-mon");
            protocols.add(WB_MON, "wb-mon");
            protocols.add(WB_EXPAK, "wb-expak");
        }

        /**
         * Converts an IP protocol value into its textual representation
         */
        public static String
        string(int type) {
            return protocols.getText(type);
        }

        /**
         * Converts a textual representation of an IP protocol into its
         * numeric code.  Integers in the range 0..255 are also accepted.
         *
         * @param s The textual representation of the protocol
         * @return The protocol code, or -1 on error.
         */
        public static int
        value(String s) {
            return protocols.getValue(s);
        }
    }

    public static class Service {
        /**
         * TCP/UDP services.  This is basically copied out of RFC 1010,
         * with MIT-ML-DEV removed, as it is not unique, and the description
         * of SWIFT-RVF fixed.
         */

        private Service() {
        }

        /**
         * Remote Job Entry
         */
        public static final int RJE = 5;

        /**
         * Echo
         */
        public static final int ECHO = 7;

        /**
         * Discard
         */
        public static final int DISCARD = 9;

        /**
         * Active Users
         */
        public static final int USERS = 11;

        /**
         * Daytime
         */
        public static final int DAYTIME = 13;

        /**
         * Quote of the Day
         */
        public static final int QUOTE = 17;

        /**
         * Character Generator
         */
        public static final int CHARGEN = 19;

        /**
         * File Transfer [Default Data]
         */
        public static final int FTP_DATA = 20;

        /**
         * File Transfer [Control]
         */
        public static final int FTP = 21;

        /**
         * Telnet
         */
        public static final int TELNET = 23;

        /**
         * Simple Mail Transfer
         */
        public static final int SMTP = 25;

        /**
         * NSW User System FE
         */
        public static final int NSW_FE = 27;

        /**
         * MSG ICP
         */
        public static final int MSG_ICP = 29;

        /**
         * MSG Authentication
         */
        public static final int MSG_AUTH = 31;

        /**
         * Display Support Protocol
         */
        public static final int DSP = 33;

        /**
         * Time
         */
        public static final int TIME = 37;

        /**
         * Resource Location Protocol
         */
        public static final int RLP = 39;

        /**
         * Graphics
         */
        public static final int GRAPHICS = 41;

        /**
         * Host Name Server
         */
        public static final int NAMESERVER = 42;

        /**
         * Who Is
         */
        public static final int NICNAME = 43;

        /**
         * MPM FLAGS Protocol
         */
        public static final int MPM_FLAGS = 44;

        /**
         * Message Processing Module [recv]
         */
        public static final int MPM = 45;

        /**
         * MPM [default send]
         */
        public static final int MPM_SND = 46;

        /**
         * NI FTP
         */
        public static final int NI_FTP = 47;

        /**
         * Login Host Protocol
         */
        public static final int LOGIN = 49;

        /**
         * IMP Logical Address Maintenance
         */
        public static final int LA_MAINT = 51;

        /**
         * Domain Name Server
         */
        public static final int DOMAIN = 53;

        /**
         * ISI Graphics Language
         */
        public static final int ISI_GL = 55;

        /**
         * NI MAIL
         */
        public static final int NI_MAIL = 61;

        /**
         * VIA Systems - FTP
         */
        public static final int VIA_FTP = 63;

        /**
         * TACACS-Database Service
         */
        public static final int TACACS_DS = 65;

        /**
         * Bootstrap Protocol Server
         */
        public static final int BOOTPS = 67;

        /**
         * Bootstrap Protocol Client
         */
        public static final int BOOTPC = 68;

        /**
         * Trivial File Transfer
         */
        public static final int TFTP = 69;

        /**
         * Remote Job Service
         */
        public static final int NETRJS_1 = 71;

        /**
         * Remote Job Service
         */
        public static final int NETRJS_2 = 72;

        /**
         * Remote Job Service
         */
        public static final int NETRJS_3 = 73;

        /**
         * Remote Job Service
         */
        public static final int NETRJS_4 = 74;

        /**
         * Finger
         */
        public static final int FINGER = 79;

        /**
         * HOSTS2 Name Server
         */
        public static final int HOSTS2_NS = 81;

        /**
         * SU/MIT Telnet Gateway
         */
        public static final int SU_MIT_TG = 89;

        /**
         * MIT Dover Spooler
         */
        public static final int MIT_DOV = 91;

        /**
         * Device Control Protocol
         */
        public static final int DCP = 93;

        /**
         * SUPDUP
         */
        public static final int SUPDUP = 95;

        /**
         * Swift Remote Virtual File Protocol
         */
        public static final int SWIFT_RVF = 97;

        /**
         * TAC News
         */
        public static final int TACNEWS = 98;

        /**
         * Metagram Relay
         */
        public static final int METAGRAM = 99;

        /**
         * NIC Host Name Server
         */
        public static final int HOSTNAME = 101;

        /**
         * ISO-TSAP
         */
        public static final int ISO_TSAP = 102;

        /**
         * X400
         */
        public static final int X400 = 103;

        /**
         * X400-SND
         */
        public static final int X400_SND = 104;

        /**
         * Mailbox Name Nameserver
         */
        public static final int CSNET_NS = 105;

        /**
         * Remote Telnet Service
         */
        public static final int RTELNET = 107;

        /**
         * Post Office Protocol - Version 2
         */
        public static final int POP_2 = 109;

        /**
         * SUN Remote Procedure Call
         */
        public static final int SUNRPC = 111;

        /**
         * Authentication Service
         */
        public static final int AUTH = 113;

        /**
         * Simple File Transfer Protocol
         */
        public static final int SFTP = 115;

        /**
         * UUCP Path Service
         */
        public static final int UUCP_PATH = 117;

        /**
         * Network News Transfer Protocol
         */
        public static final int NNTP = 119;

        /**
         * HYDRA Expedited Remote Procedure
         */
        public static final int ERPC = 121;

        /**
         * Network Time Protocol
         */
        public static final int NTP = 123;

        /**
         * Locus PC-Interface Net Map Server
         */
        public static final int LOCUS_MAP = 125;

        /**
         * Locus PC-Interface Conn Server
         */
        public static final int LOCUS_CON = 127;

        /**
         * Password Generator Protocol
         */
        public static final int PWDGEN = 129;

        /**
         * CISCO FNATIVE
         */
        public static final int CISCO_FNA = 130;

        /**
         * CISCO TNATIVE
         */
        public static final int CISCO_TNA = 131;

        /**
         * CISCO SYSMAINT
         */
        public static final int CISCO_SYS = 132;

        /**
         * Statistics Service
         */
        public static final int STATSRV = 133;

        /**
         * INGRES-NET Service
         */
        public static final int INGRES_NET = 134;

        /**
         * Location Service
         */
        public static final int LOC_SRV = 135;

        /**
         * PROFILE Naming System
         */
        public static final int PROFILE = 136;

        /**
         * NETBIOS Name Service
         */
        public static final int NETBIOS_NS = 137;

        /**
         * NETBIOS Datagram Service
         */
        public static final int NETBIOS_DGM = 138;

        /**
         * NETBIOS Session Service
         */
        public static final int NETBIOS_SSN = 139;

        /**
         * EMFIS Data Service
         */
        public static final int EMFIS_DATA = 140;

        /**
         * EMFIS Control Service
         */
        public static final int EMFIS_CNTL = 141;

        /**
         * Britton-Lee IDM
         */
        public static final int BL_IDM = 142;

        /**
         * Survey Measurement
         */
        public static final int SUR_MEAS = 243;

        /**
         * LINK
         */
        public static final int LINK = 245;

        private static Mnemonic services = new Mnemonic("TCP/UDP service",
                Mnemonic.CASE_LOWER);

        static {
            services.setMaximum(0xFFFF);
            services.setNumericAllowed(true);

            services.add(RJE, "rje");
            services.add(ECHO, "echo");
            services.add(DISCARD, "discard");
            services.add(USERS, "users");
            services.add(DAYTIME, "daytime");
            services.add(QUOTE, "quote");
            services.add(CHARGEN, "chargen");
            services.add(FTP_DATA, "ftp-data");
            services.add(FTP, "ftp");
            services.add(TELNET, "telnet");
            services.add(SMTP, "smtp");
            services.add(NSW_FE, "nsw-fe");
            services.add(MSG_ICP, "msg-icp");
            services.add(MSG_AUTH, "msg-auth");
            services.add(DSP, "dsp");
            services.add(TIME, "time");
            services.add(RLP, "rlp");
            services.add(GRAPHICS, "graphics");
            services.add(NAMESERVER, "nameserver");
            services.add(NICNAME, "nicname");
            services.add(MPM_FLAGS, "mpm-flags");
            services.add(MPM, "mpm");
            services.add(MPM_SND, "mpm-snd");
            services.add(NI_FTP, "ni-ftp");
            services.add(LOGIN, "login");
            services.add(LA_MAINT, "la-maint");
            services.add(DOMAIN, "domain");
            services.add(ISI_GL, "isi-gl");
            services.add(NI_MAIL, "ni-mail");
            services.add(VIA_FTP, "via-ftp");
            services.add(TACACS_DS, "tacacs-ds");
            services.add(BOOTPS, "bootps");
            services.add(BOOTPC, "bootpc");
            services.add(TFTP, "tftp");
            services.add(NETRJS_1, "netrjs-1");
            services.add(NETRJS_2, "netrjs-2");
            services.add(NETRJS_3, "netrjs-3");
            services.add(NETRJS_4, "netrjs-4");
            services.add(FINGER, "finger");
            services.add(HOSTS2_NS, "hosts2-ns");
            services.add(SU_MIT_TG, "su-mit-tg");
            services.add(MIT_DOV, "mit-dov");
            services.add(DCP, "dcp");
            services.add(SUPDUP, "supdup");
            services.add(SWIFT_RVF, "swift-rvf");
            services.add(TACNEWS, "tacnews");
            services.add(METAGRAM, "metagram");
            services.add(HOSTNAME, "hostname");
            services.add(ISO_TSAP, "iso-tsap");
            services.add(X400, "x400");
            services.add(X400_SND, "x400-snd");
            services.add(CSNET_NS, "csnet-ns");
            services.add(RTELNET, "rtelnet");
            services.add(POP_2, "pop-2");
            services.add(SUNRPC, "sunrpc");
            services.add(AUTH, "auth");
            services.add(SFTP, "sftp");
            services.add(UUCP_PATH, "uucp-path");
            services.add(NNTP, "nntp");
            services.add(ERPC, "erpc");
            services.add(NTP, "ntp");
            services.add(LOCUS_MAP, "locus-map");
            services.add(LOCUS_CON, "locus-con");
            services.add(PWDGEN, "pwdgen");
            services.add(CISCO_FNA, "cisco-fna");
            services.add(CISCO_TNA, "cisco-tna");
            services.add(CISCO_SYS, "cisco-sys");
            services.add(STATSRV, "statsrv");
            services.add(INGRES_NET, "ingres-net");
            services.add(LOC_SRV, "loc-srv");
            services.add(PROFILE, "profile");
            services.add(NETBIOS_NS, "netbios-ns");
            services.add(NETBIOS_DGM, "netbios-dgm");
            services.add(NETBIOS_SSN, "netbios-ssn");
            services.add(EMFIS_DATA, "emfis-data");
            services.add(EMFIS_CNTL, "emfis-cntl");
            services.add(BL_IDM, "bl-idm");
            services.add(SUR_MEAS, "sur-meas");
            services.add(LINK, "link");
        }

        /**
         * Converts a TCP/UDP service port number into its textual
         * representation.
         */
        public static String
        string(int type) {
            return services.getText(type);
        }

        /**
         * Converts a textual representation of a TCP/UDP service into its
         * port number.  Integers in the range 0..65535 are also accepted.
         *
         * @param s The textual representation of the service.
         * @return The port number, or -1 on error.
         */
        public static int
        value(String s) {
            return services.getValue(s);
        }
    }

    private byte[] address;
    private int protocol;
    private int[] services;

    WKSRecord() {
    }

    Record
    getObject() {
        return new WKSRecord();
    }

    /**
     * Creates a WKS Record from the given data
     *
     * @param address  The IP address
     * @param protocol The IP protocol number
     * @param services An array of supported services, represented by port number.
     */
    public WKSRecord(Name name, int dclass, long ttl, InetAddress address, int protocol,
                     int[] services) {
        super(name, Type.WKS, dclass, ttl);
        if (Address.familyOf(address) != Address.IPv4)
            throw new IllegalArgumentException("invalid IPv4 address");
        this.address = address.getAddress();
        this.protocol = checkU8("protocol", protocol);
        for (int i = 0; i < services.length; i++) {
            checkU16("service", services[i]);
        }
        this.services = new int[services.length];
        System.arraycopy(services, 0, this.services, 0, services.length);
        Arrays.sort(this.services);
    }

    void
    rrFromWire(DNSInput in) throws IOException {
        address = in.readByteArray(4);
        protocol = in.readU8();
        byte[] array = in.readByteArray();
        List list = new ArrayList();
        for (int i = 0; i < array.length; i++) {
            for (int j = 0; j < 8; j++) {
                int octet = array[i] & 0xFF;
                if ((octet & (1 << (7 - j))) != 0) {
                    list.add(new Integer(i * 8 + j));
                }
            }
        }
        services = new int[list.size()];
        for (int i = 0; i < list.size(); i++) {
            services[i] = ((Integer) list.get(i)).intValue();
        }
    }

    void
    rdataFromString(Tokenizer st, Name origin) throws IOException {
        String s = st.getString();
        address = Address.toByteArray(s, Address.IPv4);
        if (address == null)
            throw st.exception("invalid address");

        s = st.getString();
        protocol = Protocol.value(s);
        if (protocol < 0) {
            throw st.exception("Invalid IP protocol: " + s);
        }

        List list = new ArrayList();
        while (true) {
            Tokenizer.Token t = st.get();
            if (!t.isString())
                break;
            int service = Service.value(t.value);
            if (service < 0) {
                throw st.exception("Invalid TCP/UDP service: " +
                        t.value);
            }
            list.add(new Integer(service));
        }
        st.unget();
        services = new int[list.size()];
        for (int i = 0; i < list.size(); i++) {
            services[i] = ((Integer) list.get(i)).intValue();
        }
    }

    /**
     * Converts rdata to a String
     */
    String
    rrToString() {
        StringBuffer sb = new StringBuffer();
        sb.append(Address.toDottedQuad(address));
        sb.append(" ");
        sb.append(protocol);
        for (int i = 0; i < services.length; i++) {
            sb.append(" " + services[i]);
        }
        return sb.toString();
    }

    /**
     * Returns the IP address.
     */
    public InetAddress
    getAddress() {
        try {
            return InetAddress.getByAddress(address);
        } catch (UnknownHostException e) {
            return null;
        }
    }

    /**
     * Returns the IP protocol.
     */
    public int
    getProtocol() {
        return protocol;
    }

    /**
     * Returns the services provided by the host on the specified address.
     */
    public int[]
    getServices() {
        return services;
    }

    void
    rrToWire(DNSOutput out, Compression c, boolean canonical) {
        out.writeByteArray(address);
        out.writeU8(protocol);
        int highestPort = services[services.length - 1];
        byte[] array = new byte[highestPort / 8 + 1];
        for (int i = 0; i < services.length; i++) {
            int port = services[i];
            array[port / 8] |= (1 << (7 - port % 8));
        }
        out.writeByteArray(array);
    }

}
