const char* server_path; /* server's unix path, or "", NULL if unused */
const char* server_password; /* server's AUTH password, or "", NULL if unused */
struct timeval timeout; /* timeout for connection setup and commands */
+ int logical_db; /* the redis logical database to use */
};
static redisReply* redis_command(struct module_env*, struct cachedb_env*,
const char*, const uint8_t*, size_t);
+static void
+moddata_clean(struct redis_moddata** moddata) {
+ if(!moddata || !*moddata)
+ return;
+ if((*moddata)->ctxs) {
+ int i;
+ for(i = 0; i < (*moddata)->numctxs; i++) {
+ if((*moddata)->ctxs[i])
+ redisFree((*moddata)->ctxs[i]);
+ }
+ free((*moddata)->ctxs);
+ }
+ free(*moddata);
+ *moddata = NULL;
+}
+
static redisContext*
redis_connect(const struct redis_moddata* moddata)
{
}
freeReplyObject(rep);
}
+ if(moddata->logical_db > 0) {
+ redisReply* rep;
+ rep = redisCommand(ctx, "SELECT %d", moddata->logical_db);
+ if(!rep || rep->type == REDIS_REPLY_ERROR) {
+ log_err("failed to set logical database (%d)",
+ moddata->logical_db);
+ freeReplyObject(rep);
+ goto fail;
+ }
+ freeReplyObject(rep);
+ }
verbose(VERB_OPS, "Connection to Redis established");
return ctx;
- fail:
+fail:
if(ctx)
redisFree(ctx);
return NULL;
moddata = calloc(1, sizeof(struct redis_moddata));
if(!moddata) {
log_err("out of memory");
- return 0;
+ goto fail;
}
moddata->numctxs = env->cfg->num_threads;
moddata->ctxs = calloc(env->cfg->num_threads, sizeof(redisContext*));
if(!moddata->ctxs) {
log_err("out of memory");
- free(moddata);
- return 0;
+ goto fail;
}
/* note: server_host is a shallow reference to configured string.
* we don't have to free it in this module. */
moddata->server_password = env->cfg->redis_server_password;
moddata->timeout.tv_sec = env->cfg->redis_timeout / 1000;
moddata->timeout.tv_usec = (env->cfg->redis_timeout % 1000) * 1000;
- for(i = 0; i < moddata->numctxs; i++)
- moddata->ctxs[i] = redis_connect(moddata);
+ moddata->logical_db = env->cfg->redis_logical_db;
+ for(i = 0; i < moddata->numctxs; i++) {
+ redisContext* ctx = redis_connect(moddata);
+ if(!ctx) {
+ log_err("redis_init: failed to init redis");
+ goto fail;
+ }
+ moddata->ctxs[i] = ctx;
+ }
cachedb_env->backend_data = moddata;
if(env->cfg->redis_expire_records) {
redisReply* rep = NULL;
log_err("redis_init: failed to init redis, the "
"redis-expire-records option requires the SETEX command "
"(redis >= 2.0.0)");
- return 0;
+ goto fail;
}
redis_reply_type = rep->type;
freeReplyObject(rep);
log_err("redis_init: failed to init redis, the "
"redis-expire-records option requires the SETEX command "
"(redis >= 2.0.0)");
- return 0;
+ goto fail;
}
}
-
return 1;
+
+fail:
+ moddata_clean(&moddata);
+ return 0;
}
static void
(void)env;
verbose(VERB_OPS, "Redis deinitialization");
-
- if(!moddata)
- return;
- if(moddata->ctxs) {
- int i;
- for(i = 0; i < moddata->numctxs; i++) {
- if(moddata->ctxs[i])
- redisFree(moddata->ctxs[i]);
- }
- free(moddata->ctxs);
- }
- free(moddata);
+ moddata_clean(&moddata);
}
/*
fi
])
AC_SEARCH_LIBS([protobuf_c_message_pack], [protobuf-c], [],
- AC_MSG_ERROR([The protobuf-c library was not found. Please install protobuf-c!]))
+ AC_MSG_ERROR([The protobuf-c library was not found. Please install the development libraries for protobuf-c!]))
$2
else
$3
}
return -1;
}
- log_crypto_err("dnstap io, could not SSL_write");
+ log_crypto_err_io("dnstap io, could not SSL_write", want);
return -1;
}
return r;
"other side");
return 0;
}
- log_crypto_err("could not SSL_read");
+ log_crypto_err_io("could not SSL_read", want);
verbose(VERB_DETAIL, "dnstap io: output closed by the "
"other side");
return 0;
} else {
unsigned long err = ERR_get_error();
if(!squelch_err_ssl_handshake(err)) {
- log_crypto_err_code("dnstap io, ssl handshake failed",
- err);
+ log_crypto_err_io_code("dnstap io, ssl handshake failed",
+ want, err);
verbose(VERB_OPS, "dnstap io, ssl handshake failed "
"from %s", dtio->ip_str);
}
(data->id?data->id:""));
return 0;
}
- log_crypto_err("could not SSL_read");
+ log_crypto_err_io("could not SSL_read", want);
if(verbosity) log_info("dnstap client stream closed from %s",
(data->id?data->id:""));
return 0;
fd_set_block(data->fd);
if(data->ssl) {
if((r=SSL_write(data->ssl, acceptframe, len)) <= 0) {
- if(SSL_get_error(data->ssl, r) == SSL_ERROR_ZERO_RETURN)
+ int r2;
+ if((r2=SSL_get_error(data->ssl, r)) == SSL_ERROR_ZERO_RETURN)
log_err("SSL_write, peer closed connection");
else
- log_err("could not SSL_write");
+ log_crypto_err_io("could not SSL_write", r2);
fd_set_nonblock(data->fd);
free(acceptframe);
return 0;
if(data->ssl) {
int r;
if((r=SSL_write(data->ssl, finishframe, len)) <= 0) {
- if(SSL_get_error(data->ssl, r) == SSL_ERROR_ZERO_RETURN)
+ int r2;
+ if((r2=SSL_get_error(data->ssl, r)) == SSL_ERROR_ZERO_RETURN)
log_err("SSL_write, peer closed connection");
else
- log_err("could not SSL_write");
+ log_crypto_err_io("could not SSL_write", r2);
fd_set_nonblock(data->fd);
free(finishframe);
return 0;
struct config_strlist* cfg_item = env->cfg->dynlib_file;
struct dynlibmod_env* de = (struct dynlibmod_env*)calloc(1, sizeof(struct dynlibmod_env));
__DYNMOD dynamic_library;
+ int i;
if (!de)
{
log_err("dynlibmod[%d]: malloc failure", dynlib_mod_idx);
env->modinfo[id] = (void*) de;
de->fname = NULL;
- for(int i = dynlib_mod_idx;
+ for(i = dynlib_mod_idx;
i != 0 && cfg_item != NULL;
i--, cfg_item = cfg_item->next) {}
qs = NULL;
plen = strlen(p->str);
- if (dlen >= plen) {
+ if (dlen == plen || (dlen > plen && dname[dlen - plen - 1] == '.' )) {
ds = dname + (dlen - plen);
}
- if (qlen >= plen) {
+ if (qlen == plen || (qlen > plen && qname[qlen - plen - 1] == '.' )) {
qs = qname + (qlen - plen);
}
if ((ds && strncasecmp(p->str, ds, plen) == 0)
return sizeof(*priv) + regional_get_mem(priv->region);
}
-/** remove RR from msgparse RRset, return true if rrset is entirely bad */
-static int
-remove_rr(const char* str, sldns_buffer* pkt, struct rrset_parse* rrset,
- struct rr_parse* prev, struct rr_parse** rr, struct sockaddr_storage* addr, socklen_t addrlen)
-{
- if(verbosity >= VERB_QUERY && rrset->dname_len <= LDNS_MAX_DOMAINLEN && str) {
- uint8_t buf[LDNS_MAX_DOMAINLEN+1];
- dname_pkt_copy(pkt, buf, rrset->dname);
- log_name_addr(VERB_QUERY, str, buf, addr, addrlen);
- }
- if(prev)
- prev->next = (*rr)->next;
- else rrset->rr_first = (*rr)->next;
- if(rrset->rr_last == *rr)
- rrset->rr_last = prev;
- rrset->rr_count --;
- rrset->size -= (*rr)->size;
- /* rr struct still exists, but is unlinked, so that in the for loop
- * the rr->next works fine to continue. */
- return rrset->rr_count == 0;
-}
-
int priv_rrset_bad(struct iter_priv* priv, sldns_buffer* pkt,
struct rrset_parse* rrset)
{
INET_SIZE);
memmove(&addr, &sa, len);
if(priv_lookup_addr(priv, &addr, len)) {
- if(remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, &rr, &addr, len))
+ if(msgparse_rrset_remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, rr, &addr, len))
return 1;
continue;
}
INET6_SIZE);
memmove(&addr, &sa, len);
if(priv_lookup_addr(priv, &addr, len)) {
- if(remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, &rr, &addr, len))
+ if(msgparse_rrset_remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, rr, &addr, len))
return 1;
continue;
}
* @param request: the request that generated the response.
* @param dp: The delegation point that was being queried
* when the response was returned.
+ * @param empty_nodata_found: flag to keep track of empty nodata detection.
* @return the response type (CNAME or ANSWER).
*/
enum response_type response_type_from_server(int rdset,
- struct dns_msg* msg, struct query_info* request, struct delegpt* dp);
+ struct dns_msg* msg, struct query_info* request, struct delegpt* dp,
+ int* empty_nodata_found);
#endif /* ITERATOR_ITER_RESPTYPE_H */
struct regional;
struct module_env;
struct iter_env;
+struct module_qstate;
/**
* Cleanup the passed dns message.
* Used to determine out of bailiwick information.
* @param regional: where to allocate (new) parts of the message.
* @param env: module environment with config settings and cache.
+ * @param qstate: for setting errinf for EDE error messages.
* @param ie: iterator module environment data.
* @return: false if the message is total waste. true if scrubbed with success.
*/
int scrub_message(struct sldns_buffer* pkt, struct msg_parse* msg,
struct query_info* qinfo, uint8_t* zonename, struct regional* regional,
- struct module_env* env, struct iter_env* ie);
+ struct module_env* env, struct module_qstate* qstate,
+ struct iter_env* ie);
#endif /* ITERATOR_ITER_SCRUB_H */
case RPZ_TCP_ONLY_ACTION:
/* basically a passthru here but the tcp-only will be
* honored before the query gets sent. */
- ms->respip_action_info->action = respip_truncate;
+ ms->tcp_required = 1;
ret = NULL;
break;
case RPZ_DROP_ACTION:
case RPZ_TCP_ONLY_ACTION:
/* basically a passthru here but the tcp-only will be
* honored before the query gets sent. */
- ms->respip_action_info->action = respip_truncate;
+ ms->tcp_required = 1;
ret = NULL;
break;
case RPZ_DROP_ACTION:
case RPZ_TCP_ONLY_ACTION:
/* basically a passthru here but the tcp-only will be
* honored before the query gets sent. */
- ms->respip_action_info->action = respip_truncate;
+ ms->tcp_required = 1;
ret = NULL;
break;
case RPZ_DROP_ACTION:
rpz_action_to_string(localzone_type_to_rpz_action(lzt)));
ret = NULL;
}
+ if(r->log)
+ log_rpz_apply("qname", (z?z->name:NULL), NULL,
+ localzone_type_to_rpz_action(lzt),
+ &is->qchase, NULL, ms, r->log_name);
lock_rw_unlock(&z->lock);
lock_rw_unlock(&a->lock);
return ret;
if(want == SSL_ERROR_ZERO_RETURN) {
return NGHTTP2_ERR_EOF;
}
- log_crypto_err("could not SSL_read");
+ log_crypto_err_io("could not SSL_read", want);
return NGHTTP2_ERR_EOF;
}
return r;
if(want == SSL_ERROR_ZERO_RETURN) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
- log_crypto_err("could not SSL_write");
+ log_crypto_err_io("could not SSL_write", want);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
return r;
r = SSL_get_error(ssl, r);
if(r != SSL_ERROR_WANT_READ &&
r != SSL_ERROR_WANT_WRITE) {
- log_crypto_err("could not ssl_handshake");
+ log_crypto_err_io("could not ssl_handshake", r);
exit(1);
}
}
edns.edns_version = EDNS_ADVERTISED_VERSION;
edns.udp_size = EDNS_ADVERTISED_SIZE;
edns.bits = 0;
- if(dnssec)
+ if((dnssec & EDNS_DO))
edns.bits = EDNS_DO;
edns.padding_block_size = 0;
edns.cookie_present = 0;
.B \-d \fIsecs
Delay after the connection before sending query. This tests the timeout
on the other side, eg. if shorter the connection is closed.
+.TP
+.B \-p \fIclient
+Use proxy protocol to send the query. Specify the ipaddr@portnr of the client
+to include in PROXYv2.
+.TP
+.B IXFR=serial
+Pass the type of the query as IXFR=N to send an IXFR query with serial N.
+.TP
+.B NOTIFY[=serial]
+Pass the type of the query as NOTIFY[=N] to send a notify packet. The serial N
+of the new zone can be included.
.SH "EXAMPLES"
.LP
Some examples of use.
printf("-d secs delay after connection before sending query\n");
printf("-s use ssl\n");
printf("-h this help text\n");
+ printf("IXFR=N for the type, sends ixfr query with serial N.\n");
+ printf("NOTIFY[=N] for the type, sends notify. Can set new zone serial N.\n");
exit(1);
}
return fd;
}
+/** Append a SOA record with serial number */
+static void
+write_soa_serial_to_buf(sldns_buffer* buf, struct query_info* qinfo,
+ uint32_t serial)
+{
+ sldns_buffer_set_position(buf, sldns_buffer_limit(buf));
+ sldns_buffer_set_limit(buf, sldns_buffer_capacity(buf));
+ /* Write compressed reference to the query */
+ sldns_buffer_write_u16(buf, PTR_CREATE(LDNS_HEADER_SIZE));
+ sldns_buffer_write_u16(buf, LDNS_RR_TYPE_SOA);
+ sldns_buffer_write_u16(buf, qinfo->qclass);
+ sldns_buffer_write_u32(buf, 3600); /* TTL */
+ sldns_buffer_write_u16(buf, 1+1+4*5); /* rdatalen */
+ sldns_buffer_write_u8(buf, 0); /* primary "." */
+ sldns_buffer_write_u8(buf, 0); /* email "." */
+ sldns_buffer_write_u32(buf, serial); /* serial */
+ sldns_buffer_write_u32(buf, 0); /* refresh */
+ sldns_buffer_write_u32(buf, 0); /* retry */
+ sldns_buffer_write_u32(buf, 0); /* expire */
+ sldns_buffer_write_u32(buf, 0); /* minimum */
+ sldns_buffer_flip(buf);
+}
+
/** write a query over the TCP fd */
static void
write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id,
{
struct query_info qinfo;
size_t proxy_buf_limit = sldns_buffer_limit(proxy_buf);
+ int have_serial = 0, is_notify = 0;
+ uint32_t serial = 0;
/* qname */
qinfo.qname = sldns_str2wire_dname(strname, &qinfo.qname_len);
if(!qinfo.qname) {
exit(1);
}
- /* qtype and qclass */
- qinfo.qtype = sldns_get_rr_type_by_name(strtype);
- if(qinfo.qtype == 0 && strcmp(strtype, "TYPE0") != 0) {
- printf("cannot parse query type: '%s'\n", strtype);
- exit(1);
+ /* qtype */
+ if(strncasecmp(strtype, "IXFR=", 5) == 0) {
+ serial = (uint32_t)atoi(strtype+5);
+ have_serial = 1;
+ qinfo.qtype = LDNS_RR_TYPE_IXFR;
+ } else if(strcasecmp(strtype, "NOTIFY") == 0) {
+ is_notify = 1;
+ qinfo.qtype = LDNS_RR_TYPE_SOA;
+ } else if(strncasecmp(strtype, "NOTIFY=", 7) == 0) {
+ serial = (uint32_t)atoi(strtype+7);
+ have_serial = 1;
+ is_notify = 1;
+ qinfo.qtype = LDNS_RR_TYPE_SOA;
+ } else {
+ qinfo.qtype = sldns_get_rr_type_by_name(strtype);
+ if(qinfo.qtype == 0 && strcmp(strtype, "TYPE0") != 0) {
+ printf("cannot parse query type: '%s'\n", strtype);
+ exit(1);
+ }
}
+ /* qclass */
qinfo.qclass = sldns_get_rr_class_by_name(strclass);
if(qinfo.qclass == 0 && strcmp(strclass, "CLASS0") != 0) {
printf("cannot parse query class: '%s'\n", strclass);
sldns_buffer_write_u16_at(buf, 0, id);
sldns_buffer_write_u16_at(buf, 2, BIT_RD);
+ if(have_serial && qinfo.qtype == LDNS_RR_TYPE_IXFR) {
+ /* Attach serial to SOA record in the authority section. */
+ write_soa_serial_to_buf(buf, &qinfo, serial);
+ LDNS_NSCOUNT_SET(sldns_buffer_begin(buf), 1);
+ }
+ if(is_notify) {
+ LDNS_OPCODE_SET(sldns_buffer_begin(buf), LDNS_PACKET_NOTIFY);
+ LDNS_RD_CLR(sldns_buffer_begin(buf));
+ LDNS_AA_SET(sldns_buffer_begin(buf));
+ if(have_serial) {
+ write_soa_serial_to_buf(buf, &qinfo, serial);
+ LDNS_ANCOUNT_SET(sldns_buffer_begin(buf), 1);
+ }
+ }
+
if(1) {
/* add EDNS DO */
struct edns_data edns;
sldns_buffer* proxy_buf)
{
struct sockaddr_storage pp2_addr;
+ size_t bytes_written;
socklen_t pp2_addrlen = 0;
memset(&pp2_addr, 0, sizeof(pp2_addr));
if(*pp2_client == 0) return 0;
exit(1);
}
sldns_buffer_clear(proxy_buf);
- pp2_write_to_buf(proxy_buf, &pp2_addr, !udp);
+ bytes_written = pp2_write_to_buf(sldns_buffer_begin(proxy_buf),
+ sldns_buffer_remaining(proxy_buf), &pp2_addr, !udp);
+ sldns_buffer_set_position(proxy_buf, bytes_written);
sldns_buffer_flip(proxy_buf);
return 1;
}
r = SSL_get_error(ssl, r);
if(r != SSL_ERROR_WANT_READ &&
r != SSL_ERROR_WANT_WRITE) {
- log_crypto_err("could not ssl_handshake");
+ log_crypto_err_io("could not ssl_handshake", r);
exit(1);
}
}
break;
case 'p':
pp2_client = optarg;
+ pp_init(&sldns_write_uint16,
+ &sldns_write_uint32);
break;
case 'a':
onarrival = 1;
"out.example.com. 3600 IN CNAME www.example.com.\n"
"plan.example.com. 3600 IN CNAME nonexist.example.com.\n"
"redir.example.com. 3600 IN DNAME redir.example.org.\n"
+"redir2.example.com. 3600 IN DNAME redir2.example.org.\n"
+"obscured.redir2.example.com. 3600 IN A 10.0.0.12\n"
+"under2.redir2.example.com. 3600 IN DNAME redir3.example.net.\n"
+"doubleobscured.under2.redir2.example.com. 3600 IN A 10.0.0.13\n"
"sub.example.com. 3600 IN NS ns1.sub.example.com.\n"
"sub.example.com. 3600 IN NS ns2.sub.example.com.\n"
"ns1.sub.example.com. 3600 IN A 10.0.0.6\n"
"ns2.sub.example.com. 3600 IN AAAA 2001::7\n"
+"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n"
+"obscured.sub2.example.com. 3600 IN A 10.0.0.10\n"
+"under.sub2.example.com. 3600 IN NS ns.under.sub2.example.com.\n"
+"doubleobscured.under.sub2.example.com. 3600 IN A 10.0.0.11\n"
"*.wild.example.com. 3600 IN A 10.0.0.8\n"
"*.wild2.example.com. 3600 IN CNAME www.example.com.\n"
"*.wild3.example.com. 3600 IN A 10.0.0.8\n"
"foo.abc.redir.example.com. 0 IN CNAME foo.abc.redir.example.org.\n"
},
+ { "example.com", "redir2.example.com. DNAME", "",
+";flags QR AA rcode NOERROR\n"
+";answer section\n"
+"redir2.example.com. 3600 IN DNAME redir2.example.org.\n"
+ },
+
+ { "example.com", "abc.redir2.example.com. A", "",
+";flags QR AA rcode NOERROR\n"
+";answer section\n"
+"redir2.example.com. 3600 IN DNAME redir2.example.org.\n"
+"abc.redir2.example.com. 0 IN CNAME abc.redir2.example.org.\n"
+ },
+
+ { "example.com", "obscured.redir2.example.com. A", "",
+";flags QR AA rcode NOERROR\n"
+";answer section\n"
+"redir2.example.com. 3600 IN DNAME redir2.example.org.\n"
+"obscured.redir2.example.com. 0 IN CNAME obscured.redir2.example.org.\n"
+ },
+
+ { "example.com", "under2.redir2.example.com. A", "",
+";flags QR AA rcode NOERROR\n"
+";answer section\n"
+"redir2.example.com. 3600 IN DNAME redir2.example.org.\n"
+"under2.redir2.example.com. 0 IN CNAME under2.redir2.example.org.\n"
+ },
+
+ { "example.com", "doubleobscured.under2.redir2.example.com. A", "",
+";flags QR AA rcode NOERROR\n"
+";answer section\n"
+"redir2.example.com. 3600 IN DNAME redir2.example.org.\n"
+"doubleobscured.under2.redir2.example.com. 0 IN CNAME doubleobscured.under2.redir2.example.org.\n"
+ },
+
+ { "example.com", "foo.doubleobscured.under2.redir2.example.com. A", "",
+";flags QR AA rcode NOERROR\n"
+";answer section\n"
+"redir2.example.com. 3600 IN DNAME redir2.example.org.\n"
+"foo.doubleobscured.under2.redir2.example.com. 0 IN CNAME foo.doubleobscured.under2.redir2.example.org.\n"
+ },
+
+ { "example.com", "foo.under2.redir2.example.com. A", "",
+";flags QR AA rcode NOERROR\n"
+";answer section\n"
+"redir2.example.com. 3600 IN DNAME redir2.example.org.\n"
+"foo.under2.redir2.example.com. 0 IN CNAME foo.under2.redir2.example.org.\n"
+ },
+
{ "example.com", "sub.example.com. NS", "",
";flags QR rcode NOERROR\n"
";authority section\n"
"ns2.sub.example.com. 3600 IN AAAA 2001::7\n"
},
+ { "example.com", "sub2.example.com. A", "",
+";flags QR rcode NOERROR\n"
+";authority section\n"
+"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n"
+";additional section\n"
+"ns1.sub.example.com. 3600 IN A 10.0.0.6\n"
+ },
+
+ { "example.com", "sub2.example.com. NS", "",
+";flags QR rcode NOERROR\n"
+";authority section\n"
+"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n"
+";additional section\n"
+"ns1.sub.example.com. 3600 IN A 10.0.0.6\n"
+ },
+
+ { "example.com", "obscured.sub2.example.com. A", "",
+";flags QR rcode NOERROR\n"
+";authority section\n"
+"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n"
+";additional section\n"
+"ns1.sub.example.com. 3600 IN A 10.0.0.6\n"
+ },
+
+ { "example.com", "abc.obscured.sub2.example.com. A", "",
+";flags QR rcode NOERROR\n"
+";authority section\n"
+"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n"
+";additional section\n"
+"ns1.sub.example.com. 3600 IN A 10.0.0.6\n"
+ },
+
+ { "example.com", "under.sub2.example.com. A", "",
+";flags QR rcode NOERROR\n"
+";authority section\n"
+"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n"
+";additional section\n"
+"ns1.sub.example.com. 3600 IN A 10.0.0.6\n"
+ },
+
+ { "example.com", "under.sub2.example.com. NS", "",
+";flags QR rcode NOERROR\n"
+";authority section\n"
+"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n"
+";additional section\n"
+"ns1.sub.example.com. 3600 IN A 10.0.0.6\n"
+ },
+
+ { "example.com", "abc.under.sub2.example.com. A", "",
+";flags QR rcode NOERROR\n"
+";authority section\n"
+"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n"
+";additional section\n"
+"ns1.sub.example.com. 3600 IN A 10.0.0.6\n"
+ },
+
+ { "example.com", "doubleobscured.under.sub2.example.com. A", "",
+";flags QR rcode NOERROR\n"
+";authority section\n"
+"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n"
+";additional section\n"
+"ns1.sub.example.com. 3600 IN A 10.0.0.6\n"
+ },
+
+ { "example.com", "abc.doubleobscured.under.sub2.example.com. A", "",
+";flags QR rcode NOERROR\n"
+";authority section\n"
+"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n"
+";additional section\n"
+"ns1.sub.example.com. 3600 IN A 10.0.0.6\n"
+ },
+
{ "example.com", "wild.example.com. A", "",
";flags QR AA rcode NOERROR\n"
";authority section\n"
*
* This file contains PROXY protocol functions.
*/
-#include "config.h"
-#include "util/log.h"
#include "util/proxy_protocol.h"
-int
-pp2_write_to_buf(struct sldns_buffer* buf, struct sockaddr_storage* src,
+/**
+ * Internal struct initialized with function pointers for writing uint16 and
+ * uint32.
+ */
+struct proxy_protocol_data {
+ void (*write_uint16)(void* buf, uint16_t data);
+ void (*write_uint32)(void* buf, uint32_t data);
+};
+struct proxy_protocol_data pp_data;
+
+/**
+ * Internal lookup table; could be further generic like sldns_lookup_table
+ * for all the future generic stuff.
+ */
+struct proxy_protocol_lookup_table {
+ int id;
+ const char *text;
+};
+
+/**
+ * Internal parsing error text; could be exposed with pp_lookup_error.
+ */
+static struct proxy_protocol_lookup_table pp_parse_errors_data[] = {
+ { PP_PARSE_NOERROR, "no parse error" },
+ { PP_PARSE_SIZE, "not enough space for header" },
+ { PP_PARSE_WRONG_HEADERv2, "could not match PROXYv2 header" },
+ { PP_PARSE_UNKNOWN_CMD, "unknown command" },
+ { PP_PARSE_UNKNOWN_FAM_PROT, "unknown family and protocol" },
+};
+
+void
+pp_init(void (*write_uint16)(void* buf, uint16_t data),
+ void (*write_uint32)(void* buf, uint32_t data)) {
+ pp_data.write_uint16 = write_uint16;
+ pp_data.write_uint32 = write_uint32;
+}
+
+const char*
+pp_lookup_error(enum pp_parse_errors error) {
+ return pp_parse_errors_data[error].text;
+}
+
+size_t
+pp2_write_to_buf(uint8_t* buf, size_t buflen,
+#ifdef INET6
+ struct sockaddr_storage* src,
+#else
+ struct sockaddr_in* src,
+#endif
int stream)
{
int af;
+ size_t expected_size;
if(!src) return 0;
af = (int)((struct sockaddr_in*)src)->sin_family;
- if(sldns_buffer_remaining(buf) <
- PP2_HEADER_SIZE + (af==AF_INET?12:36)) {
+ expected_size = PP2_HEADER_SIZE + (af==AF_INET?12:36);
+ if(buflen < expected_size) {
return 0;
}
/* sig */
- sldns_buffer_write(buf, PP2_SIG, PP2_SIG_LEN);
+ memcpy(buf, PP2_SIG, PP2_SIG_LEN);
+ buf += PP2_SIG_LEN;
/* version and command */
- sldns_buffer_write_u8(buf, (PP2_VERSION << 4) | PP2_CMD_PROXY);
- if(af==AF_INET) {
+ *buf = (PP2_VERSION << 4) | PP2_CMD_PROXY;
+ buf++;
+ switch(af) {
+ case AF_INET:
/* family and protocol */
- sldns_buffer_write_u8(buf,
- (PP2_AF_INET<<4) |
- (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM));
+ *buf = (PP2_AF_INET<<4) |
+ (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM);
+ buf++;
/* length */
- sldns_buffer_write_u16(buf, 12);
+ (*pp_data.write_uint16)(buf, 12);
+ buf += 2;
/* src addr */
- sldns_buffer_write(buf,
+ memcpy(buf,
&((struct sockaddr_in*)src)->sin_addr.s_addr, 4);
+ buf += 4;
/* dst addr */
- sldns_buffer_write_u32(buf, 0);
+ (*pp_data.write_uint32)(buf, 0);
+ buf += 4;
/* src port */
- sldns_buffer_write(buf,
+ memcpy(buf,
&((struct sockaddr_in*)src)->sin_port, 2);
+ buf += 2;
+ /* dst addr */
/* dst port */
- sldns_buffer_write_u16(buf, 0);
- } else {
+ (*pp_data.write_uint16)(buf, 12);
+ break;
+#ifdef INET6
+ case AF_INET6:
/* family and protocol */
- sldns_buffer_write_u8(buf,
- (PP2_AF_INET6<<4) |
- (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM));
+ *buf = (PP2_AF_INET6<<4) |
+ (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM);
+ buf++;
/* length */
- sldns_buffer_write_u16(buf, 36);
+ (*pp_data.write_uint16)(buf, 36);
+ buf += 2;
/* src addr */
- sldns_buffer_write(buf,
+ memcpy(buf,
&((struct sockaddr_in6*)src)->sin6_addr, 16);
+ buf += 16;
/* dst addr */
- sldns_buffer_set_at(buf,
- sldns_buffer_position(buf), 0, 16);
- sldns_buffer_skip(buf, 16);
+ memset(buf, 0, 16);
+ buf += 16;
/* src port */
- sldns_buffer_write(buf,
- &((struct sockaddr_in6*)src)->sin6_port, 2);
+ memcpy(buf, &((struct sockaddr_in6*)src)->sin6_port, 2);
+ buf += 2;
/* dst port */
- sldns_buffer_write_u16(buf, 0);
+ (*pp_data.write_uint16)(buf, 0);
+ break;
+#endif /* INET6 */
+ case AF_UNIX:
+ /* fallthrough */
+ default:
+ return 0;
}
- return 1;
+ return expected_size;
}
-struct pp2_header*
-pp2_read_header(struct sldns_buffer* buf)
+int
+pp2_read_header(uint8_t* buf, size_t buflen)
{
size_t size;
- struct pp2_header* header = (struct pp2_header*)sldns_buffer_begin(buf);
+ struct pp2_header* header = (struct pp2_header*)buf;
/* Try to fail all the unsupported cases first. */
- if(sldns_buffer_remaining(buf) < PP2_HEADER_SIZE) {
- log_err("proxy_protocol: not enough space for header");
- return NULL;
+ if(buflen < PP2_HEADER_SIZE) {
+ return PP_PARSE_SIZE;
}
/* Check for PROXYv2 header */
if(memcmp(header, PP2_SIG, PP2_SIG_LEN) != 0 ||
((header->ver_cmd & 0xF0)>>4) != PP2_VERSION) {
- log_err("proxy_protocol: could not match PROXYv2 header");
- return NULL;
+ return PP_PARSE_WRONG_HEADERv2;
}
/* Check the length */
size = PP2_HEADER_SIZE + ntohs(header->len);
- if(sldns_buffer_remaining(buf) < size) {
- log_err("proxy_protocol: not enough space for header");
- return NULL;
+ if(buflen < size) {
+ return PP_PARSE_SIZE;
}
/* Check for supported commands */
if((header->ver_cmd & 0xF) != PP2_CMD_LOCAL &&
(header->ver_cmd & 0xF) != PP2_CMD_PROXY) {
- log_err("proxy_protocol: unsupported command");
- return NULL;
+ return PP_PARSE_UNKNOWN_CMD;
}
/* Check for supported family and protocol */
- if(header->fam_prot != 0x00 /* AF_UNSPEC|UNSPEC */ &&
- header->fam_prot != 0x11 /* AF_INET|STREAM */ &&
- header->fam_prot != 0x12 /* AF_INET|DGRAM */ &&
- header->fam_prot != 0x21 /* AF_INET6|STREAM */ &&
- header->fam_prot != 0x22 /* AF_INET6|DGRAM */) {
- log_err("proxy_protocol: unsupported family and protocol");
- return NULL;
+ if(header->fam_prot != PP2_UNSPEC_UNSPEC &&
+ header->fam_prot != PP2_INET_STREAM &&
+ header->fam_prot != PP2_INET_DGRAM &&
+ header->fam_prot != PP2_INET6_STREAM &&
+ header->fam_prot != PP2_INET6_DGRAM &&
+ header->fam_prot != PP2_UNIX_STREAM &&
+ header->fam_prot != PP2_UNIX_DGRAM) {
+ return PP_PARSE_UNKNOWN_FAM_PROT;
}
/* We have a correct header */
- return header;
+ return PP_PARSE_NOERROR;
}
#ifndef PROXY_PROTOCOL_H
#define PROXY_PROTOCOL_H
-#include "sldns/sbuffer.h"
+#include "config.h"
/** PROXYv2 minimum header size */
#define PP2_HEADER_SIZE 16
#define PP2_SIG "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"
#define PP2_SIG_LEN 12
-/** PROXYv2 version */
+/** PROXYv2 version (protocol value) */
#define PP2_VERSION 0x2
/**
- * PROXYv2 command.
+ * PROXYv2 command (protocol value).
*/
enum pp2_command {
PP2_CMD_LOCAL = 0x0,
};
/**
- * PROXYv2 address family.
+ * PROXYv2 address family (protocol value).
*/
enum pp2_af {
PP2_AF_UNSPEC = 0x0,
};
/**
- * PROXYv2 protocol.
+ * PROXYv2 protocol (protocol value).
*/
enum pp2_protocol {
PP2_PROT_UNSPEC = 0x0,
PP2_PROT_DGRAM = 0x2
};
+/**
+ * Expected combinations of address family and protocol values used in checks.
+ */
+enum pp2_af_protocol_combination {
+ PP2_UNSPEC_UNSPEC = (PP2_AF_UNSPEC<<4)|PP2_PROT_UNSPEC,
+ PP2_INET_STREAM = (PP2_AF_INET<<4)|PP2_PROT_STREAM,
+ PP2_INET_DGRAM = (PP2_AF_INET<<4)|PP2_PROT_DGRAM,
+ PP2_INET6_STREAM = (PP2_AF_INET6<<4)|PP2_PROT_STREAM,
+ PP2_INET6_DGRAM = (PP2_AF_INET6<<4)|PP2_PROT_DGRAM,
+ PP2_UNIX_STREAM = (PP2_AF_UNIX<<4)|PP2_PROT_STREAM,
+ PP2_UNIX_DGRAM = (PP2_AF_UNIX<<4)|PP2_PROT_DGRAM
+};
+
/**
* PROXYv2 header.
*/
} addr;
};
+/**
+ * PROXY parse errors.
+ */
+enum pp_parse_errors {
+ PP_PARSE_NOERROR = 0,
+ PP_PARSE_SIZE,
+ PP_PARSE_WRONG_HEADERv2,
+ PP_PARSE_UNKNOWN_CMD,
+ PP_PARSE_UNKNOWN_FAM_PROT,
+};
+
+/**
+ * Initialize the internal proxy structure.
+ * @param write_uint16: pointer to a function that can write uint16.
+ * @param write_uint32: pointer to a function that can write uint32.
+ */
+void pp_init(void (*write_uint16)(void* buf, uint16_t data),
+ void (*write_uint32)(void* buf, uint32_t data));
+
+/**
+ * Lookup the parsing error description.
+ * @param error: parsing error from pp2_read_header.
+ * @return the description.
+ */
+const char* pp_lookup_error(enum pp_parse_errors error);
+
/**
* Write a PROXYv2 header at the current position of the buffer.
- * @param buf: the buffer to write to.
+ * @param buf: pointer to the buffer to write data to.
+ * @param buflen: available size on the buffer.
* @param src: the source address.
* @param stream: if the protocol is stream or datagram.
* @return 1 on success, 0 on failure.
*/
-int pp2_write_to_buf(struct sldns_buffer* buf, struct sockaddr_storage* src,
+size_t pp2_write_to_buf(uint8_t* buf, size_t buflen,
+#ifdef INET6
+ struct sockaddr_storage* src,
+#else
+ struct sockaddr_in* src,
+#endif
int stream);
/**
* Read a PROXYv2 header from the current position of the buffer.
* It does initial validation and returns a pointer to the buffer position on
* success.
- * @param buf: the buffer to read from.
- * @return the pointer to the buffer position on success, NULL on error.
+ * @param buf: pointer to the buffer data to read from.
+ * @param buflen: available size on the buffer.
+ * @return parsing error, 0 on success.
*/
-struct pp2_header* pp2_read_header(struct sldns_buffer* buf);
+int pp2_read_header(uint8_t* buf, size_t buflen);
#endif /* PROXY_PROTOCOL_H */
* This file contains functions for RFC 1982 serial number arithmetic.
*/
#include "config.h"
+#include "util/rfc_1982.h"
int
compare_1982(uint32_t a, uint32_t b)
*/
#include "config.h"
+/** EDIT
+ * prevent warning from -Wmissing-prototypes
+ */
+#include "util/siphash.h"
+
/* default: SipHash-2-4 */
#define cROUNDS 2
#define dROUNDS 4