WSL2 SQL Server Connection Hang — Root Cause and Fix
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 33.7k
- Forks
- 1.8k
- Avg merge
- 3d 17h
- Merged PRs (30d)
- 116
Description
**Date:** 2026-04-20
**Environment:** WSL2 (Ubuntu, kernel 6.6.87.2-microsoft-standard-WSL2), Cisco AnyConnect VPN, `networkingMode=mirrored`
---
## Symptom
Any attempt to connect to SQL Server from WSL2 via pyodbc fails with:
```
[HYT00] Login timeout expired (SQLDriverConnect)
```
after several seconds (multiple connection attempts × ODBC login timeout + retries).
For example:
```python
import pyodbc
conn = pyodbc.connect(
"DRIVER=ODBC Driver 17 for SQL Server;"
"SERVER=myserver.example.local;"
"DATABASE=mydb;"
"Trusted_Connection=yes;"
)
```
The same connection from a native Windows process (e.g. `sqlcmd`) completes in ~1 second.
---
## Root Cause
The Microsoft ODBC Driver 17 and 18 for SQL Server (Linux) call `bind(sock, 0.0.0.0:0)` on
a freshly created TCP socket before calling `connect()`. This is a redundant but
standard pre-connection step — binding to the wildcard address on port 0 asks the
kernel to assign an ephemeral source port, which `connect()` would do automatically
anyway if `bind()` is skipped.
On this system, that `bind()` call returns **ENOSYS ("Function not implemented")**:
```
bind(3, {sa_family=AF_INET, sin_port=0, sin_addr=0.0.0.0}) = -1 ENOSYS
```
Confirmed via `strace`. Also reproducible directly:
```python
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setblocking(False)
s.bind(('0.0.0.0', 0))
# → OSError: [Errno 38] Function not implemented
```
When `bind()` fails, the ODBC driver closes the socket and retries with exponential
backoff (100 ms → 200 ms → 400 ms → 800 ms → 1 s intervals) until the login timeout
expires. TCP connectivity to the server is otherwise fine — `connect()` without a
prior `bind()` reaches the SQL Server almost immediately.
### Why does bind() fail?
In WSL2 `networkingMode=mirrored`, WSL processes share the Windows host's network
stack directly. Cisco AnyConnect installs a kernel-level socket filter that,
in this configuration, blocks `bind()` on AF_INET sockets.
The failure is consistent whenever AnyConnect is active in mirrored mode,
regardless of the underlying physical network (WiFi or ethernet). The observed
pattern — connections succeeding 1–2 times after a fresh WSL session before all
subsequent attempts fail — is consistent with
AnyConnect's socket filter initializing fully a few seconds after VPN connection
is established. Once the filter is active, bind() fails for every subsequent call
and accumulated socket errors eventually degrade WSL's networking state.
Switching WSL2 to `networkingMode=nat` restores `bind()` but breaks internet
access entirely under AnyConnect, so that is not a viable workaround.
---
## Fix
An LD_PRELOAD shim that intercepts `bind(AF_INET, 0.0.0.0:0)` and returns
success without calling the kernel. All other `bind()` calls (any non-zero
address or port) are passed through to the real implementation unchanged.
### Source (`fix_bind.c`)
```c
#define _GNU_SOURCE
#include
#include
#include
int bind(int fd, const struct sockaddr *addr, socklen_t len) {
if (addr && addr->sa_family == AF_INET) {
const struct sockaddr_in *a = (const struct sockaddr_in *)addr;
if (a->sin_addr.s_addr == 0 && a->sin_port == 0)
return 0; /* redundant bind — connect() auto-assigns source port */
}
static int (*real_bind)(int, const struct sockaddr *, socklen_t) = NULL;
if (!real_bind) real_bind = dlsym(RTLD_NEXT, "bind");
return real_bind(fd, addr, len);
}
```
### Build
```bash
gcc -shared -fPIC -o /usr/local/lib/fix_bind.so fix_bind.c -ldl
```
### Install (system-wide, survives WSL restart)
```bash
sudo bash -c 'echo "/usr/local/lib/fix_bind.so" >> /etc/ld.so.preload'
```
### Result
With the shim in place, `pyodbc.connect()` completes almost immediately instead of
timing out after several seconds.
---
## Safety
The shim intercepts only `bind(AF_INET, 0.0.0.0, port=0)`. This call is
semantically a no-op — skipping it has exactly the same observable effect as
letting it succeed, because `connect()` auto-assigns a source address and port
in both cases. No other `bind()` call is affected.
---
## Notes
If others hit this issue in WSL2 with AnyConnect and `networkingMode=mirrored`,
the shim above is the practical workaround. A cleaner fix would be for the
Microsoft ODBC Driver to make the pre-connection `bind()` call conditional on
the platform — skipping it on Linux when the address is wildcard, since
`connect()` handles source port assignment automatically.
---
*Root cause diagnosed with assistance from [Claude Code](https://claude.ai/code) (Anthropic).*
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the failure with the Python socket.bind example under WSL2 mirrored networking and Cisco AnyConnect, then use strace to confirm the ENOSYS call. Review fix_bind.c and validate the proposed behavior with pyodbc.connect; done means SQL Server connections no longer hang while other bind calls remain unaffected.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c, linux, python, ubuntu
- Domain
- networking, operating-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100