1 module libssh.session;
2 
3 import std..string;
4 import std.algorithm.mutation;
5 
6 import libssh.c_bindings.libssh;
7 import libssh.c_bindings.callbacks;
8 import libssh.c_bindings.sftp;
9 import libssh.c_bindings.server;
10 import libssh.errors;
11 import libssh.utils;
12 import libssh.channel;
13 import libssh.message;
14 import libssh.logging;
15 import libssh.scp;
16 import libssh.key;
17 import libssh.sftp;
18 import libssh.message;
19 
20 enum PollFlags : int {
21     ReadPending = SSH_READ_PENDING,
22     WritePending = SSH_WRITE_PENDING,
23 }
24 
25 enum SessionStatusFlags : int {
26     Closed = SSH_CLOSED,
27     ReadPending = SSH_READ_PENDING,
28     ClosedError = SSH_CLOSED_ERROR,
29     WritePending = SSH_WRITE_PENDING,
30 }
31 
32 enum ServerKnownState : int {
33     Ok = ssh_server_known_e.SSH_SERVER_KNOWN_OK,
34     Changed = ssh_server_known_e.SSH_SERVER_KNOWN_CHANGED,
35     FoundOther = ssh_server_known_e.SSH_SERVER_FOUND_OTHER,
36     NotKnown = ssh_server_known_e.SSH_SERVER_NOT_KNOWN,
37     FileNotFound = ssh_server_known_e.SSH_SERVER_FILE_NOT_FOUND,
38 }
39 
40 enum SessionOption : int {
41     Host = ssh_options_e.SSH_OPTIONS_HOST,
42     Port = ssh_options_e.SSH_OPTIONS_PORT,
43     PortStr = ssh_options_e.SSH_OPTIONS_PORT_STR,
44     Fd = ssh_options_e.SSH_OPTIONS_FD,
45     User = ssh_options_e.SSH_OPTIONS_USER,
46     SshDir = ssh_options_e.SSH_OPTIONS_SSH_DIR,
47     Identity = ssh_options_e.SSH_OPTIONS_IDENTITY,
48     AddIdentity = ssh_options_e.SSH_OPTIONS_ADD_IDENTITY,
49     KnownHosts = ssh_options_e.SSH_OPTIONS_KNOWNHOSTS,
50     Timeout = ssh_options_e.SSH_OPTIONS_TIMEOUT,
51     TimeoutUsec = ssh_options_e.SSH_OPTIONS_TIMEOUT_USEC,
52     Ssh1 = ssh_options_e.SSH_OPTIONS_SSH1,
53     Ssh2 = ssh_options_e.SSH_OPTIONS_SSH2,
54     LogVerbosity = ssh_options_e.SSH_OPTIONS_LOG_VERBOSITY,
55     LogVerbosityStr = ssh_options_e.SSH_OPTIONS_LOG_VERBOSITY_STR,
56     CiphersCS = ssh_options_e.SSH_OPTIONS_CIPHERS_C_S,
57     CiphersSC = ssh_options_e.SSH_OPTIONS_CIPHERS_S_C,
58     CompressionCS = ssh_options_e.SSH_OPTIONS_COMPRESSION_C_S,
59     CompressionSC = ssh_options_e.SSH_OPTIONS_COMPRESSION_S_C,
60     ProxyCommand = ssh_options_e.SSH_OPTIONS_PROXYCOMMAND,
61     BindAddr = ssh_options_e.SSH_OPTIONS_BINDADDR,
62     StrictHostkeyCheck = ssh_options_e.SSH_OPTIONS_STRICTHOSTKEYCHECK,
63     Compression = ssh_options_e.SSH_OPTIONS_COMPRESSION,
64     CompressionLevel = ssh_options_e.SSH_OPTIONS_COMPRESSION_LEVEL,
65     KeyExchange = ssh_options_e.SSH_OPTIONS_KEY_EXCHANGE,
66     Hostkeys = ssh_options_e.SSH_OPTIONS_HOSTKEYS,
67     GssapiServerIdentity = ssh_options_e.SSH_OPTIONS_GSSAPI_SERVER_IDENTITY,
68     GssapiClientIdentity = ssh_options_e.SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY,
69     GssapiDelegateCredentials = ssh_options_e.SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS,
70     HmacCS = ssh_options_e.SSH_OPTIONS_HMAC_C_S,
71     HmacSC = ssh_options_e.SSH_OPTIONS_HMAC_S_C,
72 }
73 
74 enum SSHProtocolVersion {
75     SSH1 = 1,
76     SSH2 = 2
77 }
78 
79 enum AuthState : int {
80     Success = ssh_auth_e.SSH_AUTH_SUCCESS,
81     Denied = ssh_auth_e.SSH_AUTH_DENIED,
82     Partial = ssh_auth_e.SSH_AUTH_PARTIAL,
83     Info = ssh_auth_e.SSH_AUTH_INFO,
84     Again = ssh_auth_e.SSH_AUTH_AGAIN,
85 }
86 
87 enum AuthMethod : int {
88     Unknown = SSH_AUTH_METHOD_UNKNOWN,
89     None = SSH_AUTH_METHOD_NONE,
90     Password = SSH_AUTH_METHOD_PASSWORD,
91     PublicKey = SSH_AUTH_METHOD_PUBLICKEY,
92     Hostbased = SSH_AUTH_METHOD_HOSTBASED,
93     Interactive = SSH_AUTH_METHOD_INTERACTIVE,
94     GSSAPIMic = SSH_AUTH_METHOD_GSSAPI_MIC,
95 }
96 
97 alias LogVerbosity = LogLevel;
98 alias GSSAPICreds = size_t;
99 
100 class SSHSession : Disposable {
101     alias AuthCallback = string delegate(string prompt, bool echo, bool verify);
102     alias MessageCallback = bool delegate(SSHSession session, SSHMessage message);
103     alias OpenRequestX11Callback = SSHChannel delegate(SSHSession session, string originatorAddres,
104         ushort originatorPort);
105     alias OnConnectStatusChangedCallback = void delegate(SSHSession session, float v);
106     alias OnLogCallback = void delegate(SSHSession session, LogLevel level, string msg);
107     alias OnGlobalRequestCallback = void delegate(SSHSession session, SSHMessage message);
108 
109     alias ServerAuthGSSAPIMicCallback = AuthState delegate(SSHSession session, string user, 
110         string principal);
111     alias ServerAuthNoneCallback = AuthState delegate(SSHSession session, string user);
112     alias ServerAuthPasswordCallback = AuthState delegate(SSHSession session, string user, 
113         string password);
114     alias ServerAuthPublicKeyCallback = AuthState delegate(SSHSession session, string user, 
115         SSHKey publicKey, PublicKeyState signatureState);
116     alias ServerChannelOpenRequestCallback = SSHChannel delegate(SSHSession session);
117     alias ServerServiceRequestCallback = bool delegate(SSHSession session, string service);
118     alias ServerGSSAPIAcceptSecCtxCallback = bool delegate(SSHSession session, string inputToken,
119         out string outputToken);
120     alias ServerGSSAPISelectOidCallback = string delegate(SSHSession session, string user,
121         string[] oids);
122 
123     @property OnConnectStatusChangedCallback onConnectStatusChangedCallback() {
124         return this._onConnectStatusChangedCallback;
125     }
126 
127     @property void onConnectStatusChangedCallback(OnConnectStatusChangedCallback cb) {
128         this._onConnectStatusChangedCallback = cb;
129         if (cb is null) {
130             this._sessionCallbacks.connect_status_function = null;
131         } else {
132             this._sessionCallbacks.connect_status_function = &nativeConnectStatusCallback;
133         }
134         ssh_set_callbacks(this._session, &this._sessionCallbacks);
135     }
136 
137     @property OnLogCallback onLogCallback() {
138         return this._onLogCallback;
139     }
140     
141     @property void onLogCallback(OnLogCallback cb) {
142         this._onLogCallback = cb;
143         if (cb is null) {
144             this._sessionCallbacks.log_function = null;
145         } else {
146             this._sessionCallbacks.log_function = &nativeLogFunction;
147         }
148         ssh_set_callbacks(this._session, &this._sessionCallbacks);
149     }
150 
151     @property OnGlobalRequestCallback onGlobalRequestCallback() {
152         return this._onGlobalRequestCallback;
153     }
154 
155     @property void onGlobalRequestCallback(OnGlobalRequestCallback cb) {
156         this._onGlobalRequestCallback = cb;
157         if (cb is null) {
158             this._sessionCallbacks.global_request_function = null;
159         } else {
160             this._sessionCallbacks.global_request_function = &nativeOnGlobalRequest;
161         }
162         ssh_set_callbacks(this._session, &this._sessionCallbacks);
163     }
164 
165     @property AuthCallback authCallback() {
166         return this._authCallback;
167     }
168 
169     @property void authCallback(AuthCallback cb) {
170         this._authCallback = cb;
171         if (cb is null) {
172             this._sessionCallbacks.auth_function = null;
173         } else {
174             this._sessionCallbacks.auth_function = &nativeAuthCallback;
175         }
176         ssh_set_callbacks(this._session, &this._sessionCallbacks);
177     }
178 
179     @property MessageCallback messageCallback() {
180         return this._messageCallback;
181     }
182 
183     @property void messageCallback(MessageCallback cb) {
184         this._messageCallback = cb;
185         if (cb is null) {
186             ssh_set_message_callback(this._session, null, null);
187         } else {
188             ssh_set_message_callback(this._session, &nativeOnMessageCallback, cast(void*) this);
189         }
190     }
191 
192     @property OpenRequestX11Callback openRequestX11Callback() {
193         return this._openRequestX11Callback;
194     }
195 
196     @property void openRequestX11Callback(OpenRequestX11Callback cb) {
197         this._openRequestX11Callback = cb;
198         if (cb is null) {
199             this._sessionCallbacks.channel_open_request_x11_function = null;
200         } else {
201             this._sessionCallbacks.channel_open_request_x11_function = &nativeOpenRequestX11Callback;
202         }
203         ssh_set_callbacks(this._session, &this._sessionCallbacks);
204     }
205 
206 
207     @property ServerAuthGSSAPIMicCallback serverAuthGSSAPIMicCallback() {
208         return this._serverAuthGSSAPIMicCallback;
209     }
210 
211     @property void serverAuthGSSAPIMicCallback(ServerAuthGSSAPIMicCallback cb) {
212         this._serverAuthGSSAPIMicCallback = cb;
213         if (cb is null) {
214             this._serverCallbacks.auth_gssapi_mic_function = null;
215         } else {
216             this._serverCallbacks.auth_gssapi_mic_function = &nativeServerAuthGSSAPIMicCallback;
217         }
218         ssh_set_server_callbacks(this._session, &this._serverCallbacks);
219     }
220 
221     @property ServerAuthNoneCallback serverAuthNoneCallback() {
222         return this._serverAuthNoneCallback;
223     }
224     
225     @property void serverAuthNoneCallback(ServerAuthNoneCallback cb) {
226         this._serverAuthNoneCallback = cb;
227         if (cb is null) {
228             this._serverCallbacks.auth_none_function = null;
229         } else {
230             this._serverCallbacks.auth_none_function = &nativeServerAuthNoneCallback;
231         }
232         ssh_set_server_callbacks(this._session, &this._serverCallbacks);
233     }
234 
235     @property ServerAuthPasswordCallback serverAuthPasswordCallback() {
236         return this._serverAuthPasswordCallback;
237     }
238     
239     @property void serverAuthPasswordCallback(ServerAuthPasswordCallback cb) {
240         this._serverAuthPasswordCallback = cb;
241         if (cb is null) {
242             this._serverCallbacks.auth_password_function = null;
243         } else {
244             this._serverCallbacks.auth_password_function = &nativeServerAuthPasswordCallback;
245         }
246         ssh_set_server_callbacks(this._session, &this._serverCallbacks);
247     }
248 
249     @property ServerAuthPublicKeyCallback serverAuthPublicKeyCallback() {
250         return this._serverAuthPublicKeyCallback;
251     }
252     
253     @property void serverAuthPublicKeyCallback(ServerAuthPublicKeyCallback cb) {
254         this._serverAuthPublicKeyCallback = cb;
255         if (cb is null) {
256             this._serverCallbacks.auth_pubkey_function = null;
257         } else {
258             this._serverCallbacks.auth_pubkey_function = &nativeServerAuthPublicKeyCallback;
259         }
260         ssh_set_server_callbacks(this._session, &this._serverCallbacks);
261     }
262 
263     @property ServerChannelOpenRequestCallback serverChannelOpenRequestCallback() {
264         return this._serverChannelOpenRequestCallback;
265     }
266 
267     @property void serverChannelOpenRequestCallback(ServerChannelOpenRequestCallback cb) {
268         this._serverChannelOpenRequestCallback = cb;
269         if (cb is null) {
270             this._serverCallbacks.channel_open_request_session_function = null;
271         } else {
272             this._serverCallbacks.channel_open_request_session_function = 
273                 &nativeServerChannelOpenRequestCallback;
274         }
275         ssh_set_server_callbacks(this._session, &this._serverCallbacks);
276     }
277 
278     @property ServerServiceRequestCallback serverServiceRequestCallback() {
279         return this._serverServiceRequestCallback;
280     }
281 
282     @property void serverServiceRequestCallback(ServerServiceRequestCallback cb) {
283         this._serverServiceRequestCallback = cb;
284         if (cb is null) {
285             this._serverCallbacks.service_request_function = null;
286         } else {
287             this._serverCallbacks.service_request_function = &nativeServerServiceRequestCallback;
288         }
289         ssh_set_server_callbacks(this._session, &this._serverCallbacks);
290     }
291 
292     @property ServerGSSAPIAcceptSecCtxCallback serverGSSAPIAcceptSecCtxCallback() {
293         return this._serverGSSAPIAcceptSecCtxCallback;
294     }
295 
296     @property void serverGSSAPIAcceptSecCtxCallback(ServerGSSAPIAcceptSecCtxCallback cb) {
297         this._serverGSSAPIAcceptSecCtxCallback = cb;
298         if (cb is null) {
299             this._serverCallbacks.gssapi_accept_sec_ctx_function = null;
300         } else {
301             this._serverCallbacks.gssapi_accept_sec_ctx_function = 
302                 &nativeServerGSSAPIAcceptSecCtxCallback;
303         }
304         ssh_set_server_callbacks(this._session, &this._serverCallbacks);
305     }
306 
307     @property ServerGSSAPISelectOidCallback serverGSSAPISelectOidCallback() {
308         return this._serverGSSAPISelectOidCallback;
309     }
310 
311     @property void serverGSSAPIVerifyMicCallback(ServerGSSAPISelectOidCallback cb) {
312         this._serverGSSAPISelectOidCallback = cb;
313         if (cb is null) {
314             this._serverCallbacks.gssapi_select_oid_function = null;
315         } else {
316             this._serverCallbacks.gssapi_select_oid_function = &nativeServerGSSAPISelectOidCallback;
317         }
318         ssh_set_server_callbacks(this._session, &this._serverCallbacks);
319     }
320 
321 
322     @property string cipherIn() {
323         return fromStrZ(ssh_get_cipher_in(this._session));
324     }
325 
326     @property string cipherOut() {
327         return fromStrZ(ssh_get_cipher_out(this._session));
328     }
329 
330     @property string hmacIn() {
331         return fromStrZ(ssh_get_hmac_in(this._session));
332     }
333 
334     @property string hmacOut() {
335         return fromStrZ(ssh_get_hmac_out(this._session));
336     }
337     
338     @property string kexAlgo() {
339         return fromStrZ(ssh_get_kex_algo(this._session));
340     }
341 
342     @property string clientBanner() {
343         return fromStrZ(ssh_get_clientbanner(this._session));
344     }
345 
346     @property string serverBanner() {
347         return fromStrZ(ssh_get_serverbanner(this._session));
348     }
349 
350     @property string disconnectMessage() {
351         return fromStrZ(ssh_get_disconnect_message(this._session));
352     }
353 
354     @property socket_t fd() {
355         return ssh_get_fd(this._session);
356     }
357 
358     @property string issueBanner() {
359         auto result = ssh_get_issue_banner(this._session);
360         scope(exit) ssh_string_free_char(result);
361         return copyFromStrZ(result);
362     }
363 
364     @property int openSSHVersion() {
365         return ssh_get_openssh_version(this._session);
366     }
367 
368     @property PollFlags pollFlags() {
369         return cast(PollFlags) ssh_get_poll_flags(this._session);
370     }
371 
372     @property SSHKey publicKey() {
373         ssh_key key;
374         auto rc = ssh_get_publickey(this._session, &key);
375         checkForRCError(rc, this._session);
376         return new SSHKey(key);
377     }
378 
379     @property SessionStatusFlags status() {
380         return cast(SessionStatusFlags) ssh_get_status(this._session);
381     }
382 
383     @property SSHProtocolVersion sshProtocolVersion() {
384         auto result = ssh_get_version(this._session);
385         if (result < 0) {
386             throw new SSHException(this._session);
387         }
388         return cast(SSHProtocolVersion) result;
389     }
390 
391     @property bool isBlocking() {
392         return ssh_is_blocking(this._session) == 0 ? false : true;
393     }
394 
395     @property void isBlocking(bool v) {
396         ssh_set_blocking(this._session, v ? 1 : 0);
397     }
398 
399     @property bool isConnected() {
400         return ssh_is_connected(this._session) == 0 ? false : true;
401     }
402 
403     @property ServerKnownState serverKnownState() {
404         auto result = ssh_is_server_known(this._session);
405         if (result == ssh_server_known_e.SSH_SERVER_ERROR) {
406             throw new SSHException(this._session);
407         }
408         return cast(ServerKnownState) result;
409     }
410 
411     @property string lastError() {
412         return copyFromStrZ(ssh_get_error(this._session));
413     }
414 
415     @property void authMethods(AuthMethod am) {
416         ssh_set_auth_methods(this._session, cast(int) am);
417     }
418 
419 
420     @property string host() {
421         return this.getOption(SessionOption.Host);
422     }
423 
424     @property void host(string s) {
425         this.setOption(SessionOption.Host, s);
426     }
427 
428     @property ushort port() {
429         uint value;
430         auto rc = ssh_options_get_port(this._session, &value);
431         checkForRCError(rc, this._session);
432         return cast(ushort) value;
433     }
434 
435     @property void port(ushort port) {
436         this.setOption(SessionOption.Port, cast(uint) port);
437     }
438 
439     @property void port(string port) {
440         this.setOption(SessionOption.PortStr, port);
441     }
442 
443     @property string user() {
444         return this.getOption(SessionOption.User);
445     }
446 
447     @property void user(string v) {
448         this.setOption(SessionOption.User, v);
449     }
450 
451     @property string identity() {
452         return this.getOption(SessionOption.Identity);
453     }
454     
455     @property void identity(string v) {
456         this.setOption(SessionOption.Identity, v);
457     }
458 
459     @property string proxyCommand() {
460         return this.getOption(SessionOption.ProxyCommand);
461     }
462     
463     @property void proxyCommand(string v) {
464         this.setOption(SessionOption.ProxyCommand, v);
465     }
466 
467     @property void fd(socket_t v) {
468         this.setOption(SessionOption.Fd, v);
469     }
470 
471     @property void bindAddr(string v) {
472         this.setOption(SessionOption.BindAddr, v);
473     }
474 
475     @property void sshDir(string v) {
476         this.setOption(SessionOption.SshDir, v);
477     }
478 
479     @property void knownHosts(string v) {
480         this.setOption(SessionOption.KnownHosts, v);
481     }
482 
483     @property void timeout(long v) {
484         this.setOption(SessionOption.Timeout, v);
485     }
486 
487     @property void timeoutUSec(long v) {
488         this.setOption(SessionOption.TimeoutUsec, v);
489     }
490 
491     @property void allowSSH1(bool v) {
492         this.setOption(SessionOption.Ssh1, v);
493     }
494 
495     @property void allowSSH2(bool v) {
496         this.setOption(SessionOption.Ssh2, v);
497     }
498 
499     @property void logVerbosity(LogVerbosity v) {
500         this.setOption(SessionOption.LogVerbosity, cast(int) v);
501     }
502 
503     @property void ciphersCS(string[] v) {
504         this.setOption(SessionOption.CiphersCS, v);
505     }
506 
507     @property void ciphersCS(string v) {
508         this.setOption(SessionOption.CiphersCS, v);
509     }
510 
511     @property void ciphersSC(string[] v) {
512         this.setOption(SessionOption.CiphersSC, v);
513     }
514     
515     @property void ciphersSC(string v) {
516         this.setOption(SessionOption.CiphersSC, v);
517     }
518 
519     @property void keyExchange(string[] v) {
520         this.setOption(SessionOption.KeyExchange, v);
521     }
522     
523     @property void keyExchange(string v) {
524         this.setOption(SessionOption.KeyExchange, v);
525     }
526 
527     @property void hostkeys(string[] v) {
528         this.setOption(SessionOption.Hostkeys, v);
529     }
530     
531     @property void hostkeys(string v) {
532         this.setOption(SessionOption.Hostkeys, v);
533     }
534 
535     @property void compressionCS(bool v) {
536         this.setOption(SessionOption.CompressionCS, v ? "yes" : "no");
537     }
538 
539     @property void compressionCS(string[] v) {
540         this.setOption(SessionOption.CompressionCS, v);
541     }
542     
543     @property void compressionCS(string v) {
544         this.setOption(SessionOption.CompressionCS, v);
545     }
546 
547     @property void compressionSC(bool v) {
548         this.setOption(SessionOption.CompressionSC, v ? "yes" : "no");
549     }
550 
551     @property void compressionSC(string[] v) {
552         this.setOption(SessionOption.CompressionSC, v);
553     }
554     
555     @property void compressionSC(string v) {
556         this.setOption(SessionOption.CompressionSC, v);
557     }
558 
559     @property void compression(bool v) {
560         this.setOption(SessionOption.Compression, v ? "yes" : "no");
561     }
562     
563     @property void compression(string[] v) {
564         this.setOption(SessionOption.Compression, v);
565     }
566     
567     @property void compression(string v) {
568         this.setOption(SessionOption.Compression, v);
569     }
570 
571     @property void compressonLevel(int v) {
572         assert(v >= 1 && v <= 9);
573         this.setOption(SessionOption.CompressionLevel, v);
574     }
575 
576     @property strictHostkeyCheck(bool v) {
577         this.setOption(SessionOption.StrictHostkeyCheck, v);
578     }
579 
580     @property gssapiServerIdentity(string v) {
581         this.setOption(SessionOption.GssapiServerIdentity, v);
582     }
583 
584     @property gssapiClientIdentity(string v) {
585         this.setOption(SessionOption.GssapiClientIdentity, v);
586     }
587 
588     @property gssapiDelegateCredentials(bool v) {
589         this.setOption(SessionOption.GssapiDelegateCredentials, v);
590     }
591 
592 
593     version (LIBSSH_WITH_GSSAPI) {
594         @property GSSAPICreds gssapiCreds() {
595             return cast(GSSAPICreds) ssh_gssapi_get_creds(this._session);
596         }
597         
598         @property void gssapiCreds(GSSAPICreds v) {
599             ssh_gssapi_set_creds(this._session, cast(ssh_gssapi_creds) v);
600         }
601     }
602 
603 
604     this() {
605         auto newSession = ssh_new();
606         checkForNullError(newSession, "Error while creating session object");
607         this(newSession);
608     }
609 
610     ~this() {
611         this._dispose(true);
612     }
613 
614     override void dispose() {
615         this._dispose(false);
616     }
617 
618     /**
619      * Returns false  if the session is in nonblocking mode, and call must be done again.
620      **/
621     bool connect() {
622         auto rc = ssh_connect(this._session);
623         if (rc == SSH_AGAIN) {
624             return false;
625         }
626         checkForRCError(rc, this._session);
627         return true;
628     }
629 
630     void disconnect() {
631         ssh_disconnect(this._session);
632     }
633 
634     /**
635      * Returns false on timeout
636      **/
637     bool blockingFlush(int timeout) {
638         auto rc = ssh_blocking_flush(this._session, timeout);
639         if (rc == SSH_AGAIN) {
640             return false;
641         }
642         checkForRCError(rc, this._session);
643         return true;
644     }
645 
646     SSHSession getCopy() {
647         ssh_session newSession;
648         auto rc = ssh_options_copy(this._session, &newSession);
649         checkForRCError(rc, this._session);
650         return new SSHSession(newSession);
651     }
652 
653     string getOption(SessionOption type) {
654         char* value;
655         auto rc = ssh_options_get(this._session, cast(ssh_options_e) type, &value);
656         checkForRCError(rc, this._session);
657         scope(exit) ssh_string_free_char(value);
658         return copyFromStrZ(value);
659     }
660 
661     void setOption(T)(SessionOption type, T value) {
662         auto rc = ssh_options_set(this._session, cast(ssh_options_e) type, &value);
663         checkForRCError(rc, this._session);
664     }
665 
666     void setOption(SessionOption type, string value) {
667         auto rc = ssh_options_set(this._session, cast(ssh_options_e) type, toStrZ(value));
668         checkForRCError(rc, this._session);
669     }
670 
671     void setOption(SessionOption type, bool value) {
672         int intValue = value ? 1 : 0;
673         auto rc = ssh_options_set(this._session, cast(ssh_options_e) type, &intValue);
674         checkForRCError(rc, this._session);
675     }
676 
677     void setOption(SessionOption type, string[] value) {
678         auto rc = ssh_options_set(this._session, cast(ssh_options_e) type, toStrZ(join(value, ",")));
679         checkForRCError(rc, this._session);
680     }
681 
682     void parseConfig(string fileName) {
683         auto rc = ssh_options_parse_config(this._session, toStrZ(fileName));
684         checkForRCError(rc, this._session);
685     }
686 
687     void sendDebug(string message, bool alwaysDisplay) {
688         auto rc = ssh_send_debug(this._session, toStrZ(message), alwaysDisplay ? 1 : 0);
689         checkForRCError(rc, this._session);
690     }
691 
692     void sendIgnore(string message) {
693         auto rc = ssh_send_ignore(this._session, toStrZ(message));
694         checkForRCError(rc, this._session);
695     }
696 
697     void setFdExcept() {
698         ssh_set_fd_except(this._session);
699     }
700 
701     void setFdToRead() {
702         ssh_set_fd_toread(this._session);
703     }
704 
705     void setFdToWrite() {
706         ssh_set_fd_towrite(this._session);
707     }
708 
709     void silentDisconnect() {
710         ssh_silent_disconnect(this._session);
711     }
712 
713     void writeKnownHost() {
714         auto rc = ssh_write_knownhost(this._session);
715         checkForRCError(rc, this._session);
716     }
717 
718     SSHChannel newChannel() {
719         auto result = ssh_channel_new(this._session);
720         checkForNullError(result, this._session);
721 
722         return new SSHChannel(this, result);
723     }
724 
725     SSHMessage getMessage() {
726         auto result = ssh_message_get(this._session);
727         if (result is null) {
728             return null;
729         }
730         return new SSHMessage(this, result);
731     }
732 
733 
734     AuthMethod userauthList(string username) {
735         return cast(AuthMethod) ssh_userauth_list(this._session, toStrZ(username));
736     }
737 
738     AuthState userauthNone(string username) {
739         auto rc = ssh_userauth_none(this._session, toStrZ(username));
740         if (rc == ssh_auth_e.SSH_AUTH_ERROR) {
741             throw new SSHException(this._session);
742         }
743         return cast(AuthState) rc;
744     }
745 
746     AuthState userauthPassword(string username, string password) {
747         auto rc = ssh_userauth_password(this._session, toStrZ(username), toStrZ(password));
748         if (rc == ssh_auth_e.SSH_AUTH_ERROR) {
749             throw new SSHException(this._session);
750         }
751         return cast(AuthState) rc;
752     }
753 
754     AuthState userauthPublicKey(string username, const SSHKey privateKey) {
755         assert(privateKey !is null);
756 
757         auto rc = ssh_userauth_publickey(this._session, toStrZ(username), privateKey._key);
758         if (rc == ssh_auth_e.SSH_AUTH_ERROR) {
759             throw new SSHException(this._session);
760         }
761         return cast(AuthState) rc;
762     }
763 
764     AuthState userauthTryPublicKey(string username, const SSHKey publicKey) {
765         assert(publicKey !is null);
766         
767         auto rc = ssh_userauth_publickey(this._session, toStrZ(username), publicKey._key);
768         if (rc == ssh_auth_e.SSH_AUTH_ERROR) {
769             throw new SSHException(this._session);
770         }
771         return cast(AuthState) rc;
772     }
773 
774     AuthState userauthPublicKeyAuto(string username, string passPhrase) {
775         auto rc = ssh_userauth_publickey_auto(this._session, toStrZ(username), toStrZ(passPhrase));
776         if (rc == ssh_auth_e.SSH_AUTH_ERROR) {
777             throw new SSHException(this._session);
778         }
779         return cast(AuthState) rc;
780     }
781 
782     version(Windows) { } else {
783         AuthState userauthAgent(string username) {
784             auto rc = ssh_userauth_agent(this._session, toStrZ(username));
785             if (rc == ssh_auth_e.SSH_AUTH_ERROR) {
786                 throw new SSHException(this._session);
787             }
788             return cast(AuthState) rc;
789         }
790     }
791 
792     AuthState userauthGSSAPI() {
793         auto rc = ssh_userauth_gssapi(this._session);
794         if (rc == ssh_auth_e.SSH_AUTH_ERROR) {
795             throw new SSHException(this._session);
796         }
797         return cast(AuthState) rc;
798     }
799 
800     AuthState userauthKeyboardInteractive(string username) {
801         auto rc = ssh_userauth_kbdint(this._session, toStrZ(username), null);
802         if (rc == ssh_auth_e.SSH_AUTH_ERROR) {
803             throw new SSHException(this._session);
804         }
805         return cast(AuthState) rc;
806     }
807 
808     int userauthKeyboardInteractiveGetNAnswers() {
809         auto result = ssh_userauth_kbdint_getnanswers(this._session);
810         if (result == SSH_ERROR) {
811             throw new SSHException(this._session);
812         }
813         return result;
814     }
815 
816     string userauthKeyboardInteractiveGetAnswer(uint i) {
817         auto result = ssh_userauth_kbdint_getanswer(this._session, i);
818         if (result is null) {
819             throw new SSHException(this._session);
820         }
821         return fromStrZ(result);
822     }
823 
824     int userauthKeyboardInteractiveGetNPrompts() {
825         auto result = ssh_userauth_kbdint_getnprompts(this._session);
826         if (result == SSH_ERROR) {
827             throw new SSHException(this._session);
828         }
829         return result;
830     }
831 
832     string userauthKeyboardInteractiveGetPrompt(uint i, out bool echo) {
833         char echoChar;
834         auto result = ssh_userauth_kbdint_getprompt(this._session, i, &echoChar);
835         if (result is null) {
836             throw new SSHException(this._session);
837         }
838         echo = echoChar == 0 ? false : true;
839         return fromStrZ(result);
840     }
841 
842     string userauthKeyboardInteractiveGetPrompt(uint i) {
843         auto result = ssh_userauth_kbdint_getprompt(this._session, i, null);
844         if (result is null) {
845             throw new SSHException(this._session);
846         }
847         return fromStrZ(result);
848     }
849 
850     string userauthKeyboardInteractiveGetInstruction() {
851         auto result = ssh_userauth_kbdint_getinstruction(this._session);
852         if (result is null) {
853             throw new SSHException(this._session);
854         }
855         return fromStrZ(result);
856     }
857 
858     string userauthKeyboardInteractiveGetName() {
859         auto result = ssh_userauth_kbdint_getname(this._session);
860         if (result is null) {
861             throw new SSHException(this._session);
862         }
863         return fromStrZ(result);
864     }
865 
866     void userauthKeyboardInteractiveSetAnswer(uint i, string answer) {
867         auto result = ssh_userauth_kbdint_setanswer(this._session, i, toStrZ(answer));
868         checkForRCError(result, this._session);
869     }
870 
871 
872     SSHChannel acceptForward(int timeoutMs, out ushort destPort) {
873         int destPortInt;
874         auto result = ssh_channel_accept_forward(this._session, timeoutMs, &destPortInt);
875         if (result is null) {
876             return null;
877         }
878         destPort = cast(ushort) destPortInt;
879         return new SSHChannel(this, result);
880     }
881 
882     SSHChannel acceptForward(int timeoutMs) {
883         auto result = ssh_channel_accept_forward(this._session, timeoutMs, null);
884         if (result is null) {
885             return null;
886         }
887         return new SSHChannel(this, result);
888     }
889 
890     /**
891      * return false if in nonblocking mode and call has to be done again.
892      * */
893     bool listenForward(string address, ushort port, out ushort boundPort) {
894         int boundPortInt;
895         auto rc = ssh_channel_listen_forward(this._session, toStrZ(address), port, &boundPortInt);
896         if (rc == SSH_AGAIN) {
897             return false;
898         }
899         checkForRCError(rc, this._session);
900         boundPort = cast(ushort) boundPortInt;
901         return true;
902     }
903 
904     void cancelForward(string address, ushort port) {
905         auto rc = ssh_channel_cancel_forward(this._session, toStrZ(address), port);
906         checkForRCError(rc, this._session);
907     }
908 
909 
910     SSHSCP newScp(SCPMode mode, string location) {
911         auto result = ssh_scp_new(this._session, mode, toStrZ(location));
912         checkForNullError(result, this._session);
913         return new SSHSCP(this, result);
914     }
915 
916     SFTPSession newSFTP() {
917         auto result = sftp_new(this._session);
918         checkForNullError(result, this._session);
919         return new SFTPSession(this, result);
920     }
921 
922     SFTPSession newSFTP(SSHChannel channel) {
923         auto result = sftp_new_channel(this._session, channel._channel);
924         checkForNullError(result, this._session);
925         return new SFTPSession(this, result);
926     }
927 
928     version (LIBSSH_WITH_SERVER) {
929         SFTPSession newSFTPServer(SSHChannel channel) {
930             auto result = sftp_server_new(this._session, channel._channel);
931             mixin CheckForNullError!(result, this._session);
932             return new SFTPSession(this, result);
933         }
934     }
935 
936 
937     void handleKeyExchange() {
938         auto rc = ssh_handle_key_exchange(this._session);
939         checkForRCError(rc, this._session);
940     }
941 
942     package {        
943         this(ssh_session session) {
944             this._session = session;
945             
946             ssh_callbacks_init(this._sessionCallbacks);
947             this._sessionCallbacks.userdata = cast(void*) this;
948 //
949             ssh_callbacks_init(this._serverCallbacks);
950             this._serverCallbacks.userdata = cast(void*) this;
951             
952             this._authCallback = null;
953         }
954 
955         ssh_session _session;
956 
957         void registerChannel(SSHChannel ch) {
958             this._channels ~= ch;
959         }
960 
961         void freeChannel(SSHChannel toDel) {
962             this._channels = remove!(a => a == toDel)(this._channels);
963         }
964     }
965 
966     private {
967         ssh_callbacks_struct _sessionCallbacks;
968         ssh_server_callbacks_struct _serverCallbacks;
969 
970         SSHChannel[] _channels = [];
971 
972         OnConnectStatusChangedCallback _onConnectStatusChangedCallback;
973         OnLogCallback _onLogCallback;
974         OnGlobalRequestCallback _onGlobalRequestCallback;
975         AuthCallback _authCallback;
976         MessageCallback _messageCallback;
977         OpenRequestX11Callback _openRequestX11Callback;
978 
979         ServerAuthGSSAPIMicCallback _serverAuthGSSAPIMicCallback;
980         ServerAuthNoneCallback _serverAuthNoneCallback;
981         ServerAuthPasswordCallback _serverAuthPasswordCallback;
982         ServerAuthPublicKeyCallback _serverAuthPublicKeyCallback;
983         ServerChannelOpenRequestCallback _serverChannelOpenRequestCallback;
984         ServerServiceRequestCallback _serverServiceRequestCallback;
985         ServerGSSAPIAcceptSecCtxCallback _serverGSSAPIAcceptSecCtxCallback;
986         ServerGSSAPISelectOidCallback _serverGSSAPISelectOidCallback;
987 
988         void _dispose(bool fromDtor) {
989             if (this._session !is null) {
990                 foreach (channel; this._channels) {
991                     channel.dispose();
992                 }
993                 this._channels = null;
994 
995                 ssh_free(this._session);
996                 this._session = null;
997             }
998         }
999     }
1000 }
1001 
1002 bool sshFinalize() {
1003     auto rc = ssh_finalize();
1004     return rc == SSH_OK ? true : false;
1005 }
1006 
1007 private {
1008     extern(C) void nativeConnectStatusCallback(void* userdata, float status) {
1009         auto session = cast(SSHSession) userdata;
1010 
1011         if (session is null || session._onConnectStatusChangedCallback) {
1012             return;
1013         }
1014 
1015         session._onConnectStatusChangedCallback(session, status);
1016     }
1017 
1018     extern(C) void nativeLogFunction(ssh_session session, int priority, const char* message, 
1019             void* userdata) {
1020         auto sessionObj = cast(SSHSession) userdata;
1021 
1022         if (sessionObj is null || sessionObj._onLogCallback is null) {
1023             return;
1024         }
1025 
1026         sessionObj._onLogCallback(sessionObj, cast(LogLevel) priority, fromStrZ(message));
1027     }
1028 
1029     extern(C) int nativeAuthCallback(const char *prompt, char *buf, size_t len,
1030             int echo, int verify, void *userdata) {
1031         auto session = cast(SSHSession) userdata;
1032 
1033         if (session is null || session._authCallback is null) {
1034             return SSH_ERROR;
1035         }
1036 
1037         try {
1038             auto result = session._authCallback(fromStrZ(prompt), echo == 0 ? false : true, 
1039                 verify == 0 ? false : true);
1040             if (result is null) {
1041                 return SSH_ERROR;
1042             }
1043 
1044             if (len < result.length + 1) {
1045                 return SSH_ERROR;
1046             }
1047 
1048             import core.stdc..string : memcpy;
1049             memcpy(buf, result.ptr, result.length);
1050             buf[result.length] = 0;
1051 
1052             return SSH_OK;
1053         } catch (Exception) {
1054             return SSH_ERROR;
1055         }
1056     }
1057 
1058     extern(C) void nativeOnGlobalRequest(ssh_session, ssh_message message, void* userdata) {
1059         auto sessionObj = cast(SSHSession) userdata;
1060 
1061         if (sessionObj is null || sessionObj._onGlobalRequestCallback is null) {
1062             return;
1063         }
1064 
1065         auto messageObj = new SSHMessage(sessionObj, message);
1066         scope(exit) messageObj.dispose();
1067         sessionObj._onGlobalRequestCallback(sessionObj, messageObj);
1068     }
1069 
1070     extern(C) int nativeOnMessageCallback(ssh_session session, ssh_message message, 
1071             void* userdata) {
1072         auto sessionObj = cast(SSHSession) userdata;
1073 
1074         if (sessionObj is null || sessionObj._messageCallback is null) {
1075             return SSH_ERROR;
1076         }
1077 
1078         auto messageObj = new SSHMessage(sessionObj, message);
1079         scope(exit) messageObj.dispose();
1080 
1081         try {
1082             auto result = sessionObj._messageCallback(sessionObj, messageObj);
1083             if (!result)
1084                 return SSH_ERROR;
1085             return SSH_OK;
1086         } catch (Exception) {
1087             return SSH_ERROR;
1088         }
1089             
1090     }
1091 
1092     extern(C) ssh_channel nativeOpenRequestX11Callback(ssh_session session, 
1093             const char* originator_address, int originator_port, void* userdata) {
1094         auto sessionObj = cast(SSHSession) userdata;
1095 
1096         if (sessionObj is null || sessionObj._openRequestX11Callback is null) {
1097             return null;
1098         }
1099 
1100         try {
1101             auto result = sessionObj._openRequestX11Callback(sessionObj, 
1102                 fromStrZ(originator_address), cast(ushort) originator_port);
1103             if (result is null) {
1104                 return null;
1105             }
1106             return result._channel;
1107         } catch (Exception) {
1108             return null;
1109         }
1110     }
1111 
1112     extern(C) int nativeServerAuthGSSAPIMicCallback(ssh_session, const char* user,
1113             const char* principal, void* userdata) {
1114         auto sessionObj = cast(SSHSession) userdata;
1115         
1116         if (sessionObj is null || sessionObj._serverAuthGSSAPIMicCallback is null) {
1117             return SSH_ERROR;
1118         }
1119 
1120         try {
1121             return cast(int) sessionObj._serverAuthGSSAPIMicCallback(sessionObj, 
1122                 fromStrZ(user), fromStrZ(principal));
1123         } catch (Exception) {
1124             return SSH_ERROR;
1125         }
1126     }
1127 
1128     extern(C) int nativeServerAuthNoneCallback(ssh_session, const char* user,
1129             void* userdata) {
1130         auto sessionObj = cast(SSHSession) userdata;
1131         
1132         if (sessionObj is null || sessionObj._serverAuthNoneCallback is null) {
1133             return SSH_ERROR;
1134         }
1135         
1136         try {
1137             return cast(int) sessionObj._serverAuthNoneCallback(sessionObj, 
1138                 fromStrZ(user));
1139         } catch (Exception) {
1140             return SSH_ERROR;
1141         }
1142     }
1143 
1144     extern(C) int nativeServerAuthPasswordCallback(ssh_session, const char* user,
1145             const char* password, void* userdata) {
1146         auto sessionObj = cast(SSHSession) userdata;
1147         
1148         if (sessionObj is null || sessionObj._serverAuthPasswordCallback is null) {
1149             return SSH_ERROR;
1150         }
1151         
1152         try {
1153             return cast(int) sessionObj._serverAuthPasswordCallback(sessionObj, 
1154                 fromStrZ(user), fromStrZ(password));
1155         } catch (Exception) {
1156             return SSH_ERROR;
1157         }
1158     }
1159 
1160     extern(C) int nativeServerAuthPublicKeyCallback(ssh_session, const char* user,
1161             ssh_key_struct* key, byte signatureState, void* userdata) {
1162         auto sessionObj = cast(SSHSession) userdata;
1163         
1164         if (sessionObj is null || sessionObj._serverAuthPublicKeyCallback is null) {
1165             return SSH_ERROR;
1166         }
1167         
1168         try {
1169             return cast(int) sessionObj._serverAuthPublicKeyCallback(sessionObj, 
1170                 fromStrZ(user), new SSHKey(key), cast(PublicKeyState) signatureState);
1171         } catch (Exception) {
1172             return SSH_ERROR;
1173         }
1174     }
1175 
1176     extern(C) ssh_channel nativeServerChannelOpenRequestCallback(ssh_session, void* userdata) {
1177         auto sessionObj = cast(SSHSession) userdata;
1178 
1179         if (sessionObj is null || sessionObj._serverChannelOpenRequestCallback is null) {
1180             return null;
1181         }
1182 
1183         try {
1184             auto result = sessionObj._serverChannelOpenRequestCallback(sessionObj);
1185             if (result is null) {
1186                 return null;
1187             }
1188             return result._channel;
1189         } catch(Exception) {
1190             return null;
1191         }
1192     }
1193 
1194     extern(C) int nativeServerServiceRequestCallback(ssh_session, const char* service, 
1195             void* userdata) {
1196         auto sessionObj = cast(SSHSession) userdata;
1197 
1198         if (sessionObj is null || sessionObj._serverServiceRequestCallback is null) {
1199             return SSH_ERROR;
1200         }
1201 
1202         try {
1203             if (sessionObj._serverServiceRequestCallback(sessionObj, fromStrZ(service))) {
1204                 return SSH_OK;
1205             } else {
1206                 return SSH_ERROR;
1207             }
1208         } catch(Exception) {
1209             return SSH_ERROR;
1210         }
1211     }
1212 
1213     extern(C) int nativeServerGSSAPIAcceptSecCtxCallback(ssh_session session, 
1214             ssh_string input_token, ssh_string *output_token, void *userdata) {
1215         auto sessionObj = cast(SSHSession) userdata;
1216         
1217         if (sessionObj is null || sessionObj._serverGSSAPIAcceptSecCtxCallback is null) {
1218             return SSH_ERROR;
1219         }
1220         
1221         try {
1222             auto inputTokenData = ssh_string_data(input_token);
1223             auto inputTokenLen = ssh_string_len(input_token);
1224             string inputTokenStr = (cast(immutable(char)*) inputTokenData)[0 .. inputTokenLen];
1225             string outputTokenStr;
1226 
1227             auto result = sessionObj._serverGSSAPIAcceptSecCtxCallback(sessionObj, inputTokenStr,
1228                 outputTokenStr);
1229             if (outputTokenStr is null) {
1230                 *output_token = null;
1231             } else {
1232                 *output_token = ssh_string_new(outputTokenStr.length);
1233                 ssh_string_fill(*output_token, outputTokenStr.ptr, outputTokenStr.length);
1234             }
1235 
1236             if (result) {
1237                 return SSH_OK;
1238             } else {
1239                 return SSH_ERROR;
1240             }
1241         } catch(Exception) {
1242             return SSH_ERROR;
1243         }
1244     }
1245 
1246     extern(C) ssh_string nativeServerGSSAPISelectOidCallback(ssh_session session, const char* user,
1247             int n_oid, ssh_string* oids, void* userdata) {
1248         auto sessionObj = cast(SSHSession) userdata;
1249         
1250         if (sessionObj is null || sessionObj._serverGSSAPISelectOidCallback is null) {
1251             string[] oidsArr = new string[n_oid];
1252             for (auto i = 0; i < n_oid; i++) {
1253                 auto oidData = ssh_string_data(oids[i]);
1254                 auto oidLen = ssh_string_len(oids[i]);
1255                 oidsArr[i] = (cast(immutable(char)*) oidData)[0 .. oidLen];
1256             }
1257             auto result = sessionObj._serverGSSAPISelectOidCallback(sessionObj,
1258                 fromStrZ(user), oidsArr);
1259             if (result is null) {
1260                 return null;
1261             }
1262 
1263             for (auto i = 0; i < n_oid; i++) {
1264                 if (oidsArr[i] == result) {
1265                     return oids[i];
1266                 }
1267             }
1268 
1269             return null;
1270         }
1271         
1272         try {
1273             return null;
1274         } catch(Exception) {
1275             return null;
1276         }
1277     }
1278 }