1 #include "SSH.h"
2 
3 namespace Upp {
4 
5 #define LLOG(x)       do { if(SSH::sTrace) RLOG("SshHosts: " << x); } while(false)
6 
Add(const String & host,int port,const Info & info,const String & comment)7 bool SshHosts::Add(const String& host, int port, const Info& info, const String& comment)
8 {
9 	return Add(Format("[%s]:%d", host, port), info, comment);
10 }
11 
Add(const String & host,const Info & info,const String & comment)12 bool SshHosts::Add(const String& host, const Info& info, const String& comment)
13 {
14 	ASSERT(ssh_session);
15 	Clear();
16 	bool b = handle &&
17 		libssh2_knownhost_addc(
18 			handle,
19 			~host,
20 			nullptr,
21 			~info.key, info.key.GetLength(),
22 			~comment, comment.GetLength(),
23 			info.type |
24 			LIBSSH2_KNOWNHOST_TYPE_PLAIN |
25 			LIBSSH2_KNOWNHOST_KEYENC_RAW,
26 			nullptr
27 		) == 0;
28 	return b ? b : Error();
29 }
30 
Remove(SshHost * host)31 bool SshHosts::Remove(SshHost* host)
32 {
33 	ASSERT(ssh_session);
34 	Clear();
35 	auto b = handle && libssh2_knownhost_del(handle, host) == 0;
36 	return b ? b : Error();
37 }
38 
Load(const String & filename)39 bool SshHosts::Load(const String& filename)
40 {
41 	ASSERT(ssh_session);
42 	Clear();
43 	file_path = filename;
44 	auto b = libssh2_knownhost_readfile(handle, ~file_path, LIBSSH2_KNOWNHOST_FILE_OPENSSH) >= 0;
45 	return b ? b : Error();
46 }
47 
Save()48 bool SshHosts::Save()
49 {
50 	return SaveAs(file_path);
51 }
52 
SaveAs(const String & filename)53 bool SshHosts::SaveAs(const String& filename)
54 {
55 	ASSERT(ssh_session);
56 	Clear();
57 	auto b = handle && libssh2_knownhost_writefile(handle, ~filename, LIBSSH2_KNOWNHOST_FILE_OPENSSH) == 0;
58 	return b ? b : Error();
59 }
60 
Check(const String & host,int port)61 SshHosts::Info SshHosts::Check(const String& host, int port)
62 {
63 	ASSERT(ssh_session);
64 	Clear();
65 	Info info;
66 	if(handle) {
67 		int	   type   = 0;
68 		size_t length = 0;
69 		auto*  p = libssh2_session_hostkey(ssh_session, &length, &type);
70 		if(!p) {
71 			Error();
72 			info.status = LIBSSH2_KNOWNHOST_CHECK_FAILURE;
73 			return pick(info);
74 		}
75 		info.status = libssh2_knownhost_checkp(
76 			handle,
77 			~host,
78 			port,
79 			p,
80 			length,
81 			LIBSSH2_KNOWNHOST_TYPE_PLAIN |
82 			LIBSSH2_KNOWNHOST_KEYENC_RAW,
83 			nullptr
84 		);
85 		info.key.Set(p, length);
86 		switch(type) {
87 			case LIBSSH2_HOSTKEY_TYPE_RSA:
88 				info.type = LIBSSH2_KNOWNHOST_KEY_SSHRSA;
89 				break;
90 			case LIBSSH2_HOSTKEY_TYPE_DSS:
91 				info.type = LIBSSH2_KNOWNHOST_KEY_SSHDSS;
92 				break;
93 			case LIBSSH2_HOSTKEY_TYPE_UNKNOWN:
94 				info.type = LIBSSH2_KNOWNHOST_KEY_UNKNOWN;
95 				break;
96 			default:
97 				NEVER();
98 		}
99 	}
100 	return pick(info);
101 }
102 
GetHosts()103 Vector<SshHost*> SshHosts::GetHosts()
104 {
105 	ASSERT(ssh_session);
106 	Clear();
107 	Vector<SshHost*> v;
108 	SshHost *prev = nullptr, *next = nullptr;
109 	int rc = libssh2_knownhost_get(handle, &prev, nullptr);
110 	if(rc >= 0) {
111 		v.Add(prev);
112 		if(rc == 0)
113 			while(rc < 1) {
114 				rc = libssh2_knownhost_get(handle, &next, prev);
115 				if(rc < 0) break;
116 				v.Add(next);
117 				prev = next;
118 			}
119 	}
120 	if(rc < 0) Error();
121 	return pick(v);
122 }
123 
Error()124 bool SshHosts::Error()
125 {
126 	ASSERT(ssh_session);
127 	Buffer<char*> libmsg(256, 0);
128 	int rc = libssh2_session_last_error(ssh_session, libmsg, nullptr, 0);
129 	error.a = rc;
130 	error.b = String(*libmsg);
131 	LLOG("Failed. " << error.b);
132 	return false;
133 }
134 
SshHosts(SshSession & session)135 SshHosts::SshHosts(SshSession& session)
136 {
137 	ASSERT(session.GetHandle());
138 	ssh_session   = session.GetHandle();
139 	handle = libssh2_knownhost_init(ssh_session);
140 }
141 
~SshHosts()142 SshHosts::~SshHosts()
143 {
144 	if(ssh_session && handle)
145 		libssh2_knownhost_free(handle);
146 }
147 }