Issue #8373: The filesystem path of AF_UNIX sockets now uses the filesystem

encoding and the surrogateescape error handler, rather than UTF-8.  Patch
by David Watson.
This commit is contained in:
Antoine Pitrou 2011-12-16 14:46:36 +01:00
parent ab0e9f7089
commit 6ec29e299b
4 changed files with 101 additions and 9 deletions

View File

@ -40,9 +40,23 @@ Socket families
Depending on the system and the build options, various socket families Depending on the system and the build options, various socket families
are supported by this module. are supported by this module.
Socket addresses are represented as follows: The address format required by a particular socket object is automatically
selected based on the address family specified when the socket object was
created. Socket addresses are represented as follows:
- A single string is used for the :const:`AF_UNIX` address family. - The address of an :const:`AF_UNIX` socket bound to a file system node
is represented as a string, using the file system encoding and the
``'surrogateescape'`` error handler (see :pep:`383`). An address in
Linux's abstract namespace is returned as a :class:`bytes` object with
an initial null byte; note that sockets in this namespace can
communicate with normal file system sockets, so programs intended to
run on Linux may need to deal with both types of address. A string or
:class:`bytes` object can be used for either type of address when
passing it as an argument.
.. versionchanged:: 3.3
Previously, :const:`AF_UNIX` socket paths were assumed to use UTF-8
encoding.
- A pair ``(host, port)`` is used for the :const:`AF_INET` address family, - A pair ``(host, port)`` is used for the :const:`AF_INET` address family,
where *host* is a string representing either a hostname in Internet domain where *host* is a string representing either a hostname in Internet domain

View File

@ -1538,7 +1538,6 @@ class BasicUDPTest(ThreadedUDPSocketTest):
def _testRecvFromNegative(self): def _testRecvFromNegative(self):
self.cli.sendto(MSG, 0, (HOST, self.port)) self.cli.sendto(MSG, 0, (HOST, self.port))
# Tests for the sendmsg()/recvmsg() interface. Where possible, the # Tests for the sendmsg()/recvmsg() interface. Where possible, the
# same test code is used with different families and types of socket # same test code is used with different families and types of socket
# (e.g. stream, datagram), and tests using recvmsg() are repeated # (e.g. stream, datagram), and tests using recvmsg() are repeated
@ -4241,6 +4240,66 @@ class TestLinuxAbstractNamespace(unittest.TestCase):
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s: with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
self.assertRaises(socket.error, s.bind, address) self.assertRaises(socket.error, s.bind, address)
def testStrName(self):
# Check that an abstract name can be passed as a string.
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
s.bind("\x00python\x00test\x00")
self.assertEqual(s.getsockname(), b"\x00python\x00test\x00")
finally:
s.close()
class TestUnixDomain(unittest.TestCase):
def setUp(self):
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
def tearDown(self):
self.sock.close()
def encoded(self, path):
# Return the given path encoded in the file system encoding,
# or skip the test if this is not possible.
try:
return os.fsencode(path)
except UnicodeEncodeError:
self.skipTest(
"Pathname {0!a} cannot be represented in file "
"system encoding {1!r}".format(
path, sys.getfilesystemencoding()))
def testStrAddr(self):
# Test binding to and retrieving a normal string pathname.
path = os.path.abspath(support.TESTFN)
self.sock.bind(path)
self.addCleanup(support.unlink, path)
self.assertEqual(self.sock.getsockname(), path)
def testBytesAddr(self):
# Test binding to a bytes pathname.
path = os.path.abspath(support.TESTFN)
self.sock.bind(self.encoded(path))
self.addCleanup(support.unlink, path)
self.assertEqual(self.sock.getsockname(), path)
def testSurrogateescapeBind(self):
# Test binding to a valid non-ASCII pathname, with the
# non-ASCII bytes supplied using surrogateescape encoding.
path = os.path.abspath(support.TESTFN_UNICODE)
b = self.encoded(path)
self.sock.bind(b.decode("ascii", "surrogateescape"))
self.addCleanup(support.unlink, path)
self.assertEqual(self.sock.getsockname(), path)
def testUnencodableAddr(self):
# Test binding to a pathname that cannot be encoded in the
# file system encoding.
if support.TESTFN_UNENCODABLE is None:
self.skipTest("No unencodable filename available")
path = os.path.abspath(support.TESTFN_UNENCODABLE)
self.sock.bind(path)
self.addCleanup(support.unlink, path)
self.assertEqual(self.sock.getsockname(), path)
@unittest.skipUnless(thread, 'Threading required for this test.') @unittest.skipUnless(thread, 'Threading required for this test.')
class BufferIOTest(SocketConnectedTest): class BufferIOTest(SocketConnectedTest):
@ -4517,6 +4576,8 @@ def test_main():
]) ])
if hasattr(socket, "socketpair"): if hasattr(socket, "socketpair"):
tests.append(BasicSocketPairTest) tests.append(BasicSocketPairTest)
if hasattr(socket, "AF_UNIX"):
tests.append(TestUnixDomain)
if sys.platform == 'linux': if sys.platform == 'linux':
tests.append(TestLinuxAbstractNamespace) tests.append(TestLinuxAbstractNamespace)
if isTipcAvailable(): if isTipcAvailable():

View File

@ -419,6 +419,10 @@ Core and Builtins
Library Library
------- -------
- Issue #8373: The filesystem path of AF_UNIX sockets now uses the filesystem
encoding and the surrogateescape error handler, rather than UTF-8. Patch
by David Watson.
- Issue #10350: Read and save errno before calling a function which might - Issue #10350: Read and save errno before calling a function which might
overwrite it. Original patch by Hallvard B Furuseth. overwrite it. Original patch by Hallvard B Furuseth.

View File

@ -1073,7 +1073,7 @@ makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto)
#endif /* linux */ #endif /* linux */
{ {
/* regular NULL-terminated string */ /* regular NULL-terminated string */
return PyUnicode_FromString(a->sun_path); return PyUnicode_DecodeFSDefault(a->sun_path);
} }
} }
#endif /* AF_UNIX */ #endif /* AF_UNIX */
@ -1269,8 +1269,18 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
struct sockaddr_un* addr; struct sockaddr_un* addr;
char *path; char *path;
int len; int len;
if (!PyArg_Parse(args, "s#", &path, &len)) int retval = 0;
return 0;
/* PEP 383. Not using PyUnicode_FSConverter since we need to
allow embedded nulls on Linux. */
if (PyUnicode_Check(args)) {
if ((args = PyUnicode_EncodeFSDefault(args)) == NULL)
return 0;
}
else
Py_INCREF(args);
if (!PyArg_Parse(args, "y#", &path, &len))
goto unix_out;
addr = (struct sockaddr_un*)addr_ret; addr = (struct sockaddr_un*)addr_ret;
#ifdef linux #ifdef linux
@ -1279,7 +1289,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
if (len > sizeof addr->sun_path) { if (len > sizeof addr->sun_path) {
PyErr_SetString(PyExc_OSError, PyErr_SetString(PyExc_OSError,
"AF_UNIX path too long"); "AF_UNIX path too long");
return 0; goto unix_out;
} }
} }
else else
@ -1289,7 +1299,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
if (len >= sizeof addr->sun_path) { if (len >= sizeof addr->sun_path) {
PyErr_SetString(PyExc_OSError, PyErr_SetString(PyExc_OSError,
"AF_UNIX path too long"); "AF_UNIX path too long");
return 0; goto unix_out;
} }
addr->sun_path[len] = 0; addr->sun_path[len] = 0;
} }
@ -1300,7 +1310,10 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
#else #else
*len_ret = len + offsetof(struct sockaddr_un, sun_path); *len_ret = len + offsetof(struct sockaddr_un, sun_path);
#endif #endif
return 1; retval = 1;
unix_out:
Py_DECREF(args);
return retval;
} }
#endif /* AF_UNIX */ #endif /* AF_UNIX */