- if (ads->tcpstate == server_connecting) {
- if (callb_checkfd(maxfd,writefds,ads->tcpsocket)) {
- count++;
- assert(ads->tcprecv.used==0);
- vbuf_ensure(&ads->tcprecv,1);
- if (ads->tcprecv.buf) {
- r= read(ads->tcpsocket,&ads->tcprecv.buf,1);
- if (r==0 || (r<0 && (errno==EAGAIN || errno==EWOULDBLOCK))) {
- debug("nameserver %s TCP connected",
- inet_ntoa(ads->servers[ads->tcpserver].addr));
- ads->tcpstate= server_connected;
- } else if (r>0) {
- tcpserver_broken(ads,"connect/read","sent data before first request");
- } else if (errno!=EINTR) {
- tcpserver_broken(ads,"connect",strerror(errno));
- }
+ assert(ads->tcpstate == server_broken);
+ for (qu= ads->tcpw.head; qu; qu= nqu) {
+ nqu= qu->next;
+ assert(qu->state == query_tcpw);
+ if (qu->retries > ads->nservers) {
+ LIST_UNLINK(ads->tcpw,qu);
+ adns__query_fail(qu,adns_s_allservfail);
+ }
+ }
+ ads->tcpstate= server_disconnected;
+}
+
+void adns__tcp_tryconnect(adns_state ads, struct timeval now) {
+ int r, fd, tries;
+ adns_rr_addr *addr;
+ struct protoent *proto;
+
+ for (tries=0; tries<ads->nservers; tries++) {
+ switch (ads->tcpstate) {
+ case server_connecting:
+ case server_ok:
+ case server_broken:
+ return;
+ case server_disconnected:
+ break;
+ default:
+ abort();
+ }
+
+ assert(!ads->tcpsend.used);
+ assert(!ads->tcprecv.used);
+ assert(!ads->tcprecv_skip);
+
+ proto= getprotobyname("tcp");
+ if (!proto) {
+ adns__diag(ads,-1,0,"unable to find protocol no. for TCP !");
+ return;
+ }
+ addr = &ads->servers[ads->tcpserver];
+ fd= socket(addr->addr.sa.sa_family, SOCK_STREAM, proto->p_proto);
+ if (fd<0) {
+ adns__diag(ads,-1,0,"cannot create TCP socket: %s",strerror(errno));
+ return;
+ }
+ r= adns__setnonblock(ads,fd);
+ if (r) {
+ adns__diag(ads,-1,0,"cannot make TCP socket nonblocking:"
+ " %s",strerror(r));
+ close(fd);
+ return;
+ }
+ r= connect(fd,&addr->addr.sa,addr->len);
+ ads->tcpsocket= fd;
+ ads->tcpstate= server_connecting;
+ if (r==0) { tcp_connected(ads,now); return; }
+ if (errno == EWOULDBLOCK || errno == EINPROGRESS) {
+ ads->tcptimeout= now;
+ timevaladd(&ads->tcptimeout,TCPCONNMS);
+ return;
+ }
+ adns__tcp_broken(ads,"connect",strerror(errno));
+ tcp_broken_events(ads);
+ }
+}
+
+/* Timeout handling functions. */
+
+void adns__must_gettimeofday(adns_state ads, const struct timeval **now_io,
+ struct timeval *tv_buf) {
+ const struct timeval *now;
+ int r;
+
+ now= *now_io;
+ if (now) return;
+ r= gettimeofday(tv_buf,0); if (!r) { *now_io= tv_buf; return; }
+ adns__diag(ads,-1,0,"gettimeofday failed: %s",strerror(errno));
+ adns_globalsystemfailure(ads);
+ return;
+}
+
+static void inter_immed(struct timeval **tv_io, struct timeval *tvbuf) {
+ struct timeval *rbuf;
+
+ if (!tv_io) return;
+
+ rbuf= *tv_io;
+ if (!rbuf) { *tv_io= rbuf= tvbuf; }
+
+ timerclear(rbuf);
+}
+
+static void inter_maxto(struct timeval **tv_io, struct timeval *tvbuf,
+ struct timeval maxto) {
+ struct timeval *rbuf;
+
+ if (!tv_io) return;
+ rbuf= *tv_io;
+ if (!rbuf) {
+ *tvbuf= maxto; *tv_io= tvbuf;
+ } else {
+ if (timercmp(rbuf,&maxto,>)) *rbuf= maxto;
+ }
+/*fprintf(stderr,"inter_maxto maxto=%ld.%06ld result=%ld.%06ld\n",
+ maxto.tv_sec,maxto.tv_usec,(**tv_io).tv_sec,(**tv_io).tv_usec);*/
+}
+
+static void inter_maxtoabs(struct timeval **tv_io, struct timeval *tvbuf,
+ struct timeval now, struct timeval maxtime) {
+ /* tv_io may be 0 */
+ ldiv_t dr;
+
+/*fprintf(stderr,"inter_maxtoabs now=%ld.%06ld maxtime=%ld.%06ld\n",
+ now.tv_sec,now.tv_usec,maxtime.tv_sec,maxtime.tv_usec);*/
+ if (!tv_io) return;
+ maxtime.tv_sec -= (now.tv_sec+2);
+ maxtime.tv_usec -= (now.tv_usec-2000000);
+ dr= ldiv(maxtime.tv_usec,1000000);
+ maxtime.tv_sec += dr.quot;
+ maxtime.tv_usec -= dr.quot*1000000;
+ if (maxtime.tv_sec<0) timerclear(&maxtime);
+ inter_maxto(tv_io,tvbuf,maxtime);
+}
+
+static void timeouts_queue(adns_state ads, int act,
+ struct timeval **tv_io, struct timeval *tvbuf,
+ struct timeval now, struct query_queue *queue) {
+ adns_query qu, nqu;
+
+ for (qu= queue->head; qu; qu= nqu) {
+ nqu= qu->next;
+ if (!timercmp(&now,&qu->timeout,>)) {
+ inter_maxtoabs(tv_io,tvbuf,now,qu->timeout);
+ } else {
+ if (!act) { inter_immed(tv_io,tvbuf); return; }
+ LIST_UNLINK(*queue,qu);
+ if (qu->state != query_tosend) {
+ adns__query_fail(qu,adns_s_timeout);
+ } else {
+ adns__query_send(qu,now);