Package proton :: Module _transport
[frames] | no frames]

Source Code for Module proton._transport

  1  # 
  2  # Licensed to the Apache Software Foundation (ASF) under one 
  3  # or more contributor license agreements.  See the NOTICE file 
  4  # distributed with this work for additional information 
  5  # regarding copyright ownership.  The ASF licenses this file 
  6  # to you under the Apache License, Version 2.0 (the 
  7  # "License"); you may not use this file except in compliance 
  8  # with the License.  You may obtain a copy of the License at 
  9  # 
 10  #   http://www.apache.org/licenses/LICENSE-2.0 
 11  # 
 12  # Unless required by applicable law or agreed to in writing, 
 13  # software distributed under the License is distributed on an 
 14  # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 
 15  # KIND, either express or implied.  See the License for the 
 16  # specific language governing permissions and limitations 
 17  # under the License. 
 18  # 
 19   
 20  from __future__ import absolute_import 
 21   
 22  from cproton import PN_EOS, PN_OK, PN_SASL_AUTH, PN_SASL_NONE, PN_SASL_OK, PN_SASL_PERM, PN_SASL_SYS, PN_SASL_TEMP, \ 
 23      PN_SSL_ANONYMOUS_PEER, PN_SSL_CERT_SUBJECT_CITY_OR_LOCALITY, PN_SSL_CERT_SUBJECT_COMMON_NAME, \ 
 24      PN_SSL_CERT_SUBJECT_COUNTRY_NAME, PN_SSL_CERT_SUBJECT_ORGANIZATION_NAME, PN_SSL_CERT_SUBJECT_ORGANIZATION_UNIT, \ 
 25      PN_SSL_CERT_SUBJECT_STATE_OR_PROVINCE, PN_SSL_MD5, PN_SSL_MODE_CLIENT, PN_SSL_MODE_SERVER, PN_SSL_RESUME_NEW, \ 
 26      PN_SSL_RESUME_REUSED, PN_SSL_RESUME_UNKNOWN, PN_SSL_SHA1, PN_SSL_SHA256, PN_SSL_SHA512, PN_SSL_VERIFY_PEER, \ 
 27      PN_SSL_VERIFY_PEER_NAME, PN_TRACE_DRV, PN_TRACE_FRM, PN_TRACE_OFF, PN_TRACE_RAW, pn_error_text, pn_sasl, \ 
 28      pn_sasl_allowed_mechs, pn_sasl_config_name, pn_sasl_config_path, pn_sasl_done, pn_sasl_extended, \ 
 29      pn_sasl_get_allow_insecure_mechs, pn_sasl_get_mech, pn_sasl_get_user, pn_sasl_outcome, \ 
 30      pn_sasl_set_allow_insecure_mechs, pn_ssl, pn_ssl_domain, pn_ssl_domain_allow_unsecured_client, pn_ssl_domain_free, \ 
 31      pn_ssl_domain_set_credentials, pn_ssl_domain_set_peer_authentication, pn_ssl_domain_set_trusted_ca_db, \ 
 32      pn_ssl_get_cert_fingerprint, pn_ssl_get_cipher_name, pn_ssl_get_peer_hostname, pn_ssl_get_protocol_name, \ 
 33      pn_ssl_get_remote_subject, pn_ssl_get_remote_subject_subfield, pn_ssl_init, pn_ssl_present, pn_ssl_resume_status, \ 
 34      pn_ssl_set_peer_hostname, pn_transport, pn_transport_attachments, pn_transport_bind, pn_transport_capacity, \ 
 35      pn_transport_close_head, pn_transport_close_tail, pn_transport_closed, pn_transport_condition, \ 
 36      pn_transport_connection, pn_transport_error, pn_transport_get_channel_max, pn_transport_get_frames_input, \ 
 37      pn_transport_get_frames_output, pn_transport_get_idle_timeout, pn_transport_get_max_frame, \ 
 38      pn_transport_get_pytracer, pn_transport_get_remote_idle_timeout, pn_transport_get_remote_max_frame, \ 
 39      pn_transport_get_user, pn_transport_is_authenticated, pn_transport_is_encrypted, pn_transport_log, \ 
 40      pn_transport_peek, pn_transport_pending, pn_transport_pop, pn_transport_push, pn_transport_remote_channel_max, \ 
 41      pn_transport_require_auth, pn_transport_require_encryption, pn_transport_set_channel_max, \ 
 42      pn_transport_set_idle_timeout, pn_transport_set_max_frame, pn_transport_set_pytracer, pn_transport_set_server, \ 
 43      pn_transport_tick, pn_transport_trace, pn_transport_unbind 
 44   
 45  from ._common import millis2secs, secs2millis, unicode2utf8, utf82unicode 
 46  from ._condition import cond2obj, obj2cond 
 47  from ._exceptions import EXCEPTIONS, SSLException, SSLUnavailable, SessionException, TransportException 
 48  from ._wrapper import Wrapper 
49 50 51 -class TraceAdapter:
52
53 - def __init__(self, tracer):
54 self.tracer = tracer
55
56 - def __call__(self, trans_impl, message):
57 self.tracer(Transport.wrap(trans_impl), message)
58
59 60 -class Transport(Wrapper):
61 TRACE_OFF = PN_TRACE_OFF 62 TRACE_DRV = PN_TRACE_DRV 63 TRACE_FRM = PN_TRACE_FRM 64 TRACE_RAW = PN_TRACE_RAW 65 66 CLIENT = 1 67 SERVER = 2 68 69 @staticmethod
70 - def wrap(impl):
71 if impl is None: 72 return None 73 else: 74 return Transport(_impl=impl)
75
76 - def __init__(self, mode=None, _impl=pn_transport):
77 Wrapper.__init__(self, _impl, pn_transport_attachments) 78 if mode == Transport.SERVER: 79 pn_transport_set_server(self._impl) 80 elif mode is None or mode == Transport.CLIENT: 81 pass 82 else: 83 raise TransportException("Cannot initialise Transport from mode: %s" % str(mode))
84
85 - def _init(self):
86 self._sasl = None 87 self._ssl = None 88 self._reactor = None
89
90 - def _check(self, err):
91 if err < 0: 92 exc = EXCEPTIONS.get(err, TransportException) 93 raise exc("[%s]: %s" % (err, pn_error_text(pn_transport_error(self._impl)))) 94 else: 95 return err
96
97 - def _set_tracer(self, tracer):
98 pn_transport_set_pytracer(self._impl, TraceAdapter(tracer))
99
100 - def _get_tracer(self):
101 adapter = pn_transport_get_pytracer(self._impl) 102 if adapter: 103 return adapter.tracer 104 else: 105 return None
106 107 tracer = property(_get_tracer, _set_tracer, 108 doc=""" 109 A callback for trace logging. The callback is passed the transport and log message. 110 """) 111
112 - def log(self, message):
113 pn_transport_log(self._impl, message)
114
115 - def require_auth(self, bool):
116 pn_transport_require_auth(self._impl, bool)
117 118 @property
119 - def authenticated(self):
120 return pn_transport_is_authenticated(self._impl)
121
122 - def require_encryption(self, bool):
123 pn_transport_require_encryption(self._impl, bool)
124 125 @property
126 - def encrypted(self):
127 return pn_transport_is_encrypted(self._impl)
128 129 @property
130 - def user(self):
131 return pn_transport_get_user(self._impl)
132
133 - def bind(self, connection):
134 """Assign a connection to the transport""" 135 self._check(pn_transport_bind(self._impl, connection._impl))
136
137 - def bind_nothrow(self, connection):
138 """Assign a connection to the transport""" 139 pn_transport_bind(self._impl, connection._impl)
140
141 - def unbind(self):
142 """Release the connection""" 143 self._check(pn_transport_unbind(self._impl))
144
145 - def trace(self, n):
146 pn_transport_trace(self._impl, n)
147
148 - def tick(self, now):
149 """Process any timed events (like heartbeat generation). 150 now = seconds since epoch (float). 151 """ 152 return millis2secs(pn_transport_tick(self._impl, secs2millis(now)))
153
154 - def capacity(self):
155 c = pn_transport_capacity(self._impl) 156 if c >= PN_EOS: 157 return c 158 else: 159 return self._check(c)
160
161 - def push(self, binary):
162 n = self._check(pn_transport_push(self._impl, binary)) 163 if n != len(binary): 164 raise OverflowError("unable to process all bytes: %s, %s" % (n, len(binary)))
165
166 - def close_tail(self):
167 self._check(pn_transport_close_tail(self._impl))
168
169 - def pending(self):
170 p = pn_transport_pending(self._impl) 171 if p >= PN_EOS: 172 return p 173 else: 174 return self._check(p)
175
176 - def peek(self, size):
177 cd, out = pn_transport_peek(self._impl, size) 178 if cd == PN_EOS: 179 return None 180 else: 181 self._check(cd) 182 return out
183
184 - def pop(self, size):
185 pn_transport_pop(self._impl, size)
186
187 - def close_head(self):
188 self._check(pn_transport_close_head(self._impl))
189 190 @property
191 - def closed(self):
192 return pn_transport_closed(self._impl)
193 194 # AMQP 1.0 max-frame-size
195 - def _get_max_frame_size(self):
196 return pn_transport_get_max_frame(self._impl)
197
198 - def _set_max_frame_size(self, value):
199 pn_transport_set_max_frame(self._impl, value)
200 201 max_frame_size = property(_get_max_frame_size, _set_max_frame_size, 202 doc=""" 203 Sets the maximum size for received frames (in bytes). 204 """) 205 206 @property
207 - def remote_max_frame_size(self):
208 return pn_transport_get_remote_max_frame(self._impl)
209
210 - def _get_channel_max(self):
211 return pn_transport_get_channel_max(self._impl)
212
213 - def _set_channel_max(self, value):
214 if pn_transport_set_channel_max(self._impl, value): 215 raise SessionException("Too late to change channel max.")
216 217 channel_max = property(_get_channel_max, _set_channel_max, 218 doc=""" 219 Sets the maximum channel that may be used on the transport. 220 """) 221 222 @property
223 - def remote_channel_max(self):
224 return pn_transport_remote_channel_max(self._impl)
225 226 # AMQP 1.0 idle-time-out
227 - def _get_idle_timeout(self):
228 return millis2secs(pn_transport_get_idle_timeout(self._impl))
229
230 - def _set_idle_timeout(self, sec):
231 pn_transport_set_idle_timeout(self._impl, secs2millis(sec))
232 233 idle_timeout = property(_get_idle_timeout, _set_idle_timeout, 234 doc=""" 235 The idle timeout of the connection (float, in seconds). 236 """) 237 238 @property
239 - def remote_idle_timeout(self):
240 return millis2secs(pn_transport_get_remote_idle_timeout(self._impl))
241 242 @property
243 - def frames_output(self):
244 return pn_transport_get_frames_output(self._impl)
245 246 @property
247 - def frames_input(self):
248 return pn_transport_get_frames_input(self._impl)
249
250 - def sasl(self):
251 return SASL(self)
252
253 - def ssl(self, domain=None, session_details=None):
254 # SSL factory (singleton for this transport) 255 if not self._ssl: 256 self._ssl = SSL(self, domain, session_details) 257 return self._ssl
258
259 - def _get_condition(self):
260 return cond2obj(pn_transport_condition(self._impl))
261
262 - def _set_condition(self, cond):
263 pn_cond = pn_transport_condition(self._impl) 264 obj2cond(cond, pn_cond)
265 266 condition = property(_get_condition, _set_condition, 267 doc=""" 268 The error condition (if any) of the transport. 269 """) 270 271 @property
272 - def connection(self):
273 from . import _endpoints 274 return _endpoints.Connection.wrap(pn_transport_connection(self._impl))
275
276 277 -class SASLException(TransportException):
278 pass
279
280 281 -class SASL(Wrapper):
282 OK = PN_SASL_OK 283 AUTH = PN_SASL_AUTH 284 SYS = PN_SASL_SYS 285 PERM = PN_SASL_PERM 286 TEMP = PN_SASL_TEMP 287 288 @staticmethod
289 - def extended():
290 return pn_sasl_extended()
291
292 - def __init__(self, transport):
293 Wrapper.__init__(self, transport._impl, pn_transport_attachments) 294 self._sasl = pn_sasl(transport._impl)
295
296 - def _check(self, err):
297 if err < 0: 298 exc = EXCEPTIONS.get(err, SASLException) 299 raise exc("[%s]" % (err)) 300 else: 301 return err
302 303 @property
304 - def user(self):
305 return pn_sasl_get_user(self._sasl)
306 307 @property
308 - def mech(self):
309 return pn_sasl_get_mech(self._sasl)
310 311 @property
312 - def outcome(self):
313 outcome = pn_sasl_outcome(self._sasl) 314 if outcome == PN_SASL_NONE: 315 return None 316 else: 317 return outcome
318
319 - def allowed_mechs(self, mechs):
320 if isinstance(mechs, list): 321 mechs = " ".join(mechs) 322 pn_sasl_allowed_mechs(self._sasl, unicode2utf8(mechs))
323
325 return pn_sasl_get_allow_insecure_mechs(self._sasl)
326
327 - def _set_allow_insecure_mechs(self, insecure):
328 pn_sasl_set_allow_insecure_mechs(self._sasl, insecure)
329 330 allow_insecure_mechs = property(_get_allow_insecure_mechs, _set_allow_insecure_mechs, 331 doc=""" 332 Allow unencrypted cleartext passwords (PLAIN mech) 333 """) 334
335 - def done(self, outcome):
336 pn_sasl_done(self._sasl, outcome)
337
338 - def config_name(self, name):
339 pn_sasl_config_name(self._sasl, name)
340
341 - def config_path(self, path):
342 pn_sasl_config_path(self._sasl, path)
343
344 345 -class SSLDomain(object):
346 MODE_CLIENT = PN_SSL_MODE_CLIENT 347 MODE_SERVER = PN_SSL_MODE_SERVER 348 VERIFY_PEER = PN_SSL_VERIFY_PEER 349 VERIFY_PEER_NAME = PN_SSL_VERIFY_PEER_NAME 350 ANONYMOUS_PEER = PN_SSL_ANONYMOUS_PEER 351
352 - def __init__(self, mode):
353 self._domain = pn_ssl_domain(mode) 354 if self._domain is None: 355 raise SSLUnavailable()
356
357 - def _check(self, err):
358 if err < 0: 359 exc = EXCEPTIONS.get(err, SSLException) 360 raise exc("SSL failure.") 361 else: 362 return err
363
364 - def set_credentials(self, cert_file, key_file, password):
365 return self._check(pn_ssl_domain_set_credentials(self._domain, 366 cert_file, key_file, 367 password))
368
369 - def set_trusted_ca_db(self, certificate_db):
370 return self._check(pn_ssl_domain_set_trusted_ca_db(self._domain, 371 certificate_db))
372
373 - def set_peer_authentication(self, verify_mode, trusted_CAs=None):
374 return self._check(pn_ssl_domain_set_peer_authentication(self._domain, 375 verify_mode, 376 trusted_CAs))
377
378 - def allow_unsecured_client(self):
379 return self._check(pn_ssl_domain_allow_unsecured_client(self._domain))
380
381 - def __del__(self):
382 pn_ssl_domain_free(self._domain)
383
384 385 -class SSL(object):
386 387 @staticmethod
388 - def present():
389 return pn_ssl_present()
390
391 - def _check(self, err):
392 if err < 0: 393 exc = EXCEPTIONS.get(err, SSLException) 394 raise exc("SSL failure.") 395 else: 396 return err
397
398 - def __new__(cls, transport, domain, session_details=None):
399 """Enforce a singleton SSL object per Transport""" 400 if transport._ssl: 401 # unfortunately, we've combined the allocation and the configuration in a 402 # single step. So catch any attempt by the application to provide what 403 # may be a different configuration than the original (hack) 404 ssl = transport._ssl 405 if (domain and (ssl._domain is not domain) or 406 session_details and (ssl._session_details is not session_details)): 407 raise SSLException("Cannot re-configure existing SSL object!") 408 else: 409 obj = super(SSL, cls).__new__(cls) 410 obj._domain = domain 411 obj._session_details = session_details 412 session_id = None 413 if session_details: 414 session_id = session_details.get_session_id() 415 obj._ssl = pn_ssl(transport._impl) 416 if obj._ssl is None: 417 raise SSLUnavailable() 418 if domain: 419 pn_ssl_init(obj._ssl, domain._domain, session_id) 420 transport._ssl = obj 421 return transport._ssl
422
423 - def cipher_name(self):
424 rc, name = pn_ssl_get_cipher_name(self._ssl, 128) 425 if rc: 426 return name 427 return None
428
429 - def protocol_name(self):
430 rc, name = pn_ssl_get_protocol_name(self._ssl, 128) 431 if rc: 432 return name 433 return None
434 435 SHA1 = PN_SSL_SHA1 436 SHA256 = PN_SSL_SHA256 437 SHA512 = PN_SSL_SHA512 438 MD5 = PN_SSL_MD5 439 440 CERT_COUNTRY_NAME = PN_SSL_CERT_SUBJECT_COUNTRY_NAME 441 CERT_STATE_OR_PROVINCE = PN_SSL_CERT_SUBJECT_STATE_OR_PROVINCE 442 CERT_CITY_OR_LOCALITY = PN_SSL_CERT_SUBJECT_CITY_OR_LOCALITY 443 CERT_ORGANIZATION_NAME = PN_SSL_CERT_SUBJECT_ORGANIZATION_NAME 444 CERT_ORGANIZATION_UNIT = PN_SSL_CERT_SUBJECT_ORGANIZATION_UNIT 445 CERT_COMMON_NAME = PN_SSL_CERT_SUBJECT_COMMON_NAME 446
447 - def get_cert_subject_subfield(self, subfield_name):
448 subfield_value = pn_ssl_get_remote_subject_subfield(self._ssl, subfield_name) 449 return subfield_value
450
451 - def get_cert_subject(self):
452 subject = pn_ssl_get_remote_subject(self._ssl) 453 return subject
454
456 # Pass in an unhandled enum 457 return self.get_cert_subject_subfield(10)
458 459 # Convenience functions for obtaining the subfields of the subject field.
460 - def get_cert_common_name(self):
462
463 - def get_cert_organization(self):
465 468 471
472 - def get_cert_country(self):
474 477
478 - def get_cert_fingerprint(self, fingerprint_length, digest_name):
479 rc, fingerprint_str = pn_ssl_get_cert_fingerprint(self._ssl, fingerprint_length, digest_name) 480 if rc == PN_OK: 481 return fingerprint_str 482 return None
483 484 # Convenience functions for obtaining fingerprint for specific hashing algorithms
486 return self.get_cert_fingerprint(41, 10)
487
489 return self.get_cert_fingerprint(41, SSL.SHA1)
490
492 # sha256 produces a fingerprint that is 64 characters long 493 return self.get_cert_fingerprint(65, SSL.SHA256)
494
496 # sha512 produces a fingerprint that is 128 characters long 497 return self.get_cert_fingerprint(129, SSL.SHA512)
498
499 - def get_cert_fingerprint_md5(self):
500 return self.get_cert_fingerprint(33, SSL.MD5)
501 502 @property
503 - def remote_subject(self):
504 return pn_ssl_get_remote_subject(self._ssl)
505 506 RESUME_UNKNOWN = PN_SSL_RESUME_UNKNOWN 507 RESUME_NEW = PN_SSL_RESUME_NEW 508 RESUME_REUSED = PN_SSL_RESUME_REUSED 509
510 - def resume_status(self):
511 return pn_ssl_resume_status(self._ssl)
512
513 - def _set_peer_hostname(self, hostname):
514 self._check(pn_ssl_set_peer_hostname(self._ssl, unicode2utf8(hostname)))
515
516 - def _get_peer_hostname(self):
517 err, name = pn_ssl_get_peer_hostname(self._ssl, 1024) 518 self._check(err) 519 return utf82unicode(name)
520 521 peer_hostname = property(_get_peer_hostname, _set_peer_hostname, 522 doc=""" 523 Manage the expected name of the remote peer. Used to authenticate the remote. 524 """)
525
526 527 -class SSLSessionDetails(object):
528 """ Unique identifier for the SSL session. Used to resume previous session on a new 529 SSL connection. 530 """ 531
532 - def __init__(self, session_id):
533 self._session_id = session_id
534
535 - def get_session_id(self):
536 return self._session_id
537