+static int dns_query_transaction_open_tcp(DnsQueryTransaction *t) {
+ int r;
+
+ assert(t);
+
+ if (t->tcp_fd >= 0)
+ return 0;
+
+ t->tcp_written = 0;
+ t->tcp_read = 0;
+ t->received = dns_packet_unref(t->received);
+
+ t->tcp_fd = dns_scope_tcp_socket(t->scope);
+ if (t->tcp_fd < 0)
+ return t->tcp_fd;
+
+ r = sd_event_add_io(t->query->manager->event, &t->tcp_event_source, t->tcp_fd, EPOLLIN|EPOLLOUT, on_tcp_ready, t);
+ if (r < 0) {
+ t->tcp_fd = safe_close(t->tcp_fd);
+ return r;
+ }
+
+ return 0;
+}
+
+void dns_query_transaction_reply(DnsQueryTransaction *t, DnsPacket *p) {
+ int r;
+
+ assert(t);
+ assert(p);
+ assert(t->state == DNS_QUERY_PENDING);
+
+ /* Note that this call might invalidate the query. Callers
+ * should hence not attempt to access the query or transaction
+ * after calling this function. */
+
+ if (t->received != p) {
+ dns_packet_unref(t->received);
+ t->received = dns_packet_ref(p);
+ }
+
+ if (t->tcp_fd >= 0) {
+ if (DNS_PACKET_TC(p)) {
+ /* Truncated via TCP? Somebody must be fucking with us */
+ dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
+ return;
+ }
+
+ if (DNS_PACKET_ID(p) != t->id) {
+ /* Not the reply to our query? Somebody must be fucking with us */
+ dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
+ return;
+ }
+ }
+
+ if (DNS_PACKET_TC(p)) {
+ /* Response was truncated, let's try again with good old TCP */
+ r = dns_query_transaction_open_tcp(t);
+ if (r == -ESRCH) {
+ /* No servers found? Damn! */
+ dns_query_transaction_complete(t, DNS_QUERY_NO_SERVERS);
+ return;
+ }
+ if (r < 0) {
+ /* Couldn't send? Try immediately again, with a new server */
+ dns_scope_next_dns_server(t->scope);
+
+ r = dns_query_transaction_go(t);
+ if (r < 0) {
+ dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
+ return;
+ }
+
+ return;
+ }
+ }
+
+ /* Parse and update the cache */
+ r = dns_packet_extract_rrs(p);
+ if (r < 0) {
+ dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
+ return;
+ } else if (r > 0)
+ dns_cache_put_rrs(&t->scope->cache, p->rrs, r, 0);
+
+ if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
+ dns_query_transaction_complete(t, DNS_QUERY_SUCCESS);
+ else
+ dns_query_transaction_complete(t, DNS_QUERY_FAILURE);
+}
+