1 // Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
2
3 package org.xbill.DNS;
4
5 import java.io.*;
6 import java.text.*;
7
8 /**
9 * Location - describes the physical location of hosts, networks, subnets.
10 *
11 * @author Brian Wellington
12 */
13
14 public class LOCRecord extends Record {
15
16 private static final long serialVersionUID = 9058224788126750409L;
17
18 private static NumberFormat w2, w3;
19
20 private long size, hPrecision, vPrecision;
21 private long latitude, longitude, altitude;
22
23 static {
24 w2 = new DecimalFormat();
25 w2.setMinimumIntegerDigits(2);
26
27 w3 = new DecimalFormat();
28 w3.setMinimumIntegerDigits(3);
29 }
30
LOCRecord()31 LOCRecord() {}
32
33 Record
getObject()34 getObject() {
35 return new LOCRecord();
36 }
37
38 /**
39 * Creates an LOC Record from the given data
40 * @param latitude The latitude of the center of the sphere
41 * @param longitude The longitude of the center of the sphere
42 * @param altitude The altitude of the center of the sphere, in m
43 * @param size The diameter of a sphere enclosing the described entity, in m.
44 * @param hPrecision The horizontal precision of the data, in m.
45 * @param vPrecision The vertical precision of the data, in m.
46 */
47 public
LOCRecord(Name name, int dclass, long ttl, double latitude, double longitude, double altitude, double size, double hPrecision, double vPrecision)48 LOCRecord(Name name, int dclass, long ttl, double latitude, double longitude,
49 double altitude, double size, double hPrecision, double vPrecision)
50 {
51 super(name, Type.LOC, dclass, ttl);
52 this.latitude = (long)(latitude * 3600 * 1000 + (1L << 31));
53 this.longitude = (long)(longitude * 3600 * 1000 + (1L << 31));
54 this.altitude = (long)((altitude + 100000) * 100);
55 this.size = (long)(size * 100);
56 this.hPrecision = (long)(hPrecision * 100);
57 this.vPrecision = (long)(vPrecision * 100);
58 }
59
60 void
rrFromWire(DNSInput in)61 rrFromWire(DNSInput in) throws IOException {
62 int version;
63
64 version = in.readU8();
65 if (version != 0)
66 throw new WireParseException("Invalid LOC version");
67
68 size = parseLOCformat(in.readU8());
69 hPrecision = parseLOCformat(in.readU8());
70 vPrecision = parseLOCformat(in.readU8());
71 latitude = in.readU32();
72 longitude = in.readU32();
73 altitude = in.readU32();
74 }
75
76 private double
parseFixedPoint(String s)77 parseFixedPoint(String s)
78 {
79 if (s.matches("^-?\\d+$"))
80 return Integer.parseInt(s);
81 else if (s.matches("^-?\\d+\\.\\d*$")) {
82 String [] parts = s.split("\\.");
83 double value = Integer.parseInt(parts[0]);
84 double fraction = Integer.parseInt(parts[1]);
85 if (value < 0)
86 fraction *= -1;
87 int digits = parts[1].length();
88 return value + (fraction / Math.pow(10, digits));
89 } else
90 throw new NumberFormatException();
91 }
92
93 private long
parsePosition(Tokenizer st, String type)94 parsePosition(Tokenizer st, String type) throws IOException {
95 boolean isLatitude = type.equals("latitude");
96 int deg = 0, min = 0;
97 double sec = 0;
98 long value;
99 String s;
100
101 deg = st.getUInt16();
102 if (deg > 180 || (deg > 90 && isLatitude))
103 throw st.exception("Invalid LOC " + type + " degrees");
104
105 s = st.getString();
106 try {
107 min = Integer.parseInt(s);
108 if (min < 0 || min > 59)
109 throw st.exception("Invalid LOC " + type + " minutes");
110 s = st.getString();
111 sec = parseFixedPoint(s);
112 if (sec < 0 || sec >= 60)
113 throw st.exception("Invalid LOC " + type + " seconds");
114 s = st.getString();
115 } catch (NumberFormatException e) {
116 }
117
118 if (s.length() != 1)
119 throw st.exception("Invalid LOC " + type);
120
121 value = (long) (1000 * (sec + 60L * (min + 60L * deg)));
122
123 char c = Character.toUpperCase(s.charAt(0));
124 if ((isLatitude && c == 'S') || (!isLatitude && c == 'W'))
125 value = -value;
126 else if ((isLatitude && c != 'N') || (!isLatitude && c != 'E'))
127 throw st.exception("Invalid LOC " + type);
128
129 value += (1L << 31);
130
131 return value;
132 }
133
134 private long
parseDouble(Tokenizer st, String type, boolean required, long min, long max, long defaultValue)135 parseDouble(Tokenizer st, String type, boolean required, long min, long max,
136 long defaultValue)
137 throws IOException
138 {
139 Tokenizer.Token token = st.get();
140 if (token.isEOL()) {
141 if (required)
142 throw st.exception("Invalid LOC " + type);
143 st.unget();
144 return defaultValue;
145 }
146 String s = token.value;
147 if (s.length() > 1 && s.charAt(s.length() - 1) == 'm')
148 s = s.substring(0, s.length() - 1);
149 try {
150 long value = (long)(100 * parseFixedPoint(s));
151 if (value < min || value > max)
152 throw st.exception("Invalid LOC " + type);
153 return value;
154 }
155 catch (NumberFormatException e) {
156 throw st.exception("Invalid LOC " + type);
157 }
158 }
159
160 void
rdataFromString(Tokenizer st, Name origin)161 rdataFromString(Tokenizer st, Name origin) throws IOException {
162 latitude = parsePosition(st, "latitude");
163 longitude = parsePosition(st, "longitude");
164 altitude = parseDouble(st, "altitude", true,
165 -10000000, 4284967295L, 0) + 10000000;
166 size = parseDouble(st, "size", false, 0, 9000000000L, 100);
167 hPrecision = parseDouble(st, "horizontal precision", false,
168 0, 9000000000L, 1000000);
169 vPrecision = parseDouble(st, "vertical precision", false,
170 0, 9000000000L, 1000);
171 }
172
173 private void
renderFixedPoint(StringBuffer sb, NumberFormat formatter, long value, long divisor)174 renderFixedPoint(StringBuffer sb, NumberFormat formatter, long value,
175 long divisor)
176 {
177 sb.append(value / divisor);
178 value %= divisor;
179 if (value != 0) {
180 sb.append(".");
181 sb.append(formatter.format(value));
182 }
183 }
184
185 private String
positionToString(long value, char pos, char neg)186 positionToString(long value, char pos, char neg) {
187 StringBuffer sb = new StringBuffer();
188 char direction;
189
190 long temp = value - (1L << 31);
191 if (temp < 0) {
192 temp = -temp;
193 direction = neg;
194 } else
195 direction = pos;
196
197 sb.append(temp / (3600 * 1000)); /* degrees */
198 temp = temp % (3600 * 1000);
199 sb.append(" ");
200
201 sb.append(temp / (60 * 1000)); /* minutes */
202 temp = temp % (60 * 1000);
203 sb.append(" ");
204
205 renderFixedPoint(sb, w3, temp, 1000); /* seconds */
206 sb.append(" ");
207
208 sb.append(direction);
209
210 return sb.toString();
211 }
212
213
214 /** Convert to a String */
215 String
rrToString()216 rrToString() {
217 StringBuffer sb = new StringBuffer();
218
219 /* Latitude */
220 sb.append(positionToString(latitude, 'N', 'S'));
221 sb.append(" ");
222
223 /* Latitude */
224 sb.append(positionToString(longitude, 'E', 'W'));
225 sb.append(" ");
226
227 /* Altitude */
228 renderFixedPoint(sb, w2, altitude - 10000000, 100);
229 sb.append("m ");
230
231 /* Size */
232 renderFixedPoint(sb, w2, size, 100);
233 sb.append("m ");
234
235 /* Horizontal precision */
236 renderFixedPoint(sb, w2, hPrecision, 100);
237 sb.append("m ");
238
239 /* Vertical precision */
240 renderFixedPoint(sb, w2, vPrecision, 100);
241 sb.append("m");
242
243 return sb.toString();
244 }
245
246 /** Returns the latitude */
247 public double
getLatitude()248 getLatitude() {
249 return ((double)(latitude - (1L << 31))) / (3600 * 1000);
250 }
251
252 /** Returns the longitude */
253 public double
getLongitude()254 getLongitude() {
255 return ((double)(longitude - (1L << 31))) / (3600 * 1000);
256 }
257
258 /** Returns the altitude */
259 public double
getAltitude()260 getAltitude() {
261 return ((double)(altitude - 10000000)) / 100;
262 }
263
264 /** Returns the diameter of the enclosing sphere */
265 public double
getSize()266 getSize() {
267 return ((double)size) / 100;
268 }
269
270 /** Returns the horizontal precision */
271 public double
getHPrecision()272 getHPrecision() {
273 return ((double)hPrecision) / 100;
274 }
275
276 /** Returns the horizontal precision */
277 public double
getVPrecision()278 getVPrecision() {
279 return ((double)vPrecision) / 100;
280 }
281
282 void
rrToWire(DNSOutput out, Compression c, boolean canonical)283 rrToWire(DNSOutput out, Compression c, boolean canonical) {
284 out.writeU8(0); /* version */
285 out.writeU8(toLOCformat(size));
286 out.writeU8(toLOCformat(hPrecision));
287 out.writeU8(toLOCformat(vPrecision));
288 out.writeU32(latitude);
289 out.writeU32(longitude);
290 out.writeU32(altitude);
291 }
292
293 private static long
parseLOCformat(int b)294 parseLOCformat(int b) throws WireParseException {
295 long out = b >> 4;
296 int exp = b & 0xF;
297 if (out > 9 || exp > 9)
298 throw new WireParseException("Invalid LOC Encoding");
299 while (exp-- > 0)
300 out *= 10;
301 return (out);
302 }
303
304 private int
toLOCformat(long l)305 toLOCformat(long l) {
306 byte exp = 0;
307 while (l > 9) {
308 exp++;
309 l /= 10;
310 }
311 return (int)((l << 4) + exp);
312 }
313
314 }
315