diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2013-08-27 22:53:59 (GMT) |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2013-08-27 22:53:59 (GMT) |
commit | daf455554bc21b6b5df0a016ab5fa639d36cc595 (patch) | |
tree | 216f52f9f6d9aed0406b2ce2574e5a02aa93e327 /Modules/socketmodule.c | |
parent | 46e1ce214b5711e8dae63a1b5a0a7aafb371baf0 (diff) | |
download | cpython-daf455554bc21b6b5df0a016ab5fa639d36cc595.zip cpython-daf455554bc21b6b5df0a016ab5fa639d36cc595.tar.gz cpython-daf455554bc21b6b5df0a016ab5fa639d36cc595.tar.bz2 |
Issue #18571: Implementation of the PEP 446: file descriptors and file handles
are now created non-inheritable; add functions os.get/set_inheritable(),
os.get/set_handle_inheritable() and socket.socket.get/set_inheritable().
Diffstat (limited to 'Modules/socketmodule.c')
-rw-r--r-- | Modules/socketmodule.c | 211 |
1 files changed, 189 insertions, 22 deletions
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index c5efbc1..ca8dade 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -97,13 +97,14 @@ Local naming conventions: /* Socket object documentation */ PyDoc_STRVAR(sock_doc, -"socket([family[, type[, proto]]]) -> socket object\n\ +"socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None) -> socket object\n\ \n\ Open a socket of the given type. The family argument specifies the\n\ address family; it defaults to AF_INET. The type argument specifies\n\ whether this is a stream (SOCK_STREAM, this is the default)\n\ or datagram (SOCK_DGRAM) socket. The protocol argument defaults to 0,\n\ specifying the default protocol. Keyword arguments are accepted.\n\ +The socket is created as non-inheritable.\n\ \n\ A socket object represents one endpoint of a network connection.\n\ \n\ @@ -114,7 +115,7 @@ bind(addr) -- bind the socket to a local address\n\ close() -- close the socket\n\ connect(addr) -- connect the socket to a remote address\n\ connect_ex(addr) -- connect, return an error code instead of an exception\n\ -_dup() -- return a new socket fd duplicated from fileno()\n\ +dup() -- return a new socket fd duplicated from fileno()\n\ fileno() -- return underlying file descriptor\n\ getpeername() -- return remote address [*]\n\ getsockname() -- return local address\n\ @@ -356,22 +357,7 @@ const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); #endif #ifdef MS_WINDOWS -/* On Windows a socket is really a handle not an fd */ -static SOCKET -dup_socket(SOCKET handle) -{ - WSAPROTOCOL_INFO info; - - if (WSADuplicateSocket(handle, GetCurrentProcessId(), &info)) - return INVALID_SOCKET; - - return WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, - FROM_PROTOCOL_INFO, &info, 0, WSA_FLAG_OVERLAPPED); -} #define SOCKETCLOSE closesocket -#else -/* On Unix we can use dup to duplicate the file descriptor of a socket*/ -#define dup_socket(fd) dup(fd) #endif #ifdef MS_WIN32 @@ -499,6 +485,11 @@ select_error(void) (errno == expected) #endif +#ifdef MS_WINDOWS +/* Does WSASocket() support the WSA_FLAG_NO_HANDLE_INHERIT flag? */ +static int support_wsa_no_inherit = -1; +#endif + /* Convenience function to raise an error according to errno and return a NULL pointer from a function. */ @@ -1955,6 +1946,11 @@ sock_accept(PySocketSockObject *s) PyObject *addr = NULL; PyObject *res = NULL; int timeout; +#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) + /* accept4() is available on Linux 2.6.28+ and glibc 2.10 */ + static int accept4_works = -1; +#endif + if (!getsockaddrlen(s, &addrlen)) return NULL; memset(&addrbuf, 0, addrlen); @@ -1963,10 +1959,24 @@ sock_accept(PySocketSockObject *s) return select_error(); BEGIN_SELECT_LOOP(s) + Py_BEGIN_ALLOW_THREADS timeout = internal_select_ex(s, 0, interval); if (!timeout) { +#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) + if (accept4_works != 0) { + newfd = accept4(s->sock_fd, SAS2SA(&addrbuf), &addrlen, + SOCK_CLOEXEC); + if (newfd == INVALID_SOCKET && accept4_works == -1) { + /* On Linux older than 2.6.28, accept4() fails with ENOSYS */ + accept4_works = (errno != ENOSYS); + } + } + if (accept4_works == 0) + newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen); +#else newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen); +#endif } Py_END_ALLOW_THREADS @@ -1979,6 +1989,25 @@ sock_accept(PySocketSockObject *s) if (newfd == INVALID_SOCKET) return s->errorhandler(); +#ifdef MS_WINDOWS + if (!SetHandleInformation((HANDLE)newfd, HANDLE_FLAG_INHERIT, 0)) { + PyErr_SetFromWindowsErr(0); + SOCKETCLOSE(newfd); + goto finally; + } +#else + +#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) + if (!accept4_works) +#endif + { + if (_Py_set_inheritable(newfd, 0, NULL) < 0) { + SOCKETCLOSE(newfd); + goto finally; + } + } +#endif + sock = PyLong_FromSocket_t(newfd); if (sock == NULL) { SOCKETCLOSE(newfd); @@ -3909,6 +3938,12 @@ sock_new(PyTypeObject *type, PyObject *args, PyObject *kwds) /* Initialize a new socket object. */ +#ifdef SOCK_CLOEXEC +/* socket() and socketpair() fail with EINVAL on Linux kernel older + * than 2.6.27 if SOCK_CLOEXEC flag is set in the socket type. */ +static int sock_cloexec_works = -1; +#endif + /*ARGSUSED*/ static int sock_initobj(PyObject *self, PyObject *args, PyObject *kwds) @@ -3918,6 +3953,13 @@ sock_initobj(PyObject *self, PyObject *args, PyObject *kwds) SOCKET_T fd = INVALID_SOCKET; int family = AF_INET, type = SOCK_STREAM, proto = 0; static char *keywords[] = {"family", "type", "proto", "fileno", 0}; +#ifndef MS_WINDOWS +#ifdef SOCK_CLOEXEC + int *atomic_flag_works = &sock_cloexec_works; +#else + int *atomic_flag_works = NULL; +#endif +#endif if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iiiO:socket", keywords, @@ -3962,14 +4004,74 @@ sock_initobj(PyObject *self, PyObject *args, PyObject *kwds) } } else { +#ifdef MS_WINDOWS + /* Windows implementation */ +#ifndef WSA_FLAG_NO_HANDLE_INHERIT +#define WSA_FLAG_NO_HANDLE_INHERIT 0x80 +#endif + Py_BEGIN_ALLOW_THREADS - fd = socket(family, type, proto); + if (support_wsa_no_inherit) { + fd = WSASocket(family, type, proto, + NULL, 0, + WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT); + if (fd == INVALID_SOCKET) { + /* Windows 7 or Windows 2008 R2 without SP1 or the hotfix */ + support_wsa_no_inherit = 0; + fd = socket(family, type, proto); + } + } + else { + fd = socket(family, type, proto); + } Py_END_ALLOW_THREADS if (fd == INVALID_SOCKET) { set_error(); return -1; } + + if (!support_wsa_no_inherit) { + if (!SetHandleInformation((HANDLE)fd, HANDLE_FLAG_INHERIT, 0)) { + closesocket(fd); + PyErr_SetFromWindowsErr(0); + return -1; + } + } +#else + /* UNIX */ + Py_BEGIN_ALLOW_THREADS +#ifdef SOCK_CLOEXEC + if (sock_cloexec_works != 0) { + fd = socket(family, type | SOCK_CLOEXEC, proto); + if (sock_cloexec_works == -1) { + if (fd >= 0) { + sock_cloexec_works = 1; + } + else if (errno == EINVAL) { + /* Linux older than 2.6.27 does not support SOCK_CLOEXEC */ + sock_cloexec_works = 0; + fd = socket(family, type, proto); + } + } + } + else +#endif + { + fd = socket(family, type, proto); + } + Py_END_ALLOW_THREADS + + if (fd == INVALID_SOCKET) { + set_error(); + return -1; + } + + if (_Py_set_inheritable(fd, 0, atomic_flag_works) < 0) { + SOCKETCLOSE(fd); + return -1; + } +#endif } init_sockobject(s, fd, family, type, proto); @@ -4535,16 +4637,36 @@ socket_dup(PyObject *self, PyObject *fdobj) { SOCKET_T fd, newfd; PyObject *newfdobj; - +#ifdef MS_WINDOWS + WSAPROTOCOL_INFO info; +#endif fd = PyLong_AsSocket_t(fdobj); if (fd == (SOCKET_T)(-1) && PyErr_Occurred()) return NULL; - newfd = dup_socket(fd); +#ifdef MS_WINDOWS + if (WSADuplicateSocket(fd, GetCurrentProcessId(), &info)) + return set_error(); + + newfd = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + &info, 0, WSA_FLAG_OVERLAPPED); if (newfd == INVALID_SOCKET) return set_error(); + if (!SetHandleInformation((HANDLE)newfd, HANDLE_FLAG_INHERIT, 0)) { + closesocket(newfd); + PyErr_SetFromWindowsErr(0); + return NULL; + } +#else + /* On UNIX, dup can be used to duplicate the file descriptor of a socket */ + newfd = _Py_dup(fd); + if (newfd == INVALID_SOCKET) + return NULL; +#endif + newfdobj = PyLong_FromSocket_t(newfd); if (newfdobj == NULL) SOCKETCLOSE(newfd); @@ -4572,6 +4694,12 @@ socket_socketpair(PyObject *self, PyObject *args) SOCKET_T sv[2]; int family, type = SOCK_STREAM, proto = 0; PyObject *res = NULL; +#ifdef SOCK_CLOEXEC + int *atomic_flag_works = &sock_cloexec_works; +#else + int *atomic_flag_works = NULL; +#endif + int ret; #if defined(AF_UNIX) family = AF_UNIX; @@ -4581,9 +4709,38 @@ socket_socketpair(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "|iii:socketpair", &family, &type, &proto)) return NULL; + /* Create a pair of socket fds */ - if (socketpair(family, type, proto, sv) < 0) + Py_BEGIN_ALLOW_THREADS +#ifdef SOCK_CLOEXEC + if (sock_cloexec_works != 0) { + ret = socketpair(family, type | SOCK_CLOEXEC, proto, sv); + if (sock_cloexec_works == -1) { + if (ret >= 0) { + sock_cloexec_works = 1; + } + else if (errno == EINVAL) { + /* Linux older than 2.6.27 does not support SOCK_CLOEXEC */ + sock_cloexec_works = 0; + ret = socketpair(family, type, proto, sv); + } + } + } + else +#endif + { + ret = socketpair(family, type, proto, sv); + } + Py_END_ALLOW_THREADS + + if (ret < 0) return set_error(); + + if (_Py_set_inheritable(sv[0], 0, atomic_flag_works) < 0) + goto finally; + if (_Py_set_inheritable(sv[1], 0, atomic_flag_works) < 0) + goto finally; + s0 = new_sockobject(sv[0], family, type, proto); if (s0 == NULL) goto finally; @@ -4605,7 +4762,7 @@ finally: } PyDoc_STRVAR(socketpair_doc, -"socketpair([family[, type[, proto]]]) -> (socket object, socket object)\n\ +"socketpair([family[, type [, proto]]]) -> (socket object, socket object)\n\ \n\ Create a pair of socket objects from the sockets returned by the platform\n\ socketpair() function.\n\ @@ -5539,6 +5696,16 @@ PyInit__socket(void) if (!os_init()) return NULL; +#ifdef MS_WINDOWS + if (support_wsa_no_inherit == -1) { + DWORD version = GetVersion(); + DWORD major = (DWORD)LOBYTE(LOWORD(version)); + DWORD minor = (DWORD)HIBYTE(LOWORD(version)); + /* need Windows 7 SP1, 2008 R2 SP1 or later */ + support_wsa_no_inherit = (major >= 6 && minor >= 1); + } +#endif + Py_TYPE(&sock_type) = &PyType_Type; m = PyModule_Create(&socketmodule); if (m == NULL) |