171 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Scheme
		
	
	
	
			
		
		
	
	
			171 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Scheme
		
	
	
	
| ; ----------------------------
 | |
| ; --- Query/Response-Cache ---
 | |
| ; ----------------------------
 | |
| 
 | |
| ; Cache for dnsd.scm
 | |
| 
 | |
| ; This file is part of the Scheme Untergrund Networking package
 | |
| 
 | |
| ; Copyright (c) 2005/2006 by Norbert Freudemann 
 | |
| ;                            <nofreude@informatik.uni-tuebingen.de>
 | |
| ; For copyright information, see the file COPYING which comes with
 | |
| ; the distribution.
 | |
| 
 | |
| ; Revised version of the cache implementation seen in dns.scm.
 | |
| 
 | |
| ; The cache stores data that was received during a recursive lookup.
 | |
| ; The access-key of the cache consists of a question-name/class/type, the
 | |
| ; data is a list of answers/additionals/authority.
 | |
| ; It uses r/w-lock to avoid multiple simultaneous writes.
 | |
| 
 | |
| ; Cache-Interface:
 | |
| ; -----------------
 | |
| 
 | |
| ; (dnsd-cache-clear!)           - Removes the whole data.
 | |
| ; (dnsd-cache-clean!)           - Removes expired data.
 | |
| ; (dnsd-cache-lookup? msg)      - Searches for a cached reply.
 | |
| ; (dnsd-cache-update! msg)      - Updates the data to include the given msg.
 | |
| ; (dnsd-cache-pretty-print)     - Prints the cache.
 | |
| 
 | |
| ;; Cache:
 | |
| ;; ------
 | |
| 
 | |
| (define-record-type dnsd-cache :dnsd-cache
 | |
|   (make-dnsd-cache data lock)
 | |
|   dnsd-cache?
 | |
|   (data get-dnsd-cache-data)    ; cache-data-record-type
 | |
|   (lock get-dnsd-cache-lock))   ; r/w-lock
 | |
| 
 | |
| (define-record-type cache-data :cache-data
 | |
|   (make-cache-data answer expires)
 | |
|   cache?
 | |
|   (answer cache-data-answer)    ; an answer as needed by lookup-query
 | |
|   (expires cache-data-expires)) ; expiration time of the data (+ ttl (time))
 | |
| 
 | |
| 
 | |
| ;; Create the cache:
 | |
| (define *dnsd-cache* (make-dnsd-cache (make-string-table) (make-r/w-lock)))
 | |
| 
 | |
| 
 | |
| ;; Search for the shortest TTL in the message:
 | |
| ;; TYPE: message -> number or #f
 | |
| (define (find-shortest-ttl msg) 
 | |
|   (let loop ((msg msg))
 | |
|     (cond
 | |
|      ((dns-message? msg) (loop (dns-message-reply msg)))
 | |
|      ((message? msg) (fold-right 
 | |
| 		      (lambda (e m)
 | |
| 			(let ((ttl (resource-record-ttl e)))
 | |
| 			  (if m
 | |
| 			      (if (<= m ttl) m ttl)
 | |
| 			      ttl)))
 | |
| 		      #f
 | |
| 		      (append (message-answers msg) 
 | |
| 			      (message-nameservers msg) 
 | |
| 			      (message-additionals msg)))))))
 | |
| 
 | |
| 
 | |
| ;; Make a cache-key from the message:
 | |
| ;; TYPE: message -> key-string
 | |
| (define (make-cache-key msg)
 | |
|   (let ((question (car (message-questions msg))))
 | |
|     (format #f "~a;~a;~a" (question-name question) 
 | |
| 	    (message-type-name (question-type question))
 | |
| 	    (message-class-name (question-class question)))))
 | |
| 
 | |
|   
 | |
| ;; Reset the cache:
 | |
| (define (dnsd-cache-clear!)
 | |
|   (with-r/W-lock
 | |
|    (get-dnsd-cache-lock *dnsd-cache*)
 | |
|    (lambda ()
 | |
|      (set! *dnsd-cache* 
 | |
| 	   (make-dnsd-cache (make-string-table) 
 | |
| 			   (get-dnsd-cache-lock *dnsd-cache*))))))
 | |
| 
 | |
| 
 | |
| ;; Remove expired data from the cache:
 | |
| (define (dnsd-cache-clean!)
 | |
|   (with-r/W-lock
 | |
|    (get-dnsd-cache-lock *dnsd-cache*)
 | |
|    (lambda ()
 | |
|      (let ((time (time))
 | |
| 	   (table (get-dnsd-cache-data *dnsd-cache*)))
 | |
|        (table-walk (lambda (k e) 
 | |
| 		     (if (< time (cache-data-expires e))
 | |
| 			 #t
 | |
| 			 (table-set! table k #f)))
 | |
| 		   table)))))
 | |
| 
 | |
| 
 | |
| ; Look for data in the cache. If the found answer is expired return
 | |
| ; #f and remove the answer from the cache.
 | |
| ; TYPE: message -> '(l-of-answ l-of-auth l-of-addi boolean) or #f
 | |
| (define (dnsd-cache-lookup? msg)
 | |
|   (let ((lock (get-dnsd-cache-lock *dnsd-cache*)))
 | |
|     (obtain-R/w-lock lock)
 | |
|     (let* ((data (get-dnsd-cache-data *dnsd-cache*))
 | |
| 	   (key (make-cache-key msg))
 | |
| 	   (cdata (table-ref data key)))
 | |
|       (if cdata
 | |
| 	  (if (< (time) (cache-data-expires cdata))
 | |
| 	      (let ((res (cache-data-answer cdata)))
 | |
| 		(release-R/w-lock lock)
 | |
| 		res)
 | |
| 	      (begin
 | |
| 		(release-R/w-lock lock)
 | |
| 		(obtain-r/W-lock lock)
 | |
| 		(table-set! data key #f)
 | |
| 		(release-r/W-lock lock)
 | |
| 		#f))
 | |
| 	  (begin
 | |
| 	    (release-R/w-lock lock)
 | |
| 	    #f)))))
 | |
|     
 | |
| 
 | |
| ;; Add the answer-sections (ansers/authority/additionals) and the authoritative
 | |
| ;; flag of a message to the cache:
 | |
| ;; TYPE: message -> unspecific
 | |
| (define (dnsd-cache-update! msg)
 | |
|   (with-r/W-lock
 | |
|    (get-dnsd-cache-lock *dnsd-cache*)
 | |
|    (lambda ()
 | |
|      (let ((shortest-ttl (find-shortest-ttl msg)))
 | |
|        (if (> shortest-ttl 0)
 | |
| 	   (table-set!
 | |
| 	    (get-dnsd-cache-data *dnsd-cache*)
 | |
| 	    (make-cache-key msg)
 | |
| 	    (make-cache-data
 | |
| 	     (list (message-answers msg) 
 | |
| 		   (message-nameservers msg)
 | |
| 		   (message-additionals msg) 
 | |
| 		   (header-flags (message-header msg))) ; authoritative?
 | |
| 	     (+ (time) shortest-ttl)))
 | |
| 	   #f)))))
 | |
| 
 | |
| 
 | |
| ;; Display the cache:
 | |
| (define (dnsd-cache-pretty-print)
 | |
|   (with-R/w-lock
 | |
|    (get-dnsd-cache-lock *dnsd-cache*)
 | |
|    (lambda ()
 | |
|      (let ((data (get-dnsd-cache-data *dnsd-cache*)))
 | |
|        (display "DNSD-CACHE:\n")
 | |
|        (display "-----------\n")
 | |
|        (table-walk
 | |
| 	(lambda (k e)
 | |
| 	  (let ((cache-data (cache-data-answer e)))
 | |
| 	    (display "\n*Question: ")
 | |
| 	    (display k)(newline)
 | |
| 	    (display " ---------\n")
 | |
| 	    (display " Expires in: ")
 | |
| 	    (display (- (cache-data-expires e) (time)))
 | |
| 	    (display " seconds.\n")
 | |
| 	    (display " \n  Answer-Section:\n\n")
 | |
| 	    (map (lambda (x) (pretty-print-dns-message x)) (car cache-data))
 | |
| 	    (display " \n  Authority-Section:\n\n")
 | |
| 	    (map (lambda (y) (pretty-print-dns-message y)) (cadr cache-data))
 | |
| 	    (display " \n  Additionals-Section:\n\n")
 | |
| 	    (map (lambda (z) (pretty-print-dns-message z)) (caddr cache-data))))
 | |
| 	data)))))
 |