configurable connect timeout and session idle timeout in SSH subagent
[public/netxms.git] / src / agent / subagents / ssh / session.cpp
1 /*
2 ** NetXMS SSH subagent
3 ** Copyright (C) 2004-2016 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** File: session.cpp
20 **/
21
22 #include "ssh_subagent.h"
23
24 /**
25 * SSH session constructor
26 */
27 SSHSession::SSHSession(const InetAddress& addr, UINT16 port, INT32 id)
28 {
29 m_id = id;
30 m_addr = addr;
31 m_port = port;
32 m_session = NULL;
33 m_lastAccess = 0;
34 m_user[0] = 0;
35 m_busy = false;
36 _sntprintf(m_name, MAX_SSH_SESSION_NAME_LEN, _T("nobody@%s:%d/%d"), (const TCHAR *)m_addr.toString(), m_port, m_id);
37 }
38
39 /**
40 * SSH session destructor
41 */
42 SSHSession::~SSHSession()
43 {
44 disconnect();
45 }
46
47 /**
48 * Check if session match for given target
49 */
50 bool SSHSession::match(const InetAddress& addr, UINT16 port, const TCHAR *user) const
51 {
52 return addr.equals(m_addr) && ((unsigned int)port == m_port) && !_tcscmp(m_user, user);
53 }
54
55 /**
56 * Acquire session
57 */
58 bool SSHSession::acquire()
59 {
60 if (m_busy || !isConnected())
61 return false;
62 m_busy = true;
63 return true;
64 }
65
66 /**
67 * Release session
68 */
69 void SSHSession::release()
70 {
71 m_busy = false;
72 }
73
74 /**
75 * Connect to server
76 */
77 bool SSHSession::connect(const TCHAR *user, const TCHAR *password)
78 {
79 if (m_session != NULL)
80 return false; // already connected
81
82 m_session = ssh_new();
83 if (m_session == NULL)
84 return false; // cannot create session
85
86 bool success = false;
87
88 char hostname[64];
89 ssh_options_set(m_session, SSH_OPTIONS_HOST, m_addr.toStringA(hostname));
90 ssh_options_set(m_session, SSH_OPTIONS_PORT, &m_port);
91 long timeout = (long)g_sshConnectTimeout * (long)1000; // convert milliseconds to microseconds
92 ssh_options_set(m_session, SSH_OPTIONS_TIMEOUT_USEC, &timeout);
93 #ifdef UNICODE
94 char mbuser[256];
95 WideCharToMultiByte(CP_UTF8, 0, user, -1, mbuser, 256, NULL, NULL);
96 ssh_options_set(m_session, SSH_OPTIONS_USER, mbuser);
97 #else
98 ssh_options_set(m_session, SSH_OPTIONS_USER, user);
99 #endif
100
101 if (ssh_connect(m_session) == SSH_OK)
102 {
103 #ifdef UNICODE
104 char mbpassword[256];
105 WideCharToMultiByte(CP_UTF8, 0, password, -1, mbpassword, 256, NULL, NULL);
106 if (ssh_userauth_password(m_session, NULL, mbpassword) == SSH_AUTH_SUCCESS)
107 #else
108 if (ssh_userauth_password(m_session, NULL, password) == SSH_AUTH_SUCCESS)
109 #endif
110 {
111 success = true;
112 }
113 else
114 {
115 nxlog_debug(6, _T("SSH: login as %s on %s:%d failed"), user, (const TCHAR *)m_addr.toString(), m_port);
116 }
117 }
118 else
119 {
120 nxlog_debug(6, _T("SSH: connect to %s:%d failed"), (const TCHAR *)m_addr.toString(), m_port);
121 }
122
123 if (success)
124 {
125 nx_strncpy(m_user, user, MAX_SSH_LOGIN_LEN);
126 _sntprintf(m_name, MAX_SSH_SESSION_NAME_LEN, _T("%s@%s:%d/%d"), m_user, (const TCHAR *)m_addr.toString(), m_port, m_id);
127 m_lastAccess = time(NULL);
128 }
129 else
130 {
131 if (ssh_is_connected(m_session))
132 ssh_disconnect(m_session);
133 ssh_free(m_session);
134 m_session = NULL;
135 }
136 return success;
137 }
138
139 /**
140 * Disconnect from server
141 */
142 void SSHSession::disconnect()
143 {
144 if (m_session == NULL)
145 return;
146
147 if (ssh_is_connected(m_session))
148 ssh_disconnect(m_session);
149 ssh_free(m_session);
150 m_session = NULL;
151 }
152
153 /**
154 * Execute command and capture output
155 */
156 StringList *SSHSession::execute(const TCHAR *command)
157 {
158 if ((m_session == NULL) || !ssh_is_connected(m_session))
159 return NULL;
160
161 ssh_channel channel = ssh_channel_new(m_session);
162 if (channel == NULL)
163 return NULL;
164
165 StringList *output = NULL;
166 if (ssh_channel_open_session(channel) == SSH_OK)
167 {
168 #ifdef UNICODE
169 char *mbcmd = UTF8StringFromWideString(command);
170 if (ssh_channel_request_exec(channel, mbcmd) == SSH_OK)
171 #else
172 if (ssh_channel_request_exec(channel, command) == SSH_OK)
173 #endif
174 {
175 output = new StringList();
176 char buffer[8192];
177 int nbytes = ssh_channel_read(channel, buffer, sizeof(buffer) - 1, 0);
178 int offset = 0;
179 while(nbytes > 0)
180 {
181 buffer[nbytes + offset] = 0;
182 char *curr = buffer;
183 char *eol = strchr(curr, '\n');
184 while(eol != NULL)
185 {
186 *eol = 0;
187 char *cr = strchr(curr, '\r');
188 if (cr != NULL)
189 *cr = 0;
190 output->addMBString(curr);
191 curr = eol + 1;
192 eol = strchr(curr, '\n');
193 }
194 offset = strlen(curr);
195 if (offset > 0)
196 memmove(buffer, curr, offset);
197 nbytes = ssh_channel_read(channel, &buffer[offset], sizeof(buffer) - offset - 1, 0);
198 }
199 if (nbytes == 0)
200 {
201 if (offset > 0)
202 {
203 buffer[offset] = 0;
204 char *cr = strchr(buffer, '\r');
205 if (cr != NULL)
206 *cr = 0;
207 output->addMBString(buffer);
208 }
209 ssh_channel_send_eof(channel);
210 }
211 else
212 {
213 delete_and_null(output);
214 }
215 }
216 else
217 {
218 nxlog_debug(6, _T("SSH: command \"%s\" execution on %s:%d failed"), command, (const TCHAR *)m_addr.toString(), m_port);
219 }
220 ssh_channel_close(channel);
221 }
222 else
223 {
224 nxlog_debug(6, _T("SSH: cannot open channel on %s:%d"), (const TCHAR *)m_addr.toString(), m_port);
225 }
226 ssh_channel_free(channel);
227 m_lastAccess = time(NULL);
228 return output;
229 }