Line data Source code
1 : /*
2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
3 : * Copyright Contributors to the OpenSDN project
4 : */
5 :
6 : #include <boost/bind/bind.hpp>
7 : #include <bind/bind_util.h>
8 : #include <base/address_util.h>
9 : #include <bind/bind_resolver.h>
10 :
11 : using namespace boost::placeholders;
12 :
13 : BindResolver *BindResolver::resolver_;
14 :
15 17 : void BindResolver::Init(boost::asio::io_context &io,
16 : const std::vector<DnsServer> &dns_servers,
17 : uint16_t client_port, Callback cb, uint8_t dscp) {
18 17 : assert(resolver_ == NULL);
19 17 : resolver_ = new BindResolver(io, dns_servers, client_port, cb, dscp);
20 17 : }
21 :
22 17 : void BindResolver::Shutdown() {
23 17 : if (resolver_) {
24 17 : delete resolver_;
25 17 : resolver_ = NULL;
26 : }
27 17 : }
28 :
29 17 : BindResolver::BindResolver(boost::asio::io_context &io,
30 : const std::vector<DnsServer> &dns_servers,
31 : uint16_t client_port, Callback cb,
32 17 : uint8_t dscp)
33 17 : : pkt_buf_(NULL), cb_(cb), sock_(io), dscp_value_(dscp) {
34 :
35 17 : boost::system::error_code ec;
36 17 : uint8_t size = (dns_servers.size() > max_dns_servers) ?
37 17 : dns_servers.size() : max_dns_servers;
38 17 : dns_ep_.resize(size);
39 34 : for (unsigned int i = 0; i < dns_servers.size(); ++i) {
40 : boost::asio::ip::address dns_address(
41 17 : AddressFromString(dns_servers[i].ip_, &ec));
42 : boost::asio::ip::udp::endpoint *ep =
43 17 : new boost::asio::ip::udp::endpoint(dns_address, dns_servers[i].port_);
44 17 : assert (ec.value() == 0);
45 17 : dns_ep_[i] = ep;
46 : }
47 :
48 : boost::asio::ip::udp::endpoint local_ep(boost::asio::ip::address::
49 34 : from_string("0.0.0.0", ec),
50 17 : client_port);
51 17 : sock_.open(boost::asio::ip::udp::v4(), ec);
52 17 : assert(ec.value() == 0);
53 17 : sock_.bind(local_ep, ec);
54 17 : if (ec.value() != 0) {
55 15 : local_ep.port(0);
56 15 : sock_.bind(local_ep, ec);
57 15 : assert(ec.value() == 0);
58 : }
59 17 : if (dscp_value_) {
60 0 : SetDscpSocketOption();
61 : }
62 17 : AsyncRead();
63 17 : }
64 :
65 0 : void BindResolver::SetDscpSocketOption() {
66 : /* The dscp_value_ field is expected to have DSCP value between 0 and 63 ie
67 : * in the lower order 6 bits of a byte. However, setsockopt expects DSCP
68 : * value in upper 6 bits of a byte. Hence left shift the value by 2 digits
69 : * before passing it to setsockopt */
70 0 : uint8_t value = dscp_value_ << 2;
71 0 : int retval = setsockopt(sock_.native_handle(), IPPROTO_IP, IP_TOS,
72 : reinterpret_cast<const char *>(&value), sizeof(value));
73 0 : if (retval < 0) {
74 0 : DNS_BIND_TRACE(DnsBindError, "Setting DSCP bits on socket failed for "
75 : << dscp_value_ << " with errno " << strerror(errno));
76 : }
77 0 : }
78 :
79 0 : void BindResolver::SetDscpValue(uint8_t val) {
80 0 : dscp_value_ = val;
81 0 : SetDscpSocketOption();
82 0 : }
83 :
84 0 : uint8_t BindResolver::GetDscpValue() {
85 0 : uint8_t dscp = 0;
86 0 : unsigned int optlen = sizeof(dscp);
87 0 : int retval = getsockopt(sock_.native_handle(), IPPROTO_IP, IP_TOS,
88 : reinterpret_cast<char *>(&dscp),
89 : reinterpret_cast<socklen_t *>(&optlen));
90 0 : if (retval < 0) {
91 0 : DNS_BIND_TRACE(DnsBindError, "Getting DSCP bits on socket failed "
92 : "with errno " << strerror(errno));
93 : }
94 0 : return dscp;
95 : }
96 :
97 34 : BindResolver::~BindResolver() {
98 17 : boost::system::error_code ec;
99 17 : sock_.cancel(ec);
100 17 : sock_.close(ec);
101 51 : for (unsigned int i = 0; i < dns_ep_.size(); ++i) {
102 34 : delete dns_ep_[i];
103 34 : dns_ep_[i] = NULL;
104 : }
105 17 : resolver_ = NULL;
106 17 : if (pkt_buf_) delete [] pkt_buf_;
107 34 : }
108 :
109 0 : void BindResolver::SetupResolver(const DnsServer &server, uint8_t idx) {
110 0 : if (idx >= max_dns_servers) {
111 0 : DNS_BIND_TRACE(DnsBindError, "BindResolver doesnt support more than " <<
112 : max_dns_servers << " servers; ignoring request for " <<
113 : idx);
114 0 : return;
115 : }
116 :
117 0 : if (idx < dns_ep_.size() && dns_ep_[idx]) {
118 0 : delete dns_ep_[idx];
119 0 : dns_ep_[idx] = NULL;
120 : }
121 :
122 0 : boost::system::error_code ec;
123 : boost::asio::ip::udp::endpoint *ep = new boost::asio::ip::udp::endpoint(
124 0 : AddressFromString(server.ip_, &ec), server.port_);
125 0 : assert (ec.value() == 0);
126 0 : dns_ep_[idx] = ep;
127 : }
128 :
129 0 : bool BindResolver::DnsSend(uint8_t *pkt, unsigned int dns_srv_index,
130 : std::size_t len) {
131 0 : if (dns_srv_index < dns_ep_.size() && dns_ep_[dns_srv_index] && len > 0) {
132 0 : sock_.async_send_to(
133 0 : boost::asio::buffer(pkt, len), *dns_ep_[dns_srv_index],
134 0 : boost::bind(&BindResolver::DnsSendHandler, this,
135 : boost::asio::placeholders::error,
136 : boost::asio::placeholders::bytes_transferred, pkt));
137 0 : return true;
138 : } else {
139 0 : DNS_BIND_TRACE(DnsBindError, "Invalid server index: " << dns_srv_index
140 : << ";");
141 0 : delete [] pkt;
142 0 : return false;
143 : }
144 : }
145 :
146 0 : bool BindResolver::DnsSend(uint8_t *pkt, boost::asio::ip::udp::endpoint ep,
147 : std::size_t len) {
148 0 : if (len > 0) {
149 0 : sock_.async_send_to(
150 0 : boost::asio::buffer(pkt, len),ep,
151 0 : boost::bind(&BindResolver::DnsSendHandler, this,
152 : boost::asio::placeholders::error,
153 : boost::asio::placeholders::bytes_transferred, pkt));
154 0 : return true;
155 : } else {
156 0 : DNS_BIND_TRACE(DnsBindError, "Invalid length of packet: " << len
157 : << ";");
158 0 : delete [] pkt;
159 0 : return false;
160 : }
161 : }
162 :
163 0 : void BindResolver::DnsSendHandler(const boost::system::error_code &error,
164 : std::size_t length, uint8_t *pkt) {
165 0 : if (error)
166 0 : DNS_BIND_TRACE(DnsBindError, "Error sending packet to DNS server : " <<
167 : boost::system::system_error(error).what() << ";");
168 0 : delete [] pkt;
169 0 : }
170 :
171 17 : void BindResolver::AsyncRead() {
172 17 : pkt_buf_ = new uint8_t[max_pkt_size];
173 17 : sock_.async_receive(boost::asio::buffer(pkt_buf_, max_pkt_size),
174 17 : boost::bind(&BindResolver::DnsRcvHandler, this,
175 : boost::asio::placeholders::error,
176 : boost::asio::placeholders::bytes_transferred));
177 17 : }
178 :
179 2 : void BindResolver::DnsRcvHandler(const boost::system::error_code &error,
180 : std::size_t length) {
181 2 : bool del = false;
182 2 : if (!error) {
183 0 : if (cb_)
184 0 : cb_(pkt_buf_, length);
185 : else
186 0 : del = true;
187 : } else {
188 2 : DNS_BIND_TRACE(DnsBindError, "Error receiving DNS response : " <<
189 : boost::system::system_error(error).what() << ";");
190 2 : if (error.value() == boost::asio::error::operation_aborted) {
191 2 : return;
192 : }
193 0 : del = true;
194 : }
195 0 : if (del) delete [] pkt_buf_;
196 0 : pkt_buf_ = NULL;
197 0 : AsyncRead();
198 : }
|