NCL Composer  0.1.5
 All Classes Functions Variables Pages
SimpleSSHClient.cpp
1 /* Copyright (c) 2011 Telemidia/PUC-Rio.
2  * All rights reserved. This program and the accompanying materials
3  * are made available under the terms of the Eclipse Public License v1.0
4  * which accompanies this distribution, and is available at
5  * http://www.eclipse.org/legal/epl-v10.html
6  *
7  * Contributors:
8  * Telemidia/PUC-Rio - initial API and implementation
9  */
10 #include "SimpleSSHClient.h"
11 
12 extern "C" {
13 #include <gcrypt.h>
14 #include <time.h>
15 }
16 
17 #include <QDebug>
18 
19 // This variable tell us if the libssh2 was initialized properly. This value
20 // will be equal to zero only if the libssh2 are safely initialized through
21 // \ref SimpleSSHClient::init()
22 int SimpleSSHClient::libssh2_init_rc = -1;
23 
24 // QMutex thread callbacks for libgcrypt
25 static int qmutex_mutex_init(void **priv)
26 {
27  QMutex *lock = new QMutex();
28  if (!lock)
29  return ENOMEM;
30  *priv = lock;
31  return 0;
32 }
33 
34 static int qmutex_mutex_destroy(void **lock)
35 {
36  delete reinterpret_cast<QMutex*>(*lock);
37  return 0;
38 }
39 
40 static int qmutex_mutex_lock(void **lock)
41 {
42  reinterpret_cast<QMutex*>(*lock)->lock();
43  return 0;
44 }
45 
46 static int qmutex_mutex_unlock(void **lock)
47 {
48  reinterpret_cast<QMutex*>(*lock)->unlock();
49  return 0;
50 }
51 
52 static struct gcry_thread_cbs gcry_threads_qmutex =
53 { GCRY_THREAD_OPTION_USER, NULL,
54  qmutex_mutex_init, qmutex_mutex_destroy,
55  qmutex_mutex_lock, qmutex_mutex_unlock };
56 
58 {
59  // make libgcrypt thread safe
60  // this must be called before any other libgcrypt call
61  gcry_control( GCRYCTL_SET_THREAD_CBS, &gcry_threads_qmutex );
62 
63  SimpleSSHClient::libssh2_init_rc = libssh2_init (0);
64  return libssh2_init_rc;
65 }
66 
68 {
69  libssh2_exit();
70 }
71 
72 SimpleSSHClient::SimpleSSHClient(const char *username_,
73  const char *password_,
74  const char *hostip_,
75  const char *sftp_path_)
76 {
77  this->username = username_;
78  this->password = password_;
79  this->hostip = hostip_;
80  this->sftp_path = sftp_path_;
81 
82  this->session = 0;
83 }
84 
86 {
87  int rc;
88  LIBSSH2_KNOWNHOSTS *nh;
89  unsigned long hostaddr = inet_addr(hostip.c_str());
90  struct sockaddr_in sin;
91  const char *fingerprint;
92  // size_t nread;
93  size_t len;
94  int type;
95 
96 #ifdef WIN32
97  WSADATA wsadata;
98  WSAStartup(MAKEWORD(2,0), &wsadata);
99 #endif
100 
101  if (libssh2_init_rc != 0)
102  {
103  fprintf (stderr, "libssh2 initialization failed (%d)\n", libssh2_init_rc);
104  return 1;
105  }
106 
107  /* Ultra basic "connect to port 22 on localhost"
108  * Your code is responsible for creating the socket establishing the
109  * connection
110  */
111  sock = socket(AF_INET, SOCK_STREAM, 0);
112 
113  sin.sin_family = AF_INET;
114  sin.sin_port = htons(22);
115  sin.sin_addr.s_addr = hostaddr;
116  if (connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in)) != 0)
117  {
118  fprintf(stderr, "failed to connect!\n");
119  return -1;
120  }
121 
122  /* Create a session instance */
123  session = libssh2_session_init();
124  if (!session) return -1;
125 
126  /* ... start it up. This will trade welcome banners, exchange keys,
127  * and setup crypto, compression, and MAC layers
128  */
129  while ((rc = libssh2_session_startup(session, sock)) == LIBSSH2_ERROR_EAGAIN);
130  if (rc)
131  {
132  fprintf(stderr, "Failure establishing SSH session: %d\n", rc);
133  return -1;
134  }
135 
136  nh = libssh2_knownhost_init(session);
137 
138  if(!nh)
139  {
140  /* eeek, do cleanup here */
141  return 2;
142  }
143 
144  /* read all hosts from here */
145  libssh2_knownhost_readfile(nh, "known_hosts", LIBSSH2_KNOWNHOST_FILE_OPENSSH);
146 
147  /* store all known hosts to here */
148  libssh2_knownhost_writefile(nh, "dumpfile", LIBSSH2_KNOWNHOST_FILE_OPENSSH);
149  fingerprint = libssh2_session_hostkey(session, &len, &type);
150 
151  if(fingerprint)
152  {
153  struct libssh2_knownhost *host;
154 #if LIBSSH2_VERSION_NUM >= 0x010206
155  /* introduced in 1.2.6 */
156  int check = libssh2_knownhost_checkp(nh, hostip.c_str(), 22,
157  fingerprint, len,
158  LIBSSH2_KNOWNHOST_TYPE_PLAIN|
159  LIBSSH2_KNOWNHOST_KEYENC_RAW,
160  &host);
161 #else
162  /* 1.2.5 or older */
163  int check = libssh2_knownhost_check(nh, hostip.c_str(),
164  fingerprint, len,
165  LIBSSH2_KNOWNHOST_TYPE_PLAIN|
166  LIBSSH2_KNOWNHOST_KEYENC_RAW,
167  &host);
168 #endif
169  fprintf(stderr, "Host check: %d, key: %s\n", check,
170  (check <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH)?
171  host->key:"<none>");
172 
173  /*****
174  * At this point, we could verify that 'check' tells us the key is
175  * fine or bail out.
176  *****/
177  }
178  else
179  {
180  /* eeek, do cleanup here */
181  return 3;
182  }
183  libssh2_knownhost_free(nh);
184 
185  if ( strlen(password.c_str()) != 0 )
186  {
187  /* We could authenticate via password */
188  while ((rc = libssh2_userauth_password(session, username.c_str(),
189  password.c_str()))
190  == LIBSSH2_ERROR_EAGAIN);
191  if (rc) {
192  fprintf(stderr, "Authentication by password failed.\n");
193 
194  // shutdowns
195  doDisconnect();
196  }
197  }
198  else
199  {
200  /* Or by public key */
201  while ((rc = libssh2_userauth_publickey_fromfile(session, username.c_str(),
202  "/home/user/"
203  ".ssh/id_rsa.pub",
204  "/home/user/"
205  ".ssh/id_rsa",
206  password.c_str())) ==
207  LIBSSH2_ERROR_EAGAIN);
208  if (rc)
209  {
210  fprintf(stderr, "\tAuthentication by public key failed\n");
211 
212  // shutdown
213  doDisconnect();
214  }
215  }
216 
217  fprintf(stderr, "Before sftp_init session.\n");
218  sftp_session = libssh2_sftp_init(session);
219 
220  if(!sftp_session) {
221  fprintf(stderr, "Unable to init sftp session.\n");
222  doDisconnect();
223  }
224 
225  fprintf(stderr, "sftp_init session ok.\n");
226 
227 #if 1
228  libssh2_trace(session, ~0 );
229 #endif
230 
231  return 0;
232 }
233 
235 {
236  if(sftp_session)
237  {
238  libssh2_sftp_shutdown(sftp_session);
239  }
240 
241  if(session)
242  {
243  libssh2_session_disconnect(session,
244  "Normal Shutdown, Thank you for playing");
245 
246  libssh2_session_free(session);
247  }
248 
249 #ifdef WIN32
250  closesocket(sock);
251 #else
252  close(sock);
253 #endif
254 }
255 
256 int SimpleSSHClient::sftp_copy_file(const char *localncl, const char *destpath)
257 {
258  int rc;
259  FILE *local;
260  struct stat fileinfo;
261  char mem[1024 * 100];
262  char *ptr;
263  size_t memuse;
264  size_t nread;
265  int total = 0;
266 
267  LIBSSH2_CHANNEL *channel;
268  LIBSSH2_SFTP_HANDLE *sftp_handle;
269  LIBSSH2_SFTP_ATTRIBUTES attrs;
270  bool isModified = true;
271 
272  size_t found;
273  string temp = localncl;
274  found = temp.find_last_of("/\\");
275  string nclfile = temp.substr(found+1);
276 
277  sftp_file = destpath + string("/") + nclfile;
278 
279 #ifdef WIN32
280  WSADATA wsadata;
281 
282  WSAStartup(MAKEWORD(2,0), &wsadata);
283 #endif
284 
285  //fprintf(stderr, "Copying %s to %s.\n", localncl, sftp_file.c_str());
286 
287  // \todo Make sure we are connected.
288  // \todo create subpath when it does not exists
289 
290  // Open local file
291  local = fopen(localncl, "rb");
292  if (!local)
293  {
294  fprintf(stderr, "Can't open local file %s\n", localncl);
295  return -1;
296  }
297 
298  // Get info from local file
299  stat(localncl, &fileinfo);
300 
301  libssh2_session_set_blocking(session, 1);
302 
303  rc = libssh2_sftp_stat(sftp_session, sftp_file.c_str(), &attrs);
304 
305  if(rc == 0) // sftp_stat success
306  {
307  if(attrs.mtime < fileinfo.st_mtime) // The remote file is older than the local file
308  isModified = true;
309  else
310  isModified = false;
311  }
312 
313  if(isModified) // I will copy the file
314  {
315 
316 /* \todo: Send file through SFTP only
317  // Open the remote file
318  sftp_handle = libssh2_sftp_open(sftp_session,
319  sftp_file.c_str(),
320  LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
321  LIBSSH2_SFTP_S_IRUSR|LIBSSH2_SFTP_S_IWUSR|
322  LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IROTH);
323 
324  if(!sftp_handle)
325  {
326  fprintf(stderr, "Unable to open file with SFTP.\n");
327  goto shutdown_copy;
328  }
329 
330  fprintf(stderr, "libssh2_sftp_open() is done, now send data.\n");
331 
332  do {
333  nread = fread(mem, 1, sizeof(mem), local);
334  total += nread;
335 
336  qDebug() << nread << total << fileinfo.st_size;
337 
338  if (nread <= 0) {
339  // end of file
340  break;
341  }
342  ptr = mem;
343 
344  do {
345  // write data in a loop until we block
346  rc = libssh2_sftp_write(sftp_handle, ptr, nread);
347 
348  if(rc < 0)
349  break;
350  ptr += rc;
351  nread -= rc;
352  } while (nread);
353 
354  } while (rc > 0);
355  libssh2_sftp_close(sftp_handle); */
356 
357  /* BEGIN SEND FILE THROUGH SCP */
358  /* Send a file via scp. The mode parameter must only have permissions! */
359  channel = libssh2_scp_send(session, sftp_file.c_str(), fileinfo.st_mode & 0777,
360  (unsigned long)fileinfo.st_size);
361 
362  if (!channel) {
363  char *errmsg;
364  int errlen;
365  int err = libssh2_session_last_error(session, &errmsg, &errlen, 0);
366 
367  fprintf(stderr, "Unable to open a session: (%d) %s\n", err, errmsg);
368  goto shutdown_copy;
369  }
370 
371  fprintf(stderr, "SCP session waiting to send file\n");
372  do {
373  nread = fread(mem, 1, sizeof(mem), local);
374  if (nread <= 0) {
375  /* end of file */
376  break;
377  }
378  ptr = mem;
379 
380  do {
381  /* write the same data over and over, until error or completion */
382  rc = libssh2_channel_write(channel, ptr, nread);
383 
384  if (rc < 0) {
385  fprintf(stderr, "ERROR %d\n", rc);
386  break;
387  }
388  else {
389  /* rc indicates how many bytes were written this time */
390  ptr += rc;
391  nread -= rc;
392  }
393  } while (nread);
394 
395  } while (1);
396 
397  fprintf(stderr, "Sending EOF\n");
398  libssh2_channel_send_eof(channel);
399 
400 
401  fprintf(stderr, "Waiting for EOF\n");
402  libssh2_channel_wait_eof(channel);
403 
404 
405  fprintf(stderr, "Waiting for channel to close\n");
406  libssh2_channel_wait_closed(channel);
407 
408 
409  libssh2_channel_free(channel);
410 
411  channel = NULL;
412  /* End SEND FILE THROUGH SCP */
413 
414  rc = libssh2_sftp_stat(sftp_session, sftp_file.c_str(), &attrs);
415  if(rc < 0)
416  {
417  fprintf(stderr, "I could not check if the file was copied correctly.\n");
418  goto shutdown_copy;
419  }
420 
421  fprintf(stderr, "Modification time is: %s.\n", ctime((const time_t*)&attrs.mtime));
422 
423  // Update the remote file with the same mtime of the local file.
424  attrs.mtime = fileinfo.st_mtime;
425  rc = libssh2_sftp_setstat(sftp_session, sftp_file.c_str(), &attrs);
426 
427  // Checking if we have modified the mtime correctly.
428  rc = libssh2_sftp_stat(sftp_session, sftp_file.c_str(), &attrs);
429  if(rc < 0)
430  {
431  fprintf(stderr, "I could not check if the mtime file was copied correctly.\n");
432  goto shutdown_copy;
433  }
434 
435  fprintf(stderr, "Updated modification time is: %s", ctime((const time_t*)&attrs.mtime));
436 
437  }
438  else
439  {
440  fprintf(stderr, "I will not copy the file %s, because it was not modified.\n", sftp_file.c_str());
441  }
442 
443 shutdown_copy:
444  if (local)
445  fclose(local);
446 
447  return 0;
448 }
449 
450 int SimpleSSHClient::exec_cmd(const char *command)
451 {
452  int rc;
453  LIBSSH2_CHANNEL *channel;
454 
455  int exitcode;
456  char *exitsignal=(char *)"none";
457  int bytecount = 0;
458 
459  // \todo Make sure we are connected.
460 
461  /* Exec non-blocking on the remove host */
462  channel = libssh2_channel_open_session(session);
463 
464  if( channel == NULL )
465  {
466  fprintf(stderr,"Error 1\n");
467  ::exit(1);
468  }
469 
470  /* tell libssh2 we want it done non-blocking */
471  libssh2_channel_set_blocking(channel, 1);
472 
473  while( (rc = libssh2_channel_exec(channel, command)) == LIBSSH2_ERROR_EAGAIN )
474  {
475  waitsocket(sock, session);
476  }
477 
478  if( rc != 0 )
479  {
480  fprintf(stderr, "Error 2\n");
481  ::exit( 1 );
482  }
483 
484  for( ;; )
485  {
486  /* loop until we block */
487  int rc;
488  do
489  {
490  char buffer[0x4000];
491  rc = libssh2_channel_read( channel, buffer, sizeof(buffer) );
492 
493  if( rc > 0 )
494  {
495  int i;
496  bytecount += rc;
497  fprintf(stderr, "We read:\n");
498  for( i=0; i < rc; ++i )
499  fputc( buffer[i], stderr);
500  fprintf(stderr, "\n");
501  }
502  else {
503  fprintf(stderr, "libssh2_channel_read returned %d\n", rc);
504  }
505  }
506  while( rc > 0 );
507 
508  /* this is due to blocking that would occur otherwise so we loop on
509  this condition */
510  if( rc == LIBSSH2_ERROR_EAGAIN )
511  {
512  waitsocket(sock, session);
513  }
514  else
515  break;
516  }
517 
518  exitcode = 127;
519 
520  while( (rc = libssh2_channel_close(channel)) == LIBSSH2_ERROR_EAGAIN )
521  waitsocket(sock, session);
522 
523  if( rc == 0 )
524  {
525  exitcode = libssh2_channel_get_exit_status( channel );
526 
527  // Just for libssh2 1.2.8 or greater
528  // libssh2_channel_get_exit_signal(channel, &exitsignal,
529  // NULL, NULL, NULL, NULL, NULL);
530  }
531 
532  if (!exitcode)
533  printf("\nEXIT: %d bytecount: %d\n", exitcode, bytecount);
534  else
535  printf("\nExit channel was ok!\n");
536 
537  return 0;
538 }
539 
540 int SimpleSSHClient::waitsocket(int socket_fd, LIBSSH2_SESSION *session)
541 {
542  struct timeval timeout;
543  int rc;
544  fd_set fd;
545  fd_set *writefd = NULL;
546  fd_set *readfd = NULL;
547  int dir;
548 
549  timeout.tv_sec = 10;
550  timeout.tv_usec = 0;
551 
552  FD_ZERO(&fd);
553 
554  FD_SET(socket_fd, &fd);
555 
556  /* now make sure we wait in the correct direction */
557  dir = libssh2_session_block_directions(session);
558 
559  if(dir & LIBSSH2_SESSION_BLOCK_INBOUND)
560  readfd = &fd;
561 
562  if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND)
563  writefd = &fd;
564 
565  rc = select(socket_fd + 1, readfd, writefd, NULL, &timeout);
566 
567  return rc;
568 }