summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/socket.rst15
-rw-r--r--Lib/test/test_asyncore.py3
-rw-r--r--Lib/test/test_socket.py76
-rw-r--r--Misc/ACKS1
-rw-r--r--Misc/NEWS6
-rw-r--r--Modules/socketmodule.c39
-rwxr-xr-xconfigure4
-rw-r--r--configure.in2
-rw-r--r--pyconfig.h.in3
9 files changed, 137 insertions, 12 deletions
diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst
index d662457..cdfedc4 100644
--- a/Doc/library/socket.rst
+++ b/Doc/library/socket.rst
@@ -157,6 +157,21 @@ The module :mod:`socket` exports the following constants and functions:
:func:`socket`. (Only :const:`SOCK_STREAM` and :const:`SOCK_DGRAM` appear to be
generally useful.)
+.. data:: SOCK_CLOEXEC
+ SOCK_NONBLOCK
+
+ These two constants, if defined, can be combined with the socket types and
+ allow you to set some flags atomically (thus avoiding possible race
+ conditions and the need for separate calls).
+
+ .. seealso::
+
+ `Secure File Descriptor Handling <http://udrepper.livejournal.com/20407.html>`_
+ for a more thorough explanation.
+
+ Availability: Linux >= 2.6.27.
+
+ .. versionadded:: 3.2
.. data:: SO_*
SOMAXCONN
diff --git a/Lib/test/test_asyncore.py b/Lib/test/test_asyncore.py
index 205efb9..f2ec8b8 100644
--- a/Lib/test/test_asyncore.py
+++ b/Lib/test/test_asyncore.py
@@ -692,7 +692,8 @@ class BaseTestAPI(unittest.TestCase):
s = asyncore.dispatcher()
s.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.assertEqual(s.socket.family, socket.AF_INET)
- self.assertEqual(s.socket.type, socket.SOCK_STREAM)
+ SOCK_NONBLOCK = getattr(socket, 'SOCK_NONBLOCK', 0)
+ self.assertEqual(s.socket.type, socket.SOCK_STREAM | SOCK_NONBLOCK)
def test_bind(self):
s1 = asyncore.dispatcher()
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
index 959e72a..aadfdfa 100644
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -17,6 +17,10 @@ import contextlib
from weakref import proxy
import signal
import math
+try:
+ import fcntl
+except ImportError:
+ fcntl = False
def try_address(host, port=0, family=socket.AF_INET):
"""Try to bind a socket on the given host:port and return True
@@ -902,6 +906,26 @@ class NonBlockingTCPTests(ThreadedTCPSocketTest):
def _testSetBlocking(self):
pass
+ if hasattr(socket, "SOCK_NONBLOCK"):
+ def testInitNonBlocking(self):
+ # reinit server socket
+ self.serv.close()
+ self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM |
+ socket.SOCK_NONBLOCK)
+ self.port = support.bind_port(self.serv)
+ self.serv.listen(1)
+ # actual testing
+ start = time.time()
+ try:
+ self.serv.accept()
+ except socket.error:
+ pass
+ end = time.time()
+ self.assertTrue((end - start) < 1.0, "Error creating with non-blocking mode.")
+
+ def _testInitNonBlocking(self):
+ pass
+
def testAccept(self):
# Testing non-blocking accept
self.serv.setblocking(0)
@@ -1801,6 +1825,56 @@ class ContextManagersTest(ThreadedTCPSocketTest):
self.assertTrue(sock._closed)
self.assertRaises(socket.error, sock.sendall, b'foo')
+@unittest.skipUnless(hasattr(socket, "SOCK_CLOEXEC"),
+ "SOCK_CLOEXEC not defined")
+@unittest.skipUnless(fcntl, "module fcntl not available")
+class CloexecConstantTest(unittest.TestCase):
+ def test_SOCK_CLOEXEC(self):
+ s = socket.socket(socket.AF_INET,
+ socket.SOCK_STREAM | socket.SOCK_CLOEXEC)
+ self.assertTrue(s.type & socket.SOCK_CLOEXEC)
+ self.assertTrue(fcntl.fcntl(s, fcntl.F_GETFD) & fcntl.FD_CLOEXEC)
+
+
+@unittest.skipUnless(hasattr(socket, "SOCK_NONBLOCK"),
+ "SOCK_NONBLOCK not defined")
+class NonblockConstantTest(unittest.TestCase):
+ def checkNonblock(self, s, nonblock=True, timeout=0.0):
+ if nonblock:
+ self.assertTrue(s.type & socket.SOCK_NONBLOCK)
+ self.assertEqual(s.gettimeout(), timeout)
+ else:
+ self.assertFalse(s.type & socket.SOCK_NONBLOCK)
+ self.assertEqual(s.gettimeout(), None)
+
+ def test_SOCK_NONBLOCK(self):
+ # a lot of it seems silly and redundant, but I wanted to test that
+ # changing back and forth worked ok
+ s = socket.socket(socket.AF_INET,
+ socket.SOCK_STREAM | socket.SOCK_NONBLOCK)
+ self.checkNonblock(s)
+ s.setblocking(1)
+ self.checkNonblock(s, False)
+ s.setblocking(0)
+ self.checkNonblock(s)
+ s.settimeout(None)
+ self.checkNonblock(s, False)
+ s.settimeout(2.0)
+ self.checkNonblock(s, timeout=2.0)
+ s.setblocking(1)
+ self.checkNonblock(s, False)
+ # defaulttimeout
+ t = socket.getdefaulttimeout()
+ socket.setdefaulttimeout(0.0)
+ self.checkNonblock(socket.socket())
+ socket.setdefaulttimeout(None)
+ self.checkNonblock(socket.socket(), False)
+ socket.setdefaulttimeout(2.0)
+ self.checkNonblock(socket.socket(), timeout=2.0)
+ socket.setdefaulttimeout(None)
+ self.checkNonblock(socket.socket(), False)
+ socket.setdefaulttimeout(t)
+
def test_main():
tests = [GeneralModuleTests, BasicTCPTest, TCPCloserTest, TCPTimeoutTest,
@@ -1820,6 +1894,8 @@ def test_main():
NetworkConnectionAttributesTest,
NetworkConnectionBehaviourTest,
ContextManagersTest,
+ CloexecConstantTest,
+ NonblockConstantTest
])
if hasattr(socket, "socketpair"):
tests.append(BasicSocketPairTest)
diff --git a/Misc/ACKS b/Misc/ACKS
index 36472ed..f5c6725 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -846,6 +846,7 @@ Alexandre Vassalotti
Frank Vercruesse
Mike Verdone
Jaap Vermeulen
+Nikita Vetoshkin
Al Vezza
Jacques A. Vidrine
John Viega
diff --git a/Misc/NEWS b/Misc/NEWS
index 2d78412..2089ceb 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -21,12 +21,14 @@ Core and Builtins
Library
-------
-- Issue #Issue10063: file:// scheme will stop accessing remote hosts via ftp
+- Issue #7523: Add SOCK_CLOEXEC and SOCK_NONBLOCK to the socket module,
+ where supported by the system. Patch by Nikita Vetoshkin.
+
+- Issue #10063: file:// scheme will stop accessing remote hosts via ftp
protocol. file:// urls had fallback to access remote hosts via ftp. This was
not correct, change is made to raise a URLError when a remote host is tried
to access via file:// scheme.
-
- Issue #1710703: Write structures for an empty ZIP archive when a ZipFile is
created in modes 'a' or 'w' and then closed without adding any files. Raise
BadZipfile (rather than IOError) when opening small non-ZIP files.
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
index 94d5f7c..50ca911 100644
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -615,6 +615,12 @@ internal_setblocking(PySocketSockObject *s, int block)
#ifndef MS_WINDOWS
int delay_flag;
#endif
+#ifdef SOCK_NONBLOCK
+ if (block)
+ s->sock_type &= (~SOCK_NONBLOCK);
+ else
+ s->sock_type |= SOCK_NONBLOCK;
+#endif
Py_BEGIN_ALLOW_THREADS
#ifndef MS_WINDOWS
@@ -764,12 +770,18 @@ init_sockobject(PySocketSockObject *s,
s->sock_family = family;
s->sock_type = type;
s->sock_proto = proto;
- s->sock_timeout = defaulttimeout;
s->errorhandler = &set_error;
-
- if (defaulttimeout >= 0.0)
- internal_setblocking(s, 0);
+#ifdef SOCK_NONBLOCK
+ if (type & SOCK_NONBLOCK)
+ s->sock_timeout = 0.0;
+ else
+#endif
+ {
+ s->sock_timeout = defaulttimeout;
+ if (defaulttimeout >= 0.0)
+ internal_setblocking(s, 0);
+ }
}
@@ -1645,7 +1657,9 @@ sock_accept(PySocketSockObject *s)
PyObject *addr = NULL;
PyObject *res = NULL;
int timeout;
-
+#ifdef HAVE_ACCEPT4
+ int flags = 0;
+#endif
if (!getsockaddrlen(s, &addrlen))
return NULL;
memset(&addrbuf, 0, addrlen);
@@ -1656,8 +1670,15 @@ sock_accept(PySocketSockObject *s)
BEGIN_SELECT_LOOP(s)
Py_BEGIN_ALLOW_THREADS
timeout = internal_select_ex(s, 0, interval);
- if (!timeout)
+ if (!timeout) {
+#ifdef HAVE_ACCEPT4
+ /* inherit socket flags and use accept4 call */
+ flags = s->sock_type & (SOCK_CLOEXEC | SOCK_NONBLOCK);
+ newfd = accept4(s->sock_fd, SAS2SA(&addrbuf), &addrlen, flags);
+#else
newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
+#endif /* HAVE_ACCEPT4 */
+ }
Py_END_ALLOW_THREADS
if (timeout == 1) {
@@ -4599,6 +4620,12 @@ PyInit__socket(void)
#if defined(SOCK_RDM)
PyModule_AddIntConstant(m, "SOCK_RDM", SOCK_RDM);
#endif
+#ifdef SOCK_CLOEXEC
+ PyModule_AddIntConstant(m, "SOCK_CLOEXEC", SOCK_CLOEXEC);
+#endif
+#ifdef SOCK_NONBLOCK
+ PyModule_AddIntConstant(m, "SOCK_NONBLOCK", SOCK_NONBLOCK);
+#endif
#ifdef SO_DEBUG
PyModule_AddIntConstant(m, "SO_DEBUG", SO_DEBUG);
diff --git a/configure b/configure
index 9299296..4a5890a 100755
--- a/configure
+++ b/configure
@@ -1,5 +1,5 @@
#! /bin/sh
-# From configure.in Revision: 85349 .
+# From configure.in Revision: 85422 .
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.65 for python 3.2.
#
@@ -9302,7 +9302,7 @@ fi
$as_echo "MACHDEP_OBJS" >&6; }
# checks for library functions
-for ac_func in alarm setitimer getitimer bind_textdomain_codeset chown \
+for ac_func in alarm accept4 setitimer getitimer bind_textdomain_codeset chown \
clock confstr ctermid execv fchmod fchown fork fpathconf ftime ftruncate \
gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \
getpriority getresuid getresgid getpwent getspnam getspent getsid getwd \
diff --git a/configure.in b/configure.in
index 31ed91e..dc74743 100644
--- a/configure.in
+++ b/configure.in
@@ -2550,7 +2550,7 @@ fi
AC_MSG_RESULT(MACHDEP_OBJS)
# checks for library functions
-AC_CHECK_FUNCS(alarm setitimer getitimer bind_textdomain_codeset chown \
+AC_CHECK_FUNCS(alarm accept4 setitimer getitimer bind_textdomain_codeset chown \
clock confstr ctermid execv fchmod fchown fork fpathconf ftime ftruncate \
gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \
getpriority getresuid getresgid getpwent getspnam getspent getsid getwd \
diff --git a/pyconfig.h.in b/pyconfig.h.in
index 545dbe3..99ae85b 100644
--- a/pyconfig.h.in
+++ b/pyconfig.h.in
@@ -40,6 +40,9 @@
the case on Motorola V4 (R40V4.2) */
#undef GETTIMEOFDAY_NO_TZ
+/* Define to 1 if you have the `accept4' function. */
+#undef HAVE_ACCEPT4
+
/* Define to 1 if you have the `acosh' function. */
#undef HAVE_ACOSH