2010-07-23 103 views
3

我已經實現了一個簡單的DNS服務器。 它只是迴應一個TXT記錄。我將 腳本作爲example.com的NS服務器。 NS服務器是x.y.z.k.dnsPython中的TXT記錄

$ dig demo.example.com @x.y.z.k

; <<>> DiG 9.3.6-P1-RedHat-9.3.6-4.P1.el5_4.1 <<>> demo.example.com @x.y.z.k 
;; global options: printcmd 
;; Got answer: 
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10028 
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 2 

;; QUESTION SECTION: 
;demo.example.com. IN A 

;; ANSWER SECTION: 
demo.example.com. 1000 IN TXT "test message" 

,但是當我從 公共域名服務器之一(例如Sun的4.2 DNS問同樣的問題,這是行不通的:它 當我發出了類似工作正常。 2.1):

$ dig demo.example.com @4.2.2.1(我得到同樣的事情時,我發出$ dig demo.example.com @4.2.2.1 TXT

; <<>> DiG 9.3.6-P1-RedHat-9.3.6-4.P1.el5_4.1 <<>> demo.example.com 
;; global options: printcmd 
;; Got answer: 
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 10905 
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0 

;; QUESTION SECTION: 
;demo.example.com. IN A 

;; Query time: 5837 msec 
;; SERVER: 172.16.1.1#53(172.16.1.1) 
;; WHEN: Tue Jul 20 04:01:27 2010 
;; MSG SIZE rcvd: 75 

你們有什麼想法嗎?有趣的是,如果我將 的響應類型更改爲類似CNAME而不是TXT, 它可以正常工作。

def dns_respond(resp, q, data_type): 
    rrset = dns.rrset.from_text(q.name, 1000, 
      dns.rdataclass.IN, dns.rdatatype.TXT, 'test message') 
    resp.answer.append(rrset) 
    return resp 

def dns_ok(resp, q, data = None, msg = ''): 
    return dns_respond(resp = resp, q = q, data_type = 'OK') 

def dns_error(resp, q): 
    return dns_respond(resp = resp, q = q, data_type = 'Error') 

def requestHandler(address, message): 
    resp = None 
    message_id = ord(message[0]) * 256 + ord(message[1]) 
    logging.debug('msg id = ' + str(message_id)) 
    if message_id in serving_ids: 
     # the request is already taken, drop this message 
     logging.debug('I am already serving this request.') 
     return 
    serving_ids.append(message_id) 
    msg = dns.message.from_wire(message) 
    op = msg.opcode() 
    if op == 0: 
     # standard and inverse query 
     qs = msg.question 
     if len(qs) > 0: 
      q = qs[0] 
      logging.debug('request is ' + str(q)) 
      if q.rdtype == dns.rdatatype.A: 
       resp = std_qry(msg) 
      else: 
       # not implemented 
       #resp = std_qry(msg) 
       resp = make_response(qry=msg, RCODE=4) 
    else: 
     # not implemented 
     resp = make_response(qry=msg, RCODE=4) 

    if resp: 
     s.sendto(resp.to_wire(), address) 

def std_qry(msg): 
    qs = msg.question 
    logging.debug(str(len(qs)) + ' questions.') 

    answers = [] 
    nxdomain = False 
    for q in qs: 
     resp = make_response(qry=msg) 
     logging.critical('Sending...') 
     return dns_ok(resp, q) 

def make_response(qry=None, id=None, RCODE=0): 
    if qry is None and id is None: 
     raise Exception, 'bad use of make_response' 
    if qry is None: 
     resp = dns.message.Message(id) 
     # QR = 1 
     resp.flags |= dns.flags.QR 
     if RCODE != 1: 
      raise Exception, 'bad use of make_response' 
    else: 
     resp = dns.message.make_response(qry) 
    #resp.flags |= dns.flags.AA 
    resp.flags |= dns.flags.RA 
    resp.set_rcode(RCODE) 
    return resp 
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
s.bind(('', 53)) 
logging.debug('binded to UDP port 53.') 
serving_ids = [] 

while True: 
    logging.debug('waiting requests.') 
    message, address = s.recvfrom(1024) 
    logging.debug('serving a request.') 
    requestHandler(address, message) 

回答

3

我可以看到幾個真正的bug會阻止這種工作:

  1. 你返回TXT記錄到A記錄查詢 - 這可能是顯示塞。
  2. 你應該返還AA標誌(權威的答案),而不是RA
  3. 你的頭說,有管理局和附加段兩個答案,但目前還沒有
  4. 你不刪除服務標識名單一旦他們實際上已經擔任

此外,還有一些需要確定一些協議相關的問題:

  1. 你需要能夠返回NS記錄和SOA記錄時問,或者你的代表團可能無法正常
  2. 查詢匹配應該使用全(源IP,源端口,queryID)元組考慮,而不僅僅是(queryID)
  3. 如果(rcode = 3)
  4. 如果問題的名稱與正確的子域相匹配但錯誤的記錄類型,則應該返回NOERROR(rcode = 0)而不是NOTIMPL
  5. 如果問題名稱不是正確域的一部分,您應該返回REFUSED(rcode = 5)