/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */
/*
 * generalized portable TCP routines
 *
 * Handles calls to async connect and dns routines.
 *
 * Designed and originally implemented by Lou Montulli '94
 * Modified by Judson Valeski '97
 */

#include "rosetta.h"
#include "mkutils.h"
#include "prefapi.h"
#include "mkpadpac.h"

#if defined(XP_WIN)
#define ASYNC_DNS
#endif

#include "mktcp.h"
#include "mkparse.h"
#include "mkgeturl.h"  /* for error codes, and some util functions */
#include "fe_proto.h" /* for externs */
#ifdef MOZILLA_CLIENT
#include "secnav.h"
#endif /* MOZILLA_CLIENT */
#include "merrors.h"
#ifndef NSPR20
#if defined(XP_UNIX) || defined(XP_WIN32)
#include "prnetdb.h"
#else
#define PRHostEnt struct hostent
#define PR_NETDB_BUF_SIZE 5
#endif
#else
#include "prnetdb.h"
#endif

#include "ssl.h"

#if defined(XP_OS2) /*DSR072196 - use os2sock.h*/
#include "os2sock.h"
#endif /* XP_OS2 */

#ifdef XP_UNIX
/* #### WARNING, this is duplicated in mksockrw.c
 */
# include <sys/ioctl.h>
/*
 * mcom_db.h is only included here to set BYTE_ORDER !!!
 * MAXDNAME is pilfered right out of arpa/nameser.h.
 */
# include "mcom_db.h"

# if defined(__hpux) || defined(_HPUX_SOURCE)
#  define BYTE_ORDER BIG_ENDIAN
#  define MAXDNAME        256             /* maximum domain name */
# else
#  include <arpa/nameser.h>
# endif

#include <resolv.h>

#if !defined(__osf__) && !defined(AIXV3) && !defined(_HPUX_SOURCE) && !defined(__386BSD__) && !defined(__linux) && !defined(SCO_SV)
#include <sys/filio.h>
#endif

#endif /* XP_UNIX */

#include "xp_error.h"

/* for XP_GetString() */
#include "xpgetstr.h"
extern int MK_CONNECTION_REFUSED;
extern int MK_CONNECTION_TIMED_OUT;
extern int MK_OUT_OF_MEMORY;
extern int MK_UNABLE_TO_CONNECT;
extern int MK_UNABLE_TO_CREATE_SOCKET;
extern int MK_UNABLE_TO_LOCATE_HOST;
extern int MK_UNABLE_TO_LOCATE_SOCKS_HOST;
extern int XP_ERRNO_EALREADY;
extern int XP_ERRNO_ECONNREFUSED;
extern int XP_ERRNO_EINPROGRESS;
extern int XP_ERRNO_EISCONN;
extern int XP_ERRNO_ETIMEDOUT;
extern int XP_ERRNO_EWOULDBLOCK;
extern int XP_PROGRESS_CONTACTHOST;
extern int XP_PROGRESS_LOOKUPHOST;
extern int XP_PROGRESS_NOCONNECTION;
extern int XP_PROGRESS_UNABLELOCATE;
extern int MK_PORT_ACCESS_NOT_ALLOWED;

/* An awful hack. setupSocks gets set to true after the first call to BeginConnect.
 * We don't want to be setting up the socks host/port (a call to gethostbyname) 
 * until we've gone all the way through the netscape initialization.
 */
static setupSocks=FALSE;

typedef enum {
	NET_TCP_FINISH_DNS_LOOKUP,
	NET_TCP_FINISH_CONNECT
} TCPStatesEnum;

struct _TCP_ConData {
	TCPStatesEnum       next_state;  /* states of the machine */
	PRNetAddr           net_addr;
	XP_Bool            	use_security;
	time_t              begin_time;
};

/* MAX_DNS_LIST_SIZE controls the cache of resolved hosts.
 * If it is undefined, the cache will grow without bound.
 * If it is defined and >0, then the cache will be limited
 * to that many entries.  If it is 0, there will be no cache.
 *
 * also needed for DNS failover...
 */
# define MAX_DNS_LIST_SIZE 10


/* Global TCP connect variables
 */
PUBLIC u_long NET_SocksHost=0;
PUBLIC short NET_SocksPort=0;
PUBLIC char * NET_SocksHostName=0;

PUBLIC Bool socksFailure=FALSE;

PUBLIC int NET_InGetHostByName = FALSE; /* global semaphore */

/* The struct used to define a DNS cache entry */
typedef struct _DNSEntry {
    char      *hostname;
	PRUint32  *ips;
	int32      addressCount;
	int        h_length;
	time_t     expirationTime;
} DNSEntry;
   
PRIVATE char   *  net_local_hostname=0;     /* The name of this host */
PRIVATE XP_List * dns_list=0;

#define pref_dnsExpiration "network.dnsCacheExpiration"
PRIVATE int32 dnsCacheExpiration=0;

#define DEFAULT_TCP_CONNECT_TIMEOUT 35  /* seconds */
PRIVATE uint32 net_tcp_connect_timeout = DEFAULT_TCP_CONNECT_TIMEOUT;/*seconds*/

/* set the number of seconds for the tcp connect timeout
 *
 * this timeout is used to end connections that do not
 * timeout on there own */
PUBLIC void
NET_SetTCPConnectTimeout(uint32 seconds) {
	net_tcp_connect_timeout = seconds;
}

/* socks support function
 *
 * if set to NULL socks is off.  If set to non null string the string will be
 * used as the socks host and socks will be used for all connections.
 *
 * returns 0 if the numonic hostname given is invalid or gethostbyname
 * returns host unknown
 *
 */
int NET_SetSocksHost(char * host)
{
	XP_Bool is_numeric_ip;
	char *host_cp;

#ifdef MOZ_OFFLINE
	if (NET_IsOffline()
		|| !setupSocks) {
		/* Either we're offline, or this is the very first call ot setsockshost. */
		socksFailure=FALSE;
		return 1;
	}
#endif /* MOZ_OFFLINE */

	if(host && *host)
	  {
		char *cp;
		XP_LTRACE(MKLib_trace_flag,1,("Trying to set Socks host: %s\n", host));

		/* If there's no port or it's zero, fail out so user gets
		 * an error and checks his configuration.
		 */
		if ( ((cp = XP_STRRCHR(host, ':')) != NULL) 
			&& (*(cp+1) != '\0') 
			&& (*(cp+1) != '0') ) {
			*cp = 0;
			NET_SocksPort = atoi(cp+1);
		} else {
			NET_SocksHost = 0;
			NET_SocksPort = 0;
			XP_FREEIF(NET_SocksHostName);
			NET_SocksHostName = 0;
		    XP_LTRACE(MKLib_trace_flag,1,("Couldn't find a socks port. :(\n"));
			socksFailure=TRUE;
            return 0;  /* Fail? */
		}

		is_numeric_ip = TRUE; /* init positive */
		for(host_cp = host; *host_cp; host_cp++)
		    if(!XP_IS_DIGIT(*host_cp) && *host_cp != '.')
			  {
				is_numeric_ip = FALSE;
				break;
			  }

        if (is_numeric_ip)  /* Numeric node address */
          {
		    TRACEMSG(("Socks host is numeric"));

            /* some systems return a number instead of a struct
             */
            NET_SocksHost = inet_addr(host);
			if (NET_SocksHostName) XP_FREE (NET_SocksHostName);
            NET_SocksHostName = XP_STRDUP(host);
          }
        else      /* name not number */
          {
    	    PRHostEnt *hp;
	    	PRHostEnt hpbuf;
			char dbbuf[PR_NETDB_BUF_SIZE];
			PRStatus rv;

			NET_InGetHostByName++; /* global semaphore */
		    rv = PR_GetHostByName(host, dbbuf, sizeof(dbbuf),  &hpbuf);
		    hp = (rv == PR_SUCCESS ? &hpbuf : NULL);
			NET_InGetHostByName--; /* global semaphore */

            if (!hp)
              {
                XP_LTRACE(MKLib_trace_flag,1,("mktcp.c: Can't find Socks host name `%s'\n", host));
                NET_SocksHost = 0;
				if (NET_SocksHostName) XP_FREE (NET_SocksHostName);
				NET_SocksHostName = 0;
		        XP_LTRACE(MKLib_trace_flag,1,("Socks host is bad. :(\n"));
				if (cp) {
				  *cp = ':';
				}
				socksFailure=TRUE;
                return 0;  /* Fail? */
              }
			XP_MEMCPY(&NET_SocksHost, hp->h_addr, hp->h_length);
          }
		if (cp) {
			*cp = ':';
		}
	  }
	else
	  {
        NET_SocksHost = 0;
		if (NET_SocksHostName) XP_FREE (NET_SocksHostName);
		NET_SocksHostName = NULL;
		NET_SocksPort = 0;
		XP_LTRACE(MKLib_trace_flag,1,("Clearing Socks Host\n"));
	  }
	socksFailure=FALSE;
    return(1);
}

/* Used internally as a utility function. */
PRIVATE void
NET_FreeDNSStruct(DNSEntry * theVictim)
{
	if(theVictim)
	  {
		FREE(theVictim->ips);
	    FREEIF(theVictim->hostname);
	    FREE(theVictim);
	  }
}

/* Used when user turns the cache off (dnsCacheExpiration is set to 0). */
PRIVATE void
NET_DeleteDNSList(void)
{
	XP_List * list_obj = dns_list;
	DNSEntry * dns_entry;

	if(!list_obj)
		return;
    while((dns_entry = (DNSEntry *)XP_ListNextObject(list_obj)) != 0)
    {
		XP_ListRemoveObject(dns_list, dns_entry);
		NET_FreeDNSStruct(dns_entry);
	}
	XP_ListDestroy(dns_list);
	return;
}

PUBLIC void
NET_SetDNSExpirationPref(int32 n)
{
	if((dnsCacheExpiration = n) <= 0)
		NET_DeleteDNSList();
}

MODULE_PRIVATE int PR_CALLBACK
NET_DNSExpirationPrefChanged(const char * newpref, void * data)
{
	int32 n;
	PREF_GetIntPref(pref_dnsExpiration, &n);
	NET_SetDNSExpirationPref((int32)n);
	return PREF_NOERROR;
}

/* the dnsCacheExpiration units are seconds */
PRIVATE int32
net_GetDNSExpiration(void)
{
	return dnsCacheExpiration;
}

/* net_CacheDNSEntry
 *
 * caches the results of a dns lookup for fast
 * retrieval
 */
PRIVATE void
net_CacheDNSEntry(char * hostname, 
				  PRHostEnt * host_pointer,
				  int32 addrCount)
{
#if defined(MAX_DNS_LIST_SIZE) && (MAX_DNS_LIST_SIZE == 0)

    /* If MAX_DNS_LIST_SIZE is defined, and 0, don't cache anything. */
    return;

#else /* !defined(MAX_DNS_LIST_SIZE) || (MAX_DNS_LIST_SIZE > 0) */

	/* Otherwise, MAX_DNS_LIST_SIZE is either the number of entries to
	   cache, or is undefined, meaning "unlimited." */

	int32 i;
	DNSEntry * new_entry;

	/* Make sure incomming data is valid. */
	if(!hostname || !host_pointer)
		return;
	
	/* Determine whether or not we've got a list going yet. If not, get one */
	if(!dns_list)
		dns_list = XP_ListNew();
    if(!dns_list)
		return;

	/* Create our DNSEntry object for caching. */
    if(!(new_entry = XP_NEW(DNSEntry)))
		return;

	/* Copy the host name. */
	new_entry->hostname = 0;
	StrAllocCopy(new_entry->hostname, hostname);

	if(!new_entry->hostname)
	{
		XP_FREE(new_entry);
		return;
	}

	/* Copy all the ip addresses. */
	if( !(new_entry->ips = (PRUint32 *) XP_ALLOC(sizeof(PRUint32) * addrCount)) )
	{
		XP_FREE(new_entry->hostname);
		XP_FREE(new_entry);
		return;
	}

	XP_ASSERT(host_pointer->h_length == 4);
    for(i=0; i < addrCount; i++)
    {
		XP_MEMCPY(&new_entry->ips[i], host_pointer->h_addr_list[i], 4);
    }

	/* Copy all the other data. */
	new_entry->addressCount = addrCount;
    new_entry->h_length = host_pointer->h_length;
	new_entry->expirationTime = time(NULL) + (net_GetDNSExpiration()); /* Current time plus expiration */

    XP_ListAddObject(dns_list, new_entry);

# ifdef MAX_DNS_LIST_SIZE
    /* check to make sure the list is not overflowing the maximum size. */
    if(XP_ListCount(dns_list) > MAX_DNS_LIST_SIZE)
      {
	    DNSEntry * first_entry = (DNSEntry *) XP_ListRemoveEndObject(dns_list);
	    NET_FreeDNSStruct(first_entry);
      }
# endif /* defined(MAX_DNS_LIST_SIZE) */

#endif  /* !defined(MAX_DNS_LIST_SIZE) || (MAX_DNS_LIST_SIZE > 0) */
    return;
}

/* net_CheckDNSCache
 *
 * checks the list of cached dns entries and returns
 * the dns info in the form of struct hostent if a match is
 * found for the passed in hostname
 */
PRIVATE DNSEntry *    
net_CheckDNSCache(CONST char * hostname)	
{
    XP_List *list_obj = dns_list;
    DNSEntry * dns_entry;

    if(!hostname || !dns_list)
		return(0);

    while((dns_entry = (DNSEntry *)XP_ListNextObject(list_obj)) != 0)
      {
		TRACEMSG(("net_CheckDNSCache: comparing %s and %s", hostname, dns_entry->hostname));
		if(dns_entry->hostname && !strcasecomp(hostname, dns_entry->hostname))
		  {
			/* See if the dns entry has expired, if so, get rid of it */
			if(dns_entry->expirationTime < time(NULL))
			{
				XP_ListRemoveObject(dns_list, dns_entry);
				NET_FreeDNSStruct(dns_entry);
				return 0;
			}
		   	return(dns_entry);
		  }
      }
    return(0);
}

/*  print an IP address from a sockaddr struct
 *
 *  This routine is only used for TRACE messages
 */
#if defined(XP_WIN) || defined(XP_OS2)
MODULE_PRIVATE char *CONST
NET_IpString (struct sockaddr_in *sin)
#else
MODULE_PRIVATE CONST char * 
NET_IpString (struct sockaddr_in *sin)
#endif
{
    static char buffer[32];
    int a,b,c,d;
    unsigned char *pc;

    pc = (unsigned char *) &sin->sin_addr;

    a = (int) *pc;
    b = (int) *(++pc);
    c = (int) *(++pc);
    d = (int) *(++pc);
    PR_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d", a,b,c,d);

    return buffer;
}

/* a list of dis-allowed ports for gopher connections for security reasons */
PRIVATE int net_bad_ports_table[] = {
1, 7, 9, 11, 13, 15, 17, 19, 20,
21, 23, 25, 37, 42, 43, 53, 77, 79, 87, 95, 101, 102,
103, 104, 109, 110, 111, 113, 115, 117, 119,
135, 143, 389, 512, 513, 514, 515, 526, 530, 531, 532,
540, 556, 601, 6000, 0 };

/* Lookup a host name. If ASYNC_DNS is defined a platform specific async
 * lookup will occur.
 * 
 * return: 0 on sucess, -1 on failure. MK_WAITING_FOR_LOOKUP is always
 *   returned after first call when ASYNC_DNS is defined (you have to call
 *   it again).
 * 
 * On success hostEnt is not null. */

PRIVATE int
#ifndef ASYNC_DNS
net_dns_lookup(char *host,
			   PRHostEnt **hostEnt,
			   char *dbbuf,
			   PRHostEnt *hpbuf)
#else
net_dns_lookup(MWContext *windowID,
			   char *host,
			   PRHostEnt **hostEnt,
			   PRFileDesc *socket)
#endif /* ASYNC_DNS */


{
	int status;
	NET_InGetHostByName++;
	XP_ASSERT(host);
	XP_ASSERT(hostEnt);
#ifndef ASYNC_DNS
	XP_ASSERT(dbbuf);
	XP_ASSERT(hpbuf);

	/* Not asyncronus, completes a full lookup before returing. */
	status = PR_GetHostByName(host, dbbuf, PR_NETDB_BUF_SIZE, hpbuf);

	if(status == PR_SUCCESS) {
		/* Success, hpbuf points to a valid hostent. */
		NET_InGetHostByName--;
		*hostEnt=hpbuf;
		return status;
	}
#else  /* ASYNC_DNS */
	XP_ASSERT(windowID);
	XP_ASSERT(socket);
	/* FE_StartAsyncDNSLookup  should fill in the hoststruct
	 * or leave zero the pointer if not found
	 * it can also return MK_WAITING_FOR_LOOKUP
	 * if it's not done yet.
	 *
	 * dbbuf and hpbuf are not needed in ASYNC_DNS case. */
	status = FE_AsyncDNSLookup(windowID, host, hostEnt, socket);

	if(status == MK_WAITING_FOR_LOOKUP)	{
		/* Always get here after first call to FE_Async.. for a
		 * particular host name. This ensures execution doesn't 
		 * block. */
		NET_InGetHostByName--;
		return status;
	}
	else if(status == 0) {
		/* Success, hostEnt points to a valid hostent. */
		NET_InGetHostByName--;
		return status;
	}
#endif /* ASYNC_DNS */
	/* FAIL */
	NET_InGetHostByName--;
	/* must set this pointer to NULL if we fail. FE_AsyncDNSLookup does
	 * this but PR_GetHostByName doesn't, so we cover our bases here. */
	*hostEnt = NULL;
	return -1;
}

/*  find the ip address of a host and set sin->sin_addr to that address
 * return 0 on success */

PRIVATE int 
net_FindAddress (const char *host_ptr, 
				 PRNetAddr  *net_addr,
				 MWContext  *window_id, 
				 PRFileDesc *sock) {
	PRHostEnt *hoststruct_pointer; /* Pointer to host - See netdb.h */
	DNSEntry * cache_pointer;
	char *port, *host_port=0, *host_cp;

	/* acts as a flag to determine whether or not this is the first failure. If it is, we want to sanityCheckDNS
	   which determines whether or not we've got a good net connection, sort of */
	static XP_Bool first_dns_failure=TRUE;
	static int random_host_number = -1;
	static time_t random_host_expiration = 0;
	static XP_Bool tryPAD=TRUE;
	XP_Bool is_numeric_ip;

	if(!host_ptr || !*host_ptr)
		return -1;

	/* Proxy Auto-Discovery (PAD) */
	if(tryPAD && MK_PadEnabled && MK_padPacURL && *MK_padPacURL) {
		PRHostEnt *hostEnt=NULL;
		static PRFileDesc *socket=NULL;
		if(!strncasecomp(MK_padPacURL, "file:", 5)) {
			/* Don't allow file urls because they're hard to figure out in an xp way. */
			tryPAD=FALSE;
			foundPADPAC=FALSE;
		} else {
			if(socket == NULL)
				socket=PR_NewTCPSocket();
			if(socket != NULL) {
				char *padHost=NET_ParseURL(MK_padPacURL, GET_HOST_PART);
				if(padHost && *padHost) {
					char *colon=XP_STRCHR(padHost, ':');
					int status;
#ifndef ASYNC_DNS
					char dbbuf[PR_NETDB_BUF_SIZE];
					PRHostEnt dpbuf;
#endif
					if(colon)
						*colon='\0';
#ifndef ASYNC_DNS
					status = net_dns_lookup(padHost, &hostEnt, dbbuf, &dpbuf);
#else
					status = net_dns_lookup(window_id, padHost, &hostEnt, socket);
#endif /* ASYNC_DNS */
					if(status != MK_WAITING_FOR_LOOKUP) {
						PR_Close(socket);
						socket=NULL;
						if(hostEnt != NULL)
							/* We found the padpac */
							foundPADPAC=TRUE;
						tryPAD=FALSE;
					}
					if(colon)
						*colon=':';
				} else {
					PR_Close(socket);
					socket=NULL;
					tryPAD=FALSE;
				}
				XP_FREEIF(padHost);
			} /* End socket not null */
		} 
	} /* End tryPAD */

    StrAllocCopy(host_port, host_ptr);      /* Make a copy we can mess with */
	if (!host_port)
		return -1;

    /* Parse port number if present */  
	port = XP_STRCHR(host_port, ':');  
    if (port) {
        *port++ = 0;       
        if (XP_IS_DIGIT(*port)) {
			unsigned short port_num = (unsigned short) atol(port);
			int i;

			/* check for illegal ports */
			/* if the explicit port number equals
			 * the default port number let it through
			 */
			if(port_num != PR_ntohs(net_addr->inet.port)) {
            	/* disallow well known ports */
            	for(i=0; net_bad_ports_table[i]; i++)
                	if(port_num == net_bad_ports_table[i]) {
                    	char *error_msg = XP_STRDUP(XP_GetString(MK_PORT_ACCESS_NOT_ALLOWED));
                    	if(error_msg) {
                        	FE_Alert(window_id, error_msg);
							XP_FREE(error_msg);
					  	  }

						/* return special error code
						 * that NET_BeginConnect knows
						 * about.
						 */
						XP_FREE(host_port);
                    	return(MK_UNABLE_TO_CONNECT);
                  	  }

            	net_addr->inet.port = PR_htons(port_num);
			  }

		  }
      }

    /* see if this host entry is already cached */
    if( (cache_pointer = net_CheckDNSCache(host_port)) != NULL ) {
		/* call FE_ClearDNSSelect to catch the case of a cache hit before 
		 * a successfull lookup response for this particular socket.
		 * This happens when a previous socket lookup for the same host
		 * succeeded and propagated the cache entry before this one
		 * finished.
		 */
        NET_ClearDNSSelect(window_id, sock);
		
	    net_addr->inet.ip = cache_pointer->ips[0];

	    XP_FREE(host_port);
	    return(0);  /* FOUND OK */
    }

	/* Determine whether or not we're dealing with an ip address as the host */
	is_numeric_ip = TRUE; /* init positive */
	for(host_cp = host_port; *host_cp; host_cp++)
		if(!XP_IS_DIGIT(*host_cp) && *host_cp != '.') {
			is_numeric_ip = FALSE;
			break;
		  }

    /* Parse host number if present. */  
    if (is_numeric_ip) {
		PRUint16 port = net_addr->inet.port;  /* save a copy */
		if(PR_SUCCESS != PR_StringToNetAddr(host_port, net_addr)) {
			XP_ASSERT(0);
			XP_FREE(host_port);
			return(-1); /* fail */
		}
		net_addr->inet.port = port;  /* StringToNetAddr overites the port num */
	/* name not number */
    } else {
		int32 addressCount=0;
	    char *remapped_host_port=0;

		/* randomly remap home.netscape.com to home1.netscape.com through
		 * home32.netscape.com
		 * cache the original name not the new one.
		 */
		if((!strncasecomp(host_port, "home.", 5)
		    || !strncasecomp(host_port, "rl.", 3))
			&& (strcasestr(host_port+2, ".netscape.com")
				|| strcasestr(host_port+2, ".mcom.com"))) {
			time_t cur_time = time(NULL);
			char temp_string[32];
			XP_Bool is_rl_host;

			*temp_string = '\0';

			is_rl_host = !strncasecomp(host_port, "rl.", 3);

			if(random_host_number == -1 || random_host_expiration < cur_time) {
				/* pick a new random number */
				random_host_expiration = cur_time + (60 * 5); /* five min */
				random_host_number = (XP_RANDOM() & 31);
			  }
				
			if(is_rl_host)
				PR_snprintf(temp_string, sizeof(temp_string), "rl%d%s",
					   		random_host_number+1, host_port+2);
			else
				PR_snprintf(temp_string, sizeof(temp_string), "home%d%s",
					   		random_host_number+1, host_port+4);

			StrAllocCopy(remapped_host_port, temp_string);

		    TRACEMSG(("Remapping %s to %s", host_port, remapped_host_port));
		  } 
		
#ifdef XP_UNIX
		  {
		    /* Implement a terrible hack just for unix. If the environment
		     * variable SOCKS_NS is defined, stomp on the host that the DNS
		     * code uses for host lookup to be a specific ip address. */
		    static char firstTime = 1;
		    if (firstTime) {
			    char *ns = getenv("SOCKS_NS");
			    firstTime = 0;
			    if (ns && ns[0]) {
				    /* Use a specific host for dns lookups */
					extern int res_init (void);
				    res_init();
				    _res.nsaddr_list[0].sin_addr.s_addr = inet_addr(ns);
				    _res.nscount = 1;
			      }
		      }
		  }
#endif

		{
			int status;
#ifndef ASYNC_DNS
			char dbbuf[PR_NETDB_BUF_SIZE];
			PRHostEnt dpbuf;
#endif
			/* malloc the string to prevent overflow */
			char *msg = PR_smprintf(XP_GetString(XP_PROGRESS_LOOKUPHOST), host_port);

			if(msg) {
        		NET_Progress(window_id, msg);
				XP_FREE(msg);
			  }

#ifndef ASYNC_DNS
			status = net_dns_lookup( 
						   (remapped_host_port ?
							remapped_host_port :
							host_port),
							&hoststruct_pointer,
							dbbuf,
							&dpbuf);
#else
			status = net_dns_lookup(window_id, 
							   (remapped_host_port ?
								remapped_host_port :
								host_port),
								&hoststruct_pointer,
								sock);
#endif /* ASYNC_DNS */            
			/* sucess if hoststruct_pointer is not null */
			if(status == MK_WAITING_FOR_LOOKUP) {
				XP_FREE(host_port);
				return status;
			}
		}

        if (!hoststruct_pointer) {
			if(first_dns_failure) {
				first_dns_failure = FALSE;
				/* On the proxy causes confusing messages.
				 * This function is only implemented on for XP_UNIX. */
				NET_SanityCheckDNS(window_id);
			  }
            XP_LTRACE(MKLib_trace_flag,1,("mktcp.c: Can't find host name `%s'.  Errno #%d\n",
									host_port, PR_GetError()));
			XP_FREE(host_port);
			XP_LTRACE(MKLib_trace_flag,1,("gethostbyname failed with error: %d\n", PR_GetError()));
            return -1;  /* Fail? */
        }

		/* Count the number of addresses returned in the A record. */
		for (addressCount=0; hoststruct_pointer->h_addr_list[addressCount]; addressCount++) {;}

		/* If the addressCount is zero we've got a problem */
		if (addressCount == 0) {
			XP_ASSERT(0);
			XP_FREE(host_port);
			return -1;
		}

		/* Copy the first address in the list to the sin. char ** h_addr_list */
		XP_ASSERT(hoststruct_pointer->h_length == 4);
		XP_MEMCPY(&net_addr->inet.ip, hoststruct_pointer->h_addr_list[0], 4);
	
    	/* if NET_GetDNSExpiration() returns 0 we are considering the cache disabled */
		if(net_GetDNSExpiration() > 0)
			net_CacheDNSEntry(host_port, hoststruct_pointer, addressCount);
      } /* end name not number else*/

#ifdef DEBUG
	{
		char str[64];
		PR_NetAddrToString(net_addr, str, sizeof(str));
		TRACEMSG(("TCP.c: Found address %s and port %d", str, (int)PR_ntohs(net_addr->inet.port)));
	}
#endif

	XP_FREE(host_port);
    return(0);   /* OK, we found an address */
}

/*  Find out what host this is that we are running on
 */
#ifdef XP_WIN
MODULE_PRIVATE char *CONST NET_HostName ()
#else
MODULE_PRIVATE const char * NET_HostName ()
#endif
{
	if(!net_local_hostname)
	  {
        char tmp_local_hostname[256];

		if (PR_GetSystemInfo(PR_SI_HOSTNAME, tmp_local_hostname, 254) == PR_FAILURE) /* Error, just get an empty string */
        	tmp_local_hostname[0] = 0;
	    StrAllocCopy(net_local_hostname, tmp_local_hostname);
			
        TRACEMSG(("TCP.c: Found local host name: %s", net_local_hostname));
	  }
    return net_local_hostname;
}

/* FREE left over tcp connection data if there is any
 */
MODULE_PRIVATE void 
NET_FreeTCPConData(TCP_ConData * data)
{
	TRACEMSG(("Freeing TCPConData...Done with connect (i hope)"));
	FREEIF(data)
}

PRIVATE int
net_start_first_connect(const char   *host, 
						PRFileDesc   *sock, 
						MWContext    *window_id, 
						TCP_ConData  *tcp_con_data,
						char        **error_msg)
{

    /* malloc the string to prevent overflow
     */
    int32 len = XP_STRLEN(XP_GetString(XP_PROGRESS_CONTACTHOST));
    char * buf;

    len += XP_STRLEN(host);

    buf = (char *)XP_ALLOC((len+10)*sizeof(char));
    if(buf)
      {
        PR_snprintf(buf, (len+10)*sizeof(char),
                XP_GetString(XP_PROGRESS_CONTACTHOST), host);
        NET_Progress(window_id, buf);
        FREE(buf);
      }

	/* set the begining time to be the current time.
	 * the timeout value will be  compared to this
	 * later
	 */
	tcp_con_data->begin_time = time(NULL);

#define CONNECT_TIMEOUT  0

    /* if it's not equal to PR_SUCCESS something went wrong
	 *
	 * PRNetAddr is binary compatible with struct sockaddr
     */
    if(PR_SUCCESS != PR_Connect (sock,
								 &tcp_con_data->net_addr,
								 CONNECT_TIMEOUT))
      {

        int rv = PR_GetError();

#if !defined(XP_MAC) && !defined(XP_WIN16)
		XP_ASSERT(rv != PR_WOULD_BLOCK_ERROR); /* should never happen */
#endif
        if (rv == PR_IN_PROGRESS_ERROR || rv == PR_WOULD_BLOCK_ERROR)
          {
            tcp_con_data->next_state = NET_TCP_FINISH_CONNECT;
            return(MK_WAITING_FOR_CONNECTION);  /* not connected yet */
          }
        else if (rv == PR_IS_CONNECTED_ERROR)
          {
            return(MK_CONNECTED);  /* already connected */
          }
        else
          {
            PR_Close(sock);
            if(rv == PR_CONNECT_REFUSED_ERROR)
              {
	    		*error_msg = NET_ExplainErrorDetails(MK_CONNECTION_REFUSED, host);

                XP_LTRACE(MKLib_trace_flag,1,("connect: refused\n"));

                return(MK_CONNECTION_REFUSED);
              }
            else if(rv == PR_CONNECT_TIMEOUT_ERROR)
              {
	    		*error_msg = NET_ExplainErrorDetails(MK_CONNECTION_TIMED_OUT);

                XP_LTRACE(MKLib_trace_flag,1,("connect: timed out\n"));

                return(MK_CONNECTION_TIMED_OUT);
              }
			else 
              {
			    *error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_CONNECT, rv);

                XP_LTRACE(MKLib_trace_flag,1,("connect: unable to connect %i\n", rv));

                return (MK_UNABLE_TO_CONNECT);
              }
          }
      }
 
    /* else  good connect */

    return(MK_CONNECTED);
}

/* Finds the DNSEntry with hostname in it, removes it if it only has one address, otherwise
   removes the first address and shifts the other(s) forward so they can be tried. */
PRIVATE XP_Bool
net_connection_failed(CONST char *hostname)
{
	DNSEntry *dns_entry = 0;
	char *hostCopy=NULL, *port=NULL;

	if(!hostname)
		return FALSE;

	StrAllocCopy(hostCopy, hostname);
	if(!hostCopy)
		return FALSE;

	/* Look for a port */
	if( (port = XP_STRCHR(hostCopy, ':')) != NULL )
		*port = '\0';

	dns_entry = net_CheckDNSCache(hostCopy);
	FREE(hostCopy);
	/* If we find the cached dns entry pull the address off the top because it is the one 
		that failed. Then shift the others up to the front of the list. */
	if (dns_entry)
	{
		/* If there is only one (most common case) ip address associated with this host,
		   blow the entire entry away */
		if (dns_entry->addressCount == 1)		  
		{
			XP_ListRemoveObject(dns_list, dns_entry);
			NET_FreeDNSStruct(dns_entry);
			return FALSE;
		}
		else /* Failover case */
		{
			dns_entry->addressCount--;
			/* Shift addresses up one, overwriting the first one */
			XP_MEMMOVE(dns_entry->ips,
						&dns_entry->ips[1],
						sizeof(PRUint32) * dns_entry->addressCount );
			/* Null terminate the array */
			dns_entry->ips[dns_entry->addressCount] = 0;
			return TRUE;
		}
	}
	/* If the host wasn't in the cache then caching is off or an ip address was sent in here
	   and we don't want to keep trying this address so return false. */
	return FALSE;
}


/*
 * Non blocking connect begin function.  It will most likely
 * return negative. If it does, NET_ConnectFinish() will
 * need to be called repeatably until a connect is established.
 *
 * return's negative on error
 * return's MK_WAITING_FOR_CONNECTION when continue is neccessary
 * return's MK_CONNECTED on true connect
 */ 

MODULE_PRIVATE int 
NET_BeginConnect (CONST char   *url, 
				  char   *ip_address_string,
          	      char         *prot_name, 
          	      int           def_port, 
          	      PRFileDesc     **sock, 
			      Bool       use_security,
			      TCP_ConData **tcp_con_data,
          	      MWContext    *window_id,
			      char        **error_msg,
				  u_long		socks_host,
				  short			socks_port)
{

	char * proxy=NULL;
	int32 iPort=0;
	char text[MAXHOSTNAMELEN + 8];
    CONST char *host; 
    char *althost=0; 
   	int status;
    PRFileDesc *fd;
    char *host_string=0;
    PRSocketOptionData opt;
#ifdef MOZ_OFFLINE
	if (NET_IsOffline())	/* if we're offline, ret error - dmb 11/25/96 */
		return MK_OFFLINE;	
#endif /* MOZ_OFFLINE */
	TRACEMSG(("NET_BeginConnect called for url: %s", url));

	/* One time startup flag. If this is the first time in BeginConnect,
	 * make sure the socks is setup if it needs to be.
	 */
	if(!setupSocks) {
		setupSocks=TRUE;
		if (NET_GetProxyStyle() == PROXY_STYLE_MANUAL) {
			PREF_CopyCharPref("network.hosts.socks_server",&proxy);
			if (proxy && *proxy) {
				PREF_GetIntPref("network.hosts.socks_serverport",&iPort);
				PR_snprintf(text, sizeof(text), "%s:%d", proxy, iPort);  
				NET_SetSocksHost(text);
			}
			else {
				NET_SetSocksHost(proxy); /* NULL is ok */
			}
		}
	}

	/* in case finish connect calls us 
	 */
	if(*tcp_con_data)
		NET_FreeTCPConData(*tcp_con_data);
	
	/* construct state table data
	 */	
	*tcp_con_data = XP_NEW(TCP_ConData);
	
	if(!*tcp_con_data)
	  {
	    *error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
		return(MK_OUT_OF_MEMORY);
	  }

	XP_MEMSET(*tcp_con_data, 0, sizeof(TCP_ConData));

    /* Set up Internet defaults and port*/
	PR_InitializeNetAddr(PR_IpAddrNull, (PRUint16) def_port, &(*tcp_con_data)->net_addr);

    if(NET_URL_Type(url)) 
      {
        host_string = NET_ParseURL(url, GET_HOST_PART);
		host = host_string;
      }
    else
      {
        /* assume that a hostname was passed directly 
         * instead of a URL
         */
        host = url;
      }

    /* build a socket
     */
    *sock = PR_NewTCPSocket();

HG59609

    if(*sock == NULL)
      {                                                
        int error = PR_GetError();
        TRACEMSG(("NETSOCKET call returned INVALID_SOCKET: %d", error));
        NET_FreeTCPConData(*tcp_con_data);
        *tcp_con_data = 0;
        *error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_CREATE_SOCKET, error);
        FREE_AND_CLEAR(host_string);
        return(MK_UNABLE_TO_CREATE_SOCKET);
      }

HG40252

	/* make the socket non-blocking */
	opt.option = PR_SockOpt_Nonblocking;
	opt.value.non_blocking = PR_TRUE;
	PR_SetSocketOption(*sock, &opt);

#if defined(XP_WIN16) || (defined(XP_OS2) && !defined(XP_OS2_DOUGSOCK))
	opt.option = PR_SockOpt_Linger;
	opt.value.linger.polarity = PR_TRUE;
	opt.value.linger.linger = PR_INTERVAL_NO_WAIT;
	PR_SetSocketOption(*sock, &opt);
#endif /* XP_WIN16 */

	/* copy the sock no
	 */
    fd = *sock;

HG28879
	{
		/* if the socks host is invalid */
		if ( socksFailure && (NET_GetProxyStyle() == PROXY_STYLE_MANUAL) )
		{
			char * prefSocksHost = NULL;
			/* Tell the FE about the failure */
            int32 len = XP_STRLEN(XP_GetString(XP_PROGRESS_UNABLELOCATE));
			char * buf;
			PREF_CopyCharPref("network.hosts.socks_server",&prefSocksHost);
			if(prefSocksHost)
			{
				len += XP_STRLEN(prefSocksHost);

				buf = (char *)XP_ALLOC((len+10)*sizeof(char));
				if(buf)
				  {
					PR_snprintf(buf, (len+10)*sizeof(char),
							XP_GetString(XP_PROGRESS_UNABLELOCATE), prefSocksHost);
					NET_Progress(window_id, buf);
					FREE(buf);
				  }

				/* Tell the user about the failure */
				*error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_LOCATE_SOCKS_HOST, prefSocksHost);

				/* clean up prior to leaving */
				NET_FreeTCPConData(*tcp_con_data);
				*tcp_con_data = 0;
				PR_Close(*sock);
				*sock = NULL;
				FREE_AND_CLEAR(host_string);
				XP_FREEIF(prefSocksHost);
			}
			return MK_UNABLE_TO_LOCATE_HOST;
		}
	}

HG71089

	/* if an IP address string was passed in, then use it instead of the
	 * hostname to do the DNS lookup.
	 */
	if ( ip_address_string ) {
		char *port;

		StrAllocCopy(althost, ip_address_string);

		port = XP_STRCHR(host, ':');
		if ( port ) {
			StrAllocCat(althost, port);
		}
	}
	
    status = net_FindAddress(althost?althost:host, 
				 &((*tcp_con_data)->net_addr), 
				 window_id, 
				 *sock);

	if (althost) {
		XP_FREE(althost);
	}
	
	if(status == MK_WAITING_FOR_LOOKUP)
	  {
		(*tcp_con_data)->next_state = NET_TCP_FINISH_DNS_LOOKUP;
        FREE_AND_CLEAR(host_string);
       	return(MK_WAITING_FOR_CONNECTION);  /* not connected yet */
	  }
    else if (status < 0)
      {
        {
            /* malloc the string to prevent overflow
             */
            int32 len = XP_STRLEN(XP_GetString(XP_PROGRESS_UNABLELOCATE));
            char * buf;

            len += XP_STRLEN(host);

            buf = (char *)XP_ALLOC((len+10)*sizeof(char));
            if(buf)
              {
                PR_snprintf(buf, (len+10)*sizeof(char),
                        XP_GetString(XP_PROGRESS_UNABLELOCATE), host);
                NET_Progress(window_id, buf);
                FREE(buf);
              }
        }

		NET_FreeTCPConData(*tcp_con_data);
		*tcp_con_data = 0;
		PR_Close(*sock);
	    *sock = NULL;

		/* @@@@@ HACK!  If we detect an illegal port in
		 * net_FindAddress we put up an error message
		 * and return MK_UNABLE_TO_CONNECT.  Don't
		 * print another message
		 */
		if(status != MK_UNABLE_TO_CONNECT)
	    	*error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_LOCATE_HOST, 
												*host ? host : "(no name specified)");
        FREE_AND_CLEAR(host_string);
        return MK_UNABLE_TO_LOCATE_HOST;
      }

    status = net_start_first_connect(host, *sock, window_id, 
									 *tcp_con_data, error_msg);

	if(status != MK_WAITING_FOR_CONNECTION)
	  {
		NET_FreeTCPConData(*tcp_con_data);
		*tcp_con_data = 0;
	  }
	else
	  {
       	(*tcp_con_data)->next_state = NET_TCP_FINISH_CONNECT;
		/* save in case we need it */
       	(*tcp_con_data)->use_security = use_security; 
	  }

	if(status < 0)
	  {
		net_connection_failed(host);
		PR_Close(*sock);
	    *sock = NULL;
	  }

    FREE_AND_CLEAR(host_string);
	
    return(status);
}


/*
 * Non blocking connect finishing function.  This routine polls
 * the socket until a connection finally takes place or until
 * an error occurs. NET_ConnectFinish() will need to be called 
 * repeatably until a connect is established.
 *
 * return's negative on error
 * return's MK_WAITING_FOR_CONNECTION when continue is neccessary
 * return's MK_CONNECTED on true connect
 */
PUBLIC int 
NET_FinishConnect (CONST char   *url,
                   char         *prot_name,
                   int           def_port,
                   PRFileDesc   **sock, 
			       TCP_ConData **tcp_con_data,
                   MWContext    *window_id,
				   char        **error_msg)
{
	int status;
	char *host=NULL;

	TRACEMSG(("NET_FinishConnect called for url: %s", url));

	if(!*tcp_con_data)  /* safty valve */
	  {
	    *error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
		return(MK_OUT_OF_MEMORY);
	  }

	switch((*tcp_con_data)->next_state)
	{
	case NET_TCP_FINISH_DNS_LOOKUP:
	  {
		char * host_string=NULL;	
		const char * host = NULL;

        if(NET_URL_Type(url))
          {
            host_string = NET_ParseURL(url, GET_HOST_PART);
            host = host_string;
          }
        else
          {
            /* assume that a hostname was passed directly
             * instead of a URL
             */
            host = url;
          }

		status = net_FindAddress(host, 
					&((*tcp_con_data)->net_addr),
					window_id, 
					*sock);

        if(status == MK_WAITING_FOR_LOOKUP)
          {
            (*tcp_con_data)->next_state = NET_TCP_FINISH_DNS_LOOKUP;
            return(MK_WAITING_FOR_CONNECTION);  /* not connected yet */
          }
        else if (status < 0)
          {
        	{
            	/* malloc the string to prevent overflow
             	 */
            	int32 len = XP_STRLEN(XP_GetString(XP_PROGRESS_UNABLELOCATE));
            	char * buf;
	
            	len += XP_STRLEN(host);
	
            	buf = (char *)XP_ALLOC((len+10)*sizeof(char));
            	if(buf)
              	  {
                	PR_snprintf(buf, (len+10)*sizeof(char),
                        	XP_GetString(XP_PROGRESS_UNABLELOCATE), host);
                	NET_Progress(window_id, buf);
                	FREE(buf);
              	  }
        	}

            NET_FreeTCPConData(*tcp_con_data);
            *tcp_con_data = 0;
	        *error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_LOCATE_HOST, host);
            FREE_AND_CLEAR(host_string);
            return MK_UNABLE_TO_LOCATE_HOST;
          }

        status = net_start_first_connect(host, *sock, window_id, 
										 *tcp_con_data, error_msg);

        if(status != MK_WAITING_FOR_CONNECTION)
          {
            NET_FreeTCPConData(*tcp_con_data);
            *tcp_con_data = 0;
          }
		else
		  {
        	(*tcp_con_data)->next_state = NET_TCP_FINISH_CONNECT;
		  }

        if(status < 0)
          {
			net_connection_failed(host);
			XP_LTRACE(MKLib_trace_flag,1,("mktcp.c: Error during connect %d\n", PR_GetError()));
            PR_Close(*sock);
            *sock = NULL;
          }  

        FREE_AND_CLEAR(host_string);

        return(status);

	  } /* end of this part of the case */
	
	case NET_TCP_FINISH_CONNECT:
	{
		PRPollDesc pd;

		pd.fd = *sock;
		pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT; 

        /* do another connect here to see if we are actually connected
		 *
		 * PRNetAddr is binary compatible with struct sockaddr
         */
		if(PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT) < 1)
		{
			XP_ASSERT(0);
	        return MK_WAITING_FOR_CONNECTION;  /* perhaps we should error here */
		}

#if defined(XP_WIN16)
    	if(PR_SUCCESS != PR_Connect (*sock,
								 &(*tcp_con_data)->net_addr,
								 CONNECT_TIMEOUT))
#else
        if(PR_SUCCESS != PR_GetConnectStatus(&pd))
#endif 
        {
		    int error;
    
		    error = PR_GetError();
		
#if !defined(XP_MAC) && !defined(XP_WIN16)
			XP_ASSERT(error != PR_WOULD_BLOCK_ERROR); /* should never happen */
#endif
			if (error == PR_IN_PROGRESS_ERROR || error == PR_WOULD_BLOCK_ERROR)
		      {

				/* check the begin time against the current time minus
				 * the timeout value.  Error out if past the timeout
				 */
				if((time_t)(time(NULL)-net_tcp_connect_timeout) > (*tcp_con_data)->begin_time)
				  {
					error = XP_ERRNO_ETIMEDOUT;
					goto error_out;
				  }

			    /* still waiting
			     */
                return MK_WAITING_FOR_CONNECTION;
		      }
		    else if(error !=  PR_IS_CONNECTED_ERROR)
		      {
error_out:
			
		        /* if EISCONN then we actually are connected
		         * otherwise return an error
		         */

				/* At this point we know there was a problem with the ip address we tried. */
			    XP_LTRACE(MKLib_trace_flag,1,("mktcp.c: Error during connect: %d\n", error));

				host = NET_ParseURL(url, GET_HOST_PART);
				if(!host)
					return -1;

				/* In the case when the url is a host, no protocol, NET_ParseURL returns null byte.
				   We run up against this case when a proxy was placed into the url instead of 
				   the url itself.*/
				if(*host == '\0') {
					FREE(host);
					host = NULL;
					StrAllocCopy(host, url);
				}

				/* If we should try other addresses with this tcpConnection, do it */
				if (net_connection_failed(host))
				{
					int status;

					if(*host == '\0')
					  {
						/* the url is a hostname */
						FREE(host);
						if( !(host = XP_STRDUP(url)) )
							return -1;
					  }

					/* Go get another of the ip addresses */
    				status = net_FindAddress(host, 
				 				&((*tcp_con_data)->net_addr), 
				 				window_id, 
				 				*sock);

					FREE(host);
					if(status == 0)
        				return(NET_BeginConnect (url,
												NULL,
                                 				prot_name,
                                 				def_port,
                                 				sock,
                                 				(*tcp_con_data)->use_security,
                                 				tcp_con_data,
                                 				window_id,
								 				error_msg,
												0,
												0));
					/* else fall through to the error */
				}
				else
					FREE(host);

                if (error == PR_CONNECT_REFUSED_ERROR)
					{
							char * host = NET_ParseURL(url, GET_HOST_PART);
	        			    *error_msg = NET_ExplainErrorDetails(
												MK_CONNECTION_REFUSED,
												host);
							FREE(host);
		    			    NET_FreeTCPConData(*tcp_con_data);
		    			    *tcp_con_data = 0;
                        	return MK_CONNECTION_REFUSED;
					}
                else if (error == PR_CONNECT_TIMEOUT_ERROR)
					{
		    			NET_FreeTCPConData(*tcp_con_data);
		    			*tcp_con_data = 0;
	        			*error_msg = NET_ExplainErrorDetails(MK_CONNECTION_TIMED_OUT);
                        return MK_CONNECTION_TIMED_OUT;
					}
                else
					{
		    			NET_FreeTCPConData(*tcp_con_data);
		    			*tcp_con_data = 0;
	        			*error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_CONNECT, error);
                        return MK_UNABLE_TO_CONNECT;
					}
		      }
        }
    
		TRACEMSG(("mktcp.c: Successful connection (message 1)"));
		NET_FreeTCPConData(*tcp_con_data);
		*tcp_con_data = 0;
        return MK_CONNECTED;
	}

	default:
		NET_FreeTCPConData(*tcp_con_data);
		*tcp_con_data = 0;
		TRACEMSG(("mktcp.c: bad state during connect"));

		assert(0);  /* should never happen */

	    *error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_CONNECT, 0);
		return(MK_UNABLE_TO_CONNECT);

	} /* end switch on next_state */

	XP_ASSERT(0);  /* should never get here */

	*error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_CONNECT, 0);
	return(MK_UNABLE_TO_CONNECT);
}


/* Free any memory used up
 */
MODULE_PRIVATE void
NET_CleanupTCP(void)
{
    if(dns_list)
      {
        DNSEntry * tmp_entry;

        while((tmp_entry = (DNSEntry *)XP_ListRemoveTopObject(dns_list)) != NULL)
          {
             NET_FreeDNSStruct(tmp_entry);
          }

        XP_ListDestroy(dns_list);
		dns_list = 0;
      }

	/* we really should free the socket_buffer but
	 * that is in the other module as a private :(
	 */

    FREE_AND_CLEAR(net_local_hostname);

    return;
}
