+#include "adns-internal.h"
+
+/* TCP connection management */
+
+void adns__tcp_broken(adns_state ads, const char *what, const char *why) {
+ int serv;
+
+ assert(ads->tcpstate == server_connecting || ads->tcpstate == server_ok);
+ serv= ads->tcpserver;
+ warn("TCP connection lost: %s: %s",serv,why);
+ close(ads->tcpsocket);
+ ads->tcpstate= server_disconnected;
+
+ for (qu= ads->timew; qu; qu= nqu) {
+ nqu= qu->next;
+ if (qu->state == query_udp) continue;
+ assert(qu->state == query_tcpwait || qu->state == query_tcpsent);
+ qu->state= query_tcpwait;
+ qu->tcpfailed |= (1<<serv);
+ if (qu->tcpfailed == (1<<ads->nservers)-1) {
+ DLIST_UNLINK(ads->timew,qu);
+ adns__query_fail(ads,qu,adns_s_allservfail);
+ }
+ }
+
+ ads->tcpbuf.used= 0;
+ ads->tcpserver= (serv+1)%ads->nservers;
+}
+
+static void tcp_connected(adns_state ads, struct timeval now) {
+ debug("TCP connected",ads->tcpserver);
+ ads->tcpstate= server_connected;
+ for (qu= ads->timew.head; qu; qu= nqu) {
+ nqu= qu->next;
+ if (qu->state == query_udp) continue;
+ assert (qu->state == query_tcpwait);
+ adns__query_tcp(ads,qu,now);
+ }
+}
+
+void adns__tcp_tryconnect(adns_state ads, struct timeval now) {
+ int r, fd, tries;
+ sockaddr_in addr;
+ /* fixme: single TCP timeout, not once per server */
+
+ for (tries=0; tries<ads->nservers; tries++) {
+ if (ads->tcpstate == server_connecting || ads->tcpstate == server_ok) return;
+ assert(ads->tcpstate == server_disconnected);
+ assert(!ads->tcpbuf.used);
+
+ proto= getprotobyname("tcp");
+ if (!proto) { diag(ads,"unable to find protocol number for TCP !",-1); return; }
+ fd= socket(AF_INET,SOCK_STREAM,proto->p_proto);
+ if (fd<0) { diag(ads,"cannot create TCP socket: %s",-1,strerror(errno)); return; }
+ if (!adns__setnonblock(fd)) return;
+ memset(&addr,0,sizeof(addr));
+ addr.sin_family= AF_INET;
+ addr.sin_port= htons(NSPORT);
+ addr.sin_addr= ads->servers[ads->tcpserver].addr;
+ r= connect(fd,&addr,sizeof(addr));
+ ads->tcpsocket= fd;
+ ads->tcpstate= server_connecting;
+ if (r==0) { tcp_connected(ads); continue; }
+ if (errno == EWOULDBLOCK || errno == EINPROGRESS) return;
+ adns__tcp_broken(ads,"connect",strerror(errno));
+ }