OLD | NEW |
---|---|
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library mdns.src.packet; | 5 library mdns.src.packet; |
6 | 6 |
7 import 'dart:convert'; | 7 import 'dart:convert'; |
8 import 'dart:io'; | 8 import 'dart:io'; |
9 import 'dart:typed_data'; | 9 import 'dart:typed_data'; |
10 | 10 |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
81 throw new StateError("'target' is only supported for type SRV."); | 81 throw new StateError("'target' is only supported for type SRV."); |
82 } | 82 } |
83 return _data; | 83 return _data; |
84 } | 84 } |
85 | 85 |
86 toString() => 'RR $type $_data'; | 86 toString() => 'RR $type $_data'; |
87 } | 87 } |
88 | 88 |
89 /// Result of reading a FQDN. | 89 /// Result of reading a FQDN. |
90 class FQDNReadResult { | 90 class FQDNReadResult { |
91 final List<String> fqdn; | 91 final List<String> fqdnParts; |
92 final int bytesRead; | 92 final int bytesRead; |
93 FQDNReadResult(this.fqdn, this.bytesRead); | 93 String get fqdn => fqdnParts.join('.'); |
94 FQDNReadResult(this.fqdnParts, this.bytesRead); | |
95 } | |
96 | |
97 String readFQDN(List<int> packet, [int offset = 0]) { | |
98 Uint8List data = | |
99 packet is Uint8List ? packet : new Uint8List.fromList(packet); | |
100 ByteData bd = new ByteData.view(data.buffer); | |
101 | |
102 return _readFQDN(data, bd, offset, data.length).fqdn; | |
103 } | |
104 | |
105 // Read a FQDN at the given offset. Returns a pair with the FQDN | |
106 // parts and the number of bytes consumed. | |
107 // | |
108 // If decoding fails (e.g. due to an invalid packet) `null` is returned. | |
109 FQDNReadResult _readFQDN(Uint8List data, ByteData bd, int offset, int length) { | |
110 void checkLength(int required) { | |
111 if (length < required) throw new MDnsDecodeException(required); | |
112 } | |
113 | |
114 List<String> parts = []; | |
115 int prevOffset = offset; | |
116 while (true) { | |
117 // At least one byte is required. | |
118 checkLength(offset + 1); | |
119 | |
120 // Check for compressed. | |
121 if (data[offset] & 0xc0 == 0xc0) { | |
122 // At least two bytes are required for a compressed FQDN. | |
123 checkLength(offset + 2); | |
124 | |
125 // A compressed FQDN has a new offset in the lower 14 bits. | |
126 FQDNReadResult result = _readFQDN( | |
127 data, bd, bd.getUint16(offset) & ~0xc000, length); | |
128 parts.addAll(result.fqdnParts); | |
129 offset += 2; | |
130 break; | |
131 } else { | |
132 // A normal FQDN part has a length and a UTF-8 encoded name | |
133 // part. If the length is 0 this is the end of the FQDN. | |
134 var partLength = data[offset]; | |
135 offset++; | |
136 if (partLength != 0) { | |
wibling
2015/11/10 15:02:00
NIT: > 0
Søren Gjesse
2015/11/10 15:30:42
Done.
| |
137 checkLength(offset + partLength); | |
138 var partBytes = new Uint8List.view(data.buffer, offset, partLength); | |
139 offset += partLength; | |
140 parts.add(UTF8.decode(partBytes)); | |
141 } else { | |
142 break; | |
143 } | |
144 } | |
145 } | |
146 return new FQDNReadResult(parts, offset - prevOffset); | |
94 } | 147 } |
95 | 148 |
96 /// Decode a mDNS package. | 149 /// Decode a mDNS package. |
97 /// | 150 /// |
98 /// If decoding fails (e.g. due to an invalid packet) `null` is returned. | 151 /// If decoding fails (e.g. due to an invalid packet) `null` is returned. |
99 /// | 152 /// |
100 /// See https://tools.ietf.org/html/rfc1035 for the format. | 153 /// See https://tools.ietf.org/html/rfc1035 for the format. |
101 List<ResourceRecord> decodeMDnsResponse(List<int> packet) { | 154 List<ResourceRecord> decodeMDnsResponse(List<int> packet) { |
102 int length = packet.length; | 155 int length = packet.length; |
103 if (length < headerSize) return null; | 156 if (length < headerSize) return null; |
(...skipping 14 matching lines...) Expand all Loading... | |
118 // Number of name server records. | 171 // Number of name server records. |
119 int nscount = bd.getUint16(nscountOffset); | 172 int nscount = bd.getUint16(nscountOffset); |
120 // Number of resource records. | 173 // Number of resource records. |
121 int arcount = bd.getUint16(arcountOffset); | 174 int arcount = bd.getUint16(arcountOffset); |
122 int offset = headerSize; | 175 int offset = headerSize; |
123 | 176 |
124 void checkLength(int required) { | 177 void checkLength(int required) { |
125 if (length < required) throw new MDnsDecodeException(required); | 178 if (length < required) throw new MDnsDecodeException(required); |
126 } | 179 } |
127 | 180 |
128 // Read a FQDN at the given offset. Returns a pair with the FQDN | |
129 // parts and the number of bytes consumed. | |
130 // | |
131 // If decoding fails (e.g. due to an invalid packet) `null` is returned. | |
132 FQDNReadResult readFQDN(int offset) { | |
133 List<String> parts = []; | |
134 int prevOffset = offset; | |
135 while (true) { | |
136 // At least two bytes required. | |
137 checkLength(offset + 2); | |
138 | |
139 // Check for compressed. | |
140 if (data[offset] & 0xc0 == 0xc0) { | |
141 // A compressed FQDN has a new offset in the lower 14 bits. | |
142 FQDNReadResult result = readFQDN(bd.getUint16(offset) & ~0xc000); | |
143 parts.addAll(result.fqdn); | |
144 offset += 2; | |
145 break; | |
146 } else { | |
147 // A normal FQDN part has a length and a UTF-8 encoded name | |
148 // part. If the length is 0 this is the end of the FQDN. | |
149 var partLength = data[offset]; | |
150 offset++; | |
151 if (partLength != 0) { | |
152 checkLength(offset + partLength); | |
153 var partBytes = new Uint8List.view(data.buffer, offset, partLength); | |
154 offset += partLength; | |
155 parts.add(UTF8.decode(partBytes)); | |
156 } else { | |
157 break; | |
158 } | |
159 } | |
160 } | |
161 return new FQDNReadResult(parts, offset - prevOffset); | |
162 } | |
163 | |
164 ResourceRecord readResourceRecord() { | 181 ResourceRecord readResourceRecord() { |
165 // First read the FQDN. | 182 // First read the FQDN. |
166 FQDNReadResult result = readFQDN(offset); | 183 FQDNReadResult result = _readFQDN(data, bd, offset, length); |
167 var fqdn = result.fqdn.join('.'); | 184 var fqdn = result.fqdn; |
168 offset += result.bytesRead; | 185 offset += result.bytesRead; |
169 checkLength(offset + 2); | 186 checkLength(offset + 2); |
170 int type = bd.getUint16(offset); | 187 int type = bd.getUint16(offset); |
171 offset += 2; | 188 offset += 2; |
172 // The first bit of the rrclass field is set to indicate that the answer is | 189 // The first bit of the rrclass field is set to indicate that the answer is |
173 // unique and the querier should flush the cached answer for this name | 190 // unique and the querier should flush the cached answer for this name |
174 // (RFC 6762, Sec. 10.2). We ignore it for now since we don't cache answers. | 191 // (RFC 6762, Sec. 10.2). We ignore it for now since we don't cache answers. |
175 checkLength(offset + 2); | 192 checkLength(offset + 2); |
176 int cls = bd.getUint16(offset) & 0x7fff; | 193 int cls = bd.getUint16(offset) & 0x7fff; |
177 offset += 2; | 194 offset += 2; |
(...skipping 16 matching lines...) Expand all Loading... | |
194 case RRType.SRV: | 211 case RRType.SRV: |
195 checkLength(offset + 2); | 212 checkLength(offset + 2); |
196 int priority = bd.getUint16(offset); | 213 int priority = bd.getUint16(offset); |
197 offset += 2; | 214 offset += 2; |
198 checkLength(offset + 2); | 215 checkLength(offset + 2); |
199 int weight = bd.getUint16(offset); | 216 int weight = bd.getUint16(offset); |
200 offset += 2; | 217 offset += 2; |
201 checkLength(offset + 2); | 218 checkLength(offset + 2); |
202 int port = bd.getUint16(offset); | 219 int port = bd.getUint16(offset); |
203 offset += 2; | 220 offset += 2; |
204 FQDNReadResult result = readFQDN(offset); | 221 FQDNReadResult result = _readFQDN(data, bd, offset, length); |
205 rData = result.fqdn.join('.'); | 222 rData = result.fqdn; |
206 offset += rDataLength - 6; | 223 offset += rDataLength - 6; |
207 break; | 224 break; |
208 case RRType.PTR: | 225 case RRType.PTR: |
209 checkLength(offset + rDataLength); | 226 checkLength(offset + rDataLength); |
210 FQDNReadResult result = readFQDN(offset); | 227 FQDNReadResult result = _readFQDN(data, bd, offset, length); |
211 offset += rDataLength; | 228 offset += rDataLength; |
212 rData = result.fqdn.join('.'); | 229 rData = result.fqdn; |
213 break; | 230 break; |
214 case RRType.TXT: | 231 case RRType.TXT: |
215 // TODO(karlklose): convert to a String or Map. | 232 // TODO(karlklose): convert to a String or Map. |
216 default: | 233 default: |
217 checkLength(offset + rDataLength); | 234 checkLength(offset + rDataLength); |
218 rData = new Uint8List.view(data.buffer, offset, rDataLength); | 235 rData = new Uint8List.view(data.buffer, offset, rDataLength); |
219 offset += rDataLength; | 236 offset += rDataLength; |
220 break; | 237 break; |
221 } | 238 } |
222 assert(rData != null); | 239 assert(rData != null); |
(...skipping 23 matching lines...) Expand all Loading... | |
246 return result; | 263 return result; |
247 } | 264 } |
248 | 265 |
249 /// Exceptions thrown by decoder when the packet is invalid. | 266 /// Exceptions thrown by decoder when the packet is invalid. |
250 class MDnsDecodeException implements Exception { | 267 class MDnsDecodeException implements Exception { |
251 /// Exception message. | 268 /// Exception message. |
252 final int offset; | 269 final int offset; |
253 const MDnsDecodeException(this.offset); | 270 const MDnsDecodeException(this.offset); |
254 String toString() => 'Decoding error at $offset'; | 271 String toString() => 'Decoding error at $offset'; |
255 } | 272 } |
OLD | NEW |