aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Ingebrigtsen2020-07-30 03:44:45 +0200
committerLars Ingebrigtsen2020-07-30 05:32:16 +0200
commitef7f569cbd3a69a77c09bc214baacd47737f7e01 (patch)
treede1922feb36fb72e9c8e3afb7c0b0dd6a1461334
parent789197049ca13a1434afccd6614134cc276a5074 (diff)
downloademacs-ef7f569cbd3a69a77c09bc214baacd47737f7e01.tar.gz
emacs-ef7f569cbd3a69a77c09bc214baacd47737f7e01.zip
Add the new function dns-query-asynchronous
* lisp/net/dns.el (dns-query-asynchronous): New function. (dns--lookup, dns--filter): New internal functions. (dns-query): Reimplement on top of dns-query-asynchronous.
-rw-r--r--etc/NEWS5
-rw-r--r--lisp/net/dns.el176
2 files changed, 123 insertions, 58 deletions
diff --git a/etc/NEWS b/etc/NEWS
index 8f5864961d2..fab2d85e8da 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -637,6 +637,11 @@ Formerly it made an exception for integer components of SOA records,
637because SOA serial numbers can exceed fixnum ranges on 32-bit platforms. 637because SOA serial numbers can exceed fixnum ranges on 32-bit platforms.
638Emacs now supports bignums so this old glitch is no longer needed. 638Emacs now supports bignums so this old glitch is no longer needed.
639 639
640---
641** The new function 'dns-query-asynchronous' has been added.
642It takes the same parameters as 'dns-query', but adds a callback
643parameter.
644
640** The Lisp variables 'previous-system-messages-locale' and 645** The Lisp variables 'previous-system-messages-locale' and
641'previous-system-time-locale' have been removed, as they were created 646'previous-system-time-locale' have been removed, as they were created
642by mistake and were not useful to Lisp code. 647by mistake and were not useful to Lisp code.
diff --git a/lisp/net/dns.el b/lisp/net/dns.el
index 1c46102554e..ef250f067ea 100644
--- a/lisp/net/dns.el
+++ b/lisp/net/dns.el
@@ -374,9 +374,14 @@ Parses \"/etc/resolv.conf\" or calls \"nslookup\"."
374 (set (intern key dns-cache) result) 374 (set (intern key dns-cache) result)
375 result)))) 375 result))))
376 376
377(defun dns-query (name &optional type fullp reversep) 377(defun dns-query-asynchronous (name callback &optional type fullp reversep)
378 "Query a DNS server for NAME of TYPE. 378 "Query a DNS server for NAME of TYPE.
379If FULLP, return the entire record returned. 379CALLBACK will be called with a single parameter: The result.
380
381If there's no result, or `dns-timeout' has passed, CALLBACK will
382be called with nil as the parameter.
383
384If FULLP, return the entire record.
380If REVERSEP, look up an IP address." 385If REVERSEP, look up an IP address."
381 (setq type (or type 'A)) 386 (setq type (or type 'A))
382 (unless (dns-servers-up-to-date-p) 387 (unless (dns-servers-up-to-date-p)
@@ -392,63 +397,118 @@ If REVERSEP, look up an IP address."
392 (progn 397 (progn
393 (message "No DNS server configuration found") 398 (message "No DNS server configuration found")
394 nil) 399 nil)
395 (with-temp-buffer 400 (dns--lookup name callback type fullp)))
396 (set-buffer-multibyte nil) 401
397 (let* ((process 402(defun dns--lookup (name callback type full)
398 (condition-case () 403 (with-current-buffer (generate-new-buffer " *dns*")
399 (let ((server (car dns-servers)) 404 (set-buffer-multibyte nil)
400 (coding-system-for-read 'binary) 405 (let* ((tcp nil)
401 (coding-system-for-write 'binary)) 406 (process
402 (if (featurep 'make-network-process '(:type datagram)) 407 (condition-case ()
403 (make-network-process 408 (let ((server (car dns-servers))
404 :name "dns" 409 (coding-system-for-read 'binary)
405 :coding 'binary 410 (coding-system-for-write 'binary))
406 :buffer (current-buffer) 411 (if (featurep 'make-network-process '(:type datagram))
407 :host server 412 (make-network-process
408 :service "domain" 413 :name "dns"
409 :type 'datagram) 414 :coding 'binary
410 ;; On MS-Windows datagram sockets are not 415 :buffer (current-buffer)
411 ;; supported, so we fall back on opening a TCP 416 :host server
412 ;; connection to the DNS server. 417 :service "domain"
418 :type 'datagram)
419 ;; On MS-Windows datagram sockets are not
420 ;; supported, so we fall back on opening a TCP
421 ;; connection to the DNS server.
422 (progn
423 (setq tcp t)
413 (open-network-stream "dns" (current-buffer) 424 (open-network-stream "dns" (current-buffer)
414 server "domain"))) 425 server "domain"))))
415 (error 426 (error
416 (message 427 (message
417 "dns: Got an error while trying to talk to %s" 428 "dns: Got an error while trying to talk to %s"
418 (car dns-servers)) 429 (car dns-servers))
419 nil))) 430 nil)))
420 (step 100) 431 (triggered nil)
421 (times (* dns-timeout 1000)) 432 (buffer (current-buffer))
422 (id (random 65000)) 433 timer)
423 (tcp-p (and process (not (process-contact process :type))))) 434 (if (not process)
424 (when process 435 (progn
425 (process-send-string 436 (kill-buffer buffer)
426 process 437 (funcall callback nil))
427 (dns-write `((id ,id) 438 ;; Call the callback if we don't get any response at all.
428 (opcode query) 439 (setq timer (run-at-time dns-timeout nil
429 (queries ((,name (type ,type)))) 440 (lambda ()
430 (recursion-desired-p t)) 441 (unless triggered
431 tcp-p)) 442 (setq triggered t)
432 (while (and (zerop (buffer-size)) 443 (delete-process process)
433 (> times 0)) 444 (kill-buffer buffer)
434 (let ((step-sec (/ step 1000.0))) 445 (funcall callback nil)))))
435 (sit-for step-sec) 446 (process-send-string
436 (accept-process-output process step-sec)) 447 process
437 (setq times (- times step))) 448 (dns-write `((id ,(random 65000))
438 (condition-case nil 449 (opcode query)
439 (delete-process process) 450 (queries ((,name (type ,type))))
440 (error nil)) 451 (recursion-desired-p t))
441 (when (and (>= (buffer-size) 2) 452 tcp))
442 ;; We had a time-out. 453 (set-process-filter
443 (> times 0)) 454 process
444 (let ((result (dns-read (buffer-string) tcp-p))) 455 (lambda (process string)
445 (if fullp 456 (with-current-buffer (process-buffer process)
446 result 457 (goto-char (point-max))
447 (let ((answer (car (dns-get 'answers result)))) 458 (insert string)
448 (when (eq type (dns-get 'type answer)) 459 (goto-char (point-min))
449 (if (eq type 'TXT) 460 ;; If this is DNS, then we always get the full data in
450 (dns-get-txt-answer (dns-get 'answers result)) 461 ;; one packet. If it's TCP, we may only get part of the
451 (dns-get 'data answer)))))))))))) 462 ;; data, but the first two bytes says how long the data
463 ;; is supposed to be.
464 (when (or (not tcp)
465 (>= (buffer-size) (dns-read-bytes 2)))
466 (setq triggered t)
467 (cancel-timer timer)
468 (dns--filter process callback type full tcp)))))
469 ;; In case we the process is deleted for some reason, then do
470 ;; a failure callback.
471 (set-process-sentinel
472 process
473 (lambda (_ state)
474 (when (and (eq state 'deleted)
475 ;; Ensure we don't trigger this callback twice.
476 (not triggered))
477 (setq triggered t)
478 (cancel-timer timer)
479 (kill-buffer buffer)
480 (funcall callback nil))))))))
481
482(defun dns--filter (process callback type full tcp)
483 (let ((message (buffer-string)))
484 (when (process-live-p process)
485 (delete-process process))
486 (kill-buffer (current-buffer))
487 (when (>= (length message) 2)
488 (let ((result (dns-read message tcp)))
489 (funcall callback
490 (if full
491 result
492 (let ((answer (car (dns-get 'answers result))))
493 (when (eq type (dns-get 'type answer))
494 (if (eq type 'TXT)
495 (dns-get-txt-answer (dns-get 'answers result))
496 (dns-get 'data answer))))))))))
497
498(defun dns-query (name &optional type fullp reversep)
499 "Query a DNS server for NAME of TYPE.
500If FULLP, return the entire record returned.
501If REVERSEP, look up an IP address."
502 (let ((result nil))
503 (dns-query-asynchronous
504 name
505 (lambda (response)
506 (setq result (list response)))
507 type fullp reversep)
508 ;; Loop until we get the callback.
509 (while (not result)
510 (sleep-for 0.01))
511 (car result)))
452 512
453(provide 'dns) 513(provide 'dns)
454 514