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_resolver.h"
00031 #include "udp_socket.h"
00032 #include "tcp_connection.h"
00033 #include "string_help.h"
00034 #include "event.h"
00035 #include "exception.h"
00036 #include "logger.h"
00037
00039
00040
00041 CL_DNSResolver::CL_DNSResolver()
00042 : next_query_id(1)
00043 {
00044 }
00045
00046 CL_DNSResolver::~CL_DNSResolver()
00047 {
00048 }
00049
00051
00052
00053
00055
00056
00057 CL_DNSResolver::Status CL_DNSResolver::lookup_resource(
00058 const CL_String &domain_name,
00059 const CL_String &resource_type,
00060 std::vector<CL_DNSResourceRecord> &out_records,
00061 int timeout)
00062 {
00063 CL_String dns_server_name = "198.41.0.4";
00064 std::vector<CL_DNSResourceRecord> answers, authorities, additionals;
00065 for (int i=0; i<25; i++)
00066 {
00067
00068 answers.clear();
00069 authorities.clear();
00070 additionals.clear();
00071 Status s = perform_query(
00072 domain_name,
00073 resource_type,
00074 answers,
00075 authorities,
00076 additionals,
00077 timeout,
00078 dns_server_name);
00079 if (s == status_data_not_found)
00080 {
00081 if (authorities.empty())
00082 return status_data_not_found;
00083 if (authorities[0].get_type() != TEXT("NS"))
00084 return status_data_not_found;
00085 dns_server_name = authorities[0].get_ns_nsdname();
00086 continue;
00087 }
00088 out_records = answers;
00089 return s;
00090 }
00091 return status_error;
00092 }
00093
00095
00096
00097 CL_DNSResolver::Status CL_DNSResolver::perform_query(
00098 const CL_String &domain_name,
00099 const CL_String &resource_type,
00100 std::vector<CL_DNSResourceRecord> &out_answer,
00101 std::vector<CL_DNSResourceRecord> &out_authority,
00102 std::vector<CL_DNSResourceRecord> &out_additional,
00103 int timeout,
00104 const CL_String &dns_server_name)
00105 {
00106 #define FLAG_QR 1
00107 #define OPCODE_QUERY 0
00108 #define OPCODE_IQUERY 1
00109 #define OPCODE_STATUS 2
00110 #define FLAG_AA (1<<(15-5))
00111 #define FLAG_TC (1<<(15-6))
00112 #define FLAG_RD (1<<(15-7))
00113 #define FLAG_RA (1<<(15-8))
00114 #define RCODE_SUCCESS 0
00115 #define RCODE_FORMAT_ERROR 1
00116 #define RCODE_SERVER_FAILURE 2
00117 #define RCODE_NAME_ERROR 3
00118 #define RCODE_NOT_IMPLEMENTED 4
00119 #define RCODE_REFUSED 5
00120
00121 struct Header
00122 {
00123 unsigned short id;
00124 unsigned short command;
00125 unsigned short qdcount;
00126 unsigned short ancount;
00127 unsigned short nscount;
00128 unsigned short arcount;
00129 };
00130
00131 try
00132 {
00133
00134 CL_StringA domain_name_local8 = CL_StringHelp::text_to_local8(domain_name);
00135 if (domain_name_local8.empty())
00136 {
00137 cl_log_event("dns", "Malformed domain name %1", domain_name_local8);
00138 return status_error;
00139 }
00140 if (domain_name_local8[0] != '.')
00141 domain_name_local8 = "." + domain_name_local8;
00142 if (domain_name_local8[domain_name_local8.length()-1] != '.')
00143 domain_name_local8 = domain_name_local8 + ".";
00144 if (domain_name_local8.length() == 1)
00145 {
00146 cl_log_event("dns", "Malformed domain name %1", domain_name_local8);
00147 return status_error;
00148 }
00149
00150
00151 int query_id = next_query_id++;
00152 int opcode = OPCODE_QUERY;
00153 int flags = FLAG_RD;
00154
00155
00156 int qname_length = domain_name_local8.length();
00157 int question_length = qname_length + 2 + 2;
00158 int header_length = 2 * 6;
00159 CL_ByteArray question_packet(header_length + question_length);
00160 memset(question_packet.get_data(), 0, question_packet.get_size());
00161
00162
00163 Header *header = (Header *) question_packet.get_data();
00164 header->id = htons(query_id);
00165 header->command = htons(flags + (opcode << 11));
00166 header->qdcount = htons(1);
00167 header->ancount = htons(0);
00168 header->nscount = htons(0);
00169 header->arcount = htons(0);
00170
00171
00172 char *qname = question_packet.get_data() + header_length;
00173 unsigned short &qtype = *(unsigned short *) (qname + qname_length);
00174 unsigned short &qclass = *(unsigned short *) (qname + qname_length + 2);
00175 memcpy(qname, domain_name_local8.data(), qname_length);
00176 int label_pos = 0;
00177 qname[0] = 0;
00178 for (int i = 1; i < qname_length; i++)
00179 {
00180 if (qname[i] == '.')
00181 {
00182 int label_length = i - label_pos - 1;
00183 if (label_length <= 0)
00184 {
00185 cl_log_event("dns", "Fill Question Section error, i=%1, label_pos=%2, label_length=%3, name=%4", i, label_pos, label_length, domain_name_local8);
00186 return status_error;
00187 }
00188 qname[i] = 0;
00189 qname[label_pos] = label_length;
00190 label_pos = i;
00191 }
00192 }
00193 qtype = htons(CL_DNSResourceRecord::type_to_int(resource_type));
00194 qclass = htons(CL_DNSResourceRecord::class_to_int(TEXT("IN")));
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204 CL_TCPConnection sock;
00205 sock.connect(dns_server_name, "53");
00206 unsigned short len = htons(question_packet.get_size());
00207 sock.send(&len, 2);
00208 int sent = sock.send(question_packet.get_data(), question_packet.get_size());
00209 if (sent < question_packet.get_size())
00210 throw CL_Exception("Unable to send DNS tcp packet");
00211
00212 while (timeout >= 0)
00213 {
00214
00215 if (sock.get_read_event()->wait(timeout) == false)
00216 return status_timeout;
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227 sock.receive(&len, 2);
00228 len = ntohs(len);
00229 CL_ByteArray answer_packet(len);
00230 int bytes_received = sock.receive(answer_packet.get_data(), answer_packet.get_size());
00231
00232 if (bytes_received < header_length)
00233 {
00234 cl_log_event("dns", "Invalid answer: bytes_received(%1 bytes) < header_length", bytes_received);
00235 return status_error;
00236 }
00237
00238
00239 Header *answer_header = (Header *) answer_packet.get_data();
00240 if (ntohs(answer_header->id) != query_id)
00241 {
00242
00243 timeout -= 250;
00244 continue;
00245 }
00246
00247 int rcode = (ntohs(answer_header->command) & 0x000f);
00248 switch (rcode)
00249 {
00250 case RCODE_SUCCESS:
00251 break;
00252 case RCODE_NAME_ERROR:
00253 cl_log_event("dns", "RCODE_NAME_ERROR");
00254 return status_name_not_found;
00255 case RCODE_FORMAT_ERROR:
00256 cl_log_event("dns", "RCODE_FORMAT_ERROR");
00257 return status_error;
00258 case RCODE_SERVER_FAILURE:
00259 cl_log_event("dns", "RCODE_SERVER_FAILURE");
00260 return status_error;
00261 case RCODE_NOT_IMPLEMENTED:
00262 cl_log_event("dns", "RCODE_NOT_IMPLEMENTED");
00263 return status_error;
00264 case RCODE_REFUSED:
00265 cl_log_event("dns", "RCODE_REFUSED");
00266 return status_error;
00267 default:
00268 cl_log_event("dns", "Unkown rcode %1", rcode);
00269 return status_error;
00270 }
00271
00272 int question_count = ntohs(answer_header->qdcount);
00273 int answer_count = ntohs(answer_header->ancount);
00274 int authority_count = ntohs(answer_header->nscount);
00275 int additional_count = ntohs(answer_header->arcount);
00276
00277 int pos = header_length;
00278 int size = answer_packet.get_size();
00279
00280
00281 for (int index_question = 0; index_question < question_count; index_question++)
00282 {
00283 try
00284 {
00285 pos = CL_DNSResourceRecord::find_domain_name_end(answer_packet, pos) + 2 + 2;
00286 }
00287 catch (CL_Exception e)
00288 {
00289 cl_log_event("dns", "Read question section error [%1]: %2", index_question, e.message);
00290 return status_error;
00291 }
00292 }
00293
00294 std::vector<CL_DNSResourceRecord> answers, authorities, additionals;
00295
00296
00297 for (int index_answer = 0; index_answer < answer_count; index_answer++)
00298 {
00299 CL_DNSResourceRecord record;
00300 bool result = read_record(answer_packet, pos, record);
00301 if (result == false)
00302 {
00303 cl_log_event("dns", "Read answer %1 section RR failed", index_answer);
00304 return status_error;
00305 }
00306 answers.push_back(record);
00307 }
00308
00309
00310 for (int index_authority = 0; index_authority < authority_count; index_authority++)
00311 {
00312 CL_DNSResourceRecord record;
00313 bool result = read_record(answer_packet, pos, record);
00314 if (result == false)
00315 {
00316 cl_log_event("dns", "Read authority %1 section RR failed", index_authority);
00317 return status_error;
00318 }
00319 authorities.push_back(record);
00320 }
00321
00322
00323 for (int index_additional = 0; index_additional < additional_count; index_additional++)
00324 {
00325 CL_DNSResourceRecord record;
00326 bool result = read_record(answer_packet, pos, record);
00327 if (result == false)
00328 {
00329 cl_log_event("dns", "Read additional %1 section RR failed", index_additional);
00330 return status_error;
00331 }
00332 additionals.push_back(record);
00333 }
00334
00335 out_answer = answers;
00336 out_authority = authorities;
00337 out_additional = additionals;
00338
00339 if (answer_count == 0)
00340 return status_data_not_found;
00341
00342 return status_success;
00343 }
00344
00345 return status_timeout;
00346 }
00347 catch (CL_Exception e)
00348 {
00349 cl_log_event("dns", "Exception: %1", e.message);
00350 return status_error;
00351 }
00352 }
00353
00354 bool CL_DNSResolver::read_record(CL_ByteArray &packet, int &pos, CL_DNSResourceRecord &out_record)
00355 {
00356 try
00357 {
00358 out_record.set_record(packet, pos);
00359 pos = out_record.get_rdata_offset() + out_record.get_rdata_length();
00360 return true;
00361 }
00362 catch (CL_Exception e)
00363 {
00364 return false;
00365 }
00366 }