I am attempting to write a function to verify web servers behind a load balancer. As a result, I will need to connect to an IP address that is not registered in DNS because DNS will be registered to the front-end IP address of the load balancer. Other suggestions have been to use something like "https://192.168.1.17/ and ignore certificate errors, but I need to validate the certificate as part of the process. I believe the best option would be to use a connection call back method, but I have not been able to get something that is close to working.
Here a code snippet from one of my attempts.
$SourceUri = [System.Uri]::New("https://site.domain.com/healthCheck")
[System.Net.IPAddress]$IPAddress = '192.168.1.17'
[int]$Port = 443
$SocketsHttpHandler = [System.Net.Http.SocketsHttpHandler]::New()
$SocketsHttpHandler.ConnectCallback = function:{
param([System.Net.IPAddress]$IPAddress, [int]$Port)
$Socket = [System.Net.Sockets.Socket]::New([System.Net.Sockets.SocketType]::Stream, [System.Net.Sockets.ProtocolType]::Tcp)
$Socket.NoDelay = $true
$Socket.ConnectAsync($IPAddress, $Port)
$NetworkStream = [System.Net.Sockets.NetworkStream]::New($Socket, $true)
return $NetworkStream
}
$HttpClient = [System.Net.Http.HttpClient]::New($SocketsHttpHandler($IPAddress, $Port))
$task = $HttpClient.GetStringAsync($SourceUri)
$task.wait()
if ($task.IsCompleted) {
Write-Output $task.Result
} else {
Write-Output "Something went wrong: " + $task.Exception.Message
}
Please let me know what I should do to get the call back method working. Thank you.
Update I think I made some forward progress, but receiving an error setting the callback.
$SourceUri = [System.Uri]::New("https://site.domain.com/healthCheck")
[System.Net.IPAddress]$IPAddress = '192.168.1.17'
[int]$Port = 443
$Code = @'
using System;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
public sealed class SocketCallbackClosure
{
public IPAddress IPAddress { get; }
public int Port { get; }
public Func<SocketsHttpConnectionContext, CancellationToken, Task<NetworkStream>> Callback { get; }
public SocketCallbackClosure(IPAddress IPAddress, int Port)
{
this.IPAddress = IPAddress;
this.Port = Port;
this.Callback = CallbackImpl;
}
private async Task<NetworkStream> CallbackImpl(
SocketsHttpConnectionContext ConnectionContext,
CancellationToken CToken)
{
var s = new Socket(SocketType.Stream, ProtocolType.Tcp) { NoDelay = true };
try
{
s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
s.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, 5);
s.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, 5);
s.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveRetryCount, 5);
await s.ConnectAsync(this.IPAddress, this.Port, CToken);
return new NetworkStream(s, ownsSocket: true);
}
catch
{
s.Dispose();
throw;
}
}
}
'@
Add-Type -TypeDefinition $Code
$SocketCallbackClosure = [SocketCallbackClosure]::New($IPAddress, $Port)
$SocketsHttpHandler = [System.Net.Http.SocketsHttpHandler]::New()
$SocketsHttpHandler.ConnectCallback = $SocketCallbackClosure.Callback
$HttpClient = [System.Net.Http.HttpClient]::New($SocketsHttpHandler)
$task = $HttpClient.GetStringAsync($SourceUri)
$task.wait()
if ($task.IsCompleted) {
Write-Output $task.Result
} else {
Write-Output "Something went wrong: " + $task.Exception.Message
}