;; -*- Mode:LISP; Package:TCP; Readtable:CL; Base:10 -*- #| Copyright LISP Machine, Inc. 1987 See filename "Copyright.Text" for licensing and release information. |# (defmacro without-more-processing (the-window &body body) "execute body with more processing disabled" (let ((more-p (gensym)) (window (gensym))) `(let* ((,window ,the-window) (,more-p (send ,window :more-p))) (unwind-protect (progn (send ,window :set-more-p nil) ,@body) (send ,window ':set-more-p ,more-p))))) (defvar *telnet-logging* nil) (defvar *telnet-data-log* nil) (defvar *telnet-final-stats-block* nil) (defvar *telnet-final-tcp-stats-block* nil) (defun initialize-data-log () (setq *telnet-data-log* nil)) (defun log-data (buffer) (when *telnet-logging* (push (string buffer) *telnet-data-log*))) (defparameter *telnet-receive-buffers* 8) (defparameter *telnet-receive-buffer-size* 256) (defun telnet (host) (if (ip-header-p host) (setq host (ip:ih-dest-address host)) (let ((original-host host)) (assert (numberp (setq host (parse-internet-address (setq original-host host)))) (host) "~S is not a valid Internet host specification" original-host))) (let ((socket (make-tcp-socket :keyword "User Telnet")) (opened nil) (open nil) (term-in-buffer nil) (term-out-buffer nil)) (without-more-processing *terminal-io* (initialize-data-log) (unwind-protect (cond ((setq opened (send socket :open :remote-port 23 :remote-address host :active t :auto-push t :optimistic t)) (setq term-in-buffer (make-array 256 :fill-pointer 0 :element-type '(unsigned-byte 8))) (setq term-out-buffer (make-string 256)) (loop (process-wait "Terminal or Network" #'(lambda (term) (or (send socket :listen) (and open (send term :listen)))) *terminal-io*) (cond ((send *terminal-io* :listen) (get-terminal-data term-in-buffer) (copy-data-to-net term-in-buffer socket)) ((send socket :listen) (let ((item (send socket :read-data))) (case (first item) (:open (dotimes (i *telnet-receive-buffers*) (send socket :receive (make-array *telnet-receive-buffer-size* :fill-pointer 0 :element-type '(unsigned-byte 8)))) (setq open t)) (:data ;Data from remote host (log-data (second item)) (copy-data-to-terminal (second item) term-out-buffer) (unless (eq (third item) :eof) (send socket :receive (second item)))) (:closing ;Remote side has closed (return "Closed by remote end")) (:reset (setq opened open) (return (if open "Connection reset" "Connection refused"))) (:close ;Socket closed out from under us (setq opened nil) (return "Closed locally")) ((:network-unreachable :host-unreachable :protocol-unreachable :port-unreachable) (return "Unreachable")) (:timeout (send socket :abort) (setq opened nil) (return "Timed out")) (otherwise ;;Ignore it ))))))) (t "TCP not running")) (when opened (send socket :close)) (setq *telnet-final-stats-block* (tcp-user-statistics-block socket)) (setq *telnet-final-tcp-stats-block* (tcp-user-stats socket)) (setq *telnet-data-log* (nreverse *telnet-data-log*)))))) (defun playback () (let ((term-out-buffer (make-string 256))) (dolist (x *telnet-data-log*) (copy-data-to-terminal x term-out-buffer)))) (defun get-terminal-data (buffer) (do ((limit (array-length buffer)) (index 0)) ((or (not (send *terminal-io* :listen)) (>= index limit)) (setf (fill-pointer buffer) index) buffer) (let ((char (char-to-ascii (send *terminal-io* :tyi)))) (when char ;;(send *terminal-io* :tyo char) (setf (aref buffer index) char) (incf index) (when (eq char #o15) (setf (aref buffer index) #o12) (incf index)))))) (defun copy-data-to-net (buffer socket) (when (plusp (length buffer)) (send socket :write-data (string buffer)))) (defun copy-data-to-terminal (in-buffer out-buffer) (do ((index 0 (1+ index)) (count 0) (limit (length in-buffer))) ((>= index limit) (when (plusp count) (send *terminal-io* :string-out out-buffer 0 count))) (let ((char (ascii-to-char (aref in-buffer index)))) (cond ((eq char #o210) (when (plusp count) (send *terminal-io* :string-out out-buffer 0 count) (setq count 0)) (send *terminal-io* :tyo char)) (char (setf (char out-buffer count) char) (incf count)))))) (defun char-to-ascii (lispm-char) (cond ((char-bit lispm-char :control) (logxor #o100 (char-upcase (char-to-ascii (char-code lispm-char))))) ((not (zerop (char-bits lispm-char))) nil) ((member lispm-char `(,(char-int #\return) ,(char-int #\line) ,(char-int #\tab) ,(char-int #\form))) (- lispm-char #o200)) ((= lispm-char (char-int #\Rubout)) #o177) ((graphic-char-p lispm-char) (char-code lispm-char)))) (defun ascii-to-char (ascii-char) (cond ((> ascii-char #o177) ;;high bit set nil) ((= ascii-char #o15) ;;Carriage Return -- ignore it nil) ((= ascii-char #o12) ;;Line Feed -- convert to RETURN #\return) ((member ascii-char '(#o11 #o14 #o10)) ;; Tab, Form Feed, Backspace (+ ascii-char #o200)) ((< ascii-char #o40) ;;Other control character nil) ('else ;;Normal printing character ascii-char)))