@@ -5,12 +5,14 @@ import {
55 SEQUENCE_NUMBER_MODULO ,
66 SocketState ,
77 SSHAuthenticationMethods ,
8- SSHPacketType ,
8+ PacketNameToType ,
99 SSHServiceNames ,
10+ PacketType ,
11+ PacketTypeToName ,
1012} from "./constants.js"
1113import ProtocolVersionExchange from "./ProtocolVersionExchange.js"
1214import assert from "node:assert"
13- import Packet , { packets } from "./packet.js"
15+ import Packet , { packets , Packets } from "./packet.js"
1416import KexInit from "./packets/KexInit.js"
1517import {
1618 EncryptionAlgorithm ,
@@ -31,7 +33,6 @@ import NewKeys from "./packets/NewKeys.js"
3133import UserAuthRequest from "./packets/UserAuthRequest.js"
3234import Disconnect , { DisconnectReason } from "./packets/Disconnect.js"
3335import ServiceRequest from "./packets/ServiceRequest.js"
34- import ServiceAccept from "./packets/ServiceAccept.js"
3536import Agent from "./publickey/Agent.js"
3637import NoneAgent from "./publickey/NoneAgent.js"
3738import GlobalRequest from "./packets/GlobalRequest.js"
@@ -50,10 +51,12 @@ export interface ClientOptions {
5051 serverClient ?: boolean
5152 authenticationMethodsOrder ?: SSHAuthenticationMethods [ ]
5253}
54+
55+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
5356export interface ClientOptionsRequired extends Required < ClientOptions > { }
5457
55- export type ClientEvents = {
56- debug : [ ...message : any [ ] ]
58+ export interface ClientEvents {
59+ debug : [ ...message : unknown [ ] ]
5760 error : [ error : Error ]
5861 close : [ ]
5962 connect : [ ]
@@ -67,15 +70,16 @@ export type ClientEvents = {
6770 serverNewKeys : [ ]
6871}
6972
70- export type ClientHookerHostKeyController = {
73+ export interface ClientHookerHostKeyController {
7174 allowHostKey : boolean
7275}
7376export type ClientHookerPasswordAuthContext = Readonly < {
7477 username : string
7578} >
76- export type ClientHookerPasswordAuthController = {
79+ export interface ClientHookerPasswordAuthController {
7780 password : string | undefined
7881}
82+ // eslint-disable-next-line @typescript-eslint/consistent-type-definitions
7983export type ClientHooker = {
8084 // `serverPublicKey` is the second argument because
8185 // in some cases, you don't actually need it
@@ -124,7 +128,7 @@ export default class Client extends EventEmitter<ClientEvents> {
124128 }
125129 }
126130
127- hooker : Hooker < ClientHooker > = new Hooker ( )
131+ hooker = new Hooker < ClientHooker > ( )
128132
129133 private socket ?: net . Socket
130134 private buffering : Buffer = Buffer . alloc ( 0 )
@@ -158,9 +162,9 @@ export default class Client extends EventEmitter<ClientEvents> {
158162 integrityKeyClientToServer ?: Buffer
159163 integrityKeyServerToClient ?: Buffer
160164
161- hasReceivedNewKeys : boolean = false
162- hasSentNewKeys : boolean = false
163- hasAuthenticated : boolean = false
165+ hasReceivedNewKeys = false
166+ hasSentNewKeys = false
167+ hasAuthenticated = false
164168
165169 localChannelIndex = 0
166170 channels = new Map < number , Channel > ( )
@@ -173,7 +177,7 @@ export default class Client extends EventEmitter<ClientEvents> {
173177 return this . state === SocketState . Closed
174178 }
175179
176- debug ( ...message : any [ ] ) : void {
180+ debug ( ...message : unknown [ ] ) : void {
177181 this . emit ( "debug" , ...message )
178182 }
179183
@@ -352,10 +356,10 @@ export default class Client extends EventEmitter<ClientEvents> {
352356 } ) ,
353357 )
354358
355- const serviceAnswer : ServiceAccept = await this . waitForPackets (
359+ const serviceAnswer = await this . waitForPackets (
356360 {
357- [ SSHPacketType . SSH_MSG_SERVICE_ACCEPT ] : {
358- predicate : ( packet : ServiceAccept ) => {
361+ SSH_MSG_SERVICE_ACCEPT : {
362+ predicate : ( packet ) => {
359363 return packet . data . service_name == SSHServiceNames . UserAuth
360364 } ,
361365 } ,
@@ -452,27 +456,31 @@ export default class Client extends EventEmitter<ClientEvents> {
452456 cleanup ( )
453457 reject ( error )
454458 }
455- const handler = ( ...values : any ) => {
459+ const handler = ( ...values : ClientEvents [ event ] ) => {
456460 resolve ( values )
457461 cleanup ( )
458462 }
459463 const cleanup = ( ) => {
460- this . off ( event , handler as any )
464+ // @ts -expect-error the function definition makes sure this is respected
465+ this . off ( event , handler )
461466 this . off ( "error" , onError )
462467 }
463- this . once ( event , handler as any )
468+ // @ts -expect-error the function definition makes sure this is respected
469+ this . once ( event , handler )
464470 this . once ( "error" , onError )
465471 } )
466472 }
467- waitForPacket < packet extends Packet > ( packet : SSHPacketType ) : Promise < packet > {
473+ waitForPacket < Name extends keyof typeof packets > ( name : Name ) : Promise < ( typeof packets ) [ Name ] > {
468474 return new Promise ( ( resolve , reject ) => {
475+ const classType = packets [ name ]
469476 const onError = ( error : Error ) => {
470477 cleanup ( )
471478 reject ( error )
472479 }
473480 const handler = ( p : Packet ) => {
474- if ( ( p . constructor as typeof Packet ) . type === packet ) {
475- resolve ( p as packet )
481+ if ( p instanceof classType ) {
482+ // @ts -expect-error good luck typing that
483+ resolve ( p )
476484 cleanup ( )
477485 }
478486 }
@@ -484,32 +492,42 @@ export default class Client extends EventEmitter<ClientEvents> {
484492 this . once ( "error" , onError )
485493 } )
486494 }
495+
496+ // holy fucking shit what the fuck are those types ?
487497 waitForPackets <
488- packets extends {
489- [ key in SSHPacketType ] ?: {
490- predicate : ( packet : any ) => boolean
498+ Predicates extends {
499+ [ Name in keyof Packets ] ?: {
500+ predicate : ( packet : Packets [ Name ] ) => boolean
491501 }
492502 } ,
493- > ( packets : packets , timeout : number ) : Promise < any > {
503+ > (
504+ Predicates : Predicates ,
505+ timeout : number ,
506+ ) : Promise < Packets [ Extract < keyof Predicates , keyof Packets > ] > {
494507 return new Promise ( ( resolve , reject ) => {
495508 const cleanup = ( ) => {
496509 this . off ( "packet" , onPacket )
497510 this . off ( "error" , onError )
498511 clearTimeout ( timer )
499512 }
500513 const onPacket = ( packet : Packet ) => {
501- // toString to convert the number to a string
502- // because the type key in the packets object
503- // is transformed to a stirng by javascript
504- // could also use a loose == but I prefer to be explicit
505- const packetType = ( packet . constructor as typeof Packet ) . type . toString ( )
506- for ( const [ type , { predicate } ] of Object . entries ( packets ) ) {
507- if ( packetType === type && predicate ( packet ) ) {
508- resolve ( packet )
509- cleanup ( )
510- return
511- }
514+ const packetType = ( packet . constructor as typeof Packet ) . type
515+ const packetName = PacketTypeToName [ packetType ]
516+ // we're not interested by this packet
517+ if ( ! ( packetName in Predicates ) ) return
518+ if ( ! ( packetName in packets ) ) return
519+
520+ const predicateEntry = Predicates [ packetName as keyof Predicates ]
521+ if ( ! predicateEntry ) return
522+
523+ const { predicate } = predicateEntry as unknown as {
524+ predicate : ( packet : Packet ) => boolean
512525 }
526+ if ( ! predicate ( packet ) ) return
527+
528+ // @ts -expect-error good luck typing that
529+ resolve ( packet )
530+ cleanup ( )
513531 }
514532 const onError = ( error : Error ) => {
515533 cleanup ( )
@@ -679,23 +697,28 @@ export default class Client extends EventEmitter<ClientEvents> {
679697 message = message . subarray ( 0 , 5 + n1 + n2 + macsize )
680698 this . emit ( "message" , message )
681699
682- this . debug ( "Receiving packet:" , SSHPacketType [ payload [ 0 ] ] )
700+ const packetType = payload [ 0 ] as PacketType
701+ this . debug ( "Receiving packet:" , packetType )
683702
684703 this . in_sequence_number ++
685704 this . in_sequence_number %= SEQUENCE_NUMBER_MODULO
686705
687- const packet = packets . get ( payload [ 0 ] )
688- if ( ! packet ) {
689- throw new Error ( "Invalid packet type (" + payload [ 0 ] + ")" )
706+ if ( ! ( packetType in PacketTypeToName ) ) {
707+ throw new Error ( "Invalid packet type: " + packetType )
708+ }
709+ const packetName = PacketTypeToName [ packetType ]
710+ if ( ! ( packetName in packets ) ) {
711+ throw new Error ( "Not implemented: " + packetName )
690712 }
713+ const packet = packets [ packetName as keyof typeof packets ]
691714
692715 const p = packet . parse ( payload )
693716 this . debug ( "Parsing packet:" , p )
694717
695718 this . emit ( "packet" , p )
696719
697720 switch ( packet . type ) {
698- case SSHPacketType . SSH_MSG_DISCONNECT : {
721+ case PacketNameToType . SSH_MSG_DISCONNECT : {
699722 const disconnect = p as Disconnect
700723 this . debug (
701724 "Server disconnected:" ,
@@ -707,28 +730,28 @@ export default class Client extends EventEmitter<ClientEvents> {
707730 break
708731 }
709732
710- case SSHPacketType . SSH_MSG_IGNORE :
733+ case PacketNameToType . SSH_MSG_IGNORE :
711734 this . debug ( `Received Ignore packet. Ignoring.` )
712735 break
713736
714- case SSHPacketType . SSH_MSG_DEBUG : {
737+ case PacketNameToType . SSH_MSG_DEBUG : {
715738 const debug = p as Debug
716739 this . debug ( `Received debug packet:` , [ debug . data . message ] )
717740 break
718741 }
719742
720- case SSHPacketType . SSH_MSG_KEXINIT :
743+ case PacketNameToType . SSH_MSG_KEXINIT :
721744 // handle key exchange
722745 this . emit ( "serverKexInit" , p as KexInit , payload )
723746 break
724747
725- case SSHPacketType . SSH_MSG_NEWKEYS :
748+ case PacketNameToType . SSH_MSG_NEWKEYS :
726749 this . hasReceivedNewKeys = true
727750 this . emit ( "serverNewKeys" )
728751 // handle key exchange
729752 break
730753
731- case SSHPacketType . SSH_MSG_KEXDH_REPLY :
754+ case PacketNameToType . SSH_MSG_KEXDH_REPLY :
732755 // handle key exchange
733756 this . emit ( "serverKexDHReply" , p as KexDHReply )
734757 break
0 commit comments