]>
Commit | Line | Data |
---|---|---|
89c4ed63 A |
1 | DNS-based language dictionary |
2 | =============================== | |
3 | ||
4 | This example shows how to create a simple language dictionary based on **DNS** | |
5 | service within 15 minutes. The translation will be performed using TXT resource records. | |
6 | ||
7 | Key parts | |
8 | ----------- | |
9 | ||
10 | Initialization | |
11 | ~~~~~~~~~~~~~~~~~~~~~~~ | |
12 | On **init()** module loads dictionary from a text file containing records in ``word [tab] translation`` format. | |
13 | :: | |
14 | ||
15 | def init(id, cfg): | |
16 | log_info("pythonmod: dict init") | |
17 | f = open("examples/dict_data.txt", "r") | |
18 | ... | |
19 | ||
20 | The suitable file can be found at http://slovnik.zcu.cz | |
21 | ||
22 | DNS query and word lookup | |
23 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
24 | ||
25 | Let's define the following format od DNS queries: ``word1[.]word2[.] ... wordN[.]{en,cs}[._dict_.cz.]``. | |
26 | Word lookup is done by simple ``dict`` lookup from broken DNS request. | |
27 | Query name is divided into a list of labels. This list is accesible as qname_list attribute. | |
28 | :: | |
29 | ||
30 | aword = ' '.join(qstate.qinfo.qname_list[0:-4]) #skip last four labels | |
31 | adict = qstate.qinfo.qname_list[-4] #get 4th label from the end | |
32 | ||
33 | words = [] #list of words | |
34 | if (adict == "en") and (aword in en_dict): | |
35 | words = en_dict[aword] | |
36 | ||
37 | if (adict == "cs") and (aword in cz_dict): | |
38 | words = cz_dict[aword] # CS -> EN | |
39 | ||
40 | In the first step, we get a string in the form: ``word1[space]word2[space]...word[space]``. | |
41 | In the second assignment, fourth label from the end is obtained. This label should contains *"cs"* or *"en"*. | |
42 | This label determines the direction of translation. | |
43 | ||
44 | ||
45 | Forming of a DNS reply | |
46 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
47 | ||
48 | DNS reply is formed only on valid match and added as TXT answer. | |
49 | :: | |
50 | ||
51 | msg = DNSMessage(qstate.qinfo.qname_str, RR_TYPE_TXT, RR_CLASS_IN, PKT_AA) | |
52 | ||
53 | for w in words: | |
54 | msg.answer.append("%s 300 IN TXT \"%s\"" % (qstate.qinfo.qname_str, w.replace("\"", "\\\""))) | |
55 | ||
56 | if not msg.set_return_msg(qstate): | |
57 | qstate.ext_state[id] = MODULE_ERROR | |
58 | return True | |
59 | ||
60 | qstate.return_rcode = RCODE_NOERROR | |
61 | qstate.ext_state[id] = MODULE_FINISHED | |
62 | return True | |
63 | ||
64 | In the first step, a :class:`DNSMessage` instance is created for a given query *(type TXT)*. | |
65 | The fourth argument specifies the flags *(authoritative answer)*. | |
66 | In the second step, we append TXT records containing the translation *(on the right side of RR)*. | |
67 | Then, the response is finished and ``qstate.return_msg`` contains new response. | |
68 | If no error, the module sets :attr:`module_qstate.return_rcode` and :attr:`module_qstate.ext_state`. | |
69 | ||
70 | **Steps:** | |
71 | ||
72 | 1. create :class:`DNSMessage` instance | |
73 | 2. append TXT records containing the translation | |
74 | 3. set response to ``qstate.return_msg`` | |
75 | ||
76 | Testing | |
77 | ------- | |
78 | ||
79 | Run the Unbound server: | |
80 | ||
81 | ``root@localhost>unbound -dv -c ./test-dict.conf`` | |
82 | ||
83 | In case you use own configuration file, don't forget to enable Python module:: | |
84 | ||
85 | module-config: "validator python iterator" | |
86 | ||
87 | and use valid script path:: | |
88 | ||
89 | python-script: "./examples/dict.py" | |
90 | ||
91 | The translation from english word *"a bar fly"* to Czech can be done by doing: | |
92 | ||
93 | ``>>>dig TXT @127.0.0.1 a.bar.fly.en._dict_.cz`` | |
94 | ||
95 | :: | |
96 | ||
97 | ; (1 server found) | |
98 | ;; global options: printcmd | |
99 | ;; Got answer: | |
100 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48691 | |
101 | ;; flags: aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 | |
102 | ||
103 | ;; QUESTION SECTION: | |
104 | ;a.bar.fly.en._dict_.cz. IN TXT | |
105 | ||
106 | ;; ANSWER SECTION: | |
107 | a.bar.fly.en._dict_.cz. 300 IN TXT "barov\253 povale\232" | |
108 | ||
109 | ;; Query time: 5 msec | |
110 | ;; SERVER: 127.0.0.1#53(127.0.0.1) | |
111 | ;; WHEN: Mon Jan 01 17:44:18 2009 | |
112 | ;; MSG SIZE rcvd: 67 | |
113 | ||
114 | ``>>>dig TXT @127.0.0.1 nic.cs._dict_.cz`` | |
115 | :: | |
116 | ||
117 | ; <<>> DiG 9.5.0-P2 <<>> TXT @127.0.0.1 nic.cs._dict_.cz | |
118 | ; (1 server found) | |
119 | ;; global options: printcmd | |
120 | ;; Got answer: | |
121 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58710 | |
122 | ;; flags: aa rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 0 | |
123 | ||
124 | ;; QUESTION SECTION: | |
125 | ;nic.cs._dict_.cz. IN TXT | |
126 | ||
127 | ;; ANSWER SECTION: | |
128 | nic.cs._dict_.cz. 300 IN TXT "aught" | |
129 | nic.cs._dict_.cz. 300 IN TXT "naught" | |
130 | nic.cs._dict_.cz. 300 IN TXT "nihil" | |
131 | nic.cs._dict_.cz. 300 IN TXT "nix" | |
132 | nic.cs._dict_.cz. 300 IN TXT "nothing" | |
133 | nic.cs._dict_.cz. 300 IN TXT "zilch" | |
134 | ||
135 | ;; Query time: 0 msec | |
136 | ;; SERVER: 127.0.0.1#53(127.0.0.1) | |
137 | ;; WHEN: Mon Jan 01 17:45:39 2009 | |
138 | ;; MSG SIZE rcvd: 143 | |
139 | ||
140 | Proof that the unbound still works as resolver. | |
141 | ||
142 | ``>>>dig A @127.0.0.1 www.nic.cz`` | |
143 | :: | |
144 | ||
145 | ; (1 server found) | |
146 | ;; global options: printcmd | |
147 | ;; Got answer: | |
148 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19996 | |
149 | ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 3, ADDITIONAL: 5 | |
150 | ||
151 | ;; QUESTION SECTION: | |
152 | ;www.nic.cz. IN A | |
153 | ||
154 | ;; ANSWER SECTION: | |
155 | www.nic.cz. 1662 IN A 217.31.205.50 | |
156 | ||
157 | ;; AUTHORITY SECTION: | |
158 | ... | |
159 | ||
160 | Complete source code | |
161 | -------------------- | |
162 | ||
163 | .. literalinclude:: ../../examples/dict.py | |
164 | :language: python |