nettcpendpoint.cc #1

  • //
  • guest/
  • perforce_software/
  • p4/
  • 2016-1/
  • net/
  • nettcpendpoint.cc
  • View
  • Commits
  • Open Download .zip Download (31 KB)
// -*- mode: C++; tab-width: 4; -*-
// vi:ts=8 sw=4 noexpandtab autoindent

/*
 * NetTcpEndPoint
 *
 * Copyright 1995, 1996, 2011 Perforce Software.  All rights reserved.
 *
 * This file is part of Perforce - the FAST SCM System.
 * - previously part of nettcp.cc
 */

# define NEED_ERRNO
# define NEED_SIGNAL
# ifdef OS_NT
# define NEED_FILE
# endif
# define NEED_FCNTL
# define NEED_IOCTL
# define NEED_TYPES
# define NEED_SOCKET_IO

# ifdef OS_MPEIX
# define _SOCKET_SOURCE /* for sys/types.h */
# endif

# include "netportipv6.h"	// must be included before stdhdrs.h
# include <stdhdrs.h>

# if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_DARWIN)
# include <sys/un.h>
# endif // OS_LINUX || OS_MACOSX
#include <ctype.h>

# include <error.h>
# include <strbuf.h>
# include "netaddrinfo.h"
# include <bitarray.h>
# include <debug.h>
# include <tunable.h>
# include <keepalive.h>
# include <msgrpc.h>
# include "netportparser.h"
# include "netconnect.h"
# include "netutils.h"
# include "nettcpendpoint.h"
# include "nettcptransport.h"
# include "netselect.h"
# include "netport.h"
# include "netdebug.h"
# include "netsupport.h"

static int one = 1;

/*
 * For 2012.1, default hints flags to nothing.
 * For 2012.2, perhaps default them to AI_ADDRCONFIG.
 *
 * AI_ADDRCONFIG suppresses unnecessary AAAA DNS lookups
 * from hosts that have no IPv6 connectivity,
 * thus preventing long IPv6 stalls for such hosts.
 * See RFC 2553 "Basic Socket Interface Extensions for IPv6".
 *
 * However, AI_ADDRCONFIG also prevents "localhost" (*and* "::1")
 * lookups from returning IPv6 addresses if there are no interfaces
 * with routable IPv6 addresses; this prevents IPv6 testing in 2012.1
 * until we provide IPv6 infrastructure internally.
 * In order to support internal testing of IPv6 in 2012.1
 * we don't turn this flag on.
 *
 * In 2012.1 clients must explicitly request an IPv6 connection
 * via one of the IPv6 transport prefixes; presumably they won't
 * do that at all, as IPv6 support is for internal testing only in 2012.1.
 * Therefore, the lack of AI_ADDRCONFIG should not be a problem in 2012.1.
 *
 * In 2012.2 we could consider changing the default and tcp/ssl transports
 * to IPv4+IPv6 and use the system-defined preference (by default, prefer
 * IPv6), rather than using our own prefer-v4 or prefer-v6 transport
 * prefixes.
 * See RFC 3484 "Default Address Selection for Internet Protocol version 6 (IPv6)".
 * At the same time we should turn on AI_ADDRCONFIG to prevent stalls when
 * trying to connect via IPv6 if the host can't use IPv6.  As a convenience,
 * we could consider not adding AI_ADDRCONFIG if the requested hostname is
 * either "localhost" or "::1" (there's no real need to check for the myriad
 * of localhost aliases or of the different ways to write "::1").
 */
static const int kBaseHintsFlags = 0;

/*
 * NetTcp utility routines
 */

void
NetTcpEndPoint::SetupSocket( int fd, int ai_family, AddrType type, Error *e )
{
# if defined(F_SETFD) && !defined(OS_NT)
	// attempt to set close on exec flag, ignore failures
	fcntl(fd, F_SETFD, 1);
# endif

	// Set buffer size.

	// Turn on misc options:
	//    REUSEADDR - allows listens while dead connections exist
	//    REUSEPORT - allows listens while live connections exist (BSD)
	//    KEEPALIVE - detects dead PCs

	// Do these separately: although SO_XXX look like bit flags, they're not.
	// AIX just plain chokes sometimes on these, so we don't check.
	// Well, we now check, but don't report an error unless DT_NET debugging is enabled.

	//
	// Don't set SO_REUSEADDR on Windows since it doesn't do what it's
	// supposed to. Instead of allowing you to reuse a port within the
	// TIME_WAIT period, it allows a process to bind to a port that's
	// actively being used by another process. Yuk. The good news
	// is that SO_REUSEADDR is not required to rebind within the 
	// TIME_WAIT period on Windows.
	// 

	int sz;
	TYPE_SOCKLEN rsz = sizeof( sz );
	const int MinBufSz = p4tunable.Get( P4TUNE_NET_TCPSIZE );

# ifdef SO_SNDBUF
	// never reduce the buffer size, so don't set it if we can't get the old value
	if( !getsockopt( fd, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<SOCKOPT_T *>(&sz), &rsz ) )
	{
	    if( sz < MinBufSz )
	    {
	        sz = MinBufSz;
	        do_setsockopt( "NetTcpEndPoint", fd, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<SOCKOPT_T *>(&sz), rsz );
	    }
	}
# endif

# ifdef SO_RCVBUF
	// never reduce the buffer size, so don't set it if we can't get the old value
	if( !getsockopt( fd, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<SOCKOPT_T *>(&sz), &rsz ) )
	{
	    if( sz < MinBufSz )
	    {
	        sz = MinBufSz;
	        do_setsockopt( "NetTcpEndPoint", fd, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<SOCKOPT_T *>(&sz), rsz );
	    }
	}
# endif

# if defined( SO_REUSEADDR ) && !defined( OS_NT )
	if( (type == AT_LISTEN) || (type == AT_CHECK) )
	{
	    do_setsockopt( "NetTcpEndPoint", fd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<SOCKOPT_T *>(&one), rsz );
	}
# endif

# ifdef SO_REUSEPORT
	const int reuseport = p4tunable.Get( P4TUNE_NET_REUSEPORT );
	if( ((type == AT_LISTEN) || (type == AT_CHECK)) && reuseport )
	{
	    do_setsockopt( "NetTcpEndPoint", fd, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<SOCKOPT_T *>(&one), rsz );
	}
# endif

# if defined(IPV6_V6ONLY) && !defined(OS_MINGW) && (!defined(_MSC_VER) || (_MSC_VER >= 1400))
	/*
	 * We allow the IPV6_V6ONLY sockopt on Windows only when building on VS 2005 or above,
	 * but we support only VS 2008 and later.
	 *
	 * RFC 3493 says that by default IPv6 sockets accept IPv4 connection requests.
	 * However, on Windows Vista and later the default is that they don't accept
	 * IPv4 connections.  Rather than #ifdef the defaults for all the platforms
	 * (and track them when they change their defaults), we don't rely on the default
	 * and always set this option appropriately.
	 *
	 * Note that it makes no sense to try to configure an IPv4 socket either to be
	 * IPV6-only or to be IPV6-and-IPV4, so we set it only on IPv6 sockets.
	 * Most dual-stack implementations seem to allow its use on IPv4 sockets,
	 * but it seems silly, so we don't do it.
	 *
	 * However, only dual-stack implementations provide this call, so we are #ifdef'ed
	 * on IPV6_V6ONLY.
	 */

	// don't try to set IPV6_V6ONLY on a v4 socket
	if( (type == AT_LISTEN) && (ai_family == AF_INET6) )
	{
	    const int val = GetPortParser().MustIPv6();
	    TRANSPORT_PRINTF( DEBUG_CONNECT, "NetTcpEndPoint setsockopt(IPV6_V6ONLY, %d)", val )

	    // this will fail harmlessly on Windows XP (but only if it passes the #ifdef test)
	    do_setsockopt( "NetTcpEndPoint", fd, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const SOCKOPT_T *>(&val), sizeof(val) );
	}
# endif

	MoreSocketSetup( fd, type, e );    // for subclasses to add extra behavior
}

// subclasses can do more setup here, if desired
void
NetTcpEndPoint::MoreSocketSetup( int fd, AddrType type, Error *e )
{
}

int
NetTcpEndPoint::CreateSocket(
	AddrType type,
	const NetAddrInfo &ai,
	int af_target,
	bool useAlternate,
	Error *e )
{
	int fd = -1;

	// iterate through the address list and use the first of the correct address family
	for( const addrinfo *aip = ai.begin(); aip != ai.end(); aip = aip->ai_next )
	{
	    if( useAlternate && (af_target == AF_UNSPEC) && (aip == ai.begin()) )
	    {
	        /*
	         * If we are to use an alternate address then try the first
	         * address that isn't the same family as the first entry.
	         * For RFC 3484 we try addresses in returned order,
	         * but only the first address (if any) of each family.
	         * If useAlternate was true then we must already have
	         * tried the first entry, so skip it now.
	         */
	        af_target = (aip->ai_family == AF_INET) ? AF_INET6 : AF_INET;
	        continue;
	    }

	    // don't discard any responses if no preference was given
	    if( (AF_UNSPEC != af_target) && (aip->ai_family != af_target) )
	        continue;

	    if( DEBUG_CONNECT )
	    {
	        StrBuf	addr;
	        NetUtils::GetAddress( aip->ai_family, aip->ai_addr, RAF_PORT, addr );
	        TRANSPORT_PRINTF( DEBUG_CONNECT, "NetTcpEndPoint try socket(%d, %d, %d, %s)",
	            aip->ai_family, aip->ai_socktype, aip->ai_protocol, addr.Text() );
	    }

	    fd = ::socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol);
	    if( fd == -1 )
	    {
	        // odd ... probably either EACCES or ENFILE
	        e->Net( "socket", "create" );

	        if( DEBUG_CONNECT )
	        {
	            StrBuf errnum;
	            Error::StrNetError( errnum );
	            TRANSPORT_PRINTF( DEBUG_CONNECT, "NetTcpEndPoint socket(%d, %d, %d) failed, error = %s",
	                aip->ai_family, aip->ai_socktype, aip->ai_protocol, errnum.Text() );
	        }
	        //continue;    // uncomment to try all addresses in this family
	    }
	    else
	    {
	        SetupSocket( fd, aip->ai_family, type, e );

		int result;		// the result of the connect or bind
		const char *op4;	// the name of the IPv4 operation ["connect" or "connect (IPv6)"]
		const char *op6;	// the name of the IPv6 operation ["bind" or "bind (IPv6)"]

	        switch( type )
	        {
	        case AT_CONNECT:
	            result = connect( fd, aip->ai_addr, aip->ai_addrlen );
		    op4 = "connect";
		    op6 = "connect (IPv6)";
	            break;
	        case AT_CHECK:
	        case AT_LISTEN:
	            result = bind( fd, aip->ai_addr, aip->ai_addrlen );
		    op4 = "bind";
		    op6 = "bind (IPv6)";
		    break;
		}

		// did the connect/bind fail?
		if( result == -1 )
		{
		    // preserve/restore the listen error across the GetAddress call
		    int err = Error::GetNetError();
		    StrBuf addrBuf;
		    NetUtils::GetAddress( aip->ai_family, aip->ai_addr, RAF_PORT, addrBuf );
		    Error::SetNetError(err);	// restore the "last" error

		    // use different error ids/args for IPv4 and IPv6 so the correct address arg is output
		    if( aip->ai_family == AF_INET6 )
			e->Net2( op6, addrBuf.Text() );
		    else
			e->Net( op4, addrBuf.Text() );
		    NET_CLOSE_SOCKET( fd );
		    return -1;
		}
	    }
	    break;
	}

	return fd;
}

/**
 * return true if we resolved the address, false otherwise
 */
bool
NetTcpEndPoint::GetAddrInfo( AddrType type, NetAddrInfo &ai, Error *e )
{
	StrBuf port = ai.Port();
	StrBuf host = ai.Host();
	StrBuf hostPort = "[";
	hostPort.Append( &host );
	hostPort.Append( "]:" );
	hostPort.Append( &port );

	e->Clear();
	if( port.IsNumeric() )
	{
	    int portAsInt = port.Atoi();
	    if( portAsInt < 0 || portAsInt >= 65536 )
	    {
	        e->Set( MsgRpc::TcpPortInvalid ) << port;
	        return false;
	    }
	}

	NetPortParser &pp = GetPortParser();
	int ai_family = pp.MustIPv4() ? AF_INET : (pp.MustIPv6() ? AF_INET6 : AF_UNSPEC);
	int ai_flags = kBaseHintsFlags | AI_ALL | (pp.WantIPv6() ? 0 : AI_ADDRCONFIG );

	ai.SetHintsFamily( ai_family );

	// set AI_PASSIVE to get IPv4 addresses mapped in IPv6 if host isn't specified
	if( type != AT_CONNECT )
	{
	    ai_flags |= AI_PASSIVE;

	    if( pp.MayIPv4() && pp.MayIPv6() )
	    {
	        ai_flags |= AI_V4MAPPED;
	    }
	}

	if( DEBUG_CONNECT )
	{
	    p4debug.printf( "NetTcpEndPoint::GetAddrInfo(port=%s, family=%d, flags=0x%x)\n",
	        hostPort.Text(), ai_family, ai_flags );
	}

	ai.SetHintsFlags( ai_flags );
	bool result = ai.GetInfo(e);   // resolve the host and service/port names (IPv4 and/or IPv6)
	if( !result )
	{
	    if( ai.GetStatus() == EAI_BADFLAGS )
	    {
	        /*
	         * Apparently our OS doesn't support AI_ALL or AI_V4MAPPED.
	         *     Maybe it's a single-stack server:
	         *         FreeBSD 7.0 or older
	         *         Windows XP or older
	         *         ...
	         * We'll try again without those flags.
	         */
	        ai_flags = kBaseHintsFlags | (pp.WantIPv6() ? 0 : AI_ADDRCONFIG )
	                   | ( (type == AT_CONNECT) ? 0 : AI_PASSIVE );
	        ai.SetHintsFlags( ai_flags );

		TRANSPORT_PRINTF( DEBUG_CONNECT,
			"NetTcpEndPoint::GetAddrInfo(port=%s, family=%d, flags=0x%x) [retry]",
		         hostPort.Text(), ai_family, ai_flags );

	        e->Clear();
	        result = ai.GetInfo(e);
	    }
	}

	if( !result )
	{
	    // job062842 -- allow local operation without a routable address
	    if( (ai.GetStatus() == EAI_NONAME) && (ai_flags & AI_ADDRCONFIG) )
	    {
	        /*
		 * Perhaps we don't have a routable address and are trying
		 * to run locally.  Try again without AI_ADDRCONFIG.
	         */
	        ai_flags &= ~AI_ADDRCONFIG;
	        ai.SetHintsFlags( ai_flags );

		TRANSPORT_PRINTF( DEBUG_CONNECT,
			"NetTcpEndPoint::GetAddrInfo(port=%s, family=%d, flags=0x%x) [retry-2]",
		         hostPort.Text(), ai_family, ai_flags );

	        e->Clear();
	        result = ai.GetInfo(e);
	    }
	}

	return result;
}

int
NetTcpEndPoint::BindOrConnect( AddrType type, struct Error *e )
{
	int fd = -1;

	// first setup an addrinfo from the parsed P4PORT {transport, host, port} tuple
	NetPortParser &pp = GetPortParser();
	StrBuf	host = pp.Host();
	StrBuf	port = pp.Port();

	// ListenCheck is passed "host" or "host:port"
	if( type == AT_CHECK )
	{
	    /*
	     * We're called with AT_CHECK only from ListenCheck(),
	     * which just wants to ensure that we can bind to the
	     * provided address.
	     */

	    // NetPortParser assumes "host:port" or just "port"
	    if( host.Length() == 0 )
	        host.Set( pp.HostPort() );

	    /*
	     * Don't bind to a specific port; we care about the host address
	     * for this check, not the port, and another service might be
	     * running on the licensed port.  We don't want to get an
	     * EADDRINUSE error just because the port is in use.
	     */
	    port.Set( "" );
	}
	else
	{
	    // check that the address specifies a port
	    // only if we're not checking the license
	    if( !pp.IsValid(e) )
		return -1;
	}

	NetAddrInfo ai( host, port );
	if( !GetAddrInfo(type, ai, e) )
	{
	    // give up if we didn't succeed in resolving the address
	    return -1;
	}

	// now create a socket of the appropriate address family

	/*
	 * In 2012.1 we interpret "tcp:" and "ssl:" to mean IPv4
	 * so that we don't break existing licenses and protects tables.
	 *
	 * Note that if the server specifies "tcp6:" or "ssl6:"
	 * then they are explicitly rejecting IPv4.  Because "tcp:" and "ssl:"
	 * means IPv4 then the user must use "tcp46:" or "tcp64:" (or the ssl
	 * equivalents) to specify "either IPv4 or IPv6".  The first of "4"
	 * or "6" is the preferred transport.
	 *
	 * IPv6 will also work for IPv4 connections (transport only until
	 * 2012.2), except for servers on single-stack OSes: Windows XP or
	 * FreeBSD 7.0 or earlier.
	 * Using "tcp64:" or "ssl64:" on those servers won't provide
	 * IPv4-mapped-in-IPv6.
	 */
	bool rfc3484 = pp.MustRfc3484();
	int af_target = rfc3484 ? AF_UNSPEC
	                        : (pp.PreferIPv6() ? AF_INET6 : AF_INET);

	/*
	 * RFC 3484 specifies a precedence for deciding the order of returned
	 * addresses to be used for connect/bind.  If we're following RFC 3484
	 * then we won't also apply our coarse IPv4-first or IPv6-first ordering.
	 * If we aren't following RFC 3484 then we'll still use the intra-family
	 * order provided by getaddrinfo.
	 *
	 * In 2012.1, NetPortParser.MustRfc3484() will always return false,
	 * but in 2013.1 and later it will return the value of "-v net.rfc3484"
	 * if the transport didn't specify an IPv4 or IPv6 preference (via
	 * transport prefix or numeric address).
	 */
	fd = CreateSocket( type, ai, af_target, false, e );
	if( fd == -1 )
	{
	    // didn't get a socket the first time; try again
	    if( rfc3484 )
	    {
	        // try the first address of the other family
	        fd = CreateSocket( type, ai, af_target, true, e );
	    }
	    else
	    {
	        /*
	         * If we tried and failed to get a socket of our preferred family,
	         * try to get a socket of an acceptable alternate family.
	         * The "else" clause handles IPv6-only hosts, as well as
	         * connect attempts to hosts that are reachable only via IPv6,
	         * when the preferred transport is IPv4 (currently IPv4-only is
	         * the default).
	         */
	        if( (af_target == AF_INET6) && pp.MayIPv4() ) // "tcp64:" or "ssl64:"
	            fd = CreateSocket( type, ai, AF_INET, false, e );
	        else if( (af_target == AF_INET) && pp.MayIPv6() ) // "tcp46:" or "ssl46:"
	            fd = CreateSocket( type, ai, AF_INET6, false, e );
	    }
	}

	if( fd == -1 )
	{
	    // failed to get a socket and/or bind/connect
	    return -1;
	}

	// finally, setup the socket options and return it
	e->Clear();    // because the first CreateSocket() might have set an error

	return fd;
}

/*
 * NetTcpEndPoint
 */

NetTcpEndPoint::NetTcpEndPoint( Error *e )
{
	s = -1;
	/*
	 * Initialize as "false" so client side of connection:
	 *   !isFromClient == isToServer
	 * Will be set to true if "listen" is invoked.
	 */
	isAccepted = false;

	int err = NetUtils::InitNetwork();
	if( err )
	{
	    StrNum errnum( err );

	    e->Net( "Network initialization failure", errnum.Text() );
	}
}

NetTcpEndPoint::~NetTcpEndPoint()
{
	Unlisten();
	NetUtils::CleanupNetwork();
}

void
NetTcpEndPoint::Listen( Error *e )
{
	const int backlog = p4tunable.Get( P4TUNE_NET_BACKLOG );
	isAccepted = true;

	if( ( s = BindOrConnect( AT_LISTEN, e ) ) < 0 )
	{
	    e->Set( MsgRpc::TcpListen ) << GetPortParser().HostPort();
	    return;
	}

	// Now listen

	if( listen( s, backlog ) < 0 )
	{
	    e->Net( "listen", GetPortParser().String().Text() );
	    StrBuf listenAddress;
	    GetListenAddress( s, RAF_PORT, listenAddress );
	    NET_CLOSE_SOCKET( s );
	    e->Set( MsgRpc::TcpListen ) << listenAddress;
	}

# ifdef SIGPIPE
	signal( SIGPIPE, SIG_IGN );
# endif

	if( DEBUG_CONNECT )
	{
	    StrBuf listenAddress;
	    GetListenAddress( s, RAF_PORT, listenAddress );
	    TRANSPORT_PRINTF( DEBUG_CONNECT, "NetTcpEndPoint %s listening",
	        listenAddress.Text() );
	}
}

/**
 * Return the first addrinfo pointer of the desired family;
 * return NULL if none.
 */
const addrinfo *
NetTcpEndPoint::GetMatchingAddrInfo(
	const NetAddrInfo &ai,
	int af_target,
	bool useAlternate)
{
	// iterate through the address list and use the first of the correct address family
	for( const addrinfo *aip = ai.begin(); aip != ai.end(); aip = aip->ai_next )
	{
	    if( useAlternate && (af_target == AF_UNSPEC) && (aip == ai.begin()) )
	    {
	        /*
	         * If we are to use an alternate address then try the first
	         * address that isn't the same family as the first entry.
	         * For RFC 3484 we try addresses in returned order,
	         * but only the first address (if any) of each family.
	         * If useAlternate was true then we must already have
	         * tried the first entry, so skip it now.
	         */
	        af_target = (aip->ai_family == AF_INET) ? AF_INET6 : AF_INET;
	        continue;
	    }

	    if( (AF_UNSPEC == af_target) || (aip->ai_family == af_target) )
	        return aip;
	}

	return NULL;
}

/**
 * "requestedPort" is the P4PORT (or the command-line "-p") string.
 * The "this" NetTcpEndPoint object was constructed using the
 * address[:port] in the license file.
 */
int
NetTcpEndPoint::CheaterCheck( const char *requestedPort )
{
	Error e;

	NetPortParser &ppLicensed = GetPortParser();
	StrBuf	ppLicHost = ppLicensed.Host();
	StrBuf	ppLicPort = ppLicensed.Port();

	NetPortParser ppRequested( requestedPort );
	if( !ppRequested.IsValid( &e ) )
	    return 1;

	// no host? then we were created with just a host, not a port
	if( ppLicHost.Length() == 0 )
	{
	    ppLicHost.Set( ppLicensed.Port() );
	    ppLicPort.Set( "" );
	}

	// We already called BindOrConnect() once from ListenCheck() and there
	// wasn't an error then, so there shouldn't be one now.

	NetAddrInfo ai( ppLicHost, ppLicPort );
	// see the comments in BindOrConnect about RFC 3484 compliance
	bool rfc3484 = ppLicensed.MustRfc3484();
	int af_target = rfc3484 ? AF_UNSPEC
	                        : (ppLicensed.PreferIPv6() ? AF_INET6 : AF_INET);
	if( !GetAddrInfo(AT_CHECK, ai, &e) )
	{
	    return 1;
	}

	// get the addrinfo that we would use if we were to listen
	const addrinfo *aip = GetMatchingAddrInfo( ai, af_target, false );
	if( aip == NULL )
	{
	    if( rfc3484 )
	    {
	        // try the first address of the other family
	        // pointless, really, but matches the BindOrConnect logic
	        aip = GetMatchingAddrInfo( ai, AF_UNSPEC, true );
	    }
	    else
	    {
	        if( (af_target == AF_INET6) && ppLicensed.MayIPv4() )
	            aip = GetMatchingAddrInfo( ai, AF_INET, false );
	        else if( (af_target == AF_INET) && ppLicensed.MayIPv6() )
	            aip = GetMatchingAddrInfo( ai, AF_INET6, false );
	    }
	}
	if( aip == NULL )
	{
	    // no acceptable address
	    return 1;
	}

	// Are they trying to start the server on a port other than
	// the licensed one ???

	int myPort = NetUtils::GetInPort( aip->ai_addr );
	if( myPort == -1 )
	{
	    // bad address family -- shouldn't happen
	    return 1;
	}

	// If the license file has an ip address but no port number we
	// must still allow this.

	unsigned short requestedPortNum = ppRequested.PortNum();
	if( (myPort == 0) || (myPort == requestedPortNum) )
	{
	    return 0;
	}

	return 1;
}

/*
 * Check whether a hostname or IP is loopback
 * - return true if any addresses for the host is local
 *	1) p4portstr is a P4PORT string
 *	2) empty strings, and those with a transport of "rsh:" or "jsh:" are local
 *	3) empty host portion is also local
 *	4) numeric strings (IPv4 or IPv6, optionally surrounded with [...])
 *	   are parsed and checked by NetUtils::IsLocalAddress()
 *	5) otherwise we resolve the name to a set of addresses;
 *	   if any of them are local, return true
 * - there is a lot of code here that's duplicated from BindOrConnect()
 *   and CreateSocket(), so I'll look at refactoring this later.
 * [static]
 */
bool
NetTcpEndPoint::IsLocalHost( const char *p4portstr, AddrType type )
{
	// empty host string means localhost
	if( *p4portstr == '\0' )
	    return true;

	NetPortParser	pp(p4portstr);

	// "rsh:" and "jsh:" are always local
	if( pp.MustRSH() || pp.MustJSH() )
	    return true;

	// empty host portion means localhost
	if( pp.Host().Length() == 0 )
	    return true;

	// don't bother trying to resolve a numeric address
	// note: dns names may start with a digit (see RFC 1123)
	char	ch = pp.Host().Text()[0];
	if( ch == ':' )
	    return NetUtils::IsLocalAddress( pp.Host().Text() );
	char	lastch = pp.Host().Text()[pp.Host().Length()-1];
	if( (ch == '[') && (lastch == ']') )
	{
	    if( pp.Host().Text()[1] == ':' )
		return NetUtils::IsLocalAddress( pp.Host().Text() );
	}

	// ok, let's try resolving the name

	NetAddrInfo	ai( pp.Host(), pp.Port() );
	Error		e;
	bool		useAlternate = false;
	int		af_target = AF_UNSPEC;

	int ai_family = pp.MustIPv4() ? AF_INET : (pp.MustIPv6() ? AF_INET6 : AF_UNSPEC);
	int ai_flags = kBaseHintsFlags | AI_ALL | (pp.WantIPv6() ? 0 : AI_ADDRCONFIG );

	ai.SetHintsFamily( ai_family );

	// set AI_PASSIVE to get IPv4 addresses mapped in IPv6 if host isn't specified
	if( type != AT_CONNECT )
	{
	    ai_flags |= AI_PASSIVE;

	    if( pp.MayIPv4() && pp.MayIPv6() )
	    {
	        ai_flags |= AI_V4MAPPED;
	    }
	}

	if( DEBUG_CONNECT )
	{
	    p4debug.printf( "NetTcpEndPoint::IsLocalHost(port=%s, family=%d, flags=0x%x)\n",
	        pp.Host().Text(), ai_family, ai_flags );
	}

	ai.SetHintsFlags( ai_flags );

	// This will init WinSock, if required, and clean it up too.
	NetTcpEndPoint endpoint( &e );

	bool result = ai.GetInfo(&e);   // resolve the host and service/port names (IPv4 and/or IPv6)
	if( !result )
	{
	    if( ai.GetStatus() == EAI_BADFLAGS )
	    {
	        /*
	         * Apparently our OS doesn't support AI_ALL or AI_V4MAPPED.
	         *     Maybe it's a single-stack server:
	         *         FreeBSD 7.0 or older
	         *         Windows XP or older
	         *         ...
	         * We'll try again without those flags.
	         */
	        ai_flags = kBaseHintsFlags | (pp.WantIPv6() ? 0 : AI_ADDRCONFIG )
	                   | ( (type == AT_CONNECT) ? 0 : AI_PASSIVE );
	        ai.SetHintsFlags( ai_flags );

		if( DEBUG_CONNECT )
		{
		    p4debug.printf(
			"NetTcpEndPoint::IsLocalHost(port=%s, family=%d, flags=0x%x) [retry]\n",
		         pp.Host().Text(), ai_family, ai_flags );
		}

	        e.Clear();
	        result = ai.GetInfo(&e);
	    }
	}

	if( !result )
	{
	    // job062842 -- allow local operation without a routable address
	    if( (ai.GetStatus() == EAI_NONAME) && (ai_flags & AI_ADDRCONFIG) )
	    {
	        /*
		 * Perhaps we don't have a routable address and are trying
		 * to run locally.  Try again without AI_ADDRCONFIG.
	         */
	        ai_flags &= ~AI_ADDRCONFIG;
	        ai.SetHintsFlags( ai_flags );

		if( DEBUG_CONNECT )
		{
		    p4debug.printf(
			"NetTcpEndPoint::IsLocalHost(port=%s, family=%d, flags=0x%x) [retry-2]\n",
		         pp.Host().Text(), ai_family, ai_flags );
		}

	        e.Clear();
	        result = ai.GetInfo(&e);
	    }
	}

	if( result )
	{
	    // iterate through the address list and use the first of the correct address family
	    for( const addrinfo *aip = ai.begin(); aip != ai.end(); aip = aip->ai_next )
	    {
		if( useAlternate && (af_target == AF_UNSPEC) && (aip == ai.begin()) )
		{
		    /*
		     * If we are to use an alternate address then try the first
		     * address that isn't the same family as the first entry.
		     * For RFC 3484 we try addresses in returned order,
		     * but only the first address (if any) of each family.
		     * If useAlternate was true then we must already have
		     * tried the first entry, so skip it now.
		     */
		    af_target = (aip->ai_family == AF_INET) ? AF_INET6 : AF_INET;
		    continue;
		}

		// don't discard any responses if no preference was given
		if( (AF_UNSPEC != af_target) && (aip->ai_family != af_target) )
		    continue;

		StrBuf	printableAddress;
		printableAddress.Clear();
		printableAddress.Alloc( P4_INET6_ADDRSTRLEN );
		printableAddress.SetLength(0);
		printableAddress.Terminate();
		const char	*buf = printableAddress.Text();

		NetUtils::GetAddress( aip->ai_family, aip->ai_addr, 0, printableAddress );
		result = NetUtils::IsLocalAddress(buf);
		if( DEBUG_CONNECT )
		{
		    p4debug.printf(
			"NetTcpEndPoint::IsLocalAddress(%s) = %s\n",
			buf, (result ? "true" : "false") );
		}
		if( result )
		    return true;
	    }
	}

	return false;
}

void
NetTcpEndPoint::ListenCheck( Error *e )
{
	int fd = BindOrConnect( AT_CHECK, e );
	if( fd >= 0 )
	{
	    close( fd );
	}
}

void
NetTcpEndPoint::GetListenAddress( int s, int raf_flags, StrBuf &listenAddress )
{
	struct sockaddr_storage addr;
	struct sockaddr *saddrp = reinterpret_cast<struct sockaddr *>(&addr);
	TYPE_SOCKLEN addrlen = sizeof addr;

	if( getsockname( s, saddrp, &addrlen ) < 0 || addrlen > sizeof addr )
	{
	    listenAddress.Set( "unknown" );
	}
	else
	{
	    NetUtils::GetAddress( addr.ss_family, saddrp, raf_flags, listenAddress );
	}
}

StrPtr *
NetTcpEndPoint::GetHost()
{
	ipAddr = GetPortParser().Host();

	return &ipAddr;
}

/*
 * job063713 -- ensure that IPv6 literal addresses are in our
 * standard form, per RFC 3986 : surrounded with brackets
 * - matches the behavior of NetUtils::GetAddress()
 */
StrBuf
NetTcpEndPoint::GetPrintableHost()
{
	StrPtr	host = GetPortParser().Host();

	if( (host[0] != '[') && NetUtils::IsIpV6Address(host.Text()) )
	{
	    StrBuf	tmp = "[";
	    tmp.Append( host.Text() );
	    tmp.Append( "]" );

	    return tmp;
	}

	return host;
}

void
NetTcpEndPoint::Unlisten()
{
	NET_CLOSE_SOCKET( s );
}

NetTransport *
NetTcpEndPoint::Accept( KeepAlive *keep, Error *e )
{
	TYPE_SOCKLEN lpeer;
	struct sockaddr_storage peer;
	int t, rd, wr;
	NetTcpSelector *selector = NULL;

	TRANSPORT_PRINTF( DEBUG_CONNECT, "NetTcpEndpoint accept on %d", s );

	lpeer = sizeof peer;

	if( keep )
		selector = new NetTcpSelector( s );
	rd = wr = 0;

	// Loop accepting, as it gets interrupted (by SIGCHILD) on
	// some platforms (MachTen, but not FreeBSD).

	for( ;; )
	{
		if( keep )
		{
			if( !keep->IsAlive() )
			{
				e->Set( MsgRpc::Break );
				delete selector;
				return 0;
			}
			rd = 1;
			int sr;
			if( ( sr = selector->Select( rd, wr, 500 ) ) == 0 )
				continue;
			if( sr == -1 )
			{
				e->Sys( "select", "accept" );
				delete selector;
				return 0;
			}
		}

		if( ( t = accept( s, reinterpret_cast<struct sockaddr *>(&peer), &lpeer ) ) < 0 )
		{
			if( errno != EINTR )
			{
				e->Net( "accept", "socket" );
				e->Set( MsgRpc::TcpAccept );;
				delete selector;
				return 0;
			}
		}
		else break;
	}

# ifdef F_SETFD
	// close on exec
	// so p4web's launched processes don't get our socket
	fcntl( t, F_SETFD, 1 );
# endif

	delete selector;

	NetTcpTransport *transport = new NetTcpTransport( t, true );
	if( transport )
	    transport->SetPortParser(GetPortParser());
	return transport;
}

NetTransport *
NetTcpEndPoint::Connect( Error *e )
{
	int t;

	// Set up addresses
	if( ( t = BindOrConnect( AT_CONNECT, e ) ) < 0 )
	{
	    e->Set( MsgRpc::TcpConnect ) << GetPortParser().HostPort();
	    return 0;
	}

	TRANSPORT_PRINTF( DEBUG_CONNECT, "NetTcpEndpoint connect on %d", t );

# ifdef SIGPIPE
	signal( SIGPIPE, SIG_IGN );
# endif

	NetTcpTransport *transport = new NetTcpTransport( t, false );
	if( transport )
	    transport->SetPortParser(GetPortParser());
	return transport;
}

# if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_DARWIN)
// returns -1 or error or a valid socket fd on success
socketfd_t
NetTcpEndPoint::OpenUnixSocket( const StrBuf &sockName, Error &e )
{
	//NET_ENTER();
	struct sockaddr_un sockAddr;
	socketfd_t        sock = -1;
	int count = 1;
	StrBuf buf;

	//TRANSPORT_PRINTF( DEBUG_CONNECT, "OpenUnixSocket socket filename is: \"%s\"",
	//        sockName.Text() );
	// Verify that we have gotten the filename for the socket
	if ( sockName.Length() == 0 )
	{
	    e.Set(MsgRpc::UnixDomainOpen) << "open" << "invalid filename";
	    //NET_DUMP_ERROR( e );
	    return -1;
	}

	sock = socket( PF_UNIX, SOCK_STREAM, 0 );
	if( sock < 0 )
	{
	    StrBuf buf;
	    Error::StrError(buf);
	    e.Set(MsgRpc::UnixDomainOpen) << "socket" << buf;
	    //NET_DUMP_ERROR( e );
	    return -1;
	}

	memset( &sockAddr, 0, sizeof(struct sockaddr_un) );

	sockAddr.sun_family = AF_UNIX;
	memcpy( sockAddr.sun_path,
	            sockName.Text(),
	            sockName.Length() );
	sockAddr.sun_path[ sockName.Length() ] = '\0';

	//TRANSPORT_PRINTF( DEBUG_CONNECT, "OpenUnixSocket socket filename is: \"%s\"",
	//        sockName.Text() );
	while( connect( sock,
	        (struct sockaddr *) &sockAddr,
	        sizeof(struct sockaddr_un) ) != 0 && (count++ < 10))
	{
	    if( errno == ECONNREFUSED || errno == ENOENT )
	    {
	        sleep(1);
	        continue;
	    }
	    Error::StrError(buf);
	    e.Set(MsgRpc::UnixDomainOpen) << "connect" << buf;
	    //NET_DUMP_ERROR( e );
	    return -1;
	}
	if( count >= 10 )
	{
	    Error::StrError(buf);
	    e.Set(MsgRpc::UnixDomainOpen) << "connect" << buf;
	    //NET_DUMP_ERROR( e );
	    return -1;
	}

	return sock;
}
# endif // OS_LINUX || OS_MACOSX || OS_DARWIN

# Change User Description Committed
#1 19472 Liz Lam Initial add of the 2016.1 p4/p4api source code.