aboutsummaryrefslogtreecommitdiff
path: root/external/unbound/pythonmod/doc/examples/example4.rst
blob: 6cc4847978264a4e59223afa0c9f74aa7386ccde (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
DNS-based language dictionary
===============================

This example shows how to create a simple language dictionary based on **DNS**
service within 15 minutes. The translation will be performed using TXT resource records.

Key parts
-----------

Initialization
~~~~~~~~~~~~~~~~~~~~~~~
On **init()** module loads dictionary from a text file containing records in ``word [tab] translation`` format.
::

   def init(id, cfg):
      log_info("pythonmod: dict init")
      f = open("examples/dict_data.txt", "r")
      ...

The suitable file can be found at http://slovnik.zcu.cz

DNS query and word lookup
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Let's define the following format od DNS queries: ``word1[.]word2[.] ... wordN[.]{en,cs}[._dict_.cz.]``.
Word lookup is done by simple ``dict`` lookup from broken DNS request.
Query name is divided into a list of labels. This list is accesible as qname_list attribute.
::

   aword = ' '.join(qstate.qinfo.qname_list[0:-4]) #skip last four labels
   adict = qstate.qinfo.qname_list[-4] #get 4th label from the end

   words = [] #list of words
   if (adict == "en") and (aword in en_dict):
      words = en_dict[aword] 

   if (adict == "cs") and (aword in cz_dict):
      words = cz_dict[aword] # CS -> EN

In the first step, we get a string in the form: ``word1[space]word2[space]...word[space]``.
In the second assignment, fourth label from the end is obtained. This label should contains *"cs"* or *"en"*.
This label determines the direction of translation.


Forming of a DNS reply
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

DNS reply is formed only on valid match and added as TXT answer.
::

	msg = DNSMessage(qstate.qinfo.qname_str, RR_TYPE_TXT, RR_CLASS_IN, PKT_AA)

	for w in words:
		msg.answer.append("%s 300 IN TXT \"%s\"" % (qstate.qinfo.qname_str, w.replace("\"", "\\\"")))

	if not msg.set_return_msg(qstate):
		qstate.ext_state[id] = MODULE_ERROR 
		return True

	qstate.return_rcode = RCODE_NOERROR
	qstate.ext_state[id] = MODULE_FINISHED 
	return True

In the first step, a :class:`DNSMessage` instance is created for a given query *(type TXT)*.
The fourth argument specifies the flags *(authoritative answer)*.
In the second step, we append TXT records containing the translation *(on the right side of RR)*.
Then, the response is finished and ``qstate.return_msg`` contains new response.
If no error, the module sets :attr:`module_qstate.return_rcode` and :attr:`module_qstate.ext_state`.

**Steps:**

1. create :class:`DNSMessage` instance
2. append TXT records containing the translation
3. set response to ``qstate.return_msg``

Testing
-------

Run the Unbound server:

``root@localhost>unbound -dv -c ./test-dict.conf``

In case you use own configuration file, don't forget to enable Python module::

	module-config: "validator python iterator"

and use valid script path::

	python-script: "./examples/dict.py"

The translation from english word *"a bar fly"* to Czech can be done by doing:

``>>>dig TXT @127.0.0.1 a.bar.fly.en._dict_.cz``

::	

	; (1 server found)
	;; global options:  printcmd
	;; Got answer:
	;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48691
	;; flags: aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
	
	;; QUESTION SECTION:
	;a.bar.fly.en._dict_.cz.		IN	TXT
	
	;; ANSWER SECTION:
	a.bar.fly.en._dict_.cz.	300	IN	TXT	"barov\253 povale\232"
	
	;; Query time: 5 msec
	;; SERVER: 127.0.0.1#53(127.0.0.1)
	;; WHEN: Mon Jan 01 17:44:18 2009
	;; MSG SIZE  rcvd: 67
	
``>>>dig TXT @127.0.0.1 nic.cs._dict_.cz``
::
	
	; <<>> DiG 9.5.0-P2 <<>> TXT @127.0.0.1 nic.cs._dict_.cz
	; (1 server found)
	;; global options:  printcmd
	;; Got answer:
	;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58710
	;; flags: aa rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 0
	
	;; QUESTION SECTION:
	;nic.cs._dict_.cz.		IN	TXT
	
	;; ANSWER SECTION:
	nic.cs._dict_.cz.	300	IN	TXT	"aught"
	nic.cs._dict_.cz.	300	IN	TXT	"naught"
	nic.cs._dict_.cz.	300	IN	TXT	"nihil"
	nic.cs._dict_.cz.	300	IN	TXT	"nix"
	nic.cs._dict_.cz.	300	IN	TXT	"nothing"
	nic.cs._dict_.cz.	300	IN	TXT	"zilch"
	
	;; Query time: 0 msec
	;; SERVER: 127.0.0.1#53(127.0.0.1)
	;; WHEN: Mon Jan 01 17:45:39 2009
	;; MSG SIZE  rcvd: 143

Proof that the unbound still works as resolver.

``>>>dig A @127.0.0.1 www.nic.cz``
::

	; (1 server found)
	;; global options:  printcmd
	;; Got answer:
	;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19996
	;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 3, ADDITIONAL: 5
	
	;; QUESTION SECTION:
	;www.nic.cz.			IN	A
	
	;; ANSWER SECTION:
	www.nic.cz.		1662	IN	A	217.31.205.50
	
	;; AUTHORITY SECTION:
	...

Complete source code
--------------------

.. literalinclude:: ../../examples/dict.py
   :language: python