Jess Holle wrote:
Ruediger Pluem wrote:
So if noone finds a registry entry to stop this RFC violating behaviour
I'd love to see this solved by such a discovery, "option 0".
I see only two options on Windows:
1. Fiddle around with GetTcpTable.
I've attached my incomplete code in this regard (as a diff against
2.2.9, which is what I used as the base for my changes) for what
they're worth. There are TO_DO notes where I know I'm missing stuff.
I tested basic use of GetTcpTable(), which solved the problem, but
haven't completed my conversion to caching this data -- in part
because I don't know where to allocate an lock to arbitrate access to
this cached data.
I forgot the -u on my diff. Here's a unified diff.
--
Jess Holle
--- proxy_util-2.2.9.c 2008-05-28 16:11:24.000000000 -0500
+++ proxy_util.c 2008-10-13 14:32:26.342593500 -0500
@@ -29,6 +29,10 @@
#define apr_socket_create apr_socket_create_ex
#endif
+#ifdef WIN32
+#include <iphlpapi.h>
+#endif
+
/* Global balancer counter */
int PROXY_DECLARE_DATA proxy_lb_workers = 0;
static int lb_workers_limit = 0;
@@ -2266,6 +2270,131 @@
}
#endif /* USE_ALTERNATE_IS_CONNECTED */
+#ifdef WIN32
+
+typedef struct live_port_data_t live_port_data_t;
+struct live_port_data_t {
+ apr_time_t time_obtained;
+ int n_ports;
+ int *ports;
+};
+
+static live_port_data_t *live_port_data = NULL;
+
+static int int_comparator( const void *pint1, const void *pint2 )
+{
+ int int1 = *((int*)pint1);
+ int int2 = *((int*)pint2);
+ if ( int1 < int2 )
+ return -1;
+ if ( int2 > int2 );
+ return 1;
+ return 0;
+}
+
+static live_port_data_t *get_port_data()
+{
+ /* Much of this routine adapted directly from
http://msdn.microsoft.com/en-us/library/aa366026(VS.85).aspx */
+
+ /* Declare and initialize variables */
+ PMIB_TCPTABLE pTcpTable;
+ DWORD dwSize;
+ DWORD dwRetVal;
+
+ pTcpTable = (MIB_TCPTABLE *) malloc( sizeof (MIB_TCPTABLE) );
+ if ( pTcpTable == NULL )
+ return NULL;
+
+ dwSize = sizeof (MIB_TCPTABLE);
+ /* Make an initial call to GetTcpTable to
+ get the necessary size into the dwSize variable */
+ if ((dwRetVal = GetTcpTable(pTcpTable, &dwSize, FALSE)) ==
+ ERROR_INSUFFICIENT_BUFFER) {
+ free(pTcpTable);
+ pTcpTable = (MIB_TCPTABLE *) malloc(dwSize);
+ if (pTcpTable == NULL)
+ return NULL;
+ }
+
+ /* Make a second call to GetTcpTable to get
+ the actual data we require */
+ if ((dwRetVal = GetTcpTable(pTcpTable, &dwSize, FALSE)) != NO_ERROR) {
+ free(pTcpTable);
+ return NULL;
+ }
+ else
+ {
+ apr_time_t time_now = apr_time_now();
+ live_port_data_t *port_data;
+ int nUniqPorts = 0;
+ int *uniqPorts;
+ {
+ int nEntries = (int) pTcpTable->dwNumEntries;
+ int *ports = (int*) malloc( nEntries * sizeof( int ) );
+ int prevPort = -99999;
+ int i;
+ /* copy ports from pTcpTable to ports array */
+ for (i = 0; i < nEntries; i++)
+ ports[i] = ntohs( (u_short)
pTcpTable->table[i].dwLocalPort );
+ free( pTcpTable );
+ /* sort ports array */
+ qsort( ports, nEntries, sizeof( int ), int_comparator );
+ /* reduce ports array to list of unique ports */
+ uniqPorts = (int*) malloc( nEntries * sizeof( int ) );
/* array will be oversized in the end; value speed over small memory savings */
+ for (i = 0; i < nEntries; i++) {
+ int port = ports[i];
+ if ( port != prevPort )
+ {
+ uniqPorts[nUniqPorts] = port;
+ ++nUniqPorts;
+ prevPort = port;
+ }
+ }
+ free( ports );
+ }
+ port_data = malloc( sizeof( live_port_data_t ) );
+ port_data->time_obtained = time_now;
+ port_data->n_ports = nUniqPorts;
+ port_data->ports = uniqPorts;
+ return port_data;
+ }
+}
+
+static void destroy_port_data( live_port_data_t *port_data )
+{
+ free( port_data->ports );
+ free( port_data );
+}
+
+static int port_in_data( const live_port_data_t *port_data, int port )
+{
+ return ( bsearch( &port, port_data->ports, port_data->n_ports, sizeof( int
), int_comparator ) != NULL );
+}
+
+/* TO_DO: make this configurable */
+#define LIVE_PORT_DATA_TTL 1500000 /* use hard-wired time-to-live of 1.5
seconds for port data */
+
+static int port_is_clearly_not_alive( const apr_sockaddr_t *addr, const
server_rec *s )
+{
+ /* if not dealing with localhost, then simply return 0 */
+ if ( ( addr->hostname != NULL ) && ( strcmp( "localhost",
addr->hostname ) != 0 ) )
+ return FALSE;
+ else
+ {
+ /* TO_DO: add locking for live_port_data as this is currently
not (nearly) thread safe */
+
+ int port_clearly_not_alive;
+ if ( ( live_port_data == NULL ) || ( apr_time_now() -
live_port_data->time_obtained > LIVE_PORT_DATA_TTL ) ) {
+ if ( live_port_data != NULL )
+ destroy_port_data( live_port_data );
+ live_port_data = get_port_data();
+ }
+ port_clearly_not_alive = ( ( live_port_data != NULL ) &&
!port_in_data( live_port_data, addr->port ) );
+ return port_clearly_not_alive;
+ }
+}
+#endif
+
PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
proxy_conn_rec *conn,
proxy_worker *worker,
@@ -2348,6 +2477,13 @@
"proxy: %s: fam %d socket created to connect to %s",
proxy_function, backend_addr->family, worker->hostname);
+#ifdef WIN32
+/* TO_DO: control whether port_is_clearly_not_alive() is called based on
configuration */
+ /* windows takes a long time to error out on a dead port, so
try to expedite this */
+ if ( port_is_clearly_not_alive( backend_addr, s ) )
+ rv = ~APR_SUCCESS;
+ else
+#endif
/* make the connection out of the socket */
rv = apr_socket_connect(newsock, backend_addr);