Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Kea
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
Operations
Operations
Incidents
Packages & Registries
Packages & Registries
Container Registry
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Sebastian Schrader
Kea
Commits
7bc6ee51
Commit
7bc6ee51
authored
Apr 13, 2011
by
Ocean Wang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[trac751] Move some files from asiolink library to asiodns library
parent
0eacaf1a
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
226 additions
and
210 deletions
+226
-210
src/lib/asiodns/Makefile.am
src/lib/asiodns/Makefile.am
+4
-0
src/lib/asiodns/README
src/lib/asiodns/README
+157
-0
src/lib/asiodns/asiodef.cc
src/lib/asiodns/asiodef.cc
+12
-12
src/lib/asiodns/asiodef.h
src/lib/asiodns/asiodef.h
+23
-0
src/lib/asiodns/asiodef.msg
src/lib/asiodns/asiodef.msg
+2
-2
src/lib/asiodns/io_fetch.cc
src/lib/asiodns/io_fetch.cc
+17
-21
src/lib/asiodns/qid_gen.cc
src/lib/asiodns/qid_gen.cc
+4
-4
src/lib/asiodns/qid_gen.h
src/lib/asiodns/qid_gen.h
+2
-2
src/lib/asiodns/tests/Makefile.am
src/lib/asiodns/tests/Makefile.am
+1
-0
src/lib/asiodns/tests/qid_gen_unittest.cc
src/lib/asiodns/tests/qid_gen_unittest.cc
+4
-4
src/lib/asiolink/Makefile.am
src/lib/asiolink/Makefile.am
+0
-4
src/lib/asiolink/README
src/lib/asiolink/README
+0
-160
src/lib/asiolink/tests/Makefile.am
src/lib/asiolink/tests/Makefile.am
+0
-1
No files found.
src/lib/asiodns/Makefile.am
View file @
7bc6ee51
...
...
@@ -11,12 +11,16 @@ CLEANFILES = *.gcno *.gcda
lib_LTLIBRARIES
=
libasiodns.la
libasiodns_la_SOURCES
=
dns_answer.h
libasiodns_la_SOURCES
+=
asiodef.cc asiodef.h
libasiodns_la_SOURCES
+=
dns_lookup.h
libasiodns_la_SOURCES
+=
dns_server.h
libasiodns_la_SOURCES
+=
dns_service.cc dns_service.h
libasiodns_la_SOURCES
+=
tcp_server.cc tcp_server.h
libasiodns_la_SOURCES
+=
udp_server.cc udp_server.h
libasiodns_la_SOURCES
+=
io_fetch.cc io_fetch.h
libasiodns_la_SOURCES
+=
qid_gen.cc qid_gen.h
EXTRA_DIST
=
asiodef.msg
# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
# B10_CXXFLAGS)
...
...
src/lib/asiodns/README
0 → 100644
View file @
7bc6ee51
The asiodns library is intended to provide an abstraction layer between
BIND10 modules and asiolink library.
These DNS server and client routines are written using the "stackless
coroutine" pattern invented by Chris Kohlhoff and described at
http://blog.think-async.com/2010/03/potted-guide-to-stackless-coroutines.html.
This is intended to simplify development a bit, since it allows the
routines to be written in a straightfowrard step-step-step fashion rather
than as a complex chain of separate handler functions.
Coroutine objects (i.e., UDPServer, TCPServer and IOFetch) are objects
with reenterable operator() members. When an instance of one of these
classes is called as a function, it resumes at the position where it left
off. Thus, a UDPServer can issue an asynchronous I/O call and specify
itself as the handler object; when the call completes, the UDPServer
carries on at the same position. As a result, the code can look as
if it were using synchronous, not asynchronous, I/O, providing some of
the benefit of threading but with minimal switching overhead.
So, in simplified form, the behavior of a DNS Server is:
REENTER:
while true:
YIELD packet = read_packet
FORK
if not parent:
break
# This callback informs the caller that a packet has arrived, and
# gives it a chance to update configuration, etc
SimpleCallback(packet)
YIELD answer = DNSLookup(packet, this)
response = DNSAnswer(answer)
YIELD send(response)
At each "YIELD" point, the coroutine initiates an asynchronous operation,
then pauses and turns over control to some other task on the ASIO service
queue. When the operation completes, the coroutine resumes.
DNSLookup, DNSAnswer and SimpleCallback define callback methods
used by a DNS Server to communicate with the module that called it.
They are abstract-only classes whose concrete implementations
are supplied by the calling module.
The DNSLookup callback always runs asynchronously. Concrete
implementations must be sure to call the server's "resume" method when
it is finished.
In an authoritative server, the DNSLookup implementation would examine
the query, look up the answer, then call "resume". (See the diagram
in doc/auth_process.jpg.)
In a recursive server, the DNSLookup impelemtation would initiate a
DNSQuery, which in turn would be responsible for calling the server's
"resume" method. (See the diagram in doc/recursive_process.jpg.)
A DNSQuery object is intended to handle resolution of a query over
the network when the local authoritative data sources or cache are not
sufficient. The plan is that it will make use of subsidiary DNSFetch
calls to get data from particular authoritative servers, and when it has
gotten a complete answer, it calls "resume".
In current form, however, DNSQuery is much simpler; it forwards queries
to a single upstream resolver and passes the answers back to the client.
It is constructed with the address of the forward server. Queries are
initiated with the question to ask the forward server, a buffer into
which to write the answer, and a pointer to the coroutine to be resumed
when the answer has arrived. In simplified form, the DNSQuery routine is:
REENTER:
render the question into a wire-format query packet
YIELD send(query)
YIELD response = read_packet
server->resume
Currently, DNSQuery is only implemented for UDP queries. In future work
it will be necessary to write code to fall back to TCP when circumstances
require it.
Upstream Fetches
================
Upstream fetches (queries by the resolver on behalf of a client) are made
using a slightly-modified version of the pattern described above.
Sockets
-------
First, it will be useful to understand the class hierarchy used in the
fetch logic:
IOSocket
|
IOAsioSocket
|
+-----+-----+
| |
UDPSocket TCPSocket
IOSocket is a wrapper class for a socket and is used by the authoritative
server code. It is an abstract base class, providing little more that the ability to hold the socket and to return the protocol in use.
Built on this is IOAsioSocket, which adds the open, close, asyncSend and
asyncReceive methods. This is a template class, which takes as template
argument the class of the object that will be used as the callback when the
asynchronous operation completes. This object can be of any type, but must
include an operator() method with the signature:
operator()(asio::error_code ec, size_t length)
... the two arguments being the status of the completed I/O operation and
the number of bytes transferred. (In the case of the open method, the second
argument will be zero.)
Finally, the TCPSocket and UDPSocket classes provide the body of the
asynchronous operations.
Fetch Sequence
--------------
The fetch is implemented by the IOFetch class, which takes as argument the
protocol to use. The sequence is:
REENTER:
render the question into a wire-format query packet
open() // Open socket and optionally connect
if (! synchronous) {
YIELD;
}
YIELD asyncSend(query) // Send query
do {
YIELD asyncReceive(response) // Read response
} while (! complete(response))
close() // Drop connection and close socket
server->resume
The open() method opens a socket for use. On TCP, it also makes a
connection to the remote end. So under UDP the operation will complete
immediately, but under TCP it could take a long time. One solution would be
for the open operation to post an event to the I/O queue; then both cases
could be regarded as being equivalent, with the completion being signalled
by the posting of the completion event. However UDP is the most common case
and that would involve extra overhead. So the open() returns a status
indicating whether the operation completed asynchronously. If it did, the
code yields back to the coroutine; if not the yield is bypassed.
The asynchronous send is straightforward, invoking the underlying ASIO
function. (Note that the address/port is supplied to both the open() and
asyncSend() methods - it is used by the TCPSocket in open() and by the
UDPSocket in asyncSend().)
The asyncReceive() method issues an asynchronous read and waits for completion.
The fetch object keeps track of the amount of data received so far and when
the receive completes it calls a method on the socket to determine if the
entire message has been received. (This will always be the case for UDP. On
TCP though, the message is preceded by a count field as several reads may be
required to read all the data.) The fetch loops until all the data is read.
Finally, the socket is closed and the server called to resume operation.
src/lib/asio
link
/asiodef.cc
→
src/lib/asio
dns
/asiodef.cc
View file @
7bc6ee51
...
...
@@ -5,18 +5,18 @@
#include <log/message_initializer.h>
namespace
isc
{
namespace
asio
link
{
extern
const
isc
::
log
::
MessageID
ASIO_FETCHCOMP
=
"FETCHCOMP"
;
extern
const
isc
::
log
::
MessageID
ASIO_FETCHSTOP
=
"FETCHSTOP"
;
extern
const
isc
::
log
::
MessageID
ASIO_OPENSOCK
=
"OPENSOCK"
;
extern
const
isc
::
log
::
MessageID
ASIO_RECVSOCK
=
"RECVSOCK"
;
extern
const
isc
::
log
::
MessageID
ASIO_RECVTMO
=
"RECVTMO"
;
extern
const
isc
::
log
::
MessageID
ASIO_SENDSOCK
=
"SENDSOCK"
;
extern
const
isc
::
log
::
MessageID
ASIO_UNKORIGIN
=
"UNKORIGIN"
;
extern
const
isc
::
log
::
MessageID
ASIO_UNKRESULT
=
"UNKRESULT"
;
}
// namespace asio
link
namespace
asio
dns
{
extern
const
isc
::
log
::
MessageID
ASIO
DNS
_FETCHCOMP
=
"FETCHCOMP"
;
extern
const
isc
::
log
::
MessageID
ASIO
DNS
_FETCHSTOP
=
"FETCHSTOP"
;
extern
const
isc
::
log
::
MessageID
ASIO
DNS
_OPENSOCK
=
"OPENSOCK"
;
extern
const
isc
::
log
::
MessageID
ASIO
DNS
_RECVSOCK
=
"RECVSOCK"
;
extern
const
isc
::
log
::
MessageID
ASIO
DNS
_RECVTMO
=
"RECVTMO"
;
extern
const
isc
::
log
::
MessageID
ASIO
DNS
_SENDSOCK
=
"SENDSOCK"
;
extern
const
isc
::
log
::
MessageID
ASIO
DNS
_UNKORIGIN
=
"UNKORIGIN"
;
extern
const
isc
::
log
::
MessageID
ASIO
DNS
_UNKRESULT
=
"UNKRESULT"
;
}
// namespace asio
dns
}
// namespace isc
namespace
{
...
...
src/lib/asio
link
/asiodef.h
→
src/lib/asio
dns
/asiodef.h
View file @
7bc6ee51
...
...
@@ -6,18 +6,18 @@
#include <log/message_types.h>
namespace
isc
{
namespace
asio
link
{
namespace
asio
dns
{
extern
const
isc
::
log
::
MessageID
ASIO_FETCHCOMP
;
extern
const
isc
::
log
::
MessageID
ASIO_FETCHSTOP
;
extern
const
isc
::
log
::
MessageID
ASIO_OPENSOCK
;
extern
const
isc
::
log
::
MessageID
ASIO_RECVSOCK
;
extern
const
isc
::
log
::
MessageID
ASIO_RECVTMO
;
extern
const
isc
::
log
::
MessageID
ASIO_SENDSOCK
;
extern
const
isc
::
log
::
MessageID
ASIO_UNKORIGIN
;
extern
const
isc
::
log
::
MessageID
ASIO_UNKRESULT
;
extern
const
isc
::
log
::
MessageID
ASIO
DNS
_FETCHCOMP
;
extern
const
isc
::
log
::
MessageID
ASIO
DNS
_FETCHSTOP
;
extern
const
isc
::
log
::
MessageID
ASIO
DNS
_OPENSOCK
;
extern
const
isc
::
log
::
MessageID
ASIO
DNS
_RECVSOCK
;
extern
const
isc
::
log
::
MessageID
ASIO
DNS
_RECVTMO
;
extern
const
isc
::
log
::
MessageID
ASIO
DNS
_SENDSOCK
;
extern
const
isc
::
log
::
MessageID
ASIO
DNS
_UNKORIGIN
;
extern
const
isc
::
log
::
MessageID
ASIO
DNS
_UNKRESULT
;
}
// namespace asio
link
}
// namespace asio
dns
}
// namespace isc
#endif // __ASIODEF_H
src/lib/asio
link
/asiodef.msg
→
src/lib/asio
dns
/asiodef.msg
View file @
7bc6ee51
...
...
@@ -12,8 +12,8 @@
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
$PREFIX ASIO_
$NAMESPACE asio
link
$PREFIX ASIO
DNS
_
$NAMESPACE asio
dns
FETCHCOMP upstream fetch to %s(%d) has now completed
+ A debug message, this records the the upstream fetch (a query made by the
...
...
src/lib/asiodns/io_fetch.cc
View file @
7bc6ee51
...
...
@@ -27,13 +27,10 @@
#include <dns/opcode.h>
#include <dns/rcode.h>
#include <log/logger.h>
#include <asiolink/qid_gen.h>
#include <asio.hpp>
#include <asio/deadline_timer.hpp>
#include <asio
link
/asiodef.h>
#include <asiodns/qid_gen.h>
#include <asio
dns
/asiodef.h>
#include <asiolink/io_address.h>
#include <asiolink/io_asio_socket.h>
#include <asiolink/io_endpoint.h>
...
...
@@ -42,7 +39,6 @@
#include <asiolink/tcp_socket.h>
#include <asiolink/udp_endpoint.h>
#include <asiolink/udp_socket.h>
#include <asiolink/qid_gen.h>
#include "io_fetch.h"
...
...
@@ -149,7 +145,7 @@ struct IOFetchData {
offset
(
0
),
stopped
(
false
),
timeout
(
wait
),
origin
(
ASIO_UNKORIGIN
),
origin
(
ASIO
DNS
_UNKORIGIN
),
staging
(),
qid
(
QidGenerator
::
getInstance
().
generateQid
())
{}
...
...
@@ -225,7 +221,7 @@ IOFetch::operator()(asio::error_code ec, size_t length) {
// Open a connection to the target system. For speed, if the operation
// is synchronous (i.e. UDP operation) we bypass the yield.
data_
->
origin
=
ASIO_OPENSOCK
;
data_
->
origin
=
ASIO
DNS
_OPENSOCK
;
if
(
data_
->
socket
->
isOpenSynchronous
())
{
data_
->
socket
->
open
(
data_
->
remote_snd
.
get
(),
*
this
);
}
else
{
...
...
@@ -235,10 +231,10 @@ IOFetch::operator()(asio::error_code ec, size_t length) {
do
{
// Begin an asynchronous send, and then yield. When the send completes,
// we will resume immediately after this point.
data_
->
origin
=
ASIO_SENDSOCK
;
data_
->
origin
=
ASIO
DNS
_SENDSOCK
;
CORO_YIELD
data_
->
socket
->
asyncSend
(
data_
->
msgbuf
->
getData
(),
data_
->
msgbuf
->
getLength
(),
data_
->
remote_snd
.
get
(),
*
this
);
// Now receive the response. Since TCP may not receive the entire
// message in one operation, we need to loop until we have received
// it. (This can't be done within the asyncReceive() method because
...
...
@@ -257,8 +253,8 @@ IOFetch::operator()(asio::error_code ec, size_t length) {
// the expected amount of data. Then we need to loop until we have
// received all the data before copying it back to the user's buffer.
// And we want to minimise the amount of copying...
data_
->
origin
=
ASIO_RECVSOCK
;
data_
->
origin
=
ASIO
DNS
_RECVSOCK
;
data_
->
cumulative
=
0
;
// No data yet received
data_
->
offset
=
0
;
// First data into start of buffer
data_
->
received
->
clear
();
// Clear the receive buffer
...
...
@@ -274,7 +270,7 @@ IOFetch::operator()(asio::error_code ec, size_t length) {
// Finished with this socket, so close it. This will not generate an
// I/O error, but reset the origin to unknown in case we change this.
data_
->
origin
=
ASIO_UNKORIGIN
;
data_
->
origin
=
ASIO
DNS
_UNKORIGIN
;
data_
->
socket
->
close
();
/// We are done
...
...
@@ -317,7 +313,7 @@ IOFetch::stop(Result result) {
switch
(
result
)
{
case
TIME_OUT
:
if
(
logger
.
isDebugEnabled
(
1
))
{
logger
.
debug
(
20
,
ASIO_RECVTMO
,
logger
.
debug
(
20
,
ASIO
DNS
_RECVTMO
,
data_
->
remote_snd
->
getAddress
().
toText
().
c_str
(),
static_cast
<
int
>
(
data_
->
remote_snd
->
getPort
()));
}
...
...
@@ -325,7 +321,7 @@ IOFetch::stop(Result result) {
case
SUCCESS
:
if
(
logger
.
isDebugEnabled
(
50
))
{
logger
.
debug
(
30
,
ASIO_FETCHCOMP
,
logger
.
debug
(
30
,
ASIO
DNS
_FETCHCOMP
,
data_
->
remote_rcv
->
getAddress
().
toText
().
c_str
(),
static_cast
<
int
>
(
data_
->
remote_rcv
->
getPort
()));
}
...
...
@@ -335,13 +331,13 @@ IOFetch::stop(Result result) {
// Fetch has been stopped for some other reason. This is
// allowed but as it is unusual it is logged, but with a lower
// debug level than a timeout (which is totally normal).
logger
.
debug
(
1
,
ASIO_FETCHSTOP
,
logger
.
debug
(
1
,
ASIO
DNS
_FETCHSTOP
,
data_
->
remote_snd
->
getAddress
().
toText
().
c_str
(),
static_cast
<
int
>
(
data_
->
remote_snd
->
getPort
()));
break
;
default:
logger
.
error
(
ASIO_UNKRESULT
,
static_cast
<
int
>
(
result
),
logger
.
error
(
ASIO
DNS
_UNKRESULT
,
static_cast
<
int
>
(
result
),
data_
->
remote_snd
->
getAddress
().
toText
().
c_str
(),
static_cast
<
int
>
(
data_
->
remote_snd
->
getPort
()));
}
...
...
@@ -365,10 +361,10 @@ IOFetch::stop(Result result) {
void
IOFetch
::
logIOFailure
(
asio
::
error_code
ec
)
{
// Should only get here with a known error code.
assert
((
data_
->
origin
==
ASIO_OPENSOCK
)
||
(
data_
->
origin
==
ASIO_SENDSOCK
)
||
(
data_
->
origin
==
ASIO_RECVSOCK
)
||
(
data_
->
origin
==
ASIO_UNKORIGIN
));
assert
((
data_
->
origin
==
ASIO
DNS
_OPENSOCK
)
||
(
data_
->
origin
==
ASIO
DNS
_SENDSOCK
)
||
(
data_
->
origin
==
ASIO
DNS
_RECVSOCK
)
||
(
data_
->
origin
==
ASIO
DNS
_UNKORIGIN
));
static
const
char
*
PROTOCOL
[
2
]
=
{
"TCP"
,
"UDP"
};
logger
.
error
(
data_
->
origin
,
...
...
src/lib/asio
link
/qid_gen.cc
→
src/lib/asio
dns
/qid_gen.cc
View file @
7bc6ee51
...
...
@@ -18,16 +18,16 @@
// (and other parts where we need randomness, perhaps another thing
// for a general libutil?)
#include <asio
link
/qid_gen.h>
#include <asio
dns
/qid_gen.h>
#include <sys/time.h>
namespace
{
isc
::
asio
link
::
QidGenerator
qid_generator_instance
;
isc
::
asio
dns
::
QidGenerator
qid_generator_instance
;
}
namespace
isc
{
namespace
asio
link
{
namespace
asio
dns
{
QidGenerator
&
QidGenerator
::
getInstance
()
{
...
...
@@ -52,5 +52,5 @@ QidGenerator::generateQid() {
return
(
vgen_
());
}
}
// namespace asio
link
}
// namespace asio
dns
}
// namespace isc
src/lib/asio
link
/qid_gen.h
→
src/lib/asio
dns
/qid_gen.h
View file @
7bc6ee51
...
...
@@ -28,7 +28,7 @@
namespace
isc
{
namespace
asio
link
{
namespace
asio
dns
{
/// This class generates Qids for outgoing queries
///
...
...
@@ -81,7 +81,7 @@ private:
};
}
// namespace asio
link
}
// namespace asio
dns
}
// namespace isc
#endif // __QID_GEN_H
src/lib/asiodns/tests/Makefile.am
View file @
7bc6ee51
...
...
@@ -21,6 +21,7 @@ run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
run_unittests_SOURCES
+=
io_service_unittest.cc
run_unittests_SOURCES
+=
dns_server_unittest.cc
run_unittests_SOURCES
+=
io_fetch_unittest.cc
run_unittests_SOURCES
+=
qid_gen_unittest.cc
run_unittests_CPPFLAGS
=
$(AM_CPPFLAGS)
$(GTEST_INCLUDES)
...
...
src/lib/asio
link
/tests/qid_gen_unittest.cc
→
src/lib/asio
dns
/tests/qid_gen_unittest.cc
View file @
7bc6ee51
...
...
@@ -32,15 +32,15 @@
#include <gtest/gtest.h>
#include <asio
link
/qid_gen.h>
#include <asio
dns
/qid_gen.h>
#include <dns/message.h>
// Tests the operation of the Qid generator
// Check that getInstance returns a singleton
TEST
(
QidGenerator
,
singleton
)
{
isc
::
asio
link
::
QidGenerator
&
g1
=
isc
::
asiolink
::
QidGenerator
::
getInstance
();
isc
::
asio
link
::
QidGenerator
&
g2
=
isc
::
asiolink
::
QidGenerator
::
getInstance
();
isc
::
asio
dns
::
QidGenerator
&
g1
=
isc
::
asiodns
::
QidGenerator
::
getInstance
();
isc
::
asio
dns
::
QidGenerator
&
g2
=
isc
::
asiodns
::
QidGenerator
::
getInstance
();
EXPECT_TRUE
(
&
g1
==
&
g2
);
}
...
...
@@ -51,7 +51,7 @@ TEST(QidGenerator, generate) {
// test (http://xkcd.com/221/), and check if three consecutive
// generates are not all the same.
isc
::
dns
::
qid_t
one
,
two
,
three
;
isc
::
asio
link
::
QidGenerator
&
gen
=
isc
::
asiolink
::
QidGenerator
::
getInstance
();
isc
::
asio
dns
::
QidGenerator
&
gen
=
isc
::
asiodns
::
QidGenerator
::
getInstance
();
one
=
gen
.
generateQid
();
two
=
gen
.
generateQid
();
three
=
gen
.
generateQid
();
...
...
src/lib/asiolink/Makefile.am
View file @
7bc6ee51
...
...
@@ -14,7 +14,6 @@ CLEANFILES = *.gcno *.gcda
lib_LTLIBRARIES
=
libasiolink.la
libasiolink_la_SOURCES
=
asiolink.h
libasiolink_la_SOURCES
+=
asiolink_utilities.h
libasiolink_la_SOURCES
+=
asiodef.cc asiodef.h
libasiolink_la_SOURCES
+=
dummy_io_cb.h
libasiolink_la_SOURCES
+=
interval_timer.cc interval_timer.h
libasiolink_la_SOURCES
+=
io_address.cc io_address.h
...
...
@@ -22,7 +21,6 @@ libasiolink_la_SOURCES += io_asio_socket.h
libasiolink_la_SOURCES
+=
io_endpoint.cc io_endpoint.h
libasiolink_la_SOURCES
+=
io_error.h
libasiolink_la_SOURCES
+=
io_message.h
libasiolink_la_SOURCES
+=
qid_gen.cc qid_gen.h
libasiolink_la_SOURCES
+=
io_service.h io_service.cc
libasiolink_la_SOURCES
+=
io_socket.h io_socket.cc
libasiolink_la_SOURCES
+=
simple_callback.h
...
...
@@ -31,8 +29,6 @@ libasiolink_la_SOURCES += tcp_socket.h
libasiolink_la_SOURCES
+=
udp_endpoint.h
libasiolink_la_SOURCES
+=
udp_socket.h
EXTRA_DIST
=
asiodef.msg
# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
# B10_CXXFLAGS)
libasiolink_la_CXXFLAGS
=
$(AM_CXXFLAGS)
...
...
src/lib/asiolink/README
View file @
7bc6ee51
...
...
@@ -16,167 +16,7 @@ including:
them in only one place allows us to relax strictness here, while
leaving it in place elsewhere.
Currently, the asiolink library only supports DNS servers (i.e., b10-auth
and b10-resolver). The plan is to make it more generic and allow it to
support other modules as well.
Some of the classes defined here--for example, IOSocket, IOEndpoint,
and IOAddress--are to be used by BIND 10 modules as wrappers around
ASIO-specific classes.
Other classes implement the DNS protocol on behalf of BIND 10 modules.
These DNS server and client routines are written using the "stackless
coroutine" pattern invented by Chris Kohlhoff and described at
http://blog.think-async.com/2010/03/potted-guide-to-stackless-coroutines.html.
This is intended to simplify development a bit, since it allows the
routines to be written in a straightfowrard step-step-step fashion rather
than as a complex chain of separate handler functions.
Coroutine objects (i.e., UDPServer, TCPServer and IOFetch) are objects
with reenterable operator() members. When an instance of one of these
classes is called as a function, it resumes at the position where it left
off. Thus, a UDPServer can issue an asynchronous I/O call and specify
itself as the handler object; when the call completes, the UDPServer
carries on at the same position. As a result, the code can look as
if it were using synchronous, not asynchronous, I/O, providing some of
the benefit of threading but with minimal switching overhead.
So, in simplified form, the behavior of a DNS Server is:
REENTER:
while true:
YIELD packet = read_packet
FORK
if not parent:
break
# This callback informs the caller that a packet has arrived, and
# gives it a chance to update configuration, etc
SimpleCallback(packet)
YIELD answer = DNSLookup(packet, this)
response = DNSAnswer(answer)
YIELD send(response)
At each "YIELD" point, the coroutine initiates an asynchronous operation,
then pauses and turns over control to some other task on the ASIO service
queue. When the operation completes, the coroutine resumes.
DNSLookup, DNSAnswer and SimpleCallback define callback methods
used by a DNS Server to communicate with the module that called it.
They are abstract-only classes whose concrete implementations
are supplied by the calling module.
The DNSLookup callback always runs asynchronously. Concrete
implementations must be sure to call the server's "resume" method when
it is finished.
In an authoritative server, the DNSLookup implementation would examine
the query, look up the answer, then call "resume". (See the diagram
in doc/auth_process.jpg.)
In a recursive server, the DNSLookup impelemtation would initiate a
DNSQuery, which in turn would be responsible for calling the server's
"resume" method. (See the diagram in doc/recursive_process.jpg.)
A DNSQuery object is intended to handle resolution of a query over
the network when the local authoritative data sources or cache are not
sufficient. The plan is that it will make use of subsidiary DNSFetch
calls to get data from particular authoritative servers, and when it has
gotten a complete answer, it calls "resume".
In current form, however, DNSQuery is much simpler; it forwards queries
to a single upstream resolver and passes the answers back to the client.
It is constructed with the address of the forward server. Queries are
initiated with the question to ask the forward server, a buffer into
which to write the answer, and a pointer to the coroutine to be resumed
when the answer has arrived. In simplified form, the DNSQuery routine is:
REENTER:
render the question into a wire-format query packet
YIELD send(query)
YIELD response = read_packet
server->resume
Currently, DNSQuery is only implemented for UDP queries. In future work
it will be necessary to write code to fall back to TCP when circumstances
require it.
Upstream Fetches
================
Upstream fetches (queries by the resolver on behalf of a client) are made
using a slightly-modified version of the pattern described above.
Sockets
-------
First, it will be useful to understand the class hierarchy used in the
fetch logic:
IOSocket
|
IOAsioSocket
|
+-----+-----+
| |
UDPSocket TCPSocket
IOSocket is a wrapper class for a socket and is used by the authoritative
server code. It is an abstract base class, providing little more that the ability to hold the socket and to return the protocol in use.
Built on this is IOAsioSocket, which adds the open, close, asyncSend and
asyncReceive methods. This is a template class, which takes as template
argument the class of the object that will be used as the callback when the
asynchronous operation completes. This object can be of any type, but must
include an operator() method with the signature:
operator()(asio::error_code ec, size_t length)
... the two arguments being the status of the completed I/O operation and
the number of bytes transferred. (In the case of the open method, the second
argument will be zero.)
Finally, the TCPSocket and UDPSocket classes provide the body of the
asynchronous operations.
Fetch Sequence
--------------
The fetch is implemented by the IOFetch class, which takes as argument the
protocol to use. The sequence is:
REENTER:
render the question into a wire-format query packet
open() // Open socket and optionally connect
if (! synchronous) {
YIELD;
}
YIELD asyncSend(query) // Send query
do {
YIELD asyncReceive(response) // Read response
} while (! complete(response))
close() // Drop connection and close socket
server->resume
The open() method opens a socket for use. On TCP, it also makes a
connection to the remote end. So under UDP the operation will complete
immediately, but under TCP it could take a long time. One solution would be
for the open operation to post an event to the I/O queue; then both cases
could be regarded as being equivalent, with the completion being signalled
by the posting of the completion event. However UDP is the most common case
and that would involve extra overhead. So the open() returns a status
indicating whether the operation completed asynchronously. If it did, the
code yields back to the coroutine; if not the yield is bypassed.
The asynchronous send is straightforward, invoking the underlying ASIO
function. (Note that the address/port is supplied to both the open() and
asyncSend() methods - it is used by the TCPSocket in open() and by the
UDPSocket in asyncSend().)
The asyncReceive() method issues an asynchronous read and waits for completion.
The fetch object keeps track of the amount of data received so far and when
the receive completes it calls a method on the socket to determine if the
entire message has been received. (This will always be the case for UDP. On
TCP though, the message is preceded by a count field as several reads may be
required to read all the data.) The fetch loops until all the data is read.
Finally, the socket is closed and the server called to resume operation.
src/lib/asiolink/tests/Makefile.am
View file @
7bc6ee51
...
...
@@ -27,7 +27,6 @@ run_unittests_SOURCES += tcp_endpoint_unittest.cc
run_unittests_SOURCES
+=
tcp_socket_unittest.cc
run_unittests_SOURCES
+=
udp_endpoint_unittest.cc
run_unittests_SOURCES
+=
udp_socket_unittest.cc
run_unittests_SOURCES
+=
qid_gen_unittest.cc
run_unittests_CPPFLAGS
=
$(AM_CPPFLAGS)
$(GTEST_INCLUDES)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment