import isc.util.cio.socketsession
from isc.notify.notify_out import ZONE_NEW_DATA_READY_CMD
import isc.server_common.tsig_keyring
+from isc.server_common.dns_tcp import DNSTCPContext
from isc.datasrc import DataSourceClient
import select
import errno
# or dropped by the sender, so if such error is detected we treat it
# as an internal error and don't bother to respond.
try:
- if sock.proto == socket.IPPROTO_TCP:
- raise self.InternalError('TCP requests are not yet supported')
self.__request_msg.clear(Message.PARSE)
# specify PRESERVE_ORDER as we need to handle each RR separately.
self.__request_msg.from_wire(req_data, Message.PRESERVE_ORDER)
'''
try:
- sock.sendto(data, dest)
+ if sock.proto == socket.IPPROTO_UDP:
+ sock.sendto(data, dest)
+ else:
+ tcp_ctx = DNSTCPContext(sock)
+ tcp_ctx.send(data)
+ tcp_ctx.close()
except socket.error as ex:
logger.error(DDNS_RESPONSE_SOCKET_ERROR, ClientFormatter(dest), ex)
return False
self.__fileno = fileno
self._sent_data = None
self._sent_addr = None
+ self._close_called = 0 # number of calls to close()
# customizable by tests; if set to True, sendto() will throw after
# recording the parameters.
self._raise_on_send = False
def fileno(self):
return self.__fileno
def getpeername(self):
+ if self.proto == socket.IPPROTO_UDP or \
+ self.proto == socket.IPPROTO_TCP:
+ return TEST_CLIENT4
return "fake_unix_socket"
def accept(self):
return FakeSocket(self.__fileno + 1), '/dummy/path'
self._sent_addr = addr
if self._raise_on_send:
raise socket.error('test socket failure')
+ def send(self, data):
+ if self._sent_data is None:
+ self._sent_data = data
+ else:
+ self._sent_data += data
+ return len(data)
+ def setblocking(self, on):
+ # We only need a faked NO-OP implementation.
+ pass
+ def close(self):
+ self._close_called += 1
def clear(self):
'''Clear internal instrumental data.'''
self._sent_data = None
self.__faked_result)
def check_update_response(self, resp_wire, expected_rcode=Rcode.NOERROR(),
- tsig_ctx=None):
+ tsig_ctx=None, tcp=False):
'''Check if given wire data are valid form of update response.
In this implementation, zone/prerequisite/update sections should be
be TSIG signed and the signature should be verifiable with the context
that has signed the corresponding request.
+ if tcp is True, the wire data are expected to be prepended with
+ a 2-byte length field.
+
'''
+ if tcp:
+ data_len = resp_wire[0] * 256 + resp_wire[1]
+ resp_wire = resp_wire[2:]
+ self.assertEqual(len(resp_wire), data_len)
+
msg = Message(Message.PARSE)
msg.from_wire(resp_wire)
if tsig_ctx is not None:
self.__sock._raise_on_send = True
# handle_request indicates the failure
self.assertFalse(self.server.handle_request((self.__sock, TEST_SERVER6,
- TEST_SERVER4,
+ TEST_CLIENT6,
create_msg())))
# this check ensures sendto() was really attempted.
self.check_update_response(self.__sock._sent_data, Rcode.NOERROR())
def test_tcp_request(self):
- # Right now TCP request is not supported.
+ # A simple case using TCP: all resopnse data are sent out at once.
s = self.__sock
s.proto = socket.IPPROTO_TCP
- self.assertFalse(self.server.handle_request((s, TEST_SERVER6,
- TEST_SERVER4,
- create_msg())))
- self.assertEqual((None, None), (s._sent_data, s._sent_addr))
+ self.assertTrue(self.server.handle_request((s, TEST_SERVER6,
+ TEST_CLIENT6,
+ create_msg())))
+ self.check_update_response(s._sent_data, Rcode.NOERROR(), tcp=True)
+ # In the current implementation, the socket should be closed
+ # immedidately after a successful send.
+ self.assertEqual(1, s._close_called)
def test_request_message(self):
'''Test if the request message stores RRs separately.'''