[omniORB] First IPv6 Code update

Fox, Chuck (Civ, ARL/CISD) cfox at arl.army.mil
Sat Apr 2 11:19:55 BST 2005


Hey All,
  Well, IPv6 development has begun.  I started out in libcWrapper.cc since
it has the low-level name resolution addressing code.  The good news:
The new codebase is a lot smaller & cleaner.  The bad news: It's that way 
because I'm using POSIX standard calls that may not be implemented right on 
every platform.  I personally only use this stuff on Linux/Solaris and have
seen good results there, but there may be things that still need to be done.
If there are concurrency issues with getaddrinfo/getnameinfo my best bet would
be to wrap them in mutex locks at this point.  The Linux docs claim the calls
are threadsafe, beyond that, I don't know about any other OS out there.

 I've posted the full code below, since it's smaller than a diff.  I started out with the libcWrapper.cc from omniORB 4.0.5.  The biggest API change is that
IP4AddrInfo is now renamed to just IPAddrInfo to reflect that it can handle
IPv4 or IPv6 transparently.  Even if your platform does not support IPv6 at 
all this code should still operate fine for IPv4. The only issue I could think 
of would be if you have very old headers that don't have the IPv6 constants
in them you might have to do some extra #ifndefs to get the code to compile cleanly.
  Note that I only have the IP related code for libcWrapper posted below, there are also some string methods in the original file I've left out
for brevity.

  Once again, this is prototype code, no guarantees are made, I trust the maintainers to figure out what to do with it :)

libcWrapper.cc:

#include <omniORB4/CORBA.h>

#ifdef HAS_pch
#pragma hdrstop
#endif

#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#if !defined(HAVE_STRCASECMP) || !defined(HAVE_STRNCASECMP)
#  include <ctype.h>  //  for toupper and tolower.
#endif

#ifdef __vxWorks__
#  include <hostLib.h>
#  include <resolvLib.h>
#endif

#include "libcWrapper.h"
#include "SocketCollection.h"

// UPDATE:
// Additional #includes to help with IPv6 support, no idea how
// platform specific these are, but they are OK for Linux/Solaris
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>

OMNI_NAMESPACE_BEGIN(omni)


static omni_tracedmutex non_reentrant;

LibcWrapper::AddrInfo::~AddrInfo() {}


// *** USE ::getaddrinfo() if it's available.

// UPDATE:
// IP4AddrInfo has been renamed to IPAddrInfo to reflect that it
// should be available for IPv4 or IPv6.  The pd_addr member is no
// longer a hard-coded IPv4 socket address, it is now a generic
// sockaddr pointer that can be assigned to any INET type.
// Also the new ipInfo pointer will point to an addrinfo struct
// that is dynamically created by getaddrinfo (see man getaddrinfo)
// See class method defs for more changes to accomodate this.
class IPAddrInfo : public LibcWrapper::AddrInfo
{
public:
  IPAddrInfo(CORBA::String ipAddr, CORBA::UShort port);
  virtual ~IP4AddrInfo();

  virtual struct sockaddr* addr();
  virtual int addrSize();
  virtual char* asString();
  virtual LibcWrapper::AddrInfo* next();

private:
  struct addrinfo* ipInfo; // points to results of getaddrinfo 
  struct sockaddr* pd_addr;  // points to the first sockaddr in ipInfo
};

// UPDATE:
// The constructor is now taking in a string of an IP
// address or a regular hostname instead of just the 
// straight-up IPv4 32 bit block  We will use the 
// name/IP address strings directly passed from the user 
// along with the POSIX getaddrinfo call to do this
IPAddrInfo::IPAddrInfo(CORBA::String ipAddr, CORBA::UShort port)
{
  // take in ipAddr and get resolve it into an IP socket address:
  int statusCode = 0;
  ipInfo = NULL;

  // This is a 'hints' addrinfo struct that we setup to do a few things.  First, 
  // set the AI_PASSIVE flag in the ai_flags member.  This means that a NULL ipAddr
  // string will bind us to any available network interface.  The ai_family member is 
  // PF_UNSPEC to indicate we can use PF_INET (v4) or PF_INET6 (v6) addresses. 
  // The ai_sockettype member is set to SOCK_STREAM since we listen to TCP, and the 
  // ai_protocol member is 0 to also indicate IPv4 or IPv6 protocols are OK.  See the 
  // getaddrinfo man page for more details on the hints structure.
  struct addrinfo hints;
  hints.ai_flags = AI_PASSIVE;
  hints.ai_family = PF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_protocol = 0;
  hints.ai_addr = NULL;
  hints.ai_canonname = NULL;
  hints.ai_next = NULL;

  if (    ((statusCode = getaddrinfo ((const char*) ipAddr, NULL, &hints, &ipInfo)) != 0)
       || (!ipInfo)
       || (    (ipInfo->ai_family != PF_INET)  // check to make sure this is an INET addr
            && (ipInfo->ai_family != PF_INET6))) {
     // error in the getaddrinfo... we should probably toss an exception here
     cerr << gai_strerror(statusCode) << endl;
  }

  // now assign pd_addr to the first member of the ipInfo return... note that we can definitely 
  // have situations where multiple IP's resolve the same DNS name... what to do there?
  pd_addr = ipInfo->ai_addr;
  pd_addr->sin_port = htons(port); // setup the port here
}

// UPDATE:
//  We have an allocated addrinfo struct, free up its memory:
IPAddrInfo::~IP4AddrInfo() {
  if (ipInfo != NULL) {
    freeaddrinfo (ipInfo); // freeaddrinfo is the 'destructor' of getaddrinfo
  }
  ipInfo = NULL;
  pd_addr = NULL;
}

struct sockaddr*
IPAddrInfo::addr()
{
  return pd_addr;
}

// UPDATE: return the length of ipInfo instead of the old fixed-length
int
IPAddrInfo::addrSize()
{
  return (ipInfo->ai_addrlen);
}

// UPDATE: // This is now no longer a hard-coded  IPv4 generator... we use getnameinfo to return a 
// stringified numeric IP, which can be either IPv4 or IPv6.
char*
IPAddrInfo::asString()
{
  // dynamically allocate memory based on the IPv4 or IPv6 family,
  // see netinet/in.h for constant defs of how large the buffers are:
  int statusCode = 0;
  char* result = NULL;
  size_t resultLen = 0;

  // be tricky and assign resultLen different sizes if the address family is PF_INET (IPv4),
  // othwerise it's IPv6 sized:
  resultLen = (ipInfo->ai_family == PF_INET) ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN;
  result = CORBA::string_alloc (resultLen); // sized right for IPv4 or IPv6

  // NI_NUMERICHOST will ensure that we get back a numeric hostname instead of a resolved name
  if ((statusCode = getnameinfo ((const struct sockaddr*)pd_addr, ipInfo->ai_addrlen, result,
                                  resultLen, NULL, NULL, NI_NUMERICHOST)) != 0) {
     // error in the name resolution, another great place for an exception:
     cerr << "Failed in name resolution, returning NULL  error: " << gaistrerror (statusCode)  << endl;
     delete (result);
     result = NULL;
  }

  return result;
}

LibcWrapper::AddrInfo*
IPAddrInfo::next()
{
  return 0; // Not implemented
}

// UPDATE:
//   This method is radically simplified because of the work done in the
// new IPAddrInfo constructor.   Of course, the simplifications are currently
// assuming that the host OS can use the POSIX standard calls. 
// Even if we need OS specific stuff, it should probably go into the
// constructor at this point, so refer to that method for any changes
// you might want to make.
LibcWrapper::AddrInfo* LibcWrapper::getAddrInfo(const char* node,
                                                CORBA::UShort port)
{

  return new IPAddrInfo (node, port);

}

void LibcWrapper::freeAddrInfo(LibcWrapper::AddrInfo* ai)
{
  delete ai;
}

OMNI_NAMESPACE_END(omni)



More information about the omniORB-list mailing list