#include "hydra-mod.h"

extern char *HYDRA_EXIT;
char *buf;

char *
netbios_name(char *orig)
{
  int i, len;
  char *ret = malloc(40);

  memset(ret, 0, 40);
  len = strlen(orig);
  for (i = 0; i < 16; i++) {
    if (i >= len)
      strcat(ret, "CA");
    else {
      int odiv, omod;

      odiv = (orig[i] / 16) + 'A';
      omod = (orig[i] % 16) + 'A';
      ret[strlen(ret)] = odiv;
      ret[strlen(ret)] = omod;
    }
  }
  return (ret);
}

int
smb_init(int s, char *name)
{
  char *myname = netbios_name("HYDRA");
  char *hername = netbios_name(name);
  unsigned char buf[400];
  unsigned char buf1[] = { 0x81, 0x00, 0x00, 0x44, 0x20 };
  unsigned char buf2[] = { 0x00, 0x20 };
  unsigned char buf3[] = { 0x00 };
  unsigned char prot[] = { 0x00, 0x00,
    0x00, 0x89, 0xFF, 0x53, 0x4D, 0x42, 0x72, 0x00,
    0x00, 0x00, 0x00, 0x18, 0x01, 0x20, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x66, 0x00, 0x02, 0x50, 0x43,
    0x20, 0x4E, 0x45, 0x54, 0x57, 0x4F, 0x52, 0x4B,
    0x20, 0x50, 0x52, 0x4F, 0x47, 0x52, 0x41, 0x4D,
    0x20, 0x31, 0x2E, 0x30, 0x00, 0x02, 0x4D, 0x49,
    0x43, 0x52, 0x4F, 0x53, 0x4F, 0x46, 0x54, 0x20,
    0x4E, 0x45, 0x54, 0x57, 0x4F, 0x52, 0x4B, 0x53,
    0x20, 0x31, 0x2E, 0x30, 0x33, 0x00, 0x02, 0x4D,
    0x49, 0x43, 0x52, 0x4F, 0x53, 0x4F, 0x46, 0x54,
    0x20, 0x4E, 0x45, 0x54, 0x57, 0x4F, 0x52, 0x4B,
    0x53, 0x20, 0x33, 0x2e, 0x30, 0x00, 0x02, 0x4c,
    0x41, 0x4e, 0x4d, 0x41, 0x4e, 0x31, 0x2e, 0x30,
    0x00, 0x02, 0x4c, 0x4d, 0x31, 0x2e, 0x32, 0x58,
    0x30, 0x30, 0x32, 0x00, 0x02, 0x53, 0x61, 0x6d,
    0x62, 0x61, 0x00
  };
  unsigned char *big;
  int pad = 0;

  big = malloc(sizeof(buf1) + strlen(hername) + sizeof(buf2) + strlen(myname)
               + sizeof(buf3));
  memcpy(big, buf1, sizeof(buf1));
  pad += sizeof(buf1);
  memcpy(big + pad, hername, strlen(hername));
  pad += strlen(hername);
  memcpy(big + pad, buf2, sizeof(buf2));
  pad += sizeof(buf2);
  memcpy(big + pad, myname, strlen(myname));
  pad += strlen(myname);
  memcpy(big + pad, buf3, sizeof(buf3));
  pad += sizeof(buf3);

  hydra_send(s, (char *) big, pad, 0);

  free(myname);
  free(hername);
  free(big);

  hydra_recv(s, (char *) buf, sizeof(buf));
  if (buf[0] != 0x82)
    return -1;                  /* failed */

  hydra_send(s, (char *) prot, sizeof(prot), 0);

  hydra_recv(s, (char *) buf, sizeof(buf));
  if (buf[9] == 0)
    return 0;                   /* The remote host is willing to talk to us */
  else
    return -1;                  /* failed */
}

int
smb_login(int s, char *login, char *password)
{
  int len = strlen(login) + strlen(password) + 57;
  int bcc = 2 + strlen(login) + strlen(password);
  int len_hi = len / 256, len_low = len % 256;
  int bcc_hi = bcc / 256, bcc_lo = bcc % 256;
  int pass_len = strlen(password) + 1;
  int pass_len_hi = pass_len / 256, pass_len_lo = pass_len % 256;

  unsigned char req[] = { 0x00, 0x00,
    0x00, 0x00, 0xFF, 0x53, 0x4D, 0x42, 0x73, 0x00,
    0x00, 0x00, 0x00, 0x18, 0x01, 0x20, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00,
    0x00, 0x00, 0x0A, 0xFF, 0x00, 0x00, 0x00, 0x04,
    0x11, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00
  };
  unsigned char *t;
  unsigned char buf[2048];



  req[2] = len_hi;
  req[3] = len_low;
  req[51] = pass_len_lo;
  req[52] = pass_len_hi;
  req[57] = bcc_lo;
  req[58] = bcc_hi;

  t = malloc(len + 4);
  memset(t, 0, len + 4);

  memcpy(t, req, sizeof(req));
  memcpy(t + sizeof(req), password, strlen(password) + 1);
  memcpy(t + sizeof(req) + strlen(password) + 1, login, strlen(login) + 1);

  hydra_send(s, (char *) t, sizeof(req) + strlen(password) + 1 + strlen(login) + 1, 0);
  free(t);
  hydra_recv(s, (char *) buf, sizeof(buf));

  if ((buf[4] == 0xFF) && !buf[9])
    return 0;
  else
    return -1;
}

int
start_smb(int s, unsigned long int ip, int port, unsigned char options, char *miscptr, FILE * fp)
{
  char *empty = "";
  char *login, *pass;

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

  if (smb_login(s, login, pass) == 0) {
    hydra_report_found_host(port, ip, "smb", fp);
    hydra_completed_pair_found();
    free(buf);
    hydra_disconnect(s);
    if (memcmp(hydra_get_next_pair(), &HYDRA_EXIT, sizeof(HYDRA_EXIT)) == 0)
      return 3;
    return 1;
  }
  free(buf);
  hydra_completed_pair();
  if (memcmp(hydra_get_next_pair(), &HYDRA_EXIT, sizeof(HYDRA_EXIT)) == 0)
    return 3;
  hydra_disconnect(s);
  return 1;
}

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

  hydra_register_socket(sp);
  if (memcmp(hydra_get_next_pair(), &HYDRA_EXIT, sizeof(HYDRA_EXIT)) == 0)
    return;
  for (;;) {
    switch (run) {
    case 1:                    /* connect and service init function */
      if (sock >= 0)
        sock = hydra_disconnect(sock);
//      usleep(300000);
      if ((options & OPTION_SSL) == 0) {
        if (port != 0)
          myport = port;
        sock = hydra_connect_tcp(ip, myport);
        port = myport;
      } else {
        if (port != 0)
          mysslport = port;
        sock = hydra_connect_ssl(ip, mysslport);
        port = mysslport;
      }
      if (sock < 0) {
        fprintf(stderr, "Error: Child with pid %d terminating, can not connect\n", (int) getpid());
        hydra_child_exit(1);
      }
      if (smb_init(sock, "*SMBSERVER") < 0) {
        fprintf(stderr, "Error: smb_init() failed\n");
        hydra_child_exit(2);
        exit(-1);
      }
      free(buf);
      next_run = 2;
      break;
    case 2:                    /* run the cracking function */
      next_run = start_smb(sock, ip, port, options, miscptr, fp);
      break;
    case 3:                    /* clean exit */
      if (sock >= 0)
        sock = hydra_disconnect(sock);
      hydra_child_exit(0);
      return;
    default:
      fprintf(stderr, "Caught unknown return code, exiting!\n");
      hydra_child_exit(0);
      exit(-1);
    }
    run = next_run;
  }
}
