File: schema.oracle.sql

package info (click to toggle)
pdns 3.4.1-4
  • links: PTS, VCS
  • area: main
  • in suites: jessie-kfreebsd
  • size: 9,084 kB
  • sloc: cpp: 56,420; ansic: 37,838; sh: 12,762; sql: 1,081; makefile: 734; ruby: 560; yacc: 222; lex: 120; perl: 52; python: 15
file content (484 lines) | stat: -rw-r--r-- 14,085 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
-- THIS IS NOT PRODUCTION-QUALITY CODE
-- 
-- This database schema is meant to serve as documentation-by-example for how
-- certain things might be done. It has also been used for early testing of the
-- backend. It should not be deployed as-is.

CREATE SEQUENCE zones_id_seq;

CREATE TABLE Zones (
  id INTEGER CONSTRAINT pkey_zones PRIMARY KEY,
  name VARCHAR2(512) NOT NULL,
  type VARCHAR2(32) NOT NULL,
  last_check INTEGER,
  refresh NUMBER(10,0),
  serial NUMBER(10,0) DEFAULT 0 NOT NULL,
  notified_serial NUMBER(10,0),
  CONSTRAINT chk_zones_name CHECK (name = lower(name)),
  CONSTRAINT unq_zones_name UNIQUE (name),
  CONSTRAINT chk_zones_type CHECK (
    type IN ('NATIVE', 'MASTER', 'SLAVE')
    AND (type = 'SLAVE' OR last_check IS NULL)
  ),
  CONSTRAINT chk_zones_serial CHECK (serial BETWEEN 0 AND 4294967295),
  CONSTRAINT chk_zones_nserial CHECK (notified_serial BETWEEN 0 AND 4294967295),
  CONSTRAINT chk_zones_refresh CHECK (refresh BETWEEN 0 AND 4294967295),
  CONSTRAINT chk_zones_master CHECK (type = 'MASTER' OR notified_serial IS NULL)
);

CREATE INDEX zones_type_ind ON Zones (type);


CREATE TABLE Zonemasters (
  zone_id INTEGER NOT NULL CONSTRAINT fkey_zonemasters_zones REFERENCES Zones ON DELETE CASCADE,
  master VARCHAR2(512) NOT NULL,
  CONSTRAINT unq_zonemasters_zone_master UNIQUE (zone_id, master)
);

CREATE INDEX zonemasters_zone_id_ind ON Zonemasters (zone_id);


CREATE TABLE ZoneAlsoNotify (
  zone_id INTEGER NOT NULL CONSTRAINT fkey_zonealsonotify_zones REFERENCES Zones ON DELETE CASCADE,
  hostaddr VARCHAR2(512) NOT NULL,
  CONSTRAINT unq_zonealsonotify_zone_host UNIQUE (zone_id, hostaddr)
);

CREATE INDEX zonealsonotify_zone_id_ind ON ZoneAlsoNotify (zone_id);


CREATE SEQUENCE supermasters_id_seq;

CREATE TABLE Supermasters (
  id INTEGER CONSTRAINT pkey_supermasters PRIMARY KEY,
  name VARCHAR2(64) NOT NULL,
  ip VARCHAR2(64) NOT NULL,
  nameserver VARCHAR2(512) NOT NULL
);

CREATE INDEX supermasters_ip_ind ON Supermasters (ip);


CREATE TABLE ZoneMetadata (
  zone_id INTEGER NOT NULL CONSTRAINT fkey_zonemetadata_zones REFERENCES Zones,
  meta_type VARCHAR2(64) NOT NULL,
  meta_ind INTEGER NOT NULL,
  meta_content VARCHAR2(4000),
  CONSTRAINT pkey_zonemetadata PRIMARY KEY (zone_id, meta_type, meta_ind)
);


CREATE SEQUENCE zonednskeys_id_seq;

CREATE TABLE ZoneDNSKeys (
  id INTEGER CONSTRAINT pkey_zonednskeys PRIMARY KEY,
  zone_id INTEGER NOT NULL CONSTRAINT fkey_zonednskeys_zones REFERENCES Zones,
  flags NUMBER(5,0) NOT NULL,
  active NUMBER(1,0) NOT NULL,
  keydata VARCHAR2(4000) NOT NULL,
  CONSTRAINT chk_zonednskeys_flags CHECK (flags BETWEEN 0 AND 65535),
  CONSTRAINT chk_zonednskeys_active CHECK (active IN (0, 1))
);

CREATE INDEX zonednskeys_zone_ind ON ZoneDNSKeys (zone_id);


CREATE TABLE TSIGKeys (
  name VARCHAR2(256),
  algorithm VARCHAR2(64) NOT NULL,
  secret VARCHAR2(2048) NOT NULL,
  CONSTRAINT chk_tsigkeys_name CHECK (name = lower(name)),
  CONSTRAINT chk_tsigkeys_algorithm CHECK (algorithm = lower(algorithm)),
  CONSTRAINT unq_tsigkeys_nav UNIQUE (name, algorithm, secret)
);


CREATE TABLE AccessControlList (
  acl_type VARCHAR2(64) NOT NULL,
  acl_key VARCHAR2(256) NOT NULL,
  acl_val VARCHAR2(2048),
  CONSTRAINT chk_acl_type CHECK (acl_type = 'allow-axfr'),
  CONSTRAINT unq_acl_tkv UNIQUE (acl_type, acl_key, acl_val)
);

CREATE INDEX acl_tk ON AccessControlList (acl_type, acl_key);


CREATE SEQUENCE records_id_seq;

CREATE TABLE Records (
  id INTEGER CONSTRAINT pkey_records PRIMARY KEY,
  zone_id INTEGER NOT NULL CONSTRAINT fkey_records_zones REFERENCES Zones,
  fqdn VARCHAR2(512) NOT NULL,
  revfqdn VARCHAR2(512) NOT NULL,
  fqdnhash VARCHAR2(512),
  ttl NUMBER(10,0) NOT NULL,
  type VARCHAR2(32),
  content VARCHAR2(2048),
  last_change INTEGER DEFAULT 0 NOT NULL,
  auth NUMBER(1,0) DEFAULT 1 NOT NULL,
  CONSTRAINT chk_records_fqdn CHECK (fqdn = lower(fqdn)),
  CONSTRAINT chk_records_ttl CHECK (ttl BETWEEN 0 AND 4294967295),
  CONSTRAINT chk_records_type CHECK (type = upper(type)),
  CONSTRAINT unq_records_zntc UNIQUE (zone_id, fqdn, type, content),
  CONSTRAINT chk_records_tc CHECK (
    type IS NULL AND content IS NULL OR
    type IS NOT NULL AND content IS NOT NULL
  ),
  CONSTRAINT chk_records_auth CHECK (auth IN (0, 1))
);

CREATE INDEX records_zone_id_ind ON Records (zone_id);
CREATE INDEX records_revfqdn_ind ON Records (zone_id, revfqdn);
CREATE INDEX records_fqdnhash_ind ON Records (zone_id, fqdnhash);
CREATE INDEX records_last_change_ind ON Records (last_change);

-- Only one SOA and NSEC3PARAM record per zone
CREATE UNIQUE INDEX records_zonesoa_unq_ind ON Records (
  CASE
    WHEN type IN ('SOA', 'NSEC3PARAM') THEN zone_id
    ELSE NULL
  END,
  CASE
    WHEN type IN ('SOA', 'NSEC3PARAM') THEN type
    ELSE NULL
  END
);


CREATE FUNCTION label_reverse (dnsname IN VARCHAR2) RETURN VARCHAR2 AS
  pattern   VARCHAR2(32) := '[^.]+';
  match     BINARY_INTEGER := 1;
  label     VARCHAR2(63);
  out_dnsname  VARCHAR2(512);
BEGIN
  label := REGEXP_SUBSTR(dnsname, pattern, 1, match);
  match := match + 1;
  out_dnsname := label;
  LOOP
    label := REGEXP_SUBSTR(dnsname, pattern, 1, match);
    EXIT WHEN label IS NULL;
    out_dnsname := label || ' ' || out_dnsname;
    match := match + 1;
  END LOOP;
  RETURN(out_dnsname);
END;
/

SHOW ERRORS

CREATE FUNCTION dnsname_to_raw (in_dnsname IN VARCHAR2) RETURN RAW AS
  dnsname VARCHAR2(512) := LOWER(in_dnsname);
  rawname RAW(512);

  lpos BINARY_INTEGER := 1;
  rpos BINARY_INTEGER;
  label VARCHAR2(63);

  TYPE convarray IS VARRAY(64) OF RAW(1);
  byteval convarray := convarray(
    '00', '01', '02', '03', '04', '05', '06', '07',
    '08', '09', '0A', '0B', '0C', '0D', '0E', '0F',
    '10', '11', '12', '13', '14', '15', '16', '17',
    '18', '19', '1A', '1B', '1C', '1D', '1E', '1F',
    '20', '21', '22', '23', '24', '25', '26', '27',
    '28', '29', '2A', '2B', '2C', '2D', '2E', '2F',
    '30', '31', '32', '33', '34', '35', '36', '37',
    '38', '39', '3A', '3B', '3C', '3D', '3E', '3F'
  );
BEGIN
  IF dnsname IS NULL THEN
    RETURN('00');
  END IF;

  WHILE lpos <= LENGTH(dnsname) LOOP
    rpos := INSTR(dnsname, '.', lpos);
    IF rpos = 0 THEN
      rpos := LENGTH(dnsname) + 1;
    END IF;
    label := SUBSTR(dnsname, lpos, rpos - lpos);
    rawname := UTL_RAW.CONCAT(
      rawname,
      byteval(LENGTH(label) + 1),
      UTL_I18N.STRING_TO_RAW(label, 'US7ASCII')
    );
    lpos := rpos + 1;
  END LOOP;

  IF rpos = LENGTH(dnsname) THEN
    rawname := UTL_RAW.CONCAT(rawname, '00');
  END IF;

  RETURN(rawname);
END;
/

SHOW ERRORS

-- This is clearly terrible, though it appears to work.
-- For real deployment, you could upload the dnsjava
-- library into your database and use its facilities.
CREATE FUNCTION base32hex_encode (
  in_string RAW
) RETURN VARCHAR2 AS
  off BINARY_INTEGER := 1;
  out_string VARCHAR2(6554);
  sub RAW(5);
  num INTEGER;
  TYPE convarray IS VARRAY(32) OF VARCHAR2(1);
  digit convarray := convarray(
    '0', '1', '2', '3', '4', '5', '6', '7',
    '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
    'o', 'p', 'q', 'r', 's', 't', 'u', 'v'
  );
BEGIN

  WHILE off + 4 <= UTL_RAW.LENGTH(in_string) LOOP
    sub := UTL_RAW.SUBSTR(in_string, off, 2);
    num := TO_NUMBER(sub, 'XXXX');
    out_string := out_string
      || digit(FLOOR(num / 2048) + 1)
      || digit(FLOOR(MOD(num / 64, 32)) + 1)
      || digit(FLOOR(MOD(num / 2, 32)) + 1);
    sub := UTL_RAW.SUBSTR(in_string, off + 1, 3);
    num := TO_NUMBER(sub, 'XXXXXX');
    out_string := out_string
      || digit(FLOOR(MOD(num / 4096, 32)) + 1)
      || digit(FLOOR(MOD(num / 128, 32)) + 1)
      || digit(FLOOR(MOD(num / 4, 32)) + 1);
    sub := UTL_RAW.SUBSTR(in_string, off + 3, 2);
    num := TO_NUMBER(sub, 'XXXX');
    out_string := out_string
      || digit(FLOOR(MOD(num / 32, 32)) + 1)
      || digit(FLOOR(MOD(num, 32)) + 1);
    off := off + 5;
  END LOOP;

  IF off <= UTL_RAW.LENGTH(in_string) THEN
    sub := UTL_RAW.SUBSTR(in_string, off);
    CASE UTL_RAW.LENGTH(sub)
      WHEN 1 THEN
        num := TO_NUMBER(sub, 'XX');
        out_string := out_string
          || digit(FLOOR(num / 8) + 1)
          || digit(FLOOR(MOD(num, 8)) * 4 + 1);
      WHEN 2 THEN
        num := TO_NUMBER(sub, 'XXXX');
        out_string := out_string
          || digit(FLOOR(num / 2048) + 1)
          || digit(FLOOR(MOD(num / 64, 32)) + 1)
          || digit(FLOOR(MOD(num / 2, 32)) + 1)
          || digit(FLOOR(MOD(num, 2)) * 16 + 1);
      WHEN 3 THEN
        num := TO_NUMBER(sub, 'XXXXXX');
        out_string := out_string
          || digit(FLOOR(num / 524288) + 1)
          || digit(FLOOR(MOD(num / 16384, 32)) + 1)
          || digit(FLOOR(MOD(num / 512, 32)) + 1)
          || digit(FLOOR(MOD(num / 16, 32)) + 1)
          || digit(FLOOR(MOD(num, 16)) * 2 + 1);
      WHEN 4 THEN
        num := TO_NUMBER(sub, 'XXXXXXXX');
        out_string := out_string
          || digit(FLOOR(num / 134217728) + 1)
          || digit(FLOOR(MOD(num / 4194304, 32)) + 1)
          || digit(FLOOR(MOD(num / 131072, 32)) + 1)
          || digit(FLOOR(MOD(num / 4096, 32)) + 1)
          || digit(FLOOR(MOD(num / 128, 32)) + 1)
          || digit(FLOOR(MOD(num / 4, 32)) + 1)
          || digit(FLOOR(MOD(num, 4)) * 8 + 1);
    END CASE;
  END IF;

  RETURN(out_string);
END;
/

SHOW ERRORS

CREATE FUNCTION dnsname_to_hashname (
  in_dnsname IN VARCHAR2,
  salt RAW,
  itercnt BINARY_INTEGER
) RETURN VARCHAR2 AS
  rawname RAW(512) := dnsname_to_raw(RTRIM(in_dnsname, '.') || '.');
  rawsalt RAW(32) := salt;
  hashname RAW(64);
  iter BINARY_INTEGER := 0;
BEGIN
  hashname := UTL_RAW.CONCAT(rawname, rawsalt);
  hashname := DBMS_CRYPTO.HASH(hashname, DBMS_CRYPTO.HASH_SH1);
  WHILE iter < itercnt LOOP
    hashname := UTL_RAW.CONCAT(hashname, rawsalt);
    hashname := DBMS_CRYPTO.HASH(hashname, DBMS_CRYPTO.HASH_SH1);
    iter := iter + 1;
  END LOOP;
  RETURN(base32hex_encode(hashname));
END;
/

SHOW ERRORS

CREATE PROCEDURE get_canonical_prev_next (
  in_zone_id INTEGER,
  in_fqdn VARCHAR2,
  out_prev OUT VARCHAR2,
  out_next OUT VARCHAR2
) AS
BEGIN
  SELECT * INTO out_prev
    FROM (
      SELECT fqdn
        FROM Records
        WHERE zone_id = in_zone_id
          AND revfqdn <= label_reverse(LOWER(in_fqdn))
          AND auth = 1
        ORDER BY revfqdn DESC
    ) WHERE ROWNUM = 1;

  BEGIN
    SELECT * INTO out_next
      FROM (
        SELECT fqdn
          FROM Records
          WHERE zone_id = in_zone_id
            AND revfqdn > label_reverse(LOWER(in_fqdn))
            AND auth = 1
          ORDER BY revfqdn ASC
      ) WHERE ROWNUM = 1;
  EXCEPTION
    WHEN NO_DATA_FOUND THEN
      SELECT name INTO out_next
        FROM Zones
        WHERE id = in_zone_id;
  END;
END;
/

SHOW ERRORS

CREATE PROCEDURE get_hashed_prev_next (
  in_zone_id INTEGER,
  in_fqdnhash VARCHAR2,
  out_fqdn OUT VARCHAR2,
  out_prev OUT VARCHAR2,
  out_next OUT VARCHAR2
) AS
BEGIN
  BEGIN
    SELECT * INTO out_prev, out_fqdn
      FROM (
        SELECT fqdnhash, fqdn
          FROM Records
          WHERE zone_id = in_zone_id
            AND fqdnhash <= in_fqdnhash
            AND auth = 1
          ORDER BY fqdnhash DESC
      ) WHERE ROWNUM = 1;
  EXCEPTION
    WHEN NO_DATA_FOUND THEN
      SELECT * INTO out_prev, out_fqdn
        FROM (
          SELECT fqdnhash, fqdn
            FROM Records
            WHERE zone_id = in_zone_id
              AND auth = 1
            ORDER BY fqdnhash DESC
        ) WHERE ROWNUM = 1;
  END;

  BEGIN
    SELECT * INTO out_next
      FROM (
        SELECT fqdnhash
          FROM Records
          WHERE zone_id = in_zone_id
            AND fqdnhash > in_fqdnhash
            AND auth = 1
          ORDER BY fqdnhash ASC
      ) WHERE ROWNUM = 1;
  EXCEPTION
    WHEN NO_DATA_FOUND THEN
      SELECT * INTO out_next
        FROM (
          SELECT fqdnhash 
            FROM Records
            WHERE zone_id = in_zone_id
              AND auth = 1
            ORDER BY fqdnhash ASC
        ) WHERE ROWNUM = 1;
  END;
END;
/

SHOW ERRORS

CREATE TRIGGER records_fill_columns
  BEFORE INSERT OR UPDATE ON Records
  FOR EACH ROW
BEGIN
  -- 'www.site.example' => 'example site www' for NSEC ordering
  :NEW.revfqdn := label_reverse(LOWER(:NEW.fqdn));

  -- Hash the FQDN for NSEC3 ordering
  IF :NEW.type != 'RRSIG' THEN
    DECLARE
      nsec3param_string VARCHAR2(512);
      nsec3param_pattern VARCHAR2(512) := '^(\d+) +(\d+) +(\d+) +([0-9A-Fa-f]+)';
      hashalgo BINARY_INTEGER;
      itcount BINARY_INTEGER;
      salt RAW(256);
    BEGIN
      SELECT meta_content INTO nsec3param_string
      FROM ZoneMetadata
      WHERE zone_id = :NEW.zone_id
        AND meta_type = 'NSEC3PARAM';
      hashalgo := REGEXP_SUBSTR(nsec3param_string, nsec3param_pattern, 1, 1, '', 1);
      IF hashalgo != 1 THEN
        RAISE_APPLICATION_ERROR(-20000, 'NSEC3 hash is not SHA-1');
      END IF;
      itcount := REGEXP_SUBSTR(nsec3param_string, nsec3param_pattern, 1, 1, '', 3);
      salt := REGEXP_SUBSTR(nsec3param_string, nsec3param_pattern, 1, 1, '', 4);
      :NEW.fqdnhash := dnsname_to_hashname(:NEW.fqdn, salt, itcount);
    EXCEPTION
      WHEN NO_DATA_FOUND THEN
        NULL;
    END;
  END IF;
END;
/

SHOW ERRORS

CREATE TRIGGER parse_zone_defining_records
  AFTER INSERT OR UPDATE ON Records
  FOR EACH ROW
  WHEN (NEW.type IN ('SOA'))
BEGIN
  CASE :NEW.type
    WHEN 'SOA' THEN
      DECLARE
        pattern      VARCHAR2(32) := '^[^ ]+ +[^ ]+ +(\d+) +(\d+)';
        serial_str   VARCHAR2(32) := REGEXP_SUBSTR(:NEW.content, pattern, 1, 1, '', 1);
        serial_num   NUMBER(10,0) := TO_NUMBER(serial_str);
        refresh_str  VARCHAR2(32) := REGEXP_SUBSTR(:NEW.content, pattern, 1, 1, '', 2);
      BEGIN
        IF serial_num = 0 THEN
          SELECT NVL(max(last_change), 0) INTO serial_num
          FROM Records
          WHERE zone_id = :NEW.zone_id;
        END IF;

        UPDATE Zones
        SET serial = serial_num, refresh = TO_NUMBER(refresh_str)
        WHERE id = :NEW.zone_id;
      END;
  END CASE;
END;
/

SHOW ERRORS

-- End of schema
-- vi: set sw=2 et : --