diff options
| author | Lars Ingebrigtsen | 2020-07-30 03:44:45 +0200 |
|---|---|---|
| committer | Lars Ingebrigtsen | 2020-07-30 05:32:16 +0200 |
| commit | ef7f569cbd3a69a77c09bc214baacd47737f7e01 (patch) | |
| tree | de1922feb36fb72e9c8e3afb7c0b0dd6a1461334 | |
| parent | 789197049ca13a1434afccd6614134cc276a5074 (diff) | |
| download | emacs-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/NEWS | 5 | ||||
| -rw-r--r-- | lisp/net/dns.el | 176 |
2 files changed, 123 insertions, 58 deletions
| @@ -637,6 +637,11 @@ Formerly it made an exception for integer components of SOA records, | |||
| 637 | because SOA serial numbers can exceed fixnum ranges on 32-bit platforms. | 637 | because SOA serial numbers can exceed fixnum ranges on 32-bit platforms. |
| 638 | Emacs now supports bignums so this old glitch is no longer needed. | 638 | Emacs now supports bignums so this old glitch is no longer needed. |
| 639 | 639 | ||
| 640 | --- | ||
| 641 | ** The new function 'dns-query-asynchronous' has been added. | ||
| 642 | It takes the same parameters as 'dns-query', but adds a callback | ||
| 643 | parameter. | ||
| 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 |
| 642 | by mistake and were not useful to Lisp code. | 647 | by 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. |
| 379 | If FULLP, return the entire record returned. | 379 | CALLBACK will be called with a single parameter: The result. |
| 380 | |||
| 381 | If there's no result, or `dns-timeout' has passed, CALLBACK will | ||
| 382 | be called with nil as the parameter. | ||
| 383 | |||
| 384 | If FULLP, return the entire record. | ||
| 380 | If REVERSEP, look up an IP address." | 385 | If 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. | ||
| 500 | If FULLP, return the entire record returned. | ||
| 501 | If 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 | ||