Coverage for aiocoap/defaults.py: 89%

121 statements  

« prev     ^ index     » next       coverage.py v7.6.3, created at 2024-10-15 22:10 +0000

1# SPDX-FileCopyrightText: Christian Amsüss and the aiocoap contributors 

2# 

3# SPDX-License-Identifier: MIT 

4 

5"""This module contains helpers that inspect available modules and platform 

6specifics to give sane values to aiocoap defaults. 

7 

8All of this should eventually overridable by other libraries wrapping/using 

9aiocoap and by applications using aiocoap; however, these overrides do not 

10happen in the defaults module but where these values are actually accessed, so 

11this module is considered internal to aiocoap and not part of the API. 

12 

13The ``_missing_modules`` functions are helpers for inspecting what is 

14reasonable to expect to work. They can influence default values, but should not 

15be used in the rest of the code for feature checking (just raise the 

16ImportErrors) unless it's directly user-visible ("You configured OSCORE key 

17material, but OSCORE needs the following unavailable modules") or in the test 

18suite to decide which tests to skip. 

19""" 

20 

21import os 

22import socket 

23import sys 

24import warnings 

25 

26try: 

27 import pyodide # noqa: F401 

28 import js # noqa: F401 

29except ImportError: 

30 is_pyodide = False 

31else: 

32 is_pyodide = True 

33 

34 

35def get_default_clienttransports(*, loop=None, use_env=True): 

36 """Return a list of transports that should be connected when a client 

37 context is created. 

38 

39 If an explicit ``AIOCOAP_CLIENT_TRANSPORT`` environment variable is set, it 

40 is read as a colon separated list of transport names. 

41 

42 By default, a DTLS mechanism will be picked if the required modules are 

43 available, and a UDP transport will be selected depending on whether the 

44 full udp6 transport is known to work. 

45 """ 

46 

47 if use_env and "AIOCOAP_CLIENT_TRANSPORT" in os.environ: 

48 yield from os.environ["AIOCOAP_CLIENT_TRANSPORT"].split(":") 

49 return 

50 

51 if not oscore_missing_modules(): 

52 yield "oscore" 

53 

54 if not dtls_missing_modules(): 

55 yield "tinydtls" 

56 

57 yield "tcpclient" 

58 yield "tlsclient" 

59 if not ws_missing_modules(): 

60 yield "ws" 

61 

62 if sys.platform != "linux": 

63 # udp6 was never reported to work on anything but linux; would happily 

64 # add more platforms. 

65 yield "simple6" 

66 return 

67 

68 # on android it seems that it's only the AI_V4MAPPED that causes trouble, 

69 # that should be managable in udp6 too. 

70 yield "udp6" 

71 return 

72 

73 

74def get_default_servertransports(*, loop=None, use_env=True): 

75 """Return a list of transports that should be connected when a server 

76 context is created. 

77 

78 If an explicit ``AIOCOAP_SERVER_TRANSPORT`` environment variable is set, it 

79 is read as a colon separated list of transport names. 

80 

81 By default, a DTLS mechanism will be picked if the required modules are 

82 available, and a UDP transport will be selected depending on whether the 

83 full udp6 transport is known to work. Both a simple6 and a simplesocketserver 

84 will be selected when udp6 is not available, and the simple6 will be used 

85 for any outgoing requests, which the simplesocketserver could serve but is worse 

86 at. 

87 """ 

88 

89 if use_env and "AIOCOAP_SERVER_TRANSPORT" in os.environ: 

90 yield from os.environ["AIOCOAP_SERVER_TRANSPORT"].split(":") 

91 return 

92 

93 if not oscore_missing_modules(): 

94 yield "oscore" 

95 

96 if not dtls_missing_modules(): 

97 if "AIOCOAP_DTLSSERVER_ENABLED" in os.environ: 

98 yield "tinydtls_server" 

99 yield "tinydtls" 

100 

101 yield "tcpserver" 

102 yield "tcpclient" 

103 yield "tlsserver" 

104 yield "tlsclient" 

105 if not ws_missing_modules(): 

106 yield "ws" 

107 

108 if sys.platform != "linux": 

109 # udp6 was never reported to work on anything but linux; would happily 

110 # add more platforms. 

111 yield "simple6" 

112 yield "simplesocketserver" 

113 return 

114 

115 # on android it seems that it's only the AI_V4MAPPED that causes trouble, 

116 # that should be managable in udp6 too. 

117 yield "udp6" 

118 return 

119 

120 

121def has_reuse_port(*, use_env=True): 

122 """Return true if the platform indicates support for SO_REUSEPORT. 

123 

124 Can be overridden by explicitly setting ``AIOCOAP_REUSE_PORT`` to 1 or 

125 0.""" 

126 

127 if use_env and os.environ.get("AIOCOAP_REUSE_PORT"): 

128 return bool(int(os.environ["AIOCOAP_REUSE_PORT"])) 

129 

130 return hasattr(socket, "SO_REUSEPORT") 

131 

132 

133def use_ai_v4mapped_emulation(): 

134 """This used to indicate when ai_v4mapped emulation was used. Given it is 

135 not used at all any more, the function is deprecated.""" 

136 warnings.warn( 

137 "AI_V4MAPPED emulation is not used any more at all", warnings.DeprecationWarning 

138 ) 

139 return False 

140 

141 

142# FIXME: If there were a way to check for the extras defined in setup.py, or to link these lists to what is descibed there, that'd be great. 

143 

144 

145def dtls_missing_modules(): 

146 """Return a list of modules that are missing in order to use the DTLS 

147 transport, or a false value if everything is present""" 

148 

149 missing = [] 

150 

151 try: 

152 from DTLSSocket import dtls # noqa: F401 

153 except ImportError: 

154 missing.append("DTLSSocket") 

155 

156 return missing 

157 

158 

159def oscore_missing_modules(): 

160 """Return a list of modules that are missing in order to use OSCORE, or a 

161 false value if everything is present""" 

162 missing = [] 

163 try: 

164 import cbor2 # noqa: F401 

165 except ImportError: 

166 missing.append("cbor2") 

167 try: 

168 import cryptography # noqa: F401 

169 except ImportError: 

170 missing.append("cryptography") 

171 else: 

172 try: 

173 from cryptography.hazmat.primitives.ciphers.aead import AESCCM 

174 

175 AESCCM(b"x" * 16, 8) 

176 except (cryptography.exceptions.UnsupportedAlgorithm, ImportError): 

177 missing.append("a version of OpenSSL that supports AES-CCM") 

178 try: 

179 import filelock # noqa: F401 

180 except ImportError: 

181 missing.append("filelock") 

182 

183 try: 

184 import ge25519 # noqa: F401 

185 except ImportError: 

186 missing.append("ge25519") 

187 

188 try: 

189 import lakers # noqa: F401 

190 except ImportError: 

191 missing.append("lakers-python") 

192 

193 return missing 

194 

195 

196def ws_missing_modules(): 

197 """Return a list of modules that are missing in order to user CoAP-over-WS, 

198 or a false value if everything is present""" 

199 

200 if is_pyodide: 

201 return [] 

202 

203 missing = [] 

204 try: 

205 import websockets # noqa: F401 

206 except ImportError: 

207 missing.append("websockets") 

208 

209 return missing 

210 

211 

212def linkheader_missing_modules(): 

213 """Return a list of moudles that are missing in order to use link_header 

214 functionaity (eg. running a resource directory), of a false value if 

215 everything is present.""" 

216 missing = [] 

217 # The link_header module is now provided in-tree 

218 return missing 

219 

220 

221def prettyprint_missing_modules(): 

222 """Return a list of modules that are missing in order to use pretty 

223 printing (ie. full aiocoap-client)""" 

224 missing = [] 

225 missing.extend(linkheader_missing_modules()) 

226 try: 

227 import cbor2 # noqa: F401 

228 except ImportError: 

229 missing.append("cbor2") 

230 try: 

231 import pygments # noqa: F401 

232 except ImportError: 

233 missing.append("pygments") 

234 try: 

235 import cbor_diag # noqa: F401 

236 except ImportError: 

237 missing.append("cbor-diag") 

238 return missing 

239 

240 

241missing_module_functions = { 

242 "dtls": dtls_missing_modules, 

243 "oscore": oscore_missing_modules, 

244 "linkheader": linkheader_missing_modules, 

245 "prettyprint": prettyprint_missing_modules, 

246 "ws": ws_missing_modules, 

247} 

248 

249__all__ = [ 

250 "get_default_clienttransports", 

251 "get_default_servertransports", 

252 "has_reuse_port", 

253 "dtls_missing_modules", 

254 "oscore_missing_modules", 

255 "ws_missing_modules", 

256 "linkheader_missing_modules", 

257 "prettyprint_missing_modules", 

258 "missing_module_functions", 

259]