diff --git a/src/Makefile.in b/src/Makefile.in index a97b379d..983f4fc8 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -329,7 +329,7 @@ convert.lo convert.o: $(srcdir)/convert.c \ $(srcdir)/util/orig-headers/locks.h $(srcdir)/util/auxiliary/util/log.h $(srcdir)/debug.h $(srcdir)/rr-iter.h \ $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h $(srcdir)/anchor.h $(srcdir)/gldns/wire2str.h \ $(srcdir)/gldns/str2wire.h $(srcdir)/gldns/rrdef.h $(srcdir)/gldns/parseutil.h $(srcdir)/const-info.h $(srcdir)/dict.h \ - $(srcdir)/list.h $(srcdir)/jsmn/jsmn.h $(stubbysrcdir)/src/yaml/convert_yaml_to_json.h $(srcdir)/convert.h + $(srcdir)/list.h $(srcdir)/jsmn/jsmn.h $(srcdir)/convert.h dict.lo dict.o: $(srcdir)/dict.c \ config.h \ $(srcdir)/types-internal.h \ diff --git a/src/convert.c b/src/convert.c index 03a27af2..82adf241 100644 --- a/src/convert.c +++ b/src/convert.c @@ -56,7 +56,9 @@ #include "dict.h" #include "list.h" #include "jsmn/jsmn.h" +#ifdef USE_YAML_CONFIG #include "yaml/convert_yaml_to_json.h" +#endif #include "convert.h" #include "debug.h" diff --git a/src/dnssec.c b/src/dnssec.c index d4c6375b..3bf92d58 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -1755,6 +1755,26 @@ static int dnskey_signed_rrset(struct mem_funcs *mf, time_t now, uint32_t skew, return 0; } +/* Returns whether a dnskey for keyset signed a non wildcard rrset. */ +static int a_key_signed_rrset_no_wc(struct mem_funcs *mf, time_t now, + uint32_t skew, _getdns_rrset *keyset, _getdns_rrset *rrset) +{ + _getdns_rrtype_iter dnskey_spc, *dnskey; + const uint8_t *nc_name; + int keytag; + + assert(keyset->rr_type == GETDNS_RRTYPE_DNSKEY); + + for ( dnskey = _getdns_rrtype_iter_init(&dnskey_spc, keyset) + ; dnskey ; dnskey = _getdns_rrtype_iter_next(dnskey) ) { + + if ((keytag = dnskey_signed_rrset(mf, now, skew, + dnskey, rrset, &nc_name)) && !nc_name) + return keytag; + } + return 0; +} + static int find_nsec_covering_name( struct mem_funcs *mf, time_t now, uint32_t skew, _getdns_rrset *dnskey, _getdns_rrset *rrset, const uint8_t *name, int *opt_out); @@ -2110,7 +2130,8 @@ static int find_nsec_covering_name( && (bitmap = _getdns_rdf_iter_init_at( &bitmap_spc, &nsec_rr->rr_i, 5)) - && (keytag = a_key_signed_rrset(mf, now, skew, dnskey, n)) + && (keytag = a_key_signed_rrset_no_wc( + mf, now, skew, dnskey, n)) && ( keytag & NSEC3_ITERATION_COUNT_HIGH || ( nsec3_covers_name(n, name, opt_out) @@ -2174,7 +2195,8 @@ static int find_nsec_covering_name( ) ) - && (keytag = a_key_signed_rrset(mf,now,skew, dnskey, n))) { + && (keytag = a_key_signed_rrset_no_wc( + mf, now, skew, dnskey, n))) { debug_sec_print_rrset("NSEC: ", n); debug_sec_print_dname("covered: ", name); @@ -2297,7 +2319,8 @@ static int key_proves_nonexistance( || bitmap_has_type(bitmap, GETDNS_RRTYPE_SOA)) /* And a valid signature please */ - && (keytag = a_key_signed_rrset(mf,now,skew,keyset,&nsec_rrset))) { + && (keytag = a_key_signed_rrset_no_wc( + mf, now, skew, keyset, &nsec_rrset))) { debug_sec_print_rrset("NSEC NODATA proof for: ", rrset); return keytag; @@ -2353,7 +2376,7 @@ static int key_proves_nonexistance( ) /* And a valid signature please (as always) */ - || !(keytag = a_key_signed_rrset( + || !(keytag = a_key_signed_rrset_no_wc( mf, now, skew, keyset, cover))) continue; @@ -2437,7 +2460,8 @@ static int key_proves_nonexistance( || bitmap_has_type(bitmap, GETDNS_RRTYPE_SOA)) /* It must have a valid signature */ - && (keytag = a_key_signed_rrset(mf, now, skew, keyset, ce)) + && (keytag = a_key_signed_rrset_no_wc( + mf, now, skew, keyset, ce)) /* The qname must match the NSEC3 */ && ( keytag & NSEC3_ITERATION_COUNT_HIGH @@ -2493,7 +2517,7 @@ static int key_proves_nonexistance( && !bitmap_has_type(bitmap, GETDNS_RRTYPE_SOA) ) - || !(keytag = a_key_signed_rrset( + || !(keytag = a_key_signed_rrset_no_wc( mf, now, skew, keyset, ce)) || ( !(keytag & NSEC3_ITERATION_COUNT_HIGH) && !nsec3_matches_name(ce, ce_name))) @@ -2545,7 +2569,7 @@ static int chain_node_get_trusted_keys( } else if (ta->rr_type == GETDNS_RRTYPE_DNSKEY) { /* ta is KSK */ - if ((keytag = a_key_signed_rrset( + if ((keytag = a_key_signed_rrset_no_wc( mf, now, skew, ta, &node->dnskey))) { *keys = &node->dnskey; node->dnskey_signer = keytag; @@ -2563,7 +2587,8 @@ static int chain_node_get_trusted_keys( return GETDNS_DNSSEC_INSECURE; } - if ((keytag = a_key_signed_rrset(mf,now,skew,ta,&node->ds))) { + if ((keytag = a_key_signed_rrset_no_wc( + mf, now, skew, ta, &node->ds))) { node->ds_signer = keytag; if ((keytag = ds_authenticates_keys( mf, now, skew, &node->ds, &node->dnskey))) { @@ -2597,7 +2622,7 @@ static int chain_node_get_trusted_keys( } if (key_matches_signer(ta, &node->ds)) { - if ((node->ds_signer = a_key_signed_rrset( + if ((node->ds_signer = a_key_signed_rrset_no_wc( mf, now, skew, ta, &node->ds)) && (keytag = ds_authenticates_keys( mf, now, skew, &node->ds, &node->dnskey))){ diff --git a/src/general.c b/src/general.c index 80b40a2c..3e50124d 100644 --- a/src/general.c +++ b/src/general.c @@ -697,6 +697,8 @@ getdns_general_ns(getdns_context *context, getdns_eventloop *loop, /* clean up the request */ _getdns_context_clear_outbound_request(req); _getdns_dns_req_free(req); + if (return_netreq_p) + *return_netreq_p = NULL; return r; } return GETDNS_RETURN_GOOD; diff --git a/src/server.c b/src/server.c index ef5676f8..59497bed 100644 --- a/src/server.c +++ b/src/server.c @@ -34,6 +34,10 @@ #include #endif +#if defined(HAVE_FCNTL) +#include +#endif + #include "getdns/getdns_extra.h" #include "context.h" #include "types-internal.h" @@ -118,6 +122,25 @@ typedef struct tcp_connection { size_t to_answer; } tcp_connection; +/** best effort to set nonblocking */ +static void +getdns_sock_nonblock(int sockfd) +{ +#if defined(HAVE_FCNTL) + int flag; + if((flag = fcntl(sockfd, F_GETFL)) != -1) { + flag |= O_NONBLOCK; + if(fcntl(sockfd, F_SETFL, flag) == -1) { + /* ignore error, continue blockingly */ + } + } +#elif defined(HAVE_IOCTLSOCKET) + unsigned long on = 1; + if(ioctlsocket(sockfd, FIONBIO, &on) != 0) { + /* ignore error, continue blockingly */ + } +#endif +} static void free_listen_set_when_done(listen_set *set); static void tcp_connection_destroy(tcp_connection *conn) @@ -127,22 +150,33 @@ static void tcp_connection_destroy(tcp_connection *conn) tcp_to_write *cur, *next; - if (!(mf = &conn->super.l->set->context->mf)) - return; - + mf = &conn->super.l->set->context->mf; if (getdns_context_get_eventloop(conn->super.l->set->context, &loop)) return; - if (conn->event.read_cb||conn->event.write_cb||conn->event.timeout_cb) + if (conn->event.ev) loop->vmt->clear(loop, &conn->event); - if (conn->fd >= 0) + if (conn->event.read_cb||conn->event.write_cb||conn->event.timeout_cb) { + conn->event.read_cb = conn->event.write_cb = + conn->event.timeout_cb = NULL; + } + if (conn->fd >= 0) { (void) _getdns_closesocket(conn->fd); - GETDNS_FREE(*mf, conn->read_buf); - - for (cur = conn->to_write; cur; cur = next) { - next = cur->next; - GETDNS_FREE(*mf, cur); + conn->fd = -1; + } + if (conn->read_buf) { + GETDNS_FREE(*mf, conn->read_buf); + conn->read_buf = conn->read_pos = NULL; + conn->to_read = 0; + } + if ((cur = conn->to_write)) { + while (cur) { + next = cur->next; + GETDNS_FREE(*mf, cur); + cur = next; + } + conn->to_write = NULL; } if (conn->to_answer > 0) return; @@ -191,15 +225,16 @@ static void tcp_write_cb(void *userarg) (const void *)&to_write->write_buf[to_write->written], to_write->write_buf_len - to_write->written, 0)) == -1) { - if (_getdns_socketerror_wants_retry()) - return; - - DEBUG_SERVER("I/O error from send(): %s\n", - _getdns_errnostr()); - + if (conn->fd != -1) { + if (_getdns_socketerror_wants_retry()) { + (void) loop->vmt->schedule(loop, conn->fd, + DOWNSTREAM_IDLE_TIMEOUT, &conn->event); + return; + } + DEBUG_SERVER("I/O error from send(): %s\n", + _getdns_errnostr()); + } /* IO error, close connection */ - conn->event.read_cb = conn->event.write_cb = - conn->event.timeout_cb = NULL; tcp_connection_destroy(conn); return; } @@ -317,8 +352,10 @@ getdns_reply( return GETDNS_RETURN_GOOD; } if (!(to_write = (tcp_to_write *)GETDNS_XMALLOC( - *mf, uint8_t, sizeof(tcp_to_write) + len + 2))) + *mf, uint8_t, sizeof(tcp_to_write) + len + 2))) { + tcp_connection_destroy(conn); return GETDNS_RETURN_MEMORY_ERROR; + } to_write->write_buf_len = len + 2; to_write->write_buf[0] = (len >> 8) & 0xFF; @@ -327,20 +364,27 @@ getdns_reply( to_write->next = NULL; (void) memcpy(to_write->write_buf + 2, buf, len); - /* Appen to_write to conn->to_write list */ + /* Append to_write to conn->to_write list */ for ( to_write_p = &conn->to_write ; *to_write_p ; to_write_p = &(*to_write_p)->next) ; /* pass */ *to_write_p = to_write; - loop->vmt->clear(loop, &conn->event); - conn->event.write_cb = tcp_write_cb; if (conn->to_answer > 0) conn->to_answer--; - (void) loop->vmt->schedule(loop, - conn->fd, DOWNSTREAM_IDLE_TIMEOUT, - &conn->event); + + /* When event is scheduled, and doesn't have tcp_write_cb: + * reschedule. + */ + if (conn->event.write_cb == NULL) { + if (conn->event.ev) + loop->vmt->clear(loop, &conn->event); + conn->event.write_cb = tcp_write_cb; + (void) loop->vmt->schedule(loop, + conn->fd, DOWNSTREAM_IDLE_TIMEOUT, + &conn->event); + } } /* TODO: other transport types */ @@ -366,18 +410,19 @@ static void tcp_read_cb(void *userarg) /* Reset tcp_connection idle timeout */ loop->vmt->clear(loop, &conn->event); - (void) loop->vmt->schedule(loop, conn->fd, - DOWNSTREAM_IDLE_TIMEOUT, &conn->event); - - if ((bytes_read = recv(conn->fd, + if (conn->fd == -1 || + (bytes_read = recv(conn->fd, (void *)conn->read_pos, conn->to_read, 0)) < 0) { - if (_getdns_socketerror_wants_retry()) - return; /* Come back to do the read later */ - - /* IO error, close connection */ - DEBUG_SERVER("I/O error from recv(): %s\n", - _getdns_errnostr()); - + if (conn->fd != -1) { + if (_getdns_socketerror_wants_retry()) { + (void) loop->vmt->schedule(loop, conn->fd, + DOWNSTREAM_IDLE_TIMEOUT, &conn->event); + return; /* Come back to do the read later */ + } + /* IO error, close connection */ + DEBUG_SERVER("I/O error from recv(): %s\n", + _getdns_errnostr()); + } tcp_connection_destroy(conn); return; } @@ -391,9 +436,9 @@ static void tcp_read_cb(void *userarg) conn->to_read -= bytes_read; conn->read_pos += bytes_read; if (conn->to_read) - return; /* More to read */ + ; /* Schedule for more reading */ - if (conn->read_pos - conn->read_buf == 2) { + else if (conn->read_pos - conn->read_buf == 2) { /* read length of dns msg to read */ conn->to_read = (conn->read_buf[0] << 8) | conn->read_buf[1]; if (conn->to_read > conn->read_buf_len) { @@ -413,47 +458,60 @@ static void tcp_read_cb(void *userarg) return; } conn->read_pos = conn->read_buf; - return; /* Read DNS message */ - } - if ((r = getdns_wire2msg_dict(conn->read_buf, - (conn->read_pos - conn->read_buf), &request_dict))) - ; /* FROMERR on input, ignore */ + ; /* Schedule for more reading */ - else { - conn->to_answer++; + } else { + /* Ready for reading a new packet */ - /* TODO: wish list item: - * (void) getdns_dict_set_int64( - * request_dict, "request_id", intptr_t)conn); - */ - /* Call request handler */ - conn->super.l->set->handler( - conn->super.l->set->context, GETDNS_CALLBACK_COMPLETE, - request_dict, conn->super.l->set->userarg, (intptr_t)conn); + if (!(r = getdns_wire2msg_dict(conn->read_buf, + (conn->read_pos - conn->read_buf), &request_dict))) { + conn->to_answer++; + + /* TODO: wish list item: + * (void) getdns_dict_set_int64( + * request_dict, "request_id", intptr_t)conn); + */ + /* Call request handler */ + + conn->to_answer += 1; /* conn removal protection */ + conn->super.l->set->handler( + conn->super.l->set->context, + GETDNS_CALLBACK_COMPLETE, request_dict, + conn->super.l->set->userarg, (intptr_t)conn); + conn->to_answer -= 1; /* conn removal protection */ + + if (conn->fd == -1) { + tcp_connection_destroy(conn); + return; + } + } conn->read_pos = conn->read_buf; conn->to_read = 2; - return; /* Read more requests */ + ; /* Schedule for more reading */ } - conn->read_pos = conn->read_buf; - conn->to_read = 2; /* Read more requests */ + if (!conn->event.ev) { /* event not scheduled */ + conn->event.write_cb = conn->to_write ? tcp_write_cb : NULL; + (void) loop->vmt->schedule(loop, conn->fd, + DOWNSTREAM_IDLE_TIMEOUT, &conn->event); + } } static void tcp_timeout_cb(void *userarg) { tcp_connection *conn = (tcp_connection *)userarg; + getdns_eventloop *loop; assert(userarg); - if (conn->to_answer) { - getdns_eventloop *loop; + if (getdns_context_get_eventloop( + conn->super.l->set->context, &loop)) + return; - if (getdns_context_get_eventloop( - conn->super.l->set->context, &loop)) - return; + loop->vmt->clear(loop, &conn->event); - loop->vmt->clear(loop, &conn->event); + if (conn->to_answer && conn->fd >= 0) { (void) loop->vmt->schedule(loop, conn->fd, DOWNSTREAM_IDLE_TIMEOUT, &conn->event); @@ -501,8 +559,10 @@ static void tcp_accept_cb(void *userarg) GETDNS_FREE(*mf, conn); return; } + getdns_sock_nonblock(conn->fd); if (!(conn->read_buf = malloc(DNS_REQUEST_SZ))) { /* Memory error */ + (void) _getdns_closesocket(conn->fd); GETDNS_FREE(*mf, conn); return; } @@ -518,6 +578,7 @@ static void tcp_accept_cb(void *userarg) if (!_getdns_rbtree_insert( &l->set->connections_set, &conn->super.super)) { /* Memory error */ + (void) _getdns_closesocket(conn->fd); GETDNS_FREE(*mf, conn); return; } @@ -730,8 +791,17 @@ static void remove_listeners(listen_set *set) conn_p = (tcp_connection **)&l->connections; while (*conn_p) { + tcp_connection *prev_conn_p = *conn_p; + + loop->vmt->clear(loop, &(*conn_p)->event); tcp_connection_destroy(*conn_p); - if (*conn_p && (*conn_p)->to_answer > 0) + /* tcp_connection_destroy() updates the pointer to the + * connection. For the first connection this is + * l->connections. When the connection is not actually + * destroyed, the value of *conn_p thus remains the + * same. When it is destroyed it is updated. + */ + if (*conn_p == prev_conn_p) conn_p = (tcp_connection **) &(*conn_p)->super.next; } diff --git a/src/stub.c b/src/stub.c index dba63122..1270d0a4 100644 --- a/src/stub.c +++ b/src/stub.c @@ -965,6 +965,7 @@ tls_create_object(getdns_dns_req *dnsreq, int fd, getdns_upstream *upstream) "%-40s : ERROR: Hostname Authentication not available from TLS library (check library version)\n", upstream->addr_str); upstream->tls_hs_state = GETDNS_HS_FAILED; + SSL_free(ssl); return NULL; } #endif @@ -982,6 +983,7 @@ tls_create_object(getdns_dns_req *dnsreq, int fd, getdns_upstream *upstream) DEBUG_STUB("%s %-35s: ERROR: No host name or pubkey pinset provided for TLS authentication\n", STUB_DEBUG_SETUP_TLS, __FUNC__); upstream->tls_hs_state = GETDNS_HS_FAILED; + SSL_free(ssl); return NULL; } } else { diff --git a/src/tools/getdns_query.c b/src/tools/getdns_query.c index e963fc35..3102ce1a 100644 --- a/src/tools/getdns_query.c +++ b/src/tools/getdns_query.c @@ -1613,7 +1613,43 @@ static void incoming_request_handler(getdns_context *context, fprintf(stderr, "Could set class from query: %s\n", getdns_get_errorstr_by_id(r)); - else if ((r = getdns_general(context, qname_str, qtype, + else if (qtype == GETDNS_RRTYPE_TXT && qclass == GETDNS_RRCLASS_CH && + strcasecmp(qname_str, "version.bind.") == 0) { + const char *getdns_query_version = "getdns_query " GETDNS_VERSION; + char getdns_version[100] = "getdns "; + char getdns_api_version[100] = "getdns API "; + + response = request; + (void) getdns_dict_set_bindata(response, "/answer/0/name", qname); + (void) getdns_dict_set_int(response, "/answer/0/type", qtype); + (void) getdns_dict_set_int(response, "/answer/0/class", qclass); + (void) getdns_dict_set_int(response, "/answer/0/ttl", 0); + (void) getdns_dict_util_set_string(response, + "/answer/0/rdata/txt_strings/0", getdns_query_version); + + (void) getdns_dict_set_bindata(response, "/answer/1/name", qname); + (void) getdns_dict_set_int(response, "/answer/1/type", qtype); + (void) getdns_dict_set_int(response, "/answer/1/class", qclass); + (void) getdns_dict_set_int(response, "/answer/1/ttl", 0); + (void) strncat(getdns_version + 7, + getdns_get_version(), sizeof(getdns_version) - 8); + (void) getdns_dict_util_set_string(response, + "/answer/1/rdata/txt_strings/0",getdns_version); + + (void) getdns_dict_set_bindata(response, "/answer/2/name", qname); + (void) getdns_dict_set_int(response, "/answer/2/type", qtype); + (void) getdns_dict_set_int(response, "/answer/2/class", qclass); + (void) getdns_dict_set_int(response, "/answer/2/ttl", 0); + (void) strncat(getdns_api_version + 11, + getdns_get_api_version(), sizeof(getdns_api_version) - 12); + (void) getdns_dict_util_set_string(response, + "/answer/2/rdata/txt_strings/0",getdns_api_version); + + (void) getdns_dict_set_int(response, "/header/ancount", 3); + + goto answer_request; + + } else if ((r = getdns_general(context, qname_str, qtype, qext, msg, &transaction_id, request_cb))) fprintf(stderr, "Could not schedule query: %s\n", getdns_get_errorstr_by_id(r)); @@ -1624,9 +1660,8 @@ static void incoming_request_handler(getdns_context *context, return; } error: - if (qname_str) - free(qname_str); servfail(msg, &response); +answer_request: #if defined(SERVER_DEBUG) && SERVER_DEBUG do { char *request_str = getdns_pretty_print_dict(request); @@ -1643,13 +1678,17 @@ error: /* Cancel reply */ getdns_reply(context, NULL, request_id); } + if (response && response != request) + getdns_dict_destroy(response); + + if (qname_str) + free(qname_str); + if (msg) { if (msg->request) getdns_dict_destroy(msg->request); free(msg); } - if (response) - getdns_dict_destroy(response); } static void _getdns_query_log(void *userarg, uint64_t system,