chiark / gitweb /
adns_processwriteable: assert that write did not write more than we asked
[adns.git] / src / event.c
index 5553acefee86fe488570cca1589f8193957e594f..95386f42c4fed8351a939290cd85dcfdf5bed53b 100644 (file)
@@ -6,7 +6,8 @@
  */
 /*
  *  This file is part of adns, which is
- *    Copyright (C) 1997-2000,2003,2006  Ian Jackson
+ *    Copyright (C) 1997-2000,2003,2006,2014-2016  Ian Jackson
+ *    Copyright (C) 2014  Mark Wooding
  *    Copyright (C) 1999-2000,2003,2006  Tony Finch
  *    Copyright (C) 1991 Massachusetts Institute of Technology
  *  (See the file INSTALL for full details.)
@@ -22,8 +23,7 @@
  *  GNU General Public License for more details.
  *  
  *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software Foundation,
- *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
+ *  along with this program; if not, write to the Free Software Foundation.
  */
 
 #include <errno.h>
@@ -339,7 +339,8 @@ int adns__pollfds(adns_state ads, struct pollfd pollfds_buf[MAX_POLLFDS]) {
 }
 
 int adns_processreadable(adns_state ads, int fd, const struct timeval *now) {
-  int want, dgramlen, r, i, udpaddrlen, serv, old_skip;
+  int want, dgramlen, r, i, serv, old_skip;
+  socklen_t udpaddrlen;
   byte udpbuf[DNS_MAXUDP];
   char addrbuf[ADNS_ADDR2TEXT_BUFLEN];
   struct udpsocket *udp;
@@ -443,8 +444,24 @@ int adns_processwriteable(adns_state ads, int fd, const struct timeval *now) {
     assert(ads->tcprecv.used==0);
     assert(ads->tcprecv_skip==0);
     for (;;) {
+      /* This function can be called even if the fd wasn't actually
+       * flagged as writeable.  For asynch tcp connect we have to
+       * actually use the writeability to tell us the connect has
+       * completed (or failed), so we need to double check. */
+      fd_set writeable;
+      struct timeval timeout = { 0,0 };
+      FD_ZERO(&writeable);
+      FD_SET(ads->tcpsocket,&writeable);
+      r= select(ads->tcpsocket+1,0,&writeable,0,&timeout);
+      if (r==0) break;
+      if (r<0) {
+       if (errno==EINTR) continue;
+       adns__tcp_broken(ads,"select","failed connecting writeability check");
+       r= 0; goto xit;
+      }
+      assert(FD_ISSET(ads->tcpsocket,&writeable));
       if (!adns__vbuf_ensure(&ads->tcprecv,1)) { r= ENOMEM; goto xit; }
-      r= read(ads->tcpsocket,&ads->tcprecv.buf,1);
+      r= read(ads->tcpsocket,ads->tcprecv.buf,1);
       if (r==0 || (r<0 && (errno==EAGAIN || errno==EWOULDBLOCK))) {
        tcp_connected(ads,*now);
        r= 0; goto xit;
@@ -471,6 +488,7 @@ int adns_processwriteable(adns_state ads, int fd, const struct timeval *now) {
        adns__tcp_broken(ads,"write",strerror(errno));
        r= 0; goto xit;
       } else if (r>0) {
+       assert(r <= ads->tcpsend.used);
        ads->tcpsend.used -= r;
        memmove(ads->tcpsend.buf,ads->tcpsend.buf+r,ads->tcpsend.used);
       }
@@ -608,8 +626,19 @@ xit:
 void adns_globalsystemfailure(adns_state ads) {
   adns__consistency(ads,0,cc_entex);
 
-  while (ads->udpw.head) adns__query_fail(ads->udpw.head, adns_s_systemfail);
-  while (ads->tcpw.head) adns__query_fail(ads->tcpw.head, adns_s_systemfail);
+  for (;;) {
+    adns_query qu;
+#define GSF_QQ(QQ)                             \
+    if ((qu= ads->QQ.head)) {                  \
+      LIST_UNLINK(ads->QQ,qu);                 \
+      adns__query_fail(qu, adns_s_systemfail); \
+      continue;                                        \
+    }
+    GSF_QQ(udpw);
+    GSF_QQ(tcpw);
+#undef GSF_QQ
+    break;
+  }
   
   switch (ads->tcpstate) {
   case server_connecting: