/* * @(#) DNSRecord.java - Class for representing DNS resource record. * (c) 1999-2001 Ivan Maidanski http://ivmai.chat.ru * Freeware class library sources. All rights reserved. ** * Language: Java [pure] * Tested with: JDK v1.1.6 * Last modified: 2001-01-31 23:45:00 GMT+03:00 */ /* * This software is the proprietary information of the author. ** * Permission to use, copy, and distribute this software and its * documentation for non-commercial purposes and without fee is * hereby granted provided that this copyright notice appears in all * copies. ** * This software should not be modified in any way; any found bug * should be reported to the author. ** * The author disclaims all warranties with regard to this software, * including all implied warranties of merchantability and fitness. * In no event shall the author be liable for any special, indirect * or consequential damages or any damages whatsoever resulting from * loss of use, data or profits, whether in an action of contract, * negligence or other tortuous action, arising out of or in * connection with the use or performance of this software. */ package ivmai.dns; import java.io.InvalidObjectException; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; import java.net.InetAddress; import java.net.UnknownHostException; import ivmai.util.ByteVector; import ivmai.util.Immutable; import ivmai.util.Indexable; import ivmai.util.JavaConsts; import ivmai.util.ReallyCloneable; import ivmai.util.Sortable; import ivmai.util.UnsignedInt; import ivmai.util.Verifiable; /** * Class for representing DNS resource record (as defined in * RFC1035). ** * @version 3.0 * @author Ivan Maidanski */ public final class DNSRecord implements Immutable, ReallyCloneable, Serializable, Indexable, Sortable, Verifiable { /** * The class version unique identifier for serialization * interoperability. ** * @since 2.2 */ private static final long serialVersionUID = 7339921300775713171L; /** * NOTE: These are standard DNS resource types (qType or rType). */ public static final int A = 1; /* RFC1035 */ public static final int NS = 2; /* RFC1035 */ public static final int MD = 3; /* RFC1035 */ public static final int MF = 4; /* RFC1035 */ public static final int CNAME = 5; /* RFC1035 */ public static final int SOA = 6; /* RFC1035 */ public static final int MB = 7; /* RFC1035 */ public static final int MG = 8; /* RFC1035 */ public static final int MR = 9; /* RFC1035 */ public static final int NULL = 10; /* RFC1035 */ public static final int WKS = 11; /* RFC1035 */ public static final int PTR = 12; /* RFC1035 */ public static final int HINFO = 13; /* RFC1035 */ public static final int MINFO = 14; /* RFC1035 */ public static final int MX = 15; /* RFC1035 */ public static final int TXT = 16; /* RFC1035 */ public static final int RP = 17; /* RFC1183 */ public static final int AFSDB = 18; /* RFC1183 */ public static final int X25 = 19; /* RFC1183 */ public static final int ISDN = 20; /* RFC1183 */ public static final int RT = 21; /* RFC1183 */ public static final int NSAP = 22; /* RFC1706 */ public static final int NSAP_PTR = 23; /* RFC1348 */ public static final int SIG = 24; /* RFC2535 */ public static final int KEY = 25; /* RFC2535 */ public static final int PX = 26; /* RFC2163 */ public static final int GPOS = 27; /* RFC1712 */ public static final int AAAA = 28; /* RFC1886 */ public static final int LOC = 29; /* RFC1876 */ public static final int NXT = 30; /* RFC2065 */ public static final int EID = 31; /* RFC1992 */ public static final int NIMLOC = 32; /* RFC1992 */ public static final int SRV = 33; /* RFC2052 */ public static final int ATMA = 34; /* RFC2225 */ public static final int NAPTR = 35; /* RFC2168 */ public static final int KX = 36; /* RFC2230 */ public static final int CERT = 37; /* RFC2538 */ public static final int DNAME = 39; /* RFC2672 */ /** * NOTE: These are DNS meta-data records types (rType). ** * @since 2.3 */ public static final int OPT = 41; /* RFC2671 */ public static final int TKEY = 249; /* RFC2930 */ public static final int TSIG = 250; /* RFC2845 */ /** * NOTE: These are standard DNS additional question types (qType). */ public static final int IXFR = 251; /* (zone update) RFC1995 */ public static final int AXFR = 252; /* (entire zone) RFC1035 */ public static final int MAILB = 253; /* (MB/MG/MR/MINFO) RFC1035 */ public static final int MAILA = 254; /* (MD/MF/MX) RFC1035 */ /** * NOTE: This is standard DNS question "*" type/class (qType or * qClass). */ public static final int ANY = 255; /* (wildcard) RFC1035 */ /** * NOTE: These are standard DNS resource classes (qClass or rClass). */ public static final int IN = 1; /* (Internet) RFC1035 */ public static final int CH = 3; /* (Chaos) RFC1035 */ public static final int HS = 4; /* (Hesiod) RFC1035 */ /** * NOTE: This is standard DNS record "NONE" class (rClass). */ public static final int NONE = 254; /* RFC2136 */ /** * NOTE: These are all indices for SOA resource record rData fields * (RFC1035). ** * @since 3.0 */ public static final int SOA_HOST_INDEX = 0; public static final int SOA_EMAIL_INDEX = 1; public static final int SOA_SERIAL_INDEX = 2; public static final int SOA_REFRESH_INDEX = 3; public static final int SOA_RETRY_INDEX = 4; public static final int SOA_EXPIRE_INDEX = 5; public static final int SOA_MINTTL_INDEX = 6; /** * NOTE: The default values for SOA resource record rData fields * (RFC1035). */ public static final int DEFAULT_SOA_REFRESH = 3600; public static final int DEFAULT_SOA_RETRY = 600; public static final int DEFAULT_SOA_EXPIRE = 3600000; public static final int DEFAULT_TTL = 3600; /** * NOTE: These are lengthes (in bytes) of rType, rClass, ttl, * rDataLen. ** * @since 2.1 */ public static final int RTYPE_LENGTH = 2; public static final int RCLASS_LENGTH = 2; public static final int TTL_LENGTH = 4; public static final int RDATA_LEN_LENGTH = 2; /** * NOTE: This is the length of an IPv4 address value. ** * @since 2.2 */ public static final int INET_ADDR_LEN = 4; /** * NOTE: These are integer bit masks of defined types. ** * @since 2.1 */ public static final int RTYPE_MASK = RTYPE_LENGTH < JavaConsts.INT_LENGTH ? ~(-1 << (RTYPE_LENGTH * JavaConsts.BYTE_SIZE)) : -1; public static final int RCLASS_MASK = RCLASS_LENGTH < JavaConsts.INT_LENGTH ? ~(-1 << (RCLASS_LENGTH * JavaConsts.BYTE_SIZE)) : -1; public static final int TTL_MASK = TTL_LENGTH < JavaConsts.INT_LENGTH ? ~(-1 << (TTL_LENGTH * JavaConsts.BYTE_SIZE)) : -1; public static final int RDATA_LEN_MASK = RDATA_LEN_LENGTH < JavaConsts.INT_LENGTH ? ~(-1 << (RDATA_LEN_LENGTH * JavaConsts.BYTE_SIZE)) : -1; /** * NOTE: This string represents a packed sorted reference list of * abbreviations for the standard rClass values (defined in * RFC1035). This string is used only by UnsignedInt * parseAbbreviation(str) method. ** * @since 3.0 */ public static final String RCLASS_REFER_ABBREVS = ",RCLASS,," + "*,,,,255," + "ANY,,255," + "CH,,,,,3," + "HS,,,,,4," + "IN,,,,,1," + "NONE,254,"; /** * NOTE: This string represents a packed sorted reference list of * abbreviations for the standard rType values (first defined in * RFC1035). This string is used only by UnsignedInt * parseAbbreviation(str) method. ** * @since 3.0 */ public static final String RTYPE_REFER_ABBREVS = ",RTYPE,,,,,," + "*,,,,,,,255," + "A,,,,,,,,,1," + "AAAA,,,,,28," + "AFSDB,,,,18," + "ANY,,,,,255," + "ATMA,,,,,34," + "AXFR,,,,252," + "CERT,,,,,37," + "CNAME,,,,,5," + "DNAME,,,,39," + "EID,,,,,,31," + "GPOS,,,,,27," + "HINFO,,,,13," + "ISDN,,,,,20," + "IXFR,,,,251," + "KEY,,,,,,25," + "KX,,,,,,,36," + "LOC,,,,,,29," + "MAILA,,,254," + "MAILB,,,253," + "MB,,,,,,,,7," + "MD,,,,,,,,3," + "MF,,,,,,,,4," + "MG,,,,,,,,8," + "MINFO,,,,14," + "MR,,,,,,,,9," + "MX,,,,,,,15," + "NAPTR,,,,35," + "NIMLOC,,,32," + "NS,,,,,,,,2," + "NSAP,,,,,22," + "NSAP-PTR,23," + "NSAP_PTR,23," + "NULL,,,,,10," + "NXT,,,,,,30," + "OPT,,,,,,41," + "PTR,,,,,,12," + "PX,,,,,,,26," + "RP,,,,,,,17," + "RT,,,,,,,21," + "SIG,,,,,,24," + "SOA,,,,,,,6," + "SRV,,,,,,33," + "TKEY,,,,249," + "TSIG,,,,250," + "TXT,,,,,,16," + "WKS,,,,,,11," + "X25,,,,,,19,"; /** * NOTE: This string represents a packed abbreviations list for the * standard wksProtocol values (defined in RFC1700). The string is * used only by UnsignedInt toAbbreviation(wksProtocol) method. ** * @since 3.0 */ public static final String WKS_PROTOCOL_ABBREVS = ",,,,,,,,,,,," + "0,,,,,,,,,,," + "ICMP,,,,,,,," + "IGMP,,,,,,,," + "GGP,,,,,,,,," + "IP,,,,,,,,,," + "ST,,,,,,,,,," + "TCP,,,,,,,,," + "UCL,,,,,,,,," + "EGP,,,,,,,,," + "IGP,,,,,,,,," + "BBN-RCC-MON," + "NVP-II,,,,,," + "PUP,,,,,,,,," + "ARGUS,,,,,,," + "EMCON,,,,,,," + "XNET,,,,,,,," + "CHAOS,,,,,,," + "UDP,,,,,,,,," + "MUX,,,,,,,,," + "DCN-MEAS,,,," + "HMP,,,,,,,,," + "PRM,,,,,,,,," + "XNS-IDP,,,,," + "TRUNK-1,,,,," + "TRUNK-2,,,,," + "LEAF-1,,,,,," + "LEAF-2,,,,,," + "RDP,,,,,,,,," + "IRTP,,,,,,,," + "ISO-TP4,,,,," + "NETBLT,,,,,," + "MFE-NSP,,,,," + "MERIT-INP,,," + "SEP,,,,,,,,," + ",,,,,,,,,,,," + "IDPR,,,,,,,," + "XTP,,,,,,,,," + "DDP,,,,,,,,," + "IDPR-CMTP,,," + "TPPP,,,,,,,," + "IL,,,,,,,,,," + "SIP,,,,,,,,," + "SDRP,,,,,,,," + "SR,,,,,,,,,," + "FRAG,,,,,,,," + "IDRP,,,,,,,," + "RSVP,,,,,,,," + "GRE,,,,,,,,," + "MHRP,,,,,,,," + "BNA,,,,,,,,," + "ESP,,,,,,,,," + "AH,,,,,,,,,," + "I-NLSP,,,,,," + "SWIPE,,,,,,," + "NHRP,,,,,,,," + ",,,,,,,,,,,," + ",,,,,,,,,,,," + ",,,,,,,,,,,," + ",,,,,,,,,,,," + ",,,,,,,,,,,," + ",,,,,,,,,,,," + ",,,,,,,,,,,," + "CFTP,,,,,,,," + ",,,,,,,,,,,," + "SAT-EXPAK,,," + "KRYPTOLAN,,," + "RVD,,,,,,,,," + "IPPC,,,,,,,," + ",,,,,,,,,,,," + "SAT-MON,,,,," + "VISA,,,,,,,," + "IPCV,,,,,,,," + "CPNX,,,,,,,," + "CPHB,,,,,,,," + "WSN,,,,,,,,," + "PVP,,,,,,,,," + "BR-SAT-MON,," + "SUN-ND,,,,,," + "WB-MON,,,,,," + "WB-EXPAK,,,," + "ISO-IP,,,,,," + "VMTP,,,,,,,," + "SECURE-VMTP," + "VINES,,,,,,," + "TTP,,,,,,,,," + "NSFNET-IGP,," + "DGP,,,,,,,,," + "TCF,,,,,,,,," + "IGRP,,,,,,,," + "OSPFIGP,,,,," + "SPRITE-RPC,," + "LARP,,,,,,,," + "MTP,,,,,,,,," + "AX25,,,,,,,," + "IPIP,,,,,,,," + "MICP,,,,,,,," + "SCC-SP,,,,,," + "ETHERIP,,,,," + "ENCAP,,,,,,," + ",,,,,,,,,,,," + "GMTP,,,,,,,,"; /** * NOTE: This string represents a packed sorted reference list of * abbreviations for the standard wksProtocol values (defined in * RFC1700). The string is used only by UnsignedInt * parseAbbreviation(str) method. ** * @since 3.0 */ public static final String WKS_PROTOCOL_REFER_ABBREVS = ",PROTOCOL,,,,,," + "3PC,,,,,,,,,34," + "AH,,,,,,,,,,51," + "ARGUS,,,,,,,13," + "AX.25,,,,,,,93," + "AX25,,,,,,,,93," + "BBN,,,,,,,,,10," + "BBN-RCC,,,,,10," + "BBN-RCC-MON,10," + "BBN-RCCMON,,10," + "BBN_RCC,,,,,10," + "BBN_RCCMON,,10," + "BBN_RCC_MON,10," + "BNA,,,,,,,,,49," + "BR-SAT,,,,,,76," + "BR-SAT-MON,,76," + "BR-SATMON,,,76," + "BR_SAT,,,,,,76," + "BR_SATMON,,,76," + "BR_SAT_MON,,76," + "CFTP,,,,,,,,62," + "CHAOS,,,,,,,16," + "CPHB,,,,,,,,73," + "CPNX,,,,,,,,72," + "DCN,,,,,,,,,19," + "DCN-MEAS,,,,19," + "DCN_MEAS,,,,19," + "DDP,,,,,,,,,37," + "DGP,,,,,,,,,86," + "EGP,,,,,,,,,,8," + "EMCON,,,,,,,14," + "ENCAP,,,,,,,98," + "ESP,,,,,,,,,50," + "ETHERIP,,,,,97," + "FRAG,,,,,,,,44," + "GGP,,,,,,,,,,3," + "GMTP,,,,,,,100," + "GRE,,,,,,,,,47," + "HMP,,,,,,,,,20," + "I-NLSP,,,,,,52," + "ICMP,,,,,,,,,1," + "IDPR,,,,,,,,35," + "IDPR-CMTP,,,38," + "IDPR_CMTP,,,38," + "IDRP,,,,,,,,45," + "IGMP,,,,,,,,,2," + "IGP,,,,,,,,,,9," + "IGRP,,,,,,,,88," + "IL,,,,,,,,,,40," + "INLSP,,,,,,,52," + "IP,,,,,,,,,,,4," + "IPCV,,,,,,,,71," + "IPIP,,,,,,,,94," + "IPPC,,,,,,,,67," + "IRTP,,,,,,,,28," + "ISO-IP,,,,,,80," + "ISO-TP,,,,,,29," + "ISO-TP4,,,,,29," + "ISOTP4,,,,,,29," + "ISO_IP,,,,,,80," + "ISO_TP,,,,,,29," + "ISO_TP4,,,,,29," + "I_NLSP,,,,,,52," + "KRYPTOLAN,,,65," + "LARP,,,,,,,,91," + "LEAF-1,,,,,,25," + "LEAF-2,,,,,,26," + "LEAF1,,,,,,,25," + "LEAF2,,,,,,,26," + "LEAF_1,,,,,,25," + "LEAF_2,,,,,,26," + "MERIT,,,,,,,32," + "MERIT-INP,,,32," + "MERIT_INP,,,32," + "MFE,,,,,,,,,31," + "MFE-NSP,,,,,31," + "MFE_NSP,,,,,31," + "MHRP,,,,,,,,48," + "MICP,,,,,,,,95," + "MTP,,,,,,,,,92," + "MUX,,,,,,,,,18," + "NETBLT,,,,,,30," + "NHRP,,,,,,,,54," + "NSFNET-IGP,,85," + "NSFNET_IGP,,85," + "NVP-2,,,,,,,11," + "NVP-II,,,,,,11," + "NVP2,,,,,,,,11," + "NVP_2,,,,,,,11," + "NVP_II,,,,,,11," + "OSPFIG,,,,,,89," + "OSPFIGP,,,,,89," + "PRM,,,,,,,,,21," + "PUP,,,,,,,,,12," + "PVP,,,,,,,,,75," + "RDP,,,,,,,,,27," + "RSVP,,,,,,,,46," + "RVD,,,,,,,,,66," + "SAT-EXPAK,,,64," + "SAT-MON,,,,,69," + "SAT_EXPAK,,,64," + "SAT_MON,,,,,69," + "SCC-SP,,,,,,96," + "SCC_SP,,,,,,96," + "SDRP,,,,,,,,42," + "SECURE-VMTP,82," + "SECURE_VMTP,82," + "SEP,,,,,,,,,33," + "SIP,,,,,,,,,41," + "SIP-FRAG,,,,44," + "SIP-SR,,,,,,43," + "SIPP-AH,,,,,51," + "SIPP-ESP,,,,50," + "SIPP_AH,,,,,51," + "SIPP_ESP,,,,50," + "SIP_FRAG,,,,44," + "SIP_SR,,,,,,43," + "SPRITE,,,,,,90," + "SPRITE-RPC,,90," + "SPRITE_RPC,,90," + "SR,,,,,,,,,,43," + "ST,,,,,,,,,,,5," + "SUN-ND,,,,,,77," + "SUN_ND,,,,,,77," + "SWIPE,,,,,,,53," + "TCF,,,,,,,,,87," + "TCP,,,,,,,,,,6," + "TP++,,,,,,,,39," + "TP,,,,,,,,,,39," + "TPPP,,,,,,,,39," + "TRUNK-1,,,,,23," + "TRUNK-2,,,,,24," + "TRUNK1,,,,,,23," + "TRUNK2,,,,,,24," + "TRUNK_1,,,,,23," + "TRUNK_2,,,,,24," + "TTP,,,,,,,,,84," + "UCL,,,,,,,,,,7," + "UDP,,,,,,,,,17," + "VINES,,,,,,,83," + "VISA,,,,,,,,70," + "VMTP,,,,,,,,81," + "WB-EXPAK,,,,79," + "WB-MON,,,,,,78," + "WB_EXPAK,,,,79," + "WB_MON,,,,,,78," + "WSN,,,,,,,,,74," + "X25,,,,,,,,,93," + "XNET,,,,,,,,15," + "XNS-IDP,,,,,22," + "XNS_IDP,,,,,22," + "XTP,,,,,,,,,36,"; /** * NOTE: This string represents a packed abbreviations list for the * standard wksPort values (defined in RFC1700). The string is used * only by UnsignedInt toAbbreviation(wksPort) method. ** * @since 3.0 */ public static final String WKS_PORT_ABBREVS = ",,,,,,,,,,,," + "0,,,,,,,,,,," + "tcpmux,,,,,," + "compressman," + "compressnet," + ",,,,,,,,,,,," + "rje,,,,,,,,," + ",,,,,,,,,,,," + "echo,,,,,,,," + ",,,,,,,,,,,," + "discard,,,,," + ",,,,,,,,,,,," + "systat,,,,,," + ",,,,,,,,,,,," + "daytime,,,,," + ",,,,,,,,,,,," + "netstat,,,,," + ",,,,,,,,,,,," + "qotd,,,,,,,," + "msp,,,,,,,,," + "chargen,,,,," + "ftp-data,,,," + "ftp,,,,,,,,," + ",,,,,,,,,,,," + "telnet,,,,,," + "anymail,,,,," + "smtp,,,,,,,," + ",,,,,,,,,,,," + "nsw-fe,,,,,," + ",,,,,,,,,,,," + "msg-icp,,,,," + ",,,,,,,,,,,," + "msg-auth,,,," + ",,,,,,,,,,,," + "dsp,,,,,,,,," + ",,,,,,,,,,,," + "anyprinter,," + ",,,,,,,,,,,," + "time,,,,,,,," + "rap,,,,,,,,," + "rlp,,,,,,,,," + ",,,,,,,,,,,," + "graphics,,,," + "nameserver,," + "nicname,,,,," + "mpm-flags,,," + "mpm,,,,,,,,," + "mpm-snd,,,,," + "ni-ftp,,,,,," + "auditd,,,,,," + "login,,,,,,," + "re-mail-ck,," + "la-maint,,,," + "xns-time,,,," + "domain,,,,,," + "xns-ch,,,,,," + "isi-gl,,,,,," + "xns-auth,,,," + "anyterminal," + "xns-mail,,,," + "anyfs,,,,,,," + ",,,,,,,,,,,," + "ni-mail,,,,," + "acas,,,,,,,," + ",,,,,,,,,,,," + "covia,,,,,,," + "tacacs-ds,,," + "sql-net,,,,," + "bootps,,,,,," + "bootpc,,,,,," + "tftp,,,,,,,," + "gopher,,,,,," + "netrjs-1,,,," + "netrjs-2,,,," + "netrjs-3,,,," + "netrjs-4,,,," + "anydialout,," + "deos,,,,,,,," + "anyrje,,,,,," + "vettcp,,,,,," + "finger,,,,,," + "www-http,,,," + "hosts2-ns,,," + "xfer,,,,,,,," + "mit-ml-dev,," + "ctf,,,,,,,,," + "mit-ml-dev2," + "mfcobol,,,,," + "anytermlink," + "kerberos,,,," + "su-mit-tg,,," + "dnsix,,,,,,," + "mit-dov,,,,," + "npp,,,,,,,,," + "dcp,,,,,,,,," + "objcall,,,,," + "supdup,,,,,," + "dixie,,,,,,," + "swift-rvf,,," + "tacnews,,,,," + "metagram,,,," + "newacct,,,,," + "hostname,,,," + "iso-tsap,,,," + "gppitnp,,,,," + "acr-nema,,,," + "csnet-ns,,,," + ",,,,,,,,,,,," + "rtelnet,,,,," + "snagas,,,,,," + "pop2,,,,,,,," + "pop3,,,,,,,," + "sunrpc,,,,,," + "mcidas,,,,,," + "auth,,,,,,,," + "audionews,,," + "sftp,,,,,,,," + "ansanotify,," + "uucp-path,,," + "sqlserv,,,,," + "nntp,,,,,,,," + "cfdptkt,,,,," + "erpc,,,,,,,," + "smakynet,,,," + "ntp,,,,,,,,," + "ansatrader,," + "locus-map,,," + "unitary,,,,," + "locus-con,,," + "gss-xlicen,," + "pwdgen,,,,,," + "cisco-fna,,," + "cisco-tna,,," + "cisco-sys,,," + "statsrv,,,,," + "ingres-net,," + "loc-srv,,,,," + "profile,,,,," + "netbios-ns,," + "netbios-dgm," + "netbios-ssn," + "emfis-data,," + "emfis-cntl,," + "bl-idm,,,,,," + "imap2,,,,,,," + "news,,,,,,,,"; /** * NOTE: This string represents a packed sorted reference list of * abbreviations for the standard wksPort values (defined in * RFC1700). The string is used only by UnsignedInt * parseAbbreviation(str) method. ** * @since 3.0 */ public static final String WKS_PORT_REFER_ABBREVS = ",port,,,,,,,,,,," + "3com-tsmux,,106," + "3com_tsmux,,106," + "acas,,,,,,,,,62," + "acr-nema,,,,104," + "acr_nema,,,,104," + "ansanotify,,116," + "ansatrader,,124," + "anydial,,,,,,75," + "anydialout,,,75," + "anyfilesys,,,59," + "anyfs,,,,,,,,59," + "anymail,,,,,,24," + "anyprinter,,,35," + "anyprintserv,35," + "anyrje,,,,,,,77," + "anyterminal,,57," + "anytermlink,,87," + "audionews,,,114," + "auditd,,,,,,,48," + "auth,,,,,,,,113," + "bl-idm,,,,,,142," + "bl_idm,,,,,,142," + "bootp,,,,,,,,67," + "bootpc,,,,,,,68," + "bootps,,,,,,,67," + "cfdptkt,,,,,120," + "chargen,,,,,,19," + "cisco-fna,,,130," + "cisco-sys,,,132," + "cisco-tna,,,131," + "cisco_fna,,,130," + "cisco_sys,,,132," + "cisco_tna,,,131," + "compressman,,,2," + "compressnet,,,3," + "covia,,,,,,,,64," + "csnet-ns,,,,105," + "csnet_ns,,,,105," + "ctf,,,,,,,,,,84," + "daytime,,,,,,13," + "dcp,,,,,,,,,,93," + "deos,,,,,,,,,76," + "discard,,,,,,,9," + "dixie,,,,,,,,96," + "dns,,,,,,,,,,53," + "dnsix,,,,,,,,90," + "domain,,,,,,,53," + "dsp,,,,,,,,,,33," + "echo,,,,,,,,,,7," + "emfis-cntl,,141," + "emfis-data,,140," + "emfis_cntl,,141," + "emfis_data,,140," + "erpc,,,,,,,,121," + "finger,,,,,,,79," + "ftp,,,,,,,,,,21," + "ftp-data,,,,,20," + "ftpdata,,,,,,20," + "ftp_data,,,,,20," + "gopher,,,,,,,70," + "gppitnp,,,,,103," + "graphics,,,,,41," + "gss-xlicen,,128," + "gss_xlicen,,128," + "hostname,,,,101," + "hosts-ns,,,,,81," + "hosts-ns2,,,,81," + "hosts2,,,,,,,81," + "hosts2-ns,,,,81," + "hosts2_ns,,,,81," + "hosts_ns,,,,,81," + "hosts_ns2,,,,81," + "http,,,,,,,,,80," + "imap,,,,,,,,143," + "imap2,,,,,,,143," + "ingres*net,,134," + "ingres-net,,134," + "ingresnet,,,134," + "ingres_net,,134," + "isi-gl,,,,,,,55," + "isi_gl,,,,,,,55," + "iso-tsap,,,,102," + "iso_tsap,,,,102," + "kerberos,,,,,88," + "la-maint,,,,,51," + "la_maint,,,,,51," + "loc-srv,,,,,135," + "locus-con,,,127," + "locus-map,,,125," + "locus_con,,,127," + "locus_map,,,125," + "loc_srv,,,,,135," + "login,,,,,,,,49," + "mcidas,,,,,,112," + "metagram,,,,,99," + "mfcobol,,,,,,86," + "mit-dov,,,,,,91," + "mit-ml,,,,,,,83," + "mit-ml-dev,,,83," + "mit-ml-dev2,,85," + "mit-ml2,,,,,,85," + "mit_dov,,,,,,91," + "mit_ml,,,,,,,83," + "mit_ml2,,,,,,85," + "mit_ml_dev,,,83," + "mit_ml_dev2,,85," + "mpm,,,,,,,,,,45," + "mpm-flags,,,,44," + "mpm-snd,,,,,,46," + "mpm_flags,,,,44," + "mpm_snd,,,,,,46," + "msg-auth,,,,,31," + "msg-icp,,,,,,29," + "msg_auth,,,,,31," + "msg_icp,,,,,,29," + "msp,,,,,,,,,,18," + "nameserver,,,42," + "netbios,,,,,137," + "netbios-dgm,138," + "netbios-ns,,137," + "netbios-ssn,139," + "netbios_dgm,138," + "netbios_ns,,137," + "netbios_ssn,139," + "netrjs-1,,,,,71," + "netrjs-2,,,,,72," + "netrjs-3,,,,,73," + "netrjs-4,,,,,74," + "netrjs1,,,,,,71," + "netrjs2,,,,,,72," + "netrjs3,,,,,,73," + "netrjs4,,,,,,74," + "netrjs_1,,,,,71," + "netrjs_2,,,,,72," + "netrjs_3,,,,,73," + "netrjs_4,,,,,74," + "netstat,,,,,,15," + "newacct,,,,,100," + "news,,,,,,,,144," + "ni-ftp,,,,,,,47," + "ni-mail,,,,,,61," + "nicname,,,,,,43," + "ni_ftp,,,,,,,47," + "ni_mail,,,,,,61," + "nntp,,,,,,,,119," + "npp,,,,,,,,,,92," + "nsw-fe,,,,,,,27," + "nsw_fe,,,,,,,27," + "ntp,,,,,,,,,123," + "objcall,,,,,,94," + "pop,,,,,,,,,110," + "pop2,,,,,,,,109," + "pop3,,,,,,,,110," + "profile,,,,,136," + "pwdgen,,,,,,129," + "qotd,,,,,,,,,17," + "rap,,,,,,,,,,38," + "re-mail,,,,,,50," + "re-mail-ck,,,50," + "re_mail,,,,,,50," + "re_mail_ck,,,50," + "rje,,,,,,,,,,,5," + "rlp,,,,,,,,,,39," + "rtelnet,,,,,107," + "sftp,,,,,,,,115," + "smakynet,,,,122," + "smtp,,,,,,,,,25," + "snagas,,,,,,108," + "sql*net,,,,,,66," + "sql*serv,,,,118," + "sql-net,,,,,,66," + "sql-serv,,,,118," + "sqlnet,,,,,,,66," + "sqlserv,,,,,118," + "sql_net,,,,,,66," + "sql_serv,,,,118," + "statsrv,,,,,133," + "su-mit-tg,,,,89," + "sun-rpc,,,,,111," + "sunrpc,,,,,,111," + "sun_rpc,,,,,111," + "supdup,,,,,,,95," + "su_mit_tg,,,,89," + "swift-rvf,,,,97," + "swift_rvf,,,,97," + "systat,,,,,,,11," + "tac-news,,,,,98," + "tacacs-ds,,,,65," + "tacacs_ds,,,,65," + "tacnews,,,,,,98," + "tac_news,,,,,98," + "tcpmux,,,,,,,,1," + "telnet,,,,,,,23," + "tftp,,,,,,,,,69," + "time,,,,,,,,,37," + "unitary,,,,,126," + "uucp,,,,,,,,117," + "uucp-path,,,117," + "uucppath,,,,117," + "uucp_path,,,117," + "vettcp,,,,,,,78," + "www,,,,,,,,,,80," + "www-http,,,,,80," + "www_http,,,,,80," + "xfer,,,,,,,,,82," + "xns-auth,,,,,56," + "xns-ch,,,,,,,54," + "xns-mail,,,,,58," + "xns-time,,,,,52," + "xns_auth,,,,,56," + "xns_ch,,,,,,,54," + "xns_mail,,,,,58," + "xns_time,,,,,52,"; /** * NOTE: This is (limit + 1) for number of recognized fields in * rData. ** * @since 2.3 */ protected static final int RDATA_MAX_FIELDS = 11; /** * NOTE: This is padded string-table for codes of rData fields. * Field types are encoded as follows: '0' - '9' - UnsignedInt, * 'I' - InetAddress (INET_ADDR_LEN bytes), 'N' - DNSName * (case-insensitive), 'T' - ByteVector (till the end of * rDataBytes), 'S' - String (case-insensitive, with unsigned byte * prefix for length), 'R' - ByteVector (with unsigned * RDATA_LEN_LENGTH prefix for length), ',' - undefined. Each line * contains exactly RDATA_MAX_FIELDS characters (including last ',' * to terminate line). The rest fields and the rest record types are * undefined. ** * @since 2.3 */ protected static final String RDATA_FIELDS = ",,,,,,,,,,," + "I,,,,,,,,,," /* A ip-address */ + "N,,,,,,,,,," /* NS hostname */ + "N,,,,,,,,,," /* MD hostname */ + "N,,,,,,,,,," /* MF hostname */ + "N,,,,,,,,,," /* CNAME canonic_name */ + "NN44444,,,," /* SOA hostname e-name serial refresh retry expire min_TTL */ + "N,,,,,,,,,," /* MB hostname */ + "N,,,,,,,,,," /* MG e-name */ + "N,,,,,,,,,," /* MR e-name */ + ",,,,,,,,,,," /* NULL */ + "I1T,,,,,,,," /* WKS interface protocol_abbr port_abbrs_map */ + "N,,,,,,,,,," /* PTR canonic_name */ + "SS,,,,,,,,," /* HINFO "computer" "OS" */ + "NN,,,,,,,,," /* MINFO e-name e-name */ + "2N,,,,,,,,," /* MX priority hostname */ + "SSSSSSSSSS," /* TXT "info1" . . . */ + "NN,,,,,,,,," /* RP e-name txt-record-name */ + "2N,,,,,,,,," /* AFSDB sub-type hostname */ + "S,,,,,,,,,," /* X25 "PSDN-address" */ + "SS,,,,,,,,," /* ISDN "ISDN-address" ["sub-address"] */ + "2N,,,,,,,,," /* RT priority hostname */ + "T,,,,,,,,,," /* NSAP address (hex arbitrary dot-separated) */ + "N,,,,,,,,,," /* NSAP-PTR name */ + "2114442NT,," /* SIG rType_abbr algorithm labels_count original_ttl expiration_time (UT seconds) inception_time (UT seconds) key_tag signer_name signature (base64) */ + "211T,,,,,,," /* KEY flags protocol_code algorithm key_data (base64) */ + "2NN,,,,,,,," /* PX priority name x400-name */ + "SSS,,,,,,,," /* GPOS longitude latitude altitude */ + "T,,,,,,,,,," /* AAAA ip6-addr (len == 16, hex, colon-separated shorts) */ + "1111444,,,," /* LOC version ( == 0, not shown) coded_size (pos 4) horiz_pre (pos 5) vert_pre (pos 6) latitude (pos 1) longitude (pos 2) altitude (pos 3) */ + "NT,,,,,,,,," /* NXT next_name rType_abbrs_map */ + "T,,,,,,,,,," /* EID end_point_identifier (hex) */ + "N,,,,,,,,,," /* NIMLOC end_point_locator */ + "222N,,,,,,," /* SRV priority weight port hostname */ + "T,,,,,,,,,," /* ATMA atm_address (hex) */ + "22SSSN,,,,," /* NAPTR order priority "flags" "service" "regular_expression" replacement */ + "2N,,,,,,,,," /* KX priority hostname */ + "221T,,,,,,," /* CERT type key_tag algorithm cert_data (base64) */ + ",,,,,,,,,,," + "N,,,,,,,,,," /* DNAME domain-name */ + ",,,,,,,,,,," + "2R2R2R2R2R," /* OPT option_code1 value1 (hex) . . . */; /** * NOTE: Same as RDATA_FIELDS table but for query and meta-data * records. ** * @since 2.3 */ protected static final String META_RDATA_FIELDS = ",,,,,,,,,,," /* ANY */ + ",,,,,,,,,,," /* MAILA */ + ",,,,,,,,,,," /* MAILB */ + ",,,,,,,,,,," /* AXFR */ + ",,,,,,,,,,," /* IXFR */ + "N62R22R,,,," /* TSIG algorithm-name signature_time (UT seconds) seconds (of allowed error) MAC (base64) msgId rCode other_data (base64) */ + "N4422RR,,,," /* TKEY algorithm-name inception_time (UT seconds) expiration_time (UT seconds) mode rCode key_data (base64) other_data (base64) */; /** * NOTE: A constant initialized with an instance of empty byte * array. ** * @since 3.0 */ protected static final byte[] EMPTY_BYTES = {}; /** * NOTE: rName must be != null. ** * @serial */ protected final DNSName rName; /** * NOTE: rType is an unsigned short. ** * @serial */ protected final short rType; /** * NOTE: rClass is an unsigned short. ** * @serial */ protected final short rClass; /** * NOTE: ttl is unsigned (but should be >= 0, in fact). ** * @serial */ protected final int ttl; /** * NOTE: rDataBytes must be != null and * (rDataBytes length & ~RDATA_LEN_MASK) == 0. ** * @serial */ protected final byte[] rDataBytes; /** * NOTE: Question record constructor. rName must be != null. rType * and rClass must be valid. ttl is set to 0, rDataBytes is set to * an empty byte array. */ public DNSRecord(DNSName rName, int rType, int rClass) throws NullPointerException, IllegalArgumentException { (this.rName = rName).equals(rName); if ((rType & ~RTYPE_MASK) != 0) throw new IllegalArgumentException("rType: " + UnsignedInt.toString(rType, false)); if ((rClass & ~RCLASS_MASK) != 0) throw new IllegalArgumentException("rClass: " + UnsignedInt.toString(rClass, false)); this.rType = (short)rType; this.rClass = (short)rClass; this.ttl = 0; this.rDataBytes = EMPTY_BYTES; } /** * NOTE: rName must be != null, rData must be != null, rData[index] * must be != null for any index. rType, rClass and rData must be * valid. */ public DNSRecord(DNSName rName, int rType, int rClass, int ttl, Object[] rData) throws NullPointerException, IllegalArgumentException { (this.rName = rName).equals(rName); if ((rType & ~RTYPE_MASK) != 0) throw new IllegalArgumentException("rType: " + UnsignedInt.toString(rType, false)); if ((rClass & ~RCLASS_MASK) != 0) throw new IllegalArgumentException("rClass: " + UnsignedInt.toString(rClass, false)); this.rType = (short)rType; this.rClass = (short)rClass; this.ttl = ttl; if (((this.rDataBytes = encodeRData(rType, rData)).length & ~RDATA_LEN_MASK) != 0) throw new IllegalArgumentException("rDataBytes length: " + UnsignedInt.toString(this.rDataBytes.length, false)); } /** * NOTE: rName must be != null, rDataBytes must be != null. rType, * rClass and rDataBytes length must be valid. rDataBytes array is * cloned (its content is not verified). */ public DNSRecord(DNSName rName, int rType, int rClass, int ttl, byte[] rDataBytes) throws NullPointerException, IllegalArgumentException { (this.rName = rName).equals(rName); if ((rType & ~RTYPE_MASK) != 0) throw new IllegalArgumentException("rType: " + UnsignedInt.toString(rType, false)); if ((rClass & ~RCLASS_MASK) != 0) throw new IllegalArgumentException("rClass: " + UnsignedInt.toString(rClass, false)); if ((rDataBytes.length & ~RDATA_LEN_MASK) != 0) throw new IllegalArgumentException("rDataBytes length: " + UnsignedInt.toString(rDataBytes.length, false)); this.rType = (short)rType; this.rClass = (short)rClass; this.ttl = ttl; this.rDataBytes = (byte[])rDataBytes.clone(); } /** * NOTE: record must be != null. Constructed DNS record is with * canonized rName and all recognized names in rDataBytes. ** * @since 2.3 */ public DNSRecord(DNSRecord record) throws NullPointerException { this.rName = new DNSName(record.rName); this.rClass = record.rClass; this.ttl = record.ttl; byte[] recordRDataBytes = record.rDataBytes; byte[] rDataBytes = (byte[])recordRDataBytes.clone(); this.rDataBytes = canonizeRData((this.rType = record.rType) & RTYPE_MASK, rDataBytes) ? rDataBytes : recordRDataBytes; } /** * NOTE: Constructor for changing ttl. record must be != null. * Constructed record is the same as specified one except ttl. ** * @since 2.3 */ public DNSRecord(DNSRecord record, int ttl) throws NullPointerException { this.rName = record.rName; this.rType = record.rType; this.rClass = record.rClass; this.ttl = ttl; this.rDataBytes = record.rDataBytes; } /** * NOTE: Record constructor from msgBytes array (decompression * supported). msgBytes must be != null, ofsRef must be != null and * ofsRef length > 0 (only ofsRef[0] is used). If !isResource then * ttl is set to 0, rDataBytes is set to empty byte array (without * reading them from msgBytes). ArrayIndexOutOfBoundsException is * thrown only if ofsRef length == 0 or 0 > ofsRef[0] or ofsRef[0] * >= msgBytes length. On return ofsRef[0] is new offset (just after * constructed record). If IllegalArgumentException is thrown then * if ofsRef[0] > msgBytes length then out of msgBytes array else * the content of msgBytes array is invalid. ** * @since 2.3 */ public DNSRecord(byte[] msgBytes, int[] ofsRef, boolean isResource) throws NullPointerException, ArrayIndexOutOfBoundsException, IllegalArgumentException { int offset, rDataBytesLen; this.rName = new DNSName(msgBytes, offset = ofsRef[0]); if ((rDataBytesLen = DNSName.lengthOf(msgBytes, offset)) < 0) rDataBytesLen = -rDataBytesLen; int ttl = (offset += rDataBytesLen) + (RTYPE_LENGTH + RCLASS_LENGTH); if (isResource && (ttl += TTL_LENGTH + RDATA_LEN_LENGTH) > 0 && msgBytes.length >= ttl) ttl += rDataBytesLen = UnsignedInt.getFromByteArray(msgBytes, ttl - RDATA_LEN_LENGTH, RDATA_LEN_LENGTH); if (ttl <= 0) ttl = -1 >>> 1; if ((ofsRef[0] = ttl) > msgBytes.length) throw new IllegalArgumentException("Out of msgBytes"); this.rType = (short)UnsignedInt.getFromByteArray(msgBytes, offset, RTYPE_LENGTH); this.rClass = (short)UnsignedInt.getFromByteArray(msgBytes, offset + RTYPE_LENGTH, RCLASS_LENGTH); byte[] rDataBytes = EMPTY_BYTES; ttl = 0; if (isResource) { ttl = UnsignedInt.getFromByteArray(msgBytes, offset += RTYPE_LENGTH + RCLASS_LENGTH, TTL_LENGTH); System.arraycopy(msgBytes, offset + (TTL_LENGTH + RDATA_LEN_LENGTH), rDataBytes = new byte[rDataBytesLen], 0, rDataBytesLen); rDataBytes = decompressRData(this.rType & RTYPE_MASK, rDataBytes, msgBytes); } this.ttl = ttl; this.rDataBytes = rDataBytes; } /** * NOTE: Method for putting resource record to (message) byte array. * msgBytes must be != null. Enough capacity must be ensured (at * least getTotalLen() bytes). If !isResource then ttl and * rDataBytes are ignored. If msgBytes length > baseNameOffset then * domain names compression is performed (using only content at * baseNameOffset, which must be valid and not compressed). msgBytes * array is altered. Result is new offset (just after this record). ** * @since 2.3 */ public int putTo(byte[] msgBytes, int offset, boolean isResource, int baseNameOffset) throws NullPointerException, ArrayIndexOutOfBoundsException { int rDataBytesLen; offset = this.rName.putTo(msgBytes, rDataBytesLen = offset); if (baseNameOffset < rDataBytesLen) offset = DNSName.compressAt(msgBytes, rDataBytesLen, baseNameOffset); UnsignedInt.putToByteArray(msgBytes, offset, this.rType, RTYPE_LENGTH); UnsignedInt.putToByteArray(msgBytes, offset + RTYPE_LENGTH, this.rClass, RCLASS_LENGTH); offset += RTYPE_LENGTH + RCLASS_LENGTH; if (isResource) { UnsignedInt.putToByteArray(msgBytes, offset, this.ttl, TTL_LENGTH); System.arraycopy(this.rDataBytes, 0, msgBytes, offset += TTL_LENGTH + RDATA_LEN_LENGTH, rDataBytesLen = this.rDataBytes.length); if (baseNameOffset < msgBytes.length) rDataBytesLen = compressRData(this.rType & RTYPE_MASK, msgBytes, offset, rDataBytesLen, baseNameOffset) - offset; UnsignedInt.putToByteArray(msgBytes, offset - RDATA_LEN_LENGTH, rDataBytesLen, RDATA_LEN_LENGTH); offset += rDataBytesLen; } return offset; } /** * NOTE: Result is the total length (in bytes) of this record. * Result > 0. ** * @since 2.3 */ public int getTotalLen() { return this.rName.getBytesLen() + (RTYPE_LENGTH + RCLASS_LENGTH + TTL_LENGTH + RDATA_LEN_LENGTH) + this.rDataBytes.length; } /** * NOTE: Result != null. */ public final DNSName getRName() { return this.rName; } /** * NOTE: Result >= 0. Result is the same as of getRName() * getLevel(). ** * @since 3.0 */ public int getLevel() { return this.rName.getLevel(); } /** * NOTE: Result is an unsigned short. */ public final int getRType() { return this.rType & RTYPE_MASK; } /** * NOTE: Result is an unsigned short. */ public final int getRClass() { return this.rClass & RCLASS_MASK; } /** * NOTE: Result is unsigned. */ public final int getTTL() { return this.ttl & TTL_MASK; } /** * NOTE: Result != null, result[index] != null for any index. * Expected/required length for rType >= result length. */ public final Object[] getRData() { return decodeRData(this.rType & RTYPE_MASK, this.rDataBytes); } /** * NOTE: Result != null, result is a copy. */ public final byte[] getRDataBytes() { return (byte[])this.rDataBytes.clone(); } /** * NOTE: Result >= 0. */ public final int getRDataBytesLen() { return this.rDataBytes.length; } /** * NOTE: Result is the number of elements accessible through * getAt(int). ** * @since 2.2 */ public int length() { return 5; } /** * NOTE: Result is (new Object[] { getRName(), * new UnsignedInt(getRType()), new UnsignedInt(getRClass()), * new UnsignedInt(getTTL()), new ByteVector(getRDataBytes()) * })[index]. ** * @since 2.2 */ public Object getAt(int index) throws ArrayIndexOutOfBoundsException { if (index <= 0) { if (index == 0) return this.rName; } else if (index <= 3) return new UnsignedInt(index < 2 ? this.rType & RTYPE_MASK : index == 2 ? this.rClass & RCLASS_MASK : this.ttl & TTL_MASK); else if (index == 4) return new ByteVector((byte[])this.rDataBytes.clone()); throw new ArrayIndexOutOfBoundsException(index); } /** * NOTE: rData must be != null, rData[index] must be != null for any * index. rData length may be not adequate to rType. rData * elements/fields must be instances of ByteArray, DNSName, * InetAddress, String or UnsignedInt (according to rType and field * number). Result != null (result length is not verified). */ public static final byte[] encodeRData(int rType, Object[] rData) throws NullPointerException, IllegalArgumentException { int offset = 0, code, rDataLen = rData.length, bytesLen = 0; byte[] rDataBytes = EMPTY_BYTES; String fields = META_RDATA_FIELDS; int rTypeValue = rType; if ((code = JavaConsts.BYTE_MASK - rType) < 0 || fields.length() / RDATA_MAX_FIELDS <= code) { code = 0; if ((fields = RDATA_FIELDS).length() / RDATA_MAX_FIELDS > rType && rType >= 0) code = rType; } byte[] newBytes; rType = code * RDATA_MAX_FIELDS; for (int index = 0; index < rDataLen; index++) { Object value = rData[index]; value.equals(value); int len = -1; if ((char)((code = fields.charAt(rType++)) - '0') <= '9' - '0') { if (value instanceof Number) { len = ((Number)value).intValue(); if ((code -= '0') >= JavaConsts.INT_LENGTH || ((-1 << (code * JavaConsts.BYTE_SIZE)) & len) == 0) { if (bytesLen - offset < code) { if (offset + code <= 0) bytesLen = -1 >>> 1; else if ((bytesLen += bytesLen >> 1) <= offset + code) bytesLen = offset + code; System.arraycopy(rDataBytes, 0, newBytes = new byte[bytesLen], 0, offset); rDataBytes = newBytes; } UnsignedInt.putToByteArray(rDataBytes, offset, len, code); len = code; } else len = -1; } } else if (code != 'S') { byte[] bytes = null; if (code == 'I') { if (value instanceof InetAddress && (bytes = ((InetAddress)value).getAddress()).length != INET_ADDR_LEN) bytes = null; } else if (code == 'N') { if (value instanceof DNSName) bytes = ((DNSName)value).getBytes(); } else if ((code == 'T' || code == 'R') && value instanceof ByteVector) { bytes = ((ByteVector)value).array(); if (code == 'R') if ((bytes.length & ~RDATA_LEN_MASK) == 0) offset += RDATA_LEN_LENGTH; else bytes = null; else if (bytes.length <= 0) bytes = null; else if (offset <= 0) bytes = (byte[])bytes.clone(); } if (bytes != null) { len = bytes.length; if (offset != 0) { if (bytesLen - offset < len) { if (offset + len <= 0) bytesLen = -1 >>> 1; else if ((bytesLen += bytesLen >> 1) <= offset + len) bytesLen = offset + len; System.arraycopy(rDataBytes, 0, newBytes = new byte[bytesLen], 0, code == 'R' ? offset - RDATA_LEN_LENGTH : offset); rDataBytes = newBytes; } if (code == 'R') UnsignedInt.putToByteArray(rDataBytes, offset - RDATA_LEN_LENGTH, len, RDATA_LEN_LENGTH); System.arraycopy(bytes, 0, rDataBytes, offset, len); } else { rDataBytes = bytes; bytesLen = len; } } } else if (value instanceof String) { String str = (String)value; if (((len = str.length()) & ~JavaConsts.BYTE_MASK) == 0) { if (++len > bytesLen - offset) { if (offset + len <= 0) bytesLen = -1 >>> 1; else if ((bytesLen += bytesLen >> 1) <= offset + len) bytesLen = offset + len; System.arraycopy(rDataBytes, 0, newBytes = new byte[bytesLen], 0, offset); rDataBytes = newBytes; } while (--len > 0 && (code = str.charAt(len - 1)) <= JavaConsts.BYTE_MASK) rDataBytes[offset + len] = (byte)code; } if (len == 0) rDataBytes[offset++] = (byte)(len = str.length()); else len = -1; } if (len < 0) throw new IllegalArgumentException("rData[" + UnsignedInt.toString(index, false) + "]: " + value.toString() + " (" + rTypeAbbreviation(rTypeValue) + " type)"); offset += len; } if (bytesLen > offset) { System.arraycopy(rDataBytes, 0, newBytes = new byte[offset], 0, offset); rDataBytes = newBytes; } return rDataBytes; } /** * NOTE: rDataBytes must be != null (may be ill-formed). Extra bytes * are ignored. Result != null and result[index] != null for any * index (each result[index] is instance of ByteArray, DNSName, * InetAddress, String or UnsignedInt according to rType). Result * length may be less than expected. */ public static final Object[] decodeRData(int rType, byte[] rDataBytes) throws NullPointerException { int offset = 0, code, bytesLen = rDataBytes.length; String fields = META_RDATA_FIELDS; if ((code = JavaConsts.BYTE_MASK - rType) < 0 || fields.length() / RDATA_MAX_FIELDS <= code) { code = 0; if ((fields = RDATA_FIELDS).length() / RDATA_MAX_FIELDS > rType && rType >= 0) code = rType; } for (rType = code *= RDATA_MAX_FIELDS; fields.charAt(code) != ','; code++); Object[] rData = new Object[code - rType]; for (int index = 0, len; (code = fields.charAt(rType++)) != ','; index++) { Object value = null; if ((char)(len = code - '0') <= '9' - '0') { if (bytesLen - offset >= len) value = new UnsignedInt( UnsignedInt.getFromByteArray(rDataBytes, offset, len)); } else if (code == 'I') { try { if ((len = INET_ADDR_LEN) <= bytesLen - offset) value = InetAddress.getByName(ByteVector.toString(rDataBytes, offset, len, '.', true)); } catch (UnknownHostException e) {} } else if (offset < bytesLen) if (code == 'N') { if ((len = DNSName.lengthOf(rDataBytes, offset)) > 0) { try { value = new DNSName(rDataBytes, offset); } catch (IllegalArgumentException e) {} } } else if (code != 'S') { len = bytesLen - offset; if (code != 'R' || len >= RDATA_LEN_LENGTH && (len = UnsignedInt.getFromByteArray(rDataBytes, offset, RDATA_LEN_LENGTH)) <= bytesLen - offset - RDATA_LEN_LENGTH) { byte[] bytes; if (code == 'R') offset += RDATA_LEN_LENGTH; System.arraycopy(rDataBytes, offset, bytes = new byte[len], 0, len); value = new ByteVector(bytes); } } else if ((len = rDataBytes[offset] & JavaConsts.BYTE_MASK) < bytesLen - offset) { char[] chars = new char[len]; int end = offset += len + 1; while (len > 0) chars[--len] = (char)(rDataBytes[--offset] & JavaConsts.BYTE_MASK); offset = end; value = new String(chars); } if ((rData[index] = value) == null) { Object[] newRData; System.arraycopy(rData, 0, newRData = new Object[index], 0, index); rData = newRData; break; } offset += len; } return rData; } /** * NOTE: rDataBytes must be != null, msgBytes must be != null. * Result != null. DNS name decompression is supported through * msgBytes. If result == rDataBytes then rDataBytes content is not * compressed. Original rDataBytes array itself and msgBytes array * are not altered. */ public static final byte[] decompressRData(int rType, byte[] rDataBytes, byte[] msgBytes) throws NullPointerException { int offset = 0, code, bytesLen = rDataBytes.length; String fields = META_RDATA_FIELDS; if ((code = JavaConsts.BYTE_MASK - rType) < 0 || fields.length() / RDATA_MAX_FIELDS <= code) { code = 0; if ((fields = RDATA_FIELDS).length() / RDATA_MAX_FIELDS > rType && rType >= 0) code = rType; } byte[] bytes; for (rType = code * RDATA_MAX_FIELDS; offset < bytesLen; offset += code) if ((code = fields.charAt(rType++)) == 'N') if ((bytes = DNSName.decompress(rDataBytes, offset, msgBytes)) != null && (code = DNSName.lengthOf(rDataBytes = bytes, offset)) > 0) bytesLen = rDataBytes.length; else break; else if (code == 'I') code = INET_ADDR_LEN; else if (code == 'S') code = (rDataBytes[offset] & JavaConsts.BYTE_MASK) + 1; else if (code == 'R') if (bytesLen - offset >= RDATA_LEN_LENGTH) code = UnsignedInt.getFromByteArray(rDataBytes, offset, RDATA_LEN_LENGTH) + RDATA_LEN_LENGTH; else break; else if ((char)(code -= '0') > '9' - '0') break; return rDataBytes; } /** * NOTE: If offset > baseNameOffset then compression of recognized * domain names is performed if possible (using only content at * baseNameOffset, which must be valid and not compressed). msgBytes * array is altered. Result is new offset (just after this processed * rData bytes). ** * @since 2.3 */ public static final int compressRData(int rType, byte[] msgBytes, int offset, int rDataBytesLen, int baseNameOffset) throws NullPointerException, ArrayIndexOutOfBoundsException { int code, newOffset; String fields = META_RDATA_FIELDS; if ((code = JavaConsts.BYTE_MASK - rType) < 0 || fields.length() / RDATA_MAX_FIELDS <= code) { code = 0; if ((fields = RDATA_FIELDS).length() / RDATA_MAX_FIELDS > rType && rType >= 0) code = rType; } for (rType = code * RDATA_MAX_FIELDS; rDataBytesLen > 0; offset += code, rDataBytesLen -= code) if ((code = fields.charAt(rType++)) == 'N') if ((code = DNSName.lengthOf(msgBytes, offset)) > 0) { if ((newOffset = DNSName.compressAt(msgBytes, offset, baseNameOffset)) - offset < code) { System.arraycopy(msgBytes, offset + code, msgBytes, newOffset, rDataBytesLen -= code); offset = newOffset; code = 0; } } else break; else if (code == 'I') code = INET_ADDR_LEN; else if (code == 'S') code = (msgBytes[offset] & JavaConsts.BYTE_MASK) + 1; else if (code == 'R') if (rDataBytesLen >= RDATA_LEN_LENGTH) code = UnsignedInt.getFromByteArray(msgBytes, offset, RDATA_LEN_LENGTH) + RDATA_LEN_LENGTH; else break; else if ((char)(code -= '0') > '9' - '0') break; return offset + rDataBytesLen; } /** * NOTE: rDataBytes must be != null. All found domain names in rData * are canonized as specified in RFC2535. Result is true if and only * if rDataBytes array is altered. ** * @since 2.3 */ public static final boolean canonizeRData(int rType, byte[] rDataBytes) throws NullPointerException { int offset = 0, code, bytesLen = rDataBytes.length; String fields = META_RDATA_FIELDS; if ((code = JavaConsts.BYTE_MASK - rType) < 0 || fields.length() / RDATA_MAX_FIELDS <= code) { code = 0; if ((fields = RDATA_FIELDS).length() / RDATA_MAX_FIELDS > rType && rType >= 0) code = rType; } boolean changed = false; for (rType = code * RDATA_MAX_FIELDS; offset < bytesLen; offset += code) if ((code = fields.charAt(rType++)) == 'N') if ((code = DNSName.lengthOf(rDataBytes, offset)) > 0) changed |= DNSName.canonize(rDataBytes, offset); else break; else if (code == 'I') code = INET_ADDR_LEN; else if (code == 'S') code = (rDataBytes[offset] & JavaConsts.BYTE_MASK) + 1; else if (code == 'R') if (bytesLen - offset >= RDATA_LEN_LENGTH) code = UnsignedInt.getFromByteArray(rDataBytes, offset, RDATA_LEN_LENGTH) + RDATA_LEN_LENGTH; else break; else if ((char)(code -= '0') > '9' - '0') break; return changed; } /** * NOTE: rDataBytes must be != null. Only recognized rData fields * are hashed. Domain names and strings are hashed ignoring letters * case (to comply with RFC2535). */ public static final int hashCodeRData(int rType, byte[] rDataBytes) throws NullPointerException { int offset = 0, hash = 0, code, bytesLen = rDataBytes.length; String fields = META_RDATA_FIELDS; if ((code = JavaConsts.BYTE_MASK - rType) < 0 || fields.length() / RDATA_MAX_FIELDS <= code) { code = 0; if ((fields = RDATA_FIELDS).length() / RDATA_MAX_FIELDS > rType && rType >= 0) code = rType; } rType = code * RDATA_MAX_FIELDS; while (offset < bytesLen) if ((code = fields.charAt(rType++)) == 'N' || code == 'S') { if (code == 'S') if ((code = rDataBytes[offset] & JavaConsts.BYTE_MASK) < bytesLen - offset) hash ^= code; else break; else if ((code = DNSName.lengthOf(rDataBytes, offset) - 1) >= 0) hash ^= rDataBytes[offset] & JavaConsts.BYTE_MASK; else break; offset++; for (int value; code-- > 0; hash = ((hash << 5) - hash) ^ value) if ((char)((value = rDataBytes[offset++] & JavaConsts.BYTE_MASK) - 'A') <= 'Z' - 'A') value += 'a' - 'A'; hash = (hash << 5) - hash; } else { if (code == 'I') code = INET_ADDR_LEN; else if (code == 'R') if (bytesLen - offset >= RDATA_LEN_LENGTH) code = UnsignedInt.getFromByteArray(rDataBytes, offset, RDATA_LEN_LENGTH) + RDATA_LEN_LENGTH; else break; else if (code == 'T') code = bytesLen - offset; else if ((char)(code -= '0') > '9' - '0') break; if (bytesLen - offset < code) break; while (code-- > 0) { hash ^= rDataBytes[offset++] & JavaConsts.BYTE_MASK; hash = (hash << 5) - hash; } } return hash ^ offset; } /** * NOTE: rDataBytesA and rDataBytesB must be != null. If * !decodeRData then rData contents are treated as unsigned byte * arrays with upper-case letters in domain names converted to * lower-case (as specified in RFC2535). Else any recognized domain * name in rData is compared in label-by-label lower-case manner, * any found string is compared (before comparing length of string) * in the lower-case manner, content of any recognized ByteVector is * compared before its length. */ public static final int compareRData(int rType, byte[] rDataBytesA, byte[] rDataBytesB, boolean decodeRData) throws NullPointerException { if (rDataBytesA != rDataBytesB) { int offset = 0, code = rDataBytesA.length, bytesLen, diff = 0; if ((bytesLen = rDataBytesB.length) >= code) bytesLen = code; String fields = META_RDATA_FIELDS; if ((code = JavaConsts.BYTE_MASK - rType) < 0 || fields.length() / RDATA_MAX_FIELDS <= code) { code = 0; if ((fields = RDATA_FIELDS).length() / RDATA_MAX_FIELDS > rType && rType >= 0) code = rType; } rType = code * RDATA_MAX_FIELDS; while (offset < bytesLen) { if ((code = fields.charAt(rType++)) == 'N') if ((code = DNSName.lengthOf(rDataBytesA, offset)) <= 0 || (diff = DNSName.lengthOf(rDataBytesB, offset)) <= 0) { diff = 0; code = bytesLen - offset; } else { if (decodeRData) { diff = DNSName.compareNames(rDataBytesA, offset, rDataBytesB, offset); offset += code; } else for (code = rDataBytesA[offset] & JavaConsts.BYTE_MASK; (diff = code - (rDataBytesB[offset++] & JavaConsts.BYTE_MASK)) == 0 && code > 0; code = rDataBytesA[offset] & JavaConsts.BYTE_MASK) for (int valueA, valueB; code-- > 0; offset++) if (rDataBytesA[offset] != rDataBytesB[offset]) { if ((char)((valueA = rDataBytesA[offset] & JavaConsts.BYTE_MASK) - 'A') <= 'Z' - 'A') valueA += 'a' - 'A'; if ((char)((valueB = rDataBytesB[offset] & JavaConsts.BYTE_MASK) - 'A') <= 'Z' - 'A') valueB += 'a' - 'A'; if ((valueA -= valueB) != 0) return valueA; } code = 0; } else if (code == 'S') { code = rDataBytesA[offset] & JavaConsts.BYTE_MASK; if (decodeRData) { if ((diff = code - (rDataBytesB[offset++] & JavaConsts.BYTE_MASK)) > 0) code -= diff; if (bytesLen - offset <= code) code = bytesLen - offset; for (int valueA, valueB; code-- > 0; offset++) if (rDataBytesA[offset] != rDataBytesB[offset]) { if ((char)((valueA = rDataBytesA[offset] & JavaConsts.BYTE_MASK) - 'A') <= 'Z' - 'A') valueA += 'a' - 'A'; if ((char)((valueB = rDataBytesB[offset] & JavaConsts.BYTE_MASK) - 'A') <= 'Z' - 'A') valueB += 'a' - 'A'; if ((valueA -= valueB) != 0) return valueA; } } code++; } else if (code == 'I') code = INET_ADDR_LEN; else if (code == 'R') { if ((code = RDATA_LEN_LENGTH) <= bytesLen - offset) { code = UnsignedInt.getFromByteArray(rDataBytesA, offset, RDATA_LEN_LENGTH) + RDATA_LEN_LENGTH; if (decodeRData) { if ((diff = (code -= RDATA_LEN_LENGTH) - UnsignedInt.getFromByteArray(rDataBytesB, offset, RDATA_LEN_LENGTH)) >= 0) code -= diff; offset += RDATA_LEN_LENGTH; } } } else if ((char)(code -= '0') > '9' - '0') code = bytesLen - offset; if (bytesLen - offset <= code) code = bytesLen - offset; while (code-- > 0) { if (rDataBytesA[offset] != rDataBytesB[offset]) return (rDataBytesA[offset] & JavaConsts.BYTE_MASK) - (rDataBytesB[offset] & JavaConsts.BYTE_MASK); offset++; } if (diff != 0) return diff; } } return rDataBytesA.length - rDataBytesB.length; } /** * NOTE: Result >= decodeRData(rType, rDataBytes) length. ** * @since 3.0 */ public static final int maxRDataLen(int rType) { int code; String fields = META_RDATA_FIELDS; if ((code = JavaConsts.BYTE_MASK - rType) < 0 || fields.length() / RDATA_MAX_FIELDS <= code) { code = 0; if ((fields = RDATA_FIELDS).length() / RDATA_MAX_FIELDS > rType && rType >= 0) code = rType; } for (rType = code *= RDATA_MAX_FIELDS; fields.charAt(code) != ','; code++); return code - rType; } /** * NOTE: str must be != null, beginIndex and endIndex must be in the * range. Result is true if and only if str region represents a * valid IP address according to RFC1123 (that is, has a form of * 'd.d.d.d' (with a possible '.' at the end), where 'd' is a * decimal value within the unsigned byte range with some possible * leading zeros). ** * @since 2.0 */ public static final boolean isIPAddress(String str, int beginIndex, int endIndex) throws NullPointerException, StringIndexOutOfBoundsException { int count = str.length(); if (beginIndex < 0) throw new StringIndexOutOfBoundsException(beginIndex); if (endIndex < beginIndex || endIndex > count) throw new StringIndexOutOfBoundsException(endIndex); if (endIndex - beginIndex >= (INET_ADDR_LEN << 1) - 1) { int group = INET_ADDR_LEN - 1, value = 0; count = 0; do { char ch; if ((ch = str.charAt(beginIndex++)) != '.') { ch -= '0'; if (ch > '9' - '0' || (value = value * ('9' - '0' + 1) + ch) > JavaConsts.BYTE_MASK) return false; count++; } else if (count <= 0) return false; else if (beginIndex < endIndex && --group >= 0) value = count = 0; else break; } while (beginIndex < endIndex); if (group == 0) return true; } return false; } /** * NOTE: host must be != null. host address is converted to reversed * Inet host address name (according to RFC1035). Result != null. ** * @since 2.0 */ public static final DNSName toRevAddressName(InetAddress host) throws NullPointerException { byte[] address = host.getAddress(); StringBuffer sBuf = new StringBuffer(15); int offset; if ((offset = address.length) > 0) do { sBuf.append(UnsignedInt.toString(address[--offset] & JavaConsts.BYTE_MASK, true)); if (offset <= 0) break; sBuf.append((char)DNSName.SEPARATOR); } while (true); return new DNSName(new String(sBuf), DNSName.IN_ADDR_ARPA); } /** * NOTE: ptr must be != null. Host ptr is converted to Inet host * address (according to RFC1035). Result != null. ** * @since 2.0 */ public static final InetAddress fromRevAddressName(DNSName ptr) throws NullPointerException, IllegalArgumentException { if (ptr.isInDomain(DNSName.IN_ADDR_ARPA, true)) { StringBuffer sBuf = new StringBuffer(15); int index = DNSName.IN_ADDR_ARPA.getLevel(); int level = ptr.getLevel(); do { sBuf.append(ptr.getLabelAt(index++)); if (index >= level) break; sBuf.append('.'); } while (true); String str = new String(sBuf); if (isIPAddress(str, 0, str.length())) { try { return InetAddress.getByName(str); } catch (UnknownHostException e) {} } } throw new IllegalArgumentException("ptr: " + ptr.getAbsolute()); } /** * NOTE: hostAAAA must be != null, offset and len must be valid. * hostAAAA IPv6 address is converted to reversed Inet IPv6 host * address name (according to RFC1886). Result != null. ** * @since 2.0 */ public static final DNSName toRevIp6Name(byte[] hostAAAA, int offset, int len) throws NullPointerException, ArrayIndexOutOfBoundsException { int digit = hostAAAA.length; byte value; digit = 0; if (len > 0) { value = hostAAAA[offset]; value = hostAAAA[(offset += len) - 1]; if ((digit = (len << 2) - 1) < 0) digit = -1 >>> 1; } StringBuffer sBuf = new StringBuffer(digit); if (len > 0) do { if ((digit = (value = hostAAAA[--offset]) & ((1 << 4) - 1)) > '9' - '0') digit += 'a' - '9' - 1; sBuf.append((char)(digit + '0')). append((char)DNSName.SEPARATOR); if ((digit = (value >>> 4) & ((1 << 4) - 1)) > '9' - '0') digit += 'a' - '9' - 1; sBuf.append((char)(digit + '0')); if (--len <= 0) break; sBuf.append((char)DNSName.SEPARATOR); } while (true); return new DNSName(new String(sBuf), DNSName.IP6_INT); } /** * NOTE: ptr must be != null. Host IPv6 ptr is converted to Inet * IPv6 host address (according to RFC1886). Result != null, result * length >= 0. ** * @since 2.0 */ public static final byte[] fromRevIp6Name(DNSName ptr) throws NullPointerException, IllegalArgumentException { if (ptr.isInDomain(DNSName.IP6_INT, false)) { char digit; int index = DNSName.IP6_INT.getLevel(); int count = ptr.getLevel() - index, offset = 0; byte[] hostAAAA = new byte[(count + 1) >> 1]; do { if (--count < 0) return hostAAAA; String label; if ((label = ptr.getLabelAt(index++)).length() != 1) break; if ((digit = (char)(label.charAt(0) - '0')) > '9' - '0') { digit -= 'A' - '0'; if (digit >= 'a' - 'A') digit -= 'a' - 'A'; if (digit >= (1 << 4) - ('9' - '0' + 1)) break; digit += '9' - '0' + 1; } if ((offset & 1) == 0) hostAAAA[offset >> 1] = (byte)(digit << 4); else hostAAAA[offset >> 1] |= (byte)digit; offset++; } while (true); } throw new IllegalArgumentException("ptr: " + ptr.getAbsolute()); } /** * NOTE: Result != null, result length() > 0, result is 'in-line'. * These abbreviations are defined in RFC1035. */ public static final String rClassAbbreviation(int rClass) { if (rClass == IN) return "IN"; if (rClass == ANY) return "*"; if (rClass == NONE) return "NONE"; if (rClass == HS) return "HS"; if (rClass == CH) return "CH"; return "RCLASS" + UnsignedInt.toString(rClass, false); } /** * NOTE: Result != null, result length() > 0, result is 'in-line'. * These abbreviations are first defined in RFC1035. */ public static final String rTypeAbbreviation(int rType) { switch (rType) { case A: return "A"; case NS: return "NS"; case MD: return "MD"; case MF: return "MF"; case CNAME: return "CNAME"; case SOA: return "SOA"; case MB: return "MB"; case MG: return "MG"; case MR: return "MR"; case NULL: return "NULL"; case WKS: return "WKS"; case PTR: return "PTR"; case HINFO: return "HINFO"; case MINFO: return "MINFO"; case MX: return "MX"; case TXT: return "TXT"; case RP: return "RP"; case AFSDB: return "AFSDB"; case X25: return "X25"; case ISDN: return "ISDN"; case RT: return "RT"; case NSAP: return "NSAP"; case NSAP_PTR: return "NSAP-PTR"; case SIG: return "SIG"; case KEY: return "KEY"; case PX: return "PX"; case GPOS: return "GPOS"; case AAAA: return "AAAA"; case LOC: return "LOC"; case NXT: return "NXT"; case EID: return "EID"; case NIMLOC: return "NIMLOC"; case SRV: return "SRV"; case ATMA: return "ATMA"; case NAPTR: return "NAPTR"; case KX: return "KX"; case CERT: return "CERT"; case DNAME: return "DNAME"; case OPT: return "OPT"; } switch (rType) { case TKEY: return "TKEY"; case TSIG: return "TSIG"; case IXFR: return "IXFR"; case AXFR: return "AXFR"; case MAILB: return "MAILB"; case MAILA: return "MAILA"; case ANY: return "*"; } return "RTYPE" + UnsignedInt.toString(rType, false); } /** * NOTE: portMap must be != null, offset and len must be valid. * Result != null, result is 'in-line' (defined in RFC1035). */ public static final String wksPortMapToString(byte[] portMap, int offset, int len) throws NullPointerException, ArrayIndexOutOfBoundsException { int wksPort = portMap.length; if (len > 0) wksPort = portMap[offset] | portMap[offset + len - 1]; byte bits = 0, oldBits; StringBuffer sBuf = new StringBuffer(58); boolean next = false; wksPort = 0; len++; do { if ((oldBits = (byte)(bits << 1)) == 0) if (--len > 0) bits = portMap[offset++]; else break; if (bits < 0) { if (next) sBuf.append(' '); sBuf.append(UnsignedInt.toAbbreviation(wksPort, WKS_PORT_ABBREVS)); next = true; } bits <<= 1; if (oldBits == 0) bits++; } while (++wksPort > 0); return new String(sBuf); } /** * NOTE: typeMap must be != null, offset and len must be valid. * Result != null, result is 'in-line' (defined in RFC2065). */ public static final String rTypeMapToString(byte[] typeMap, int offset, int len) throws NullPointerException, ArrayIndexOutOfBoundsException { int rType = typeMap.length; if (len > 0) rType = typeMap[offset] | typeMap[offset + len - 1]; byte bits = 0, oldBits; StringBuffer sBuf = new StringBuffer(58); boolean next = false; rType = 0; len++; do { if ((oldBits = (byte)(bits << 1)) == 0) if (--len > 0) bits = typeMap[offset++]; else break; if (bits < 0) { if (next) sBuf.append(' '); sBuf.append(rTypeAbbreviation(rType)); next = true; } bits <<= 1; if (oldBits == 0) bits++; } while (++rType > 0); return new String(sBuf); } /** * NOTE: address must be != null, offset and len must be valid. * Result != null, result is 'in-line' (defined in RFC1884). ** * @since 3.0 */ public static final String addressToString(byte[] address, int offset, int len, boolean lowerCase, boolean zeroPadding, boolean noCompactFormat, boolean noMixedFormat) throws NullPointerException, ArrayIndexOutOfBoundsException { int digit = address.length, shift, value, zeros, count; digit = 0; if (len > 0) { digit = address[offset] | address[offset + len - 1]; if ((digit = len << 1) < 0) digit = -1 >>> 1; } StringBuffer sBuf = new StringBuffer(digit); if (len > 0) { shift = count = 0; value = (zeros = offset) + len; for (digit = (len & 1) + offset; digit < value; digit += 2, shift++) if ((address[digit + 1] | address[digit]) != 0) if (noCompactFormat) break; else { if (count < shift) { count = shift; zeros = digit; } shift = -1; } if (count < shift) { count = shift; zeros = digit; } if ((zeros -= count <<= 1) == offset) { digit = len - count; value = offset + count; if (!noCompactFormat && count > 2) { len = digit; offset = value; sBuf.append(':').append(':'); } if (!noMixedFormat && (digit == INET_ADDR_LEN || digit == INET_ADDR_LEN + 2 && (address[value + 1] & address[value]) == -1)) len += count = -INET_ADDR_LEN; } if (len > 0) { boolean next = zeroPadding; do { if ((value = address[offset++] & JavaConsts.BYTE_MASK) != 0 || next) { shift = (JavaConsts.BYTE_SIZE - 1) & ~3; if (!next) { while (value >> shift == 0) shift -= 4; next = true; } do { if ((digit = (value >>> shift) & ((1 << 4) - 1)) > '9' - '0') { digit += 'a' - '9' - 1; if (!lowerCase) digit -= 'a' - 'A'; } sBuf.append((char)(digit + '0')); } while ((shift -= 4) >= 0); } if (--len <= 0) break; if ((len & 1) == 0) { if (!next) { sBuf.append('0'); next = true; } sBuf.append(':'); if (offset == zeros && count > 2) { sBuf.append(':'); if ((len -= count) <= 0) break; offset += count; } next = zeroPadding; } } while (true); if (!next) sBuf.append('0'); if (count < 0) sBuf.append(':'); } if (count < 0) do { digit = value = address[offset++] & JavaConsts.BYTE_MASK; shift = 1; while ((digit /= '9' - '0' + 1) > 0) shift *= '9' - '0' + 1; do { sBuf.append((char)((digit = value / shift) + '0')); if (shift <= 1) break; value -= digit * shift; shift /= '9' - '0' + 1; } while (true); if (++count >= 0) break; sBuf.append('.'); } while (true); } return new String(sBuf); } /** * NOTE: str must be != null (RFC1035). Escaping is not allowed in * str. Result != null, result length() > 0, result is 'in-line' * (escaped). */ public static final String quoteString(String str) throws NullPointerException { int index = 0, len = str.length(); StringBuffer sBuf = new StringBuffer(len + 2); sBuf.append((char)DNSName.QUOTE); for (char ch; index < len; sBuf.append(ch)) if ((ch = str.charAt(index++)) == DNSName.QUOTE || ch == DNSName.ESCAPE) sBuf.append((char)DNSName.ESCAPE); else if ((char)(ch - ' ') >= (JavaConsts.BYTE_MASK >>> 1) - ' ') { sBuf.append((char)DNSName.ESCAPE).append((char)(ch / (('9' - '0' + 1) * ('9' - '0' + 1)) % ('9' - '0' + 1) + '0')). append((char)(ch / ('9' - '0' + 1) % ('9' - '0' + 1) + '0')); ch = (char)(ch % ('9' - '0' + 1) + '0'); } return new String(sBuf.append((char)DNSName.QUOTE)); } /** * NOTE: Converts geographic location parameter to a string * (RFC1876). Default fields index order is 4, 5, 6, 1, 2, 3 * (Latitude, Longitude, Altitude, Size, Horiz_Pre, Vert_Pre). Other * fields are converted to empty strings. Result != null, result * length() > 0 (for valid indices), result is 'in-line'. */ public static final String locToString(int value, int index) { StringBuffer sBuf = new StringBuffer(15); if (index > 0 && index <= 6) { char sign = 'm'; if (index > 3) { if (index == 6) { if ((value = (index = value) - 10000000) < 0 && index >= 0) { sBuf.append('-'); value = -value; } if ((value -= (index = ((value >>> 1) / 100) << 1) * 100) >= 100) { value -= 100; index++; } } else { sign = 'E'; if (index == 4) sign = 'N'; if ((value -= 1 << 31) < 0) { sign = 'W'; if (index == 4) sign = 'S'; if ((value = -value) <= 0) value = 0; } value -= (index = value / (1000 * 60 * 60)) * (1000 * 60 * 60); } int digit = 1; while ((digit *= 10) <= index); do { int num; sBuf.append((char)((num = index / (digit /= 10)) + '0')); index -= num * digit; } while (digit > 1); if (sign != 'm') { digit = (index = value / (1000 * 60)) / 10; sBuf.append(' ').append((char)(digit + '0')). append((char)(index - digit * 10 + '0')); digit = (index = (value -= index * (1000 * 60)) / 1000) / 10; sBuf.append(' ').append((char)(digit + '0')). append((char)(index - digit * 10 + '0')); value -= index * 1000; } if (value > 0) { sBuf.append('.'); if (sign != 'm') { value -= (index = value / 100) * 100; sBuf.append((char)(index + '0')); } index = value / 10; sBuf.append((char)(index + '0')). append((char)(value - index * 10 + '0')); } if (sign != 'm') sBuf.append(' '); } else { index = (value & 15) - 2; if ((value >>>= 4) == 0) index = 0; if (index < 0) { sBuf.append('0').append('.'); if (index < -1) sBuf.append('0'); } if (value >= 9) value = 9; sBuf.append((char)(value + '0')); while (index-- > 0) sBuf.append('0'); } sBuf.append(sign); } return new String(sBuf); } /** * NOTE: Converts UT seconds to a string (as defined in RFC2535). * value is the unsigned number of seconds elapsed since the * midnight of the 1st January, 1970 (Gregorian), GMT, ignoring leap * seconds. The output date/time format is "yyyyMMddHHmmss". Result * != null, result length() > 0, result is 'in-line'. ** * @since 2.3 */ public static final String timeToString(int value) { int digit, day, month, year; StringBuffer sBuf = new StringBuffer(14); if ((value -= (day = ((value >>> 1) / (60 * 60 * 24)) << 1) * (60 * 60 * 24)) >= 60 * 60 * 24) { value -= 60 * 60 * 24; day++; } day += 1969 / 4 - 1969 / 100 + 1969 / 400 + 1969 * 365; day -= (month = day / ((365 * 100 + 25 - 1) * 4 + 1)) * ((365 * 100 + 25 - 1) * 4 + 1); day -= (digit = day / (365 * 100 + 25 - 1)) * (365 * 100 + 25 - 1); day -= (year = day / (365 * 4 + 1)) * (365 * 4 + 1); month = month * 100 + year; day -= (year = day / 365) * 365; if (((digit | year) >> 2) != 0) { day = 365; year--; } year += digit * 100 + (month << 2) + 1; if (day >= 31 + 28) { if ((year & 3) != 0 || year % 100 == 0 && year % 400 != 0) day++; if (day > 31 + 28) day++; } month = (day * 12 + (365 + 2 + 6)) / (365 + 2); day -= (((month >> 3) + month) >> 1) + month * 30 - 31; year -= (digit = year / 1000) * 1000; sBuf.append((char)(digit % 10 + '0')); year -= (digit = year / 100) * 100; sBuf.append((char)(digit + '0')); year -= (digit = year / 10) * 10; sBuf.append((char)(digit + '0')).append((char)(year + '0')); digit = month / 10; sBuf.append((char)(digit + '0')). append((char)(month - digit * 10 + '0')); digit = day / 10; sBuf.append((char)(digit + '0')). append((char)(day - digit * 10 + '0')); digit = (day = value / (60 * 60)) / 10; sBuf.append((char)(digit + '0')). append((char)(day - digit * 10 + '0')); digit = (day = (value -= day * (60 * 60)) / 60) / 10; sBuf.append((char)(digit + '0')). append((char)(day - digit * 10 + '0')); digit = (value -= day * 60) / 10; sBuf.append((char)(digit + '0')). append((char)(value - digit * 10 + '0')); return new String(sBuf); } /** * NOTE: rDataValue must be != null. domain may be == null. Result * != null, result is 'in-line', result is as defined in RFC1035. */ public static final String rDataToString(int rType, Object rDataValue, int index, DNSName domain) throws NullPointerException { if (rDataValue instanceof Number) { int intValue = ((Number)rDataValue).intValue(); switch (rType) { case WKS: if (index != 1 || (intValue & ~JavaConsts.BYTE_MASK) != 0) break; return UnsignedInt.toAbbreviation(intValue, WKS_PROTOCOL_ABBREVS); case SIG: if (index >> 1 == 2) return timeToString(intValue); if (index != 0 || (intValue & ~RTYPE_MASK) != 0) break; return rTypeAbbreviation(intValue); case LOC: if (((6 - index) | index) < 0 || index <= 3 && (intValue & ~JavaConsts.BYTE_MASK) != 0) break; return locToString(intValue, index); case TKEY: if ((index - 1) >> 1 == 0) return timeToString(intValue); if (index != 4) break; return UnsignedInt.toAbbreviation(intValue, DNSMsgHeader.RCODE_ABBREVS); case TSIG: if (index == 1) return timeToString(intValue); if (index != 5) break; return UnsignedInt.toAbbreviation(intValue, DNSMsgHeader.RCODE_ABBREVS); } } else if (rDataValue instanceof InetAddress) return ((InetAddress)rDataValue).getHostAddress(); else if (rDataValue instanceof DNSName) return ((DNSName)rDataValue).getRelative(domain); else if (rDataValue instanceof String) return quoteString((String)rDataValue); else if (rDataValue instanceof ByteVector) { byte[] bytes; int len = (bytes = ((ByteVector)rDataValue).array()).length; switch (rType) { case WKS: return wksPortMapToString(bytes, 0, len); case NXT: return rTypeMapToString(bytes, 0, len); case NSAP: return ByteVector.toHexString(bytes, 0, len, false, '.', 1, true); case SIG: case KEY: case CERT: case TKEY: case TSIG: return ByteVector.toBase64String(bytes, 0, len); case AAAA: return addressToString(bytes, 0, len, false, false, false, false); } return ByteVector.toHexString(bytes, 0, len, true, ' ', 0, true); } return rDataValue.toString(); } /** * NOTE: Result is the index for rData array corresponding to * textualIndex. Negative textualIndex is treated as zero. In fact, * textualIndex should be iterated from 0 to maxRDataLen(rType) - 1, * inclusive. Result >= 0, result may be >= rData length. ** * @since 3.0 */ public static final int rDataIndex(int rType, int textualIndex) { if (textualIndex <= 0) textualIndex = 0; if (rType == LOC && textualIndex != -1 >>> 1 && ++textualIndex <= 6 && (textualIndex -= 3) <= 0) textualIndex += 6; return textualIndex; } /** * NOTE: records must be != null, records[index] != null for any * index. Negative len is treated as zero. If rType != ANY then only * records with specified rType are accepted. If field is missing or * field value is equal to the previous one then it is ignored. Only * values of the specified field of rData of records are included * into resulting array. Result is exact instanceof Object[], * result[index] != null for any index, max(len, 0) >= result * length. ** * @since 2.1 */ public static final Object[] getFieldsAt(int field, int rType, DNSRecord[] records, int offset, int len) throws NullPointerException, ArrayIndexOutOfBoundsException { Object[] values = new Object[len > 0 ? len : 0], rData; int index = 0; Object prevValue = null; while (len-- > 0) { DNSRecord record = records[offset++]; if ((rType == ANY || (record.rType & RTYPE_MASK) == rType) && (rData = decodeRData(rType, record.rDataBytes)).length > field && !rData[field].equals(prevValue)) values[index++] = prevValue = rData[field]; } if (values.length > index) { Object[] newValues; System.arraycopy(values, 0, newValues = new Object[index], 0, index); values = newValues; } return values; } /** * NOTE: qdRecord may be == null. ANY qClass and IXFR, AXFR, MAILB, * MAILA and ANY qTypes of qdRecord are treated specially (like * wildcards). ** * @since 2.0 */ public boolean equalsQuery(DNSRecord qdRecord) { if (this == qdRecord) return true; int qType, rType; if (qdRecord != null) if ((qType = qdRecord.rClass & RCLASS_MASK) == ANY || qType == NONE || (this.rClass & RCLASS_MASK) == qType) if ((qType = qdRecord.rType & RTYPE_MASK) == IXFR || qType == AXFR) return this.rName.isInDomain(qdRecord.rName, false); else if (qType == ANY || (rType = this.rType & RTYPE_MASK) == qType || qType == MAILA && (rType == MX || rType == MD || rType == MF) || qType == MAILB && (rType == MB || rType == MG || rType == MR || rType == MINFO)) return this.rName.equals(qdRecord.rName); return false; } /** * NOTE: record may be == null. ** * @since 2.3 */ public boolean equalsExact(DNSRecord record) { if (this != record) { if (record == null || this.rType != record.rType || this.rClass != record.rClass || this.ttl != record.ttl || !this.rName.equalsExact(record.rName)) return false; int offset; byte[] rDataBytes = this.rDataBytes, recordRDataBytes; if ((recordRDataBytes = record.rDataBytes) != rDataBytes) if ((offset = rDataBytes.length) != recordRDataBytes.length) return false; else while (offset-- > 0) if (rDataBytes[offset] != recordRDataBytes[offset]) return false; } return true; } public Object clone() { Object obj; try { if ((obj = super.clone()) instanceof DNSRecord && obj != this) return obj; } catch (CloneNotSupportedException e) {} throw new InternalError("CloneNotSupportedException"); } /** * NOTE: ttl is omitted (as if ttl == 0). */ public int hashCode() { int rType = this.rType & RTYPE_MASK; return (((((((this.rName.hashCode() * 31) ^ rType) * 31) ^ (this.rClass & RCLASS_MASK)) * (31 * 31)) ^ hashCodeRData(rType, this.rDataBytes)) * 31) ^ 5; } /** * NOTE: ttl is ignored. rData contents are compared in the * case-insensitive manner for DNSName fields. All unrecognized * fields are ignored too. */ public boolean equals(Object obj) { return obj == this || obj instanceof DNSRecord && compareTo((DNSRecord)obj, false) == 0; } /** * NOTE: Method for canonical ordering (according to RFC2535). ** * @since 2.3 */ public boolean greaterThan(Object obj) { return obj != this && obj instanceof DNSRecord && compareTo((DNSRecord)obj, false) > 0; } /** * NOTE: record must be != null. rClass is compared first, then * rName is compared, then rType is compared (SOA is handled * specially to be less than other types, SIG(rType) for any rType * is handled specially to be greater than rType and less than rType * + 1), and then rData contents are compared as byte arrays (with * upper-case letters in DNSName fields set to lower-case). ttl is * ignored. If decodeRData then rData contents are compared in the * field-by-field manner, each recognized field is compared * according to its type (in addition, strings are * case-insensitive). Records canonical order (decodeRData == false) * is defined in RFC2535 (except for SOA handling but it is * important since SOA record must be the first record in a zone). */ public int compareTo(DNSRecord record, boolean decodeRData) throws NullPointerException { int diff = 0; if (this != record && (diff = (this.rClass & RCLASS_MASK) - (record.rClass & RCLASS_MASK)) == 0 && (diff = this.rName.compareTo(record.rName)) == 0) { int rType = this.rType & RTYPE_MASK, cover; byte[] rDataBytes = this.rDataBytes; byte[] recordRDataBytes = record.rDataBytes; diff = (diff = record.rType & RTYPE_MASK) == rType ? compareRData(diff, rDataBytes, recordRDataBytes, decodeRData) : rType == SOA ? -1 : diff == SOA ? 1 : rType == SIG && rDataBytes.length >= RTYPE_LENGTH ? ((cover = UnsignedInt.getFromByteArray(rDataBytes, 0, RTYPE_LENGTH)) == SOA || cover < diff ? -1 : 1) : diff == SIG && recordRDataBytes.length >= RTYPE_LENGTH ? ((cover = UnsignedInt.getFromByteArray(recordRDataBytes, 0, RTYPE_LENGTH)) == SOA || cover < rType ? 1 : -1) : rType - diff; } return diff; } /** * NOTE: If domain == null then current zone domain is not * specified. If prevRecord == null then default rName, rClass and * ttl values are not specified. Else all defaults are omitted in * the result. The textual output format is * "[relativeRName] [rClassAbbr] [ttl] rTypeAbbr [rData]" (as * specified in RFC1035). If tabSeparated then all fields are * separated with a single tab character else these fields are * separated (and padded to be mostly aligned) with blanks. All * rData elements are separated with a single blank. Result != null, * result length() > 0, result is 'in-line'. */ public String toString(DNSName domain, DNSRecord prevRecord, boolean tabSeparated) { StringBuffer sBuf = new StringBuffer(50); int rType = this.rType & RTYPE_MASK, len, value, pos; boolean padding = true; Object[] rData; String str; if ((pos = len = (rData = decodeRData(rType, this.rDataBytes)).length) > 0) { value = 50; do { pos--; value -= (str = rDataToString(rType, rData[pos], pos, domain)).length(); rData[pos] = str; } while (pos > 0); if (value < len) padding = false; } str = null; value = this.rClass; if (prevRecord == null || prevRecord.rClass != value) str = rClassAbbreviation(value & RCLASS_MASK); boolean ttlShown = false; value = this.ttl; if (prevRecord == null || prevRecord.ttl != value) ttlShown = true; DNSName rName = this.rName; if (ttlShown || str != null || !rName.equals(prevRecord.rName)) { String nameStr; sBuf.append(nameStr = rName.getRelative(domain)); if (!ttlShown && (char)(nameStr.charAt(0) - '0') <= '9' - '0') ttlShown = true; } if (tabSeparated) sBuf.append('\t'); if (str != null) { if (!tabSeparated) { if (padding) pos = 11 - sBuf.length(); do { sBuf.append(' '); } while (--pos > 0); } sBuf.append(str); } if (tabSeparated) sBuf.append('\t'); if (ttlShown) { str = UnsignedInt.toString(value & TTL_MASK, true); if (!tabSeparated) { if (padding) pos = 19 - sBuf.length() - str.length(); do { sBuf.append(' '); } while (--pos > 0); } sBuf.append(str); } if (!tabSeparated) { if (padding) pos = 20 - sBuf.length(); do { sBuf.append(' '); } while (--pos > 0); } else sBuf.append('\t'); sBuf.append(rTypeAbbreviation(rType)); if (len > 0) { if (!tabSeparated) { if (padding) pos = 26 - sBuf.length(); do { sBuf.append(' '); } while (--pos > 0); pos = 0; } else sBuf.append('\t'); int max = maxRDataLen(rType); padding = false; do { if ((value = rDataIndex(rType, pos)) < len) { if (padding) sBuf.append(' '); sBuf.append((String)rData[value]); padding = true; } } while (++pos < max); } return new String(sBuf); } /** * NOTE: No default values. Result length > 0, result is 'in-line', * result format is specified in RFC1035 (fields are separated with * blanks). */ public String toString() { return toString(null, null, false); } /** * NOTE: Check record and its name objects for their integrity. For * debug purpose only. ** * @since 2.3 */ public void integrityCheck() { DNSName rName; byte[] rDataBytes; if ((rName = this.rName) == null) throw new InternalError("rName: null"); if ((rDataBytes = this.rDataBytes) == null) throw new InternalError("rDataBytes: null"); if ((rDataBytes.length & ~RDATA_LEN_MASK) != 0) throw new InternalError("rDataBytes length: " + UnsignedInt.toString(rDataBytes.length, false)); rName.integrityCheck(); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); DNSName rName; byte[] rDataBytes; if ((rName = this.rName) == null) throw new InvalidObjectException("rName: null"); if ((rDataBytes = this.rDataBytes) == null) throw new InvalidObjectException("rDataBytes: null"); if ((rDataBytes.length & ~RDATA_LEN_MASK) != 0) throw new InvalidObjectException("rDataBytes length: " + UnsignedInt.toString(rDataBytes.length, false)); } }