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