diff options
author | Volker Lendecke <vl@samba.org> | 2017-12-28 22:35:46 +0100 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2018-01-04 00:37:21 +0100 |
commit | 623883083b5f9b5f07466fee99080c0c7f588551 (patch) | |
tree | 84e418ec6e5d1c658889c2ec0875307689f3cfa2 /libcli | |
parent | 507c9b6906ace462692eb499b1e217b5ea131c04 (diff) | |
download | samba-623883083b5f9b5f07466fee99080c0c7f588551.tar.gz |
libdns: dns/tcp client
Same signature as the UDP client in the same file. This opens and closes
the socket per request. In the future, we might want to create a
persistent TCP connection for our internal DNS server's forwarder. That
will require proper handling of in-flight requests. Something for
another day.
Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
Diffstat (limited to 'libcli')
-rw-r--r-- | libcli/dns/dns.c | 219 | ||||
-rw-r--r-- | libcli/dns/libdns.h | 10 |
2 files changed, 229 insertions, 0 deletions
diff --git a/libcli/dns/dns.c b/libcli/dns/dns.c index 7d066d802c1..9e180fe3fc9 100644 --- a/libcli/dns/dns.c +++ b/libcli/dns/dns.c @@ -176,3 +176,222 @@ int dns_udp_request_recv(struct tevent_req *req, return 0; } + +struct dns_tcp_request_state { + struct tevent_context *ev; + struct tstream_context *stream; + const uint8_t *query; + size_t query_len; + + uint8_t dns_msglen_hdr[2]; + struct iovec iov[2]; + + size_t nread; + uint8_t *reply; +}; + +static void dns_tcp_request_connected(struct tevent_req *subreq); +static void dns_tcp_request_sent(struct tevent_req *subreq); +static int dns_tcp_request_next_vector(struct tstream_context *stream, + void *private_data, + TALLOC_CTX *mem_ctx, + struct iovec **_vector, + size_t *_count); +static void dns_tcp_request_received(struct tevent_req *subreq); + +struct tevent_req *dns_tcp_request_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const char *server_addr_string, + const uint8_t *query, + size_t query_len) +{ + struct tevent_req *req, *subreq; + struct dns_tcp_request_state *state; + struct tsocket_address *local, *remote; + int ret; + + req = tevent_req_create(mem_ctx, &state, + struct dns_tcp_request_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->query = query; + state->query_len = query_len; + + if (query_len > UINT16_MAX) { + tevent_req_error(req, EMSGSIZE); + return tevent_req_post(req, ev); + } + + ret = tsocket_address_inet_from_strings(state, "ip", NULL, 0, &local); + if (ret != 0) { + tevent_req_error(req, errno); + return tevent_req_post(req, ev); + } + + ret = tsocket_address_inet_from_strings( + state, "ip", server_addr_string, DNS_SERVICE_PORT, &remote); + if (ret != 0) { + tevent_req_error(req, errno); + return tevent_req_post(req, ev); + } + + subreq = tstream_inet_tcp_connect_send(state, state->ev, + local, remote); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, dns_tcp_request_connected, req); + + return req; +} + +static void dns_tcp_request_connected(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct dns_tcp_request_state *state = tevent_req_data( + req, struct dns_tcp_request_state); + int ret, err; + + ret = tstream_inet_tcp_connect_recv(subreq, &err, state, + &state->stream, NULL); + TALLOC_FREE(subreq); + if (ret == -1) { + tevent_req_error(req, err); + return; + } + + RSSVAL(state->dns_msglen_hdr, 0, state->query_len); + state->iov[0] = (struct iovec) { + .iov_base = state->dns_msglen_hdr, + .iov_len = sizeof(state->dns_msglen_hdr) + }; + state->iov[1] = (struct iovec) { + .iov_base = discard_const_p(void, state->query), + .iov_len = state->query_len + }; + + subreq = tstream_writev_send(state, state->ev, state->stream, + state->iov, ARRAY_SIZE(state->iov)); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, dns_tcp_request_sent, req); +} + +static void dns_tcp_request_sent(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct dns_tcp_request_state *state = tevent_req_data( + req, struct dns_tcp_request_state); + int ret, err; + + ret = tstream_writev_recv(subreq, &err); + TALLOC_FREE(subreq); + if (ret == -1) { + tevent_req_error(req, err); + return; + } + + subreq = tstream_readv_pdu_send(state, state->ev, state->stream, + dns_tcp_request_next_vector, state); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, dns_tcp_request_received, req); +} + +static int dns_tcp_request_next_vector(struct tstream_context *stream, + void *private_data, + TALLOC_CTX *mem_ctx, + struct iovec **_vector, + size_t *_count) +{ + struct dns_tcp_request_state *state = talloc_get_type_abort( + private_data, struct dns_tcp_request_state); + struct iovec *vector; + uint16_t msglen; + + if (state->nread == 0) { + vector = talloc_array(mem_ctx, struct iovec, 1); + if (vector == NULL) { + return -1; + } + vector[0] = (struct iovec) { + .iov_base = state->dns_msglen_hdr, + .iov_len = sizeof(state->dns_msglen_hdr) + }; + state->nread = sizeof(state->dns_msglen_hdr); + + *_vector = vector; + *_count = 1; + return 0; + } + + if (state->nread == sizeof(state->dns_msglen_hdr)) { + msglen = RSVAL(state->dns_msglen_hdr, 0); + + state->reply = talloc_array(state, uint8_t, msglen); + if (state->reply == NULL) { + return -1; + } + + vector = talloc_array(mem_ctx, struct iovec, 1); + if (vector == NULL) { + return -1; + } + vector[0] = (struct iovec) { + .iov_base = state->reply, + .iov_len = msglen + }; + state->nread += msglen; + + *_vector = vector; + *_count = 1; + return 0; + } + + *_vector = NULL; + *_count = 0; + return 0; +} + +static void dns_tcp_request_received(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + int ret, err; + + ret = tstream_readv_pdu_recv(subreq, &err); + TALLOC_FREE(subreq); + if (ret == -1) { + tevent_req_error(req, err); + return; + } + + tevent_req_done(req); +} + +int dns_tcp_request_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + uint8_t **reply, + size_t *reply_len) +{ + struct dns_tcp_request_state *state = tevent_req_data( + req, struct dns_tcp_request_state); + int err; + + if (tevent_req_is_unix_error(req, &err)) { + tevent_req_received(req); + return err; + } + + *reply_len = talloc_array_length(state->reply); + *reply = talloc_move(mem_ctx, &state->reply); + tevent_req_received(req); + + return 0; +} diff --git a/libcli/dns/libdns.h b/libcli/dns/libdns.h index 91353a8e84d..1fa73d3fb22 100644 --- a/libcli/dns/libdns.h +++ b/libcli/dns/libdns.h @@ -50,4 +50,14 @@ int dns_udp_request_recv(struct tevent_req *req, uint8_t **reply, size_t *reply_len); +struct tevent_req *dns_tcp_request_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const char *server_addr_string, + const uint8_t *query, + size_t query_len); +int dns_tcp_request_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + uint8_t **reply, + size_t *reply_len); + #endif /*__LIBDNS_H__*/ |