summaryrefslogtreecommitdiff
path: root/src/libknot/packet/query.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libknot/packet/query.c')
-rw-r--r--src/libknot/packet/query.c228
1 files changed, 228 insertions, 0 deletions
diff --git a/src/libknot/packet/query.c b/src/libknot/packet/query.c
new file mode 100644
index 0000000..63e902a
--- /dev/null
+++ b/src/libknot/packet/query.c
@@ -0,0 +1,228 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include "packet/query.h"
+
+#include "util/error.h"
+#include "util/wire.h"
+
+/*----------------------------------------------------------------------------*/
+
+int knot_query_rr_to_wire(const knot_rrset_t *rrset, const knot_rdata_t *rdata,
+ uint8_t **wire, uint8_t *endp)
+{
+ /* Store owner. */
+ knot_dname_t *owner = rrset->owner;
+ if (*wire + owner->size > endp) {
+ return KNOT_ENOMEM;
+ }
+ memcpy(*wire, owner->name, owner->size);
+ *wire += owner->size;
+
+ if (*wire + 10 > endp) {
+ return KNOT_ENOMEM;
+ }
+
+ /* Write RR header. */
+ knot_wire_write_u16(*wire, rrset->type); *wire += 2;
+ knot_wire_write_u16(*wire, rrset->rclass); *wire += 2;
+ knot_wire_write_u32(*wire, rrset->ttl); *wire += 4;
+ knot_wire_write_u16(*wire, 0); *wire += 2; /* RDLENGTH reserve. */
+ uint8_t *rdlength_p = *wire - 2;
+ uint16_t rdlength = 0;
+
+ /* Write data. */
+ knot_dname_t *dname = 0;
+ uint16_t *raw_data = 0;
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(rrset->type);
+
+ for (int i = 0; i < rdata->count; ++i) {
+ switch (desc->wireformat[i]) {
+ case KNOT_RDATA_WF_COMPRESSED_DNAME:
+ case KNOT_RDATA_WF_UNCOMPRESSED_DNAME:
+ case KNOT_RDATA_WF_LITERAL_DNAME:
+
+ /* Check space for dname. */
+ dname = knot_rdata_item(rdata, i)->dname;
+ if (*wire + dname->size > endp) {
+ return KNOT_ESPACE;
+ }
+
+ /* Save domain name. */
+ memcpy(*wire, dname->name, dname->size);
+ *wire += dname->size;
+ rdlength += dname->size;
+ break;
+ default:
+ raw_data = knot_rdata_item(rdata, i)->raw_data;
+ if (*wire + raw_data[0] > endp) {
+ return KNOT_ESPACE;
+ }
+
+ /* Copy data. */
+ memcpy(*wire, raw_data + 1, raw_data[0]);
+ *wire += raw_data[0];
+ rdlength += raw_data[0];
+ break;
+
+ }
+ }
+
+ /* Store rdlength. */
+ knot_wire_write_u16(rdlength_p, rdlength);
+
+ return KNOT_EOK;
+}
+/*----------------------------------------------------------------------------*/
+
+int knot_query_dnssec_requested(const knot_packet_t *query)
+{
+ if (query == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return ((knot_edns_get_version(&query->opt_rr) != EDNS_NOT_SUPPORTED)
+ && knot_edns_do(&query->opt_rr));
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_query_nsid_requested(const knot_packet_t *query)
+{
+ if (query == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return ((knot_edns_get_version(&query->opt_rr) != EDNS_NOT_SUPPORTED)
+ && knot_edns_has_option(&query->opt_rr, EDNS_OPTION_NSID));
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_query_edns_supported(const knot_packet_t *query)
+{
+ if (query == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return (knot_edns_get_version(&query->opt_rr) != EDNS_NOT_SUPPORTED);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_query_init(knot_packet_t *query)
+{
+ if (query == NULL) {
+ return KNOT_EBADARG;
+ }
+ // set the qr bit to 0
+ knot_wire_flags_clear_qr(&query->header.flags1);
+
+ uint8_t *pos = query->wireformat;
+ knot_packet_header_to_wire(&query->header, &pos, &query->size);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_query_set_question(knot_packet_t *query,
+ const knot_question_t *question)
+{
+ if (query == NULL || question == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ query->question.qname = question->qname;
+ query->question.qclass = question->qclass;
+ query->question.qtype = question->qtype;
+ query->header.qdcount = 1;
+
+ // convert the Question to wire format right away
+ knot_packet_question_to_wire(query);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_query_set_opcode(knot_packet_t *query, uint8_t opcode)
+{
+ if (query == NULL) {
+ return KNOT_EBADARG;
+ }
+ // set the OPCODE in the structure
+ knot_wire_flags_set_opcode(&query->header.flags1, opcode);
+ // set the OPCODE in the wire format
+ knot_wire_set_opcode(query->wireformat, opcode);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_query_add_rrset_authority(knot_packet_t *query,
+ const knot_rrset_t *rrset)
+{
+ if (query == NULL || rrset == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ if (query->ns_rrsets == query->max_ns_rrsets) {
+ size_t oldsize = query->max_ns_rrsets * sizeof(knot_rrset_t *);
+ ++query->max_ns_rrsets;
+ size_t newsize = query->max_ns_rrsets * sizeof(knot_rrset_t *);
+ const knot_rrset_t ** na = malloc(newsize);
+ if (na == 0) {
+ query->max_ns_rrsets = 0;
+ return KNOT_ENOMEM;
+ } else {
+ memcpy(na, query->authority, oldsize);
+ free(query->authority);
+ query->authority = na;
+ }
+ }
+
+ /* Append to packet. */
+ query->authority[query->ns_rrsets] = rrset;
+
+ /* Write to wire. */
+ uint8_t *startp = query->wireformat + query->size;
+ uint8_t *endp = query->wireformat + query->max_size;
+
+ assert(endp - startp > query->opt_rr.size + query->tsig_size);
+ // reserve space for OPT RR
+ endp -= query->opt_rr.size;
+ /*! \note [TSIG] reserve space for TSIG RR */
+ endp -= query->tsig_size;
+
+ uint8_t *pos = startp;
+
+ const knot_rdata_t *rdata = 0;
+ while ((rdata = knot_rrset_rdata_next(rrset, rdata))) {
+ knot_query_rr_to_wire(rrset, rdata, &pos, endp);
+ }
+
+ size_t written = (pos - startp);
+ query->size += written;
+ ++query->ns_rrsets;
+ ++query->header.nscount;
+
+ return KNOT_EOK;
+}
+