From 883e62da46a04142e1c2fbd20f0d74f3e38261b8 Mon Sep 17 00:00:00 2001 From: benoitc Date: Mon, 25 Nov 2019 11:53:02 +0100 Subject: [PATCH] socketfromfd: fix cross platform usage on osx and maybe some other systems, SO_PROTOCOL is not set, also the socket family is not detected correctly. This patch remove default values in socketfromfd and do the following: * detect proper family using getsockname instead of getsocktopt * if no type is given, default to SOCK_STREAM (we don't have any other type) * if no protocol is given, default to 0 and let the system take care of it. --- gunicorn/socketfromfd.py | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/gunicorn/socketfromfd.py b/gunicorn/socketfromfd.py index a7f7b899..521e0f00 100644 --- a/gunicorn/socketfromfd.py +++ b/gunicorn/socketfromfd.py @@ -16,10 +16,6 @@ from .util import find_library __all__ = ('fromfd',) -SO_DOMAIN = getattr(socket, 'SO_DOMAIN', 39) -SO_TYPE = getattr(socket, 'SO_TYPE', 3) -SO_PROTOCOL = getattr(socket, 'SO_PROTOCOL', 38) - _libc_name = find_library('c') if _libc_name is not None: if sys.platform.startswith("aix"): @@ -47,8 +43,12 @@ def _errcheck_errno(result, func, arguments): if platform.system() == 'SunOS': _libc_getsockopt = libc._so_getsockopt + _libc_getsockname = libc._so_getsockname else: _libc_getsockopt = libc.getsockopt + _libc_getsockname = libc.getsockname + + _libc_getsockopt.argtypes = [ ctypes.c_int, # int sockfd ctypes.c_int, # int level @@ -59,6 +59,17 @@ _libc_getsockopt.argtypes = [ _libc_getsockopt.restype = ctypes.c_int # 0: ok, -1: err _libc_getsockopt.errcheck = _errcheck_errno +class SockAddr(ctypes.Structure): + _fields_ = [ + ('sa_len', ctypes.c_uint8), + ('sa_family', ctypes.c_uint8), + ('sa_data', ctypes.c_char * 14) + ] +_libc_getsockname.argtypes = [ + ctypes.c_int, + ctypes.POINTER(SockAddr), + ctypes.POINTER(ctypes.c_int) +] def _raw_getsockopt(fd, level, optname): """Make raw getsockopt() call for int32 optval @@ -74,6 +85,11 @@ def _raw_getsockopt(fd, level, optname): ctypes.byref(optval), ctypes.byref(optlen)) return optval.value +def _raw_getsockname(fd): + sockaddr = SockAddr() + len = ctypes.c_int(ctypes.sizeof(sockaddr)) + _libc_getsockname(fd, sockaddr, len) + return sockaddr.sa_family def fromfd(fd, keep_fd=True): """Create a socket from a file descriptor @@ -92,10 +108,15 @@ def fromfd(fd, keep_fd=True): :return: socket.socket instance :raises OSError: for invalid socket fd """ - family = _raw_getsockopt(fd, socket.SOL_SOCKET, SO_DOMAIN) - typ = _raw_getsockopt(fd, socket.SOL_SOCKET, SO_TYPE) - proto = _raw_getsockopt(fd, socket.SOL_SOCKET, SO_PROTOCOL) - s + family = _raw_getsockname(fd) + if hasattr(socket, 'SO_TYPE'): + typ = _raw_getsockopt(fd, socket.SOL_SOCKET, getattr(socket, 'SO_TYPE')) + else: + typ = socket.SOCK_STREAM + if hasattr(socket, 'SO_PROTOCOL'): + proto = _raw_getsockopt(fd, socket.SOL_SOCKET, getattr(socket, 'SO_PROTOCOL')) + else: + proto = 0 if keep_fd: return socket.fromfd(fd, family, typ, proto) else: