Skip to content

Commit 19e7d48

Browse files
animalizezooba
authored andcommitted
bpo-32394: Remove some TCP options on old version Windows. (GH-5523)
1 parent 3f2e6f1 commit 19e7d48

File tree

4 files changed

+101
-0
lines changed

4 files changed

+101
-0
lines changed

Doc/library/socket.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,9 +320,16 @@ Constants
320320
``SO_DOMAIN``, ``SO_PROTOCOL``, ``SO_PEERSEC``, ``SO_PASSSEC``,
321321
``TCP_USER_TIMEOUT``, ``TCP_CONGESTION`` were added.
322322

323+
.. versionchanged:: 3.6.5
324+
On Windows, ``TCP_FASTOPEN``, ``TCP_KEEPCNT`` appear if run-time Windows
325+
supports.
326+
323327
.. versionchanged:: 3.7
324328
``TCP_NOTSENT_LOWAT`` was added.
325329

330+
On Windows, ``TCP_KEEPIDLE``, ``TCP_KEEPINTVL`` appear if run-time Windows
331+
supports.
332+
326333
.. data:: AF_CAN
327334
PF_CAN
328335
SOL_CAN_*

Lib/test/test_socket.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5945,6 +5945,27 @@ def test_sendmsg_afalg_args(self):
59455945
with self.assertRaises(TypeError):
59465946
sock.sendmsg_afalg(op=socket.ALG_OP_ENCRYPT, assoclen=-1)
59475947

5948+
@unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
5949+
class TestMSWindowsTCPFlags(unittest.TestCase):
5950+
knownTCPFlags = {
5951+
# avaliable since long time ago
5952+
'TCP_MAXSEG',
5953+
'TCP_NODELAY',
5954+
# available starting with Windows 10 1607
5955+
'TCP_FASTOPEN',
5956+
# available starting with Windows 10 1703
5957+
'TCP_KEEPCNT',
5958+
# available starting with Windows 10 1709
5959+
'TCP_KEEPIDLE',
5960+
'TCP_KEEPINTVL'
5961+
}
5962+
5963+
def test_new_tcp_flags(self):
5964+
provided = [s for s in dir(socket) if s.startswith('TCP')]
5965+
unknown = [s for s in provided if s not in self.knownTCPFlags]
5966+
5967+
self.assertEqual([], unknown,
5968+
"New TCP flags were discovered. See bpo-32394 for more information")
59485969

59495970
def test_main():
59505971
tests = [GeneralModuleTests, BasicTCPTest, TCPCloserTest, TCPTimeoutTest,
@@ -6005,6 +6026,7 @@ def test_main():
60056026
SendfileUsingSendTest,
60066027
SendfileUsingSendfileTest,
60076028
])
6029+
tests.append(TestMSWindowsTCPFlags)
60086030

60096031
thread_info = support.threading_setup()
60106032
support.run_unittest(*tests)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
socket: Remove TCP_FASTOPEN,TCP_KEEPCNT,TCP_KEEPIDLE,TCP_KEEPINTVL flags on
2+
older version Windows during run-time.

Modules/socketmodule.c

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,70 @@ if_indextoname(index) -- return the corresponding interface name\n\
305305
/* Provides the IsWindows7SP1OrGreater() function */
306306
#include <VersionHelpers.h>
307307

308+
/* remove some flags on older version Windows during run-time.
309+
https://msdn.microsoft.com/en-us/library/windows/desktop/ms738596.aspx */
310+
typedef struct {
311+
DWORD build_number; /* available starting with this Win10 BuildNumber */
312+
const char flag_name[20];
313+
} FlagRuntimeInfo;
314+
315+
/* IMPORTANT: make sure the list ordered by descending build_number */
316+
static FlagRuntimeInfo win_runtime_flags[] = {
317+
/* available starting with Windows 10 1709 */
318+
{16299, "TCP_KEEPIDLE"},
319+
{16299, "TCP_KEEPINTVL"},
320+
/* available starting with Windows 10 1703 */
321+
{15063, "TCP_KEEPCNT"},
322+
/* available starting with Windows 10 1607 */
323+
{14393, "TCP_FASTOPEN"}
324+
};
325+
326+
static void
327+
remove_unusable_flags(PyObject *m)
328+
{
329+
PyObject *dict;
330+
OSVERSIONINFOEX info;
331+
DWORDLONG dwlConditionMask;
332+
333+
dict = PyModule_GetDict(m);
334+
if (dict == NULL) {
335+
return;
336+
}
337+
338+
/* set to Windows 10, except BuildNumber. */
339+
memset(&info, 0, sizeof(info));
340+
info.dwOSVersionInfoSize = sizeof(info);
341+
info.dwMajorVersion = 10;
342+
info.dwMinorVersion = 0;
343+
344+
/* set Condition Mask */
345+
dwlConditionMask = 0;
346+
VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
347+
VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
348+
VER_SET_CONDITION(dwlConditionMask, VER_BUILDNUMBER, VER_GREATER_EQUAL);
349+
350+
for (int i=0; i<sizeof(win_runtime_flags)/sizeof(FlagRuntimeInfo); i++) {
351+
info.dwBuildNumber = win_runtime_flags[i].build_number;
352+
/* greater than or equal to the specified version?
353+
Compatibility Mode will not cheat VerifyVersionInfo(...) */
354+
if (VerifyVersionInfo(
355+
&info,
356+
VER_MAJORVERSION|VER_MINORVERSION|VER_BUILDNUMBER,
357+
dwlConditionMask)) {
358+
break;
359+
}
360+
else {
361+
if (PyDict_GetItemString(
362+
dict,
363+
win_runtime_flags[i].flag_name) != NULL) {
364+
PyDict_DelItemString(
365+
dict,
366+
win_runtime_flags[i].flag_name);
367+
}
368+
}
369+
}
370+
}
371+
308372
#endif
309373

310374
#include <stddef.h>
@@ -7890,5 +7954,11 @@ PyInit__socket(void)
78907954
#if defined(USE_GETHOSTBYNAME_LOCK) || defined(USE_GETADDRINFO_LOCK)
78917955
netdb_lock = PyThread_allocate_lock();
78927956
#endif
7957+
7958+
#ifdef MS_WINDOWS
7959+
/* remove some flags on older version Windows during run-time */
7960+
remove_unusable_flags(m);
7961+
#endif
7962+
78937963
return m;
78947964
}

0 commit comments

Comments
 (0)