1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10:
11: class Beatnik_Driver_ldap2dns extends Beatnik_Driver
12: {
13: 14: 15: 16:
17: var $_LDAP;
18:
19: 20: 21: 22: 23:
24: var $_connected = false;
25:
26: 27: 28: 29: 30:
31: function Beatnik_Driver_ldap2dns($params = array())
32: {
33: parent::Beatnik_Driver($params);
34: $this->_connect();
35: }
36:
37: 38: 39: 40: 41:
42: function getRecDriverTypes()
43: {
44: return array(
45: 'a+ptr' => 'A + PTR',
46: );
47: }
48:
49: 50: 51: 52: 53: 54: 55:
56: function getRecDriverFields($type) {
57: $recset = array();
58: switch($type) {
59: case 'a+ptr':
60: $recset['hostname'] = array(
61: 'name' => 'Hostname',
62: 'description' => 'Hostname',
63: 'type' => 'text',
64: 'maxlength' => 0,
65: 'required' => true,
66: 'infoset' => 'basic',
67: 'index' => 1,
68: );
69: $recset['cipaddr'] = array(
70: 'name' => 'IP Address',
71: 'description' => 'IP Address to be forward and reverse mapped',
72: 'type' => 'ipaddress',
73: 'maxlength' => 0,
74: 'required' => true,
75: 'infoset' => 'basic',
76: 'index' => 2,
77: );
78: break;
79: }
80:
81: $recset['timestamp'] = array(
82: 'name' => 'Timestamp',
83: 'description' => '"Do Not Issue Before/After" Timestamp',
84: 'type' => 'int',
85: 'maxlength' => 0,
86: 'required' => false,
87: 'infoset' => 'advanced',
88: 'index' => 100,
89: );
90: $recset['location'] = array(
91: 'name' => 'Location',
92: 'description' => 'Location Restriction',
93: 'type' => 'text',
94: 'maxlength' => 2,
95: 'required' => false,
96: 'infoset' => 'advanced',
97: 'index' => 101,
98: );
99:
100: return $recset;
101: }
102:
103: 104: 105: 106: 107: 108: 109:
110: function _getDomains()
111: {
112: static $zonedata = array();
113: if (count($zonedata) > 0) {
114:
115:
116: return $zonedata;
117: }
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132: $res = ldap_list($this->_LDAP,
133: $this->_params['basedn'],
134: "(objectClass=dnszone)");
135:
136: if ($res === false) {
137: throw new Beatnik_Exception("Unable to locate any DNS zones " .
138: "underneath ".$this->_params['basedn']);
139: }
140:
141: $res = ldap_get_entries($this->_LDAP, $res);
142:
143: if ($res === false) {
144: throw new Beatnik_Exception(sprintf(_("Unable to retrieve data from LDAP results: %s"), @ldap_error($this->_LDAP)));
145: }
146:
147: $fields = Beatnik::getRecFields('soa');
148: $i = 0;
149:
150:
151: while ($i < $res['count']) {
152: $tmp = array();
153:
154: foreach ($fields as $field => $fieldinfo) {
155: $key = strtolower($this->_getAttrByField($field));
156: if ($key === null) {
157:
158: continue;
159: }
160:
161: if ($key == 'dn') {
162: $val = @ldap_explode_dn($res[$i]['dn'], 1);
163: $tmp[$field] = $val[0];
164: continue;
165: }
166: @$tmp[$field] = $res[$i][$key][0];
167: }
168:
169:
170: $zonedata[] = $tmp;
171:
172:
173: $i++;
174: }
175: return $zonedata;
176: }
177:
178: 179: 180: 181: 182: 183: 184: 185: 186: 187:
188: function _getAttrByField($field)
189: {
190: $field = strtolower($field);
191: $fields = array(
192: 'hostname' => 'dnsdomainname',
193: 'zonename' => 'dnszonename',
194: 'serial' => 'dnsserial',
195: 'refresh' => 'dnsrefresh',
196: 'retry' => 'dnsretry',
197: 'expire' => 'dnsexpire',
198: 'minimum' => 'dnsminimum',
199: 'zonecontact' => 'dnsadminmailbox',
200: 'zonens' => 'dnszonemaster',
201: 'ttl' => 'dnsttl',
202: 'timestamp' => 'dnstimestamp',
203: 'location' => 'dnslocation',
204: 'ipaddr' => 'dnsipaddr',
205: 'ip6addr' => 'dnsipaddr',
206: 'cipaddr' => 'dnscipaddr',
207: 'pointer' => 'dnscname',
208: 'pref' => 'dnspreference',
209: 'priority' => 'dnssrvpriority',
210: 'weight' => 'dnssrvweight',
211: 'port' => 'dnssrvport',
212: 'text' => 'dnscname',
213: 'id' => 'dn',
214: );
215:
216: if (!isset($fields[$field])) {
217: return null;
218: }
219:
220: return $fields[$field];
221:
222: }
223:
224: 225: 226: 227: 228: 229: 230:
231: function getRecords($domain)
232: {
233: $domain = $this->cleanFilterString($domain);
234: $dn = $this->_params['dn'].'='.$domain.','.$this->_params['basedn'];
235: $res = @ldap_list($this->_LDAP, $dn, '(objectClass=dnsrrset)');
236:
237: if ($res === false) {
238: throw new Beatnik_Exception("Unable to locate any DNS data for $domain");
239: }
240:
241:
242: $zonedata = array();
243: $res = @ldap_get_entries($this->_LDAP, $res);
244: if ($res === false) {
245: throw new Beatnik_Exception(sprintf(_("Internal error: %s"), @ldap_error($this->_LDAP)));
246: }
247:
248: $i = 0;
249: while ($i < $res['count']) {
250: $rectype = $res[$i]['dnstype'][0];
251:
252: if ($rectype == 'a' && isset($res[$i]['dnscipaddr'])) {
253: $rectype = 'a+ptr';
254: }
255: if (!isset($zonedata[$rectype])) {
256:
257: $zonedata[$rectype] = array();
258: }
259: $tmp = array();
260: foreach (Beatnik::getRecFields($rectype) as $field => $fielddata) {
261: $key = $this->_getAttrByField($field);
262: if ($key === null) {
263:
264: continue;
265: }
266:
267: if ($key == 'dn') {
268: $val = @ldap_explode_dn($res[$i]['dn'], 1);
269: $tmp[$field] = $val[0];
270: continue;
271: }
272:
273:
274: $tmp[$field] = @$res[$i][$key][0];
275: }
276:
277: $zonedata[$rectype][] = $tmp;
278:
279:
280: $i++;
281: }
282:
283: return $zonedata;
284: }
285:
286: 287: 288: 289: 290: 291: 292: 293: 294:
295: function _deleteRecord(&$info)
296: {
297:
298: if (!isset($info['id'])) {
299: throw new Beatnik_Exception(_("Unable to delete record: No record ID specified."));
300: }
301:
302:
303: $dnattr = $this->_params['dn'];
304:
305: $suffix = $dnattr . '=' . $_SESSION['beatnik']['curdomain']['zonename'] . ',' . $this->_params['basedn'];
306: if ($info['rectype'] == 'soa') {
307:
308: throw new Beatnik_Exception(_("Unsupported recursive delete."));
309:
310: $domain = $this->cleanDNString($info['zonename']);
311: $dn = $suffix;
312: } else {
313: $domain = $this->cleanDNString($_SESSION['beatnik']['curdomain']['zonename']);
314:
315: $dn = $dnattr . '=' . $this->cleanDNString($info['id']) . ',' . $suffix;
316: }
317:
318: $res = @ldap_delete($this->_LDAP, $dn);
319: if ($res === false) {
320: throw new Beatnik_Exception(sprintf(_("Unable to delete record. Reason: %s"), @ldap_error($this->_LDAP)));
321: }
322: return true;
323: }
324:
325: 326: 327: 328: 329: 330: 331: 332: 333:
334: function _saveRecord($info)
335: {
336:
337: $rectype = strtolower($info['rectype']);
338: $rdata = false;
339: foreach (Beatnik::getRecTypes() as $rtype => $rdata) {
340: if ($rectype == $rtype) {
341: break;
342: }
343: $rdata = false;
344: }
345:
346: if (!$rdata) {
347: throw new Beatnik_Exception(_("Invalid record type specified."));
348: }
349:
350: $recfields = Beatnik::getRecFields($rectype);
351:
352: $entry = array();
353:
354: if ($rectype == 'a+ptr') {
355:
356: $entry['dnstype'] = 'a';
357: } else {
358: $entry['dnstype'] = $rectype;
359: }
360:
361: $id = strtoupper($rectype);
362:
363:
364: foreach ($recfields as $field => $fdata) {
365:
366: $key = $this->_getAttrByField($field);
367:
368: if ($key === null || $key == 'dn') {
369:
370: continue;
371: }
372:
373: if (!isset($info[$field]) && isset($fdata['default'])) {
374:
375: $val = $fdata['default'];
376: } else {
377:
378: if (isset($info[$field]) && strlen($info[$field])) {
379: $entry[$key] = $info[$field];
380: } else {
381:
382: $info[$field] = '';
383:
384:
385:
386:
387: if (isset($info['id'])) {
388: list($type, $record) = $this->getRecord($info['id']);
389: if ($record && isset($record[$field])) {
390: $entry[$key] = array();
391: }
392: }
393: }
394: }
395:
396: if (!isset($entry[$key]) && $fdata['required']) {
397:
398: throw new Beatnik_Exception(sprintf(_("Missing required field %s to save record."), $fdata['name']));
399: }
400:
401:
402:
403: $id .= '-'.$this->cleanDNString($info[$field]);
404: }
405:
406:
407: $key = $this->_params['dn'];
408: $dn = '';
409:
410: if ($rectype == 'soa') {
411: $domain = $this->cleanDNString($info['zonename']);
412: $entry[$key] = $domain;
413: $id = $domain;
414: $dn = $key.'='.$domain;
415: $suffix = $this->_params['basedn'];
416: } else {
417:
418: $id = $this->cleanDNString($id);
419: $entry[$key] = $id;
420: $dn = $key.'='.$id;
421:
422: $domain = $this->cleanDNString($_SESSION['beatnik']['curdomain']['zonename']);
423:
424: $suffix = $key.'='.$domain.','.$this->_params['basedn'];
425: }
426:
427:
428: if (isset($info['id'])) {
429:
430: $oldRDN = $key . '=' . $this->cleanDNString($info['id']);
431: if ($dn != $oldRDN) {
432:
433:
434: if ($rectype == 'soa') {
435: throw new Beatnik_Exception(_("Unsupported operation: cannot rename a domain."));
436: }
437: $res = @ldap_rename($this->_LDAP, $oldRDN . ',' . $suffix,
438: $dn, $suffix, true);
439: if ($res === false) {
440: throw new Beatnik_Exception(sprintf(_("Unable to rename old object. Reason: %s"), @ldap_error($this->_LDAP)));
441: }
442: }
443:
444:
445: $dn .= ',' . $suffix;
446:
447:
448: $res = @ldap_mod_replace($this->_LDAP, $dn, $entry);
449: if ($res === false) {
450: throw new Beatnik_Exception(sprintf(_("Unable to modify record. Reason: %s"), @ldap_error($this->_LDAP)));
451: }
452:
453: } else {
454:
455:
456: $dn .= ',' . $suffix;
457:
458: $entry['objectclass'] = array();
459: $entry['objectclass'][] = 'top';
460: $entry['objectclass'][] = 'dnszone';
461: if ($rectype != 'soa') {
462:
463: $entry['objectclass'][] = 'dnsrrset';
464: }
465: $res = @ldap_add($this->_LDAP, $dn, $entry);
466: if ($res === false) {
467: throw new Beatnik_Exception(sprintf(_("Unable to add record to LDAP. Reason: %s"), @ldap_error($this->_LDAP)));
468: }
469: }
470:
471: return $id;
472: }
473:
474: function cleanFilterString($string) {
475: return preg_replace(
476: array('/\*/', '/\(/', '/\)/', '/\x00/'),
477: array('\2a', '\28', '\29', '\00'),
478: $string
479: );
480: }
481:
482: function cleanDNString($string) {
483: return preg_replace(
484: array('/=/', '/,/', '/\+/'),
485: array('-', '~', ''),
486: $string);
487: }
488:
489: 490: 491: 492: 493: 494: 495: 496: 497: 498:
499: function _connect()
500: {
501: if (!$this->_connected) {
502: Horde::assertDriverConfig($this->_params, 'storage',
503: array('hostspec', 'basedn', 'binddn', 'password', 'dn'));
504:
505: $port = (isset($this->_params['port'])) ?
506: $this->_params['port'] : 389;
507:
508: $this->_LDAP = ldap_connect($this->_params['hostspec'], $port);
509: if (!$this->_LDAP) {
510: throw new Beatnik_Exception("Unable to connect to LDAP server $hostname on $port");
511: }
512: $res = ldap_set_option($this->_LDAP, LDAP_OPT_PROTOCOL_VERSION, $this->_params['version']);
513: if ($res === false) {
514: throw new Beatnik_Exception("Unable to set LDAP protocol version");
515: }
516: $res = ldap_bind($this->_LDAP, $this->_params['binddn'], $this->_params['password']);
517: if ($res === false) {
518: throw new Beatnik_Exception("Unable to bind to the LDAP server. Check authentication credentials.");
519: }
520:
521: $this->_connected = true;
522: }
523: return true;
524: }
525: }
526: