summaryrefslogtreecommitdiff
path: root/lisp/url/url-util.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/url/url-util.el')
-rw-r--r--lisp/url/url-util.el29
1 files changed, 29 insertions, 0 deletions
diff --git a/lisp/url/url-util.el b/lisp/url/url-util.el
index 85bfb65cb68..77e015068a3 100644
--- a/lisp/url/url-util.el
+++ b/lisp/url/url-util.el
@@ -627,6 +627,35 @@ Creates FILE and its parent directories if they do not exist."
(error "Danger: `%s' is a symbolic link" file))
(set-file-modes file #o0600))))
+(autoload 'dns-query "dns")
+
+(defvar url--domain-cache (make-hash-table :test 'equal :size 17)
+ "Cache to minimize dns lookups.")
+
+;;;###autoload
+(defun url-domain (url)
+ "Return the domain of the host of the url, or nil if url does
+not contain a registered name."
+ ;; Determining the domain of a name can not be done with simple
+ ;; textual manipulations. a.b.c is either host a in domain b.c
+ ;; (www.google.com), or domain a.b.c with no separate host
+ ;; (bbc.co.uk). Instead of guessing based on tld (which in any case
+ ;; may be inaccurate in the face of subdelegations), we look for
+ ;; domain delegations in DNS.
+ ;;
+ ;; Domain delegations change rarely enough that we won't bother with
+ ;; cache invalidation, I think.
+ (let* ((host-parts (split-string (url-host url) "\\."))
+ (result (gethash host-parts url--domain-cache 'not-found)))
+ (when (eq result 'not-found)
+ (setq result
+ (cl-loop for parts on host-parts
+ for dom = (mapconcat #'identity parts ".")
+ when (dns-query dom 'SOA)
+ return dom))
+ (puthash host-parts result url--domain-cache))
+ result))
+
(provide 'url-util)
;;; url-util.el ends here