More channel management, and steps toward a repl server

This commit is contained in:
Tony Garnock-Jones 2011-10-24 18:36:08 -04:00
parent 1ab7cecf97
commit 9290dbea34
2 changed files with 186 additions and 42 deletions

54
repl-server.rkt Normal file
View File

@ -0,0 +1,54 @@
#lang racket/base
;; (Temporary) example client and server
(require racket/tcp)
(require racket/pretty)
(require racket/match)
(require "conversation.rkt")
(require "ssh-numbers.rkt")
(require "ssh-session.rkt")
(require "standard-thread.rkt")
(define (t-client)
(let-values (((i o) (tcp-connect "localhost"
2323
;;22
)))
(let ((api (ssh-session 'client i o)))
(printf "Got API ~v\n" api)
(semaphore-wait (make-semaphore 0)))))
(define (repl-channel handle)
(semaphore-wait (make-semaphore 0)))
(define (t-server)
(define s (tcp-listen 2322 4 #t "localhost"))
(printf "Accepting...\n")
(let loop ()
(let-values (((i o) (tcp-accept s)))
(standard-thread
(lambda ()
(simple-ssh-server (ssh-session 'server i o)
(lambda (channel-type extra-request-data state)
(match channel-type
(#"session"
(define channel-room (make-room 'channel))
(spy-on channel-room)
(standard-thread (lambda ()
(repl-channel
(join-room channel-room 'repl
#:break-on-departure? #t))))
(wait-for-members channel-room '(repl))
(values `(ok ,channel-room #"")
state))
(else
(values `(error ,SSH_OPEN_ADMINISTRATIVELY_PROHIBITED
"Unknown channel type")
state))))
'no-state)))
(loop))))
(if (getenv "clientmode")
(t-client)
(t-server))

View File

@ -16,12 +16,22 @@
(require "conversation.rkt")
(require "standard-thread.rkt")
(require "ordered-rpc.rkt")
(require "ssh-numbers.rkt")
(require "ssh-message-types.rkt")
(require "ssh-exceptions.rkt")
(require "ssh-transport.rkt")
(provide required-peer-identification-regex
client-preamble-lines
client-identification-string
rekey-interval
rekey-volume
ssh-session
simple-ssh-server)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Data definitions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -64,12 +74,28 @@
total-transferred
rekey-state
authentication-state
continuations
channel-map
is-server?
local-id
remote-id
session-id) ;; starts off #f until initial keying
#:transparent)
;; A ChannelState is a (ssh-channel ...) TODO
;; Named ssh-channel to avoid conflicts with Racket's built-in
;; synchronous channels.
(struct ssh-channel (room-handle ;; RoomHandle
my-ref ;; Uint32
your-ref ;; Maybe<Uint32>
type ;; String
continuations ;; TransactionManager (see ordered-rpc.rkt)
outbound-window ;; Maybe<Natural>
outbound-packet-size ;; Maybe<Natural>
inbound-window ;; Natural
)
#:transparent)
;; Generic inputs into the exchange-hash part of key
;; exchange. Diffie-Hellman uses these fields along with the host key,
;; the exchange values, and the shared secret to get the final hash.
@ -484,13 +510,32 @@
(write-message!/flush (ssh-msg-userauth-failure '(none) #f) conn)
conn)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Channel management
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (allocate-channel conn room type your-ref outbound-window outbound-packet-size)
(define my-ref (hash-count (connection-channel-map conn)))
(define ch (ssh-channel (join-room room 'session)
my-ref
your-ref
type
(make-transaction-manager)
outbound-window
outbound-packet-size
1048576 ;; TODO: parameterize? Make configurable by app?
))
(values ch
(struct-copy connection conn
[channel-map (hash-set (connection-channel-map conn) my-ref ch)])))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Connection service
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (start-connection-service conn)
(set-handlers conn
SSH_MSG_GLOBAL_REQUEST handle-msg-global-request
;; TODO: SSH_MSG_GLOBAL_REQUEST handle-msg-global-request
SSH_MSG_CHANNEL_OPEN handle-msg-channel-open
SSH_MSG_CHANNEL_WINDOW_ADJUST handle-msg-window-adjust
SSH_MSG_CHANNEL_DATA handle-msg-channel-data
@ -504,8 +549,49 @@
conn)
(define (handle-msg-channel-open packet message conn)
(log-error "TODO: Unimplemented: handle-msg-channel-open")
conn)
(match message
((ssh-msg-channel-open channel-type*
sender-channel
initial-window-size
maximum-packet-size
extra-request-data*)
(define channel-type (bit-string->bytes channel-type*))
(define extra-request-data (bit-string->bytes extra-request-data*))
(app-request conn
`(open-channel ,channel-type ,extra-request-data)
(lambda (reply conn)
(match reply
(`(ok ,(? room? room) ,(? bytes? extra-reply-data))
(let-values (((ch conn) (allocate-channel conn
room
channel-type
sender-channel
initial-window-size
maximum-packet-size)))
(write-message!/flush (ssh-msg-channel-open-confirmation
sender-channel
(ssh-channel-my-ref ch)
(ssh-channel-inbound-window ch)
(default-packet-limit) ;; TODO get from reader
extra-reply-data)
conn)
conn))
(`(error ,reason-code ,description)
(write-message!/flush (ssh-msg-channel-open-failure
sender-channel
reason-code
(string->bytes/utf-8 description)
#"")
conn)
conn)))
(lambda (reason conn)
(write-message!/flush (ssh-msg-channel-open-failure
sender-channel
0
#"Internal error"
#"")
conn)
conn)))))
(define (handle-msg-window-adjust packet message conn)
(log-error "TODO: Unimplemented: handle-msg-window-adjust")
@ -545,6 +631,27 @@
(write-message! message conn)
(flush-outbound-messages! conn))
(define (app-notify conn message)
(send (connection-session-room-handle conn) say message)
conn)
(define (app-request conn message k-ok k-error)
(define-values (transaction new-continuations)
(open-transaction (connection-continuations conn) (list k-ok k-error)))
(send (connection-session-room-handle conn) say (rpc-request 'session transaction message))
(struct-copy connection conn [continuations new-continuations]))
(define (finish-app-request conn txn context-extractor message-or-reason)
(close-transaction! txn (list context-extractor message-or-reason))
(let loop ((conn conn))
(if (transaction-available? (connection-continuations conn))
(let-values (((txn rest) (dequeue-transaction (connection-continuations conn))))
(match-define (list context-extractor message-or-reason) (transaction-value txn))
(loop ((context-extractor (transaction-context txn))
message-or-reason
(struct-copy connection conn [continuations rest]))))
conn)))
(define (maybe-send-disconnect-message! e conn)
(when (not (exn:fail:contract:protocol-originated-at-peer? e))
(write-message!/flush (ssh-msg-disconnect (exn:fail:contract:protocol-reason-code e)
@ -601,7 +708,14 @@
(disconnect-with-error/local-info
departure
SSH_DISCONNECT_BY_APPLICATION
"Application disconnected")))))))))
"Application disconnected"))
((says _ (rpc-reply transaction message) _)
;; TODO: not cap-secure. Introduce sealers, or indirect.
(loop (finish-app-request conn transaction car message)))
((says _ (rpc-error transaction reason) _)
;; TODO: not cap-secure. Introduce sealers, or indirect.
(loop (finish-app-request conn transaction cadr reason)))
)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Session choreography
@ -671,51 +785,27 @@
0
(rekey-in-seconds-or-bytes -1 -1 0)
#f
(make-transaction-manager)
(hash)
(case role ((client) #f) ((server) #t))
local-identification-string
peer-identification-string
#f))))
(make-object ssh-session% session-room))
(join-room session-room 'app #:break-on-departure? #t))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Session API
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define ssh-session%
(class* object% ()
(init room-init)
(super-new)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Test driver code
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require racket/tcp)
(require racket/pretty)
(define (t-client)
(let-values (((i o) (tcp-connect "localhost"
2323
;;22
)))
(let ((api (ssh-session 'client i o)))
(printf "Got API ~v\n" api)
(semaphore-wait (make-semaphore 0)))))
(define (t-server)
(define s (tcp-listen 2322 4 #t "localhost"))
(printf "Accepting...\n")
(let loop ()
(let-values (((i o) (tcp-accept s)))
(standard-thread
(lambda ()
(let ((api (ssh-session 'server i o)))
(printf "Got API ~v\n" api)
(semaphore-wait (make-semaphore 0)))))
(loop))))
(if (getenv "servermode")
(t-server)
(t-client))
(define (simple-ssh-server handle channel-open-callback state)
(let loop ((state state))
(match (send handle listen)
((arrived _) (loop state))
((says _ (rpc-request reply-to id message) _)
(match message
(`(open-channel ,channel-type ,extra-request-data)
(define-values (reply-body new-state)
(channel-open-callback channel-type extra-request-data state))
(send handle say (rpc-reply id reply-body))
(loop new-state)))))))