(also on Socket.socket().setsockopt() incompatibility with Alpine Linux (and musl libc) — sourcehut lists)
I know that two of the three most suspicious statements it is a fault of interpreter, and it is a fault of libc, but I am slowly sliding towards this. When investigating problems with time representation on 32bit platforms, I run the test suite of M2Crypto in i386/alpine and I found problems with socket.setsockopt call. When running the code distilled into this short script, I found some problems:
import socket
import struct
from M2Crypto import SSL
ctx = SSL.Context()
s = SSL.Connection(ctx)
timeout = SSL.timeout()
print(f"timeout.sec = {timeout.sec}")
print(f"timeout.microsec = {timeout.microsec}")
print(f"timeout.pack() = {timeout.pack()}")
print(f"socket.SOL_SOCKET = {socket.SOL_SOCKET}")
print(f"socket.SO_RCVTIMEO = {socket.SO_RCVTIMEO}")
s.socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, timeout.pack())
When running on i386/debian there is no problem and the script outputs:
$ python3 small_test.py
timeout.sec = 600
timeout.microsec = 0
timeout.pack() = b'X\x02\x00\x00\x00\x00\x00\x00'
socket.SOL_SOCKET = 1
socket.SO_RCVTIMEO = 20
$
while when it is run i386/alpine it fails:
$ python3 small_test.py
Traceback (most recent call last):
File "/builds/m2crypto/m2crypto/small_test.py", line 14, in <module>
s.socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, timeout.pack())
timeout.sec = 600
timeout.microsec = 0
timeout.pack() = b'X\x02\x00\x00\x00\x00\x00\x00'
socket.SOL_SOCKET = 1
socket.SO_RCVTIMEO = 66
OSError: [Errno 22] Invalid argument
$
When studying the problem, I found this comment by @tiran:
SO_RCVTIME0 works only with operating system level sockets. A SSLSocket is not an OS level. It’s a high level abstraction layer that wraps either a file descriptor or a memory BIO. A read operation on a SSLSocket can perform write, a write operation can perform read. For the initial handshake, it will do both.
This means that SO_RCVTIME0 is not supported. Either you have to use the SSLSocket’s timeout feature or do your own socket io and use a memory BIO. The internal timeout feature is build around select()/poll() syscall and low level OpenSSL calls.
Is this relevant here? s.socket is not SSLSocket or anything like that, but if I understand correctly it is just the plain socket.socket underlying all those complicated BIO constructs, isn’t it?