[3.13] gh-132099: Harmonize Bluetooth address handling (GH-132486) (GH-132497)

Now all protocols always accept the Bluetooth address as string and
getsockname() always returns the Bluetooth address as string.

* BTPROTO_SCO now accepts not only bytes, but str.
* BTPROTO_SCO now checks address for embedded null.
* On *BSD, BTPROTO_HCI now accepts str instead of bytes.
* On FreeBSD, getsockname() for BTPROTO_HCI now returns str instead of bytes.
* On NetBSD and DragonFly BSD, BTPROTO_HCI now checks address for embedded null.
(cherry picked from commit 1fc1df8dcc)
This commit is contained in:
Serhiy Storchaka 2025-04-14 19:36:04 +03:00 committed by GitHub
parent a50aa3325b
commit d321b6ec82
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 58 additions and 38 deletions

View file

@ -147,9 +147,8 @@ created. Socket addresses are represented as follows:
- On Linux it accepts a tuple ``(device_id,)`` where ``device_id`` - On Linux it accepts a tuple ``(device_id,)`` where ``device_id``
is an integer specifying the number of the Bluetooth device. is an integer specifying the number of the Bluetooth device.
- On FreeBSD, NetBSD and DragonFly BSD it accepts ``bdaddr`` where ``bdaddr`` - On FreeBSD, NetBSD and DragonFly BSD it accepts ``bdaddr``
is a :class:`bytes` object containing the Bluetooth address in a where ``bdaddr`` is the Bluetooth address as a string.
string format. (ex. ``b'12:23:34:45:56:67'``)
.. versionchanged:: 3.2 .. versionchanged:: 3.2
NetBSD and DragonFlyBSD support added. NetBSD and DragonFlyBSD support added.
@ -157,10 +156,10 @@ created. Socket addresses are represented as follows:
.. versionchanged:: 3.13.3 .. versionchanged:: 3.13.3
FreeBSD support added. FreeBSD support added.
- :const:`BTPROTO_SCO` accepts ``bdaddr`` where ``bdaddr`` is a - :const:`BTPROTO_SCO` accepts ``bdaddr`` where ``bdaddr`` is
:class:`bytes` object containing the Bluetooth address in a the Bluetooth address as a string or a :class:`bytes` object.
string format. (ex. ``b'12:23:34:45:56:67'``) This protocol is not (ex. ``'12:23:34:45:56:67'`` or ``b'12:23:34:45:56:67'``)
supported under FreeBSD. This protocol is not supported under FreeBSD.
- :const:`AF_ALG` is a Linux-only socket based interface to Kernel - :const:`AF_ALG` is a Linux-only socket based interface to Kernel
cryptography. An algorithm socket is configured with a tuple of two to four cryptography. An algorithm socket is configured with a tuple of two to four

View file

@ -2656,6 +2656,8 @@ class BasicBluetoothTest(unittest.TestCase):
f.bind(socket.BDADDR_ANY) f.bind(socket.BDADDR_ANY)
with self.assertRaises(OSError): with self.assertRaises(OSError):
f.bind((socket.BDADDR_ANY.encode(), 0x1001)) f.bind((socket.BDADDR_ANY.encode(), 0x1001))
with self.assertRaises(OSError):
f.bind(('\ud812', 0x1001))
def testBindRfcommSocket(self): def testBindRfcommSocket(self):
with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_STREAM, socket.BTPROTO_RFCOMM) as s: with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_STREAM, socket.BTPROTO_RFCOMM) as s:
@ -2687,6 +2689,8 @@ class BasicBluetoothTest(unittest.TestCase):
s.bind((socket.BDADDR_ANY, channel, 0)) s.bind((socket.BDADDR_ANY, channel, 0))
with self.assertRaises(OSError): with self.assertRaises(OSError):
s.bind((socket.BDADDR_ANY + '\0', channel)) s.bind((socket.BDADDR_ANY + '\0', channel))
with self.assertRaises(OSError):
s.bind('\ud812')
with self.assertRaises(OSError): with self.assertRaises(OSError):
s.bind(('invalid', channel)) s.bind(('invalid', channel))
@ -2694,7 +2698,7 @@ class BasicBluetoothTest(unittest.TestCase):
def testBindHciSocket(self): def testBindHciSocket(self):
with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI) as s: with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI) as s:
if sys.platform.startswith(('netbsd', 'dragonfly', 'freebsd')): if sys.platform.startswith(('netbsd', 'dragonfly', 'freebsd')):
s.bind(socket.BDADDR_ANY.encode()) s.bind(socket.BDADDR_ANY)
addr = s.getsockname() addr = s.getsockname()
self.assertEqual(addr, socket.BDADDR_ANY) self.assertEqual(addr, socket.BDADDR_ANY)
else: else:
@ -2713,14 +2717,17 @@ class BasicBluetoothTest(unittest.TestCase):
with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI) as s: with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI) as s:
if sys.platform.startswith(('netbsd', 'dragonfly', 'freebsd')): if sys.platform.startswith(('netbsd', 'dragonfly', 'freebsd')):
with self.assertRaises(OSError): with self.assertRaises(OSError):
s.bind(socket.BDADDR_ANY) s.bind(socket.BDADDR_ANY.encode())
with self.assertRaises(OSError): with self.assertRaises(OSError):
s.bind((socket.BDADDR_ANY.encode(),)) s.bind((socket.BDADDR_ANY,))
if sys.platform.startswith('freebsd'): with self.assertRaises(OSError):
with self.assertRaises(ValueError): s.bind(socket.BDADDR_ANY + '\0')
s.bind(socket.BDADDR_ANY.encode() + b'\0') with self.assertRaises((ValueError, OSError)):
with self.assertRaises(ValueError): s.bind(socket.BDADDR_ANY + ' '*100)
s.bind(socket.BDADDR_ANY.encode() + b' '*100) with self.assertRaises(OSError):
s.bind('\ud812')
with self.assertRaises(OSError):
s.bind('invalid')
with self.assertRaises(OSError): with self.assertRaises(OSError):
s.bind(b'invalid') s.bind(b'invalid')
else: else:
@ -2731,11 +2738,18 @@ class BasicBluetoothTest(unittest.TestCase):
s.bind((dev, 0)) s.bind((dev, 0))
with self.assertRaises(OSError): with self.assertRaises(OSError):
s.bind(dev) s.bind(dev)
with self.assertRaises(OSError):
s.bind(socket.BDADDR_ANY)
with self.assertRaises(OSError): with self.assertRaises(OSError):
s.bind(socket.BDADDR_ANY.encode()) s.bind(socket.BDADDR_ANY.encode())
@unittest.skipUnless(hasattr(socket, 'BTPROTO_SCO'), 'Bluetooth SCO sockets required for this test') @unittest.skipUnless(hasattr(socket, 'BTPROTO_SCO'), 'Bluetooth SCO sockets required for this test')
def testBindScoSocket(self): def testBindScoSocket(self):
with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_SEQPACKET, socket.BTPROTO_SCO) as s:
s.bind(socket.BDADDR_ANY)
addr = s.getsockname()
self.assertEqual(addr, socket.BDADDR_ANY)
with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_SEQPACKET, socket.BTPROTO_SCO) as s: with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_SEQPACKET, socket.BTPROTO_SCO) as s:
s.bind(socket.BDADDR_ANY.encode()) s.bind(socket.BDADDR_ANY.encode())
addr = s.getsockname() addr = s.getsockname()
@ -2745,9 +2759,17 @@ class BasicBluetoothTest(unittest.TestCase):
def testBadScoAddr(self): def testBadScoAddr(self):
with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_SEQPACKET, socket.BTPROTO_SCO) as s: with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_SEQPACKET, socket.BTPROTO_SCO) as s:
with self.assertRaises(OSError): with self.assertRaises(OSError):
s.bind(socket.BDADDR_ANY) s.bind((socket.BDADDR_ANY,))
with self.assertRaises(OSError): with self.assertRaises(OSError):
s.bind((socket.BDADDR_ANY.encode(),)) s.bind((socket.BDADDR_ANY.encode(),))
with self.assertRaises(ValueError):
s.bind(socket.BDADDR_ANY + '\0')
with self.assertRaises(ValueError):
s.bind(socket.BDADDR_ANY.encode() + b'\0')
with self.assertRaises(UnicodeEncodeError):
s.bind('\ud812')
with self.assertRaises(OSError):
s.bind('invalid')
with self.assertRaises(OSError): with self.assertRaises(OSError):
s.bind(b'invalid') s.bind(b'invalid')

View file

@ -1495,7 +1495,7 @@ makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto)
#elif defined(__FreeBSD__) #elif defined(__FreeBSD__)
const char *node = _BT_HCI_MEMB(a, node); const char *node = _BT_HCI_MEMB(a, node);
size_t len = strnlen(node, sizeof(_BT_HCI_MEMB(a, node))); size_t len = strnlen(node, sizeof(_BT_HCI_MEMB(a, node)));
return PyBytes_FromStringAndSize(node, (Py_ssize_t)len); return PyUnicode_FromStringAndSize(node, (Py_ssize_t)len);
#else #else
return makebdaddr(&_BT_HCI_MEMB(a, bdaddr)); return makebdaddr(&_BT_HCI_MEMB(a, bdaddr));
#endif #endif
@ -2075,36 +2075,25 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
return 0; return 0;
} }
_BT_HCI_MEMB(addr, dev) = dev; _BT_HCI_MEMB(addr, dev) = dev;
#elif defined(__FreeBSD__) #else
if (!PyBytes_Check(args)) { const char *straddr;
if (!PyArg_Parse(args, "s", &straddr)) {
PyErr_Format(PyExc_OSError, "%s: " PyErr_Format(PyExc_OSError, "%s: "
"wrong node format", caller); "wrong format", caller);
return 0; return 0;
} }
const char *straddr = PyBytes_AS_STRING(args); # if defined(__FreeBSD__)
size_t len = PyBytes_GET_SIZE(args); if (strlen(straddr) > sizeof(_BT_HCI_MEMB(addr, node))) {
if (strlen(straddr) != len) {
PyErr_Format(PyExc_ValueError, "%s: "
"node contains embedded null character", caller);
return 0;
}
if (len > sizeof(_BT_HCI_MEMB(addr, node))) {
PyErr_Format(PyExc_ValueError, "%s: " PyErr_Format(PyExc_ValueError, "%s: "
"node too long", caller); "node too long", caller);
return 0; return 0;
} }
strncpy(_BT_HCI_MEMB(addr, node), straddr, strncpy(_BT_HCI_MEMB(addr, node), straddr,
sizeof(_BT_HCI_MEMB(addr, node))); sizeof(_BT_HCI_MEMB(addr, node)));
#else # else
const char *straddr;
if (!PyBytes_Check(args)) {
PyErr_Format(PyExc_OSError, "%s: "
"wrong format", caller);
return 0;
}
straddr = PyBytes_AS_STRING(args);
if (setbdaddr(straddr, &_BT_HCI_MEMB(addr, bdaddr)) < 0) if (setbdaddr(straddr, &_BT_HCI_MEMB(addr, bdaddr)) < 0)
return 0; return 0;
# endif
#endif #endif
*len_ret = sizeof *addr; *len_ret = sizeof *addr;
return 1; return 1;
@ -2117,12 +2106,22 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
struct sockaddr_sco *addr = &addrbuf->bt_sco; struct sockaddr_sco *addr = &addrbuf->bt_sco;
memset(addr, 0, sizeof(struct sockaddr_sco)); memset(addr, 0, sizeof(struct sockaddr_sco));
_BT_SCO_MEMB(addr, family) = AF_BLUETOOTH; _BT_SCO_MEMB(addr, family) = AF_BLUETOOTH;
if (!PyBytes_Check(args)) {
if (PyBytes_Check(args)) {
if (!PyArg_Parse(args, "y", &straddr)) {
return 0;
}
}
else if (PyUnicode_Check(args)) {
if (!PyArg_Parse(args, "s", &straddr)) {
return 0;
}
}
else {
PyErr_Format(PyExc_OSError, PyErr_Format(PyExc_OSError,
"%s(): wrong format", caller); "%s(): wrong format", caller);
return 0; return 0;
} }
straddr = PyBytes_AS_STRING(args);
if (setbdaddr(straddr, &_BT_SCO_MEMB(addr, bdaddr)) < 0) if (setbdaddr(straddr, &_BT_SCO_MEMB(addr, bdaddr)) < 0)
return 0; return 0;