Merge branch 'master' of ssh://repo.or.cz/srv/git/ail
[ail.git] / source / dns.cpp
blob27a1314d8ca723601234e42c2edcba77f8e946f0
1 #include <ail/dns.hpp>
2 #include <ail/string.hpp>
3 #include <ail/net.hpp>
5 #include <boost/bind.hpp>
6 #include <boost/foreach.hpp>
8 namespace ail
10 namespace
12 std::size_t const dns_buffer_size = 1024;
15 bool dns_request::operator==(dns_request const & other) const
17 return transaction_id == other.transaction_id && name == other.name;
20 dns_client::dns_client(boost::asio::io_service & io_service, std::string const & server, ushort port):
21 io_service(io_service),
22 resolver(io_service),
23 server(server),
24 port(port),
25 current_id(0),
26 socket(io_service, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), port)),
27 got_endpoint(false)
29 receive_data();
32 void dns_client::lookup(std::string const & name, dns_lookup_handler_type lookup_handler)
34 boost::mutex::scoped_lock scoped_lock(mutex);
36 //boost::asio::ip::udp::resolver::query query(server, "dns");
37 boost::asio::ip::udp::resolver::query query(server, "53");
39 dns_request request;
40 request.transaction_id = current_id;
41 request.name = name;
42 request.lookup_handler = lookup_handler;
44 pending_requests.push_back(request);
46 if(got_endpoint)
47 send_dns_request(request);
48 else
50 resolver.async_resolve(query, boost::bind(&dns_client::resolve_event, this, boost::asio::placeholders::error, boost::asio::placeholders::iterator, request));
51 io_service.stop();
54 current_id++;
57 void dns_client::resolve_event(boost::system::error_code const & error, boost::asio::ip::udp::resolver::iterator endpoint_iterator, dns_request & request)
59 if(!error)
61 boost::mutex::scoped_lock scoped_lock(mutex);
63 //std::cout << "Retrieved a DNS endpoint" << std::endl;
64 got_endpoint = true;
65 endpoint = *endpoint_iterator;
67 send_dns_request(request);
69 else
70 error_occured(request, "Unable to resolve DNS server address: " + error.message());
73 void dns_client::continue_processing(char * receive_buffer)
75 delete receive_buffer;
76 receive_data();
79 void dns_client::receive_event(boost::system::error_code const & error, std::size_t bytes_received, char * receive_buffer)
81 boost::mutex::scoped_lock scoped_lock(mutex);
83 std::string malformed_packet_message = "DNS server returned a malformed packet (" + number_to_string<std::size_t>(bytes_received) + " byte(s))";
85 if(!error)
87 //std::cout << "Received " << bytes_received << " byte(s)" << std::endl;
89 std::string packet(receive_buffer, bytes_received);
90 bit_reader reader(packet);
92 std::size_t word = 16;
94 ushort transaction_id;
96 try
98 transaction_id = static_cast<ushort>(reader.read(word));
100 catch(exception &)
102 global_error(malformed_packet_message);
103 continue_processing(receive_buffer);
104 return;
107 request_vector::iterator iterator;
108 if(get_request(transaction_id, iterator))
110 dns_lookup_result output;
111 dns_request & request = *iterator;
112 output.name = request.name;
116 bool is_response = reader.read_bool();
117 ulong opcode = reader.read(4);
118 bool authoritative = reader.read_bool();
119 bool truncated = reader.read_bool();
120 bool recursion_desired = reader.read_bool();
121 bool recursion_available = reader.read_bool();
122 reader.read_bool();
123 bool answer_authenticated = reader.read_bool();
124 reader.read_bool();
125 ulong reply_code = reader.read(4);
127 ulong questions = reader.read(word);
128 ulong answer_records = reader.read(word);
129 ulong authority_records = reader.read(word);
130 ulong additional_records = reader.read(word);
132 for(ulong i = 0; i < questions; i++)
133 output.questions.push_back(read_question(reader));
135 for(ulong i = 0; i < answer_records; i++)
136 output.answers.push_back(read_answer(reader));
138 output.success = (answer_records > 0);
139 if(output.success)
141 BOOST_FOREACH(dns_answer & answer, output.answers)
143 if(answer.is_ip)
145 output.address = answer.address;
146 break;
150 else
152 error_occured(request, "DNS reply does not contain any answers");
153 continue_processing(receive_buffer);
154 return;
157 for(ulong i = 0; i < authority_records; i++)
158 output.authoritative_nameservers.push_back(read_answer(reader));
160 for(ulong i = 0; i < additional_records; i++)
161 output.additional_records.push_back(read_answer(reader));
163 request.lookup_handler(output);
165 catch(exception &)
167 error_occured(request, malformed_packet_message);
170 else
171 global_error("Invalid transaction ID returned by DNS server: 0x" + number_to_string<ushort>(transaction_id, std::ios_base::hex));
173 else
174 global_error("Receive error: " + error.message());
176 continue_processing(receive_buffer);
179 void dns_client::send_event(boost::system::error_code const & error, std::size_t bytes_received, dns_request & request)
181 boost::mutex::scoped_lock scoped_lock(mutex);
183 if(!error)
185 //std::cout << "Send event (" << bytes_received << " bytes), deallocating buffer" << std::endl;
187 else
188 error_occured(request, "Unable to send data to DNS server");
190 delete request.buffer;
193 void dns_client::receive_data()
195 char * receive_buffer = new char[dns_buffer_size];
196 socket.async_receive_from(boost::asio::buffer(receive_buffer, dns_buffer_size), endpoint, boost::bind(&dns_client::receive_event, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, receive_buffer));
197 io_service.stop();
200 void dns_client::send_dns_request(dns_request & request)
202 string_vector tokens = ail::tokenise(request.name, ".");
204 std::string packet =
205 big_endian_string(request.transaction_id, 2) +
206 std::string("\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00", 10);
208 BOOST_FOREACH(std::string const & current_token, tokens)
209 packet += big_endian_string(static_cast<ulong>(current_token.size()), 1) + current_token;
211 packet += std::string("\x00\x00\x01\x00\x01", 5);
213 std::size_t packet_size = packet.size();
214 request.buffer = new char[packet_size];
215 std::memcpy(request.buffer, packet.c_str(), packet_size);
217 socket.async_send_to(boost::asio::buffer(request.buffer, packet_size), endpoint, boost::bind(&dns_client::send_event, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, request));
218 io_service.stop();
221 void dns_client::global_error(std::string const & message)
223 BOOST_FOREACH(dns_request & request, pending_requests)
225 dns_lookup_result result;
226 result.success = false;
227 result.error_message = message;
228 request.lookup_handler(result);
230 pending_requests.clear();
233 void dns_client::error_occured(dns_request & request, std::string const & message)
235 dns_lookup_result result;
236 result.success = false;
237 result.name = request.name;
238 result.error_message = message;
239 request.lookup_handler(result);
240 erase_request(request);
243 void dns_client::erase_request(dns_request & request)
245 for(request_vector::iterator i = pending_requests.begin(), end = pending_requests.end(); i != end; i++)
247 if(*i == request)
249 pending_requests.erase(i);
250 break;
255 bool dns_client::get_request(ushort transaction_id, request_vector::iterator & output)
257 for(request_vector::iterator i = pending_requests.begin(), end = pending_requests.end(); i != end; i++)
259 if(i->transaction_id == transaction_id)
261 output = i;
262 return true;
265 return false;
268 dns_question dns_client::read_question(bit_reader & reader)
270 dns_question output;
271 output.name = read_name(reader);
272 output.type = reader.read_bytes(2);
273 output.dns_class = reader.read_bytes(2);
274 //std::cout << "Question: " << output.name << std::endl;
275 return output;
278 dns_answer dns_client::read_answer(bit_reader & reader)
280 dns_answer output;
281 output.name = read_name(reader);
282 output.type = reader.read_bytes(2);
283 output.dns_class = reader.read_bytes(2);
284 output.time_to_live = reader.read_bytes(4);
285 output.is_unknown = false;
287 ulong data_length = reader.read_bytes(2);
288 switch(output.type)
290 //A, IPv4 address
291 case 1:
293 output.is_ip = true;
294 ulong numeric_ip = reader.read_bytes(4);
295 output.address = convert_ipv4(numeric_ip);
296 break;
299 //AAAA, IPv6 address
300 case 28:
301 output.is_ip = true;
302 output.address = convert_ipv6(reader.string(16));
303 break;
305 //NS, nameserver, name
306 case 2:
308 //CNAME, canonical name, name
309 case 5:
310 output.address = read_name(reader);
311 break;
313 //unknown
314 default:
315 output.is_unknown = true;
316 output.other_data = reader.string(data_length);
317 break;
320 return output;
323 std::string dns_client::read_name(bit_reader & reader)
325 std::string address;
327 bool fix_offset = false;
328 std::size_t final_offset;
330 bool is_first = true;
331 while(true)
333 ulong length = reader.read_bytes(1);
334 if(length == 0)
335 break;
336 if(length >= 0xc0)
338 ulong subtrahend = 0xc000;
339 ulong new_offset = ((length << bits_per_byte) | reader.read_bytes(1)) - subtrahend;
340 if(!fix_offset)
342 fix_offset = true;
343 final_offset = reader.get_offset();
345 reader.set_offset(bits_per_byte * new_offset);
346 continue;
348 if(is_first)
349 is_first = false;
350 else
351 address += ".";
352 address += reader.string(length);
355 if(fix_offset)
356 reader.set_offset(final_offset);
358 //std::cout << "Read name: " << address << std::endl;
360 return address;