Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

dns_resolver.cpp

Go to the documentation of this file.
00001 /*
00002 **  ClanLib SDK
00003 **  Copyright (c) 1997-2005 The ClanLib Team
00004 **
00005 **  This software is provided 'as-is', without any express or implied
00006 **  warranty.  In no event will the authors be held liable for any damages
00007 **  arising from the use of this software.
00008 **
00009 **  Permission is granted to anyone to use this software for any purpose,
00010 **  including commercial applications, and to alter it and redistribute it
00011 **  freely, subject to the following restrictions:
00012 **
00013 **  1. The origin of this software must not be misrepresented; you must not
00014 **     claim that you wrote the original software. If you use this software
00015 **     in a product, an acknowledgment in the product documentation would be
00016 **     appreciated but is not required.
00017 **  2. Altered source versions must be plainly marked as such, and must not be
00018 **     misrepresented as being the original software.
00019 **  3. This notice may not be removed or altered from any source distribution.
00020 **
00021 **  Note: Some of the libraries ClanLib link to may have additional
00022 **  requirements or restrictions.
00023 **
00024 **  File Author(s):
00025 **
00026 **    Magnus Norddahl
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 // CL_DNSResolver Construction:
00040 
00041 CL_DNSResolver::CL_DNSResolver()
00042 : next_query_id(1)
00043 {
00044 }
00045 
00046 CL_DNSResolver::~CL_DNSResolver()
00047 {
00048 }
00049 
00051 // CL_DNSResolver Attributes:
00052 
00053 
00055 // CL_DNSResolver Operations:
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"; // "192.197.212.68"
00064         std::vector<CL_DNSResourceRecord> answers, authorities, additionals;
00065         for (int i=0; i<25; i++)
00066         {
00067 //              cl_log_event("dns", "looking up %1 for %2, using dns server %3", resource_type, domain_name, dns_server_name);
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 // CL_DNSResolver Implementation:
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                 // Convert domain name to be a set of label's (.label1.label2.label3.)
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                 // Query command:
00151                 int query_id = next_query_id++;
00152                 int opcode = OPCODE_QUERY;
00153                 int flags = FLAG_RD;
00154 
00155                 // Create packet:       
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                 // Fill Header Section:
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                 // Fill Question Section:
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                 // Send packet:
00198                 CL_IPAddress dns_server(dns_server_name, "53");
00199                 CL_UDPSocket sock;
00200                 int sent = sock.send(question_packet.get_data(), question_packet.get_size(), dns_server);
00201                 if (sent < question_packet.get_size())
00202                         throw CL_Exception("Unable to send DNS udp packet");
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                         // Wait for response:
00215                         if (sock.get_read_event()->wait(timeout) == false)
00216                                 return status_timeout;
00217                                 
00218 /*                      
00219                         // Get answer packet:
00220                         CL_IPAddress from;
00221                         CL_ByteArray answer_packet(1024);
00222                         int bytes_received = sock.receive(answer_packet.get_data(), answer_packet.get_size(), from);
00223                         if (bytes_received < header_length)
00224                                 return status_error;
00225                         answer_packet.set_size(bytes_received);
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                         // Parse answer packet:
00239                         Header *answer_header = (Header *) answer_packet.get_data();
00240                         if (ntohs(answer_header->id) != query_id)
00241                         {
00242                                 // Wrong packet. Go back on waiting.
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                         // Read question section:                       
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                         // Read answer section:
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                         // Read authority section:
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                         // Read additional section:
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 }

Generated on Sat Feb 19 22:51:15 2005 for npcore by  doxygen 1.4.1