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 = &nativeSessionAuthCallback;
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 	version(Windows)
606         {
607             if (!is_ssh_initialized())
608                 ssh_init();
609         }
610         auto newSession = ssh_new();
611         checkForNullError(newSession, "Error while creating session object");
612         this(newSession);
613     }
614 
615     ~this() {
616         this._dispose(true);
617     }
618 
619     override void dispose() {
620         this._dispose(false);
621     }
622 
623     /**
624      * Returns false  if the session is in nonblocking mode, and call must be done again.
625      **/
626     bool connect() {
627         auto rc = ssh_connect(this._session);
628         if (rc == SSH_AGAIN) {
629             return false;
630         }
631         checkForRCError(rc, this._session);
632         return true;
633     }
634 
635     void disconnect() {
636         ssh_disconnect(this._session);
637     }
638 
639     /**
640      * Returns false on timeout
641      **/
642     bool blockingFlush(int timeout) {
643         auto rc = ssh_blocking_flush(this._session, timeout);
644         if (rc == SSH_AGAIN) {
645             return false;
646         }
647         checkForRCError(rc, this._session);
648         return true;
649     }
650 
651     SSHSession getCopy() {
652         ssh_session newSession;
653         auto rc = ssh_options_copy(this._session, &newSession);
654         checkForRCError(rc, this._session);
655         return new SSHSession(newSession);
656     }
657 
658     string getOption(SessionOption type) {
659         char* value;
660         auto rc = ssh_options_get(this._session, cast(ssh_options_e) type, &value);
661         checkForRCError(rc, this._session);
662         scope(exit) ssh_string_free_char(value);
663         return copyFromStrZ(value);
664     }
665 
666     void setOption(T)(SessionOption type, T value) {
667         auto rc = ssh_options_set(this._session, cast(ssh_options_e) type, &value);
668         checkForRCError(rc, this._session);
669     }
670 
671     void setOption(SessionOption type, string value) {
672         auto rc = ssh_options_set(this._session, cast(ssh_options_e) type, toStrZ(value));
673         checkForRCError(rc, this._session);
674     }
675 
676     void setOption(SessionOption type, bool value) {
677         int intValue = value ? 1 : 0;
678         auto rc = ssh_options_set(this._session, cast(ssh_options_e) type, &intValue);
679         checkForRCError(rc, this._session);
680     }
681 
682     void setOption(SessionOption type, string[] value) {
683         auto rc = ssh_options_set(this._session, cast(ssh_options_e) type, toStrZ(join(value, ",")));
684         checkForRCError(rc, this._session);
685     }
686 
687     void parseConfig(string fileName) {
688         auto rc = ssh_options_parse_config(this._session, toStrZ(fileName));
689         checkForRCError(rc, this._session);
690     }
691 
692     void sendDebug(string message, bool alwaysDisplay) {
693         auto rc = ssh_send_debug(this._session, toStrZ(message), alwaysDisplay ? 1 : 0);
694         checkForRCError(rc, this._session);
695     }
696 
697     void sendIgnore(string message) {
698         auto rc = ssh_send_ignore(this._session, toStrZ(message));
699         checkForRCError(rc, this._session);
700     }
701 
702     void setFdExcept() {
703         ssh_set_fd_except(this._session);
704     }
705 
706     void setFdToRead() {
707         ssh_set_fd_toread(this._session);
708     }
709 
710     void setFdToWrite() {
711         ssh_set_fd_towrite(this._session);
712     }
713 
714     void silentDisconnect() {
715         ssh_silent_disconnect(this._session);
716     }
717 
718     void writeKnownHost() {
719         auto rc = ssh_write_knownhost(this._session);
720         checkForRCError(rc, this._session);
721     }
722 
723     SSHChannel newChannel() {
724         auto result = ssh_channel_new(this._session);
725         checkForNullError(result, this._session);
726 
727         return new SSHChannel(this, result);
728     }
729 
730     SSHMessage getMessage() {
731         auto result = ssh_message_get(this._session);
732         if (result is null) {
733             return null;
734         }
735         return new SSHMessage(this, result);
736     }
737 
738 
739     AuthMethod userauthList(string username) {
740         return cast(AuthMethod) ssh_userauth_list(this._session, toStrZ(username));
741     }
742 
743     AuthState userauthNone(string username) {
744         auto rc = ssh_userauth_none(this._session, toStrZ(username));
745         if (rc == ssh_auth_e.SSH_AUTH_ERROR) {
746             throw new SSHException(this._session);
747         }
748         return cast(AuthState) rc;
749     }
750 
751     AuthState userauthPassword(string username, string password) {
752         auto rc = ssh_userauth_password(this._session, toStrZ(username), toStrZ(password));
753         if (rc == ssh_auth_e.SSH_AUTH_ERROR) {
754             throw new SSHException(this._session);
755         }
756         return cast(AuthState) rc;
757     }
758 
759     AuthState userauthPublicKey(string username, const SSHKey privateKey) {
760         assert(privateKey !is null);
761 
762         auto rc = ssh_userauth_publickey(this._session, toStrZ(username), privateKey._key);
763         if (rc == ssh_auth_e.SSH_AUTH_ERROR) {
764             throw new SSHException(this._session);
765         }
766         return cast(AuthState) rc;
767     }
768 
769     AuthState userauthTryPublicKey(string username, const SSHKey publicKey) {
770         assert(publicKey !is null);
771         
772         auto rc = ssh_userauth_publickey(this._session, toStrZ(username), publicKey._key);
773         if (rc == ssh_auth_e.SSH_AUTH_ERROR) {
774             throw new SSHException(this._session);
775         }
776         return cast(AuthState) rc;
777     }
778 
779     AuthState userauthPublicKeyAuto(string username, string passPhrase) {
780         auto rc = ssh_userauth_publickey_auto(this._session, toStrZ(username), toStrZ(passPhrase));
781         if (rc == ssh_auth_e.SSH_AUTH_ERROR) {
782             throw new SSHException(this._session);
783         }
784         return cast(AuthState) rc;
785     }
786 
787     version(Windows) { } else {
788         AuthState userauthAgent(string username) {
789             auto rc = ssh_userauth_agent(this._session, toStrZ(username));
790             if (rc == ssh_auth_e.SSH_AUTH_ERROR) {
791                 throw new SSHException(this._session);
792             }
793             return cast(AuthState) rc;
794         }
795     }
796 
797     AuthState userauthGSSAPI() {
798         auto rc = ssh_userauth_gssapi(this._session);
799         if (rc == ssh_auth_e.SSH_AUTH_ERROR) {
800             throw new SSHException(this._session);
801         }
802         return cast(AuthState) rc;
803     }
804 
805     AuthState userauthKeyboardInteractive(string username) {
806         auto rc = ssh_userauth_kbdint(this._session, toStrZ(username), null);
807         if (rc == ssh_auth_e.SSH_AUTH_ERROR) {
808             throw new SSHException(this._session);
809         }
810         return cast(AuthState) rc;
811     }
812 
813     int userauthKeyboardInteractiveGetNAnswers() {
814         auto result = ssh_userauth_kbdint_getnanswers(this._session);
815         if (result == SSH_ERROR) {
816             throw new SSHException(this._session);
817         }
818         return result;
819     }
820 
821     string userauthKeyboardInteractiveGetAnswer(uint i) {
822         auto result = ssh_userauth_kbdint_getanswer(this._session, i);
823         if (result is null) {
824             throw new SSHException(this._session);
825         }
826         return fromStrZ(result);
827     }
828 
829     int userauthKeyboardInteractiveGetNPrompts() {
830         auto result = ssh_userauth_kbdint_getnprompts(this._session);
831         if (result == SSH_ERROR) {
832             throw new SSHException(this._session);
833         }
834         return result;
835     }
836 
837     string userauthKeyboardInteractiveGetPrompt(uint i, out bool echo) {
838         char echoChar;
839         auto result = ssh_userauth_kbdint_getprompt(this._session, i, &echoChar);
840         if (result is null) {
841             throw new SSHException(this._session);
842         }
843         echo = echoChar == 0 ? false : true;
844         return fromStrZ(result);
845     }
846 
847     string userauthKeyboardInteractiveGetPrompt(uint i) {
848         auto result = ssh_userauth_kbdint_getprompt(this._session, i, null);
849         if (result is null) {
850             throw new SSHException(this._session);
851         }
852         return fromStrZ(result);
853     }
854 
855     string userauthKeyboardInteractiveGetInstruction() {
856         auto result = ssh_userauth_kbdint_getinstruction(this._session);
857         if (result is null) {
858             throw new SSHException(this._session);
859         }
860         return fromStrZ(result);
861     }
862 
863     string userauthKeyboardInteractiveGetName() {
864         auto result = ssh_userauth_kbdint_getname(this._session);
865         if (result is null) {
866             throw new SSHException(this._session);
867         }
868         return fromStrZ(result);
869     }
870 
871     void userauthKeyboardInteractiveSetAnswer(uint i, string answer) {
872         auto result = ssh_userauth_kbdint_setanswer(this._session, i, toStrZ(answer));
873         checkForRCError(result, this._session);
874     }
875 
876 
877     SSHChannel acceptForward(int timeoutMs, out ushort destPort) {
878         int destPortInt;
879         auto result = ssh_channel_accept_forward(this._session, timeoutMs, &destPortInt);
880         if (result is null) {
881             return null;
882         }
883         destPort = cast(ushort) destPortInt;
884         return new SSHChannel(this, result);
885     }
886 
887     SSHChannel acceptForward(int timeoutMs) {
888         auto result = ssh_channel_accept_forward(this._session, timeoutMs, null);
889         if (result is null) {
890             return null;
891         }
892         return new SSHChannel(this, result);
893     }
894 
895     /**
896      * return false if in nonblocking mode and call has to be done again.
897      * */
898     bool listenForward(string address, ushort port, out ushort boundPort) {
899         int boundPortInt;
900         auto rc = ssh_channel_listen_forward(this._session, toStrZ(address), port, &boundPortInt);
901         if (rc == SSH_AGAIN) {
902             return false;
903         }
904         checkForRCError(rc, this._session);
905         boundPort = cast(ushort) boundPortInt;
906         return true;
907     }
908 
909     void cancelForward(string address, ushort port) {
910         auto rc = ssh_channel_cancel_forward(this._session, toStrZ(address), port);
911         checkForRCError(rc, this._session);
912     }
913 
914 
915     SSHSCP newScp(SCPMode mode, string location) {
916         auto result = ssh_scp_new(this._session, mode, toStrZ(location));
917         checkForNullError(result, this._session);
918         return new SSHSCP(this, result);
919     }
920 
921     SFTPSession newSFTP() {
922         auto result = sftp_new(this._session);
923 	auto rc = sftp_init(result);
924 	if (rc != SSH_OK) {
925 		throw new SFTPException(rc, this._session);
926 	}
927         checkForNullError(result, this._session);
928         return new SFTPSession(this, result);
929     }
930 
931     SFTPSession newSFTP(SSHChannel channel) {
932         auto result = sftp_new_channel(this._session, channel._channel);
933 	auto rc = sftp_init(result);
934 	if (rc != SSH_OK) {
935 		throw new SFTPException(rc, this._session);
936 	}
937         checkForNullError(result, this._session);
938         checkForNullError(result, this._session);
939         return new SFTPSession(this, result);
940     }
941 
942     version (LIBSSH_WITH_SERVER) {
943         SFTPSession newSFTPServer(SSHChannel channel) {
944             auto result = sftp_server_new(this._session, channel._channel);
945             mixin CheckForNullError!(result, this._session);
946             return new SFTPSession(this, result);
947         }
948     }
949 
950 
951     void handleKeyExchange() {
952         auto rc = ssh_handle_key_exchange(this._session);
953         checkForRCError(rc, this._session);
954     }
955 
956     package {        
957         this(ssh_session session) {
958             this._session = session;
959             
960             ssh_callbacks_init(this._sessionCallbacks);
961             this._sessionCallbacks.userdata = cast(void*) this;
962 //
963             ssh_callbacks_init(this._serverCallbacks);
964             this._serverCallbacks.userdata = cast(void*) this;
965             
966             this._authCallback = null;
967         }
968 
969         ssh_session _session;
970 
971         void registerChannel(SSHChannel ch) {
972             this._channels ~= ch;
973         }
974 
975         void freeChannel(SSHChannel toDel) {
976             this._channels = remove!(a => a == toDel)(this._channels);
977         }
978     }
979 
980     private {
981         ssh_callbacks_struct _sessionCallbacks;
982         ssh_server_callbacks_struct _serverCallbacks;
983 
984         SSHChannel[] _channels = [];
985 
986         OnConnectStatusChangedCallback _onConnectStatusChangedCallback;
987         OnLogCallback _onLogCallback;
988         OnGlobalRequestCallback _onGlobalRequestCallback;
989         AuthCallback _authCallback;
990         MessageCallback _messageCallback;
991         OpenRequestX11Callback _openRequestX11Callback;
992 
993         ServerAuthGSSAPIMicCallback _serverAuthGSSAPIMicCallback;
994         ServerAuthNoneCallback _serverAuthNoneCallback;
995         ServerAuthPasswordCallback _serverAuthPasswordCallback;
996         ServerAuthPublicKeyCallback _serverAuthPublicKeyCallback;
997         ServerChannelOpenRequestCallback _serverChannelOpenRequestCallback;
998         ServerServiceRequestCallback _serverServiceRequestCallback;
999         ServerGSSAPIAcceptSecCtxCallback _serverGSSAPIAcceptSecCtxCallback;
1000         ServerGSSAPISelectOidCallback _serverGSSAPISelectOidCallback;
1001 
1002         void _dispose(bool fromDtor) {
1003             if (this._session !is null) {
1004                 foreach (channel; this._channels) {
1005                     channel.dispose();
1006                 }
1007                 this._channels = null;
1008 
1009                 ssh_free(this._session);
1010                 this._session = null;
1011             }
1012         }
1013     }
1014 }
1015 
1016 bool sshFinalize() {
1017     auto rc = ssh_finalize();
1018     return rc == SSH_OK ? true : false;
1019 }
1020 
1021 private {
1022     extern(C) void nativeConnectStatusCallback(void* userdata, float status) {
1023         auto session = cast(SSHSession) userdata;
1024 
1025         if (session is null || session._onConnectStatusChangedCallback) {
1026             return;
1027         }
1028 
1029         session._onConnectStatusChangedCallback(session, status);
1030     }
1031 
1032     extern(C) void nativeLogFunction(ssh_session session, int priority, const char* message, 
1033             void* userdata) {
1034         auto sessionObj = cast(SSHSession) userdata;
1035 
1036         if (sessionObj is null || sessionObj._onLogCallback is null) {
1037             return;
1038         }
1039 
1040         sessionObj._onLogCallback(sessionObj, cast(LogLevel) priority, fromStrZ(message));
1041     }
1042 
1043     extern(C) int nativeSessionAuthCallback(const char *prompt, char *buf, size_t len,
1044             int echo, int verify, void *userdata) {
1045         auto session = cast(SSHSession) userdata;
1046 
1047         if (session is null || session._authCallback is null) {
1048             return SSH_ERROR;
1049         }
1050 
1051         try {
1052             auto result = session._authCallback(fromStrZ(prompt), echo == 0 ? false : true, 
1053                 verify == 0 ? false : true);
1054             if (result is null) {
1055                 return SSH_ERROR;
1056             }
1057 
1058             if (len < result.length + 1) {
1059                 return SSH_ERROR;
1060             }
1061 
1062             import core.stdc..string : memcpy;
1063             memcpy(buf, result.ptr, result.length);
1064             buf[result.length] = 0;
1065 
1066             return SSH_OK;
1067         } catch (Exception) {
1068             return SSH_ERROR;
1069         }
1070     }
1071 
1072     extern(C) void nativeOnGlobalRequest(ssh_session, ssh_message message, void* userdata) {
1073         auto sessionObj = cast(SSHSession) userdata;
1074 
1075         if (sessionObj is null || sessionObj._onGlobalRequestCallback is null) {
1076             return;
1077         }
1078 
1079         auto messageObj = new SSHMessage(sessionObj, message);
1080         scope(exit) messageObj.dispose();
1081         sessionObj._onGlobalRequestCallback(sessionObj, messageObj);
1082     }
1083 
1084     extern(C) int nativeOnMessageCallback(ssh_session session, ssh_message message, 
1085             void* userdata) {
1086         auto sessionObj = cast(SSHSession) userdata;
1087 
1088         if (sessionObj is null || sessionObj._messageCallback is null) {
1089             return SSH_ERROR;
1090         }
1091 
1092         auto messageObj = new SSHMessage(sessionObj, message);
1093         scope(exit) messageObj.dispose();
1094 
1095         try {
1096             auto result = sessionObj._messageCallback(sessionObj, messageObj);
1097             if (!result)
1098                 return SSH_ERROR;
1099             return SSH_OK;
1100         } catch (Exception) {
1101             return SSH_ERROR;
1102         }
1103             
1104     }
1105 
1106     extern(C) ssh_channel nativeOpenRequestX11Callback(ssh_session session, 
1107             const char* originator_address, int originator_port, void* userdata) {
1108         auto sessionObj = cast(SSHSession) userdata;
1109 
1110         if (sessionObj is null || sessionObj._openRequestX11Callback is null) {
1111             return null;
1112         }
1113 
1114         try {
1115             auto result = sessionObj._openRequestX11Callback(sessionObj, 
1116                 fromStrZ(originator_address), cast(ushort) originator_port);
1117             if (result is null) {
1118                 return null;
1119             }
1120             return result._channel;
1121         } catch (Exception) {
1122             return null;
1123         }
1124     }
1125 
1126     extern(C) int nativeServerAuthGSSAPIMicCallback(ssh_session, const char* user,
1127             const char* principal, void* userdata) {
1128         auto sessionObj = cast(SSHSession) userdata;
1129         
1130         if (sessionObj is null || sessionObj._serverAuthGSSAPIMicCallback is null) {
1131             return SSH_ERROR;
1132         }
1133 
1134         try {
1135             return cast(int) sessionObj._serverAuthGSSAPIMicCallback(sessionObj, 
1136                 fromStrZ(user), fromStrZ(principal));
1137         } catch (Exception) {
1138             return SSH_ERROR;
1139         }
1140     }
1141 
1142     extern(C) int nativeServerAuthNoneCallback(ssh_session, const char* user,
1143             void* userdata) {
1144         auto sessionObj = cast(SSHSession) userdata;
1145         
1146         if (sessionObj is null || sessionObj._serverAuthNoneCallback is null) {
1147             return SSH_ERROR;
1148         }
1149         
1150         try {
1151             return cast(int) sessionObj._serverAuthNoneCallback(sessionObj, 
1152                 fromStrZ(user));
1153         } catch (Exception) {
1154             return SSH_ERROR;
1155         }
1156     }
1157 
1158     extern(C) int nativeServerAuthPasswordCallback(ssh_session, const char* user,
1159             const char* password, void* userdata) {
1160         auto sessionObj = cast(SSHSession) userdata;
1161         
1162         if (sessionObj is null || sessionObj._serverAuthPasswordCallback is null) {
1163             return SSH_ERROR;
1164         }
1165         
1166         try {
1167             return cast(int) sessionObj._serverAuthPasswordCallback(sessionObj, 
1168                 fromStrZ(user), fromStrZ(password));
1169         } catch (Exception) {
1170             return SSH_ERROR;
1171         }
1172     }
1173 
1174     extern(C) int nativeServerAuthPublicKeyCallback(ssh_session, const char* user,
1175             ssh_key_struct* key, byte signatureState, void* userdata) {
1176         auto sessionObj = cast(SSHSession) userdata;
1177         
1178         if (sessionObj is null || sessionObj._serverAuthPublicKeyCallback is null) {
1179             return SSH_ERROR;
1180         }
1181         
1182         try {
1183             return cast(int) sessionObj._serverAuthPublicKeyCallback(sessionObj, 
1184                 fromStrZ(user), new SSHKey(key), cast(PublicKeyState) signatureState);
1185         } catch (Exception) {
1186             return SSH_ERROR;
1187         }
1188     }
1189 
1190     extern(C) ssh_channel nativeServerChannelOpenRequestCallback(ssh_session, void* userdata) {
1191         auto sessionObj = cast(SSHSession) userdata;
1192 
1193         if (sessionObj is null || sessionObj._serverChannelOpenRequestCallback is null) {
1194             return null;
1195         }
1196 
1197         try {
1198             auto result = sessionObj._serverChannelOpenRequestCallback(sessionObj);
1199             if (result is null) {
1200                 return null;
1201             }
1202             return result._channel;
1203         } catch(Exception) {
1204             return null;
1205         }
1206     }
1207 
1208     extern(C) int nativeServerServiceRequestCallback(ssh_session, const char* service, 
1209             void* userdata) {
1210         auto sessionObj = cast(SSHSession) userdata;
1211 
1212         if (sessionObj is null || sessionObj._serverServiceRequestCallback is null) {
1213             return SSH_ERROR;
1214         }
1215 
1216         try {
1217             if (sessionObj._serverServiceRequestCallback(sessionObj, fromStrZ(service))) {
1218                 return SSH_OK;
1219             } else {
1220                 return SSH_ERROR;
1221             }
1222         } catch(Exception) {
1223             return SSH_ERROR;
1224         }
1225     }
1226 
1227     extern(C) int nativeServerGSSAPIAcceptSecCtxCallback(ssh_session session, 
1228             ssh_string input_token, ssh_string *output_token, void *userdata) {
1229         auto sessionObj = cast(SSHSession) userdata;
1230         
1231         if (sessionObj is null || sessionObj._serverGSSAPIAcceptSecCtxCallback is null) {
1232             return SSH_ERROR;
1233         }
1234         
1235         try {
1236             auto inputTokenData = ssh_string_data(input_token);
1237             auto inputTokenLen = ssh_string_len(input_token);
1238             string inputTokenStr = (cast(immutable(char)*) inputTokenData)[0 .. inputTokenLen];
1239             string outputTokenStr;
1240 
1241             auto result = sessionObj._serverGSSAPIAcceptSecCtxCallback(sessionObj, inputTokenStr,
1242                 outputTokenStr);
1243             if (outputTokenStr is null) {
1244                 *output_token = null;
1245             } else {
1246                 *output_token = ssh_string_new(outputTokenStr.length);
1247                 ssh_string_fill(*output_token, outputTokenStr.ptr, outputTokenStr.length);
1248             }
1249 
1250             if (result) {
1251                 return SSH_OK;
1252             } else {
1253                 return SSH_ERROR;
1254             }
1255         } catch(Exception) {
1256             return SSH_ERROR;
1257         }
1258     }
1259 
1260     extern(C) ssh_string nativeServerGSSAPISelectOidCallback(ssh_session session, const char* user,
1261             int n_oid, ssh_string* oids, void* userdata) {
1262         auto sessionObj = cast(SSHSession) userdata;
1263         
1264         if (sessionObj is null || sessionObj._serverGSSAPISelectOidCallback is null) {
1265             string[] oidsArr = new string[n_oid];
1266             for (auto i = 0; i < n_oid; i++) {
1267                 auto oidData = ssh_string_data(oids[i]);
1268                 auto oidLen = ssh_string_len(oids[i]);
1269                 oidsArr[i] = (cast(immutable(char)*) oidData)[0 .. oidLen];
1270             }
1271             auto result = sessionObj._serverGSSAPISelectOidCallback(sessionObj,
1272                 fromStrZ(user), oidsArr);
1273             if (result is null) {
1274                 return null;
1275             }
1276 
1277             for (auto i = 0; i < n_oid; i++) {
1278                 if (oidsArr[i] == result) {
1279                     return oids[i];
1280                 }
1281             }
1282 
1283             return null;
1284         }
1285         
1286         try {
1287             return null;
1288         } catch(Exception) {
1289             return null;
1290         }
1291     }
1292 }