diff options
| author | Peder O. Klingenberg | 2017-05-23 20:34:08 -0400 |
|---|---|---|
| committer | Glenn Morris | 2017-05-23 20:34:08 -0400 |
| commit | 8f6550b38c8b467a8f26c63050bd842f4fdc0b1e (patch) | |
| tree | 5c33e5238776ff33a2f7bb004eca88afffadcd3b | |
| parent | ef9f5c672a8e248dd7bd682101c03feb2e527340 (diff) | |
| download | emacs-8f6550b38c8b467a8f26c63050bd842f4fdc0b1e.tar.gz emacs-8f6550b38c8b467a8f26c63050bd842f4fdc0b1e.zip | |
New dns-mode command for IPv6 address conversion
This converts IPv6 addresses to a format suitable for
reverse lookup zone files. (Bug#26820)
* lisp/textmodes/dns-mode.el (dns-mode-map, dns-mode-menu):
Add dns-mode-ipv6-to-nibbles.
(dns-mode-ipv6-to-nibbles, dns-mode-reverse-and-expand-ipv6):
New functions.
* test/lisp/dns-mode-tests.el: New file.
; * etc/NEWS: Mention this.
| -rw-r--r-- | etc/NEWS | 4 | ||||
| -rw-r--r-- | lisp/textmodes/dns-mode.el | 99 | ||||
| -rw-r--r-- | test/lisp/dns-mode-tests.el | 58 |
3 files changed, 160 insertions, 1 deletions
| @@ -850,6 +850,10 @@ This is done with the help of 'c-or-c++-mode' function which analyses | |||
| 850 | contents of the buffer to determine whether it's a C or C++ source | 850 | contents of the buffer to determine whether it's a C or C++ source |
| 851 | file. | 851 | file. |
| 852 | 852 | ||
| 853 | --- | ||
| 854 | ** New DNS mode command 'dns-mode-ipv6-to-nibbles' to convert IPv6 addresses | ||
| 855 | to a format suitable for reverse lookup zone files. | ||
| 856 | |||
| 853 | ** Flymake | 857 | ** Flymake |
| 854 | 858 | ||
| 855 | +++ | 859 | +++ |
diff --git a/lisp/textmodes/dns-mode.el b/lisp/textmodes/dns-mode.el index cc8bad6337b..7bdadbfe6f1 100644 --- a/lisp/textmodes/dns-mode.el +++ b/lisp/textmodes/dns-mode.el | |||
| @@ -147,6 +147,7 @@ manually with \\[dns-mode-soa-increment-serial]." | |||
| 147 | (defvar dns-mode-map | 147 | (defvar dns-mode-map |
| 148 | (let ((map (make-sparse-keymap))) | 148 | (let ((map (make-sparse-keymap))) |
| 149 | (define-key map "\C-c\C-s" 'dns-mode-soa-increment-serial) | 149 | (define-key map "\C-c\C-s" 'dns-mode-soa-increment-serial) |
| 150 | (define-key map "\C-c\C-e" 'dns-mode-ipv6-to-nibbles) | ||
| 150 | map) | 151 | map) |
| 151 | "Keymap for DNS master file mode.") | 152 | "Keymap for DNS master file mode.") |
| 152 | 153 | ||
| @@ -158,7 +159,8 @@ manually with \\[dns-mode-soa-increment-serial]." | |||
| 158 | (easy-menu-define dns-mode-menu dns-mode-map | 159 | (easy-menu-define dns-mode-menu dns-mode-map |
| 159 | "DNS Menu." | 160 | "DNS Menu." |
| 160 | '("DNS" | 161 | '("DNS" |
| 161 | ["Increment SOA serial" dns-mode-soa-increment-serial t])) | 162 | ["Increment SOA serial" dns-mode-soa-increment-serial t] |
| 163 | ["Convert IPv6 address to nibbles" dns-mode-ipv6-to-nibbles t])) | ||
| 162 | 164 | ||
| 163 | ;; Mode. | 165 | ;; Mode. |
| 164 | 166 | ||
| @@ -254,6 +256,101 @@ This function is run from `before-save-hook'." | |||
| 254 | ;; We return nil in case this is used in write-contents-functions. | 256 | ;; We return nil in case this is used in write-contents-functions. |
| 255 | nil))) | 257 | nil))) |
| 256 | 258 | ||
| 259 | ;;;###autoload | ||
| 260 | (defun dns-mode-ipv6-to-nibbles (&optional negate-prefix) | ||
| 261 | "Convert an IPv6 address around or before point. | ||
| 262 | Replace the address by its ip6.arpa-representation for use in | ||
| 263 | reverse zone files, placing the original address in the kill ring. | ||
| 264 | |||
| 265 | The address can be: a complete address (no prefix designator); | ||
| 266 | with a normal prefix designator (e.g. /48), in which case only | ||
| 267 | the required number of nibbles are output; or with a negative | ||
| 268 | prefix designator (e.g. /-112), in which case only the part of | ||
| 269 | the address *not* covered by the absolute value of the prefix | ||
| 270 | length is output, as a relative address (without \".ip6.arpa.\" at | ||
| 271 | the end). This is useful when $ORIGIN is specified in the zone file. | ||
| 272 | |||
| 273 | Optional prefix argument NEGATE-PREFIX negates the value of the | ||
| 274 | detected prefix length. | ||
| 275 | |||
| 276 | Examples: | ||
| 277 | |||
| 278 | 2001:db8::12 => | ||
| 279 | 2.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. | ||
| 280 | |||
| 281 | 2001:db8::12/32 => | ||
| 282 | 8.b.d.0.1.0.0.2.ip6.arpa. | ||
| 283 | |||
| 284 | 2001:db8::12/-32 => | ||
| 285 | 2.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 | ||
| 286 | |||
| 287 | ::42/112 (with prefix argument) => | ||
| 288 | 2.4.0.0" | ||
| 289 | (interactive "P") | ||
| 290 | (skip-syntax-backward " ") | ||
| 291 | (skip-syntax-backward "w_.") | ||
| 292 | (re-search-forward "\\([[:xdigit:]:]+\\)\\(/-?[0-9]\\{2,3\\}\\)?") | ||
| 293 | (kill-new (match-string 0)) | ||
| 294 | (let ((address (match-string 1)) | ||
| 295 | (prefix-length (match-string 2))) | ||
| 296 | (when prefix-length | ||
| 297 | (setq prefix-length (string-to-number (substring prefix-length 1))) | ||
| 298 | (if negate-prefix | ||
| 299 | (setq prefix-length (- prefix-length)))) | ||
| 300 | (replace-match | ||
| 301 | (save-match-data | ||
| 302 | (dns-mode-reverse-and-expand-ipv6 address prefix-length))))) | ||
| 303 | |||
| 304 | (defun dns-mode-reverse-and-expand-ipv6 (address &optional prefix-length) | ||
| 305 | "Convert an IPv6 address to (parts of) an ip6.arpa nibble format. | ||
| 306 | ADDRESS is an IPv6 address in the usual colon-separated | ||
| 307 | format, without a prefix designator at the end. | ||
| 308 | |||
| 309 | Optional PREFIX-LENGTH is a number whose absolute value is the | ||
| 310 | length in bits of the network part of the address. If nil, | ||
| 311 | return an absolute address representing the full IPv6 address. | ||
| 312 | If positive, return an absolute address representing the network | ||
| 313 | prefix indicated. If negative, return a relative address | ||
| 314 | representing the host parts of the address with respect to the | ||
| 315 | indicated network prefix. | ||
| 316 | |||
| 317 | See `dns-mode-ipv6-to-nibbles' for examples." | ||
| 318 | (let* ((chunks (split-string address ":")) | ||
| 319 | (prefix-length-nibbles (if prefix-length | ||
| 320 | (ceiling (abs prefix-length) 4) | ||
| 321 | 32)) | ||
| 322 | (filler-chunks (- 8 (length (remove "" chunks)))) | ||
| 323 | (expanded-address | ||
| 324 | (apply #'concat | ||
| 325 | (cl-loop with filler-done = nil | ||
| 326 | for chunk in chunks | ||
| 327 | if (and (not filler-done) | ||
| 328 | (string= "" chunk)) | ||
| 329 | append (prog1 | ||
| 330 | (cl-loop repeat filler-chunks | ||
| 331 | collect "0000") | ||
| 332 | (setq filler-done t)) | ||
| 333 | else | ||
| 334 | if (not (string= "" chunk)) | ||
| 335 | collect (format "%04x" | ||
| 336 | (string-to-number chunk 16))))) | ||
| 337 | (rev-address-nibbles | ||
| 338 | (nreverse (if (and prefix-length | ||
| 339 | (cl-minusp prefix-length)) | ||
| 340 | (substring expanded-address prefix-length-nibbles) | ||
| 341 | (substring expanded-address 0 prefix-length-nibbles))))) | ||
| 342 | (with-temp-buffer | ||
| 343 | (cl-loop for char across rev-address-nibbles | ||
| 344 | do | ||
| 345 | (insert char) | ||
| 346 | (insert ".")) | ||
| 347 | (if (and prefix-length | ||
| 348 | (cl-minusp prefix-length)) | ||
| 349 | (delete-char -1) | ||
| 350 | (insert "ip6.arpa.")) | ||
| 351 | (insert " ") | ||
| 352 | (buffer-string)))) | ||
| 353 | |||
| 257 | (provide 'dns-mode) | 354 | (provide 'dns-mode) |
| 258 | 355 | ||
| 259 | ;;; dns-mode.el ends here | 356 | ;;; dns-mode.el ends here |
diff --git a/test/lisp/dns-mode-tests.el b/test/lisp/dns-mode-tests.el new file mode 100644 index 00000000000..34e86201d82 --- /dev/null +++ b/test/lisp/dns-mode-tests.el | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | ;;; dns-mode-tests.el --- Test suite for dns-mode -*- lexical-binding: t; -*- | ||
| 2 | |||
| 3 | ;; Copyright (C) 2017 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Author: Peder O. Klingenberg <peder@klingenberg.no> | ||
| 6 | ;; Keywords: dns zone | ||
| 7 | |||
| 8 | ;; This file is part of GNU Emacs. | ||
| 9 | |||
| 10 | ;; GNU Emacs is free software: you can redistribute it and/or modify | ||
| 11 | ;; it under the terms of the GNU General Public License as published by | ||
| 12 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 13 | ;; (at your option) any later version. | ||
| 14 | |||
| 15 | ;; GNU Emacs is distributed in the hope that it will be useful, | ||
| 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 18 | ;; GNU General Public License for more details. | ||
| 19 | |||
| 20 | ;; You should have received a copy of the GNU General Public License | ||
| 21 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | ||
| 22 | |||
| 23 | ;;; Code: | ||
| 24 | |||
| 25 | (require 'ert) | ||
| 26 | (require 'dns-mode) | ||
| 27 | |||
| 28 | ;;; IPv6 reverse zones | ||
| 29 | (ert-deftest dns-mode-ipv6-conversion () | ||
| 30 | (let ((address "2001:db8::42")) | ||
| 31 | (should (equal (dns-mode-reverse-and-expand-ipv6 address) | ||
| 32 | "2.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. ")) | ||
| 33 | (should (equal (dns-mode-reverse-and-expand-ipv6 address 32) | ||
| 34 | "8.b.d.0.1.0.0.2.ip6.arpa. ")) | ||
| 35 | (should (equal (dns-mode-reverse-and-expand-ipv6 address -112) | ||
| 36 | "2.4.0.0 ")))) | ||
| 37 | |||
| 38 | (ert-deftest dns-mode-ipv6-text-replacement () | ||
| 39 | (let ((address "2001:db8::42/32")) | ||
| 40 | (with-temp-buffer | ||
| 41 | ;; Conversion with point directly after address | ||
| 42 | (insert address) | ||
| 43 | (dns-mode-ipv6-to-nibbles nil) | ||
| 44 | (should (equal (buffer-string) "8.b.d.0.1.0.0.2.ip6.arpa. ")) | ||
| 45 | ;; Kill ring contains the expected | ||
| 46 | (erase-buffer) | ||
| 47 | (yank) | ||
| 48 | (should (equal (buffer-string) address)) | ||
| 49 | ;; Point at beginning of address (and prefix arg to command) | ||
| 50 | (goto-char (point-min)) | ||
| 51 | (dns-mode-ipv6-to-nibbles t) | ||
| 52 | (should (equal (buffer-string) "2.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 ")) | ||
| 53 | ;; Point separated from address by whitespace | ||
| 54 | (erase-buffer) | ||
| 55 | (insert address) | ||
| 56 | (insert " ") | ||
| 57 | (dns-mode-ipv6-to-nibbles nil) | ||
| 58 | (should (equal (buffer-string) "8.b.d.0.1.0.0.2.ip6.arpa. "))))) | ||