This is the mail archive of the cygwin mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

IPv6 help (Re: inetutils, r* commands)


<tolstoy mode>
   There are two questions, below...so skip to
   those if you don't need the background.
</tolstoy mode>

I've been trying to deal with the various issues that have cropped up in
inetutils since its last release, and since 1.7.1 was released. It seems
that most of the issues are related to the IPv6 support in cygwin,
giving results back to the daemons that they are not prepared for.

Well.

inetutils appears hopeless; I can't see where any work has gone in to
supporting IPv6.  So, I plan to *replace* inetutils with a number of
smaller packages, derived from sources where such work HAS already occurred.

I'm using this page as my starting point:
http://www.deepspace6.net/docs/ipv6_status_page_apps.html

Also, I plan to ITA xinetd, and drop inetutils' version of inetd with no
direct replacement.  Similarly, I plan to drop syslogd in favor of
Corinna's syslog-ng.  In these two cases, I'll repackage the existing
binaries that have no direct replacement, so that people's installations
won't just "break" all of the sudden.  But these two apps will
definitely be EOLed.


So, first up: rsh/rsh-server, based on the fedora release(s) of the same
name.  The fedora rpm is based in turn on netkit-rsh, but heavily
patched.  However, the underlying codebase still shows its age: lots of
BSD signal functions (sigblock, sigsetmask, etc) which we don't support.
 However, those fixes are relatively easy.

The clients (rsh, rlogin, rcp, rexec) all seem to work when contacting a
separate (linux) server (*).  And the servers all seem to work when
contacted by a separate (linux) client (**).  But, connecting locally
doesn't appear to work correctly.

(*) It seems that you now need to have an identd server running on the
*client* box, or r* authentication takes 30 seconds or so.  We don't
currrently have one of these ported; I'll try to do that at some point
unless someone beats me to it.  I've been using the following (closed
source, free-as-in-beer) version that seems to be well-regarded:
   http://rndware.info/products/windows-ident-server.html


(**) After I modified the function that compares the client's IP address
extracted from the connection, with the IP address determined by reverse
DNS lookup.  At least on my Vista machine, ALL local connections come in
as IPv6, as ::ffff:192.168.x.y. (or ::ffff:127.0.0.1)  As the original
"address checker" did this:

         memset(&hints, 0, sizeof(hints));
         hints.ai_family = PF_UNSPEC;
         error = getaddrinfo(hname_buf, NULL, &hints, &res);
         res0 = res;
         while (res) {
           if (soaddr_eq_ip(fromp, res->ai_addr)) {
             hostok = 1;
             break;
           }
           res = res->ai_next;
        }
        freeaddrinfo(res0);

where the comparison function soaddr_eq_ip did:

static int
soaddr_eq_ip(const struct sockaddr *s1, const struct sockaddr *s2)
{
  if (s1->sa_family != s2->sa_family)
     return 0; /* no match */

  ...

Well, since ALL of the values returned by getaddrinfo were IPv4, all
connections were rejected.  So, I made soaddr_eq_ip somewhat smarter
(pasted below), and now -- as I said above -- connections from other
machines on my local net work.

But...localhost (or using the localhost's actual name/IP) do not.

Here's some debugging output from rlogind, when doing 'rlogin localhost'
from the same machine:

mymachine rlogind: PID 5960: warning: /etc/hosts.allow, line 11: host
name/address mismatch: 127.0.0.1 != mymachine
mymachine rlogind: PID 5960: connect from ::ffff:127.0.0.1
(::ffff:127.0.0.1)
mymachine rlogind: PID 5960: doit: hostok=0
mymachine rlogind: PID 5960: soaddr_eq_ip: (::ffff:127.0.0.1,192.168.199.1)
mymachine rlogind: PID 5960: soaddr_eq_ip: (::ffff:127.0.0.1,192.168.154.1)
mymachine rlogind: PID 5960: soaddr_eq_ip: (::ffff:127.0.0.1,192.168.1.3)
mymachine rlogind: PID 5960: doit: hostok=0


(hostok=0 means "no match/reject connection).

Notice that getaddrinfo returns three different networks.  Two of these
are inactive (.199.1 and .154.1).  192.168.1.3 is mymachine's "real" IP
addr.  But 127.0.0.1 is /not/ included in the list...so it can't be matched.

/etc/hosts has:
127.0.0.1       localhost
::1             localhost
# Start of entries inserted by Spybot - Search & Destroy
<a whole bunch of crap mapped to 127.0.0.1>

So....

*********************
QUESTION #1.  Should cygwin's getaddrinfo return an entry for the
loopback interface?
*********************


Well, next I tried using 'rlogin mymachine'.  On this, I think
tcp_wrappers choked:

rlogind: PID 5072: refused connect from mymachine (::ffff:192.168.199.1)

Notice that only the "first" interface from the previous list --
assuming getaddrinfo returned its results in the same order as before --
is (in)validated.  So, probably a bug -- or incompatibility of
assumptions between tcp_wrappers and cygwin1.dll. I'll have to dig into
that, later. FWIW, hosts.allow has:

 ALL : localhost 127.0.0.1/32 [::1]/128 : allow
 rlogind: 192.168.1.0/255.255.255.0
 rshd: 192.168.1.0/255.255.255.0
 rexecd: 192.168.1.0/255.255.255.0




Finally, I tried 'rlogin mymachine.mydomain'.  This was even weirder.
With my fix outlined above, it kinda works -- THAT part, anyway:

rlogind: PID 7904: connect from mymachine.mydomain (::ffff:192.168.1.3)
rlogind: PID 7904: doit: hostok=0
rlogind: PID 7904: soaddr_eq_ip: (::ffff:192.168.1.3,192.168.1.3)
rlogind: PID 7904: doit: hostok=1

Yay! hostok=1!  So, what's it look like over in the client window?

$ rlogin mymachine.mydomain
Last login: Mon Mar 15 22:34:40 from mymachine.mydomain
      ____________________,
______________________________________
   .QQQQQQQQQQQQQQQQQQQQQQQQL_         |
      |
 .gQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ__   |
      |
 gQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ==   |                    _.---.)
      |
 QQQQQQQQQrlogin: closed connection.

Oops.  This doesn't bode well -- it means that even if the answer to #1
is yes, all that will accomplish is moving the 'rlogin localhost' into
the same boat with 'rlogin fqdn' -- where it looks like the forked
rlogind process is crashing. (It doesn't do that when coming from a
different box).  Well, more debugging...


So, it's getting there -- remember that all these difficulties are
solely with regards to same-machine connections.  The clients seem to
work fine with actual outbound connections, and the servers work fine
(now) with actual inbound ones.


So, what's the second question?


*********************
QUESTION #2.  Is there a cleaner way to do the address matching than the
version that I've modified below? I basically only changed the guts of
soaddr_eq_ip(); the rest is factory equipment...
*********************


(find_hostname is the actual entry point; it populates *portname as well
as setting *hostok)



/*
 * Check whether host h is in our local domain,
 * defined as sharing the last two components of the domain part,
 * or the entire domain part if the local domain has only one component.
 * If either name is unqualified (contains no '.'),
 * assume that the host is local, as it will be
 * interpreted as such.
 */
static int
local_domain(const char *h)
{
  char localhost[MAXHOSTNAMELEN];
  const char *p1, *p2;

  localhost[0] = 0;
  (void) gethostname(localhost, sizeof(localhost));
  p1 = topdomain(localhost);
  p2 = topdomain(h);
  if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2))
    return(1);
  return(0);
}

static int
soaddr_eq_ip(const struct sockaddr *s1, const struct sockaddr *s2)
{
  const struct sockaddr_in6 * ipv6p = NULL;
  const struct sockaddr_in  * ipv4p = NULL;

  if (s1->sa_family != s2->sa_family) {
    /* handle IPv4-in-IPv6 */
    if (s1->sa_family == AF_INET6) {
      ipv6p = ((const struct sockaddr_in6 *)s1);
    } else if (s1->sa_family == AF_INET) {
      ipv4p = ((const struct sockaddr_in *)s1);
    } else {
      syslog(LOG_INFO, "soaddr_eq_ip: s1 is neither IPv4 nor IPv6");
      return 0;
    }
    if (s2->sa_family == AF_INET6) {
      ipv6p = ((const struct sockaddr_in6 *)s2);
    } else if (s2->sa_family == AF_INET) {
      ipv4p = ((const struct sockaddr_in *)s2);
    } else {
      syslog(LOG_INFO, "soaddr_eq_ip: s2 is neither IPv4 nor IPv6");
      return 0;
    }
    if (IN6_IS_ADDR_V4MAPPED(&(ipv6p->sin6_addr))) {
      char ipv6buf[INET6_ADDRSTRLEN + 1];
      char ipv4buf[INET_ADDRSTRLEN + 1];
      memset (ipv6buf, 0x00, INET6_ADDRSTRLEN + 1);
      memset (ipv4buf, 0x00, INET_ADDRSTRLEN + 1);
      if (inet_ntop(AF_INET6,
                    (const void *)&(ipv6p->sin6_addr),
                    ipv6buf, INET6_ADDRSTRLEN+1) != NULL &&
          inet_ntop(AF_INET,
                    (const void *)&(ipv4p->sin_addr),
                    ipv4buf, INET_ADDRSTRLEN+1) != NULL&&
          ipv4p->sin_addr.s_addr != INADDR_ANY &&
          strncmp (ipv6buf, "::ffff:", 7) == 0)
      {

          syslog(LOG_INFO, "soaddr_eq_ip: (%s,%s)",ipv6buf,ipv4buf);
          return (strncmp(ipv6buf+7, ipv4buf, sizeof(ipv4buf)) == 0);
      }
      syslog(LOG_INFO,
             "soaddr_eq_ip: failed to compare IPv4-mapped address");
      return 0;
    }
    syslog(LOG_INFO,
           "soaddr_eq_ip: IPv6 value is not IPv4-mapped addr");
    return 0;
  }
  if (s2->sa_family == AF_INET6) {
    syslog(LOG_INFO, "soaddr_eq_ip: comparing two AF_INET6 addrs");
    return (memcmp(
      (const void*)(
        &((const struct sockaddr_in6 *)s1)->sin6_addr
        ),
      (const void*)(
        &((const struct sockaddr_in6 *)s2)->sin6_addr
        ),
      sizeof(struct in6_addr))
        == 0);
  }
  else {
    syslog(LOG_INFO, "soaddr_eq_ip: comparing two AF_INET addrs");
    return (memcmp(
      (const void*)(
        &((const struct sockaddr_in *)s1)->sin_addr
        ),
      (const void*)(
        &((const struct sockaddr_in *)s2)->sin_addr
        ),
      sizeof(struct in_addr))
        == 0);
  }
}

static char *
find_hostname(struct sockaddr *fromp, socklen_t fromlen,
  char *portname, int *hostokp)
{
  int error;
  char *hname;
  char hname_buf[NI_MAXHOST];
  int hostok = 0;

  error = getnameinfo(fromp, fromlen,
    hname_buf, sizeof(hname_buf), portname, NI_MAXSERV,
    NI_NUMERICSERV);
  if ((error == EAI_AGAIN) && !check_all)
    error = getnameinfo(fromp, fromlen,
            hname_buf, sizeof(hname_buf), portname, NI_MAXSERV,
            NI_NUMERICSERV|NI_NUMERICHOST);
  assert(error == 0);

  if (check_all || local_domain(hname_buf)) {
    /*
     * If name returned is in our domain,
     * attempt to verify that we haven't been fooled by someone
     * in a remote net; look up the name and check that this
     * address corresponds to the name.
     */
    struct addrinfo hints;
    struct addrinfo *res0, *res;

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = PF_UNSPEC;
    error = getaddrinfo(hname_buf, NULL, &hints, &res);
    assert(error == 0);

    res0 = res;
    while (res) {
      if (soaddr_eq_ip(fromp, res->ai_addr)) {
        hostok = 1;
        break;
      }
      res = res->ai_next;
    }
    freeaddrinfo(res0);
  }
  else {
    hostok = 1;
  }

  hname = strdup(hname_buf);

  /*
   * Actually it might be null if we're out of memory, but
   * where do we go then? We'd have to bail anyhow.
   */
  assert(hname != NULL);

  *hostokp = hostok;

  return hname;
}



--
Chuck




--
Problem reports:       http://cygwin.com/problems.html
FAQ:                   http://cygwin.com/faq/
Documentation:         http://cygwin.com/docs.html
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]