Package dkim :: Module asyncsupport
[hide private]
[frames] | no frames]

Source Code for Module dkim.asyncsupport

  1  # This software is provided 'as-is', without any express or implied 
  2  # warranty.  In no event will the author be held liable for any damages 
  3  # arising from the use of this software. 
  4  # 
  5  # Permission is granted to anyone to use this software for any purpose, 
  6  # including commercial applications, and to alter it and redistribute it 
  7  # freely, subject to the following restrictions: 
  8  # 
  9  # 1. The origin of this software must not be misrepresented; you must not 
 10  #    claim that you wrote the original software. If you use this software 
 11  #    in a product, an acknowledgment in the product documentation would be 
 12  #    appreciated but is not required. 
 13  # 2. Altered source versions must be plainly marked as such, and must not be 
 14  #    misrepresented as being the original software. 
 15  # 3. This notice may not be removed or altered from any source distribution. 
 16  # 
 17  # Copyright (c) 2008 Greg Hewgill http://hewgill.com 
 18  # 
 19  # This has been modified from the original software. 
 20  # Copyright (c) 2011 William Grant <me@williamgrant.id.au> 
 21  # 
 22  # This has been modified from the original software. 
 23  # Copyright (c) 2016 Google, Inc. 
 24  # Contact: Brandon Long <blong@google.com> 
 25  # 
 26  # This has been modified from the original software. 
 27  # Copyright (c) 2016, 2017, 2018, 2019 Scott Kitterman <scott@kitterman.com> 
 28  # 
 29  # This has been modified from the original software. 
 30  # Copyright (c) 2017 Valimail Inc 
 31  # Contact: Gene Shuman <gene@valimail.com> 
 32   
 33  import asyncio 
 34  import aiodns 
 35  import base64 
 36  import dkim 
 37  import re 
 38   
 39  __all__ = [ 
 40      'get_txt_async', 
 41      'load_pk_from_dns_async', 
 42      'verify_async' 
 43      ] 
 44   
 45   
46 -async def get_txt_async(name, timeout=5):
47 """Return a TXT record associated with a DNS name in an asnyc loop. For 48 DKIM we can assume there is only one.""" 49 50 # Note: This will use the existing loop or create one if needed 51 loop = asyncio.get_event_loop() 52 resolver = aiodns.DNSResolver(loop=loop, timeout=timeout) 53 54 async def query(name, qtype): 55 return await resolver.query(name, qtype)
56 57 #q = query(name, 'TXT') 58 try: 59 result = await query(name, 'TXT') 60 except aiodns.error.DNSError: 61 result = None 62 63 if result: 64 return result[0].text 65 else: 66 return None 67 68
69 -async def load_pk_from_dns_async(name, dnsfunc, timeout=5):
70 s = await dnsfunc(name, timeout=timeout) 71 pk, keysize, ktag, seqtlsrpt = dkim.evaluate_pk(name, s) 72 return pk, keysize, ktag, seqtlsrpt
73
74 -class DKIM(dkim.DKIM):
75 #: Sign an RFC822 message and return the DKIM-Signature header line. 76 #: 77 #: Identical to dkim.DKIM, except uses aiodns and can be awaited in an 78 #: ascyncio context. See dkim.DKIM for details. 79 80 # Abstract helper method to verify a signed header 81 #: @param sig: List of (key, value) tuples containing tag=values of the header 82 #: @param include_headers: headers to validate b= signature against 83 #: @param sig_header: (header_name, header_value) 84 #: @param dnsfunc: interface to dns
85 - async def verify_sig(self, sig, include_headers, sig_header, dnsfunc):
86 name = sig[b's'] + b"._domainkey." + sig[b'd'] + b"." 87 try: 88 self.pk, self.keysize, self.ktag, self.seqtlsrpt = await load_pk_from_dns_async(name, 89 dnsfunc, timeout=self.timeout) 90 except dkim.KeyFormatError as e: 91 self.logger.error("%s" % e) 92 return False 93 return self.verify_sig_process(sig, include_headers, sig_header, dnsfunc)
94 95
96 - async def verify(self,idx=0,dnsfunc=get_txt_async):
97 sig, include_headers, sigheaders = self.verify_headerprep(idx=0) 98 return await self.verify_sig(sig, include_headers, sigheaders[idx], dnsfunc)
99 100
101 -async def verify_async(message, logger=None, dnsfunc=None, minkey=1024, 102 timeout=5, tlsrpt=False):
103 """Verify the first (topmost) DKIM signature on an RFC822 formatted message in an asyncio contxt. 104 @param message: an RFC822 formatted message (with either \\n or \\r\\n line endings) 105 @param logger: a logger to which debug info will be written (default None) 106 @param timeout: number of seconds for DNS lookup timeout (default = 5) 107 @param tlsrpt: message is an RFC 8460 TLS report (default False) 108 False: Not a tlsrpt, True: Is a tlsrpt, 'strict': tlsrpt, invalid if 109 service type is missing. For signing, if True, length is never used. 110 @return: True if signature verifies or False otherwise 111 """ 112 # type: (bytes, any, function, int) -> bool 113 # Note: This will use the existing loop or create one if needed 114 loop = asyncio.get_event_loop() 115 if not dnsfunc: 116 dnsfunc=get_txt_async 117 d = DKIM(message,logger=logger,minkey=minkey,timeout=timeout,tlsrpt=tlsrpt) 118 try: 119 return await d.verify(dnsfunc=dnsfunc) 120 except dkim.DKIMException as x: 121 if logger is not None: 122 logger.error("%s" % x) 123 return False
124