chiark / gitweb /
server/peer.c: Handle mobile peers switching addresses.
authorMark Wooding <mdw@distorted.org.uk>
Mon, 7 May 2012 11:29:33 +0000 (12:29 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Mon, 7 May 2012 14:32:06 +0000 (15:32 +0100)
Previously, we would track a mobile peer if it changed its address to
one that wasn't currently in use by any peer; but it seems that some
benighted NAT boxes will actually mix up the addresses assigned to
active peers, so we must spot the decryption errors and try to match
them up again.

server/peer.c

index a62b20a..02fceed 100644 (file)
@@ -188,45 +188,71 @@ static int p_encrypt(peer *p, int ty, buf *bin, buf *bout)
 static int p_decrypt(peer **pp, addr *a, size_t n,
                     int ty, buf *bin, buf *bout)
 {
-  peer *p;
-  peer_byaddr *pa;
+  peer *p, *q;
+  peer_byaddr *pa, *qa;
   int err = KSERR_DECRYPT;
   unsigned f;
 
-  if (*pp) {
-    p = *pp;
+  /* --- If we have a match on the source address then try that first --- */
+
+  q = *pp;
+  if (q) {
     T( trace(T_PEER, "peer: decrypting packet from known peer `%s'",
-            p_name(p)); )
-    err = ksl_decrypt(&p->ks, ty, bin, bout);
-  } else {
+            p_name(q)); )
+    if ((err = ksl_decrypt(&q->ks, ty, bin, bout)) != KSERR_DECRYPT ||
+       !(q->spec.f & PSF_MOBILE) || nmobile == 1) {
+      p = q;
+      goto match;
+    }
+    T( trace(T_PEER, "peer: failed to decrypt: try other mobile peers...",
+            p_name(q)); )
+  } else if (nmobile)
+    T( trace(T_PEER, "peer: unknown source: trying mobile peers..."); )
+  else {
     p = 0;
-    if (nmobile) {
-      T( trace(T_PEER, "peer: unknown source: trying mobile peers..."); )
-      FOREACH_PEER(q, {
-       if (!(q->spec.f & PSF_MOBILE)) continue;
-       if ((err = ksl_decrypt(&q->ks, ty, bin, bout)) == KSERR_DECRYPT) {
-         T( trace(T_PEER, "peer: peer `%s' failed to decrypt",
-                  p_name(q)); )
-         continue;
-       } else {
-         p = *pp = q;
-         IF_TRACING(T_PEER, {
-           if (!err)
-             trace(T_PEER, "peer: peer `%s' reports success", p_name(p));
-           else {
-             trace(T_PEER, "peer: peer `%s' reports decryption error %d",
-                   p_name(p), err);
-           }
-         })
-         break;
+    goto searched;
+  }
+
+  /* --- See whether any mobile peer is interested --- */
+
+  FOREACH_PEER(pp, {
+    if (pp == q || !(pp->spec.f & PSF_MOBILE)) continue;
+    if ((err = ksl_decrypt(&pp->ks, ty, bin, bout)) == KSERR_DECRYPT) {
+      T( trace(T_PEER, "peer: peer `%s' failed to decrypt",
+              p_name(pp)); )
+       continue;
+    } else {
+      p = pp;
+      IF_TRACING(T_PEER, {
+       if (!err)
+         trace(T_PEER, "peer: peer `%s' reports success", p_name(pp));
+       else {
+         trace(T_PEER, "peer: peer `%s' reports decryption error %d",
+               p_name(pp), err);
        }
-      });
-    }
-    if (!p) {
-      a_warn("PEER", "-", "unexpected-source", "?ADDR", a, A_END);
-      return (-1);
+      })
+      break;
     }
-    if (!err) {
+  });
+
+  /* --- We've searched the mobile peers --- */
+
+searched:
+  if (!p) {
+    a_warn("PEER", "-", "unexpected-source", "?ADDR", a, A_END);
+    return (-1);
+  }
+
+  /* --- We found one that accepted, so update the peer's address --- *
+   *
+   * If we had an initial guess of which peer this packet came from -- i.e.,
+   * @q@ is not null -- then swap the addresses over.  This doesn't leave the
+   * evicted peer in an especially good state, but it ought to get sorted out
+   * soon enough.
+   */
+
+  if (!err) {
+    if (!q) {
       T( trace(T_PEER, "peer: updating address for `%s'", p_name(p)); )
       pa = am_find(&byaddr, a, sizeof(peer_byaddr), &f); assert(!f);
       am_remove(&byaddr, p->byaddr);
@@ -234,8 +260,18 @@ static int p_decrypt(peer **pp, addr *a, size_t n,
       pa->p = p;
       p->spec.sa = *a;
       a_notify("NEWADDR", "?PEER", p, "?ADDR", a, A_END);
+    } else {
+      T( trace(T_PEER, "peer: swapping addresses for `%s' and `%s'",
+              p_name(p), p_name(q)); )
+      pa = p->byaddr; qa = q->byaddr;
+      pa->p = q; q->byaddr = pa; q->spec.sa = p->spec.sa;
+      qa->p = p; p->byaddr = qa; p->spec.sa = *a;
+      a_notify("NEWADDR", "?PEER", p, "?ADDR", a, A_END);
+      a_notify("NEWADDR", "?PEER", q, "?ADDR", &q->spec.sa, A_END);
     }
   }
+
+match:
   p_rxupdstats(p, n);
   if (err) {
     if (p) p->st.n_reject++;