|
- 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()
- }
|