xfr-client.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. #!/usr/bin/env python3
  2. # Copyright (C) 2013 Internet Systems Consortium.
  3. #
  4. # Permission to use, copy, modify, and distribute this software for any
  5. # purpose with or without fee is hereby granted, provided that the above
  6. # copyright notice and this permission notice appear in all copies.
  7. #
  8. # THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
  9. # DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
  10. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
  11. # INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
  12. # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
  13. # FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  14. # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
  15. # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. # A simple XFR client program with some customized behavior.
  17. # This is intended to provide some less common or even invalid client behavior
  18. # for some specific tests on outbound zone transfer.
  19. # It currently only supports AXFR, but can be extended to support IXFR
  20. # as we see the need for it.
  21. #
  22. # For command line usage, run this program with -h option.
  23. from isc.dns import *
  24. import sys
  25. import socket
  26. import struct
  27. import time
  28. from optparse import OptionParser
  29. parser = OptionParser(usage='usage: %prog [options] zone_name')
  30. parser.add_option('-d', '--delay', dest='delay', action='store', default=None,
  31. help='delay (sec) before receiving responses, ' +
  32. 'emulating slow clients')
  33. parser.add_option('-s', '--server', dest='server_addr', action='store',
  34. default='::1',
  35. help="master server's address [default: %default]")
  36. parser.add_option('-p', '--port', dest='server_port', action='store',
  37. default=53,
  38. help="master server's TCP port [default: %default]")
  39. (options, args) = parser.parse_args()
  40. if len(args) == 0:
  41. parser.error('missing argument')
  42. # Parse arguments and options, and creates client socket.
  43. zone_name = Name(args[0])
  44. gai = socket.getaddrinfo(options.server_addr, int(options.server_port), 0,
  45. socket.SOCK_STREAM, socket.IPPROTO_TCP,
  46. socket.AI_NUMERICHOST|socket.AI_NUMERICSERV)
  47. server_family, server_socktype, server_proto, server_sockaddr = \
  48. (gai[0][0], gai[0][1], gai[0][2], gai[0][4])
  49. s = socket.socket(server_family, server_socktype, server_proto)
  50. s.connect(server_sockaddr)
  51. s.settimeout(60) # safety net in case of hangup situation
  52. # Build XFR query.
  53. axfr_qry = Message(Message.RENDER)
  54. axfr_qry.set_rcode(Rcode.NOERROR)
  55. axfr_qry.set_opcode(Opcode.QUERY)
  56. axfr_qry.add_question(Question(zone_name, RRClass.IN, RRType.AXFR))
  57. renderer = MessageRenderer()
  58. axfr_qry.to_wire(renderer)
  59. qry_data = renderer.get_data()
  60. # Send the query
  61. hlen_data = struct.pack('H', socket.htons(len(qry_data)))
  62. s.send(hlen_data)
  63. s.send(qry_data)
  64. # If specified wait for the given period
  65. if options.delay is not None:
  66. time.sleep(int(options.delay))
  67. def get_request_response(s, size):
  68. """A helper function to receive data of specified length from a socket."""
  69. recv_size = 0
  70. data = b''
  71. while recv_size < size:
  72. need_recv_size = size - recv_size
  73. tmp_data = s.recv(need_recv_size)
  74. if len(tmp_data) == 0:
  75. return None
  76. recv_size += len(tmp_data)
  77. data += tmp_data
  78. return data
  79. # Receive responses until the connection is terminated, and dump the
  80. # number of received answer RRs to stdout.
  81. num_rrs = 0
  82. while True:
  83. hlen_data = get_request_response(s, 2)
  84. if hlen_data is None:
  85. break
  86. resp_data = get_request_response(
  87. s, socket.ntohs(struct.unpack('H', hlen_data)[0]))
  88. msg = Message(Message.PARSE)
  89. msg.from_wire(resp_data, Message.PRESERVE_ORDER)
  90. num_rrs += msg.get_rr_count(Message.SECTION_ANSWER)
  91. print(str(num_rrs))