1<?php
2
3/**
4 * DNS Library for handling lookups and updates.
5 *
6 * Copyright (c) 2020, Mike Pultz <mike@mikepultz.com>. All rights reserved.
7 *
8 * See LICENSE for more details.
9 *
10 * @category  Networking
11 * @package   Net_DNS2
12 * @author    Mike Pultz <mike@mikepultz.com>
13 * @copyright 2020 Mike Pultz <mike@mikepultz.com>
14 * @license   http://www.opensource.org/licenses/bsd-license.php  BSD License
15 * @link      https://netdns2.com/
16 * @since     File available since Release 0.6.0
17 *
18 */
19
20/**
21 * The main dynamic DNS notifier class.
22 *
23 * This class provices functions to handle DNS notify requests as defined by RFC 1996.
24 *
25 * This is separate from the Net_DNS2_Resolver class, as while the underlying
26 * protocol is the same, the functionality is completely different.
27 *
28 * Generally, query (recursive) lookups are done against caching server, while
29 * notify requests are done against authoratative servers.
30 *
31 */
32class Net_DNS2_Notifier extends Net_DNS2
33{
34    /*
35     * a Net_DNS2_Packet_Request object used for the notify request
36     */
37    private $_packet;
38
39    /**
40     * Constructor - builds a new Net_DNS2_Notifier objected used for doing
41     * DNS notification for a changed zone
42     *
43     * @param string $zone    the domain name to use for DNS updates
44     * @param mixed  $options an array of config options or null
45     *
46     * @throws Net_DNS2_Exception
47     * @access public
48     *
49     */
50    public function __construct($zone, array $options = null)
51    {
52        parent::__construct($options);
53
54        //
55        // create the packet
56        //
57        $this->_packet = new Net_DNS2_Packet_Request(
58            strtolower(trim($zone, " \n\r\t.")), 'SOA', 'IN'
59        );
60
61        //
62        // make sure the opcode on the packet is set to NOTIFY
63        //
64        $this->_packet->header->opcode = Net_DNS2_Lookups::OPCODE_NOTIFY;
65    }
66
67    /**
68     * checks that the given name matches the name for the zone we're notifying
69     *
70     * @param string $name The name to be checked.
71     *
72     * @return boolean
73     * @throws Net_DNS2_Exception
74     * @access private
75     *
76     */
77    private function _checkName($name)
78    {
79        if (!preg_match('/' . $this->_packet->question[0]->qname . '$/', $name)) {
80
81            throw new Net_DNS2_Exception(
82                'name provided (' . $name . ') does not match zone name (' .
83                $this->_packet->question[0]->qname . ')',
84                Net_DNS2_Lookups::E_PACKET_INVALID
85            );
86        }
87
88        return true;
89    }
90
91    /**
92     *   3.7 - Add RR to notify
93     *
94     * @param Net_DNS2_RR $rr the Net_DNS2_RR object to be sent in the notify message
95     *
96     * @return boolean
97     * @throws Net_DNS2_Exception
98     * @access public
99     *
100     */
101    public function add(Net_DNS2_RR $rr)
102    {
103        $this->_checkName($rr->name);
104        //
105        // add the RR to the "notify" section
106        //
107        if (!in_array($rr, $this->_packet->answer)) {
108            $this->_packet->answer[] = $rr;
109        }
110        return true;
111    }
112
113    /**
114     * add a signature to the request for authentication
115     *
116     * @param string $keyname   the key name to use for the TSIG RR
117     * @param string $signature the key to sign the request.
118     *
119     * @return     boolean
120     * @access     public
121     * @see        Net_DNS2::signTSIG()
122     * @deprecated function deprecated in 1.1.0
123     *
124     */
125    public function signature($keyname, $signature, $algorithm = Net_DNS2_RR_TSIG::HMAC_MD5)
126    {
127        return $this->signTSIG($keyname, $signature, $algorithm);
128    }
129
130    /**
131     * returns the current internal packet object.
132     *
133     * @return Net_DNS2_Packet_Request
134     * @access public
135     #
136     */
137    public function packet()
138    {
139        //
140        // take a copy
141        //
142        $p = $this->_packet;
143
144        //
145        // check for an authentication method; either TSIG or SIG
146        //
147        if (   ($this->auth_signature instanceof Net_DNS2_RR_TSIG)
148            || ($this->auth_signature instanceof Net_DNS2_RR_SIG)
149        ) {
150            $p->additional[] = $this->auth_signature;
151        }
152
153        //
154        // update the counts
155        //
156        $p->header->qdcount = count($p->question);
157        $p->header->ancount = count($p->answer);
158        $p->header->nscount = count($p->authority);
159        $p->header->arcount = count($p->additional);
160
161        return $p;
162    }
163
164    /**
165     * executes the notify request
166     *
167     * @param Net_DNS2_Packet_Response &$response ref to the response object
168     *
169     * @return boolean
170     * @throws Net_DNS2_Exception
171     * @access public
172     *
173     */
174    public function notify(&$response = null)
175    {
176        //
177        // check for an authentication method; either TSIG or SIG
178        //
179        if (   ($this->auth_signature instanceof Net_DNS2_RR_TSIG)
180            || ($this->auth_signature instanceof Net_DNS2_RR_SIG)
181        ) {
182            $this->_packet->additional[] = $this->auth_signature;
183        }
184
185        //
186        // update the counts
187        //
188        $this->_packet->header->qdcount = count($this->_packet->question);
189        $this->_packet->header->ancount = count($this->_packet->answer);
190        $this->_packet->header->nscount = count($this->_packet->authority);
191        $this->_packet->header->arcount = count($this->_packet->additional);
192
193        //
194        // make sure we have some data to send
195        //
196        if ($this->_packet->header->qdcount == 0) {
197            throw new Net_DNS2_Exception(
198                'empty headers- nothing to send!',
199                Net_DNS2_Lookups::E_PACKET_INVALID
200            );
201        }
202
203        //
204        // send the packet and get back the response
205        //
206        $response = $this->sendPacket($this->_packet, $this->use_tcp);
207
208        //
209        // clear the internal packet so if we make another request, we don't have
210        // old data being sent.
211        //
212        $this->_packet->reset();
213
214        //
215        // for notifies, we just need to know it worked- we don't actualy need to
216        // return the response object
217        //
218        return true;
219    }
220}
221