param( [string]$ConfigPath = (Join-Path (Split-Path -Parent $PSScriptRoot) 'config.ini') ) $ErrorActionPreference = 'Stop' $settings = @{} foreach ($line in Get-Content -LiteralPath $ConfigPath -Encoding UTF8) { if ($line -match '^\s*([A-Z_]+)\s*=\s*(.*?)\s*$') { $settings[$matches[1]] = $matches[2] } } $smtpHost = $settings['SMTP_HOST'] $smtpPort = 0 if ([string]::IsNullOrWhiteSpace($smtpHost) -or -not [int]::TryParse($settings['SMTP_PORT'], [ref]$smtpPort) -or $smtpPort -lt 1 -or $smtpPort -gt 65535) { throw 'Valid SMTP_HOST and SMTP_PORT are required in the config file' } function Read-Reply($reader) { $lines = New-Object 'System.Collections.Generic.List[string]' do { $line = $reader.ReadLine() if ($null -eq $line -or $line -notmatch '^\d{3}[- ]') { throw "Invalid or incomplete SMTP reply: $line" } $lines.Add($line) } while ($line.Substring(3, 1) -eq '-') return ,$lines.ToArray() } $client = New-Object System.Net.Sockets.TcpClient try { $connect = $client.BeginConnect($smtpHost, $smtpPort, $null, $null) if (-not $connect.AsyncWaitHandle.WaitOne(10000)) { throw 'SMTP connection timed out' } $client.EndConnect($connect) $client.ReceiveTimeout = 10000 $client.SendTimeout = 10000 $stream = $client.GetStream() $reader = New-Object System.IO.StreamReader($stream, [System.Text.Encoding]::ASCII) $writer = New-Object System.IO.StreamWriter($stream, [System.Text.Encoding]::ASCII) $writer.NewLine = "`r`n" $writer.AutoFlush = $true $greeting = Read-Reply $reader if (-not $greeting[-1].StartsWith('220')) { throw "SMTP greeting rejected: $($greeting[-1])" } $writer.WriteLine('EHLO localhost') $ehlo = Read-Reply $reader if (-not $ehlo[-1].StartsWith('250')) { throw "EHLO rejected: $($ehlo[-1])" } if (-not ($ehlo | Where-Object { $_ -match '^250[- ]STARTTLS(?:\s|$)' })) { throw 'Server did not advertise STARTTLS' } $writer.WriteLine('STARTTLS') $startTls = Read-Reply $reader if (-not $startTls[-1].StartsWith('220')) { throw "STARTTLS rejected: $($startTls[-1])" } $secureStream = New-Object System.Net.Security.SslStream($stream, $false) $secureStream.AuthenticateAsClient($smtpHost) if (-not $secureStream.IsAuthenticated -or -not $secureStream.IsEncrypted) { throw 'TLS negotiation did not establish an encrypted session' } Write-Output "SMTP STARTTLS OK: $smtpHost`:$smtpPort ($($secureStream.SslProtocol))" $secureStream.Close() } finally { $client.Close() }