1 /* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2
3 This program is free software: you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation, either version 3 of the License, or
6 (at your option) any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <https://www.gnu.org/licenses/>.
15 */
16
17 #include <strings.h>
18 #include <stdlib.h>
19
20 #include "knot/nameserver/chaos.h"
21 #include "knot/conf/conf.h"
22 #include "libknot/libknot.h"
23
24 #define WISH "Knot DNS developers wish you "
25 #define HOPE "Knot DNS developers hope you "
26
27 static const char *wishes[] = {
28 HOPE "have all your important life questions answered without SERVFAIL.",
29 WISH "many wonderful people in your domain.",
30 WISH "non-empty lymph nodes.",
31 HOPE "resolve the . of your problems.",
32 WISH "long enough TTL.",
33 HOPE "become authoritative master in your domain.",
34 HOPE "always find useful PTR in CHAOS.",
35 "Canonical name is known to both DNS experts and Ubuntu users.",
36 HOPE "never forget both your name and address.",
37 "Don't fix broken CNAME chains with glue!",
38 WISH "no Additional section in your TODO list.",
39 HOPE "won't find surprising news in today's journal.",
40 HOPE "perform rollover often just when playing roulette.",
41 HOPE "get notified before your domain registration expires.",
42 };
43
44 #undef WISH
45 #undef HOPE
46
get_txt_response_string(knot_pkt_t * response)47 static const char *get_txt_response_string(knot_pkt_t *response)
48 {
49 char qname[32];
50 if (knot_dname_to_str(qname, knot_pkt_qname(response), sizeof(qname)) == NULL) {
51 return NULL;
52 }
53
54 const char *response_str = NULL;
55
56 /* Allow hostname.bind. for compatibility. */
57 if (strcasecmp("id.server.", qname) == 0 ||
58 strcasecmp("hostname.bind.", qname) == 0) {
59 conf_val_t val = conf_get(conf(), C_SRV, C_IDENT);
60 if (val.code == KNOT_EOK) {
61 response_str = conf_str(&val); // Can be NULL!
62 } else {
63 response_str = conf()->hostname;
64 }
65 /* Allow version.bind. for compatibility. */
66 } else if (strcasecmp("version.server.", qname) == 0 ||
67 strcasecmp("version.bind.", qname) == 0) {
68 conf_val_t val = conf_get(conf(), C_SRV, C_VERSION);
69 if (val.code == KNOT_EOK) {
70 response_str = conf_str(&val); // Can be NULL!
71 } else {
72 response_str = "Knot DNS " PACKAGE_VERSION;
73 }
74 } else if (strcasecmp("fortune.", qname) == 0) {
75 conf_val_t val = conf_get(conf(), C_SRV, C_VERSION);
76 if (val.code != KNOT_EOK) {
77 uint16_t wishno = knot_wire_get_id(response->wire) %
78 (sizeof(wishes) / sizeof(wishes[0]));
79 response_str = wishes[wishno];
80 }
81 }
82
83 return response_str;
84 }
85
create_txt_rrset(knot_rrset_t * rrset,const knot_dname_t * owner,const char * response_str,knot_mm_t * mm)86 static int create_txt_rrset(knot_rrset_t *rrset, const knot_dname_t *owner,
87 const char *response_str, knot_mm_t *mm)
88 {
89 /* Truncate response to one TXT label. */
90 size_t response_len = strlen(response_str);
91 if (response_len > UINT8_MAX) {
92 response_len = UINT8_MAX;
93 }
94
95 knot_dname_t *rowner = knot_dname_copy(owner, mm);
96 if (rowner == NULL) {
97 return KNOT_ENOMEM;
98 }
99
100 knot_rrset_init(rrset, rowner, KNOT_RRTYPE_TXT, KNOT_CLASS_CH, 0);
101 uint8_t rdata[response_len + 1];
102
103 rdata[0] = response_len;
104 memcpy(&rdata[1], response_str, response_len);
105
106 int ret = knot_rrset_add_rdata(rrset, rdata, response_len + 1, mm);
107 if (ret != KNOT_EOK) {
108 knot_dname_free(rrset->owner, mm);
109 return ret;
110 }
111
112 return KNOT_EOK;
113 }
114
answer_txt(knot_pkt_t * response)115 static int answer_txt(knot_pkt_t *response)
116 {
117 const char *response_str = get_txt_response_string(response);
118 if (response_str == NULL || response_str[0] == '\0') {
119 return KNOT_RCODE_REFUSED;
120 }
121
122 knot_rrset_t rrset;
123 int ret = create_txt_rrset(&rrset, knot_pkt_qname(response),
124 response_str, &response->mm);
125 if (ret != KNOT_EOK) {
126 return KNOT_RCODE_SERVFAIL;
127 }
128
129 int result = knot_pkt_put(response, 0, &rrset, KNOT_PF_FREE);
130 if (result != KNOT_EOK) {
131 knot_rrset_clear(&rrset, &response->mm);
132 return KNOT_RCODE_SERVFAIL;
133 }
134
135 return KNOT_RCODE_NOERROR;
136 }
137
knot_chaos_answer(knot_pkt_t * pkt)138 int knot_chaos_answer(knot_pkt_t *pkt)
139 {
140 if (knot_pkt_qtype(pkt) != KNOT_RRTYPE_TXT) {
141 return KNOT_RCODE_REFUSED;
142 }
143
144 return answer_txt(pkt);
145 }
146