Skip to content

gh-132099: Harmonize Bluetooth address handling #132486

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions Doc/library/socket.rst
Original file line number Diff line number Diff line change
Expand Up @@ -158,19 +158,18 @@ created. Socket addresses are represented as follows:

- On Linux it accepts a tuple ``(device_id,)`` where ``device_id``
is an integer specifying the number of the Bluetooth device.
- On FreeBSD, NetBSD and DragonFly BSD it accepts ``bdaddr`` where ``bdaddr``
is a :class:`bytes` object containing the Bluetooth address in a
string format. (ex. ``b'12:23:34:45:56:67'``)
- On FreeBSD, NetBSD and DragonFly BSD it accepts ``bdaddr``
where ``bdaddr`` is the Bluetooth address as a string.

.. versionchanged:: 3.2
NetBSD and DragonFlyBSD support added.

.. versionchanged:: 3.13.3
FreeBSD support added.

- :const:`BTPROTO_SCO` accepts ``bdaddr`` where ``bdaddr`` is a
:class:`bytes` object containing the Bluetooth address in a
string format. (ex. ``b'12:23:34:45:56:67'``)
- :const:`BTPROTO_SCO` accepts ``bdaddr`` where ``bdaddr`` is
the Bluetooth address as a string or a :class:`bytes` object.
(ex. ``'12:23:34:45:56:67'`` or ``b'12:23:34:45:56:67'``)

.. versionchanged:: next
FreeBSD support added.
Expand Down
40 changes: 31 additions & 9 deletions Lib/test/test_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -2681,6 +2681,8 @@ def testBadL2capAddr(self):
f.bind(socket.BDADDR_ANY)
with self.assertRaises(OSError):
f.bind((socket.BDADDR_ANY.encode(), 0x1001))
with self.assertRaises(OSError):
f.bind(('\ud812', 0x1001))

def testBindRfcommSocket(self):
with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_STREAM, socket.BTPROTO_RFCOMM) as s:
Expand Down Expand Up @@ -2712,14 +2714,16 @@ def testBadRfcommAddr(self):
s.bind((socket.BDADDR_ANY, channel, 0))
with self.assertRaises(OSError):
s.bind((socket.BDADDR_ANY + '\0', channel))
with self.assertRaises(OSError):
s.bind('\ud812')
with self.assertRaises(OSError):
s.bind(('invalid', channel))

@unittest.skipUnless(hasattr(socket, 'BTPROTO_HCI'), 'Bluetooth HCI sockets required for this test')
def testBindHciSocket(self):
with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI) as s:
if sys.platform.startswith(('netbsd', 'dragonfly', 'freebsd')):
s.bind(socket.BDADDR_ANY.encode())
s.bind(socket.BDADDR_ANY)
addr = s.getsockname()
self.assertEqual(addr, socket.BDADDR_ANY)
else:
Expand All @@ -2738,14 +2742,17 @@ def testBadHciAddr(self):
with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI) as s:
if sys.platform.startswith(('netbsd', 'dragonfly', 'freebsd')):
with self.assertRaises(OSError):
s.bind(socket.BDADDR_ANY)
s.bind(socket.BDADDR_ANY.encode())
with self.assertRaises(OSError):
s.bind((socket.BDADDR_ANY.encode(),))
if sys.platform.startswith('freebsd'):
with self.assertRaises(ValueError):
s.bind(socket.BDADDR_ANY.encode() + b'\0')
with self.assertRaises(ValueError):
s.bind(socket.BDADDR_ANY.encode() + b' '*100)
s.bind((socket.BDADDR_ANY,))
with self.assertRaises(OSError):
s.bind(socket.BDADDR_ANY + '\0')
with self.assertRaises((ValueError, OSError)):
s.bind(socket.BDADDR_ANY + ' '*100)
with self.assertRaises(OSError):
s.bind('\ud812')
with self.assertRaises(OSError):
s.bind('invalid')
with self.assertRaises(OSError):
s.bind(b'invalid')
else:
Expand All @@ -2756,11 +2763,18 @@ def testBadHciAddr(self):
s.bind((dev, 0))
with self.assertRaises(OSError):
s.bind(dev)
with self.assertRaises(OSError):
s.bind(socket.BDADDR_ANY)
with self.assertRaises(OSError):
s.bind(socket.BDADDR_ANY.encode())

@unittest.skipUnless(hasattr(socket, 'BTPROTO_SCO'), 'Bluetooth SCO sockets required for this test')
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:
s.bind(socket.BDADDR_ANY.encode())
addr = s.getsockname()
Expand All @@ -2770,9 +2784,17 @@ def testBindScoSocket(self):
def testBadScoAddr(self):
with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_SEQPACKET, socket.BTPROTO_SCO) as s:
with self.assertRaises(OSError):
s.bind(socket.BDADDR_ANY)
s.bind((socket.BDADDR_ANY,))
with self.assertRaises(OSError):
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):
s.bind(b'invalid')

Expand Down
43 changes: 21 additions & 22 deletions Modules/socketmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1546,7 +1546,7 @@ makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto)
#elif defined(__FreeBSD__)
const char *node = _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
return makebdaddr(&_BT_HCI_MEMB(a, bdaddr));
#endif
Expand Down Expand Up @@ -2145,36 +2145,25 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
return 0;
}
_BT_HCI_MEMB(addr, dev) = dev;
#elif defined(__FreeBSD__)
if (!PyBytes_Check(args)) {
#else
const char *straddr;
if (!PyArg_Parse(args, "s", &straddr)) {
PyErr_Format(PyExc_OSError, "%s: "
"wrong node format", caller);
return 0;
}
const char *straddr = PyBytes_AS_STRING(args);
size_t len = PyBytes_GET_SIZE(args);
if (strlen(straddr) != len) {
PyErr_Format(PyExc_ValueError, "%s: "
"node contains embedded null character", caller);
"wrong format", caller);
return 0;
}
if (len > sizeof(_BT_HCI_MEMB(addr, node))) {
# if defined(__FreeBSD__)
if (strlen(straddr) > sizeof(_BT_HCI_MEMB(addr, node))) {
PyErr_Format(PyExc_ValueError, "%s: "
"node too long", caller);
return 0;
}
strncpy(_BT_HCI_MEMB(addr, node), straddr,
sizeof(_BT_HCI_MEMB(addr, node)));
#else
const char *straddr;
if (!PyBytes_Check(args)) {
PyErr_Format(PyExc_OSError, "%s: "
"wrong format", caller);
return 0;
}
straddr = PyBytes_AS_STRING(args);
# else
if (setbdaddr(straddr, &_BT_HCI_MEMB(addr, bdaddr)) < 0)
return 0;
# endif
#endif
*len_ret = sizeof *addr;
return 1;
Expand All @@ -2188,12 +2177,22 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
struct sockaddr_sco *addr = &addrbuf->bt_sco;
memset(addr, 0, sizeof(struct sockaddr_sco));
_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,
"%s(): wrong format", caller);
return 0;
}
straddr = PyBytes_AS_STRING(args);
if (setbdaddr(straddr, &_BT_SCO_MEMB(addr, bdaddr)) < 0)
return 0;

Expand Down
Loading