1 /++
2 	Minimal bindings for libssh2. (just what I needed for my terminal emulator, but I'd accept more, and even wrappers if you wanted to.)
3 
4 	Just link with it on Linux, but it'll need a couple dlls and a lib on windows.
5 +/
6 module arsd.libssh2;
7 
8 // some day: https://libssh2.org/examples/x11.html
9 // and https://stackoverflow.com/questions/1580750/example-code-of-libssh2-being-used-for-port-forwarding#_=_
10 
11 version(libssh_sftp_example)
12 void main() {
13 	import std.socket;
14 
15 	if(libssh2_init(0))
16 		throw new Exception("libssh2_init");
17 	scope(exit)
18 		libssh2_exit();
19 
20 	auto socket = new Socket(AddressFamily.INET, SocketType.STREAM);
21 	socket.connect(new InternetAddress("localhost", 22));
22 	scope(exit) socket.close();
23 
24 	auto session = libssh2_session_init_ex(null, null, null, null);
25 	if(session is null) throw new Exception("init session");
26 	scope(exit)
27 		libssh2_session_disconnect_ex(session, 0, "normal", "EN");
28 
29 	if(libssh2_session_handshake(session, socket.handle))
30 		throw new Exception("handshake");
31 
32 	auto fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
33 
34 	if(auto err = libssh2_userauth_publickey_fromfile_ex(session, "me".ptr, "me".length, "/home/me/.ssh/id_rsa.pub", "/home/me/.ssh/id_rsa", null))
35 		throw new Exception("auth");
36 
37 
38 	auto channel = libssh2_channel_open_ex(session, "session".ptr, "session".length, LIBSSH2_CHANNEL_WINDOW_DEFAULT, LIBSSH2_CHANNEL_PACKET_DEFAULT, null, 0);
39 
40 	if(channel is null)
41 		throw new Exception("channel open");
42 
43 	scope(exit)
44 		libssh2_channel_free(channel);
45 
46 	auto sftp_session = libssh2_sftp_init(session);
47 	if(sftp_session is null)
48 		throw new Exception("no sftp");
49 	scope(exit) libssh2_sftp_shutdown(sftp_session);
50 
51 	libssh2_session_set_blocking(session, 1);
52 
53 	auto filename = "/home/me/arsd/libssh2.d";
54 	auto handle = libssh2_sftp_open_ex(sftp_session, filename.ptr, cast(int) filename.length, LIBSSH2_FXF_READ, 0, LIBSSH2_SFTP_OPENFILE);
55 	if(handle is null) throw new Exception("no file");
56 	scope(exit) libssh2_sftp_close_handle(handle);
57 
58 	char[1024] buffer;
59 	again:
60 	auto got = libssh2_sftp_read(handle, buffer.ptr, buffer.length);
61 
62 	import std.stdio;
63 	writeln(buffer[0 .. got]);
64 	if(got > 0)
65 		goto again;
66 }
67 
68 
69 version(libssh_example)
70 void main() {
71 	import std.socket;
72 
73 	if(libssh2_init(0))
74 		throw new Exception("libssh2_init");
75 	scope(exit)
76 		libssh2_exit();
77 
78 	auto socket = new Socket(AddressFamily.INET, SocketType.STREAM);
79 	socket.connect(new InternetAddress("localhost", 22));
80 	scope(exit) socket.close();
81 
82 	auto session = libssh2_session_init_ex(null, null, null, null);
83 	if(session is null) throw new Exception("init session");
84 	scope(exit)
85 		libssh2_session_disconnect_ex(session, 0, "normal", "EN");
86 
87 	if(libssh2_session_handshake(session, socket.handle))
88 		throw new Exception("handshake");
89 
90 	auto fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
91 
92 	/*
93 	import core.stdc.stdio;
94 	for(int i = 0; i < 20; i++)
95 		printf("%02X ", fingerprint[i]);
96 	*/
97 
98 	/*
99 	auto got = libssh2_userauth_list(session, "me", 2);
100 	if(got is null) throw new Exception("list");
101 	import core.stdc.stdio;
102 	printf("%s\n", got);
103 	*/
104 
105 	if(auto err = libssh2_userauth_publickey_fromfile_ex(session, "me".ptr, "me".length, "/home/me/.ssh/id_rsa.pub", "/home/me/.ssh/id_rsa", null))
106 		throw new Exception("auth");
107 
108 
109 	auto channel = libssh2_channel_open_ex(session, "session".ptr, "session".length, LIBSSH2_CHANNEL_WINDOW_DEFAULT, LIBSSH2_CHANNEL_PACKET_DEFAULT, null, 0);
110 
111 	if(channel is null)
112 		throw new Exception("channel open");
113 
114 	scope(exit)
115 		libssh2_channel_free(channel);
116 
117 	libssh2_channel_setenv_ex(channel, "ELVISBG".dup.ptr, "ELVISBG".length, "dark".ptr, "dark".length);
118 
119 	if(libssh2_channel_request_pty_ex(channel, "xterm", "xterm".length, null, 0, 80, 24, 0, 0))
120 		throw new Exception("pty");
121 
122 	if(libssh2_channel_process_startup(channel, "shell".ptr, "shell".length, null, 0))
123 		throw new Exception("process_startup");
124 
125 	libssh2_keepalive_config(session, 0, 60);
126 	libssh2_session_set_blocking(session, 0);
127 
128 
129 	char[1024] buffer;
130 	again:
131 	auto got = libssh2_channel_read_ex(channel, 0, buffer.ptr, buffer.length);
132 	if(got == LIBSSH2_ERROR_EAGAIN) {
133 		import core.thread;
134 		Thread.sleep(msecs(500));
135 		goto again;
136 	}
137 
138 	import std.stdio;
139 	writeln(buffer[0 .. got]);
140 }
141 
142 
143 
144 
145 import std.socket : socket_t;
146 
147 version(Windows) {
148 	pragma(lib, "libssh2");
149 } else {
150 	pragma(lib, "ssh2");
151 }
152 
153 version(X86)
154 	alias ssize_t = int;
155 else version(X86_64)
156 	alias ssize_t = long;
157 
158 import core.stdc.config;
159 
160 extern(C) {
161 	struct LIBSSH2_SESSION {}
162 	LIBSSH2_SESSION* libssh2_session_init_ex(void* myalloc, void* myfree, void* myrealloc, void* abstract_);
163 
164 	int libssh2_session_handshake(LIBSSH2_SESSION* session, socket_t socket);
165 
166 	enum int LIBSSH2_HOSTKEY_HASH_MD5 = 1;
167 	enum int LIBSSH2_HOSTKEY_HASH_SHA1 = 2;
168 	const(char)* libssh2_hostkey_hash(LIBSSH2_SESSION*, int hash_type);
169 
170 	/* sftp */
171 	struct LIBSSH2_SFTP {}
172 	struct LIBSSH2_SFTP_HANDLE {}
173 	LIBSSH2_SFTP* libssh2_sftp_init(LIBSSH2_SESSION *session);
174 	int libssh2_sftp_shutdown(LIBSSH2_SFTP *sftp);
175 	c_ulong libssh2_sftp_last_error(LIBSSH2_SFTP *sftp);
176 	int libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle);
177 	int libssh2_sftp_shutdown(LIBSSH2_SFTP *sftp);
178 
179 	enum LIBSSH2_SFTP_OPENFILE = 0;
180 	enum LIBSSH2_SFTP_OPENDIR = 1;
181 
182 	/* Flags for rename_ex() */
183 	enum LIBSSH2_SFTP_RENAME_OVERWRITE = 0x00000001;
184 	enum LIBSSH2_SFTP_RENAME_ATOMIC = 0x00000002;
185 	enum LIBSSH2_SFTP_RENAME_NATIVE = 0x00000004;
186 
187 	/* Flags for stat_ex() */
188 	enum LIBSSH2_SFTP_STAT = 0;
189 	enum LIBSSH2_SFTP_LSTAT = 1;
190 	enum LIBSSH2_SFTP_SETSTAT = 2;
191 
192 	/* Flags for symlink_ex() */
193 	enum LIBSSH2_SFTP_SYMLINK = 0;
194 	enum LIBSSH2_SFTP_READLINK = 1;
195 	enum LIBSSH2_SFTP_REALPATH = 2;
196 
197 	/* Flags for sftp_mkdir() */
198 	enum LIBSSH2_SFTP_DEFAULT_MODE = -1;
199 
200 	/* SFTP attribute flag bits */
201 	enum LIBSSH2_SFTP_ATTR_SIZE = 0x00000001;
202 	enum LIBSSH2_SFTP_ATTR_UIDGID = 0x00000002;
203 	enum LIBSSH2_SFTP_ATTR_PERMISSIONS = 0x00000004;
204 	enum LIBSSH2_SFTP_ATTR_ACMODTIME = 0x00000008;
205 	enum LIBSSH2_SFTP_ATTR_EXTENDED = 0x80000000;
206 
207 	/* SFTP statvfs flag bits */
208 	enum LIBSSH2_SFTP_ST_RDONLY = 0x00000001;
209 	enum LIBSSH2_SFTP_ST_NOSUID = 0x00000002;
210 
211 	enum LIBSSH2_SFTP_TYPE_REGULAR = 1;
212 	enum LIBSSH2_SFTP_TYPE_DIRECTORY = 2;
213 	enum LIBSSH2_SFTP_TYPE_SYMLINK = 3;
214 	enum LIBSSH2_SFTP_TYPE_SPECIAL = 4;
215 	enum LIBSSH2_SFTP_TYPE_UNKNOWN = 5;
216 	enum LIBSSH2_SFTP_TYPE_SOCKET = 6;
217 	enum LIBSSH2_SFTP_TYPE_CHAR_DEVICE = 7;
218 	enum LIBSSH2_SFTP_TYPE_BLOCK_DEVICE = 8;
219 	enum LIBSSH2_SFTP_TYPE_FIFO = 9;
220 
221 
222 	/* File type */
223 	enum LIBSSH2_SFTP_S_IFMT = 0xF000;     /* type of file mask */
224 	enum LIBSSH2_SFTP_S_IFIFO = 0x1000;     /* named pipe (fifo) */
225 	enum LIBSSH2_SFTP_S_IFCHR = 0x2000;     /* character special */
226 	enum LIBSSH2_SFTP_S_IFDIR = 0x4000;     /* directory */
227 	enum LIBSSH2_SFTP_S_IFBLK = 0x6000;     /* block special */
228 	enum LIBSSH2_SFTP_S_IFREG = 0x8000;     /* regular */
229 	enum LIBSSH2_SFTP_S_IFLNK = 0xA000;     /* symbolic link */
230 	enum LIBSSH2_SFTP_S_IFSOCK = 0xC000;     /* socket */
231 
232 	enum LIBSSH2_FXF_READ = 0x00000001;
233 	enum LIBSSH2_FXF_WRITE = 0x00000002;
234 	enum LIBSSH2_FXF_APPEND = 0x00000004;
235 	enum LIBSSH2_FXF_CREAT = 0x00000008;
236 	enum LIBSSH2_FXF_TRUNC = 0x00000010;
237 	enum LIBSSH2_FXF_EXCL = 0x00000020;
238 
239 	enum LIBSSH2_FX {
240 		OK = 0,
241 		EOF = 1,
242 		NO_SUCH_FILE = 2,
243 		PERMISSION_DENIED = 3,
244 		FAILURE = 4,
245 		BAD_MESSAGE = 5,
246 		NO_CONNECTION = 6,
247 		CONNECTION_LOST = 7,
248 		OP_UNSUPPORTED = 8,
249 		INVALID_HANDLE = 9,
250 		NO_SUCH_PATH = 10,
251 		FILE_ALREADY_EXISTS = 11,
252 		WRITE_PROTECT = 12,
253 		NO_MEDIA = 13,
254 		NO_SPACE_ON_FILESYSTEM = 14,
255 		QUOTA_EXCEEDED = 15,
256 		UNKNOWN_PRINCIPAL = 16,
257 		LOCK_CONFLICT = 17,
258 		DIR_NOT_EMPTY = 18,
259 		NOT_A_DIRECTORY = 19,
260 		INVALID_FILENAME = 20,
261 		LINK_LOOP = 21,
262 	}
263 
264 	LIBSSH2_SFTP_HANDLE * libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp, const char *filename, uint filename_len, c_ulong flags, c_long mode, int open_type);
265 
266 
267 	ssize_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, char *buffer, size_t buffer_maxlen);
268 	ssize_t libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer, size_t count);
269 
270 	enum LIBSSH2_SFTP_ATTR {
271 		SIZE            = 0x00000001,
272 		UIDGID          = 0x00000002,
273 		PERMISSIONS     = 0x00000004,
274 		ACMODTIME       = 0x00000008,
275 		EXTENDED        = 0x80000000,
276 	}
277 
278 	struct LIBSSH2_SFTP_ATTRIBUTES {
279 		c_ulong flags; // see LIBSSH2_SFTP_ATTR
280 
281 		ulong filesize;
282 		c_ulong uid, gid;
283 		c_ulong permissions;
284 		c_ulong atime, mtime;
285 	}
286 
287 	int libssh2_sftp_readdir_ex(LIBSSH2_SFTP_HANDLE *handle,
288                         char *buffer, size_t buffer_maxlen,
289                         char *longentry, size_t longentry_maxlen, // longentry is just a user-friendly display
290                         LIBSSH2_SFTP_ATTRIBUTES *attrs);
291 	int libssh2_sftp_stat_ex(LIBSSH2_SFTP *sftp,
292 					     const char *path,
293 					     uint,
294 					     int stat_type,
295 					     LIBSSH2_SFTP_ATTRIBUTES *attrs);
296 	int libssh2_sftp_fstatvfs(LIBSSH2_SFTP_HANDLE *handle,
297 					      LIBSSH2_SFTP_STATVFS *st);
298 	int libssh2_sftp_statvfs(LIBSSH2_SFTP *sftp,
299 					     const char *path,
300 					     size_t path_len,
301 					     LIBSSH2_SFTP_STATVFS *st);
302 	int libssh2_sftp_rmdir_ex(LIBSSH2_SFTP *sftp,
303 					      const char *path,
304 					      uint);
305 	int libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp,
306 					      const char *path,
307 					      uint, c_long mode);
308 	int libssh2_sftp_unlink_ex(LIBSSH2_SFTP *sftp,
309 					       const char *filename,
310 					       uint);
311 	int libssh2_sftp_symlink_ex(LIBSSH2_SFTP *sftp,
312 						const char *path,
313 						uint,
314 						char *target,
315 						uint,
316 						int link_type);
317 	int libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp,
318 					       const char *source_filename,
319 					       uint,
320 					       const char *dest_filename,
321 					       uint,
322 					       c_long flags);
323 
324 	struct LIBSSH2_SFTP_STATVFS {
325 		ulong  f_bsize;    /* file system block size */
326 		ulong  f_frsize;   /* fragment size */
327 		ulong  f_blocks;   /* size of fs in f_frsize units */
328 		ulong  f_bfree;    /* # free blocks */
329 		ulong  f_bavail;   /* # free blocks for non-root */
330 		ulong  f_files;    /* # inodes */
331 		ulong  f_ffree;    /* # free inodes */
332 		ulong  f_favail;   /* # free inodes for non-root */
333 		ulong  f_fsid;     /* file system ID */
334 		ulong  f_flag;     /* mount flags */
335 		ulong  f_namemax;  /* maximum filename length */
336 	}
337 
338 
339 	/* end sftp */
340 
341 	int libssh2_userauth_password_ex(LIBSSH2_SESSION *session,
342                     const char *username,
343                     uint username_len,
344                     const char *password,
345                     uint password_len,
346 		    void* passwd_change_cb);
347                     //LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb)));
348 
349 	//int libssh2_userauth_password(LIBSSH2_SESSION*, const char* username, const char* password);
350 	int libssh2_userauth_publickey_fromfile_ex(
351 		LIBSSH2_SESSION* session,
352 		const char *username,
353 		uint ousername_len,
354 		const char *publickey,
355 		const char *privatekey,
356 		const char *passphrase);
357 
358 	struct LIBSSH2_LISTENER {}
359 	LIBSSH2_LISTENER * libssh2_channel_forward_listen_ex(LIBSSH2_SESSION *session, const char *host,
360 		  int port, int *bound_port,
361 		  int queue_maxsize);
362 	int libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener);
363 	LIBSSH2_CHANNEL * libssh2_channel_forward_accept(LIBSSH2_LISTENER *listener);
364 	LIBSSH2_CHANNEL * libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION *session, const char *host,
365                                 int port, const char *shost, int sport);
366 
367 	struct LIBSSH2_CHANNEL {}
368 	LIBSSH2_CHANNEL* libssh2_channel_open_ex(
369 		LIBSSH2_SESSION *session,
370 		const char *channel_type,
371 		uint channel_type_len,
372 		uint window_size,
373 		uint packet_size,
374 		const char *message,
375 		uint message_len);
376 	// channel_open_session calls the above
377 
378 	int libssh2_channel_setenv_ex(
379 		LIBSSH2_CHANNEL* channel,
380 		char* varname,
381 		uint varname_len,
382 		const char *value,
383 		uint value_len);
384 
385 	enum LIBSSH2_CHANNEL_WINDOW_DEFAULT = (256*1024);
386 	enum LIBSSH2_CHANNEL_PACKET_DEFAULT = 32768;
387 
388 	int libssh2_session_last_error(LIBSSH2_SESSION *session, char **errmsg, int *errmsg_len, int want_buf);
389 
390 	int libssh2_channel_request_pty_ex(LIBSSH2_CHANNEL *channel, const char *term, uint term_len, const char *modes, uint modes_len, int width, int height, int width_px, int height_px);
391 
392 	int libssh2_channel_process_startup(
393 		LIBSSH2_CHANNEL* channel,
394 		const char *request,
395 		uint request_len,
396 		const char *message,
397 		uint message_len);
398 
399 
400 	int libssh2_channel_free(LIBSSH2_CHANNEL *channel);
401 	int libssh2_session_disconnect_ex(LIBSSH2_SESSION *session, int reason, const char *description, const char *lang);
402 	int libssh2_session_free(LIBSSH2_SESSION *session);
403 
404 	int libssh2_init(int flags);
405 	void libssh2_exit();
406 
407 	// stream_id 0 == normal, 1 == error.
408 	ssize_t libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id, void *buf, size_t buflen);
409 
410 	ssize_t libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel,
411                                   int stream_id, const(void)* buf,
412                                   size_t buflen);
413 
414 	void libssh2_session_set_blocking(LIBSSH2_SESSION* session, int blocking);
415 
416 	void libssh2_keepalive_config(LIBSSH2_SESSION *session,
417 		int want_reply,
418 		uint interval);
419 
420 	int libssh2_keepalive_send(LIBSSH2_SESSION *session,
421 		int *seconds_to_next);
422 
423 	LIBSSH2_CHANNEL * libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION *session, const char *host, int port, const char *shost, int sport);
424 
425 	int libssh2_channel_request_pty_size_ex(LIBSSH2_CHANNEL *channel,
426 		int width, int height,
427 		int width_px,
428 		int height_px);
429 
430 	char *
431  libssh2_userauth_list(LIBSSH2_SESSION *session, const char *username,
432                        uint username_len);
433 
434 	int libssh2_channel_eof(LIBSSH2_CHANNEL*);
435 	int libssh2_channel_close(LIBSSH2_CHANNEL*);
436 	int libssh2_channel_wait_closed(LIBSSH2_CHANNEL *channel);
437 
438 	enum LIBSSH2_ERROR_EAGAIN = -37;
439 
440 	int libssh2_session_flag(LIBSSH2_SESSION*, int, int);
441 	enum LIBSSH2_FLAG_SIGPIPE = 1;
442 	enum LIBSSH2_FLAG_COMPRESS = 2;
443 
444 
445 	int libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL *channel,
446                                            int single_connection,
447                                            const char *auth_proto,
448                                            const char *auth_cookie,
449                                            int screen_number);
450 
451 
452 int libssh2_channel_get_exit_status(LIBSSH2_CHANNEL* channel);
453 int libssh2_channel_get_exit_signal(LIBSSH2_CHANNEL *channel, char **exitsignal, size_t *exitsignal_len, char **errmsg, size_t *errmsg_len, char **langtag, size_t *langtag_len);
454 
455 int libssh2_channel_send_eof(LIBSSH2_CHANNEL *channel);
456 
457 }