Overview

Packages

  • Beatnik
  • None

Classes

  • Autogenerate
  • Beatnik
  • Beatnik_Driver
  • Beatnik_Driver_ldap2dns
  • Beatnik_Driver_pdnsgsql
  • Beatnik_Driver_sql
  • DeleteRecord
  • EditRecord
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * The Beatnik_Driver_sql class implements a SQL driver for managing DNS records
  4:  * in the PowerDNS generic SQL driver.  The PowerDNS generic SQL driver aims to
  5:  * support MySQL, PostgreSQL, SQLite and Oracle.  This driver attempts to do the
  6:  * same as long as the default queries are used.
  7:  *
  8:  * Copyright 2008-2012 Horde LLC (http://www.horde.org/)
  9:  *
 10:  * See the enclosed file COPYING for license information (GPL). If you
 11:  * did not receive this file, see http://www.horde.org/licenses/gpl.
 12:  *
 13:  * @author Ben Klang <ben@alkaloid.net>
 14:  * @package Beatnik
 15:  */
 16: 
 17: class Beatnik_Driver_pdnsgsql extends Beatnik_Driver
 18: {
 19:     /**
 20:      * Hash containing connection parameters.
 21:      *
 22:      * @var array
 23:      */
 24:     var $_params = array();
 25: 
 26:     /**
 27:      * Handle for the current database connection.
 28:      *
 29:      * @var DB
 30:      */
 31:     var $_db;
 32: 
 33:     /**
 34:      * Handle for the current database connection, used for writing. Defaults
 35:      * to the same handle as $_db if a separate write database is not required.
 36:      *
 37:      * @var DB
 38:      */
 39:     var $_write_db;
 40: 
 41:     /**
 42:      * Boolean indicating whether or not we're connected to the SQL server.
 43:      *
 44:      * @var boolean
 45:      */
 46:     var $_connected = false;
 47: 
 48:     /**
 49:     * Constructs a new Beatnik DB driver object.
 50:     *
 51:     * @param array  $params    A hash containing connection parameters.
 52:     */
 53:     function __construct($params = array())
 54:     {
 55:         $params = array_merge(array(
 56:             'domains_table' => 'domains',
 57:             'records_table' => 'records'
 58:         ), $params);
 59: 
 60:         parent::__construct($params);
 61:     }
 62: 
 63:     /**
 64:      * Gets all zones
 65:      *
 66:      * @access private
 67:      *
 68:      * @return array Array with zone records numerically indexed
 69:      */
 70:     function _getDomains()
 71:     {
 72:         $this->_connect();
 73: 
 74:         $query = 'SELECT d.id, d.name AS name, r.content AS content, ' .
 75:                  'r.ttl AS ttl FROM ' . $this->_params['domains_table'] .
 76:                  ' AS d JOIN ' . $this->_params['records_table'] . ' AS r ON ' .
 77:                  'r.domain_id = d.id WHERE r.type = \'SOA\'';
 78:         Horde::logMessage('SQL Query by Beatnik_Driver_pdnsgsql::_getDomains(): ' .  $query, 'DEBUG');
 79: 
 80:         $domainlist =  $this->_db->getAll($query, null, DB_FETCHMODE_ASSOC);
 81:         if (is_a($domainlist, 'PEAR_Error')) {
 82:             Horde::logMessage($domainlist, 'ERR');
 83:             throw new Beatnik_Exception(_("Error getting domain list.  Details have been logged for the administrator."));
 84:         }
 85: 
 86:         $results = array();
 87:         foreach ($domainlist as $info) {
 88:             $soa = explode(' ', $info['content']);
 89:             if (count($soa) != 7) {
 90:                 Horde::logMessage(sprintf('Invalid SOA found for %s, skipping.', $info['name']), 'WARN');
 91:                 continue;
 92:             }
 93: 
 94:             $d = array();
 95:             $d['id'] = $info['id'];
 96:             $d['zonename'] = $info['name'];
 97:             $d['zonemaster'] = $d['zonens'] = $soa[0];
 98:             $d['admin'] = $d['zonecontact'] = $soa[1];
 99:             $d['serial'] = $soa[2];
100:             $d['refresh'] = $soa[3];
101:             $d['retry'] = $soa[4];
102:             $d['expire'] = $soa[5];
103:             $d['minimum'] = $soa[6];
104:             $results[] = $d;
105:         }
106: 
107:         return $results;
108:     }
109: 
110:     /**
111:      * Return SOA for a single domain
112:      *
113:      * @param string $domain   Domain for which to return SOA information
114:      *
115:      * @return array           Domain SOA
116:      */
117:     function getDomain($domainname)
118:     {
119:         $this->_connect();
120: 
121:         $query = 'SELECT d.id AS id, d.name AS name, r.content AS content, ' .
122:                  'r.ttl AS ttl FROM ' . $this->_params['domains_table'] .
123:                  ' AS d JOIN ' . $this->_params['records_table'] . ' AS r ON ' .
124:                  'r.domain_id = d.id WHERE r.type = \'SOA\' AND d.name = ?';
125:         $values = array($domainname);
126:         Horde::logMessage('SQL Query by Beatnik_Driver_pdnsgsql::getDomain(): ' .  $query, 'DEBUG');
127: 
128:         $result =  $this->_db->getAll($query, $values, DB_FETCHMODE_ASSOC);
129:         if (is_a($result, 'PEAR_Error')) {
130:             Horde::logMessage($result, 'ERR');
131:             throw new Beatnik_Exception(_("An error occurred while searching the database.  Details have been logged for the administrator."), __FILE__, __LINE__, PEAR_LOG_ERR);
132:         }
133: 
134:         if (count($result) != 1) {
135:             throw new Beatnik_Exception(_("Too many domains matched that name.  Contact your administrator."));
136:         }
137: 
138:         $info = $result[0];
139: 
140:         $soa = explode(' ', $info['content']);
141:         if (count($soa) != 7) {
142:             Horde::logMessage(sprintf('Invalid SOA found for %s, skipping.', $info['name']), 'WARN');
143:             throw new Beatnik_Exception(_("Corrupt SOA found for zone.  Contact your administrator."), __FILE__, __LINE__, PEAR_LOG_ERR);
144:         }
145: 
146:         $ret = array();
147:         $ret['id'] = $info['id'];
148:         $ret['zonename'] = $info['name'];
149:         $ret['zonemaster'] = $soa[0];
150:         $ret['admin'] = $soa[1];
151:         $ret['serial'] = $soa[2];
152:         $ret['refresh'] = $soa[3];
153:         $ret['retry'] = $soa[4];
154:         $ret['expire'] = $soa[5];
155:         $ret['minimum'] = $soa[6];
156: 
157:         return $ret;
158:     }
159: 
160:     /**
161:      * Gets all records associated with the given zone
162:      *
163:      * @param string $domain Retrieve records for this domain
164:      *
165:      * @return array Array with zone records
166:      */
167:     function getRecords($domain)
168:     {
169:         $this->_connect();
170: 
171:         $zonedata = array();
172: 
173:         $query = 'SELECT d.id AS domain_id, r.id AS id, d.name AS domain, ' .
174:                  'r.name AS name, r.type AS type, r.content AS content, ' .
175:                  'r.ttl AS ttl, r.prio AS prio FROM ' .
176:                   $this->_params['domains_table'] . ' AS d JOIN ' .
177:                   $this->_params['records_table'] . ' AS r ON ' .
178:                   'd.id = r.domain_id AND d.name = ?';
179:         $values = array($domain);
180: 
181:         Horde::logMessage('SQL Query by Beatnik_Driver_pdnsgsql::getRecords(): ' . $query, 'DEBUG');
182:         $result = $this->_db->getAll($query, $values, DB_FETCHMODE_ASSOC);
183:         if (is_a($result, 'PEAR_Error')) {
184:             Horde::logMessage($result, 'ERR');
185:             throw new Beatnik_Exception(_("An error occurred while searching the database.  Details have been logged for the administrator."), __FILE__, __LINE__, PEAR_LOG_ERR);
186:         }
187: 
188:         foreach ($result as $rec) {
189:             $type = strtolower($rec['type']);
190:             if (!isset($zonedata[$type])) {
191:                 $zonedata[$type] = array();
192:             }
193: 
194:             $tmp = array();
195:             $tmp['id'] = $rec['id'];
196:             $tmp['ttl'] = $rec['ttl'];
197:             switch($type) {
198:             case 'soa':
199:                 $soa = explode(' ', $rec['content']);
200:                 if (count($soa) != 7) {
201:                     Horde::logMessage(sprintf('Invalid SOA found for %s, skipping.', $info['name']), 'WARN');
202:                 }
203: 
204:                 $tmp['zonename'] = $rec['name'];
205:                 $tmp['zonens'] = $soa[0];
206:                 $tmp['zonecontact'] = $soa[1];
207:                 $tmp['serial'] = $soa[2];
208:                 $tmp['refresh'] = $soa[3];
209:                 $tmp['retry'] = $soa[4];
210:                 $tmp['expire'] = $soa[5];
211:                 $tmp['minimum'] = $soa[6];
212:                 break;
213: 
214:             case 'a':
215:                 $tmp['hostname'] = $rec['name'];
216:                 $tmp['ipaddr'] = $rec['content'];
217:                 break;
218: 
219:             case 'ptr':
220:                 $tmp['hostname'] = $rec['name'];
221:                 $tmp['pointer'] = $rec['content'];
222:                 break;
223: 
224:             case 'mx':
225:                 $tmp['pointer'] = $rec['content'];
226:                 $tmp['pref'] = $rec['prio'];
227:                 break;
228: 
229:             case 'cname':
230:                 $tmp['hostname'] = $rec['name'];
231:                 $tmp['pointer'] = $rec['content'];
232:                 break;
233: 
234:             case 'ns':
235:                 $tmp['hostname'] = $rec['name'];
236:                 $tmp['pointer'] = $rec['content'];
237:                 break;
238: 
239:             case 'srv':
240:                 $srv = preg_split('/\s+/', trim($rec['content']));
241:                 if (count($srv) != 3) {
242:                     Horde::logMessage(sprintf('Invalid SRV data found for %s, skipping.', $rec['name']), 'WARN');
243:                     continue;
244:                 }
245:                 $tmp['hostname'] = $rec['name'];
246:                 $tmp['weight'] = $srv[0];
247:                 $tmp['port'] = $srv[1];
248:                 $tmp['pointer'] = $srv[2];
249:                 $tmp['priority'] = $rec['prio'];
250:                 break;
251: 
252:             case 'txt':
253:                 $tmp['hostname'] = $rec['name'];
254:                 $tmp['text'] = $rec['content'];
255:                 break;
256:             }
257: 
258:             $zonedata[$type][] = $tmp;
259:         }
260: 
261:         return $zonedata;
262:     }
263: 
264:     /**
265:      * Saves a new or edited record to the DNS backend
266:      *
267:      * @access private
268:      *
269:      * @param array $info Array of record data
270:      *
271:      * @return boolean true on success
272:      */
273:     function _saveRecord($info)
274:     {
275:         $this->_connect();
276: 
277:         $change_date = time();
278:         $domain_id = $_SESSION['beatnik']['curdomain']['id'];
279: 
280:         switch($info['rectype']) {
281:         case 'soa':
282:             if (empty($info['refresh'])) {
283:                 // 24 hours
284:                 $info['refresh'] = 86400;
285:             }
286:             if (empty($info['retry'])) {
287:                // 2 hours
288:                $info['retry'] = 7200;
289:             }
290:             if (empty($info['expire'])) {
291:                // 1000 hours
292:                $info['expire'] = 3600000;
293:             }
294:             if (empty($info['minimum'])) {
295:                // 2 days
296:                $info['miniumum'] = 172800;
297:             }
298: 
299:             $name = $info['zonename'];
300:             $type = 'SOA';
301:             $content = $info['zonens'] . ' ' . $info['zonecontact'] . ' ' .
302:                        $info['serial'] . ' ' . $info['refresh'] . ' ' .
303:                        $info['retry'] . ' ' . $info['expire'] . ' ' .
304:                        $info['minimum'];
305:             $ttl = $info['ttl'];
306:             $prio = null;
307:             break;
308: 
309:         case 'a':
310:             $name = $info['hostname'];
311:             $type = 'A';
312:             $content = $info['ipaddr'];
313:             $ttl = $info['ttl'];
314:             $prio = null;
315:             break;
316: 
317:         case 'ptr':
318:             $name = $info['hostname'];
319:             $type = 'PTR';
320:             $content = $info['pointer'];
321:             $ttl = $info['ttl'];
322:             $prio = null;
323:             break;
324: 
325:         case 'mx':
326:             $name = $_SESSION['beatnik']['curdomain']['zonename'];
327:             $type = 'MX';
328:             $content = $info['pointer'];
329:             $ttl = $info['ttl'];
330:             $prio = $info['pref'];
331:             break;
332: 
333:         case 'cname':
334:             $name = $info['hostname'];
335:             $type = 'CNAME';
336:             $content = $info['pointer'];
337:             $ttl = $info['ttl'];
338:             $prio = null;
339:             break;
340: 
341:         case 'ns':
342:             $name = $info['hostname'];
343:             $type = 'NS';
344:             $content = $info['pointer'];
345:             $ttl = $info['ttl'];
346:             $prio = null;
347:             break;
348: 
349:         case 'srv':
350:             $name = $info['hostname'];
351:             $type = 'SRV';
352:             $content = $info['weight'] . ' ' . $info['port'] . ' ' .
353:                        $info['pointer'];
354:             $ttl = $info['ttl'];
355:             $prio = $info['priority'];
356:             break;
357: 
358:         case 'txt':
359:             $name = $info['hostname'];
360:             $type = 'TXT';
361:             $content = $info['text'];
362:             $ttl = $info['ttl'];
363:             $prio = null;
364:             break;
365:         }
366: 
367:         if (!empty($info['id'])) {
368:             $query = 'UPDATE ' . $this->_params['records_table'] . ' SET ' .
369:                      'name = ?, type = ?, content = ?, ttl = ?, ' .
370:                      'prio = ' . (empty($prio) ? 'NULL' : $prio) . ', ' .
371:                      'change_date = ? WHERE id = ?';
372:             $values = array($name, $type, $content, $ttl);
373:             if (!empty($prio)) {
374:                 $values[] = $prio;
375:             }
376:             $values[] = $change_date;
377:             $values[] = $info['id'];
378:         } else {
379:             $query = 'INSERT INTO ' . $this->_params['records_table'] . ' ' .
380:                      '(domain_id, name, type, content, ttl, prio, ' .
381:                      'change_date) VALUES (?, ?, ?, ?, ?, ' .
382:                      (empty($prio) ? 'NULL' : '?') . ', ?)';
383:             $values = array($domain_id, $name, $type, $content, $ttl);
384:             if (!empty($prio)) {
385:                 $values[] = $prio;
386:             }
387:             $values[] = $change_date;
388:         }
389: 
390:         Horde::logMessage('SQL Query by Beatnik_Driver_pdnsgsql::_saveRecord(): ' . $query, 'DEBUG');
391:         $result = $this->_write_db->query($query, $values);
392:         if (is_a($result, 'PEAR_Error')) {
393:             return $result;
394:         }
395: 
396:         return true;
397:     }
398: 
399:     /**
400:      * Delete record from backend
401:      *
402:      * @access private
403:      *
404:      * @param array $data  Reference to array of record data to be deleted
405:      *
406:      * @return boolean true on success, PEAR::Error on error
407:      */
408:     function _deleteRecord($data)
409:     {
410:         $this->_connect();
411: 
412:         throw new Beatnik_Exception(_("Not implemented."));
413:     }
414: 
415:     /**
416:      * Attempts to open a persistent connection to the SQL server.
417:      *
418:      * @access private
419:      *
420:      * @return boolean  True on success.
421:      */
422:     function _connect()
423:     {
424:         if ($this->_connected) {
425:             return true;
426:         }
427: 
428:         $this->_db = $GLOBALS['injector']->getInstance('Horde_Core_Factory_DbPear')->create('read', 'beatnik', 'storage');
429:         $this->_write_db = $GLOBALS['injector']->getInstance('Horde_Core_Factory_DbPear')->create('rw', 'beatnik', 'storage');
430: 
431:         return true;
432:     }
433: 
434:     /**
435:      * Disconnects from the SQL server and cleans up the connection.
436:      *
437:      * @access private
438:      *
439:      * @return boolean  True on success, false on failure.
440:      */
441:     function _disconnect()
442:     {
443:         if ($this->_connected) {
444:             $this->_connected = false;
445:             $this->_db->disconnect();
446:             $this->_write_db->disconnect();
447:         }
448: 
449:         return true;
450:     }
451: 
452: }
453: 
API documentation generated by ApiGen