[Python-checkins] bpo-36590: Add Bluetooth RFCOMM and support for Windows. (GH-12767)

Steve Dower webhook-mailer at python.org
Fri Aug 2 16:30:02 EDT 2019


https://github.com/python/cpython/commit/8fbece135d7615e836a845ca39223097046c8b8b
commit: 8fbece135d7615e836a845ca39223097046c8b8b
branch: master
author: Greg Bowser <topnotcher at gmail.com>
committer: Steve Dower <steve.dower at python.org>
date: 2019-08-02T13:29:52-07:00
summary:

bpo-36590: Add Bluetooth RFCOMM and support for Windows. (GH-12767)

Support for RFCOMM, L2CAP, HCI, SCO is based on the BTPROTO_* macros
being defined. Winsock only supports RFCOMM, even though it has a
BTHPROTO_L2CAP macro. L2CAP support would build on windows, but not
necessarily work.

This also adds some basic unittests for constants (all of which existed
prior to this commit, just not on windows) and creating sockets.

pair: Nate Duarte <slacknate at gmail.com>

files:
A Misc/NEWS.d/next/Windows/2019-04-10-21-13-26.bpo-36590.ZTaKcu.rst
M Lib/test/test_socket.py
M Modules/socketmodule.c
M Modules/socketmodule.h

diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
index 11b2a38ad76d..ce816cd603ec 100644
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -114,6 +114,19 @@ def _have_socket_vsock():
     return ret
 
 
+def _have_socket_bluetooth():
+    """Check whether AF_BLUETOOTH sockets are supported on this host."""
+    try:
+        # RFCOMM is supported by all platforms with bluetooth support. Windows
+        # does not support omitting the protocol.
+        s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_STREAM, socket.BTPROTO_RFCOMM)
+    except (AttributeError, OSError):
+        return False
+    else:
+        s.close()
+    return True
+
+
 @contextlib.contextmanager
 def socket_setdefaulttimeout(timeout):
     old_timeout = socket.getdefaulttimeout()
@@ -138,6 +151,8 @@ def socket_setdefaulttimeout(timeout):
 
 HAVE_SOCKET_UDPLITE = hasattr(socket, "IPPROTO_UDPLITE")
 
+HAVE_SOCKET_BLUETOOTH = _have_socket_bluetooth()
+
 # Size in bytes of the int type
 SIZEOF_INT = array.array("i").itemsize
 
@@ -2257,6 +2272,45 @@ def testSocketBufferSize(self):
                              socket.SO_VM_SOCKETS_BUFFER_MIN_SIZE))
 
 
+ at unittest.skipUnless(HAVE_SOCKET_BLUETOOTH,
+                     'Bluetooth sockets required for this test.')
+class BasicBluetoothTest(unittest.TestCase):
+
+    def testBluetoothConstants(self):
+        socket.BDADDR_ANY
+        socket.BDADDR_LOCAL
+        socket.AF_BLUETOOTH
+        socket.BTPROTO_RFCOMM
+
+        if sys.platform != "win32":
+            socket.BTPROTO_HCI
+            socket.SOL_HCI
+            socket.BTPROTO_L2CAP
+
+            if not sys.platform.startswith("freebsd"):
+                socket.BTPROTO_SCO
+
+    def testCreateRfcommSocket(self):
+        with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_STREAM, socket.BTPROTO_RFCOMM) as s:
+            pass
+
+    @unittest.skipIf(sys.platform == "win32", "windows does not support L2CAP sockets")
+    def testCreateL2capSocket(self):
+        with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_SEQPACKET, socket.BTPROTO_L2CAP) as s:
+            pass
+
+    @unittest.skipIf(sys.platform == "win32", "windows does not support HCI sockets")
+    def testCreateHciSocket(self):
+        with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI) as s:
+            pass
+
+    @unittest.skipIf(sys.platform == "win32" or sys.platform.startswith("freebsd"),
+                     "windows and freebsd do not support SCO sockets")
+    def testCreateScoSocket(self):
+        with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_SEQPACKET, socket.BTPROTO_SCO) as s:
+            pass
+
+
 class BasicTCPTest(SocketConnectedTest):
 
     def __init__(self, methodName='runTest'):
@@ -6416,6 +6470,7 @@ def test_main():
         BasicVSOCKTest,
         ThreadedVSOCKSocketStreamTest,
     ])
+    tests.append(BasicBluetoothTest)
     tests.extend([
         CmsgMacroTests,
         SendmsgUDPTest,
diff --git a/Misc/NEWS.d/next/Windows/2019-04-10-21-13-26.bpo-36590.ZTaKcu.rst b/Misc/NEWS.d/next/Windows/2019-04-10-21-13-26.bpo-36590.ZTaKcu.rst
new file mode 100644
index 000000000000..6a186bbeed74
--- /dev/null
+++ b/Misc/NEWS.d/next/Windows/2019-04-10-21-13-26.bpo-36590.ZTaKcu.rst
@@ -0,0 +1 @@
+Add native Bluetooth RFCOMM support to socket module.
\ No newline at end of file
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
old mode 100644
new mode 100755
index 99e350b4333b..f220c2636319
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -520,6 +520,15 @@ remove_unusable_flags(PyObject *m)
 #endif
 #endif
 
+#ifdef MS_WINDOWS
+#define sockaddr_rc SOCKADDR_BTH_REDEF
+
+#define USE_BLUETOOTH 1
+#define AF_BLUETOOTH AF_BTH
+#define BTPROTO_RFCOMM BTHPROTO_RFCOMM
+#define _BT_RC_MEMB(sa, memb) ((sa)->memb)
+#endif
+
 /* Convert "sock_addr_t *" to "struct sockaddr *". */
 #define SAS2SA(x)       (&((x)->sa))
 
@@ -1256,12 +1265,23 @@ setbdaddr(const char *name, bdaddr_t *bdaddr)
     n = sscanf(name, "%X:%X:%X:%X:%X:%X%c",
                &b5, &b4, &b3, &b2, &b1, &b0, &ch);
     if (n == 6 && (b0 | b1 | b2 | b3 | b4 | b5) < 256) {
+
+#ifdef MS_WINDOWS
+        *bdaddr = (ULONGLONG)(b0 & 0xFF);
+        *bdaddr |= ((ULONGLONG)(b1 & 0xFF) << 8);
+        *bdaddr |= ((ULONGLONG)(b2 & 0xFF) << 16);
+        *bdaddr |= ((ULONGLONG)(b3 & 0xFF) << 24);
+        *bdaddr |= ((ULONGLONG)(b4 & 0xFF) << 32);
+        *bdaddr |= ((ULONGLONG)(b5 & 0xFF) << 40);
+#else
         bdaddr->b[0] = b0;
         bdaddr->b[1] = b1;
         bdaddr->b[2] = b2;
         bdaddr->b[3] = b3;
         bdaddr->b[4] = b4;
         bdaddr->b[5] = b5;
+#endif
+
         return 6;
     } else {
         PyErr_SetString(PyExc_OSError, "bad bluetooth address");
@@ -1278,9 +1298,23 @@ makebdaddr(bdaddr_t *bdaddr)
 {
     char buf[(6 * 2) + 5 + 1];
 
+#ifdef MS_WINDOWS
+    int i;
+    unsigned int octets[6];
+
+    for (i = 0; i < 6; ++i) {
+        octets[i] = ((*bdaddr) >> (8 * i)) & 0xFF;
+    }
+
+    sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X",
+        octets[5], octets[4], octets[3],
+        octets[2], octets[1], octets[0]);
+#else
     sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X",
         bdaddr->b[5], bdaddr->b[4], bdaddr->b[3],
         bdaddr->b[2], bdaddr->b[1], bdaddr->b[0]);
+#endif
+
     return PyUnicode_FromString(buf);
 }
 #endif
@@ -1378,6 +1412,7 @@ makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto)
     case AF_BLUETOOTH:
         switch (proto) {
 
+#ifdef BTPROTO_L2CAP
         case BTPROTO_L2CAP:
         {
             struct sockaddr_l2 *a = (struct sockaddr_l2 *) addr;
@@ -1392,6 +1427,8 @@ makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto)
             return ret;
         }
 
+#endif /* BTPROTO_L2CAP */
+
         case BTPROTO_RFCOMM:
         {
             struct sockaddr_rc *a = (struct sockaddr_rc *) addr;
@@ -1406,6 +1443,7 @@ makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto)
             return ret;
         }
 
+#ifdef BTPROTO_HCI
         case BTPROTO_HCI:
         {
             struct sockaddr_hci *a = (struct sockaddr_hci *) addr;
@@ -1425,6 +1463,7 @@ makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto)
             return makebdaddr(&_BT_SCO_MEMB(a, bdaddr));
         }
 #endif /* !__FreeBSD__ */
+#endif /* BTPROTO_HCI */
 
         default:
             PyErr_SetString(PyExc_ValueError,
@@ -1879,6 +1918,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
     case AF_BLUETOOTH:
     {
         switch (s->sock_proto) {
+#ifdef BTPROTO_L2CAP
         case BTPROTO_L2CAP:
         {
             struct sockaddr_l2 *addr;
@@ -1899,6 +1939,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
             *len_ret = sizeof *addr;
             return 1;
         }
+#endif /* BTPROTO_L2CAP */
         case BTPROTO_RFCOMM:
         {
             struct sockaddr_rc *addr;
@@ -1918,6 +1959,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
             *len_ret = sizeof *addr;
             return 1;
         }
+#ifdef BTPROTO_HCI
         case BTPROTO_HCI:
         {
             struct sockaddr_hci *addr = (struct sockaddr_hci *)addr_ret;
@@ -1964,6 +2006,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
             return 1;
         }
 #endif /* !__FreeBSD__ */
+#endif /* BTPROTO_HCI */
         default:
             PyErr_Format(PyExc_OSError,
                          "%s(): unknown Bluetooth protocol", caller);
@@ -2385,12 +2428,15 @@ getsockaddrlen(PySocketSockObject *s, socklen_t *len_ret)
         switch(s->sock_proto)
         {
 
+#ifdef BTPROTO_L2CAP
         case BTPROTO_L2CAP:
             *len_ret = sizeof (struct sockaddr_l2);
             return 1;
+#endif /* BTPROTO_L2CAP */
         case BTPROTO_RFCOMM:
             *len_ret = sizeof (struct sockaddr_rc);
             return 1;
+#ifdef BTPROTO_HCI
         case BTPROTO_HCI:
             *len_ret = sizeof (struct sockaddr_hci);
             return 1;
@@ -2399,6 +2445,7 @@ getsockaddrlen(PySocketSockObject *s, socklen_t *len_ret)
             *len_ret = sizeof (struct sockaddr_sco);
             return 1;
 #endif /* !__FreeBSD__ */
+#endif /* BTPROTO_HCI */
         default:
             PyErr_SetString(PyExc_OSError, "getsockaddrlen: "
                             "unknown BT protocol");
@@ -7273,23 +7320,29 @@ PyInit__socket(void)
 
 #ifdef USE_BLUETOOTH
     PyModule_AddIntMacro(m, AF_BLUETOOTH);
+#ifdef BTPROTO_L2CAP
     PyModule_AddIntMacro(m, BTPROTO_L2CAP);
+#endif /* BTPROTO_L2CAP */
+#ifdef BTPROTO_HCI
     PyModule_AddIntMacro(m, BTPROTO_HCI);
     PyModule_AddIntMacro(m, SOL_HCI);
 #if !defined(__NetBSD__) && !defined(__DragonFly__)
     PyModule_AddIntMacro(m, HCI_FILTER);
-#endif
 #if !defined(__FreeBSD__)
-#if !defined(__NetBSD__) && !defined(__DragonFly__)
     PyModule_AddIntMacro(m, HCI_TIME_STAMP);
-#endif
     PyModule_AddIntMacro(m, HCI_DATA_DIR);
-    PyModule_AddIntMacro(m, BTPROTO_SCO);
-#endif
+#endif /* !__FreeBSD__ */
+#endif /* !__NetBSD__ && !__DragonFly__ */
+#endif /* BTPROTO_HCI */
+#ifdef BTPROTO_RFCOMM
     PyModule_AddIntMacro(m, BTPROTO_RFCOMM);
+#endif /* BTPROTO_RFCOMM */
     PyModule_AddStringConstant(m, "BDADDR_ANY", "00:00:00:00:00:00");
     PyModule_AddStringConstant(m, "BDADDR_LOCAL", "00:00:00:FF:FF:FF");
-#endif
+#ifdef BTPROTO_SCO
+    PyModule_AddIntMacro(m, BTPROTO_SCO);
+#endif /* BTPROTO_SCO */
+#endif /* USE_BLUETOOTH */
 
 #ifdef AF_CAN
     /* Controller Area Network */
diff --git a/Modules/socketmodule.h b/Modules/socketmodule.h
old mode 100644
new mode 100755
index dff1f8f4e9ce..3d95fe709ac7
--- a/Modules/socketmodule.h
+++ b/Modules/socketmodule.h
@@ -14,6 +14,47 @@
 
 #else /* MS_WINDOWS */
 # include <winsock2.h>
+
+/*
+ * If Windows has bluetooth support, include bluetooth constants.
+ */
+#ifdef AF_BTH
+# include <ws2bth.h>
+# include <pshpack1.h>
+
+/*
+ * The current implementation assumes the bdaddr in the sockaddr structs
+ * will be a bdaddr_t. We treat this as an opaque type: on *nix systems, it
+ * will be a struct with a single member (an array of six bytes). On windows,
+ * we typedef this to ULONGLONG to match the Windows definition.
+ */
+typedef ULONGLONG bdaddr_t;
+
+/*
+ * Redefine SOCKADDR_BTH to provide names compatible with _BT_RC_MEMB() macros.
+ */
+struct SOCKADDR_BTH_REDEF {
+    union {
+        USHORT    addressFamily;
+        USHORT    family;
+    };
+
+    union {
+        ULONGLONG btAddr;
+        bdaddr_t  bdaddr;
+    };
+
+    GUID      serviceClassId;
+
+    union {
+        ULONG     port;
+        ULONG     channel;
+    };
+
+};
+# include <poppack.h>
+#endif
+
 /* Windows 'supports' CMSG_LEN, but does not follow the POSIX standard
  * interface at all, so there is no point including the code that
  * attempts to use it.
@@ -199,6 +240,8 @@ typedef union sock_addr {
     struct sockaddr_rc bt_rc;
     struct sockaddr_sco bt_sco;
     struct sockaddr_hci bt_hci;
+#elif defined(MS_WINDOWS)
+    struct SOCKADDR_BTH_REDEF bt_rc;
 #endif
 #ifdef HAVE_NETPACKET_PACKET_H
     struct sockaddr_ll ll;



More information about the Python-checkins mailing list