3
[_ð]J*  ã               @   sh   d Z ddlmZ ddlZddlZddlZddlZddlZddl	m
Z
 dZdZdZdZG dd	„ d	eƒZdS )
z*Help for building DNS wire format messagesé    )ÚBytesIONé   )Úlongé   é   c               @   sŒ   e Zd ZdZddd„Zdd„ Zd	d
„ Zejj	fdd„Z
dd„ Zdd„ Zddd„Zejjfdd„Zejjfdd„Zdd„ Zdd„ Zdd„ ZdS )ÚRendereraõ  Helper class for building DNS wire-format messages.

    Most applications can use the higher-level L{dns.message.Message}
    class and its to_wire() method to generate wire-format messages.
    This class is for those applications which need finer control
    over the generation of messages.

    Typical use::

        r = dns.renderer.Renderer(id=1, flags=0x80, max_size=512)
        r.add_question(qname, qtype, qclass)
        r.add_rrset(dns.renderer.ANSWER, rrset_1)
        r.add_rrset(dns.renderer.ANSWER, rrset_2)
        r.add_rrset(dns.renderer.AUTHORITY, ns_rrset)
        r.add_edns(0, 0, 4096)
        r.add_rrset(dns.renderer.ADDTIONAL, ad_rrset_1)
        r.add_rrset(dns.renderer.ADDTIONAL, ad_rrset_2)
        r.write_header()
        r.add_tsig(keyname, secret, 300, 1, 0, '', request_mac)
        wire = r.get_wire()

    output, a BytesIO, where rendering is written

    id: the message id

    flags: the message flags

    max_size: the maximum size of the message

    origin: the origin to use when rendering relative names

    compress: the compression table

    section: an int, the section currently being rendered

    counts: list of the number of RRs in each section

    mac: the MAC of the rendered message (if TSIG was used)
    Nr   éÿÿ  c             C   sh   t ƒ | _|dkr tjddƒ| _n|| _|| _|| _|| _i | _t	| _
ddddg| _| jjdƒ d| _dS )zInitialize a new renderer.Nr   iÿÿ  ó    é   Ú s               )r   ÚoutputÚrandomÚrandintÚidÚflagsÚmax_sizeÚoriginÚcompressÚQUESTIONÚsectionÚcountsÚwriteÚmac)Úselfr   r   r   r   © r   ú1/tmp/pip-build-yz_zf6az/dnspython/dns/renderer.pyÚ__init__M   s    zRenderer.__init__c             C   s^   | j j|ƒ | j jƒ  g }x(| jjƒ D ]\}}||kr&|j|ƒ q&W x|D ]}| j|= qJW dS )z™Truncate the output buffer at offset *where*, and remove any
        compression table entries that pointed beyond the truncation
        point.
        N)r   ÚseekÚtruncater   ÚitemsÚappend)r   ÚwhereZkeys_to_deleteÚkÚvr   r   r   Ú	_rollback^   s    

zRenderer._rollbackc             C   s&   | j |kr"| j |krtjj‚|| _ dS )a  Set the renderer's current section.

        Sections must be rendered order: QUESTION, ANSWER, AUTHORITY,
        ADDITIONAL.  Sections may be empty.

        Raises dns.exception.FormError if an attempt was made to set
        a section value less than the current section.
        N)r   ÚdnsÚ	exceptionZ	FormError)r   r   r   r   r   Ú_set_sectionm   s    


zRenderer._set_sectionc             C   sz   | j tƒ | jjƒ }|j| j| j| jƒ | jjtj	d||ƒƒ | jjƒ }|| j
krd| j|ƒ tjj‚| jt  d7  < dS )zAdd a question to the message.z!HHr   N)r'   r   r   ÚtellÚto_wirer   r   r   ÚstructÚpackr   r$   r%   r&   ÚTooBigr   )r   ÚqnameZrdtypeZrdclassÚbeforeÚafterr   r   r   Úadd_question|   s    




zRenderer.add_questionc             K   sh   | j |ƒ | jjƒ }|j| j| j| jf|Ž}| jjƒ }|| jkrR| j|ƒ tj	j
‚| j|  |7  < dS )zŠAdd the rrset to the specified section.

        Any keyword arguments are passed on to the rdataset's to_wire()
        routine.
        N)r'   r   r(   r)   r   r   r   r$   r%   r&   r,   r   )r   r   ZrrsetÚkwr.   Únr/   r   r   r   Ú	add_rrset‰   s    




zRenderer.add_rrsetc             K   sj   | j |ƒ | jjƒ }|j|| j| j| jf|Ž}| jjƒ }|| jkrT| j|ƒ tj	j
‚| j|  |7  < dS )zÁAdd the rdataset to the specified section, using the specified
        name as the owner name.

        Any keyword arguments are passed on to the rdataset's to_wire()
        routine.
        N)r'   r   r(   r)   r   r   r   r$   r%   r&   r,   r   )r   r   ÚnameZrdatasetr1   r.   r2   r/   r   r   r   Úadd_rdataset™   s    




zRenderer.add_rdatasetc             C   sˆ  |t dƒM }||d> O }| jtƒ | jjƒ }| jjtjddtj	j
||dƒƒ |dk	�rJ| jjƒ }x’|D ]Š}tjd|jdƒ}| jj|ƒ | jjƒ }	|j| jƒ | jjƒ }
|
|	 dk s¶t‚| jj|	d ƒ tjd	|
|	 ƒ}| jj|ƒ | jjddƒ qfW | jjƒ }|| dk �st‚| jj|d ƒ tjd	|| ƒ}| jj|ƒ | jjddƒ | jjƒ }|| jk�rr| j|ƒ tjj‚| jt  d
7  < dS )z&Add an EDNS OPT record to the message.l   ÿ~ é   z!BHHIHr   Nz!HHi   r   z!Hr   )r   r'   Ú
ADDITIONALr   r(   r   r*   r+   r%   Ú	rdatatypeÚOPTÚotyper)   ÚAssertionErrorr   r   r$   r&   r,   r   )r   ZednsZ	ednsflagsÚpayloadÚoptionsr.   ZlstartÚoptÚstuffÚstartÚendZlendr/   r   r   r   Úadd_edns«   s<    










zRenderer.add_ednsc	             C   sJ   | j jƒ }	tjj|	||ttjƒ ƒ||||||d�
\}
| _}| j|
|ƒ dS )z$Add a TSIG signature to the message.)Ú	algorithmN)	r   Úgetvaluer%   ÚtsigÚsignÚintÚtimer   Ú_write_tsig)r   ÚkeynameÚsecretÚfudger   Ú
tsig_errorÚ
other_dataÚrequest_macrC   ÚsÚ
tsig_rdataÚctxr   r   r   Úadd_tsigÎ   s    

zRenderer.add_tsigc
             C   sT   | j jƒ }
tjj|
||ttjƒ ƒ|||||||dkd|	d�\}| _}| j||ƒ |S )ay  Add a TSIG signature to the message. Unlike add_tsig(), this can be
        used for a series of consecutive DNS envelopes, e.g. for a zone
        transfer over TCP [RFC2845, 4.4].

        For the first message in the sequence, give ctx=None. For each
        subsequent message, give the ctx that was returned from the
        add_multi_tsig() call for the previous message.NT)rR   ÚfirstZmultirC   )	r   rD   r%   rE   rF   rG   rH   r   rI   )r   rR   rJ   rK   rL   r   rM   rN   rO   rC   rP   rQ   r   r   r   Úadd_multi_tsigß   s     

zRenderer.add_multi_tsigc             C   s  | j tƒ | jjƒ }|j| j| j| jƒ | jjtj	dt
jjt
jjddƒƒ | jjƒ }| jj|ƒ | jjƒ }|| dk szt‚|| jkr–| j|ƒ t
jj‚| jj|d ƒ | jjtj	d|| ƒƒ | jt  d7  < | jjdƒ | jjtj	d| jt ƒƒ | jjddƒ d S )Nz!HHIHr   i   r   z!Hr   é
   )r'   r7   r   r(   r)   r   r   r   r*   r+   r%   r8   ZTSIGÚ
rdataclassÚANYr;   r   r$   r&   r,   r   r   )r   rQ   rJ   r.   Zrdata_startr/   r   r   r   rI   û   s$    





zRenderer._write_tsigc          
   C   sX   | j jdƒ | j jtjd| j| j| jd | jd | jd | jd ƒƒ | j jddƒ dS )z¾Write the DNS message header.

        Writing the DNS message header is done after all sections
        have been rendered, but before the optional TSIG signature
        is added.
        r   z!HHHHHHr   r   r   N)r   r   r   r*   r+   r   r   r   )r   r   r   r   Úwrite_header  s
    zRenderer.write_headerc             C   s
   | j jƒ S )zReturn the wire format message.)r   rD   )r   r   r   r   Úget_wire   s    zRenderer.get_wire)Nr   r   N)N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r$   r'   r%   rW   ÚINr0   r3   r5   rB   rE   Zdefault_algorithmrS   rU   rI   rY   rZ   r   r   r   r   r   $   s   '

$r   )r^   Úior   r*   r   rH   Zdns.exceptionr%   Zdns.tsigÚ_compatr   r   ZANSWERZ	AUTHORITYr7   Úobjectr   r   r   r   r   Ú<module>   s   