00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include "precomp.h"
00030 #include "dns_resource_record.h"
00031 #include "string_help.h"
00032 #include "exception.h"
00033 #ifdef WIN32
00034 #include <winsock.h>
00035 #else
00036 #include <sys/types.h>
00037 #include <netinet/in.h>
00038 #endif
00039
00041
00042
00043 CL_DNSResourceRecord::CL_DNSResourceRecord()
00044 : record_offset(0), rdata_offset(0), rdata_length(0)
00045 {
00046 }
00047
00048 CL_DNSResourceRecord::CL_DNSResourceRecord(const CL_DNSResourceRecord &other)
00049 : record_offset(0), rdata_offset(0), rdata_length(0)
00050 {
00051 packet = other.packet;
00052 record_offset = other.record_offset;
00053 rdata_offset = other.rdata_offset;
00054 rdata_length = other.rdata_length;
00055 }
00056
00057 CL_DNSResourceRecord::~CL_DNSResourceRecord()
00058 {
00059 }
00060
00062
00063
00064 CL_String CL_DNSResourceRecord::get_name() const
00065 {
00066 return read_domain_name(packet, record_offset);
00067 }
00068
00069 CL_String CL_DNSResourceRecord::get_type() const
00070 {
00071 int type_offset = find_domain_name_end(packet, record_offset);
00072 int rr_data_offset = type_offset + 2 + 2 + 4 + 2;
00073 if (packet.get_size() < rr_data_offset)
00074 throw CL_Exception(TEXT("Premature end of DNS resource record"));
00075 unsigned short rr_type = ntohs(*(unsigned short *) (packet.get_data() + type_offset));
00076 unsigned short rr_class = ntohs(*(unsigned short *) (packet.get_data() + type_offset+2));
00077 unsigned int rr_ttl = ntohl(*(unsigned int *) (packet.get_data() + type_offset+4));
00078 unsigned short rr_data_length = ntohs(*(unsigned short *) (packet.get_data() + type_offset+8));
00079 return type_from_int(rr_type);
00080 }
00081
00082 CL_String CL_DNSResourceRecord::get_class() const
00083 {
00084 int type_offset = find_domain_name_end(packet, record_offset);
00085 int rr_data_offset = type_offset + 2 + 2 + 4 + 2;
00086 if (packet.get_size() < rr_data_offset)
00087 throw CL_Exception(TEXT("Premature end of DNS resource record"));
00088 unsigned short rr_type = ntohs(*(unsigned short *) (packet.get_data() + type_offset));
00089 unsigned short rr_class = ntohs(*(unsigned short *) (packet.get_data() + type_offset+2));
00090 unsigned int rr_ttl = ntohl(*(unsigned int *) (packet.get_data() + type_offset+4));
00091 unsigned short rr_data_length = ntohs(*(unsigned short *) (packet.get_data() + type_offset+8));
00092 return class_from_int(rr_class);
00093 }
00094
00095 int CL_DNSResourceRecord::get_ttl() const
00096 {
00097 int type_offset = find_domain_name_end(packet, record_offset);
00098 int rr_data_offset = type_offset + 2 + 2 + 4 + 2;
00099 if (packet.get_size() < rr_data_offset)
00100 throw CL_Exception(TEXT("Premature end of DNS resource record"));
00101 unsigned short rr_type = ntohs(*(unsigned short *) (packet.get_data() + type_offset));
00102 unsigned short rr_class = ntohs(*(unsigned short *) (packet.get_data() + type_offset+2));
00103 unsigned int rr_ttl = ntohl(*(unsigned int *) (packet.get_data() + type_offset+4));
00104 unsigned short rr_data_length = ntohs(*(unsigned short *) (packet.get_data() + type_offset+8));
00105 return rr_ttl;
00106 }
00107
00108 const CL_ByteArray &CL_DNSResourceRecord::get_packet() const
00109 {
00110 return packet;
00111 }
00112
00113 int CL_DNSResourceRecord::get_record_offset() const
00114 {
00115 return record_offset;
00116 }
00117
00118 int CL_DNSResourceRecord::get_rdata_offset() const
00119 {
00120 return rdata_offset;
00121 }
00122
00123 int CL_DNSResourceRecord::get_rdata_length() const
00124 {
00125 return rdata_length;
00126 }
00127
00128 CL_String CL_DNSResourceRecord::get_cname_cname() const
00129 {
00130 return read_domain_name(packet, rdata_offset);
00131 }
00132
00133 int CL_DNSResourceRecord::get_mx_preference() const
00134 {
00135 if (packet.get_size() < rdata_offset + 2)
00136 throw CL_Exception(TEXT("Premature end of resource data section"));
00137 short *preference = (short *) (packet.get_data() + rdata_offset);
00138 return ntohs(*preference);
00139 }
00140
00141 CL_String CL_DNSResourceRecord::get_mx_exchange() const
00142 {
00143 return read_domain_name(packet, rdata_offset + 2);
00144 }
00145
00146 CL_String CL_DNSResourceRecord::get_ns_nsdname() const
00147 {
00148 return read_domain_name(packet, rdata_offset);
00149 }
00150
00151 CL_String CL_DNSResourceRecord::get_ptr_ptrdname() const
00152 {
00153 return read_domain_name(packet, rdata_offset);
00154 }
00155
00156 CL_String CL_DNSResourceRecord::get_soa_mname() const
00157 {
00158 return read_domain_name(packet, rdata_offset);
00159 }
00160
00161 CL_String CL_DNSResourceRecord::get_soa_rname() const
00162 {
00163 int pos = find_domain_name_end(packet, rdata_offset);
00164 return read_domain_name(packet, pos);
00165 }
00166
00167 unsigned int CL_DNSResourceRecord::get_soa_serial() const
00168 {
00169 int pos = find_domain_name_end(packet, rdata_offset);
00170 pos = find_domain_name_end(packet, pos);
00171 if (packet.get_size() < pos + 4)
00172 throw CL_Exception(TEXT("Premature end of resource data section"));
00173 unsigned int *serial = (unsigned int *) (packet.get_data() + pos);
00174 return ntohl(*serial);
00175 }
00176
00177 int CL_DNSResourceRecord::get_soa_refresh() const
00178 {
00179 int pos = find_domain_name_end(packet, rdata_offset);
00180 pos = find_domain_name_end(packet, pos) + 4;
00181 if (packet.get_size() < pos + 4)
00182 throw CL_Exception(TEXT("Premature end of resource data section"));
00183 int *refresh = (int *) (packet.get_data() + pos);
00184 return ntohl(*refresh);
00185 }
00186
00187 int CL_DNSResourceRecord::get_soa_retry() const
00188 {
00189 int pos = find_domain_name_end(packet, rdata_offset);
00190 pos = find_domain_name_end(packet, pos) + 8;
00191 if (packet.get_size() < pos + 4)
00192 throw CL_Exception(TEXT("Premature end of resource data section"));
00193 int *retry = (int *) (packet.get_data() + pos);
00194 return ntohl(*retry);
00195 }
00196
00197 int CL_DNSResourceRecord::get_soa_expire() const
00198 {
00199 int pos = find_domain_name_end(packet, rdata_offset);
00200 pos = find_domain_name_end(packet, pos) + 12;
00201 if (packet.get_size() < pos + 4)
00202 throw CL_Exception(TEXT("Premature end of resource data section"));
00203 int *expire = (int *) (packet.get_data() + pos);
00204 return ntohl(*expire);
00205 }
00206
00207 unsigned int CL_DNSResourceRecord::get_soa_minimum() const
00208 {
00209 int pos = find_domain_name_end(packet, rdata_offset);
00210 pos = find_domain_name_end(packet, pos) + 16;
00211 if (packet.get_size() < pos + 4)
00212 throw CL_Exception(TEXT("Premature end of resource data section"));
00213 unsigned int *minimum = (unsigned int *) (packet.get_data() + pos);
00214 return ntohl(*minimum);
00215 }
00216
00217 unsigned int CL_DNSResourceRecord::get_wks_address() const
00218 {
00219 if (packet.get_size() < rdata_offset + 4)
00220 throw CL_Exception(TEXT("Premature end of resource data section"));
00221 unsigned int *address = (unsigned int *) (packet.get_data() + rdata_offset);
00222 return ntohs(*address);
00223 }
00224
00225 unsigned char CL_DNSResourceRecord::get_wks_protocol() const
00226 {
00227 if (packet.get_size() < rdata_offset + 5)
00228 throw CL_Exception(TEXT("Premature end of resource data section"));
00229 unsigned char *protocol = (unsigned char *) (packet.get_data() + rdata_offset + 4);
00230 return *protocol;
00231 }
00232
00233 CL_ByteArray CL_DNSResourceRecord::get_wks_bit_map() const
00234 {
00235 int len = rdata_length - 5;
00236 if (len < 0)
00237 throw CL_Exception(TEXT("Premature end of resource data section"));
00238 CL_ByteArray bit_map(len);
00239 memcpy(bit_map.get_data(), packet.get_data() + rdata_offset + 5, len);
00240 return bit_map;
00241 }
00242
00244
00245
00246 CL_DNSResourceRecord &CL_DNSResourceRecord::operator =(const CL_DNSResourceRecord &other)
00247 {
00248 packet = other.packet;
00249 record_offset = other.record_offset;
00250 rdata_offset = other.rdata_offset;
00251 rdata_length = other.rdata_length;
00252 return *this;
00253 }
00254
00255 void CL_DNSResourceRecord::set_record(const CL_ByteArray &new_packet, int new_record_offset)
00256 {
00257
00258
00259 int type_offset = find_domain_name_end(new_packet, new_record_offset);
00260 int rr_data_offset = type_offset + 2 + 2 + 4 + 2;
00261 if (new_packet.get_size() < rr_data_offset)
00262 throw CL_Exception(TEXT("Premature end of DNS resource record"));
00263
00264 unsigned short rr_type = ntohs(*(unsigned short *) (new_packet.get_data() + type_offset));
00265 unsigned short rr_class = ntohs(*(unsigned short *) (new_packet.get_data() + type_offset+2));
00266 unsigned int rr_ttl = ntohl(*(unsigned int *) (new_packet.get_data() + type_offset+4));
00267 unsigned short rr_data_length = ntohs(*(unsigned short *) (new_packet.get_data() + type_offset+8));
00268
00269 if (new_packet.get_size() < rr_data_offset + rr_data_length)
00270 throw CL_Exception(TEXT("Premature end of DNS resource record RDATA section"));
00271
00272
00273
00274 packet = new_packet;
00275 rdata_offset = rr_data_offset;
00276 rdata_length = rr_data_length;
00277 record_offset = new_record_offset;
00278 }
00279
00280 struct RRType
00281 {
00282 char *name;
00283 int value;
00284 char *description;
00285 };
00286
00287 static RRType rr_types[] =
00288 {
00289 "A", 1, "a host address",
00290 "NS", 2, "an authoritative name server",
00291 "MD", 3, "a mail destination (Obsolete - use MX)",
00292 "MF", 4, "a mail forwarder (Obsolete - use MX)",
00293 "CNAME", 5, "the canonical name for an alias",
00294 "SOA", 6, "marks the start of a zone of authority",
00295 "MB", 7, "a mailbox domain name (EXPERIMENTAL)",
00296 "MG", 8, "a mail group member (EXPERIMENTAL)",
00297 "MR", 9, "a mail rename domain name (EXPERIMENTAL)",
00298 "NULL", 10, "a null RR (EXPERIMENTAL)",
00299 "WKS", 11, "a well known service description",
00300 "PTR", 12, "a domain name pointer",
00301 "HINFO", 13, "host information",
00302 "MINFO", 14, "mailbox or mail list information",
00303 "MX", 15, "mail exchange",
00304 "TXT", 16, "text strings",
00305 "AXFR", 252, "qtype: A request for a transfer of an entire zone",
00306 "MAILB", 253, "qtype: A request for mailbox-related records (MB, MG or MR)",
00307 "MAILA", 254, "qtype: A request for mail agent RRs (Obsolete - see MX)",
00308 "*", 255, "qtype: A request for all records",
00309 0, 0, 0
00310 };
00311
00312 struct ClassType
00313 {
00314 char *name;
00315 int value;
00316 char *description;
00317 };
00318
00319 static ClassType class_types[] =
00320 {
00321 "IN", 1, "the Internet",
00322 "CS", 2, "the CSNET class (Obsolete)",
00323 "CH", 3, "the CHAOS class",
00324 "HS", 4, "Hesiod [Dyer 87]",
00325 "*", 255, "qclass: Any class",
00326 0, 0, 0
00327 };
00328
00329 int CL_DNSResourceRecord::type_to_int(const CL_String &qtype)
00330 {
00331 CL_StringA qtype_local8 = CL_StringHelp::text_to_local8(qtype);
00332 for (int index = 0; rr_types[index].name != 0; index++)
00333 {
00334 if (rr_types[index].name == qtype_local8)
00335 return rr_types[index].value;
00336 }
00337
00338 int value = CL_StringHelp::local8_to_int(qtype_local8);
00339 if (value <= 0)
00340 throw CL_Exception(TEXT("Unknown DNS resource record type ") + qtype);
00341 return value;
00342 }
00343
00344 CL_String CL_DNSResourceRecord::type_from_int(int qtype)
00345 {
00346 for (int index = 0; rr_types[index].name != 0; index++)
00347 {
00348 if (rr_types[index].value == qtype)
00349 return CL_StringHelp::local8_to_text(rr_types[index].name);
00350 }
00351 return CL_StringHelp::int_to_text(qtype);
00352 }
00353
00354 CL_String CL_DNSResourceRecord::type_description(const CL_String &qtype)
00355 {
00356 CL_StringA qtype_local8 = CL_StringHelp::text_to_local8(qtype);
00357 for (int index = 0; rr_types[index].name != 0; index++)
00358 {
00359 if (rr_types[index].name == qtype_local8)
00360 return CL_StringHelp::local8_to_text(rr_types[index].description);
00361 }
00362 return TEXT("Unknown type ") + qtype;
00363 }
00364
00365 CL_String CL_DNSResourceRecord::type_description(int qtype)
00366 {
00367 for (int index = 0; rr_types[index].name != 0; index++)
00368 {
00369 if (rr_types[index].value == qtype)
00370 return CL_StringHelp::local8_to_text(rr_types[index].description);
00371 }
00372 return TEXT("Unknown type ") + CL_StringHelp::int_to_text(qtype);
00373 }
00374
00375 int CL_DNSResourceRecord::class_to_int(const CL_String &qclass)
00376 {
00377 CL_StringA qclass_local8 = CL_StringHelp::text_to_local8(qclass);
00378 for (int index = 0; class_types[index].name != 0; index++)
00379 {
00380 if (class_types[index].name == qclass_local8)
00381 return class_types[index].value;
00382 }
00383
00384 int value = CL_StringHelp::local8_to_int(qclass_local8);
00385 if (value <= 0)
00386 throw CL_Exception(TEXT("Unknown DNS resource record type ") + qclass);
00387 return value;
00388 }
00389
00390 CL_String CL_DNSResourceRecord::class_from_int(int qclass)
00391 {
00392 for (int index = 0; class_types[index].name != 0; index++)
00393 {
00394 if (class_types[index].value == qclass)
00395 return CL_StringHelp::local8_to_text(class_types[index].name);
00396 }
00397 return CL_StringHelp::int_to_text(qclass);
00398 }
00399
00400 CL_String CL_DNSResourceRecord::class_description(const CL_String &qclass)
00401 {
00402 CL_StringA qclass_local8 = CL_StringHelp::text_to_local8(qclass);
00403 for (int index = 0; class_types[index].name != 0; index++)
00404 {
00405 if (class_types[index].name == qclass_local8)
00406 return CL_StringHelp::local8_to_text(class_types[index].description);
00407 }
00408 return TEXT("Unknown class ") + qclass;
00409 }
00410
00411 CL_String CL_DNSResourceRecord::class_description(int qclass)
00412 {
00413 for (int index = 0; class_types[index].name != 0; index++)
00414 {
00415 if (class_types[index].value == qclass)
00416 return CL_StringHelp::local8_to_text(class_types[index].description);
00417 }
00418 return TEXT("Unknown class ") + CL_StringHelp::int_to_text(qclass);
00419 }
00420
00422
00423
00424 int CL_DNSResourceRecord::find_domain_name_end(const CL_ByteArray &packet, int offset)
00425 {
00426 const unsigned char *data = (const unsigned char *) packet.get_data();
00427 while (true)
00428 {
00429 if (offset >= packet.get_size())
00430 throw CL_Exception(TEXT("Premature end of resource data domain name"));
00431
00432 if (data[offset] > 0 && data[offset] < 64)
00433 {
00434 offset += data[offset] + 1;
00435 }
00436 else if ((data[offset] & 0xc0) == 0xc0)
00437 {
00438 offset += 2;
00439 break;
00440 }
00441 else if (data[offset] == 0)
00442 {
00443 offset++;
00444 break;
00445 }
00446 else
00447 {
00448 throw CL_Exception(TEXT("Malformed resource data domain name (find_domain_name_end)"));
00449 }
00450 }
00451 return offset;
00452 }
00453
00454 CL_String CL_DNSResourceRecord::read_domain_name(const CL_ByteArray &packet, int offset)
00455 {
00456 CL_String name;
00457 const unsigned char *data = (const unsigned char *) packet.get_data();
00458 while (true)
00459 {
00460 if (offset >= packet.get_size())
00461 throw CL_Exception(TEXT("Premature end of resource data domain name"));
00462
00463 if (data[offset] > 0 && data[offset] < 64)
00464 {
00465 if (offset+1+data[offset] >= packet.get_size())
00466 throw CL_Exception(TEXT("Premature end of resource data domain name"));
00467
00468 if (!name.empty())
00469 name.append(TEXT("."));
00470
00471 name.append(CL_StringHelp::local8_to_text(CL_StringA((const char *) data+offset+1, data[offset])));
00472 offset += data[offset] + 1;
00473
00474 if (name.length() > 512)
00475 throw CL_Exception(TEXT("Domain name exceeds 512 characters!"));
00476 }
00477 else if ((data[offset] & 0xc0) == 0xc0)
00478 {
00479 if (offset+1 >= packet.get_size())
00480 throw CL_Exception(TEXT("Premature end of resource data domain name"));
00481
00482 offset = (int(data[offset] & 0x3f) << 8) + data[offset+1];
00483 }
00484 else if (data[offset] == 0)
00485 {
00486 break;
00487 }
00488 else
00489 {
00490 throw CL_Exception(TEXT("Malformed resource data domain name"));
00491 }
00492 }
00493 return name;
00494 }