+ /* Ignore further UDP if all slots full or TCP-mode */
+ if (next == nqueries) pfd[nqueries].events = 0;
+
+ /* If answer is truncated (TC bit), fallback to TCP */
+ if (answers[i][2] & 2) {
+ alens[i] = -1;
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
+ r = start_tcp(pfd+i, family, ns+j, sl, queries[i], qlens[i]);
+ pthread_setcancelstate(cs, 0);
+ if (r >= 0) {
+ qpos[i] = r;
+ apos[i] = 0;
+ }
+ continue;
+ }
+ }
+
+ for (i=0; i<nqueries; i++) if (pfd[i].revents & POLLOUT) {
+ struct msghdr mh = {
+ .msg_iovlen = 2,
+ .msg_iov = (struct iovec [2]){
+ { .iov_base = (uint8_t[]){ qlens[i]>>8, qlens[i] }, .iov_len = 2 },
+ { .iov_base = (void *)queries[i], .iov_len = qlens[i] } }
+ };
+ step_mh(&mh, qpos[i]);
+ r = sendmsg(pfd[i].fd, &mh, MSG_NOSIGNAL);
+ if (r < 0) goto out;
+ qpos[i] += r;
+ if (qpos[i] == qlens[i]+2)
+ pfd[i].events = POLLIN;
+ }
+
+ for (i=0; i<nqueries; i++) if (pfd[i].revents & POLLIN) {
+ struct msghdr mh = {
+ .msg_iovlen = 2,
+ .msg_iov = (struct iovec [2]){
+ { .iov_base = alen_buf[i], .iov_len = 2 },
+ { .iov_base = answers[i], .iov_len = asize } }
+ };
+ step_mh(&mh, apos[i]);
+ r = recvmsg(pfd[i].fd, &mh, 0);
+ if (r < 0) goto out;
+ apos[i] += r;
+ if (apos[i] < 2) continue;
+ int alen = alen_buf[i][0]*256 + alen_buf[i][1];
+ if (alen < 13) goto out;
+ if (apos[i] < alen+2 && apos[i] < asize+2)
+ continue;
+ int rcode = answers[i][3] & 15;
+ if (rcode != 0 && rcode != 3)
+ goto out;
+
+ /* Storing the length here commits the accepted answer.
+ * Immediately close TCP socket so as not to consume
+ * resources we no longer need. */
+ alens[i] = alen;
+ __syscall(SYS_close, pfd[i].fd);
+ pfd[i].fd = -1;