Description
CosmoSQLClient-Swift fails to establish connections to Azure SQL Database, throwing an IOError: Connection reset by peer (errno: 54) during the TLS handshake phase.
Environment
- Package Version: v1.6.4
- Platform: macOS (also likely affects Linux)
- Target Database: Azure SQL Database
- Swift Version: 5.9+
Reproduction
import CosmoMSSQL
let config = MSSQLConnection.Configuration(
host: "your-server.database.windows.net",
port: 1433,
database: "your_database",
username: "username",
password: "password",
tls: .require,
trustServerCertificate: true
)
let pool = MSSQLConnectionPool(configuration: config)
let rows = try await pool.withConnection { conn in
try await conn.query("SELECT 1")
}
// ❌ Fails with: IOError: Connection reset by peer
Expected Behavior
Successfully connects to Azure SQL Database and executes queries.
Actual Behavior
Connection fails during TLS handshake with error:
IOError: read(descriptor:pointer:size:): Connection reset by peer (errno: 54)
Root Cause
The TDSTLSFramer class in Sources/CosmoMSSQL/TDS/TDSTLSFramer.swift does not properly accumulate fragmented TCP packets. When Azure SQL Database sends a TLS ServerHello response that exceeds the TCP segment size (typically ~4KB TDS packet in ~1.5KB TCP segments), SwiftNIO delivers the data across multiple channelRead callbacks. The current implementation does not maintain state between these calls, causing incomplete packet parsing.
Debug trace showing the issue:
[TDSTLSFramer] Inbound: 2048 bytes // First fragment
[TDSTLSFramer] Incomplete packet - lengthBE=4096, available=2048
[TDSTLSFramer] Inbound: 2700 bytes // Second fragment (not accumulated!)
[TDSTLSFramer] Incomplete packet - lengthBE=8390891566170136948 // Corrupt length
❌ Connection reset
Proposed Fix
Add buffer accumulation to TDSTLSFramer:
final class TDSTLSFramer: ChannelDuplexHandler, @unchecked Sendable {
// ... existing code ...
// ADD: Buffer to accumulate fragmented packets
private var accumulatedBuffer: ByteBuffer?
func channelRead(context: ChannelHandlerContext, data: NIOAny) {
guard active else {
context.fireChannelRead(data)
return
}
var incoming = unwrapInboundIn(data)
// Accumulate with previous data
if var accumulated = accumulatedBuffer {
accumulated.writeBuffer(&incoming)
accumulatedBuffer = accumulated
} else {
accumulatedBuffer = incoming
}
guard var buf = accumulatedBuffer else { return }
// Process complete TDS packets
while buf.readableBytes >= 8 {
guard
let lengthBE: UInt16 = buf.getInteger(at: buf.readerIndex + 2, endianness: .big),
Int(lengthBE) <= buf.readableBytes,
Int(lengthBE) >= 8
else {
accumulatedBuffer = buf // Save for next read
return
}
let packetLen = Int(lengthBE)
var packet = buf.readSlice(length: packetLen)!
packet.moveReaderIndex(forwardBy: 8)
context.fireChannelRead(wrapInboundOut(packet))
}
// Clear if fully processed
accumulatedBuffer = buf.readableBytes == 0 ? nil : buf
}
}
Additional Issue (Minor)
The Pre-Login packet advertises TDS 8.0, but TDS 7.4 is more widely compatible:
File: Sources/CosmoMSSQL/TDS/TDSPreLogin.swift line 24
// Change from:
var clientVersion: (UInt8, UInt8, UInt8, UInt8) = (8, 0, 0, 0)
// To:
var clientVersion: (UInt8, UInt8, UInt8, UInt8) = (7, 4, 0, 0)
Verification
After applying the fix, the connection succeeds:
✅ Connected successfully!
✅ Query executed successfully!
Tested against Azure SQL Database with successful SELECT queries on production databases.
Impact
High: Affects all users connecting to Azure SQL Database and potentially on-premises SQL Server instances with large TLS certificates. This is a critical bug for production use with Azure.
Files Affected
Sources/CosmoMSSQL/TDS/TDSTLSFramer.swift (critical)
Sources/CosmoMSSQL/TDS/TDSPreLogin.swift (compatibility improvement)
I'm happy to submit a PR with these fixes if helpful. Full technical analysis available upon request.
Description
CosmoSQLClient-Swift fails to establish connections to Azure SQL Database, throwing an
IOError: Connection reset by peer (errno: 54)during the TLS handshake phase.Environment
Reproduction
Expected Behavior
Successfully connects to Azure SQL Database and executes queries.
Actual Behavior
Connection fails during TLS handshake with error:
Root Cause
The
TDSTLSFramerclass inSources/CosmoMSSQL/TDS/TDSTLSFramer.swiftdoes not properly accumulate fragmented TCP packets. When Azure SQL Database sends a TLS ServerHello response that exceeds the TCP segment size (typically ~4KB TDS packet in ~1.5KB TCP segments), SwiftNIO delivers the data across multiplechannelReadcallbacks. The current implementation does not maintain state between these calls, causing incomplete packet parsing.Debug trace showing the issue:
Proposed Fix
Add buffer accumulation to
TDSTLSFramer:Additional Issue (Minor)
The Pre-Login packet advertises TDS 8.0, but TDS 7.4 is more widely compatible:
File:
Sources/CosmoMSSQL/TDS/TDSPreLogin.swiftline 24Verification
After applying the fix, the connection succeeds:
Tested against Azure SQL Database with successful SELECT queries on production databases.
Impact
High: Affects all users connecting to Azure SQL Database and potentially on-premises SQL Server instances with large TLS certificates. This is a critical bug for production use with Azure.
Files Affected
Sources/CosmoMSSQL/TDS/TDSTLSFramer.swift(critical)Sources/CosmoMSSQL/TDS/TDSPreLogin.swift(compatibility improvement)I'm happy to submit a PR with these fixes if helpful. Full technical analysis available upon request.