Beispiel #1
0
// Cribbed from directConnectToSockAddr().  Could probably just call it
// between SetSyscalls(), but I don't know that SO_KEEPALIVE doesn't matter.
int open_tcp_stream( const condor_sockaddr & sa ) {
	int scm = SetSyscalls( SYS_LOCAL | SYS_UNMAPPED );

	int fd = socket( sa.get_aftype(), SOCK_STREAM, 0 );
	assert( fd != -1 );

	int rv = _condor_local_bind( TRUE, fd );
	if( rv != TRUE ) {
		close( fd );
		SetSyscalls( scm );
		return -1;
	}

	// condor_connect ends up pulling in param() via ip6_get_scope_id(),
	// which we can't allow, since this function is linked into standard
	// universe jobs.
	// rv = condor_connect( fd, sa );
	rv = connect( fd, sa.to_sockaddr(), sa.get_socklen() );
	if( rv != 0 ) {
		dprintf( D_ALWAYS, "condor_connect() failed - errno = %d (rv %d)\n", errno, rv );
		close( fd );
		SetSyscalls( scm );
		return -1;
	}

	SetSyscalls( scm );
	return fd;
}
Beispiel #2
0
MyString get_hostname(const condor_sockaddr& addr) {
	MyString ret;
	if (nodns_enabled())
		return convert_ipaddr_to_hostname(addr);

	condor_sockaddr targ_addr;

	// just like sin_to_string(), if given address is 0.0.0.0 or equivalent,
	// it changes to local IP address.
	if (addr.is_addr_any())
		targ_addr = get_local_ipaddr(addr.get_protocol());
	else
		targ_addr = addr;

	int e;
	char hostname[NI_MAXHOST];

	// if given address is link-local IPv6 address, it will have %NICname
	// at the end of string
	// we would like to avoid it
	if (targ_addr.is_ipv6())
		targ_addr.set_scope_id(0);

	e = condor_getnameinfo(targ_addr, hostname, sizeof(hostname), NULL, 0, 0);
	if (e)
		return ret;

	ret = hostname;
	return ret;
}
Beispiel #3
0
condor_sockaddr get_local_ipaddr(condor_protocol proto)
{
	init_local_hostname();
	if ((proto == CP_IPV4) && local_ipv4addr.is_ipv4()) { return local_ipv4addr; }
	if ((proto == CP_IPV6) && local_ipv6addr.is_ipv6()) { return local_ipv6addr; }
	return local_ipaddr;
}
Beispiel #4
0
bool condor_sockaddr::compare_address(const condor_sockaddr& addr) const
{
	if (is_ipv4()) {
		if (!addr.is_ipv4())
			return false;
		return v4.sin_addr.s_addr == addr.v4.sin_addr.s_addr;
	} else if (is_ipv6()) {
		if (!addr.is_ipv6())
			return false;
		return memcmp((const void*)&v6.sin6_addr,
					  (const void*)&addr.v6.sin6_addr,
					  sizeof(in6_addr)) == 0;
	}
	return false;
}
Beispiel #5
0
MyString convert_ipaddr_to_hostname(const condor_sockaddr& addr)
{
	MyString ret;
	MyString default_domain;
	if (!param(default_domain, "DEFAULT_DOMAIN_NAME")) {
		dprintf(D_HOSTNAME,
				"NO_DNS: DEFAULT_DOMAIN_NAME must be defined in your "
				"top-level config file\n");
		return ret;
	}

	ret = addr.to_ip_string();
	for (int i = 0; i < ret.Length(); ++i) {
		if (ret[i] == '.' || ret[i] == ':')
			ret.setChar(i, '-');
	}
	ret += ".";
	ret += default_domain;

	// Hostnames can't begin with -, as per RFC 1123
	// ipv6 zero-compression could cause this, esp. for the loopback addr
	if (ret[0] == '-') {
		ret = "0" + ret;
	}

	return ret;
}
Beispiel #6
0
std::vector<MyString> get_hostname_with_alias(const condor_sockaddr& addr)
{
	std::vector<MyString> prelim_ret;
	std::vector<MyString> actual_ret;

	MyString hostname = get_hostname(addr);
	if (hostname.IsEmpty())
		return prelim_ret;

	// we now start to construct a list (prelim_ret) of the hostname and all
	// the aliases.  first the name itself.
	prelim_ret.push_back(hostname);

	if (nodns_enabled())
		// don't need to verify this... the string is actually an IP here
		return prelim_ret; // no need to call further DNS functions.

	// now, add the aliases

	hostent* ent;
		//int aftype = addr.get_aftype();
		//ent = gethostbyname2(hostname.Value(), addr.get_aftype());

		// really should call gethostbyname2() however most platforms do not
		// support. (Solaris, HP-UX, IRIX)
		// complete DNS aliases can be only obtained by gethostbyname.
		// however, what happens if we call it in IPv6-only system?
		// can we get DNS aliases for the hostname that only contains
		// IPv6 addresses?
	ent = gethostbyname(hostname.Value());

	if (ent) {
		char** alias = ent->h_aliases;
		for (; *alias; ++alias) {
			prelim_ret.push_back(MyString(*alias));
		}
	}

	// WARNING! there is a reason this is implimented as two separate loops,
	// so please don't try to combine them.
	//
	// calling verify_name_has_ip() will potentially overwrite static data that
	// is referred to by ent->h_aliases (man 3 gethostbyname, see notes).  so
	// first, we push the name and then all aliases into the MyString vector
	// prelim_ret, and then verify them in the following loop.

	for (unsigned int i = 0; i < prelim_ret.size(); i++) {
		if(verify_name_has_ip(prelim_ret[i], addr)) {
			actual_ret.push_back(prelim_ret[i]);
		} else {
			dprintf(D_ALWAYS, "WARNING: forward resolution of %s doesn't match %s!\n",
					prelim_ret[i].Value(), addr.to_ip_string().Value());
		}
	}

	return actual_ret;
}
Beispiel #7
0
bool verify_name_has_ip(MyString name, condor_sockaddr addr){
	std::vector<condor_sockaddr> addrs;
	bool found = false;

	addrs = resolve_hostname(name);
	dprintf(D_FULLDEBUG, "IPVERIFY: checking %s against %s\n", name.Value(), addr.to_ip_string().Value());
	for(unsigned int i = 0; i < addrs.size(); i++) {
		// compare MyStrings
		// addr.to_ip_string
		if(addrs[i].to_ip_string() == addr.to_ip_string()) {
			dprintf(D_FULLDEBUG, "IPVERIFY: matched %s to %s\n", addrs[i].to_ip_string().Value(), addr.to_ip_string().Value());
			found = true;
		} else {
			dprintf(D_FULLDEBUG, "IPVERIFY: comparing %s to %s\n", addrs[i].to_ip_string().Value(), addr.to_ip_string().Value());
		}
	}
	dprintf(D_FULLDEBUG, "IPVERIFY: ip found is %i\n", found);

	return found;
}
Beispiel #8
0
static void replace_higher_scoring_addr(const char * reason, condor_sockaddr & current, int & current_score,
	const condor_sockaddr & potential, int potential_score) {
	const char * result = "skipped for low score";
	if(current_score < potential_score) {
		current = potential;
		current_score = potential_score;
		result = "new winner";
	}

	dprintf(D_HOSTNAME, "%s: %s (score %d) %s\n",
		reason,
		potential.to_ip_string().Value(),
		potential_score,
		result);
}
Beispiel #9
0
bool init_local_hostname_impl()
{
	bool local_hostname_initialized = false;
	if (param(local_hostname, "NETWORK_HOSTNAME")) {
		local_hostname_initialized = true;
		dprintf(D_HOSTNAME, "NETWORK_HOSTNAME says we are %s\n", local_hostname.Value());
	}

	if( ! local_hostname_initialized ) {
		// [TODO:IPV6] condor_gethostname is not IPv6 safe. Reimplement it.
		char hostname[MAXHOSTNAMELEN];
		int ret = condor_gethostname(hostname, sizeof(hostname));
		if (ret) {
			dprintf(D_ALWAYS, "condor_gethostname() failed. Cannot initialize "
					"local hostname, ip address, FQDN.\n");
			return false;
		}
		local_hostname = hostname;
	}

	MyString test_hostname = local_hostname;

	bool local_ipaddr_initialized = false;
	bool local_ipv4addr_initialized = false;
	bool local_ipv6addr_initialized = false;

	MyString network_interface;
	if (param(network_interface, "NETWORK_INTERFACE")) {
		if(local_ipaddr_initialized == false &&
			local_ipaddr.from_ip_string(network_interface)) {
			local_ipaddr_initialized = true;
			if(local_ipaddr.is_ipv4()) { 
				local_ipv4addr = local_ipaddr;
				local_ipv4addr_initialized = true;
			}
			if(local_ipaddr.is_ipv6()) { 
				local_ipv6addr = local_ipaddr;
				local_ipv6addr_initialized = true;
			}
		}
	}

	if( ! local_ipaddr_initialized ) {
		std::string ipv4, ipv6, ipbest;
		if( network_interface_to_ip("NETWORK_INTERFACE", network_interface.Value(), ipv4, ipv6, ipbest, NULL)) {
			ASSERT(local_ipaddr.from_ip_string(ipbest));
			// If this fails, network_interface_to_ip returns something invalid.
			local_ipaddr_initialized = true;
		} else {
			dprintf(D_ALWAYS, "Unable to identify IP address from interfaces.  None match NETWORK_INTERFACE=%s. Problems are likely.\n", network_interface.Value());
		}
		if((!ipv4.empty()) && local_ipv4addr.from_ip_string(ipv4)) {
			local_ipv4addr_initialized = true;
			ASSERT(local_ipv4addr.is_ipv4());
		}
		if((!ipv6.empty()) && local_ipv6addr.from_ip_string(ipv6)) {
			local_ipv6addr_initialized = true;
			ASSERT(local_ipv6addr.is_ipv6());
		}
	}

	bool local_fqdn_initialized = false;
	if (nodns_enabled()) {
			// condor_gethostname() returns a hostname with
			// DEFAULT_DOMAIN_NAME. Thus, it is always fqdn
		local_fqdn = local_hostname;
		local_fqdn_initialized = true;
		if (!local_ipaddr_initialized) {
			local_ipaddr = convert_hostname_to_ipaddr(local_hostname);
			local_ipaddr_initialized = true;
		}
	}

	addrinfo_iterator ai;

	if( ! nodns_enabled() ) {
		const int MAX_TRIES = 20;
		const int SLEEP_DUR = 3;
		bool gai_success = false;
		for(int try_count = 1; true; try_count++) {
			addrinfo hint = get_default_hint();
			hint.ai_family = AF_UNSPEC;
			int ret = ipv6_getaddrinfo(test_hostname.Value(), NULL, ai, hint);
			if(ret == 0) { gai_success = true; break; }
			if(ret != EAI_AGAIN ) {
				dprintf(D_ALWAYS, "init_local_hostname_impl: ipv6_getaddrinfo() could not look up '%s': %s (%d).  Error is not recoverable; giving up.  Problems are likely.\n", test_hostname.Value(), gai_strerror(ret), ret );
				gai_success = false;
				break;
			}

			dprintf(D_ALWAYS, "init_local_hostname_impl: ipv6_getaddrinfo() returned EAI_AGAIN for '%s'.  Will try again after sleeping %d seconds (try %d of %d).\n", test_hostname.Value(), SLEEP_DUR, try_count + 1, MAX_TRIES );
			if(try_count == MAX_TRIES) {
				dprintf(D_ALWAYS, "init_local_hostname_impl: ipv6_getaddrinfo() never succeeded. Giving up. Problems are likely\n");
				break;
			}
			sleep(SLEEP_DUR);
		}

		if(gai_success) {
			int local_hostname_desireability = 0;
#ifdef TEST_DNS_TODO
			int local_ipaddr_desireability = 0;
			int local_ipv4addr_desireability = 0;
			int local_ipv6addr_desireability = 0;
#endif
			while (addrinfo* info = ai.next()) {
				// TODO: the only time ai_canonname should be set is the first
				// record. Why are we testing its desirability?
				const char* name = info->ai_canonname;
				if (!name)
					continue;
				condor_sockaddr addr(info->ai_addr);

				int desireability = addr.desirability();

				const char * result = "skipped for low score";
				if(desireability > local_hostname_desireability) {
					result = "new winner";
					dprintf(D_HOSTNAME, "   I like it.\n");
					local_hostname_desireability = desireability;

					const char* dotpos = strchr(name, '.');
					if (dotpos) { // consider it as a FQDN
						local_fqdn = name;
						local_hostname = local_fqdn.Substr(0, dotpos-name-1);
					} else {
						local_hostname = name;
						local_fqdn = local_hostname;
						MyString default_domain;
						if (param(default_domain, "DEFAULT_DOMAIN_NAME")) {
							if (default_domain[0] != '.')
								local_fqdn += ".";
							local_fqdn += default_domain;
						}
					}
				}
				dprintf(D_HOSTNAME, "hostname: %s (score %d) %s\n", name, desireability, result);

#ifdef TEST_DNS_TODO
				// Resist urge to set local_ip*addr_initialized=true,
				// We want to repeatedly retest this looking for 
				// better results.
				if (!local_ipaddr_initialized) {
					replace_higher_scoring_addr("IP", 
						local_ipaddr, local_ipaddr_desireability, 
						addr, desireability);
				}

				if (addr.is_ipv4() && !local_ipv4addr_initialized) {
					replace_higher_scoring_addr("IPv4", 
						local_ipv4addr, local_ipv4addr_desireability, 
						addr, desireability);
				}

				if (addr.is_ipv6() && !local_ipv6addr_initialized) {
					replace_higher_scoring_addr("IPv6", 
						local_ipv6addr, local_ipv6addr_desireability, 
						addr, desireability);
				}
#else
	// Make Fedora quit complaining.
	if( local_ipv4addr_initialized && local_ipv6addr_initialized && local_fqdn_initialized ) {
		local_ipv4addr_initialized = false;
		local_ipv6addr_initialized = false;
		local_fqdn_initialized = false;
	}
#endif
			}
		}

	}

	return true;
}
Beispiel #10
0
void reset_local_hostname() {
	if( ! init_local_hostname_impl() ) {
		dprintf( D_ALWAYS, "Something went wrong identifying my hostname and IP address.\n" );
		hostname_initialized = false;
	} else {
		dprintf( D_HOSTNAME, "I am: hostname: %s, fully qualified doman name: %s, IP: %s, IPv4: %s, IPv6: %s\n", local_hostname.Value(), local_fqdn.Value(), local_ipaddr.to_ip_string().Value(), local_ipv4addr.to_ip_string().Value(), local_ipv6addr.to_ip_string().Value() );
		hostname_initialized = true;
	}
}
Beispiel #11
0
/*
 *	@returns: the number of bytes sent, if succeeds
 *	          -1, if fails
 */
int _condorOutMsg::sendMsg(const int sock,
                           const condor_sockaddr& who,
                           _condorMsgID msgID,
                           unsigned char * mac)
{
	_condorPacket* tempPkt;
	int seqNo = 0, msgLen = 0, sent;
	int total = 0;
    unsigned char * md = mac;
    //char str[10000];

	if(headPacket->empty()) // empty message
		return 0;
   
	while(headPacket != lastPacket) {
		tempPkt    = headPacket;
		headPacket = headPacket->next;
		tempPkt->makeHeader(false, seqNo++, msgID, md);
		msgLen    += tempPkt->length;

		
		sent = condor_sendto(sock, tempPkt->dataGram,
		              tempPkt->length + SAFE_MSG_HEADER_SIZE,
                      0, who);

		if(sent != tempPkt->length + SAFE_MSG_HEADER_SIZE) {
			dprintf(D_ALWAYS, "sendMsg:sendto failed - errno: %d\n", errno);
			headPacket = tempPkt;
			clearMsg();
			return -1;
		}
        //int i;
        //str[0] = 0;
        //for (i=0; i<tempPkt->length + SAFE_MSG_HEADER_SIZE; i++) {
        //    sprintf(&str[strlen(str)], "%02x,", tempPkt->dataGram[i]);
        //}
        //dprintf(D_NETWORK, "--->packet [%d bytes]: %s\n", sent, str);

		dprintf( D_NETWORK, "SEND [%d] %s ", sent, sock_to_string(sock) );
		dprintf( D_NETWORK|D_NOHEADER, "%s\n",
				 who.to_sinful().Value());
		total += sent;
		delete tempPkt;
        md = 0;
	}

	// headPacket = lastPacket
    if(seqNo == 0) { // a short message
		msgLen = lastPacket->length;
        lastPacket->makeHeader(true, 0, msgID, md);
			// Short messages are sent without initial "magic" header,
			// because we don't need to specify sequence number,
			// and presumably for backwards compatibility with ancient
			// versions of Condor.  The crypto header may still
			// be there, since that is in the buffer starting at
			// the position pointed to by "data".
		sent = condor_sendto(sock, lastPacket->data, lastPacket->length,
							 0, who);
		if(sent != lastPacket->length) {
			dprintf( D_ALWAYS, 
				 "SafeMsg: sending small msg failed. errno: %d\n",
				 errno );
			headPacket->reset();
			return -1;
		}
        //str[0] = 0;
        //for (i=0; i<lastPacket->length + SAFE_MSG_HEADER_SIZE; i++) {
        //    sprintf(&str[strlen(str)], "%02x,", lastPacket->dataGram[i]);
        //}
        //dprintf(D_NETWORK, "--->packet [%d bytes]: %s\n", sent, str);
		dprintf( D_NETWORK, "SEND [%d] %s ", sent, sock_to_string(sock) );
		dprintf( D_NETWORK|D_NOHEADER, "%s\n", who.to_sinful().Value());
		total = sent;
    }
    else {
        lastPacket->makeHeader(true, seqNo, msgID, md);
        msgLen += lastPacket->length;
        sent = condor_sendto(sock, lastPacket->dataGram,
                      lastPacket->length + SAFE_MSG_HEADER_SIZE,
                      0, who);
        if(sent != lastPacket->length + SAFE_MSG_HEADER_SIZE) {
            dprintf( D_ALWAYS, "SafeMsg: sending last packet failed. errno: %d\n", errno );
            headPacket->reset();
            return -1;
        }
        //str[0] = 0;
        //for (i=0; i<lastPacket->length + SAFE_MSG_HEADER_SIZE; i++) {
        //    sprintf(&str[strlen(str)], "%02x,", lastPacket->dataGram[i]);
        //}
        //dprintf(D_NETWORK, "--->packet [%d bytes]: %s\n", sent, str);
        dprintf( D_NETWORK, "SEND [%d] %s ", sent, sock_to_string(sock) );
        dprintf( D_NETWORK|D_NOHEADER, "%s\n", who.to_sinful().Value());
        total += sent;
    }

	headPacket->reset();
	noMsgSent++;
	if(noMsgSent == 1)
		avgMsgSize = msgLen;
	else
		avgMsgSize = ((noMsgSent - 1) * avgMsgSize + msgLen) / noMsgSent;
	return total;
}
Beispiel #12
0
void init_local_hostname()
{
		// [m.]
		// initializing local hostname, ip address, fqdn was
		// super complex.
		//
		// implementation was scattered over condor_netdb and
		// my_hostname, get_full_hostname.
		//
		// above them has duplicated code in many ways.
		// so I aggregated all of them into here.

	bool ipaddr_inited = false;
	char hostname[MAXHOSTNAMELEN];
	int ret;

		// [TODO:IPV6] condor_gethostname is not IPv6 safe.
		// reimplement it.
	ret = condor_gethostname(hostname, sizeof(hostname));
	if (ret) {
		dprintf(D_ALWAYS, "condor_gethostname() failed. Cannot initialize "
				"local hostname, ip address, FQDN.\n");
		return;
	}

	dprintf(D_HOSTNAME, "condor_gethostname() claims we are %s\n", hostname);

	// Fallback case.
	local_hostname = hostname;

		// if NETWORK_INTERFACE is defined, we use that as a local ip addr.
	MyString network_interface;
	if (param(network_interface, "NETWORK_INTERFACE", "*")) {
		if (local_ipaddr.from_ip_string(network_interface))
			ipaddr_inited = true;
	}

		// Dig around for an IP address in the interfaces
		// TODO WARNING: Will only return IPv4 addresses!
	if( ! ipaddr_inited ) {
		std::string ip;
		if( ! network_interface_to_ip("NETWORK_INTERFACE", network_interface.Value(), ip, NULL)) {
			dprintf(D_ALWAYS, "Unable to identify IP address from interfaces.  None matches NETWORK_INTERFACE=%s. Problems are likely.\n", network_interface.Value());
			return;
		}
		if ( ! local_ipaddr.from_ip_string(ip))
		{
			// Should not happen; means network_interface_to_ip returned
			// invalid IP address.
			ASSERT(FALSE);
		}
		ipaddr_inited = true;
	}

		// now initialize hostname and fqdn
	if (nodns_enabled()) { // if nodns is enabled, we can cut some slack.
			// condor_gethostname() returns a hostname with
			// DEFAULT_DOMAIN_NAME. Thus, it is always fqdn
		local_fqdn = hostname;
		if (!ipaddr_inited) {
			local_ipaddr = convert_hostname_to_ipaddr(local_hostname);
		}
		return;
	}

	addrinfo_iterator ai;
	ret = ipv6_getaddrinfo(hostname, NULL, ai);
	if (ret) {
			// write some error message
		dprintf(D_HOSTNAME, "hostname %s cannot be resolved by getaddrinfo\n",
				hostname);
		return;
	}
	
	int local_hostname_desireability = 0;
	while (addrinfo* info = ai.next()) {
		const char* name = info->ai_canonname;
		if (!name)
			continue;
		condor_sockaddr addr(info->ai_addr);

		int desireability = 0;
		if (addr.is_loopback())            { desireability = 1; }
		else if(addr.is_private_network()) { desireability = 2; }
		else                               { desireability = 3; }
		dprintf(D_HOSTNAME, "Considering %s (Ranked at %d) as possible local hostname versus %s/%s (%d)\n", name, desireability, local_hostname.Value(), local_fqdn.Value(), local_hostname_desireability);
		if(desireability < local_hostname_desireability) { continue; }
		local_hostname_desireability = desireability;

		if (!ipaddr_inited)
			local_ipaddr = addr;

		const char* dotpos = strchr(name, '.');
		if (dotpos) { // consider it as a FQDN
			local_fqdn = name;
			local_hostname = local_fqdn.Substr(0, dotpos-name-1);
		} else {
			local_hostname = name;
			local_fqdn = local_hostname;
			MyString default_domain;
			if (param(default_domain, "DEFAULT_DOMAIN_NAME")) {
				if (default_domain[0] != '.')
					local_fqdn += ".";
				local_fqdn += default_domain;
			}
		}
	}

	dprintf(D_HOSTNAME, "Identifying myself as: Short:: %s, Long: %s, IP: %s\n", local_hostname.Value(), local_fqdn.Value(), local_ipaddr.to_ip_string().Value());
	hostname_initialized = true;
}
Beispiel #13
0
int
IpVerify::Verify( DCpermission perm, const condor_sockaddr& addr, const char * user, MyString *allow_reason, MyString *deny_reason )
{
	perm_mask_t mask;
	in6_addr sin6_addr;
	const char *thehost;
    const char * who = user;
	MyString peer_description; // we build this up as we go along (DNS etc.)

	if( !did_init ) {
		Init();
	}
	/*
	 * Be Warned:  careful about parameter "sin" being NULL.  It could be, in
	 * which case we should return FALSE (unless perm is ALLOW)
	 *
	 */

	switch ( perm ) {

	case ALLOW:
		return USER_AUTH_SUCCESS;
		break;

	default:
		break;
	}

	sin6_addr = addr.to_ipv6_address();
	mask = 0;	// must initialize to zero because we logical-or bits into this

    if (who == NULL || *who == '\0') {
        who = TotallyWild;
    }

	if ( perm >= LAST_PERM || !PermTypeArray[perm] ) {
		EXCEPT("IpVerify::Verify: called with unknown permission %d\n",perm);
	}


		// see if a authorization hole has been dyamically punched (via
		// PunchHole) for this perm / user / IP
		// Note that the permission hierarchy is dealt with in
		// PunchHole(), by punching a hole for all implied levels.
		// Therefore, if there is a hole or an implied hole, we will
		// always find it here before we get into the subsequent code
		// which recursively calls Verify() to traverse the hierarchy.
		// This is important, because we do not want holes to find
		// there way into the authorization cache.
		//
	if ( PunchedHoleArray[perm] != NULL ) {
		HolePunchTable_t* hpt = PunchedHoleArray[perm];
		MyString ip_str_buf = addr.to_ip_string();
		const char* ip_str = ip_str_buf.Value();
		MyString id_with_ip;
		MyString id;
		int count;
		if ( who != TotallyWild ) {
			id_with_ip.sprintf("%s/%s", who, ip_str);
			id = who;
			if ( hpt->lookup(id, count) != -1 )	{
				if( allow_reason ) {
					allow_reason->sprintf(
						"%s authorization has been made automatic for %s",
						PermString(perm), id.Value() );
				}
				return USER_AUTH_SUCCESS;
			}
			if ( hpt->lookup(id_with_ip, count) != -1 ) {
				if( allow_reason ) {
					allow_reason->sprintf(
						"%s authorization has been made automatic for %s",
						PermString(perm), id_with_ip.Value() );
				}
				return USER_AUTH_SUCCESS;
			}
		}
		id = ip_str;
		if ( hpt->lookup(id, count) != -1 ) {
			if( allow_reason ) {
				allow_reason->sprintf(
					"%s authorization has been made automatic for %s",
					PermString(perm), id.Value() );
			}
			return USER_AUTH_SUCCESS;
		}
	}

	if ( PermTypeArray[perm]->behavior == USERVERIFY_ALLOW ) {
			// allow if no HOSTALLOW_* or HOSTDENY_* restrictions 
			// specified.
		if( allow_reason ) {
			allow_reason->sprintf(
				"%s authorization policy allows access by anyone",
				PermString(perm));
		}
		return USER_AUTH_SUCCESS;
	}
		
	if ( PermTypeArray[perm]->behavior == USERVERIFY_DENY ) {
			// deny
		if( deny_reason ) {
			deny_reason->sprintf(
				"%s authorization policy denies all access",
				PermString(perm));
		}
		return USER_AUTH_FAILURE;
	}
		
	if( LookupCachedVerifyResult(perm,sin6_addr,who,mask) ) {
		if( deny_reason && (mask&deny_mask(perm)) ) {
			deny_reason->sprintf(
				"cached result for %s; see first case for the full reason",
				PermString(perm));
		}
		else if( allow_reason && (mask&allow_mask(perm)) ) {
			allow_reason->sprintf(
				"cached result for %s; see first case for the full reason",
				PermString(perm));
		}
	}
	else {
		mask = 0;

			// if the deny bit is already set, skip further DENY analysis
		perm_mask_t const deny_resolved = deny_mask(perm);
			// if the allow or deny bit is already set,
			// skip further ALLOW analysis
		perm_mask_t const allow_resolved = allow_mask(perm)|deny_mask(perm);

			// check for matching subnets in ip/mask style
		char ipstr[INET6_ADDRSTRLEN] = { 0, };
   		addr.to_ip_string(ipstr, INET6_ADDRSTRLEN);

		peer_description = addr.to_ip_string();

		if ( !(mask&deny_resolved) && lookup_user_ip_deny(perm,who,ipstr)) {
			mask |= deny_mask(perm);
			if( deny_reason ) {
				deny_reason->sprintf(
					"%s authorization policy denies IP address %s",
					PermString(perm), addr.to_ip_string().Value() );
			}
		}

		if ( !(mask&allow_resolved) && lookup_user_ip_allow(perm,who,ipstr)) {
			mask |= allow_mask(perm);
			if( allow_reason ) {
				allow_reason->sprintf(
					"%s authorization policy allows IP address %s",
					PermString(perm), addr.to_ip_string().Value() );
			}
		}


		std::vector<MyString> hostnames;
		// now scan through hostname strings
		if( !(mask&allow_resolved) || !(mask&deny_resolved) ) {
			hostnames = get_hostname_with_alias(addr);
		}

		for (unsigned int i = 0; i < hostnames.size(); ++i) {
			thehost = hostnames[i].Value();
			peer_description.append_to_list(thehost);

			if ( !(mask&deny_resolved) && lookup_user_host_deny(perm,who,thehost) ) {
				mask |= deny_mask(perm);
				if( deny_reason ) {
					deny_reason->sprintf(
						"%s authorization policy denies hostname %s",
						PermString(perm), thehost );
				}
			}

			if ( !(mask&allow_resolved) && lookup_user_host_allow(perm,who,thehost) ) {
				mask |= allow_mask(perm);
				if( allow_reason ) {
					allow_reason->sprintf(
						"%s authorization policy allows hostname %s",
						PermString(perm), thehost );
				}
			}
		}
			// if we found something via our hostname or subnet mactching, we now have 
			// a mask, and we should add it into our table so we need not
			// do a gethostbyaddr() next time.  if we still do not have a mask
			// (perhaps because this host doesn't appear in any list), create one
			// and then add to the table.
			// But first, check our parent permission levels in the
			// authorization heirarchy.
			// DAEMON and ADMINISTRATOR imply WRITE.
			// WRITE, NEGOTIATOR, and CONFIG_PERM imply READ.
		bool determined_by_parent = false;
		if ( mask == 0 ) {
			if ( PermTypeArray[perm]->behavior == USERVERIFY_ONLY_DENIES ) {
				dprintf(D_SECURITY,"IPVERIFY: %s at %s not matched to deny list, so allowing.\n",who, addr.to_sinful().Value());
				if( allow_reason ) {
					allow_reason->sprintf(
						"%s authorization policy does not deny, so allowing",
						PermString(perm));
				}

				mask |= allow_mask(perm);
			} else {
				DCpermissionHierarchy hierarchy( perm );
				DCpermission const *parent_perms =
					hierarchy.getPermsIAmDirectlyImpliedBy();
				bool parent_allowed = false;
				for( ; *parent_perms != LAST_PERM; parent_perms++ ) {
					if( Verify( *parent_perms, addr, user, allow_reason, NULL ) == USER_AUTH_SUCCESS ) {
						determined_by_parent = true;
						parent_allowed = true;
						dprintf(D_SECURITY,"IPVERIFY: allowing %s at %s for %s because %s is allowed\n",who, addr.to_sinful().Value(),PermString(perm),PermString(*parent_perms));
						if( allow_reason ) {
							MyString tmp = *allow_reason;
							allow_reason->sprintf(
								"%s is implied by %s; %s",
								PermString(perm),
								PermString(*parent_perms),
								tmp.Value());
						}
						break;
					}
				}
				if( parent_allowed ) {
					mask |= allow_mask(perm);
				}
				else {
					mask |= deny_mask(perm);

					if( !determined_by_parent && deny_reason ) {
							// We don't just allow anyone, and this request
							// did not match any of the entries we do allow.
							// In case the reason we didn't match is
							// because of a typo or a DNS problem, record
							// all the hostnames we searched for.
						deny_reason->sprintf(
							"%s authorization policy contains no matching "
							"ALLOW entry for this request"
							"; identifiers used for this host: %s, hostname size = %lu, "
							"original ip address = %s",
							PermString(perm),
							peer_description.Value(),
							(unsigned long)hostnames.size(),
							ipstr);
					}
				}
			}
		}

		if( !determined_by_parent && (mask&allow_mask(perm)) ) {
			// In case we are allowing because of not matching a DENY
			// entry that the user expected us to match (e.g. because
			// of typo or DNS problem), record all the hostnames we
			// searched for.
			if( allow_reason && !peer_description.IsEmpty() ) {
				allow_reason->sprintf_cat(
					"; identifiers used for this remote host: %s",
					peer_description.Value());
			}
		}

			// finally, add the mask we computed into the table with this IP addr
			add_hash_entry(sin6_addr, who, mask);			
	}  // end of if find_match is FALSE

		// decode the mask and return True or False to the user.
	if ( mask & deny_mask(perm) ) {
		return USER_AUTH_FAILURE;
	}

	if ( mask & allow_mask(perm) ) {
		return USER_AUTH_SUCCESS;
	}

	return USER_AUTH_FAILURE;
}