// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) package org.xbill.DNS; import java.io.*; import java.text.*; /** * Location - describes the physical location of hosts, networks, subnets. * * @author Brian Wellington */ public class LOCRecord extends Record { private static final long serialVersionUID = 9058224788126750409L; private static NumberFormat w2, w3; private long size, hPrecision, vPrecision; private long latitude, longitude, altitude; static { w2 = new DecimalFormat(); w2.setMinimumIntegerDigits(2); w3 = new DecimalFormat(); w3.setMinimumIntegerDigits(3); } LOCRecord() {} Record getObject() { return new LOCRecord(); } /** * Creates an LOC Record from the given data * @param latitude The latitude of the center of the sphere * @param longitude The longitude of the center of the sphere * @param altitude The altitude of the center of the sphere, in m * @param size The diameter of a sphere enclosing the described entity, in m. * @param hPrecision The horizontal precision of the data, in m. * @param vPrecision The vertical precision of the data, in m. */ public LOCRecord(Name name, int dclass, long ttl, double latitude, double longitude, double altitude, double size, double hPrecision, double vPrecision) { super(name, Type.LOC, dclass, ttl); this.latitude = (long)(latitude * 3600 * 1000 + (1L << 31)); this.longitude = (long)(longitude * 3600 * 1000 + (1L << 31)); this.altitude = (long)((altitude + 100000) * 100); this.size = (long)(size * 100); this.hPrecision = (long)(hPrecision * 100); this.vPrecision = (long)(vPrecision * 100); } void rrFromWire(DNSInput in) throws IOException { int version; version = in.readU8(); if (version != 0) throw new WireParseException("Invalid LOC version"); size = parseLOCformat(in.readU8()); hPrecision = parseLOCformat(in.readU8()); vPrecision = parseLOCformat(in.readU8()); latitude = in.readU32(); longitude = in.readU32(); altitude = in.readU32(); } private double parseFixedPoint(String s) { if (s.matches("^-?\\d+$")) return Integer.parseInt(s); else if (s.matches("^-?\\d+\\.\\d*$")) { String [] parts = s.split("\\."); double value = Integer.parseInt(parts[0]); double fraction = Integer.parseInt(parts[1]); if (value < 0) fraction *= -1; int digits = parts[1].length(); return value + (fraction / Math.pow(10, digits)); } else throw new NumberFormatException(); } private long parsePosition(Tokenizer st, String type) throws IOException { boolean isLatitude = type.equals("latitude"); int deg = 0, min = 0; double sec = 0; long value; String s; deg = st.getUInt16(); if (deg > 180 || (deg > 90 && isLatitude)) throw st.exception("Invalid LOC " + type + " degrees"); s = st.getString(); try { min = Integer.parseInt(s); if (min < 0 || min > 59) throw st.exception("Invalid LOC " + type + " minutes"); s = st.getString(); sec = parseFixedPoint(s); if (sec < 0 || sec >= 60) throw st.exception("Invalid LOC " + type + " seconds"); s = st.getString(); } catch (NumberFormatException e) { } if (s.length() != 1) throw st.exception("Invalid LOC " + type); value = (long) (1000 * (sec + 60L * (min + 60L * deg))); char c = Character.toUpperCase(s.charAt(0)); if ((isLatitude && c == 'S') || (!isLatitude && c == 'W')) value = -value; else if ((isLatitude && c != 'N') || (!isLatitude && c != 'E')) throw st.exception("Invalid LOC " + type); value += (1L << 31); return value; } private long parseDouble(Tokenizer st, String type, boolean required, long min, long max, long defaultValue) throws IOException { Tokenizer.Token token = st.get(); if (token.isEOL()) { if (required) throw st.exception("Invalid LOC " + type); st.unget(); return defaultValue; } String s = token.value; if (s.length() > 1 && s.charAt(s.length() - 1) == 'm') s = s.substring(0, s.length() - 1); try { long value = (long)(100 * parseFixedPoint(s)); if (value < min || value > max) throw st.exception("Invalid LOC " + type); return value; } catch (NumberFormatException e) { throw st.exception("Invalid LOC " + type); } } void rdataFromString(Tokenizer st, Name origin) throws IOException { latitude = parsePosition(st, "latitude"); longitude = parsePosition(st, "longitude"); altitude = parseDouble(st, "altitude", true, -10000000, 4284967295L, 0) + 10000000; size = parseDouble(st, "size", false, 0, 9000000000L, 100); hPrecision = parseDouble(st, "horizontal precision", false, 0, 9000000000L, 1000000); vPrecision = parseDouble(st, "vertical precision", false, 0, 9000000000L, 1000); } private void renderFixedPoint(StringBuffer sb, NumberFormat formatter, long value, long divisor) { sb.append(value / divisor); value %= divisor; if (value != 0) { sb.append("."); sb.append(formatter.format(value)); } } private String positionToString(long value, char pos, char neg) { StringBuffer sb = new StringBuffer(); char direction; long temp = value - (1L << 31); if (temp < 0) { temp = -temp; direction = neg; } else direction = pos; sb.append(temp / (3600 * 1000)); /* degrees */ temp = temp % (3600 * 1000); sb.append(" "); sb.append(temp / (60 * 1000)); /* minutes */ temp = temp % (60 * 1000); sb.append(" "); renderFixedPoint(sb, w3, temp, 1000); /* seconds */ sb.append(" "); sb.append(direction); return sb.toString(); } /** Convert to a String */ String rrToString() { StringBuffer sb = new StringBuffer(); /* Latitude */ sb.append(positionToString(latitude, 'N', 'S')); sb.append(" "); /* Latitude */ sb.append(positionToString(longitude, 'E', 'W')); sb.append(" "); /* Altitude */ renderFixedPoint(sb, w2, altitude - 10000000, 100); sb.append("m "); /* Size */ renderFixedPoint(sb, w2, size, 100); sb.append("m "); /* Horizontal precision */ renderFixedPoint(sb, w2, hPrecision, 100); sb.append("m "); /* Vertical precision */ renderFixedPoint(sb, w2, vPrecision, 100); sb.append("m"); return sb.toString(); } /** Returns the latitude */ public double getLatitude() { return ((double)(latitude - (1L << 31))) / (3600 * 1000); } /** Returns the longitude */ public double getLongitude() { return ((double)(longitude - (1L << 31))) / (3600 * 1000); } /** Returns the altitude */ public double getAltitude() { return ((double)(altitude - 10000000)) / 100; } /** Returns the diameter of the enclosing sphere */ public double getSize() { return ((double)size) / 100; } /** Returns the horizontal precision */ public double getHPrecision() { return ((double)hPrecision) / 100; } /** Returns the horizontal precision */ public double getVPrecision() { return ((double)vPrecision) / 100; } void rrToWire(DNSOutput out, Compression c, boolean canonical) { out.writeU8(0); /* version */ out.writeU8(toLOCformat(size)); out.writeU8(toLOCformat(hPrecision)); out.writeU8(toLOCformat(vPrecision)); out.writeU32(latitude); out.writeU32(longitude); out.writeU32(altitude); } private static long parseLOCformat(int b) throws WireParseException { long out = b >> 4; int exp = b & 0xF; if (out > 9 || exp > 9) throw new WireParseException("Invalid LOC Encoding"); while (exp-- > 0) out *= 10; return (out); } private int toLOCformat(long l) { byte exp = 0; while (l > 9) { exp++; l /= 10; } return (int)((l << 4) + exp); } }