- 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));
- }
+ adns__debug(ads,ads->tcpserver,0,"TCP connected");
+ ads->tcpstate= server_ok;
+ for (qu= ads->timew.head; qu; qu= nqu) {
+ nqu= qu->next;
+ if (qu->state == query_tosend) continue;
+ assert (qu->state == query_tcpwait);
+ adns__query_tcp(qu,now);
+ }
+}
+
+void adns__tcp_tryconnect(adns_state ads, struct timeval now) {
+ int r, fd, tries;
+ struct sockaddr_in addr;
+ struct protoent *proto;
+
+ for (tries=0; tries<ads->nservers; tries++) {
+ if (ads->tcpstate == server_connecting || ads->tcpstate == server_ok) return;
+ assert(ads->tcpstate == server_disconnected);
+ 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; }
+ fd= socket(AF_INET,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;
+ }
+ memset(&addr,0,sizeof(addr));
+ addr.sin_family= AF_INET;
+ addr.sin_port= htons(DNS_PORT);
+ addr.sin_addr= ads->servers[ads->tcpserver].addr;
+ r= connect(fd,(const struct sockaddr*)&addr,sizeof(addr));
+ ads->tcpsocket= fd;
+ ads->tcpstate= server_connecting;
+ if (r==0) { tcp_connected(ads,now); continue; }
+ if (errno == EWOULDBLOCK || errno == EINPROGRESS) return;
+ adns__tcp_broken(ads,"connect",strerror(errno));
+ }
+}
+
+/* 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_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) {
+ 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);
+}
+
+void adns__timeouts(adns_state ads, int act,
+ struct timeval **tv_io, struct timeval *tvbuf,
+ struct timeval now) {
+ adns_query qu, nqu;
+
+ for (qu= ads->timew.head; qu; qu= nqu) {
+ nqu= qu->next;
+ if (!timercmp(&now,&qu->timeout,>)) {
+ if (!tv_io) continue;
+ inter_maxtoabs(tv_io,tvbuf,now,qu->timeout);
+ } else {
+ if (!act) {
+ tvbuf->tv_sec= 0;
+ tvbuf->tv_usec= 0;
+ *tv_io= tvbuf;
+ return;
+ }
+ LIST_UNLINK(ads->timew,qu);
+ if (qu->state != query_tosend) {
+ adns__query_fail(qu,adns_s_timeout);
+ } else {
+ adns__query_send(qu,now);