#include "hydra-mod.h"
#ifndef LIBSSH
void
dummy_ssh() {
  printf("\n");
}
#else

#warning "If compilation of hydra-ssh2 fails, you are not using v0.4.x. Download from http://www.libssh.org"

#include <libssh/libssh.h>

ssh_session session = NULL;

extern char *HYDRA_EXIT;
int new_session = 1;

int start_ssh2(int s, unsigned long int ip, int port, unsigned char options, char *miscptr, FILE * fp) {
  char *empty = "";
  char *login, *pass, keep_login[300];
  char *buf;
  struct sockaddr_in targetip;
  int auth_state = 0, rc = 0, i = 0;

  if (strlen(login = hydra_get_next_login()) == 0)
    login = empty;
  if (strlen(pass = hydra_get_next_password()) == 0)
    pass = empty;

  memset(&targetip, 0, sizeof(targetip));
  memcpy(&targetip.sin_addr.s_addr, &ip, 4);
  targetip.sin_family = AF_INET;
#ifdef CYGWIN
  buf = inet_ntoa((struct in_addr) targetip.sin_addr);
#else
  buf = malloc(20);
  inet_ntop(AF_INET, &targetip.sin_addr, buf, 20);
#endif
  if (new_session) {
    session = ssh_new();
    ssh_options_set(session, SSH_OPTIONS_PORT, &port);
    ssh_options_set(session, SSH_OPTIONS_HOST, buf);
    ssh_options_set(session, SSH_OPTIONS_USER, login);
    ssh_options_set(session, SSH_OPTIONS_COMPRESSION_C_S, "none");
    ssh_options_set(session, SSH_OPTIONS_COMPRESSION_S_C, "none");
    if (ssh_connect(session) != 0) {
      ssh_disconnect(session);
      return 3;
    }

#ifndef CYGWIN
    free(buf);
    buf = NULL;
#endif
    if ((rc = ssh_userauth_none(session, NULL)) == SSH_AUTH_ERROR)
      return 4;
    else if (rc == SSH_AUTH_SUCCESS) {
      hydra_report_found_host(port, ip, "ssh2", fp);
      hydra_completed_pair_found();
      if (memcmp(hydra_get_next_pair(), &HYDRA_EXIT, sizeof(HYDRA_EXIT)) == 0)
        return 2;
      else
        return 1;           
    }
  } else
    new_session = 1;

  auth_state = ssh_auth_list(session);
  if ((auth_state & SSH_AUTH_METHOD_PASSWORD) > 0) {
    auth_state = ssh_userauth_password(session, NULL, pass);
  } else if ((auth_state & SSH_AUTH_METHOD_INTERACTIVE) > 0) {
    auth_state = ssh_userauth_kbdint(session, NULL, NULL);
    while (auth_state == SSH_AUTH_INFO) {
      rc = ssh_userauth_kbdint_getnprompts(session);
      for (i = 0; i < rc; i++)
        ssh_userauth_kbdint_setanswer(session, i, pass);
      auth_state = ssh_userauth_kbdint(session, NULL, NULL);
    }
  } else
    return 5;

  if (auth_state == SSH_AUTH_SUCCESS || auth_state == SSH_AUTH_PARTIAL) {
    ssh_disconnect(session);	/* this automagically frees the ssh_opt buffer */
    hydra_report_found_host(port, ip, "ssh2", fp);
    hydra_completed_pair_found();
    if (memcmp(hydra_get_next_pair(), &HYDRA_EXIT, sizeof(HYDRA_EXIT)) == 0)
      return 2;
    return 1;
  } else {
    strcpy(keep_login, login);
    hydra_completed_pair();
    if (memcmp(hydra_get_next_pair(), &HYDRA_EXIT, sizeof(HYDRA_EXIT)) == 0)
      return 2;
    login = hydra_get_next_login();
    if (strcmp(login, keep_login) == 0)
      new_session = 0;
    else
      ssh_disconnect(session);	/* this automagically frees the session buffer */
    return 1;
  }

  /* not reached */
  return 1;
}

void service_ssh2(unsigned long int ip, int sp, unsigned char options, char *miscptr, FILE * fp, int port) {
  int run = 1, next_run = 1, sock = -1;

  hydra_register_socket(sp);
  if (memcmp(hydra_get_next_pair(), &HYDRA_EXIT, sizeof(HYDRA_EXIT)) == 0)
    return;
  while (1) {
    switch (run) {
    case 1:                    /* connect and service init function */
      next_run = start_ssh2(sock, ip, port, options, miscptr, fp);
      break;
    case 2:
      hydra_child_exit(0);
    case 3:                    /* clean exit */
      fprintf(stderr, "Error: could not connect to target port %d\n", port);
      hydra_child_exit(1);
    case 4:
      fprintf(stderr, "Error: ssh2 protocol error\n");
      hydra_child_exit(2);
    case 5:
      fprintf(stderr, "Error: ssh2 target does not support password auth\n");
      hydra_child_exit(2);
    default:
      hydra_report(stderr, "Caught unknown return code, exiting!\n");
      hydra_child_exit(-1);
    }
    run = next_run;
  }
}

#endif
