mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 14:45:29 +00:00
1619 lines
44 KiB
C
1619 lines
44 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
*
|
|
* 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 "netutils.h"
|
|
#include "prerror.h"
|
|
#include "prsystem.h"
|
|
#include "prefapi.h"
|
|
#include "mkpadpac.h"
|
|
#include "mkprefs.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"
|
|
|
|
#include "ssl.h"
|
|
#include "xp_error.h"
|
|
|
|
#if defined(XP_OS2) /*DSR072196 - use os2sock.h*/
|
|
#include "os2sock.h"
|
|
#endif /* XP_OS2 */
|
|
|
|
#include "timing.h"
|
|
|
|
#if defined(SMOOTH_PROGRESS)
|
|
#include "progress.h"
|
|
#endif
|
|
|
|
#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(HAVE_SYS_FILIO_H)
|
|
#include <sys/filio.h>
|
|
#endif
|
|
|
|
#endif /* XP_UNIX */
|
|
|
|
/* 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;
|
|
HG93765
|
|
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 unsigned long NET_SocksHost=0;
|
|
PUBLIC short NET_SocksPort=0;
|
|
PUBLIC char * NET_SocksHostName=0;
|
|
|
|
PUBLIC PRBool 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 XP_List * dns_list=0;
|
|
|
|
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)
|
|
{
|
|
PRBool 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;
|
|
TRACEMSG(("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 = PL_strrchr(host, ':')) != NULL)
|
|
&& (*(cp+1) != '\0')
|
|
&& (*(cp+1) != '0') ) {
|
|
*cp = 0;
|
|
NET_SocksPort = atoi(cp+1);
|
|
} else {
|
|
NET_SocksHost = 0;
|
|
NET_SocksPort = 0;
|
|
PR_FREEIF(NET_SocksHostName);
|
|
NET_SocksHostName = 0;
|
|
TRACEMSG(("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(!NET_IS_DIGIT(*host_cp) && *host_cp != '.')
|
|
{
|
|
is_numeric_ip = FALSE;
|
|
break;
|
|
}
|
|
|
|
if (is_numeric_ip) /* Numeric node address */
|
|
{
|
|
PRNetAddr netAddr;
|
|
PRStatus status;
|
|
TRACEMSG(("Socks host is numeric"));
|
|
|
|
/* some systems return a number instead of a struct */
|
|
status = PR_StringToNetAddr(host, &netAddr);
|
|
if (PR_SUCCESS == status) {
|
|
NET_SocksHost = netAddr.inet.ip;
|
|
}
|
|
if (NET_SocksHostName) PR_Free (NET_SocksHostName);
|
|
NET_SocksHostName = PL_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)
|
|
{
|
|
TRACEMSG(("mktcp.c: Can't find Socks host name `%s'\n", host));
|
|
NET_SocksHost = 0;
|
|
if (NET_SocksHostName) PR_Free (NET_SocksHostName);
|
|
NET_SocksHostName = 0;
|
|
TRACEMSG(("Socks host is bad. :(\n"));
|
|
if (cp) {
|
|
*cp = ':';
|
|
}
|
|
socksFailure=TRUE;
|
|
return 0; /* Fail? */
|
|
}
|
|
memcpy(&NET_SocksHost, hp->h_addr_list[0], hp->h_length);
|
|
}
|
|
if (cp) {
|
|
*cp = ':';
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NET_SocksHost = 0;
|
|
if (NET_SocksHostName) PR_Free (NET_SocksHostName);
|
|
NET_SocksHostName = NULL;
|
|
NET_SocksPort = 0;
|
|
TRACEMSG(("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)
|
|
{
|
|
PRInt32 n;
|
|
if (PREF_OK != PREF_GetIntPref(pref_dnsExpiration, &n) )
|
|
n = DEF_DNS_EXPIRATION;
|
|
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 = PR_NEW(DNSEntry)))
|
|
return;
|
|
|
|
/* Copy the host name. */
|
|
new_entry->hostname = 0;
|
|
StrAllocCopy(new_entry->hostname, hostname);
|
|
|
|
if(!new_entry->hostname)
|
|
{
|
|
PR_Free(new_entry);
|
|
return;
|
|
}
|
|
|
|
/* Copy all the ip addresses. */
|
|
if( !(new_entry->ips = (PRUint32 *) PR_Malloc(sizeof(PRUint32) * addrCount)) )
|
|
{
|
|
PR_Free(new_entry->hostname);
|
|
PR_Free(new_entry);
|
|
return;
|
|
}
|
|
|
|
PR_ASSERT(host_pointer->h_length == 4);
|
|
for(i=0; i < addrCount; i++)
|
|
{
|
|
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 && !PL_strcasecmp(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);
|
|
}
|
|
|
|
/* 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. */
|
|
#if defined(XP_WIN)
|
|
extern int NET_AsyncDNSLookup(void* aContext,
|
|
const char* aHostPort,
|
|
PRHostEnt** aHoststructPtrPtr,
|
|
PRFileDesc* aSocket);
|
|
#endif
|
|
|
|
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++;
|
|
PR_ASSERT(host);
|
|
PR_ASSERT(hostEnt);
|
|
#ifndef ASYNC_DNS
|
|
PR_ASSERT(dbbuf);
|
|
PR_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 */
|
|
PR_ASSERT(windowID);
|
|
PR_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 = NET_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(!PL_strncasecmp(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=PL_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;
|
|
}
|
|
PR_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 = PL_strchr(host_port, ':');
|
|
if (port) {
|
|
*port++ = 0;
|
|
if (NET_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 = PL_strdup(XP_GetString(MK_PORT_ACCESS_NOT_ALLOWED));
|
|
if(error_msg) {
|
|
FE_Alert(window_id, error_msg);
|
|
PR_Free(error_msg);
|
|
}
|
|
|
|
/* return special error code
|
|
* that NET_BeginConnect knows
|
|
* about.
|
|
*/
|
|
PR_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];
|
|
|
|
PR_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(!NET_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)) {
|
|
PR_ASSERT(0);
|
|
PR_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((!PL_strncasecmp(host_port, "home.", 5)
|
|
|| !PL_strncasecmp(host_port, "rl.", 3))
|
|
&& (PL_strcasestr(host_port+2, ".netscape.com")
|
|
|| PL_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 = !PL_strncasecmp(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) {
|
|
PRNetAddr netAddr;
|
|
PRStatus status;
|
|
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();
|
|
status = PR_StringToNetAddr(ns, &netAddr);
|
|
if (PR_SUCCESS == status) {
|
|
_res.nsaddr_list[0].sin_addr.s_addr = netAddr.inet.ip;
|
|
}
|
|
_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) {
|
|
#if defined(SMOOTH_PROGRESS)
|
|
PM_Status(window_id, NULL, msg);
|
|
#else
|
|
NET_Progress(window_id, msg);
|
|
#endif
|
|
PR_Free(msg);
|
|
}
|
|
|
|
TIMING_STARTCLOCK_NAME("dns:lookup", (remapped_host_port ? remapped_host_port : host_port));
|
|
|
|
#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) {
|
|
PR_Free(host_port);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
TIMING_STOPCLOCK_NAME("dns:lookup", (remapped_host_port ? remapped_host_port : host_port),
|
|
window_id, (hoststruct_pointer ? "ok" : "error"));
|
|
|
|
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);
|
|
}
|
|
TRACEMSG(("mktcp.c: Can't find host name `%s'. Errno #%d\n",
|
|
host_port, PR_GetError()));
|
|
PR_Free(host_port);
|
|
TRACEMSG(("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) {
|
|
PR_ASSERT(0);
|
|
PR_Free(host_port);
|
|
return -1;
|
|
}
|
|
|
|
/* Copy the first address in the list to the sin. char ** h_addr_list */
|
|
PR_ASSERT(hoststruct_pointer->h_length == 4);
|
|
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
|
|
|
|
PR_Free(host_port);
|
|
return(0); /* OK, we found an address */
|
|
}
|
|
|
|
/* 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,
|
|
PRUint32 localIP)
|
|
{
|
|
|
|
/* malloc the string to prevent overflow
|
|
*/
|
|
int32 len = PL_strlen(XP_GetString(XP_PROGRESS_CONTACTHOST));
|
|
char * buf;
|
|
|
|
len += PL_strlen(host);
|
|
|
|
buf = (char *)PR_Malloc((len+10)*sizeof(char));
|
|
if(buf)
|
|
{
|
|
PR_snprintf(buf, (len+10)*sizeof(char),
|
|
XP_GetString(XP_PROGRESS_CONTACTHOST), host);
|
|
#if defined(SMOOTH_PROGRESS)
|
|
PM_Status(window_id, NULL, buf);
|
|
#else
|
|
NET_Progress(window_id, buf);
|
|
#endif
|
|
FREE(buf);
|
|
}
|
|
|
|
HG26300
|
|
/* 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
|
|
|
|
/* Bind to a user specified local IP address if specified.
|
|
* Netlib does not check the validity of this address.
|
|
* The user is guaranteing it's existence and usability. */
|
|
if ( localIP > 0 ) {
|
|
PRStatus status;
|
|
PRErrorCode errorCode; /* see http://www.mozilla.org/docs/refList/refNSPR/prerr.htm#1027954 */
|
|
PRNetAddr *addr = (PRNetAddr *)PR_Malloc(sizeof(PRNetAddr));
|
|
if (!addr)
|
|
return MK_UNABLE_TO_CONNECT;
|
|
status = PR_InitializeNetAddr(PR_IpAddrNull, 0, addr);
|
|
if (status != PR_SUCCESS) {
|
|
errorCode = PR_GetError();
|
|
} else {
|
|
addr->inet.ip = localIP;
|
|
status = PR_Bind(sock, addr);
|
|
if (status != PR_SUCCESS) {
|
|
errorCode = PR_GetError();
|
|
}
|
|
}
|
|
PR_Free(addr);
|
|
}
|
|
|
|
/* 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)
|
|
PR_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);
|
|
|
|
TRACEMSG(("connect: refused\n"));
|
|
|
|
return(MK_CONNECTION_REFUSED);
|
|
}
|
|
else if(rv == PR_CONNECT_TIMEOUT_ERROR)
|
|
{
|
|
*error_msg = NET_ExplainErrorDetails(MK_CONNECTION_TIMED_OUT);
|
|
|
|
TRACEMSG(("connect: timed out\n"));
|
|
|
|
return(MK_CONNECTION_TIMED_OUT);
|
|
}
|
|
else
|
|
{
|
|
*error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_CONNECT, rv);
|
|
|
|
TRACEMSG(("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 = PL_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 */
|
|
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,
|
|
HG92743
|
|
TCP_ConData **tcp_con_data,
|
|
MWContext *window_id,
|
|
char **error_msg,
|
|
unsigned long socks_host,
|
|
short socks_port,
|
|
PRUint32 localIP)
|
|
{
|
|
|
|
char * proxy=NULL;
|
|
PRInt32 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) {
|
|
if ( (PREF_OK != PREF_CopyCharPref(pref_socksServer,&proxy))
|
|
|| !proxy || !*proxy ) {
|
|
NET_SetSocksHost(NULL); /* NULL is ok */
|
|
} else {
|
|
if ( PREF_OK == PREF_GetIntPref(pref_socksPort,&iPort) ) {
|
|
PR_snprintf(text, sizeof(text), "%s:%d", proxy, iPort);
|
|
NET_SetSocksHost(text);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* in case finish connect calls us
|
|
*/
|
|
if(*tcp_con_data)
|
|
NET_FreeTCPConData(*tcp_con_data);
|
|
|
|
/* construct state table data
|
|
*/
|
|
*tcp_con_data = PR_NEW(TCP_ConData);
|
|
|
|
if(!*tcp_con_data)
|
|
{
|
|
*error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
|
|
return(MK_OUT_OF_MEMORY);
|
|
}
|
|
|
|
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 = PL_strlen(XP_GetString(XP_PROGRESS_UNABLELOCATE));
|
|
char * buf;
|
|
if( (PREF_OK == PREF_CopyCharPref(pref_socksServer,&prefSocksHost))
|
|
&& prefSocksHost)
|
|
{
|
|
len += PL_strlen(prefSocksHost);
|
|
|
|
buf = (char *)PR_Malloc((len+10)*sizeof(char));
|
|
if(buf)
|
|
{
|
|
PR_snprintf(buf, (len+10)*sizeof(char),
|
|
XP_GetString(XP_PROGRESS_UNABLELOCATE), prefSocksHost);
|
|
#if defined(SMOOTH_PROGRESS)
|
|
PM_Status(window_id, NULL, buf);
|
|
#else
|
|
NET_Progress(window_id, buf);
|
|
#endif
|
|
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);
|
|
PR_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 = PL_strchr(host, ':');
|
|
if ( port ) {
|
|
StrAllocCat(althost, port);
|
|
}
|
|
}
|
|
|
|
status = net_FindAddress(althost?althost:host,
|
|
&((*tcp_con_data)->net_addr),
|
|
window_id,
|
|
*sock);
|
|
|
|
if (althost) {
|
|
PR_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 = PL_strlen(XP_GetString(XP_PROGRESS_UNABLELOCATE));
|
|
char * buf;
|
|
|
|
len += PL_strlen(host);
|
|
|
|
buf = (char *)PR_Malloc((len+10)*sizeof(char));
|
|
if(buf)
|
|
{
|
|
PR_snprintf(buf, (len+10)*sizeof(char),
|
|
XP_GetString(XP_PROGRESS_UNABLELOCATE), host);
|
|
#if defined(SMOOTH_PROGRESS)
|
|
PM_Status(window_id, NULL, buf);
|
|
#else
|
|
NET_Progress(window_id, buf);
|
|
#endif
|
|
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;
|
|
}
|
|
|
|
TIMING_STARTCLOCK_NAME("tcp:connect", url);
|
|
|
|
status = net_start_first_connect(host, *sock, window_id,
|
|
*tcp_con_data, error_msg, localIP);
|
|
|
|
if(status != MK_WAITING_FOR_CONNECTION)
|
|
{
|
|
TIMING_STOPCLOCK_NAME("tcp:connect", url, window_id, "error");
|
|
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 */
|
|
HG83665
|
|
}
|
|
|
|
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,
|
|
PRUint32 localIP)
|
|
{
|
|
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;
|
|
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 = PL_strlen(XP_GetString(XP_PROGRESS_UNABLELOCATE));
|
|
char * buf;
|
|
|
|
len += PL_strlen(host);
|
|
|
|
buf = (char *)PR_Malloc((len+10)*sizeof(char));
|
|
if(buf)
|
|
{
|
|
PR_snprintf(buf, (len+10)*sizeof(char),
|
|
XP_GetString(XP_PROGRESS_UNABLELOCATE), host);
|
|
#if defined(SMOOTH_PROGRESS)
|
|
PM_Status(window_id, NULL, buf);
|
|
#else
|
|
NET_Progress(window_id, buf);
|
|
#endif
|
|
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;
|
|
}
|
|
|
|
TIMING_STARTCLOCK_NAME("tcp:connect", url);
|
|
|
|
status = net_start_first_connect(host, *sock, window_id,
|
|
*tcp_con_data, error_msg, localIP);
|
|
|
|
if(status != MK_WAITING_FOR_CONNECTION)
|
|
{
|
|
TIMING_STOPCLOCK_NAME("tcp:connect", url, window_id, "error");
|
|
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);
|
|
TRACEMSG(("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)
|
|
{
|
|
PR_ASSERT(0);
|
|
return MK_WAITING_FOR_CONNECTION; /* perhaps we should error here */
|
|
}
|
|
|
|
#if defined(XP_WIN16)
|
|
/* Bind to a user specified local IP address if specified.
|
|
* Netlib does not check the validity of this address.
|
|
* The user is guaranteing it's existence and usability. */
|
|
if ( localIP > 0 ) {
|
|
PRStatus status;
|
|
PRErrorCode errorCode; /* see http://www.mozilla.org/docs/refList/refNSPR/prerr.htm#1027954 */
|
|
PRNetAddr *addr = (PRNetAddr *)PR_Malloc(sizeof(PRNetAddr));
|
|
if (!addr)
|
|
return MK_UNABLE_TO_CONNECT;
|
|
status = PR_InitializeNetAddr(PR_IpAddrNull, 0, addr);
|
|
if (status != PR_SUCCESS) {
|
|
errorCode = PR_GetError();
|
|
} else {
|
|
addr->inet.ip = localIP;
|
|
status = PR_Bind(sock, addr);
|
|
if (status != PR_SUCCESS) {
|
|
errorCode = PR_GetError();
|
|
}
|
|
}
|
|
PR_Free(addr);
|
|
}
|
|
|
|
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)
|
|
PR_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. */
|
|
TRACEMSG(("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 = PL_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,
|
|
HG98376
|
|
tcp_con_data,
|
|
window_id,
|
|
error_msg,
|
|
0,
|
|
0,
|
|
localIP));
|
|
/* else fall through to the error */
|
|
}
|
|
else
|
|
FREE(host);
|
|
|
|
TIMING_STOPCLOCK_NAME("tcp:connect", url, window_id, "error");
|
|
|
|
HG92362
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
TIMING_STOPCLOCK_NAME("tcp:connect", url, window_id, "ok");
|
|
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 */
|
|
|
|
PR_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 :(
|
|
*/
|
|
return;
|
|
}
|