/*
 * hydra (c) 2001-2013 by van Hauser / THC <vh@thc.org>
 * http://www.thc.org
 *
 * Parallized network login hacker. Usage only allowed for legal purposes.
 *
 * License: GNU AFFERO GENERAL PUBLIC LICENSE v3.0, see LICENSE file
 */

#include "hydra.h"
#include "bfg.h"

extern void service_telnet(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_ftp(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_ftps(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_pop3(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_vmauthd(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_imap(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_ldap2(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_ldap3(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_ldap3_cram_md5(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_ldap3_digest_md5(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_cisco(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_cisco_enable(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_vnc(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_socks5(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_rexec(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_rlogin(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_rsh(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_nntp(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_http_head(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_http_get(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_http_get_form(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_http_post_form(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_icq(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_pcnfs(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_mssql(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_cvs(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_snmp(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_smtp(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_smtp_enum(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_teamspeak(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_pcanywhere(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_http_proxy(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_xmpp(char *target, char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_irc(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_http_proxy_urlenum(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
// ADD NEW SERVICES HERE

#ifdef HAVE_MATH_H
extern void service_mysql(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_mysql_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
#endif
#ifdef LIBPOSTGRES
extern void service_postgres(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_postgres_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
#endif
#ifdef LIBOPENSSL
extern void service_smb(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_smb_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_oracle_listener(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_oracle_listener_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_oracle_sid(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_oracle_sid_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_sip(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_sip_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_rdp(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_rdp_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
#endif
#ifdef LIBSAPR3
extern void service_sapr3(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_sapr3_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
#endif
#ifdef LIBFIREBIRD
extern void service_firebird(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_firebird_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
#endif
#ifdef LIBAFP
extern void service_afp(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_afp_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
#endif
#ifdef LIBNCP
extern void service_ncp(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_ncp_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
#endif
#ifdef LIBSSH
extern void service_ssh(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_ssh_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern void service_sshkey(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_sshkey_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
#endif
#ifdef LIBSVN
extern void service_svn(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_svn_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
#endif
#ifdef LIBORACLE
extern void service_oracle(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_oracle_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
#endif

extern int service_cisco_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_cisco_enable_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_cvs_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_smtp_enum_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_http_form_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_ftp_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_http_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_icq_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_imap_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_irc_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_ldap_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_mssql_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_nntp_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_pcanywhere_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_pcnfs_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_pop3_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_http_proxy_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_asterisk_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_rexec_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_rlogin_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_rsh_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_smtp_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_snmp_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_socks5_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_teamspeak_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_telnet_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_http_proxy_urlenum_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_vmauthd_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_vnc_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
extern int service_xmpp_init(char *ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
// ADD NEW SERVICES HERE


// ADD NEW SERVICES HERE
char *SERVICES = "asterisk afp cisco cisco-enable cvs firebird ftp ftps http[s]-{head|get} http[s]-{get|post}-form http-proxy http-proxy-urlenum icq imap[s] irc ldap2[s] ldap3[-{cram|digest}md5][s] mssql mysql ncp nntp oracle oracle-listener oracle-sid pcanywhere pcnfs pop3[s] postgres rdp rexec rlogin rsh sapr3 sip smb smtp[s] smtp-enum snmp socks5 ssh sshkey svn teamspeak telnet[s] vmauthd vnc xmpp";

#define MAXBUF       520
#define MAXLINESIZE  ( ( MAXBUF / 2 ) - 4 )
#define MAXTASKS     128
#define MAXSERVERS   16
#define MAXFAIL      3
#define MAXENDWAIT   20
#define WAITTIME     32
#define TASKS        16
#define SKIPLOGIN    256
#define USLEEP_LOOP  10
#define MAX_LINES    50000000 // 50 millions, do not put more than 65millions
#define MAX_BYTES    500000000 // 500 millions, do not put more than 650millions

#define RESTOREFILE "./hydra.restore"

#define PROGRAM   "Hydra"
#define VERSION   "v7.5"
#define AUTHOR    "van Hauser/THC"
#define EMAIL     "<vh@thc.org>"
#define AUTHOR2   "David Maciejak"
#define EMAIL2    "<david.maciejak@gmail.com>"
#define RESOURCE  "http://www.thc.org/thc-hydra"

extern char *hydra_strcasestr(const char *haystack, const char *needle);
extern void hydra_tobase64(unsigned char *buf, int buflen, int bufsize);
extern char *hydra_string_replace(const char *string, const char *substr, const char *replacement);
extern char *hydra_address2string(char *address);
extern int colored_output;

void hydra_kill_head(int head_no, int killit, int fail);

// some structure definitions
typedef struct {
  pid_t pid;
  int sp[2];
  int target_no;
  char *current_login_ptr;
  char *current_pass_ptr;
  char reverse[256];
  int active;
  int redo;
  time_t last_seen;
} hydra_head;

typedef struct {
  char *target;
  char ip[36];
  char *login_ptr;
  char *pass_ptr;
  unsigned long int login_no;
  unsigned long int pass_no;
  unsigned long int sent;
  int pass_state;
  int use_count;
  int done;                     // 0 if active, 1 if finished scanning, 2 if error (for RESTOREFILE), 3 could not be resolved
  int fail_count;
  int redo_state;
  int redo;
  int ok;
  int failed;
  int skipcnt;
  char *redo_login[MAXTASKS * 2 + 2];
  char *redo_pass[MAXTASKS * 2 + 2];
  char *skiplogin[SKIPLOGIN];
//  char *bfg_ptr[MAXTASKS];
} hydra_target;

typedef struct {
  int active;                   // active tasks of hydra_options.max_use
  int targets;
  int finished;
  int exit;
  unsigned long int todo_all;
  unsigned long int todo;
  unsigned long int sent;
  unsigned long int found;
  unsigned long int countlogin;
  unsigned long int countpass;
  size_t  sizelogin;
  size_t sizepass;
  FILE *ofp;
} hydra_brain;

typedef struct {
  int mode;                     // valid modes: 0 = -l -p, 1 = -l -P, 2 = -L -p, 3 = -L -P, 4 = -l -x, 6 = -L -x, +8 if -e r, +16 if -e n, +32 if -e s, 64 = -C | bit 128 undefined
  int loop_mode;                // valid modes: 0 = password, 1 = user
  int ssl;
  int restore;
  int debug;                    // is external - for restore 
  int verbose;                  // is external - for restore 
  int showAttempt;
  int tasks;
  int try_null_password;
  int try_password_same_as_login;
  int try_password_reverse_login;
  int exit_found;
  int max_use;
  char *login;
  char *loginfile;
  char *pass;
  char *passfile;
  char *outfile_ptr;
  char *infile_ptr;
  char *colonfile;
  int waittime;                 // is external - for restore 
  int conwait;                  // is external - for restore 
  unsigned int port;            // is external - for restore 
  char *miscptr;
  char *server;
  char *service;
  char bfg;
} hydra_option;

typedef struct {
  char *name;
  int port;
  int port_ssl;
} hydra_portlist;

// external vars 
extern char HYDRA_EXIT[5];
extern int errno;
extern int debug;
extern int verbose;
extern int waittime;
extern int port;
extern int found;
extern int use_proxy;
extern int proxy_string_port;
extern char proxy_string_ip[36];
extern char proxy_string_type[10];
extern char *proxy_authentication;
extern char *cmdlinetarget;
extern char *fe80;

// required global vars 
char *prg;
size_t size_of_data = -1;
hydra_head **hydra_heads = NULL;
hydra_target **hydra_targets = NULL;
hydra_option hydra_options;
hydra_brain hydra_brains;
char *sck = NULL;
int prefer_ipv6 = 0, conwait = 0, loop_cnt = 0, fck = 0, options = 0, killed = 0;
int child_head_no = -1, child_socket;

// moved for restore feature 
int process_restore = 0, dont_unlink;
char *login_ptr = NULL, *pass_ptr = "", *csv_ptr = NULL, *servers_ptr = NULL;
size_t countservers = 1, sizeservers = 0;
char empty_login[2] = "", unsupported[500] = "";

// required to save stack memory
char snpbuf[MAXBUF];
int snpdone, snp_is_redo, snpbuflen, snpi, snpj, snpdont;

#include "performance.h"

void help(int ext) {
  printf("Syntax: hydra [[[-l LOGIN|-L FILE] [-p PASS|-P FILE]] | [-C FILE]] [-e nsr]"
         " [-o FILE] [-t TASKS] [-M FILE [-T TASKS]] [-w TIME] [-W TIME] [-f] [-s PORT]"
#ifdef HAVE_MATH_H
         " [-x MIN:MAX:CHARSET]"
#endif
         " [-SuvV46] "
         //"[server service [OPT]]|"
         "[service://server[:PORT][/OPT]]\n");
  printf("\nOptions:\n");
  if (ext) printf("  -R        restore a previous aborted/crashed session\n");
#ifdef LIBOPENSSL
  if (ext) printf("  -S        perform an SSL connect\n");
#endif
  if (ext) printf("  -s PORT   if the service is on a different default port, define it here\n");
  printf("  -l LOGIN or -L FILE  login with LOGIN name, or load several logins from FILE\n");
  printf("  -p PASS  or -P FILE  try password PASS, or load several passwords from FILE\n");
#ifdef HAVE_MATH_H
  if (ext) printf("  -x MIN:MAX:CHARSET  password bruteforce generation, type \"-x -h\" to get help\n");
#endif
  if (ext) printf("  -e nsr    try \"n\" null password, \"s\" login as pass and/or \"r\" reversed login\n");
  if (ext) printf("  -u        loop around users, not passwords (effective! implied with -x)\n");
  printf("  -C FILE   colon separated \"login:pass\" format, instead of -L/-P options\n");
  printf("  -M FILE   list of servers to be attacked in parallel, one entry per line\n");
  if (ext) printf("  -o FILE   write found login/password pairs to FILE instead of stdout\n");
  if (ext) printf("  -f / -F   exit when a login/pass pair is found (-M: -f per host, -F global)\n");
  printf("  -t TASKS  run TASKS number of connects in parallel (per host, default: %d)\n", TASKS);
  if (ext) printf("  -w / -W TIME  waittime for responses (%ds) / between connects per thread\n", WAITTIME);
  if (ext) printf("  -4 / -6   prefer IPv4 (default) or IPv6 addresses\n");
  if (ext) printf("  -v / -V / -d  verbose mode / show login+pass for each attempt / debug mode \n");
  printf("  -U        service module usage details\n");
  if (ext == 0) printf("  -h        more command line options (complete help)\n");
  printf("  server    the target server (use either this OR the -M option)\n");
  printf("  service   the service to crack (see below for supported protocols)\n");
  printf("  OPT       some service modules support additional input (-U for module help)\n");

  printf("\nSupported services: %s\n", SERVICES);
  printf("\n%s is a tool to guess/crack valid login/password pairs - usage only allowed\nfor legal purposes. This tool is licensed under AGPL v3.0.\nThe newest version is always available at %s\n", PROGRAM, RESOURCE);
  if (ext && strlen(unsupported) > 0) {
    if (unsupported[strlen(unsupported) - 1] == ' ')
      unsupported[strlen(unsupported) - 1] = 0;
    printf("These services were not compiled in: %s.\n", unsupported);
  }
  if (ext) {
    printf("\nUse HYDRA_PROXY_HTTP/HYDRA_PROXY and HYDRA_PROXY_AUTH environment for a proxy.\n");
    printf("E.g.:  %% export HTTP_PROXY=socks5://127.0.0.1:9150 (or socks4:// or connect://)\n");
    printf("       %% export HTTP_PROXY_HTTP=http://proxy:8080\n");
    printf("       %% export HTTP_PROXY_AUTH=user:pass\n");
  }
  
  printf("\nExample%s:%s  hydra -l user -P passlist.txt ftp://192.168.0.1\n", ext == 0 ? "" : "s", ext == 0 ? "" : "\n");
  if (ext) {
    printf("  hydra -L userlist.txt -p defaultpw imap://192.168.0.1/PLAIN\n");
    printf("  hydra -C defaults.txt -6 pop3s://[fe80::2c:31ff:fe12:ac11]:143/TLS:DIGEST-MD5\n");
  }
  exit(-1);
}

void help_bfg() {
  printf("Hydra bruteforce password generation option usage:\n\n"
         "  -x MIN:MAX:CHARSET\n\n"
         "     MIN     is the minimum number of characters in the password\n"
         "     MAX     is the maximum number of characters in the password\n"
         "     CHARSET is a specification of the characters to use in the generation\n"
         "             valid CHARSET values are: 'a' for lowercase letters,\n"
         "             'A' for uppercase letters, '1' for numbers, and for all others,\n"
         "             just add their real representation.\n\n"
         "Examples:\n"
         "   -x 3:5:a  generate passwords from length 3 to 5 with all lowercase letters\n"
         "   -x 5:8:A1 generate passwords from length 5 to 8 with uppercase and numbers\n"
         "   -x 1:3:/  generate passwords from length 1 to 3 containing only slashes\n"
         "   -x 5:5:/%%,.-  generate passwords with length 5 which consists only of /%%,.-\n");
  printf("\nThe bruteforce mode was made by Jan Dlabal, http://houbysoft.com/bfg/\n");
  exit(-1);
}

void module_usage() {
  int find = 0;

  if (hydra_options.service) {
    printf("\nHelp for module %s:\n============================================================================\n", hydra_options.service);
    if ((strcmp(hydra_options.service, "oracle") == 0) || (strcmp(hydra_options.service, "ora") == 0)) {
      printf("Module oracle / ora is optionally taking the ORACLE SID, default is \"ORCL\"\n\n");
      find = 1;
    }
    if ((strcmp(hydra_options.service, "oracle-listener") == 0) || (strcmp(hydra_options.service, "tns") == 0)) {
      printf("Module oracle-listener / tns is optionally taking the mode the password is stored as, could be PLAIN (default) or CLEAR\n\n");
      find = 1;
    }
    if (strcmp(hydra_options.service, "cvs") == 0) {
      printf("Module cvs is optionally taking the repository name to attack, default is \"/root\"\n\n");
      find = 1;
    }
    if (strcmp(hydra_options.service, "xmpp") == 0) {
      printf("Module xmpp is optionally taking one authentication type of:\n"
             "  LOGIN (default), PLAIN, CRAM-MD5, DIGEST-MD5, SCRAM-SHA1\n\n"
             "Note, the target passed should be a fdqn as the value is used in the Jabber init request, example: hermes.jabber.org\n\n");
      find = 1;
    }
    if (!find && (strcmp(hydra_options.service, "pop3") == 0)) {
      printf("Module pop3 is optionally taking one authentication type of:\n"
             "  CLEAR (default), LOGIN, PLAIN, CRAM-MD5, CRAM-SHA1,\n"
             "  CRAM-SHA256, DIGEST-MD5, NTLM.\n"
             "Additionally TLS encryption via STLS can be enforced with the TLS option.\n\n"
             "Example: pop3://target/TLS:PLAIN\n");
      find = 1;
    }
    if (!find && (strcmp(hydra_options.service, "rdp") == 0)) {
      printf("Module rdp is optionally taking the windows domain name.\n"
             "For example:\nhydra rdp://192.168.0.1/firstdomainname -l john -p doe\n\n");
      find = 1;
    }
    if (!find && (strcmp(hydra_options.service, "nntp") == 0)) {
      printf("Module nntp is optionally taking one authentication type of:\n"
             "  USER (default), LOGIN, PLAIN, CRAM-MD5, DIGEST-MD5, NTLM\n\n");
      find = 1;
    }
    if (!find && (strcmp(hydra_options.service, "imap") == 0)) {
      printf("Module imap is optionally taking one authentication type of:\n"
             "  CLEAR or APOP (default), LOGIN, PLAIN, CRAM-MD5, CRAM-SHA1,\n"
             "  CRAM-SHA256, DIGEST-MD5, NTLM\n"
             "Additionally TLS encryption via STARTTLS can be enforced with the TLS option.\n\n"
             "Example: imap://target/TLS:PLAIN\n");
      find = 1;
    }
    if (!find && (strcmp(hydra_options.service, "smtp-enum")) == 0) {
      printf("Module smtp-enum is optionally taking one SMTP command of:\n\n"
             "VRFY (default), EXPN, RCPT (which will connect using \"root\" account)\n"
             "login parameter is used as username and password parameter as the domain name\n"
             "For example to test if john@localhost exists on 192.168.0.1:\n"
             "hydra smtp-enum://192.168.0.1/vrfy -l john -p localhost\n\n");
      find = 1;
    }
    if (!find && (strcmp(hydra_options.service, "smtp")) == 0) {
      printf("Module smtp is optionally taking one authentication type of:\n"
             "  LOGIN (default), PLAIN, CRAM-MD5, DIGEST-MD5, NTLM\n\n"
             "Additionally TLS encryption via STARTTLS can be enforced with the TLS option.\n\n"
             "Example: smtp://target/TLS:PLAIN\n");
      find = 1;
    }
    if (!find && (strcmp(hydra_options.service, "svn") == 0)) {
      printf("Module svn is optionally taking the repository name to attack, default is \"trunk\"\n\n");
      find = 1;
    }
    if (!find && (strcmp(hydra_options.service, "ncp") == 0)) {
      printf("Module ncp is optionally taking the full context, for example \".O=cx\"\n\n");
      find = 1;
    }
    if (!find && (strcmp(hydra_options.service, "firebird") == 0)) {
      printf("Module firebird is optionally taking the database path to attack,\n"
             "default is \"C:\\Program Files\\Firebird\\Firebird_1_5\\security.fdb\"\n\n");
      find = 1;
    }
    if (!find && (strcmp(hydra_options.service, "mysql") == 0)) {
      printf("Module mysql is optionally taking the database to attack, default is \"mysql\"\n\n");
      find = 1;
    }
    if (!find && (strcmp(hydra_options.service, "irc") == 0)) {
      printf("Module irc is optionally taking the general server password, if the server is requiring one\n"
             "and none is passed the password from -p/-P will be used\n\n");
      find = 1;
    }
    if (!find && (strcmp(hydra_options.service, "postgres") == 0)) {
      printf("Module postgres is optionally taking the database to attack, default is \"template1\"\n\n");
      find = 1;
    }
    if (!find && (strcmp(hydra_options.service, "telnet") == 0)) {
      printf("Module telnet is optionally taking the string which is displayed after\n"
             "a successful login (case insensitive), use if the default in the telnet\n"
             "module produces too many false positives\n\n");
      find = 1;
    }
    if (!find && (strcmp(hydra_options.service, "sapr3") == 0)) {
      printf("Module sapr3 requires the client id, a number between 0 and 99\n\n");
      find = 1;
    }
    if (!find && (strcmp(hydra_options.service, "sshkey") == 0)) {
      printf("Module sshkey does not provide additional options, although the semantic for\n"
             "options -p and -P is changed:\n"
             "  -p expects a path to an unencrypted private key in PEM format.\n"
             "  -P expects a filename containing a list of path to some unencrypted\n"
             "     private keys in PEM format.\n\n");
      find = 1;
    }
    if (!find && (strcmp(hydra_options.service, "cisco-enable") == 0)) {
      printf("Module cisco-enable is optionally taking the logon password for the cisco device\n"
             "Note: if AAA authentication is used, use the -l option for the username\n"
             "and the optional parameter for the password of the user.\n"
             "Examples:\n"
             "  hydra -P pass.txt target cisco-enable  (direct console access)\n"
             "  hydra -P pass.txt -m cisco target cisco-enable  (Logon password cisco)\n"
             "  hydra -l foo -m bar -P pass.txt -m cisco target cisco-enable  (AAA Login foo, password bar)\n");
      find = 1;
    }
    if (!find && (strcmp(hydra_options.service, "cisco") == 0)) {
      printf("Module cisco is optionally taking the keyword ENTER, it then sends an initial\n"
             "ENTER when connecting to the service.\n");
      find = 1;
    }
    if (!find && ((strcmp(hydra_options.service, "ldap2") == 0)
                  || (strcmp(hydra_options.service, "ldap3") == 0)
                  || (strcmp(hydra_options.service, "ldap3-crammd5") == 0)
                  || (strcmp(hydra_options.service, "ldap3-digestmd5") == 0))
      ) {
      printf("Module %s is optionally taking the DN (depending of the auth method choosed\n"
             "Note: you can also specify the DN as login when Simple auth method is used).\n"
             "The keyword \"^USER^\" is replaced with the login.\n"
             "Special notes for Simple method has 3 operation modes: anonymous, (no user no pass),\n"
             "unauthenticated (user but no pass), user/pass authenticated (user and pass).\n"
             "So don't forget to set empty string as user/pass to test all modes.\n"
             "Hint: to authenticate to a windows active directy ldap, this is usually\n"
             " cn=^USER^,cn=users,dc=foo,dc=bar,dc=com for domain foo.bar.com\n\n", hydra_options.service);
      find = 1;
    }
    if (!find && ((strcmp(hydra_options.service, "smb") == 0) || (strcmp(hydra_options.service, "smbnt") == 0))) {
      printf("Module smb default value is set to test both local and domain account, using a simple password with NTLM dialect.\n"
             "Note: you can set the group type using LOCAL or DOMAIN keyword\n"
             "      or other_domain:{value} to specify a trusted domain.\n"
             "      you can set the password type using HASH or MACHINE keyword\n"
             "      (to use the Machine's NetBIOS name as the password).\n"
             "      you can set the dialect using NTLMV2, NTLM, LMV2, LM keyword.\n"
             "Example: \n"
             "      hydra smb://microsoft.com  -l admin -p tooeasy -m \"local lmv2\"\n"
             "      hydra smb://microsoft.com  -l admin -p D5731CFC6C2A069C21FD0D49CAEBC9EA:2126EE7712D37E265FD63F2C84D2B13D::: -m \"local hash\"\n"
             "      hydra smb://microsoft.com  -l admin -p tooeasy -m \"other_domain:SECONDDOMAIN\"\n\n");
      find = 1;
    }
    if (!find && ((strcmp(hydra_options.service, "http-get-form") == 0)
                  || (strcmp(hydra_options.service, "https-get-form") == 0)
                  || (strcmp(hydra_options.service, "http-post-form") == 0)
                  || (strcmp(hydra_options.service, "https-post-form") == 0)
                  || (strncmp(hydra_options.service, "http-form", 9) == 0)
                  || (strncmp(hydra_options.service, "https-form", 10) == 0)
                 )
      ) {
      printf("Module %s requires the page and the parameters for the web form.\n\n"
             "By default this module is configured to follow a maximum of 5 redirections in\n"
             "a row. It always gathers a new cookie from the same URL without variables\n"
             "The parameters take three \":\" separated values, plus optional values.\n"
             "(Note: if you need a colon in the option string as value, escape it with \"\\:\", but do not escape a \"\\\" with \"\\\\\".)\n" 
             "\nSyntax:   <url>:<form parameters>:<condition string>[:<optional>[:<optional>]\n"
             "First is the page on the server to GET or POST to (URL).\n"
             "Second is the POST/GET variables (taken from either the browser, proxy, etc.\n"
             " with usernames and passwords being replaced in the \"^USER^\" and \"^PASS^\"\n"
             " placeholders (FORM PARAMETERS)\n"
             "Third is the string that it checks for an *invalid* login (by default)\n"
             " Invalid condition login check can be preceded by \"F=\", successful condition\n"
             " login check must be preceded by \"S=\".\n"
             " This is where most people get it wrong. You have to check the webapp what a\n"
             " failed string looks like and put it in this parameter!\n"
             "The following parameters are optional:\n"
             " C=/page/uri     to define a different page to gather initial cookies from\n"
             " H=My-Hdr: foo   to send a user defined HTTP header with each request\n"
             "                 ^USER^ and ^PASS^ can also be put into these headers!\n"
             "Examples:\n"
             " \"/login.php:user=^USER^&pass=^PASS^:incorrect\"\n"
             " \"/login.php:user=^USER^&pass=^PASS^&colon=colon\\:escape:S=authlog=.*success\"\n"
             " \"/login.php:user=^USER^&pass=^PASS^&mid=123:authlog=.*failed\"\n"
             " \"/:user=^USER&pass=^PASS^:failed:H=Authorization: Basic dT1w:H=X-User: ^USER^\"\n"
             " \"/exchweb/bin/auth/owaauth.dll:destination=http%%3A%%2F%%2F<target>%%2Fexchange&flags=0&username=<domain>%%5C^USER^&password=^PASS^&SubmitCreds=x&trusted=0:reason=:C=/exchweb\"\n",
             hydra_options.service);
      find = 1;
    }
    if (!find && (strcmp(hydra_options.service, "http-proxy") == 0)) {
      printf("Module http-proxy is optionally taking the page to authenticate at.\n"
             "Default is http://www.microsoft.com/)\n"
             "Basic, DIGEST-MD5 and NTLM are supported and negotiated automatically.\n\n");
      find = 1;
    }
    if (!find && (strcmp(hydra_options.service, "http-proxy-urlenum") == 0)) {
      printf("Module http-proxy-urlenum only uses the -L option, not -x or -p/-P option.\n"
             "The -L loginfile must contain the URL list to try through the proxy.\n"
             "The proxy credentials cann be put as the optional parameter, e.g.\n"
             "   hydra -L urllist.txt -s 3128 target.com http-proxy-urlenum user:pass\n"
             "   hydra -L urllist.txt http-proxy-urlenum://target.com:3128/user:pass\n\n");
      find = 1;
    }
    if (!find && (strncmp(hydra_options.service, "snmp", 4) == 0)) {
      printf("Module snmp is optionally taking the following parameters:\n");
      printf("   READ  perform read requests (default)\n");
      printf("   WRITE perform write requests\n");
      printf("   1     use SNMP version 1 (default)\n");
      printf("   2     use SNMP version 2\n");
      printf("   3     use SNMP version 3\n");
      printf("           Note that SNMP version 3 usually uses both login and passwords!\n");
      printf("           SNMP version 3 has the following optional sub parameters:\n");
      printf("             MD5   use MD5 authentication (default)\n");
      printf("             SHA   use SHA authentication\n");
      printf("             DES   use DES encryption\n");
      printf("             AES   use AES encryption\n");
      printf("           if no -p/-P parameter is given, SNMPv3 noauth is performed, which\n");
      printf("           only requires a password (or username) not both.\n");
      printf("To combine the options, use colons (\":\"), e.g.:\n");
      printf("   hydra -L user.txt -P pass.txt -m 3:SHA:AES:READ target.com snmp\n");
      printf("   hydra -P pass.txt -m 2 target.com snmp\n");
      find = 1;
    }
    if (!find && ((strcmp(hydra_options.service, "http-get") == 0)
                  || (strcmp(hydra_options.service, "https-get") == 0)
                  || (strcmp(hydra_options.service, "http-post") == 0)
                  || (strcmp(hydra_options.service, "https-post") == 0))
      ) {
      printf("Module %s requires the page to authenticate.\n"
             "For example:  \"/secret\" or \"http://bla.com/foo/bar\" or \"https://test.com:8080/members\"\n\n",
             hydra_options.service);
      find = 1;
    }
  }
  if (!find)                    // this is also printed if the module does not exist at all
    printf("The Module %s does not need or support optional parameters\n", hydra_options.service);
  exit(0);
}

void hydra_debug(int force, char *string) {
  int i;

  if (!debug && !force)
    return;

  printf("[DEBUG] Code: %s   Time: %lu\n", string, (unsigned long int) time(NULL));
  printf("[DEBUG] Options: mode %d  ssl %d  restore %d  showAttempt %d  tasks %d  max_use %d tnp %d  tpsal %d  tprl %d  exit_found %d  miscptr %s  service %s\n",
         hydra_options.mode, hydra_options.ssl, hydra_options.restore, hydra_options.showAttempt, hydra_options.tasks, hydra_options.max_use,
         hydra_options.try_null_password, hydra_options.try_password_same_as_login, hydra_options.try_password_reverse_login, hydra_options.exit_found,
         hydra_options.miscptr == NULL ? "(null)" : hydra_options.miscptr, hydra_options.service);
  printf("[DEBUG] Brains: active %d  targets %d  finished %d  todo_all %lu  todo %lu  sent %lu  found %lu  countlogin %lu  sizelogin %lu  countpass %lu  sizepass %lu\n",
         hydra_brains.active, hydra_brains.targets, hydra_brains.finished, hydra_brains.todo_all, hydra_brains.todo, hydra_brains.sent, hydra_brains.found,
         (unsigned long int) hydra_brains.countlogin, (unsigned long int) hydra_brains.sizelogin, (unsigned long int) hydra_brains.countpass,
         (unsigned long int) hydra_brains.sizepass);
  for (i = 0; i < hydra_brains.targets; i++)
    printf
      ("[DEBUG] Target %d - target %s  ip %s  login_no %lu  pass_no %lu  sent %lu  pass_state %d  use_count %d  failed %d  done %d  fail_count %d  login_ptr %s  pass_ptr %s\n",
       i, hydra_targets[i]->target == NULL ? "(null)" : hydra_targets[i]->target, hydra_address2string(hydra_targets[i]->ip), hydra_targets[i]->login_no,
       hydra_targets[i]->pass_no, hydra_targets[i]->sent, hydra_targets[i]->pass_state, hydra_targets[i]->use_count, hydra_targets[i]->failed, hydra_targets[i]->done,
       hydra_targets[i]->fail_count, hydra_targets[i]->login_ptr == NULL ? "(null)" : hydra_targets[i]->login_ptr,
       hydra_targets[i]->pass_ptr == NULL ? "(null)" : hydra_targets[i]->pass_ptr);
  if (hydra_heads != NULL)
    for (i = 0; i < hydra_options.max_use; i++)
      printf("[DEBUG] Task %d - pid %d  active %d  redo %d  current_login_ptr %s  current_pass_ptr %s\n",
             i, (int) hydra_heads[i]->pid, hydra_heads[i]->active, hydra_heads[i]->redo,
             hydra_heads[i]->current_login_ptr == NULL ? "(null)" : hydra_heads[i]->current_login_ptr,
             hydra_heads[i]->current_pass_ptr == NULL ? "(null)" : hydra_heads[i]->current_pass_ptr);
}

void bail(char *text) {
  fprintf(stderr, "[ERROR] %s\n", text);
  exit(-1);
}

/*
void hydra_bfg_remove(int head_no) {
  int i = 0, j = 0;
  char *ptr;
  
  if (hydra_heads[head_no]->current_pass_ptr == NULL ||
      hydra_heads[head_no]->current_pass_ptr == hydra_targets[hydra_heads[head_no]->target_no]->pass_ptr ||
      hydra_heads[head_no]->current_pass_ptr[0] == 0)
    return;
  if (hydra_brains.countlogin > 1) {
    for (i = 0; i < hydra_options.max_use && j < 2; i++)
      if (hydra_targets[hydra_heads[head_no]->target_no]->bfg_ptr[i] == hydra_heads[head_no]->current_pass_ptr)
        j++;
    if (j != 1)
      return;
  }
  if (debug)
    printf("[DEBUG] bfg free of child %d ptr %p (%s)\n", head_no, hydra_heads[head_no]->current_pass_ptr, hydra_heads[head_no]->current_pass_ptr);
  // to prevent a race condition
  ptr = hydra_heads[head_no]->current_pass_ptr;
  hydra_targets[hydra_heads[head_no]->target_no]->bfg_ptr[i] = NULL;
  hydra_heads[head_no]->current_pass_ptr = NULL;
  free(ptr);
}
*/

void hydra_restore_write(int print_msg) {
  FILE *f;
  hydra_brain brain;
  char mynull[4] = { 0, 0, 0, 0 };
  int i = 0, j = 0;
  hydra_head hh;

  if (process_restore != 1)
    return;

  for (i = 0; i < hydra_brains.targets; i++)
    if (hydra_targets[j]->done != 1 && hydra_targets[j]->done != 3)
      j++;
  if (j == 0) {
    process_restore = 0;
    return;
  }

  if ((f = fopen(RESTOREFILE, "w")) == NULL) {
    fprintf(stderr, "[ERROR] Can not create restore file (%s) - \n", RESTOREFILE);
    perror("");
    process_restore = 0;
    return;
  } else
    if (debug)
      printf("[DEBUG] Writing restore file... ");

  fprintf(f, "%s\n", PROGRAM);
  memcpy(&brain, &hydra_brains, sizeof(hydra_brain));
  brain.targets = i;
  brain.ofp = NULL;
  brain.finished = brain.active = 0;
  fck = fwrite(&bf_options, sizeof(bf_options), 1, f);
  if (bf_options.crs != NULL)
    fck = fwrite(bf_options.crs, BF_CHARSMAX, 1, f);
  else
    fck = fwrite(mynull, sizeof(mynull), 1, f);
  fck = fwrite(&brain, sizeof(hydra_brain), 1, f);
  fck = fwrite(&hydra_options, sizeof(hydra_option), 1, f);
  fprintf(f, "%s\n", hydra_options.server == NULL ? "" : hydra_options.server);
  if (hydra_options.outfile_ptr == NULL)
    fprintf(f, "\n");
  else
    fprintf(f, "%s\n", hydra_options.outfile_ptr);
  fprintf(f, "%s\n%s\n", hydra_options.miscptr == NULL ? "" : hydra_options.miscptr, hydra_options.service);
  fck = fwrite(login_ptr, hydra_brains.sizelogin, 1, f);
  if (hydra_options.colonfile == NULL || hydra_options.colonfile == empty_login)
    fck = fwrite(pass_ptr, hydra_brains.sizepass, 1, f);
  for (j = 0; j < hydra_brains.targets; j++)
    if (hydra_targets[j]->done != 1) {
      fck = fwrite(hydra_targets[j], sizeof(hydra_target), 1, f);
      fprintf(f, "%s\n%d\n%d\n", hydra_targets[j]->target == NULL ? "" : hydra_targets[j]->target, (int)(hydra_targets[j]->login_ptr - login_ptr), (int)(hydra_targets[j]->pass_ptr - pass_ptr));
      fprintf(f, "%s\n%s\n", hydra_targets[j]->login_ptr, hydra_targets[j]->pass_ptr);
      if (hydra_targets[j]->redo)
        for (i = 0; i < hydra_targets[j]->redo; i++)
          fprintf(f, "%s\n%s\n", hydra_targets[j]->redo_login[i], hydra_targets[j]->redo_pass[i]);
      if (hydra_targets[j]->skipcnt)
        for (i = 0; i < hydra_targets[j]->skipcnt; i++)
          fprintf(f, "%s\n", hydra_targets[j]->skiplogin[i]);
    }
  for (j = 0; j < hydra_options.max_use; j++) {
    memcpy((char*)&hh, hydra_heads[j], sizeof(hydra_head));
    hh.active = 0;   // re-enable disabled heads
    if ((hh.current_login_ptr != NULL && hh.current_login_ptr != empty_login)
        || (hh.current_pass_ptr != NULL && hh.current_pass_ptr != empty_login)) {
      hh.redo = 1;
      if (print_msg && debug)
        printf("[DEBUG] we will redo the following combination: target %s  login \"%s\"  pass \"%s\"\n", hydra_targets[hh.target_no]->target,
               hh.current_login_ptr, hh.current_pass_ptr);
    }
    fck = fwrite((char*)&hh, sizeof(hydra_head), 1, f);
    if (hh.redo/* && (hydra_options.bfg == 0 || (hh.current_pass_ptr == hydra_targets[hh.target_no]->bfg_ptr[j] && isprint((char) hh.current_pass_ptr[0])))*/)
      fprintf(f, "%s\n%s\n", hh.current_login_ptr == NULL ? "" : hh.current_login_ptr, hh.current_pass_ptr == NULL ? "" : hh.current_pass_ptr);
    else
      fprintf(f, "\n\n");
  }

  fprintf(f, "%s\n", PROGRAM);
  fclose(f);
  if (debug)
    printf("done\n");
  if (print_msg)
    printf("The session file ./hydra.restore was written. Type \"hydra -R\" to resume session.\n");
  hydra_debug(0, "hydra_restore_write()");
}

void hydra_restore_read() {
  FILE *f;
  char mynull[4];
  int i, j;
  char out[1024], *ptr;

  ptr = out;
  if ((f = fopen(RESTOREFILE, "r")) == NULL) {
    fprintf(stderr, "[ERROR] restore file (%s) not found - ", RESTOREFILE);
    perror("");
    exit(-1);
  }

  sck = fgets(out, sizeof(out), f);
  if (out[0] != 0 && out[strlen(out) - 1] == '\n')
    out[strlen(out) - 1] = 0;
  if (strcmp(out, PROGRAM) != 0) {
    fprintf(stderr, "[ERROR] invalid restore file (begin)\n");
    exit(-1);
  }
  fck = (int) fread(&bf_options, sizeof(bf_options), 1, f);
  fck = (int) fread(mynull, sizeof(mynull), 1, f);
  if (mynull[0] + mynull[1] + mynull[2] + mynull[3] == 0) {
    bf_options.crs = NULL;
  } else {
    bf_options.crs = malloc(BF_CHARSMAX);
    memcpy(bf_options.crs, mynull, sizeof(mynull));
    fck = fread(bf_options.crs + sizeof(mynull), BF_CHARSMAX - sizeof(mynull), 1, f);
  }

  fck = (int) fread(&hydra_brains, sizeof(hydra_brain), 1, f);
  hydra_brains.ofp = stdout;
  fck = (int) fread(&hydra_options, sizeof(hydra_option), 1, f);
  hydra_options.restore = 1;
  verbose = hydra_options.verbose;
  debug = hydra_options.debug;
  waittime = hydra_options.waittime;
  conwait = hydra_options.conwait;
  port = hydra_options.port;
  sck = fgets(out, sizeof(out), f);
  if (out[0] != 0 && out[strlen(out) - 1] == '\n')
    out[strlen(out) - 1] = 0;
  hydra_options.server = strdup(out);
  sck = fgets(out, sizeof(out), f);
  if (out[0] != 0 && out[strlen(out) - 1] == '\n')
    out[strlen(out) - 1] = 0;
  if (strlen(out) > 0) {
    hydra_options.outfile_ptr = malloc(strlen(out) + 1);
    strcpy(hydra_options.outfile_ptr, out);
  } else
    hydra_options.outfile_ptr = NULL;
  sck = fgets(out, sizeof(out), f);
  if (out[0] != 0 && out[strlen(out) - 1] == '\n')
    out[strlen(out) - 1] = 0;
  if (strlen(out) == 0)
    hydra_options.miscptr = NULL;
  else {
    hydra_options.miscptr = malloc(strlen(out) + 1);
    strcpy(hydra_options.miscptr, out);
  }
  sck = fgets(out, sizeof(out), f);
  if (out[0] != 0 && out[strlen(out) - 1] == '\n')
    out[strlen(out) - 1] = 0;
  hydra_options.service = malloc(strlen(out) + 1);
  strcpy(hydra_options.service, out);
  
  login_ptr = malloc(hydra_brains.sizelogin);
  fck = (int) fread(login_ptr, hydra_brains.sizelogin, 1, f);
  if ((hydra_options.mode & 64) != 64) { // NOT colonfile mode
    pass_ptr = malloc(hydra_brains.sizepass);
    fck = (int) fread(pass_ptr, hydra_brains.sizepass, 1, f);
  } else {                      // colonfile mode 
    hydra_options.colonfile = empty_login;      // dummy 
    pass_ptr = csv_ptr = login_ptr;
  }

  hydra_targets = malloc(hydra_brains.targets * sizeof(hydra_targets));
  for (j = 0; j < hydra_brains.targets; j++) {
    hydra_targets[j] = malloc(sizeof(hydra_target));
    fck = (int) fread(hydra_targets[j], sizeof(hydra_target), 1, f);
    sck = fgets(out, sizeof(out), f);
    if (out[0] != 0 && out[strlen(out) - 1] == '\n')
      out[strlen(out) - 1] = 0;
    hydra_targets[j]->target = malloc(strlen(out) + 1);
    strcpy(hydra_targets[j]->target, out);
    sck = fgets(out, sizeof(out), f);
    hydra_targets[j]->login_ptr = login_ptr + atoi(out);
    sck = fgets(out, sizeof(out), f);
    hydra_targets[j]->pass_ptr = pass_ptr + atoi(out);
    sck = fgets(out, sizeof(out), f); // target login_ptr, ignord
    sck = fgets(out, sizeof(out), f);
    if (hydra_options.bfg) {
      if (out[0] != 0 && out[strlen(out) - 1] == '\n')
        out[strlen(out) - 1] = 0;
      hydra_targets[j]->pass_ptr = malloc(strlen(out) + 1);
      strcpy(hydra_targets[j]->pass_ptr, out);
    }
    if (hydra_targets[j]->redo > 0)
      for (i = 0; i < hydra_targets[j]->redo; i++) {
        sck = fgets(out, sizeof(out), f);
        if (out[0] != 0 && out[strlen(out) - 1] == '\n')
          out[strlen(out) - 1] = 0;
        hydra_targets[j]->redo_login[i] = malloc(strlen(out) + 1);
        strcpy(hydra_targets[j]->redo_login[i], out);
        sck = fgets(out, sizeof(out), f);
        if (out[0] != 0 && out[strlen(out) - 1] == '\n')
          out[strlen(out) - 1] = 0;
        hydra_targets[j]->redo_pass[i] = malloc(strlen(out) + 1);
        strcpy(hydra_targets[j]->redo_pass[i], out);
      }
    if (hydra_targets[j]->skipcnt > 0)
      for (i = 0; i < hydra_targets[j]->skipcnt; i++) {
        sck = fgets(out, sizeof(out), f);
        if (out[0] != 0 && out[strlen(out) - 1] == '\n')
          out[strlen(out) - 1] = 0;
        hydra_targets[j]->skiplogin[i] = malloc(strlen(out) + 1);
        strcpy(hydra_targets[j]->skiplogin[i], out);
      }
    hydra_targets[j]->fail_count = 0;
    hydra_targets[j]->use_count = 0;
    hydra_targets[j]->failed = 0;
  }
  hydra_heads = malloc(hydra_options.max_use * sizeof(hydra_heads));
  for (j = 0; j < hydra_options.max_use; j++) {
    hydra_heads[j] = malloc(sizeof(hydra_head));
    fck = (int) fread(hydra_heads[j], sizeof(hydra_head), 1, f);
    hydra_heads[j]->sp[0] = -1;
    hydra_heads[j]->sp[1] = -1;
    sck = fgets(out, sizeof(out), f);
    if (hydra_heads[j]->redo) {
      if (out[0] != 0 && out[strlen(out) - 1] == '\n')
        out[strlen(out) - 1] = 0;
      hydra_heads[j]->current_login_ptr = malloc(strlen(out) + 1);
      strcpy(hydra_heads[j]->current_login_ptr, out);
    }
    sck = fgets(out, sizeof(out), f);
    if (hydra_heads[j]->redo) {
      if (out[0] != 0 && out[strlen(out) - 1] == '\n')
        out[strlen(out) - 1] = 0;
      if (out[0] != 0 || hydra_heads[j]->current_login_ptr[0] != 0) {
        hydra_heads[j]->current_pass_ptr = malloc(strlen(out) + 1);
        strcpy(hydra_heads[j]->current_pass_ptr, out);
        if (debug)
          printf("[DEBUG] redo: %d %s/%s\n", j, hydra_heads[j]->current_login_ptr, hydra_heads[j]->current_pass_ptr);
      } else {
        hydra_heads[j]->redo = 0;
        free(hydra_heads[j]->current_login_ptr);
        hydra_heads[j]->current_login_ptr = hydra_heads[j]->current_pass_ptr = empty_login;
      }
    } else {
      hydra_heads[j]->current_login_ptr = hydra_heads[j]->current_pass_ptr = empty_login;
    }
  }
  sck = fgets(out, sizeof(out), f);
  if (out[0] != 0 && out[strlen(out) - 1] == '\n')
    out[strlen(out) - 1] = 0;
  if (strcmp(out, PROGRAM) != 0) {
    fprintf(stderr, "[ERROR] invalid restore file (end)\n");
    exit(-1);
  }
  fclose(f);
  hydra_debug(0, "hydra_restore_read");
}

void killed_childs(int signo) {
  int pid, i;

  killed++;
  pid = wait3(NULL, WNOHANG, NULL);
  for (i = 0; i < hydra_options.max_use; i++) {
    if (pid == hydra_heads[i]->pid) {
      hydra_heads[i]->pid = -1;
      hydra_kill_head(i, 1, 0);
      return;
    }
  }
}

void killed_childs_report(int signo) {
  if (debug)
    printf("[DEBUG] children crashed! (%d)\n", child_head_no);
  fck = write(child_socket, "E", 1);
  _exit(-1);
}

void kill_children(int signo) {
  int i;

  if (verbose)
    fprintf(stderr, "[ERROR] Received signal %d, going down ...\n", signo);
  if (process_restore == 1)
    hydra_restore_write(1);
  if (hydra_heads != NULL) {
    for (i = 0; i < hydra_options.max_use; i++)
      if (hydra_heads[i] != NULL && hydra_heads[i]->pid > 0)
        kill(hydra_heads[i]->pid, SIGTERM);
    for (i = 0; i < hydra_options.max_use; i++)
      if (hydra_heads[i] != NULL && hydra_heads[i]->pid > 0)
        kill(hydra_heads[i]->pid, SIGKILL);
  }
  exit(0);
}

unsigned long int countlines(FILE *fp, int colonmode) {
  size_t lines = 0;
  char *buf = malloc(MAXLINESIZE);
  int only_one_empty_line = 0;
  struct stat st;

  while (!feof(fp)) {
    if (fgets(buf, MAXLINESIZE, fp) != NULL) {
      if (buf[0] != 0) {
        if (buf[0] == '\r' || buf[0] == '\n') {
          if (only_one_empty_line == 0) {
            only_one_empty_line = 1;
            lines++;
          }
        } else {
          lines++;
        }
      }
    }
  }
  rewind(fp);
  free(buf);
  fstat(fileno(fp), &st);
  size_of_data = st.st_size + 1;
  return lines;
}

void fill_mem(char *ptr, FILE *fp, int colonmode) {
  char tmp[MAXBUF + 4] = "", *ptr2;
  unsigned int len;
  int only_one_empty_line = 0;

  while (!feof(fp)) {
    if (fgets(tmp, MAXLINESIZE, fp) != NULL) {
      if (tmp[0] != 0) {
        if (tmp[strlen(tmp) - 1] == '\n')
          tmp[strlen(tmp) - 1] = '\0';
        if (tmp[0] != 0 && tmp[strlen(tmp) - 1] == '\r')
          tmp[strlen(tmp) - 1] = '\0';
        if ((len = strlen(tmp)) > 0 || (only_one_empty_line == 0 && colonmode == 0)) {
          if (len == 0 && colonmode == 0) {
            only_one_empty_line = 1;
            len = 1;
            tmp[len] = 0;
          }
          if (colonmode) {
            if ((ptr2 = index(tmp, ':')) == NULL) {
              fprintf(stderr, "[ERROR] invalid line in colon file (-C), missing colon in line: %s\n", tmp);
              exit(-1);
            } else {
//              if (tmp[0] == ':') {
//                *ptr = 0;
//                ptr++;
//              }
//              if (tmp[len - 1] == ':' && len > 1) {
//                len++;
//                tmp[len - 1] = 0;
//              }
              *ptr2 = 0;
            }
          }
          memcpy(ptr, tmp, len);
          ptr += len;
          *ptr = '\0';
          ptr++;
        }
      }
    }
  }
  fclose(fp);
}

char *hydra_build_time() {
  static char datetime[24];
  struct tm *the_time;
  time_t epoch;

  time(&epoch);
  the_time = localtime(&epoch);
  strftime(datetime, sizeof(datetime), "%Y-%m-%d %H:%M:%S", the_time);
  return (char *) &datetime;
}

void hydra_service_init(int target_no) {
  int x = 2;

#ifdef LIBAFP
  if (strcmp(hydra_options.service, "afp") == 0)
    x = service_afp_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
#endif
  if (strcmp(hydra_options.service, "asterisk") == 0)
    x = service_asterisk_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "cisco-enable") == 0)
    x = service_cisco_enable_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "cvs") == 0)
    x = service_cvs_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "cisco") == 0)
    x = service_cisco_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
#ifdef LIBFIREBIRD
  if (strcmp(hydra_options.service, "firebird") == 0)
    x = service_firebird_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
#endif
  if (strcmp(hydra_options.service, "ftp") == 0 || strcmp(hydra_options.service, "ftps") == 0)
    x = service_ftp_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "http-get") == 0 || strcmp(hydra_options.service, "http-head") == 0)
    x = service_http_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "http-form") == 0 || strcmp(hydra_options.service, "http-get-form") == 0 || strcmp(hydra_options.service, "http-post-form") == 0)
    x = service_http_form_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "http-proxy") == 0)
    x = service_http_proxy_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "http-proxy-urlenum") == 0)
    x = service_http_proxy_urlenum_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "icq") == 0)
    x = service_icq_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "imap") == 0)
    x = service_imap_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "irc") == 0)
    x = service_irc_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strncmp(hydra_options.service, "ldap", 4) == 0)
    x = service_ldap_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
#ifdef LIBOPENSSL
  if (strcmp(hydra_options.service, "sip") == 0)
    x = service_sip_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "smb") == 0 || strcmp(hydra_options.service, "smbnt") == 0)
    x = service_smb_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "oracle-listener") == 0)
    x = service_oracle_listener_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "oracle-sid") == 0)
    x = service_oracle_sid_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "rdp") == 0)
    x = service_rdp_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
#endif
  if (strcmp(hydra_options.service, "mssql") == 0)
    x = service_mssql_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
#ifdef HAVE_MATH_H
  if (strcmp(hydra_options.service, "mysql") == 0)
    x = service_mysql_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
#endif
#ifdef LIBNCP
  if (strcmp(hydra_options.service, "ncp") == 0)
    x = service_ncp_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
#endif
  if (strcmp(hydra_options.service, "nntp") == 0)
    x = service_nntp_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
#ifdef LIBORACLE
  if (strcmp(hydra_options.service, "oracle") == 0)
    x = service_oracle_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
#endif
  if (strcmp(hydra_options.service, "pcanywhere") == 0)
    x = service_pcanywhere_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "pcnfs") == 0)
    x = service_pcnfs_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "pop3") == 0)
    x = service_pop3_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
#ifdef LIBPOSTGRES
  if (strcmp(hydra_options.service, "postgres") == 0)
    x = service_postgres_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
#endif
  if (strcmp(hydra_options.service, "rexec") == 0)
    x = service_rexec_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "rlogin") == 0)
    x = service_rlogin_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "rsh") == 0)
    x = service_rsh_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
#ifdef LIBSAPR3
  if (strcmp(hydra_options.service, "sapr3") == 0)
    x = service_sapr3_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
#endif
  if (strcmp(hydra_options.service, "smtp") == 0)
    x = service_smtp_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "smtp-enum") == 0)
    x = service_smtp_enum_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "snmp") == 0)
    x = service_snmp_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "socks5") == 0)
    x = service_socks5_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
#ifdef LIBSSH
  if (strcmp(hydra_options.service, "ssh") == 0)
    x = service_ssh_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "sshkey") == 0)
    x = service_sshkey_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
#endif
#ifdef LIBSVN
  if (strcmp(hydra_options.service, "svn") == 0)
    x = service_svn_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
#endif
  if (strcmp(hydra_options.service, "teamspeak") == 0)
    x = service_teamspeak_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "telnet") == 0)
    x = service_telnet_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "vmauthd") == 0)
    x = service_vmauthd_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "vnc") == 0)
    x = service_vnc_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
  if (strcmp(hydra_options.service, "xmpp") == 0)
    x = service_xmpp_init(hydra_targets[target_no]->ip, -1, options, hydra_options.miscptr, hydra_brains.ofp, port);
// ADD NEW SERVICES HERE

  
  if (x != 0 && x != 2)
    exit(-1);
}


int hydra_spawn_head(int head_no, int target_no) {
  int i;

  if (hydra_heads[head_no]->active < 0) {
    printf("DEBUG-ERROR - child %d should not be respawned!\n", head_no);
    return -1;
  }

  if (socketpair(PF_UNIX, SOCK_STREAM, 0, hydra_heads[head_no]->sp) == 0) {
    child_head_no = head_no;
    if ((hydra_heads[head_no]->pid = fork()) == 0) {    // THIS IS THE CHILD 
      // set new signals for child 
      process_restore = 0;
      child_socket = hydra_heads[head_no]->sp[1];
      signal(SIGCHLD, killed_childs);
      signal(SIGTERM, exit);
#ifdef SIGBUS
      signal(SIGBUS, exit);
#endif
      signal(SIGSEGV, killed_childs_report);
      signal(SIGHUP, exit);
      signal(SIGINT, exit);
      signal(SIGPIPE, exit);
      // free structures to make memory available 
      cmdlinetarget = hydra_targets[target_no]->target;
      for (i = 0; i < hydra_options.max_use; i++)
        if (i != head_no)
          free(hydra_heads[i]);
      for (i = 0; i < countservers; i++)
        if (i != target_no)
          free(hydra_targets[i]);
      if (hydra_options.loginfile != NULL)
        free(login_ptr);
      if (hydra_options.passfile != NULL)
        free(pass_ptr);
      if (hydra_options.colonfile != NULL && hydra_options.colonfile != empty_login)
        free(csv_ptr);
//    we must keep servers_ptr for cmdlinetarget to work
      if (debug)
        printf("[DEBUG] head_no %d has pid %d\n", head_no, getpid());

      // now call crack module 
      if (strcmp(hydra_options.service, "asterisk") == 0)
        service_asterisk(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "telnet") == 0)
        service_telnet(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "ftp") == 0)
        service_ftp(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "ftps") == 0)
        service_ftps(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "pop3") == 0)
        service_pop3(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "imap") == 0)
        service_imap(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "vmauthd") == 0)
        service_vmauthd(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "ldap2") == 0)
        service_ldap2(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "ldap3") == 0)
        service_ldap3(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "http-head") == 0)
        service_http_head(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "ldap3-crammd5") == 0)
        service_ldap3_cram_md5(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "ldap3-digestmd5") == 0)
        service_ldap3_digest_md5(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "http-get") == 0)
        service_http_get(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "http-get-form") == 0)
        service_http_get_form(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "http-post-form") == 0)
        service_http_post_form(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "http-proxy") == 0)
        service_http_proxy(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "http-proxy-urlenum") == 0)
        service_http_proxy_urlenum(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "cisco") == 0)
        service_cisco(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "cisco-enable") == 0)
        service_cisco_enable(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "socks5") == 0)
        service_socks5(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "vnc") == 0)
        service_vnc(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "rexec") == 0)
        service_rexec(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "rlogin") == 0)
        service_rlogin(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "rsh") == 0)
        service_rsh(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "nntp") == 0)
        service_nntp(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "icq") == 0)
        service_icq(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "pcnfs") == 0)
        service_pcnfs(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
#ifdef HAVE_MATH_H
      if (strcmp(hydra_options.service, "mysql") == 0)
        service_mysql(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
#endif
      if (strcmp(hydra_options.service, "mssql") == 0)
        service_mssql(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
#ifdef LIBOPENSSL
      if (strcmp(hydra_options.service, "oracle-listener") == 0)
        service_oracle_listener(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "oracle-sid") == 0)
        service_oracle_sid(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
#endif
#ifdef LIBORACLE
      if (strcmp(hydra_options.service, "oracle") == 0)
        service_oracle(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
#endif
#ifdef LIBPOSTGRES
      if (strcmp(hydra_options.service, "postgres") == 0)
        service_postgres(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
#endif
#ifdef LIBFIREBIRD
      if (strcmp(hydra_options.service, "firebird") == 0)
        service_firebird(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
#endif
#ifdef LIBAFP
      if (strcmp(hydra_options.service, "afp") == 0)
        service_afp(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
#endif
#ifdef LIBNCP
      if (strcmp(hydra_options.service, "ncp") == 0)
        service_ncp(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
#endif
      if (strcmp(hydra_options.service, "pcanywhere") == 0)
        service_pcanywhere(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "cvs") == 0)
        service_cvs(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
#ifdef LIBSVN
      if (strcmp(hydra_options.service, "svn") == 0)
        service_svn(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
#endif
      if (strcmp(hydra_options.service, "snmp") == 0)
        service_snmp(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
#ifdef LIBOPENSSL
      if ((strcmp(hydra_options.service, "smb") == 0) || (strcmp(hydra_options.service, "smbnt") == 0))
        service_smb(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
#endif
#ifdef LIBSAPR3
      if (strcmp(hydra_options.service, "sapr3") == 0)
        service_sapr3(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
#endif
#ifdef LIBSSH
      if (strcmp(hydra_options.service, "ssh") == 0)
        service_ssh(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "sshkey") == 0)
        service_sshkey(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
#endif
      if (strcmp(hydra_options.service, "smtp") == 0)
        service_smtp(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "smtp-enum") == 0)
        service_smtp_enum(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "teamspeak") == 0)
        service_teamspeak(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
#ifdef LIBOPENSSL
      if (strcmp(hydra_options.service, "sip") == 0)
        service_sip(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
#endif
      if (strcmp(hydra_options.service, "xmpp") == 0)
        service_xmpp(hydra_targets[target_no]->target, hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
      if (strcmp(hydra_options.service, "irc") == 0)
        service_irc(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
#ifdef LIBOPENSSL
      if (strcmp(hydra_options.service, "rdp") == 0)
        service_rdp(hydra_targets[target_no]->ip, hydra_heads[head_no]->sp[1], options, hydra_options.miscptr, hydra_brains.ofp, port);
#endif
// ADD NEW SERVICES HERE 

      // just in case a module returns (which it shouldnt) we let it exit here 
      exit(-1);
    } else {
      child_head_no = -1;
      if (hydra_heads[head_no]->pid > 0) {
        fck = write(hydra_heads[head_no]->sp[1], "n", 1);       // yes, a small "n" - this way we can distinguish later if the client successfully tested a pair and is requesting a new one or the mother did that 
        fcntl(hydra_heads[head_no]->sp[0], F_SETFL, O_NONBLOCK);
        if (hydra_heads[head_no]->redo != 1)
          hydra_heads[head_no]->target_no = target_no;
        hydra_heads[head_no]->active = 1;
        hydra_targets[hydra_heads[head_no]->target_no]->use_count++;
        hydra_brains.active++;
        hydra_heads[head_no]->last_seen = time(NULL);
        if (debug)
          printf("[DEBUG] child %d spawned for target %d with pid %d\n", head_no, hydra_heads[head_no]->target_no, hydra_heads[head_no]->pid);
      } else {
        perror("[ERROR] Fork for children failed");
        hydra_heads[head_no]->sp[0] = -1;
        hydra_heads[head_no]->active = 0;
        return -1;
      }
    }
  } else {
    perror("[ERROR] socketpair creation failed");
    hydra_heads[head_no]->sp[0] = -1;
    hydra_heads[head_no]->active = 0;
    return -1;
  }
  return 0;
}

int hydra_lookup_port(char *service) {
  int i = 0, port = -2;

  hydra_portlist hydra_portlists[] = {
    {"ftp", PORT_FTP, PORT_FTP_SSL},
    {"ftps", PORT_FTP, PORT_FTP_SSL},
    {"http-head", PORT_HTTP, PORT_HTTP_SSL},
    {"http-get", PORT_HTTP, PORT_HTTP_SSL},
    {"http-get-form", PORT_HTTP, PORT_HTTP_SSL},
    {"http-post-form", PORT_HTTP, PORT_HTTP_SSL},
    {"https-get-form", PORT_HTTP, PORT_HTTP_SSL},
    {"https-post-form", PORT_HTTP, PORT_HTTP_SSL},
    {"https-head", PORT_HTTP, PORT_HTTP_SSL},
    {"https-get", PORT_HTTP, PORT_HTTP_SSL},
    {"http-proxy", PORT_HTTP_PROXY, PORT_HTTP_PROXY_SSL},
    {"http-proxy-urlenum", PORT_HTTP_PROXY, PORT_HTTP_PROXY_SSL},
    {"icq", PORT_ICQ, PORT_ICQ_SSL},
    {"imap", PORT_IMAP, PORT_IMAP_SSL},
    {"ldap2", PORT_LDAP, PORT_LDAP_SSL},
    {"ldap3", PORT_LDAP, PORT_LDAP_SSL},
    {"ldap3-crammd5", PORT_LDAP, PORT_LDAP_SSL},
    {"ldap3-digestmd5", PORT_LDAP, PORT_LDAP_SSL},
    {"oracle-listener", PORT_ORACLE, PORT_ORACLE_SSL},
    {"oracle-sid", PORT_ORACLE, PORT_ORACLE_SSL},
    {"oracle", PORT_ORACLE, PORT_ORACLE_SSL},
    {"mssql", PORT_MSSQL, PORT_MSSQL_SSL},
    {"mysql", PORT_MYSQL, PORT_MYSQL_SSL},
    {"postgres", PORT_POSTGRES, PORT_POSTGRES_SSL},
    {"pcanywhere", PORT_PCANYWHERE, PORT_PCANYWHERE_SSL},
    {"nntp", PORT_NNTP, PORT_NNTP_SSL},
    {"pcnfs", PORT_PCNFS, PORT_PCNFS_SSL},
    {"pop3", PORT_POP3, PORT_POP3_SSL},
    {"rexec", PORT_REXEC, PORT_REXEC_SSL},
    {"rlogin", PORT_RLOGIN, PORT_RLOGIN_SSL},
    {"rsh", PORT_RSH, PORT_RSH_SSL},
    {"sapr3", PORT_SAPR3, PORT_SAPR3_SSL},
    {"smb", PORT_SMBNT, PORT_SMBNT_SSL},
    {"smbnt", PORT_SMBNT, PORT_SMBNT_SSL},
    {"socks5", PORT_SOCKS5, PORT_SOCKS5_SSL},
    {"ssh", PORT_SSH, PORT_SSH_SSL},
    {"sshkey", PORT_SSH, PORT_SSH_SSL},
    {"telnet", PORT_TELNET, PORT_TELNET_SSL},
    {"cisco", PORT_TELNET, PORT_TELNET_SSL},
    {"cisco-enable", PORT_TELNET, PORT_TELNET_SSL},
    {"vnc", PORT_VNC, PORT_VNC_SSL},
    {"snmp", PORT_SNMP, PORT_SNMP_SSL},
    {"cvs", PORT_CVS, PORT_CVS_SSL},
    {"svn", PORT_SVN, PORT_SVN_SSL},
    {"firebird", PORT_FIREBIRD, PORT_FIREBIRD_SSL},
    {"afp", PORT_AFP, PORT_AFP_SSL},
    {"ncp", PORT_NCP, PORT_NCP_SSL},
    {"smtp", PORT_SMTP, PORT_SMTP_SSL},
    {"smtp-enum", PORT_SMTP, PORT_SMTP_SSL},
    {"teamspeak", PORT_TEAMSPEAK, PORT_TEAMSPEAK_SSL},
    {"sip", PORT_SIP, PORT_SIP_SSL},
    {"vmauthd", PORT_VMAUTHD, PORT_VMAUTHD_SSL},
    {"xmpp", PORT_XMPP, PORT_XMPP_SSL},
    {"irc", PORT_IRC, PORT_IRC_SSL},
    {"rdp", PORT_RDP, PORT_RDP_SSL},
    {"asterisk", PORT_ASTERISK, PORT_ASTERISK_SSL},
    {"", PORT_NOPORT, PORT_NOPORT}
// ADD NEW SERVICES HERE - add new port numbers to hydra.h 
  };

  while (strlen(hydra_portlists[i].name) > 0 && port == -2) {
    if (strcmp(service, hydra_portlists[i].name) == 0) {
      if (hydra_options.ssl)
        port = hydra_portlists[i].port_ssl;
      else
        port = hydra_portlists[i].port;
    }
    i++;
  }
  if (port < 1)
    return -1;
  else
    return port;
}

// killit = 1 : kill(pid); fail = 1 : redo, fail = 2 : disable
void hydra_kill_head(int head_no, int killit, int fail) {
  if (hydra_heads[head_no]->active > 0) {
    close(hydra_heads[head_no]->sp[0]);
    close(hydra_heads[head_no]->sp[1]);
  }
  if (killit) {
    if (hydra_heads[head_no]->pid > 0)
      kill(hydra_heads[head_no]->pid, SIGTERM);
    hydra_brains.active--;
  }
  if (hydra_heads[head_no]->active > 0) {
    hydra_heads[head_no]->active = 0;
    hydra_targets[hydra_heads[head_no]->target_no]->use_count--;
  }
  if (fail == 1)
    hydra_heads[head_no]->redo = 1;
  else if (fail == 2) {
    hydra_heads[head_no]->active = -1;
    hydra_targets[hydra_heads[head_no]->target_no]->failed++;
  }
  if (hydra_heads[head_no]->pid > 0 && killit)
    kill(hydra_heads[head_no]->pid, SIGKILL);
  hydra_heads[head_no]->pid = -1;
  if (hydra_options.bfg && hydra_targets[hydra_heads[head_no]->target_no]->pass_state == 3
      && strlen(hydra_heads[head_no]->current_pass_ptr) > 0 &&
      hydra_heads[head_no]->current_pass_ptr != hydra_heads[head_no]->current_login_ptr) {
    free(hydra_heads[head_no]->current_pass_ptr);
    hydra_heads[head_no]->current_pass_ptr = empty_login;
//    hydra_bfg_remove(head_no);
//    hydra_targets[hydra_heads[head_no]->target_no]->bfg_ptr[head_no] = NULL;
  }
  (void) wait3(NULL, WNOHANG, NULL);
}

void hydra_increase_fail_count(int target_no, int head_no) {
  int i, k;

  hydra_targets[target_no]->fail_count++;
  if (debug)
    printf("[DEBUG] hydra_increase_fail_count: %d >= %d => disable\n", hydra_targets[target_no]->fail_count, MAXFAIL + (hydra_options.tasks <= 4 && hydra_targets[target_no]->ok ? 6 - hydra_options.tasks : 1) + (hydra_options.tasks - hydra_targets[target_no]->failed < 5 && hydra_targets[target_no]->ok ? 6 - (hydra_options.tasks - hydra_targets[target_no]->failed) : 1) + (hydra_targets[target_no]->ok ? 2 : -2));
  if (hydra_targets[target_no]->fail_count >= MAXFAIL + 
      (hydra_options.tasks <= 4 && hydra_targets[target_no]->ok ? 6 - hydra_options.tasks : 1) + (hydra_options.tasks - hydra_targets[target_no]->failed < 5 && hydra_targets[target_no]->ok ? 6 - (hydra_options.tasks - hydra_targets[target_no]->failed) : 1) + (hydra_targets[target_no]->ok ? 2 : -2)
     ) {
    k = 0;
    for (i = 0; i < hydra_options.max_use; i++)
      if (hydra_heads[i]->active >= 0 && hydra_heads[i]->target_no == target_no)
        k++;
    if (k <= 1) {
      if (hydra_targets[target_no]->fail_count >= MAXFAIL + hydra_options.tasks * hydra_targets[target_no]->ok) {
        hydra_kill_head(head_no, 1, 2);
        if (hydra_targets[target_no]->done == 0 && hydra_options.max_use == hydra_targets[target_no]->failed) {
          if (hydra_targets[target_no]->ok == 1)
            hydra_targets[target_no]->done = 2;   // mark target as done by errors
          else
            hydra_targets[target_no]->done = 3;   // mark target as done by unable to connect
          hydra_brains.finished++;
          fprintf(stderr, "[ERROR] Too many connect errors to target, disabling %s://%s%s%s:%d\n", hydra_options.service, hydra_targets[target_no]->ip[0] == 16 && index(hydra_targets[target_no]->target, ':') != NULL ? "[" : "", hydra_targets[target_no]->target, hydra_targets[target_no]->ip[0] == 16 && index(hydra_targets[target_no]->target, ':') != NULL ? "]" : "", port);
        }
      }                         // we keep the last one alive as long as it make sense
    } else {
      hydra_kill_head(head_no, 1, 2);
      hydra_targets[target_no]->fail_count--;
      if (k < 5 && hydra_targets[target_no]->ok)
        hydra_targets[target_no]->fail_count--;
      if (k == 2 && hydra_targets[target_no]->ok)
        hydra_targets[target_no]->fail_count--;
      if (verbose)
        printf("[VERBOSE] Disabled child %d because of too many errors\n", head_no);
    }
    // we need to put this in a list, otherwise we fail one login+pw test
    if (hydra_targets[target_no]->done == 0
        && hydra_targets[target_no]->redo <= hydra_options.max_use * 2
        && ((hydra_heads[head_no]->current_login_ptr != empty_login && hydra_heads[head_no]->current_pass_ptr != empty_login)
            || (hydra_heads[head_no]->current_login_ptr != NULL && hydra_heads[head_no]->current_pass_ptr != NULL))) {
      hydra_targets[target_no]->redo_login[hydra_targets[target_no]->redo] = hydra_heads[head_no]->current_login_ptr;
      hydra_targets[target_no]->redo_pass[hydra_targets[target_no]->redo] = hydra_heads[head_no]->current_pass_ptr;
      hydra_targets[target_no]->redo++;
      if (debug)
        printf("[DEBUG] - will be retried at the end: ip %s - login %s - pass %s - child %d\n", hydra_targets[target_no]->target,
               hydra_heads[head_no]->current_login_ptr, hydra_heads[head_no]->current_pass_ptr, head_no);
      hydra_heads[head_no]->current_login_ptr = empty_login;
      hydra_heads[head_no]->current_pass_ptr = empty_login;
    }
  } else {
    hydra_kill_head(head_no, 1, 1);
    if (verbose)
      printf("[VERBOSE] Retrying connection for child %d\n", head_no);
  }
}

char *hydra_reverse_login(int head_no, char *login) {
  int i, j = strlen(login);
  
  if (j > 248)
    j = 248;
  else if (j == 0)
    return empty_login;
  for (i = 0; i < j; i++)
    hydra_heads[head_no]->reverse[i] = login[j - (i + 1)];
  hydra_heads[head_no]->reverse[j] = 0;
  
  return hydra_heads[head_no]->reverse;
}

int hydra_send_next_pair(int target_no, int head_no) {
  // variables moved to save stack
  snpdone = 0;
  snp_is_redo = 0;
  snpdont = 0;
  loop_cnt++;
  if (hydra_targets[target_no]->sent >= hydra_brains.todo) {
    if (hydra_targets[target_no]->done == 0) {
      hydra_targets[target_no]->done = 1;
      hydra_brains.finished++;
      if (verbose) printf("[STATUS] attack finished for %s (waiting for children to complete tests)\n", hydra_targets[target_no]->target);
    }
    return -1;
  }
  if (loop_cnt > (hydra_brains.countlogin * 2) + 1 && loop_cnt > (hydra_brains.countpass * 2) + 1) {
    if (debug)
      printf("[DEBUG] too many loops in send_next_pair, returning -1 (loop_cnt %d, sent %ld, todo %ld)\n", loop_cnt, hydra_targets[target_no]->sent, hydra_brains.todo);
    return -1;
  }

  if (debug)
    printf("[DEBUG] send_next_pair_init target %d, head %d, redo %d, redo_state %d, pass_state %d. loop_mode %d, curlogin %s, curpass %s, tlogin %s, tpass %s, logincnt %lu/%lu, passcnt %lu/%lu, loop_cnt %d\n",
      target_no, head_no, hydra_heads[head_no]->redo, hydra_targets[target_no]->redo_state, hydra_targets[target_no]->pass_state, hydra_options.loop_mode, hydra_heads[head_no]->current_login_ptr, hydra_heads[head_no]->current_pass_ptr, hydra_targets[target_no]->login_ptr, hydra_targets[target_no]->pass_ptr, hydra_targets[target_no]->login_no, hydra_brains.countlogin, hydra_targets[target_no]->pass_no, hydra_brains.countpass, loop_cnt);
  if (hydra_heads[head_no]->redo && hydra_heads[head_no]->current_login_ptr != NULL && hydra_heads[head_no]->current_pass_ptr != NULL) {
    hydra_heads[head_no]->redo = 0;
    snp_is_redo = 1;
    snpdone = 1;
  } else {
    if (debug && (hydra_heads[head_no]->current_login_ptr != NULL || hydra_heads[head_no]->current_pass_ptr != NULL))
      printf("[COMPLETED] target %s - login \"%s\" - pass \"%s\" - child %d - %lu of %lu\n",
             hydra_targets[target_no]->target, hydra_heads[head_no]->current_login_ptr, hydra_heads[head_no]->current_pass_ptr, head_no,
             hydra_targets[target_no]->sent, hydra_brains.todo);
    hydra_heads[head_no]->redo = 0;
    if (hydra_targets[target_no]->redo_state > 0) {
      if (hydra_targets[target_no]->redo_state + 1 <= hydra_targets[target_no]->redo) {
        hydra_heads[head_no]->current_pass_ptr = hydra_targets[target_no]->redo_pass[hydra_targets[target_no]->redo_state - 1];
        hydra_heads[head_no]->current_login_ptr = hydra_targets[target_no]->redo_login[hydra_targets[target_no]->redo_state - 1];
        hydra_targets[target_no]->redo_state++;
        snpdone = 1;
      }                         // no else, that way a later lost pair is still added and done
    } else {                    // normale state, no redo
      if (hydra_targets[target_no]->done) {
        loop_cnt = 0;
        return -1;              // head will be disabled by main while()
      }
      if (hydra_options.loop_mode == 0) { // one user after another
        if (hydra_targets[target_no]->login_no < hydra_brains.countlogin) {
          // as we loop password in mode == 0 we set the current login first
          hydra_heads[head_no]->current_login_ptr = hydra_targets[target_no]->login_ptr;
          // then we do the extra options -e ns handling
          if (hydra_targets[target_no]->pass_state == 0 && snpdone == 0) {
            if (hydra_options.try_password_same_as_login) {
              hydra_heads[head_no]->current_pass_ptr = hydra_targets[target_no]->login_ptr;
              snpdone = 1;
              hydra_targets[target_no]->pass_no++;
            }
            hydra_targets[target_no]->pass_state++;
          }
          if (hydra_targets[target_no]->pass_state == 1 && snpdone == 0) {
            // small check that there is a login name (could also be emtpy) and if we already tried empty password it would be a double
            if (hydra_options.try_null_password) {
              if (hydra_options.try_password_same_as_login == 0 || (hydra_targets[target_no]->login_ptr != NULL && strlen(hydra_targets[target_no]->login_ptr) > 0)) {
                hydra_heads[head_no]->current_pass_ptr = empty_login;
                snpdone = 1;
              } else {
                hydra_brains.sent++;
                hydra_targets[target_no]->sent++;
              }
              hydra_targets[target_no]->pass_no++;
            }
            hydra_targets[target_no]->pass_state++;
          }
          if (hydra_targets[target_no]->pass_state == 2 && snpdone == 0) {
            // small check that there is a login name (could also be emtpy) and if we already tried empty password it would be a double
            if (hydra_options.try_password_reverse_login) {
              if ((hydra_options.try_password_same_as_login == 0 || strcmp(hydra_targets[target_no]->login_ptr, hydra_reverse_login(head_no, hydra_heads[head_no]->current_login_ptr)) != 0)
                   &&
                  (hydra_options.try_null_password == 0 || (hydra_targets[target_no]->login_ptr != NULL && strlen(hydra_targets[target_no]->login_ptr) > 0))) {
                hydra_heads[head_no]->current_pass_ptr = hydra_reverse_login(head_no, hydra_heads[head_no]->current_login_ptr);
                snpdone = 1;
              } else {
                hydra_brains.sent++;
                hydra_targets[target_no]->sent++;
              }
              hydra_targets[target_no]->pass_no++;
            }
            hydra_targets[target_no]->pass_state++;
          }

          // now we handle the -C -l/-L -p/-P data
          if (hydra_targets[target_no]->pass_state == 3 && snpdone == 0) {
            if ((hydra_options.mode & 64) == 64) { // colon mode
              hydra_heads[head_no]->current_login_ptr = hydra_targets[target_no]->login_ptr;
              hydra_heads[head_no]->current_pass_ptr = hydra_targets[target_no]->pass_ptr;
              hydra_targets[target_no]->login_no++;
              snpdone = 1;
              hydra_targets[target_no]->login_ptr = hydra_targets[target_no]->pass_ptr;
              //hydra_targets[target_no]->login_ptr++;
              while (*hydra_targets[target_no]->login_ptr != 0)
                hydra_targets[target_no]->login_ptr++;
              hydra_targets[target_no]->login_ptr++;
              hydra_targets[target_no]->pass_ptr = hydra_targets[target_no]->login_ptr;
              //hydra_targets[target_no]->pass_ptr++;
              while (*hydra_targets[target_no]->pass_ptr != 0)
                hydra_targets[target_no]->pass_ptr++;
              hydra_targets[target_no]->pass_ptr++;
              if (strcmp(hydra_targets[target_no]->login_ptr, hydra_heads[head_no]->current_login_ptr) != 0)
                hydra_targets[target_no]->pass_state = 0;
              if ((hydra_options.try_password_same_as_login && strcmp(hydra_heads[head_no]->current_pass_ptr, hydra_heads[head_no]->current_login_ptr) == 0)
                   ||
                  (hydra_options.try_null_password && strlen(hydra_heads[head_no]->current_pass_ptr) == 0)
                   || 
                  (hydra_options.try_password_reverse_login && strcmp(hydra_heads[head_no]->current_pass_ptr, hydra_reverse_login(head_no, hydra_heads[head_no]->current_login_ptr)) == 0)) {
                hydra_brains.sent++;
                hydra_targets[target_no]->sent++;
                if (debug)
                  printf("[DEBUG] double detected (-C)\n");
                return hydra_send_next_pair(target_no, head_no);  // little trick to keep the code small
              }
            } else { // standard -l -L -p -P mode
              hydra_heads[head_no]->current_pass_ptr = hydra_targets[target_no]->pass_ptr;
              hydra_targets[target_no]->pass_no++;
              // double check
              if (hydra_targets[target_no]->pass_no >= hydra_brains.countpass) {
                // all passwords done, next user for next password
                hydra_targets[target_no]->login_ptr++;
                while (*hydra_targets[target_no]->login_ptr != 0)
                  hydra_targets[target_no]->login_ptr++;
                hydra_targets[target_no]->login_ptr++;
                hydra_targets[target_no]->pass_ptr = pass_ptr;
                hydra_targets[target_no]->login_no++;
                hydra_targets[target_no]->pass_no = 0;
                hydra_targets[target_no]->pass_state = 0;
                if (hydra_brains.countpass == hydra_options.try_password_reverse_login + hydra_options.try_null_password + hydra_options.try_password_same_as_login)
                  return hydra_send_next_pair(target_no, head_no);
              } else {
                hydra_targets[target_no]->pass_ptr++;
                while (*hydra_targets[target_no]->pass_ptr != 0)
                  hydra_targets[target_no]->pass_ptr++;
                hydra_targets[target_no]->pass_ptr++;
              }
              if ((hydra_options.try_password_same_as_login && strcmp(hydra_heads[head_no]->current_pass_ptr, hydra_heads[head_no]->current_login_ptr) == 0)
                   ||
                  (hydra_options.try_null_password && strlen(hydra_heads[head_no]->current_pass_ptr) == 0)
                   ||
                  (hydra_options.try_password_reverse_login && strcmp(hydra_heads[head_no]->current_pass_ptr, hydra_reverse_login(head_no, hydra_heads[head_no]->current_login_ptr)) == 0)) {
                hydra_brains.sent++;
                hydra_targets[target_no]->sent++;
                if (debug)
                  printf("[DEBUG] double detected (-Pp)\n");
                return hydra_send_next_pair(target_no, head_no);  // little trick to keep the code small
              }
              snpdone = 1;
            }
          }
        }
      } else { // loop_mode == 1
        if (hydra_targets[target_no]->pass_no < hydra_brains.countpass) {
          hydra_heads[head_no]->current_login_ptr = hydra_targets[target_no]->login_ptr;
          if (hydra_targets[target_no]->pass_state == 0)
            hydra_heads[head_no]->current_pass_ptr = hydra_heads[head_no]->current_login_ptr;
          else if (hydra_targets[target_no]->pass_state == 1)
            hydra_heads[head_no]->current_pass_ptr = empty_login;
          else if (hydra_targets[target_no]->pass_state == 2)
            hydra_heads[head_no]->current_pass_ptr = hydra_reverse_login(head_no, hydra_heads[head_no]->current_login_ptr);
          else {
            if (hydra_options.bfg && hydra_targets[target_no]->pass_state == 3
                && hydra_heads[head_no]->current_pass_ptr != NULL &&
                strlen(hydra_heads[head_no]->current_pass_ptr) > 0 &&
                hydra_heads[head_no]->current_pass_ptr != hydra_heads[head_no]->current_login_ptr)
              free(hydra_heads[head_no]->current_pass_ptr);
            hydra_heads[head_no]->current_pass_ptr = strdup(hydra_targets[target_no]->pass_ptr);
          }
          hydra_targets[target_no]->login_no++;
          snpdone = 1;

          if (hydra_targets[target_no]->login_no >= hydra_brains.countlogin) {
            if (hydra_targets[target_no]->pass_state < 3) {
              hydra_targets[target_no]->pass_state++;
              if (hydra_targets[target_no]->pass_state == 1 && hydra_options.try_null_password == 0)
                hydra_targets[target_no]->pass_state++;
              if (hydra_targets[target_no]->pass_state == 2 && hydra_options.try_password_reverse_login == 0)
                hydra_targets[target_no]->pass_state++;
              if (hydra_targets[target_no]->pass_state == 3)
                snpdont = 1;
              hydra_targets[target_no]->pass_no++;
            }
            
            if (hydra_targets[target_no]->pass_state == 3) {
              if (snpdont) {
                hydra_targets[target_no]->pass_ptr = pass_ptr;
              } else {
                if ((hydra_options.mode & 4) == 4) { // bfg mode
#ifndef HAVE_MATH_H
                  sleep(1);
#else
                  hydra_targets[target_no]->pass_ptr = bf_next();
                  if (debug)
                    printf("[DEBUG] bfg new password for next child: %s\n", hydra_targets[target_no]->pass_ptr);
#endif
                } else { // -p -P mode
                  hydra_targets[target_no]->pass_ptr++;
                  while (*hydra_targets[target_no]->pass_ptr != 0)
                    hydra_targets[target_no]->pass_ptr++;
                  hydra_targets[target_no]->pass_ptr++;
                }
                hydra_targets[target_no]->pass_no++;
              }
            }
            
            hydra_targets[target_no]->login_no = 0;
            hydra_targets[target_no]->login_ptr = login_ptr;
          } else if (hydra_targets[target_no]->login_no < hydra_brains.countlogin) {
            hydra_targets[target_no]->login_ptr++;
            while (*hydra_targets[target_no]->login_ptr != 0)
              hydra_targets[target_no]->login_ptr++;
            hydra_targets[target_no]->login_ptr++;
          }
          if (hydra_targets[target_no]->pass_state == 3 && snpdont == 0) {
            if ((hydra_options.try_null_password && strlen(hydra_heads[head_no]->current_pass_ptr) < 1)
                 ||
                (hydra_options.try_password_same_as_login && strcmp(hydra_heads[head_no]->current_pass_ptr, hydra_heads[head_no]->current_login_ptr) == 0)
                 ||
                (hydra_options.try_password_reverse_login && strcmp(hydra_heads[head_no]->current_login_ptr, hydra_heads[head_no]->current_pass_ptr) == 0)) {
              hydra_brains.sent++;
              hydra_targets[target_no]->sent++;
              if (debug)
                printf("[DEBUG] double detected (1)\n");
              return hydra_send_next_pair(target_no, head_no);  // little trick to keep the code small
            }
          }
        }
      }
    }

    if (debug)
      printf("[DEBUG] send_next_pair_mid done %d, pass_state %d, clogin %s, cpass %s, tlogin %s, tpass %s\n",
       snpdone, hydra_targets[target_no]->pass_state, hydra_heads[head_no]->current_login_ptr, hydra_heads[head_no]->current_pass_ptr, hydra_targets[target_no]->login_ptr, hydra_targets[target_no]->pass_ptr);
    
    // no pair? then we go for redo state
    if (!snpdone && hydra_targets[target_no]->redo_state == 0 && hydra_targets[target_no]->redo > 0) {
      if (debug)
        printf("[DEBUG] Entering redo_state\n");
      hydra_targets[target_no]->redo_state++;
      return hydra_send_next_pair(target_no, head_no);  // little trick to keep the code small
    }
  }

  if (!snpdone || hydra_targets[target_no]->skipcnt >= hydra_brains.countlogin) {
    fck = write(hydra_heads[head_no]->sp[0], HYDRA_EXIT, sizeof(HYDRA_EXIT));
    if (hydra_targets[target_no]->use_count <= 1) {
      if (hydra_targets[target_no]->done == 0) {
        hydra_targets[target_no]->done = 1;
        hydra_brains.finished++;
        printf("[STATUS] attack finished for %s (waiting for children to finish) ...\n", hydra_targets[target_no]->target);
      }
    }
    //hydra_kill_head(head_no, 1, 2); // done in main while loop
  } else {
    if (hydra_targets[target_no]->skipcnt > 0) {
      snpj = 0;
      for (snpi = 0; snpi < hydra_targets[target_no]->skipcnt && snpj == 0; snpi++)
        if (strcmp(hydra_heads[head_no]->current_login_ptr, hydra_targets[target_no]->skiplogin[snpi]) == 0)
          snpj = 1;
      if (snpj) {
        if (snp_is_redo == 0) {
          hydra_brains.sent++;
          hydra_targets[target_no]->sent++;
        }
        if (debug)
          printf("[DEBUG] double found for %s == %s, skipping\n", hydra_heads[head_no]->current_login_ptr, hydra_targets[target_no]->skiplogin[snpi - 1]);
        // only if -l/L -p/P with -u and if loginptr was not justed increased
        if ((hydra_options.mode & 64) != 64 && hydra_options.loop_mode == 0 && hydra_targets[target_no]->pass_no > 0) {  // -l -P (not! -u)
          // increase login_ptr to next
          hydra_targets[target_no]->login_no++;
          if (hydra_targets[target_no]->login_no < hydra_brains.countlogin) {
            hydra_targets[target_no]->login_ptr++;
            while (*hydra_targets[target_no]->login_ptr != 0)
              hydra_targets[target_no]->login_ptr++;
            hydra_targets[target_no]->login_ptr++;
          }
          // add count
          hydra_brains.sent += hydra_brains.countpass - hydra_targets[target_no]->pass_no;
          hydra_targets[target_no]->sent += hydra_brains.countpass - hydra_targets[target_no]->pass_no;
          // reset password list
          hydra_targets[target_no]->pass_ptr = pass_ptr;
          hydra_targets[target_no]->pass_no = 0;
          hydra_targets[target_no]->pass_state = 0;
        }
        return hydra_send_next_pair(target_no, head_no);  // little trick to keep the code small
      }
    }
    
    memset(&snpbuf, 0, sizeof(snpbuf));
    strncpy(snpbuf, hydra_heads[head_no]->current_login_ptr, MAXLINESIZE - 3);
    if (strlen(hydra_heads[head_no]->current_login_ptr) > MAXLINESIZE - 3)
      snpbuflen = MAXLINESIZE - 2;
    else
      snpbuflen = strlen(hydra_heads[head_no]->current_login_ptr) + 1;
    strncpy(snpbuf + snpbuflen, hydra_heads[head_no]->current_pass_ptr, MAXLINESIZE - snpbuflen - 1);
    if (strlen(hydra_heads[head_no]->current_pass_ptr) > MAXLINESIZE - snpbuflen - 1)
      snpbuflen += MAXLINESIZE - snpbuflen - 1;
    else
      snpbuflen += strlen(hydra_heads[head_no]->current_pass_ptr) + 1;
    if (snp_is_redo == 0) {
      hydra_brains.sent++;
      hydra_targets[target_no]->sent++;
    } else
      if (debug)
        printf("[DEBUG] send_next_pair_redo done %d, pass_state %d, clogin %s, cpass %s, tlogin %s, tpass %s, is_redo %d\n",
               snpdone, hydra_targets[target_no]->pass_state, hydra_heads[head_no]->current_login_ptr, hydra_heads[head_no]->current_pass_ptr, hydra_targets[target_no]->login_ptr, hydra_targets[target_no]->pass_ptr, snp_is_redo);
    //hydra_dump_data(snpbuf, snpbuflen, "SENT");
    fck = write(hydra_heads[head_no]->sp[0], snpbuf, snpbuflen);
    if (fck < snpbuflen) {
      if (verbose)
        fprintf(stderr, "[ERROR] can not write to child %d, restarting it ...\n", head_no);
      hydra_increase_fail_count(target_no, head_no);
      loop_cnt = 0;
      return 0;                 // not prevent disabling it, if its needed its already done in the above line
    }
    if (debug || hydra_options.showAttempt) {
      printf("[%sATTEMPT] target %s - login \"%s\" - pass \"%s\" - %lu of %lu [child %d]\n",
             snp_is_redo ? "RE-" : "", hydra_targets[target_no]->target, hydra_heads[head_no]->current_login_ptr, hydra_heads[head_no]->current_pass_ptr,
             hydra_targets[target_no]->sent, hydra_brains.todo + hydra_targets[target_no]->redo, head_no);
    }
    loop_cnt = 0;
    return 0;
  }
  loop_cnt = 0;
  return -1;
}

void hydra_skip_user(int target_no, char *username) {
  int i;

  if (username == NULL || *username == 0)
    return;

  // double check
  for (i = 0; i < hydra_targets[target_no]->skipcnt; i++)
    if (strcmp(username, hydra_targets[target_no]->skiplogin[i]) == 0)
      return;

  if (hydra_targets[target_no]->skipcnt < SKIPLOGIN && (hydra_targets[target_no]->skiplogin[hydra_targets[target_no]->skipcnt] = malloc(strlen(username) + 1)) != NULL) {
    strcpy(hydra_targets[target_no]->skiplogin[hydra_targets[target_no]->skipcnt], username);
    hydra_targets[target_no]->skipcnt++;
  }
  if (hydra_options.loop_mode == 0 && (hydra_options.mode & 64) != 64) {
    if (memcmp(username, hydra_targets[target_no]->login_ptr, strlen(username)) == 0) {
      if (debug)
        printf("[DEBUG] skipping username %s\n", username);
      // increase count
      hydra_brains.sent += hydra_brains.countpass - hydra_targets[target_no]->pass_no;
      hydra_targets[target_no]->sent += hydra_brains.countpass - hydra_targets[target_no]->pass_no;
      // step to next login
      hydra_targets[target_no]->login_no++;
      if (hydra_targets[target_no]->login_no < hydra_brains.countlogin) {
        hydra_targets[target_no]->login_ptr++;
        while (*hydra_targets[target_no]->login_ptr != 0)
          hydra_targets[target_no]->login_ptr++;
        hydra_targets[target_no]->login_ptr++;
      }
      // reset password state
      hydra_targets[target_no]->pass_ptr = pass_ptr;
      hydra_targets[target_no]->pass_no = 0;
      hydra_targets[target_no]->pass_state = 0;
    }
  }
}

int hydra_check_for_exit_condition() {
  int i, k = 0;

  if (hydra_brains.exit) {
    if (debug)
      printf("[DEBUG] exit was forced\n");
    return -1;
  }
  if (hydra_brains.targets <= hydra_brains.finished && hydra_brains.active < 1) {
    if (debug)
      printf("[DEBUG] all targets done and all heads finished\n");
    return 1;
  }
  if (hydra_brains.active < 1) {
    // no head active?! check if they are all disabled, if so, we are done
    for (i = 0; i < hydra_options.max_use && k == 0; i++)
      if (hydra_heads[i]->active >= 0)
        k = 1;
    if (k == 0) {
      fprintf(stderr, "[ERROR] all children were disabled due too many connection errors\n");
      return -1;
    }
  }
  return 0;
}

int hydra_select_target() {
  int target_no = -1, i, j = 0;
  
  for (i = 0; i < hydra_brains.targets; i++)
    if (hydra_targets[i]->use_count < hydra_options.tasks)
      if (j < hydra_options.tasks - hydra_targets[i]->failed - hydra_targets[i]->use_count) {
        target_no = i;
        j = hydra_options.tasks - hydra_targets[i]->failed - hydra_targets[i]->use_count;
      }
  return target_no;
}

int main(int argc, char *argv[]) {
  char *proxy_string = NULL, *device = NULL, *memcheck;
  FILE *lfp = NULL, *pfp = NULL, *cfp = NULL, *ifp = NULL;
  size_t countinfile = 1, sizeinfile = 0;
  unsigned long int math2;
  int i = 0, j = 0, k, error = 0, modusage = 0;
  int head_no = 0, target_no = 0, exit_condition = 0, readres;
  time_t starttime, elapsed_status, elapsed_restore, status_print = 59, tmp_time;
  char *tmpptr;
  char rc, buf[MAXBUF];
  fd_set fdreadheads;
  int max_fd;
  struct addrinfo hints, *res, *p;
  struct sockaddr_in6 *ipv6 = NULL;
  struct sockaddr_in *ipv4 = NULL;

  printf("%s %s (c)2013 by %s & %s - for legal purposes only\n\n", PROGRAM, VERSION, AUTHOR, AUTHOR2);
#ifndef LIBPOSTGRES
  SERVICES = hydra_string_replace(SERVICES, "postgres ", "");
  strcat(unsupported, "postgres ");
#endif
#ifndef LIBSAPR3
  SERVICES = hydra_string_replace(SERVICES, "sapr3 ", "");
  strcat(unsupported, "sapr3 ");
#endif
#ifndef LIBFIREBIRD
  SERVICES = hydra_string_replace(SERVICES, "firebird ", "");
  strcat(unsupported, "firebird ");
#endif
#ifndef LIBAFP
  SERVICES = hydra_string_replace(SERVICES, "afp ", "");
  strcat(unsupported, "afp ");
#endif
#ifndef LIBNCP
  SERVICES = hydra_string_replace(SERVICES, "ncp ", "");
  strcat(unsupported, "ncp ");
#endif
#ifndef LIBSSH
  SERVICES = hydra_string_replace(SERVICES, "ssh ", "");
  strcat(unsupported, "ssh ");
  SERVICES = hydra_string_replace(SERVICES, "sshkey ", "");
  strcat(unsupported, "sshkey ");
#endif
#ifndef LIBSVN
  SERVICES = hydra_string_replace(SERVICES, "svn ", "");
  strcat(unsupported, "svn ");
#endif
#ifndef LIBORACLE
  SERVICES = hydra_string_replace(SERVICES, "oracle ", "");
  strcat(unsupported, "oracle ");
#endif
#ifndef LIBMYSQLCLIENT
  SERVICES = hydra_string_replace(SERVICES, "mysql ", "mysql(v4) ");
  strcat(unsupported, "mysql5 ");
#endif
#ifndef LIBOPENSSL
  // for ftps
  SERVICES = hydra_string_replace(SERVICES, " ftps", "");
  // for pop3
  SERVICES = hydra_string_replace(SERVICES, "pop3[s]", "pop3");
  // for imap
  SERVICES = hydra_string_replace(SERVICES, "imap[s]", "imap");
  // for smtp
  SERVICES = hydra_string_replace(SERVICES, "smtp[s]", "smtp");
  // for telnet
  SERVICES = hydra_string_replace(SERVICES, "telnet[s]", "telnet");
  // for http[s]-{head|get}
  SERVICES = hydra_string_replace(SERVICES, "http[s]", "http");
  // for http[s]-{get|post}-form
  SERVICES = hydra_string_replace(SERVICES, "http[s]", "http");
  // for ldap3
  SERVICES = hydra_string_replace(SERVICES, "[-{cram|digest}md5]", "");
  // for sip
  SERVICES = hydra_string_replace(SERVICES, " sip", "");
  // for rdp
  SERVICES = hydra_string_replace(SERVICES, " rdp", "");
  // for oracle-listener
  SERVICES = hydra_string_replace(SERVICES, " oracle-listener", "");
  // general
  SERVICES = hydra_string_replace(SERVICES, "[s]", "");
  // for oracle-sid
  SERVICES = hydra_string_replace(SERVICES, " oracle-sid", "");
  strcat(unsupported, "SSL-services (ftps, sip, rdp, oracle-services, ...) ");
#endif
#ifndef HAVE_MATH_H
  if (strlen(unsupported) > 0)
    strcat(unsupported, "and ");
  strcat(unsupported, "password bruteforce generation ");
#endif
#ifndef HAVE_PCRE
  if (strlen(unsupported) > 0)
    strcat(unsupported, "and ");
  strcat(unsupported, "regex support ");
#endif

  (void) setvbuf(stdout, NULL, _IONBF, 0);
  (void) setvbuf(stderr, NULL, _IONBF, 0);
  // set defaults 
  memset(&hydra_options, 0, sizeof(hydra_options));
  memset(&hydra_brains, 0, sizeof(hydra_brains));
  prg = argv[0];
  hydra_options.debug = debug = 0;
  hydra_options.verbose = verbose = 0;
  found = 0;
  use_proxy = 0;
  proxy_string_ip[0] = 0;
  proxy_string_port = 0;
  strcpy(proxy_string_type, "connect");
  proxy_authentication = cmdlinetarget = NULL;
  hydra_options.login = NULL;
  hydra_options.loginfile = NULL;
  hydra_options.pass = NULL;
  hydra_options.passfile = NULL;
  hydra_options.tasks = TASKS;
  hydra_options.max_use = MAXTASKS;
  hydra_brains.ofp = stdout;
  hydra_brains.targets = 1;
  hydra_options.waittime = waittime = WAITTIME;

  // command line processing
  if (argc > 1 && strncmp(argv[1], "-h", 2) == 0)
    help(1);
  if (argc < 3 && (argc < 2 || strcmp(argv[1], "-R") != 0))
    help(0);
  while ((i = getopt(argc, argv, "h64Rde:vVl:fFg:L:p:P:o:M:C:t:T:m:w:W:s:SUux:")) >= 0) {
    switch (i) {
    case 'h':
      help(1);
    case 'u':
      hydra_options.loop_mode = 1;
      break;
    case '6':
      prefer_ipv6 = 1;
      break;
    case '4':
      prefer_ipv6 = 0;
      break;
    case 'R':
      hydra_options.restore = 1;
      if (argc != 2)
        bail("no option may be supplied together with -R");
      break;
    case 'd':
      hydra_options.debug = debug = 1;
      ++verbose;
      break;
    case 'e':
      i = 0;
      while (i < strlen(optarg)) {
        switch (optarg[i]) {
        case 'r':
          hydra_options.try_password_reverse_login = 1;
          hydra_options.mode = hydra_options.mode | 8;
          break;
        case 'n':
          hydra_options.try_null_password = 1;
          hydra_options.mode = hydra_options.mode | 16;
          break;
        case 's':
          hydra_options.try_password_same_as_login = 1;
          hydra_options.mode = hydra_options.mode | 32;
          break;
        default:
          fprintf(stderr, "[ERROR] unknown mode %c for option -e, only supporting \"n\", \"s\" and \"r\"\n", optarg[i]);
          exit(-1);
        }
        i++;
      }
      break;
    case 'v':
      hydra_options.verbose = verbose = 1;
      break;
    case 'V':
      hydra_options.showAttempt = 1;
      break;
    case 'l':
      hydra_options.login = optarg;
      break;
    case 'L':
      hydra_options.loginfile = optarg;
      hydra_options.mode = hydra_options.mode | 2;
      break;
    case 'p':
      hydra_options.pass = optarg;
      break;
    case 'P':
      hydra_options.passfile = optarg;
      hydra_options.mode = hydra_options.mode | 1;
      break;
    case 'f':
      hydra_options.exit_found = 1;
      break;
    case 'F':
      hydra_options.exit_found = 2;
      break;
    case 'o':
      hydra_options.outfile_ptr = optarg;
      colored_output = 0;
      break;
    case 'M':
      hydra_options.infile_ptr = optarg;
      break;
    case 'C':
      hydra_options.colonfile = optarg;
      hydra_options.mode = 64;
      break;
    case 'm':
      hydra_options.miscptr = optarg;
      break;
    case 'w':
      hydra_options.waittime = waittime = atoi(optarg);
      if (waittime < 1) {
        fprintf(stderr, "[ERROR] waittime must be larger than 0\n");
        exit(-1);
      } else
        if (waittime < 5)
          fprintf(stderr, "[WARNING] the waittime you set is low, this can result in errornous results\n");
      break;
    case 'W':
      hydra_options.conwait = conwait = atoi(optarg);
      break;
    case 's':
      hydra_options.port = port = atoi(optarg);
      break;
    case 'S':
#ifndef LIBOPENSSL
      fprintf(stderr, "[WARNING] hydra was compiled without SSL support. Install openssl and recompile! Option ignored...\n");
      hydra_options.ssl = 0;
      break;
#else
      hydra_options.ssl = 1;
      break;
#endif
    case 't':
      hydra_options.tasks = atoi(optarg);
      break;
    case 'T':
      hydra_options.max_use = atoi(optarg);
      break;
    case 'U':
      modusage = 1;
      break;
    case 'x':
#ifndef HAVE_MATH_H
      fprintf(stderr, "[ERROR] -x option is not available as math.h was not found at compile time\n");
      exit(-1);
#else
      if (strcmp(optarg, "-h") == 0)
        help_bfg();
      bf_options.arg = optarg;
      hydra_options.bfg = 1;
      hydra_options.mode = hydra_options.mode | 4;
      hydra_options.loop_mode = 1;
      break;
#endif
    default:
      exit(-1);
    }
  }

  //check if output is redirected from the shell or in a file
  if (colored_output && !isatty(fileno(stdout)))
    colored_output = 0;

#ifdef LIBNCURSES
  //then check if the term is color enabled using ncurses lib
  if (colored_output) {
    if (!setupterm(NULL,1,NULL) && (tigetnum("colors") <= 0)) {
        colored_output = 0;
    }
  }
#else
  //don't want border line effect so disabling color output
  //if we are not sure about the term
  colored_output = 0;
#endif

  if (debug)
    printf("[DEBUG] Ouput color flag is %d\n", colored_output);

  printf("%s (%s) starting at %s\n", PROGRAM, RESOURCE, hydra_build_time());
  if (debug) {
    printf("[DEBUG] cmdline: ");
    for (i = 0; i < argc; i++)
      printf("%s ", argv[i]);
    printf("\n");
  }
  if (hydra_options.login != NULL && hydra_options.loginfile != NULL)
    bail("You can only use -L OR -l, not both\n");
  if (hydra_options.pass != NULL && hydra_options.passfile != NULL)
    bail("You can only use -P OR -p, not both\n");
  if (hydra_options.restore) {
    hydra_restore_read();
    // stuff we have to copy from the non-restore part 
    if (strncmp(hydra_options.service, "http-", 5) == 0) {
      if (getenv("HYDRA_PROXY_HTTP") && getenv("HYDRA_PROXY"))
        bail("Found HYDRA_PROXY_HTTP *and* HYDRA_PROXY environment variables - you can use only ONE for the service http-head/http-get!");
      if (getenv("HYDRA_PROXY_HTTP")) {
        printf("[INFO] Using HTTP Proxy: %s\n", getenv("HYDRA_PROXY_HTTP"));
        use_proxy = 1;
      }
    }
  } else {                      // normal mode, aka non-restore mode 
    if (hydra_options.colonfile)
      hydra_options.loop_mode = 0; // just to be sure
    if (hydra_options.infile_ptr != NULL) {
      if (optind + 2 < argc)
        bail("The -M FILE option can not be used together with a host on the commandline");
      if (optind + 1 > argc)
        bail("You need to define a service to attack");
      if (optind + 2 == argc)
        fprintf(stderr, "[WARNING] With the -M FILE option you can not specify a server on the commandline. Lets hope you did everything right!\n");
      hydra_options.server = NULL;
      hydra_options.service = argv[optind];
      if (optind + 2 == argc)
        hydra_options.miscptr = argv[optind + 1];
    } else if (optind + 2 != argc && optind + 3 != argc) {
      // check if targetdef follow syntax <service-name>://<target>[:<port-number>][/<parameters>] or it's a syntax error 
      char *targetdef = argv[optind];
      char *service_pos;

      if ((targetdef != NULL) && (strstr(targetdef, "://") != NULL)) {
        char *targetport_sep;
        char *port_pos = NULL, *param_pos = NULL;

        service_pos = strstr(targetdef, "://");
        if ((service_pos - targetdef) == 0)
          bail("could not identify service");
        if ((hydra_options.service = malloc(1 + service_pos - targetdef)) == NULL)
          bail("could not alloc memory");
        strncpy(hydra_options.service, targetdef, service_pos - targetdef);
        hydra_options.service[service_pos - targetdef] = '\0';

        // check if we specify a port 
        if (prefer_ipv6)
          targetport_sep = "]:";
        else
          targetport_sep = ":";
        port_pos = strstr(service_pos + strlen("://"), targetport_sep);
        param_pos = strstr(service_pos + strlen("://"), "/");
        if (param_pos != NULL && param_pos < port_pos)
          port_pos = NULL;

        if (port_pos != NULL) {
          *port_pos = 0;
          // removing ://[ 
          if (port_pos - service_pos - 2 - strlen(targetport_sep) == 0)
            bail("identifying server address");
          if (prefer_ipv6 && ((service_pos + 3)[0] != '['))
            bail("identifying ipv6 server address");
          if ((hydra_options.server = malloc(1 + port_pos - service_pos - 2 - strlen(targetport_sep))) == NULL)
            bail("could not allocate memory");
          strncpy(hydra_options.server, service_pos + 3, port_pos - service_pos - 3);
          hydra_options.server[port_pos - service_pos - 3] = '\0';
        }
        // check if we specify a parameter 
        if ((param_pos != NULL) && (port_pos == NULL)) {
          if (param_pos - service_pos - 3 == 0)
            bail("could not identify server address");
          if ((hydra_options.server = malloc(1 + param_pos - service_pos - 3)) == NULL)
            bail("could not allocate memory");
          if (service_pos[3] == '[') {
            strncpy(hydra_options.server, service_pos + 4, param_pos - service_pos - 3);
            hydra_options.server[param_pos - 4 - service_pos] = '\0';
          } else {
            strncpy(hydra_options.server, service_pos + 3, param_pos - service_pos - 3);
            hydra_options.server[param_pos - 3 - service_pos] = '\0';
          }
          if (hydra_options.server[strlen(hydra_options.server) - 1] == ']')
            hydra_options.server[strlen(hydra_options.server) - 1] = 0;
        }
        if ((port_pos == NULL) && (param_pos == NULL)) {
          if (strlen(targetdef) - strlen(hydra_options.service) - 3 == 0)
            bail("could not identify server address");
          if ((hydra_options.server = malloc(1 + strlen(targetdef) - strlen(hydra_options.service) - 3)) == NULL)
            bail("could not allocate memory");
          if (service_pos[3] == '[')
            strcpy(hydra_options.server, service_pos + 4);
          else
            strcpy(hydra_options.server, service_pos + 3);
          if (hydra_options.server[strlen(hydra_options.server) - 1] == ']')
            hydra_options.server[strlen(hydra_options.server) - 1] = 0;
        } else {
          char port_temp[6] = "";

          if (port_pos) {
            // set the port 
            if (param_pos == NULL) {
              hydra_options.port = port = atoi(port_pos + strlen(targetport_sep));
            } else {
              if (param_pos - port_pos - strlen(targetport_sep) > 0) {
                if (param_pos - port_pos - strlen(targetport_sep) > sizeof(port_temp) - 1)
                  bail("invalid port number");
                strncpy(port_temp, port_pos + strlen(targetport_sep), param_pos - port_pos - strlen(targetport_sep));
                port_temp[strlen(port_temp)] = '\0';
                hydra_options.port = port = atoi(port_temp);
              }
            }
          }
          if (param_pos) {
            int size_of_miscptr = 0;

            if (strstr(hydra_options.service, "http") != NULL && strstr(hydra_options.service, "http-proxy") == NULL && param_pos[1] != '/') {
              param_pos--;
            }

            size_of_miscptr = strlen(param_pos);

            if (size_of_miscptr > 0) {
              if ((hydra_options.miscptr = malloc(1 + size_of_miscptr)) == NULL)
                bail("could not allocate memory");
              strcpy(hydra_options.miscptr, param_pos + 1);
            }
          }
        }
        if (debug)
          printf("[DEBUG] opt:%d argc:%d mod:%s tgt:%s port:%d misc:%s\n", optind, argc, hydra_options.service, hydra_options.server, hydra_options.port, hydra_options.miscptr);
      } else {
        hydra_options.server = NULL;
        hydra_options.service = NULL;

        if (modusage)
          hydra_options.service = targetdef;
        else
          help(0);
      }
    } else {
      hydra_options.server = argv[optind];
      cmdlinetarget = argv[optind];
      hydra_options.service = argv[optind + 1];
      if (optind + 3 == argc)
        hydra_options.miscptr = argv[optind + 2];
    }

    if (strcmp(hydra_options.service, "pop3s") == 0 || strcmp(hydra_options.service, "smtps") == 0 || strcmp(hydra_options.service, "imaps") == 0 || strcmp(hydra_options.service, "telnets") == 0 || (strncmp(hydra_options.service, "ldap", 4) == 0 && hydra_options.service[strlen(hydra_options.service) - 1] == 's')) {
      hydra_options.ssl = 1;
      hydra_options.service[strlen(hydra_options.service) - 1] = 0;
    }

    if (getenv("HYDRA_PROXY_HTTP") || getenv("HYDRA_PROXY")) {
      if (strcmp(hydra_options.service, "afp") == 0 || strcmp(hydra_options.service, "firebird") == 0 || strncmp(hydra_options.service, "mysql", 5) == 0 || 
          strcmp(hydra_options.service, "ncp") == 0 || strcmp(hydra_options.service, "oracle") == 0 || strcmp(hydra_options.service, "postgres") == 0 || 
          strncmp(hydra_options.service, "ssh", 3) == 0 || strcmp(hydra_options.service, "sshkey") == 0 || strcmp(hydra_options.service, "svn") == 0 ||
          strcmp(hydra_options.service, "sapr3") == 0) {
        fprintf(stderr, "[WARNING] module %s does not support HYDRA_PROXY* !\n", hydra_options.service);
        proxy_string = NULL;
      }
    }

    /* here start the services */
    
    if (strcmp(hydra_options.service, "ssl") == 0 || strcmp(hydra_options.service, "www") == 0 || strcmp(hydra_options.service, "http") == 0
        || strcmp(hydra_options.service, "https") == 0) {
      fprintf(stderr, "[WARNING] The service http has been replaced with http-head and http-get, using by default GET method. Same for https.\n");
      if (strcmp(hydra_options.service, "http") == 0) {
        hydra_options.service=malloc(strlen("http-get")+1);
        strcpy(hydra_options.service, "http-get");
      }
      if (strcmp(hydra_options.service, "https") == 0) {
        hydra_options.service=malloc(strlen("https-get")+1);
        strcpy(hydra_options.service, "https-get");
      }
    }

    if (strcmp(hydra_options.service, "http-form-get") == 0)
      strcpy(hydra_options.service, "http-get-form");
    if (strcmp(hydra_options.service, "https-form-get") == 0)
      strcpy(hydra_options.service, "https-get-form");
    if (strcmp(hydra_options.service, "http-form-post") == 0)
      strcpy(hydra_options.service, "http-post-form");
    if (strcmp(hydra_options.service, "https-form-post") == 0)
      strcpy(hydra_options.service, "https-post-form");

    if (modusage == 1)
      module_usage();

    i = 0;
    if (strcmp(hydra_options.service, "telnet") == 0) {
      fprintf(stderr, "[WARNING] telnet is by its nature unreliable to analyze reliable, if possible better choose FTP or SSH if available\n");
      i = 1;
    }
    if (strcmp(hydra_options.service, "ftp") == 0)
      i = 1;
    if (strcmp(hydra_options.service, "ftps") == 0) {
      fprintf(stderr, "[WARNING] you enabled ftp-SSL (auth tls) mode. If you want to use direct SSL ftp, use -S and the ftp module instead.\n");
      i = 1;
    }
    if (strcmp(hydra_options.service, "pop3") == 0)
      i = 1;
    if (strcmp(hydra_options.service, "imap") == 0)
      i = 1;
    if (strcmp(hydra_options.service, "asterisk") == 0)
      i = 1;
   if (strcmp(hydra_options.service, "vmauthd") == 0)
      i = 1;
    if (strcmp(hydra_options.service, "rexec") == 0)
      i = 1;
    if (strcmp(hydra_options.service, "rlogin") == 0)
      i = 1;
    if (strcmp(hydra_options.service, "rsh") == 0)
      i = 3;
    if (strcmp(hydra_options.service, "nntp") == 0)
      i = 1;
    if (strcmp(hydra_options.service, "socks5") == 0)
      i = 1;
    if (strcmp(hydra_options.service, "icq") == 0) {
      fprintf(stderr, "[WARNING] The icq module is not working with the modern protocol version! (somebody else will need to fix this as I don't care for icq)\n");
      i = 1;
    }
    if (strcmp(hydra_options.service, "mysql") == 0) {
      i = 1;
      if (hydra_options.tasks > 4) {
        fprintf(stderr, "[INFO] Reduced number of tasks to 4 (mysql does not like many parallel connections)\n");
        hydra_options.tasks = 4;
      }
    }
    if (strcmp(hydra_options.service, "mssql") == 0)
      i = 1;
    if ((strcmp(hydra_options.service, "oracle-listener") == 0) || (strcmp(hydra_options.service, "tns") == 0)) {
      i = 2;
      hydra_options.service=malloc(strlen("oracle-listener")+1);
      strcpy(hydra_options.service, "oracle-listener");
    }
    if ((strcmp(hydra_options.service, "oracle-sid") == 0) || (strcmp(hydra_options.service, "sid") == 0)) {
      i = 3;
      hydra_options.service=malloc(strlen("oracle-sid")+1);
      strcpy(hydra_options.service, "oracle-sid");
    }
#ifdef LIBORACLE
    if ((strcmp(hydra_options.service, "oracle") == 0) || (strcmp(hydra_options.service, "ora") == 0)) {
      i = 1;
      hydra_options.service=malloc(strlen("oracle")+1);
      strcpy(hydra_options.service, "oracle");
    }
#endif
    if (strcmp(hydra_options.service, "postgres") == 0)
#ifdef LIBPOSTGRES
      i = 1;
#else
      bail("Compiled without LIBPOSTGRES support, module not available!");
#endif
    if (strcmp(hydra_options.service, "firebird") == 0)
#ifdef LIBFIREBIRD
      i = 1;
#else
      bail("Compiled without LIBFIREBIRD support, module not available!");
#endif
    if (strcmp(hydra_options.service, "afp") == 0)
#ifdef LIBAFP
      i = 1;
#else
      bail("Compiled without LIBAFP support, module not available!");
#endif
    if (strcmp(hydra_options.service, "svn") == 0)
#ifdef LIBSVN
      i = 1;
#else
      bail("Compiled without LIBSVN support, module not available!");
#endif
    if (strcmp(hydra_options.service, "ncp") == 0)
#ifdef LIBNCP
      i = 1;
#else
      bail("Compiled without LIBNCP support, module not available!");
#endif
    if (strcmp(hydra_options.service, "pcanywhere") == 0)
      i = 1;
    if (strcmp(hydra_options.service, "http-proxy") == 0) {
      i = 1;
      if (hydra_options.miscptr != NULL && strncmp(hydra_options.miscptr, "http://", 7) != 0)

        bail("module option must start with http://");
    }
    if (strcmp(hydra_options.service, "cvs") == 0) {
      i = 1;
      if (hydra_options.miscptr == NULL || (strlen(hydra_options.miscptr) == 0)) {
        fprintf(stderr, "[INFO] The CVS repository path wasn't passed so using /root by default\n");
      }
    }
    if (strcmp(hydra_options.service, "svn") == 0) {
      i = 1;
      if (hydra_options.miscptr == NULL || (strlen(hydra_options.miscptr) == 0)) {
        fprintf(stderr, "[INFO] The SVN repository path wasn't passed so using /trunk by default\n");
      }
    }
    if (strcmp(hydra_options.service, "ssh") == 0 || strcmp(hydra_options.service, "sshkey") == 0)
#ifdef LIBSSH
      i = 1;
#else
      bail("Compiled without LIBSSH v0.4.x support, module is not available!");
#endif
    if (strcmp(hydra_options.service, "smtp") == 0)
      i = 1;
    if (strcmp(hydra_options.service, "smtp-enum") == 0)
      i = 1;
    if (strcmp(hydra_options.service, "teamspeak") == 0)
      i = 1;

// ADD NEW SERVICES HERE 
    if ((strcmp(hydra_options.service, "smb") == 0) || (strcmp(hydra_options.service, "smbnt") == 0)) {
      if (hydra_options.tasks > 1) {
        fprintf(stderr, "[INFO] Reduced number of tasks to 1 (smb does not like parallel connections)\n");
        hydra_options.tasks = 1;
      }
      i = 1;
    }
    if ((strcmp(hydra_options.service, "smb") == 0) || (strcmp(hydra_options.service, "smbnt") == 0)) {
#ifdef LIBOPENSSL
      if (hydra_options.tasks > 1) {
        fprintf(stderr, "[INFO] Reduced number of tasks to 1 (smb does not like parallel connections)\n");
        hydra_options.tasks = 1;
      }
      i = 1;
#endif
    }
    if ((strcmp(hydra_options.service, "smb") == 0) || (strcmp(hydra_options.service, "smbnt") == 0) ||
        (strcmp(hydra_options.service, "sip") == 0) || (strcmp(hydra_options.service, "rdp") == 0) ||
        (strcmp(hydra_options.service, "oracle-listener") == 0) || (strcmp(hydra_options.service, "oracle-sid") == 0)) {
#ifndef LIBOPENSSL
      bail("Compiled without OPENSSL support, module not available!");
#endif
    }
    if (strcmp(hydra_options.service, "pcnfs") == 0) {
      i = 1;
      if (port == 0)
        bail("You must set the port for pcnfs with -s (run \"rpcinfo -p %s\" and look for the pcnfs v2 UDP port)");
    }
    if (strcmp(hydra_options.service, "sapr3") == 0) {
#ifdef LIBSAPR3
      i = 1;
      if (port == PORT_SAPR3)
        bail("You must set the port for sapr3 with -s <port>, it should lie between 3200 and 3699.");
      if (port < 3200 || port > 3699)
        fprintf(stderr, "[WARNING] The port is not in the range 3200 to 3399 - please ensure it is ok!\n");
      if (hydra_options.miscptr == NULL || atoi(hydra_options.miscptr) < 0 || atoi(hydra_options.miscptr) > 999 || !isdigit(hydra_options.miscptr[0]))
        bail("You must set the client ID (0-999) as an additional option or via -m");
#else
      bail("Compiled without LIBSAPR3 support, module not available!");
#endif
    }
    if (strcmp(hydra_options.service, "cisco") == 0) {
      i = 2;
      if (hydra_options.tasks > 4)
        fprintf(stderr, "[WARNING] you should set the number of parallel task to 4 for cisco services.\n");
    }
    if (strncmp(hydra_options.service, "snmpv", 5) == 0) {
      hydra_options.service[4] = hydra_options.service[5];
      hydra_options.service[5] = 0;
    }
    if (strcmp(hydra_options.service, "snmp") == 0 || strcmp(hydra_options.service, "snmp1") == 0) {
      hydra_options.service[4] = 0;
      i = 2;
    }
    if (strcmp(hydra_options.service, "snmp2") == 0 || strcmp(hydra_options.service, "snmp3") == 0) {
      if (hydra_options.miscptr == NULL)
        hydra_options.miscptr = strdup(hydra_options.service + 4);
      else {
        tmpptr = malloc(strlen(hydra_options.miscptr) + 4);
        strcpy(tmpptr, hydra_options.miscptr);
        strcat(tmpptr, ":");
        strcat(tmpptr, hydra_options.service + 4);
        hydra_options.miscptr = tmpptr;
      }
      hydra_options.service[4] = 0;
      i = 2;
    }
    if (strcmp(hydra_options.service, "snmp") == 0 && hydra_options.miscptr != NULL) {
      char *lptr;
      j = 1;
      tmpptr = strdup(hydra_options.miscptr);
      lptr = strtok(tmpptr, ":");
      while(lptr != NULL) {
        i = 0;
        if (strcasecmp(lptr, "1") == 0 || strcasecmp(lptr, "2") == 0 || strcasecmp(lptr, "3") == 0) {
          i = 1;
          j = lptr[0] - '0' + (j & 252);
        }
        else if (strcasecmp(lptr, "READ") == 0 || strcasecmp(lptr, "WRITE") == 0 || strcasecmp(lptr, "PLAIN") == 0)
          i = 1;
        else if (strcasecmp(lptr, "MD5") == 0) {
          i = 1;
          j = 4 + (j & 51);
        } else if (strcasecmp(lptr, "SHA") == 0 || strcasecmp(lptr, "SHA1") == 0) {
          i = 1;
          j = 8 + (j & 51);
        } else if (strcasecmp(lptr, "DES") == 0) {
          i = 1;
          j = 16 + (j & 15);
        } else if (strcasecmp(lptr, "AES") == 0) {
          i = 1;
          j = 32 + (j & 15);
        }
        if (i == 0) {
          fprintf(stderr, "[ERROR] unknown parameter in module option: %s\n", lptr);
          exit(-1);
        }
        lptr = strtok(NULL, ":");
      }
      i = 2;
      if ((j & 3) < 3 && j > 2)
        fprintf(stderr, "[WARNING] SNMPv1 and SNMPv2 do not support hash and encryption, ignored\n");
      if ((j & 3) == 3) {
        fprintf(stderr, "[WARNING] SNMPv3 is still in beta state, use at own risk and report problems\n");
        if (j >= 16)
          bail("The SNMPv3 module so far only support authentication (md5/sha), not yet encryption\n");
        if (hydra_options.colonfile == NULL && ((hydra_options.login == NULL && hydra_options.loginfile == NULL) || (hydra_options.pass == NULL && hydra_options.passfile == NULL && hydra_options.bfg == 0))) {
          if (j > 3) {
            fprintf(stderr, "[ERROR] you specified SNMPv3, defined hashing/encryption but only gave one of login or password list. Either supply both logins and passwords (this is what is usually used in SNMPv3), or remove the hashing/encryption option (unusual)\n");
            exit(-1);
          }
          fprintf(stderr, "[WARNING] you specified SNMPv3 but gave no logins, NoAuthNoPriv is assumed. This is an unusual case, you should know what you are doing\n");
          tmpptr = malloc(strlen(hydra_options.miscptr) + 8);
          strcpy(tmpptr, hydra_options.miscptr);
          strcat(tmpptr, ":");
          strcat(tmpptr, "PLAIN");
          hydra_options.miscptr = tmpptr;
        } else {
          i = 1; // snmpv3 with login+pass mode
#ifndef LIBOPENSSL
          bail("hydra was not compiled with OPENSSL support, snmpv3 can only be used on NoAuthNoPriv mode (only logins, no passwords)!");
#endif
          printf("[INFO] Using %s SNMPv3 with %s authentication and %s privacy\n", j > 16 ? "AuthPriv" : "AuthNoPriv", (j & 8) == 8 ? "SHA" : "MD5", (j & 16) == 16 ? "DES" : (j > 16) ? "AES" : "no");
        }
      }
    }
    if (strcmp(hydra_options.service, "sip") == 0) {
      if (hydra_options.miscptr == NULL) {
        if (hydra_options.server != NULL) {
          hydra_options.miscptr = hydra_options.server;
          i = 1;
        } else {
          bail("The sip module does not work with multiple servers (-M)\n");
        }
      } else {
        i = 1;
      }
    }
    if (strcmp(hydra_options.service, "ldap") == 0) {
      bail("Please select ldap2 or ldap3 for simple authentication or ldap3-crammd5 or ldap3-digestmd5\n");
    }
    if (strcmp(hydra_options.service, "ldap2") == 0 || strcmp(hydra_options.service, "ldap3") == 0) {
      i = 1;
      if ((hydra_options.miscptr != NULL && hydra_options.login != NULL)
          || (hydra_options.miscptr != NULL && hydra_options.loginfile != NULL) || (hydra_options.login != NULL && hydra_options.loginfile != NULL))
        bail("you may only use one of -l, -L or -m\n");
      if (hydra_options.login == NULL && hydra_options.loginfile == NULL && hydra_options.miscptr == NULL)
        fprintf(stderr, "[WARNING] no DN to authenticate is defined, using DN of null (use -m, -l or -L to define DNs)\n");
      if (hydra_options.login == NULL && hydra_options.loginfile == NULL) {
        i = 2;
      }
    }
    if (strcmp(hydra_options.service, "ldap3-crammd5") == 0 || strcmp(hydra_options.service, "ldap3-digestmd5") == 0) {
      i = 1;
      if (hydra_options.login == NULL && hydra_options.loginfile == NULL)
        bail("-l or -L option is required to specify the login\n");
      if (hydra_options.miscptr == NULL)
        bail("-m option is required to specify the DN\n");
    }

    if (strcmp(hydra_options.service, "cisco-enable") == 0) {
      i = 4;
      if (hydra_options.login == NULL)
        hydra_options.login = empty_login;
      if (hydra_options.miscptr == NULL) {
        fprintf(stderr, "[WARNING] You did not supply the initial support to the Cisco via -l, assuming direct console access\n");
      }
      if (hydra_options.tasks > 4)
        fprintf(stderr, "[WARNING] you should set the number of parallel task to 4 for cisco enable services.\n");
    }
    if (strcmp(hydra_options.service, "http-proxy-urlenum") == 0) {
      i = 4;
      hydra_options.pass = empty_login;
      if (hydra_options.miscptr == NULL) {
        fprintf(stderr, "[WARNING] You did not supply proxy credentials via the optional parameter\n");
      }
      if (hydra_options.bfg || hydra_options.passfile != NULL)
        bail("the http-proxy-urlenum does not need the -p/-P or -x option");
    }
    if (strcmp(hydra_options.service, "vnc") == 0) {
      i = 2;
      if (hydra_options.tasks > 4)
        fprintf(stderr, "[WARNING] you should set the number of parallel task to 4 for vnc services.\n");
    }
    if (strcmp(hydra_options.service, "https-head") == 0 || strcmp(hydra_options.service, "https-get") == 0) {
#ifdef LIBOPENSSL
      i = 1;
      hydra_options.ssl = 1;
      if (strcmp(hydra_options.service, "https-head") == 0)
        strcpy(hydra_options.service, "http-head");
      else
        strcpy(hydra_options.service, "http-get");
#else
      bail("Compiled without SSL support, module not available");
#endif
    }
    if (strcmp(hydra_options.service, "http-get") == 0 || strcmp(hydra_options.service, "http-head") == 0) {
      i = 1;
      if (hydra_options.miscptr == NULL) {
        fprintf(stderr, "[WARNING] You must supply the web page as an additional option or via -m, default path set to /\n");
        hydra_options.miscptr = malloc(2);
        hydra_options.miscptr = "/";
      }
      if (*hydra_options.miscptr != '/' && strstr(hydra_options.miscptr, "://") == NULL)
        bail("The web page you supplied must start with a \"/\", \"http://\" or \"https://\", e.g. \"/protected/login\"");
      if (getenv("HYDRA_PROXY_HTTP") && getenv("HYDRA_PROXY"))
        bail("Found HYDRA_PROXY_HTTP *and* HYDRA_PROXY environment variables - you can use only ONE for the service http-head/http-get!");
      if (getenv("HYDRA_PROXY_HTTP")) {
        printf("[INFO] Using HTTP Proxy: %s\n", getenv("HYDRA_PROXY_HTTP"));
        use_proxy = 1;
      }
      if (strcmp(hydra_options.service, "http-head") == 0)
        fprintf(stderr, "[WARNING] http-head auth does not work with every server, better use http-get\n");
    }

    if (strcmp(hydra_options.service, "http-get-form") == 0 || strcmp(hydra_options.service, "http-post-form") == 0 || strcmp(hydra_options.service, "https-get-form") == 0 || strcmp(hydra_options.service, "https-post-form") == 0 ) {
      char bufferurl[1024], *url, *variables, *cond, *optional1;

      if (strncmp(hydra_options.service, "http-", 5) == 0) {
        i = 1;
      } else { // https
#ifdef LIBOPENSSL
        i = 1;
        hydra_options.ssl = 1;
        if (strcmp(hydra_options.service, "https-post-form") == 0)
          strcpy(hydra_options.service, "http-post-form");
        else
          strcpy(hydra_options.service, "http-get-form");
#else
        bail("Compiled without SSL support, module not available");
#endif
      }
      if (hydra_options.miscptr == NULL) {
        fprintf(stderr, "[WARNING] You must supply the web page as an additional option or via -m, default path set to /\n");
        hydra_options.miscptr = malloc(2);
        hydra_options.miscptr = "/";
      }
      //if (*hydra_options.miscptr != '/' && strstr(hydra_options.miscptr, "://") == NULL)
      //  bail("The web page you supplied must start with a \"/\", \"http://\" or \"https://\", e.g. \"/protected/login\"");
      if (hydra_options.miscptr[0] != '/')
        bail("optional parameter must start with a '/' slash!\n");
      if (getenv("HYDRA_PROXY_HTTP") && getenv("HYDRA_PROXY"))
        bail("Found HYDRA_PROXY_HTTP *and* HYDRA_PROXY environment variables - you can use only ONE for the service http-head/http-get!");
      if (getenv("HYDRA_PROXY_HTTP")) {
        printf("[INFO] Using HTTP Proxy: %s\n", getenv("HYDRA_PROXY_HTTP"));
        use_proxy = 1;
      }
      
      if (strstr(hydra_options.miscptr, "\\:") != NULL) {
        fprintf(stderr, "[WARNING] escape sequence \\: detected in module option, no parameter verification is performed.\n");
      } else {
        sprintf(bufferurl, "%.1000s", hydra_options.miscptr);
        url = strtok(bufferurl, ":");
        variables = strtok(NULL, ":");
        cond = strtok(NULL, ":");
        optional1 = strtok(NULL, "\n");
        if ((variables == NULL ) || (strstr(variables, "^USER^") == NULL && strstr(variables, "^PASS^") == NULL)) {
          fprintf(stderr, "[ERROR] the variables argument needs at least the strings ^USER^ or ^PASS^: %s\n", variables);
          exit(-1);
        }
        if ((url == NULL) || (cond == NULL)) {
          fprintf(stderr, "[ERROR] Wrong syntax, requires three arguments separated by a colon which may not be null: %s\n", bufferurl);
          exit(-1);
        }
        while ((optional1 = strtok(NULL, ":")) != NULL) {
          if (optional1[1] != '=' && optional1[1] != ':' && optional1[1] != 0) {
            fprintf(stderr, "[ERROR] Wrong syntax of optional argument: %s\n", optional1);
            exit(-1);
          }
          switch (optional1[0]) {
          case 'C':              // fall through
          case 'c':
            if (optional1[1] != '=' || optional1[2] != '/') {
              fprintf(stderr, "[ERROR] Wrong syntax of parameter C, must look like 'C=/url/of/page', not http:// etc.: %s\n", optional1);
              exit(-1);
            }
            break;
          case 'H':              // fall through
          case 'h':
            if (optional1[1] != '=' || strtok(NULL, ":") == NULL) {
              fprintf(stderr, "[ERROR] Wrong syntax of parameter H, must look like 'H=X-My-Header: MyValue', no http:// : %s\n", optional1);
              exit(-1);
            }
            break;
          default:
            fprintf(stderr, "[ERROR] Unknown optional argument: %s", optional1);
          }
        }
      }
    }

    if (strcmp(hydra_options.service, "xmpp") == 0)
      i = 1;
    if (strcmp(hydra_options.service, "irc") == 0)
      i = 1;
    if (strcmp(hydra_options.service, "rdp") == 0) {
      if (hydra_options.tasks > 4)
        fprintf(stderr, "[WARNING] rdp servers often don't like many connections, use -t 1 or -t 4 to reduce the number of parallel connections and -W 1 or -W 3 to wait between connection to allow the server to recover\n");
      //if (hydra_options.tasks > 4) {
      //  fprintf(stderr, "[INFO] Reduced number of tasks to 4 (rdp does not like many parallel connections)\n");
      //  hydra_options.tasks = 4;
      //}
      //if (conwait == 0)
      //  hydra_options.conwait = conwait = 1;
      i = 1;
    }
    // ADD NEW SERVICES HERE 

    if (i == 0) {
      fprintf(stderr, "[ERROR] Unknown service: %s\n", hydra_options.service);
      exit(-1);
    }

    if (hydra_options.loop_mode && hydra_options.colonfile != NULL)
      bail("The  loop mode option (-u) works with all modes - except colon files (-C)\n");
    if (strncmp(hydra_options.service, "http-", strlen("http-")) != 0 && strcmp(hydra_options.service, "http-head") != 0 && getenv("HYDRA_PROXY_HTTP") != NULL)
      fprintf(stderr, "[WARNING] the HYDRA_PROXY_HTTP environment variable works only with the http-head/http-get module, ignored...\n");
    if (i == 2) {
      if (hydra_options.colonfile != NULL || ((hydra_options.login != NULL || hydra_options.loginfile != NULL) && (hydra_options.pass != NULL || hydra_options.passfile != NULL || hydra_options.bfg > 0)))
        bail("The cisco, oracle-listener, snmp and vnc modules are only using the -p or -P option, not login (-l, -L) or colon file (-C).\nUse the telnet module for cisco using \"Username:\" authentication.\n");
      if ((hydra_options.login != NULL || hydra_options.loginfile != NULL) && (hydra_options.pass == NULL || hydra_options.passfile == NULL)) {
        hydra_options.pass = hydra_options.login;
        hydra_options.passfile = hydra_options.loginfile;
      }
      hydra_options.login = empty_login;
      hydra_options.loginfile = NULL;
    }
    if (i == 3) {
      if (hydra_options.colonfile != NULL || hydra_options.bfg > 0 || ((hydra_options.login != NULL || hydra_options.loginfile != NULL) && (hydra_options.pass != NULL || hydra_options.passfile != NULL)))
        bail("The rsh, oracle-sid login is neither using the -p, -P or -x options nor colon file (-C)\n");
      if ((hydra_options.login == NULL || hydra_options.loginfile == NULL) && (hydra_options.pass != NULL || hydra_options.passfile != NULL)) {
        hydra_options.login = hydra_options.pass;
        hydra_options.loginfile = hydra_options.passfile;
      }
      hydra_options.pass = empty_login;
      hydra_options.passfile = NULL;
    }
    if (i == 3 && hydra_options.login == NULL && hydra_options.loginfile == NULL)
      bail("I need at least either the -l or -L option to know the login");
    if (i == 2 && hydra_options.pass == NULL && hydra_options.passfile == NULL && hydra_options.bfg == 0)
      bail("I need at least either the -p, -P or -x option to have a password to try");
    if (i == 1 && hydra_options.login == NULL && hydra_options.loginfile == NULL && hydra_options.colonfile == NULL)
      bail("I need at least either the -l, -L or -C option to know the login");
    if (hydra_options.colonfile != NULL && ((hydra_options.bfg != 0 || hydra_options.login != NULL || hydra_options.loginfile != NULL)
                                            || (hydra_options.pass != NULL && hydra_options.passfile != NULL)))
      bail("The -C option is standalone, don't use it with -l/L, -p/P or -x!");
    if ((hydra_options.bfg)
        && ( (hydra_options.pass != NULL) || (hydra_options.passfile != NULL)
            || (hydra_options.colonfile != NULL) ) )
      bail("The -x (password bruteforce generation option) doesn't work with -p/P, -C or -e!\n");
    if (hydra_options.try_password_reverse_login == 0 && hydra_options.try_password_same_as_login == 0 && hydra_options.try_null_password == 0 && (i != 3 && (hydra_options.pass == NULL && hydra_options.passfile == NULL
             && hydra_options.colonfile == NULL)) && hydra_options.bfg == 0) {
      // test if the service is smtp-enum as it could be used either with a login+pass or only a login 
      if (strstr(hydra_options.service, "smtp-enum") != NULL)
        hydra_options.pass = empty_login;
      else
        bail("I need at least the -e, -p, -P or -x option to have some passwords!");
    }
    if (hydra_options.tasks < 1 || hydra_options.tasks > MAXTASKS) {
      fprintf(stderr, "[ERROR] Option -t needs to be a number between 1 and %d\n", MAXTASKS);
      exit(-1);
    }
    if (hydra_options.max_use > MAXTASKS) {
      fprintf(stderr, "[WARNING] reducing maximum tasks to MAXTASKS (%d)\n", MAXTASKS);
      hydra_options.max_use = MAXTASKS;
    }

    if (hydra_options.colonfile == NULL) {
      if (hydra_options.loginfile != NULL) {
        if ((lfp = fopen(hydra_options.loginfile, "r")) == NULL)
          bail("File for logins not found!");
        hydra_brains.countlogin = countlines(lfp, 0);
        hydra_brains.sizelogin = size_of_data;
        if (hydra_brains.countlogin == 0)
          bail("File for logins is empty!");
        if (hydra_brains.countlogin > MAX_LINES) {
          fprintf(stderr, "[ERROR] Maximum number of logins is %d, this file has %lu entries.\n", MAX_LINES, hydra_brains.countlogin);
          exit(-1);
        }
        if (hydra_brains.sizelogin > MAX_BYTES) {
          fprintf(stderr, "[ERROR] Maximum size of the login file is %d, this file has %lu bytes.\n", MAX_BYTES, (unsigned long int)hydra_brains.sizelogin);
          exit(-1);
        }
        login_ptr = malloc(hydra_brains.sizelogin + hydra_brains.countlogin + 8);
        if (login_ptr == NULL)
          bail("Could not allocate enough memory for login file data");
        memset(login_ptr, 0, hydra_brains.sizelogin + hydra_brains.countlogin + 8);
        fill_mem(login_ptr, lfp, 0);
      } else {
        login_ptr = hydra_options.login;
        hydra_brains.sizelogin = strlen(hydra_options.login) + 1;
        hydra_brains.countlogin = 1;
      }
      if (hydra_options.passfile != NULL) {
        if ((pfp = fopen(hydra_options.passfile, "r")) == NULL)
          bail("File for passwords not found!");
        hydra_brains.countpass = countlines(pfp, 0);
        hydra_brains.sizepass = size_of_data;
        if (hydra_brains.countpass == 0)
          bail("File for passwords is empty!");
        if (hydra_brains.countpass > MAX_LINES) {
          fprintf(stderr, "[ERROR] Maximum number of passwords is %d, this file has %lu entries.\n", MAX_LINES, hydra_brains.countpass);
          exit(-1);
        }
        if (hydra_brains.sizepass > MAX_BYTES) {
          fprintf(stderr, "[ERROR] Maximum size of the password file is %d, this file has %lu bytes.\n", MAX_BYTES, (unsigned long int)hydra_brains.sizepass);
          exit(-1);
        }
        pass_ptr = malloc(hydra_brains.sizepass + hydra_brains.countpass + 8);
        if (pass_ptr == NULL)
          bail("Could not allocate enough memory for password file data");
        memset(pass_ptr, 0, hydra_brains.sizepass + hydra_brains.countpass + 8);
        fill_mem(pass_ptr, pfp, 0);
      } else {
        if (hydra_options.pass != NULL) {
          pass_ptr = hydra_options.pass;
          hydra_brains.countpass = 1;
          hydra_brains.sizepass = strlen(hydra_options.pass) + 1;
        } else {
          if (hydra_options.bfg) {
#ifdef HAVE_MATH_H
            if (bf_init(bf_options.arg))
              exit(-1);         // error description is handled by bf_init
            pass_ptr = bf_next();
            hydra_brains.countpass += bf_get_pcount();
            hydra_brains.sizepass += BF_BUFLEN;
#else
            sleep(1);
#endif
          } else {
            pass_ptr = hydra_options.pass = empty_login;
            hydra_brains.countpass = 0;
            hydra_brains.sizepass = 1;
          }
        }
      }
    } else {
      if ((cfp = fopen(hydra_options.colonfile, "r")) == NULL)
        bail("File with login:password information not found!");
      hydra_brains.countlogin = countlines(cfp, 1);
      hydra_brains.sizelogin = size_of_data;
      if (hydra_brains.countlogin == 0)
        bail("File for login:password information is empty!");
        if (hydra_brains.countlogin > MAX_LINES / 2) {
          fprintf(stderr, "[ERROR] Maximum number of colon file entries is %d, this file has %lu entries.\n", MAX_LINES / 2, hydra_brains.countlogin);
          exit(-1);
        }
        if (hydra_brains.sizelogin > MAX_BYTES / 2) {
          fprintf(stderr, "[ERROR] Maximum size of the colon file is %d, this file has %lu bytes.\n", MAX_BYTES / 2, (unsigned long int)hydra_brains.sizelogin);
          exit(-1);
        }
      csv_ptr = malloc(hydra_brains.sizelogin + 2 * hydra_brains.countlogin + 8);
      if (csv_ptr == NULL)
        bail("Could not allocate enough memory for colon file data");
      memset(csv_ptr, 0, hydra_brains.sizelogin + 2 * hydra_brains.countlogin + 8);
      fill_mem(csv_ptr, cfp, 1);
//printf("count: %d, size: %d\n", hydra_brains.countlogin, hydra_brains.sizelogin);
//hydra_dump_data(csv_ptr, hydra_brains.sizelogin + hydra_brains.countlogin + 8, "colon data");
      hydra_brains.countpass = 1;
      pass_ptr = login_ptr = csv_ptr;
      while (*pass_ptr != 0)
        pass_ptr++;
      pass_ptr++;
    }

    hydra_brains.countpass += hydra_options.try_password_reverse_login + hydra_options.try_password_same_as_login + hydra_options.try_null_password;
    if ((memcheck = malloc(102400)) == NULL) {
      fprintf(stderr, "[ERROR] your wordlist is too large, not enough memory!\n");
      exit(-1);
    }
    free(memcheck);
    if (fopen(RESTOREFILE, "r") != NULL) {
      fprintf(stderr, "[WARNING] Restorefile (%s) from a previous session found, to prevent overwriting, you have 10 seconds to abort...\n", RESTOREFILE);
      sleep(10);
    }

    if (hydra_options.infile_ptr != NULL) {
      if ((ifp = fopen(hydra_options.infile_ptr, "r")) == NULL)
        bail("File for IP addresses not found!");
      hydra_brains.targets = countservers = countinfile = countlines(ifp, 0);
      if (countinfile == 0)
        bail("File for IP addresses is empty!");
      hydra_targets = malloc(sizeof(hydra_targets) * (countservers + 1) + 8);
      if (hydra_targets == NULL)
        bail("Could not allocate enough memory for target data");
      sizeinfile = size_of_data;
      if (countinfile > MAX_LINES / 1000) {
        fprintf(stderr, "[ERROR] Maximum number of target file entries is %d, this file has %d entries.\n", MAX_LINES / 1000, countinfile);
        exit(-1);
      }
      if (sizeinfile > MAX_BYTES / 1000) {
        fprintf(stderr, "[ERROR] Maximum size of the server file is %d, this file has %d bytes.\n", MAX_BYTES / 1000, sizeinfile);
        exit(-1);
      }
      servers_ptr = malloc(sizeinfile + countservers + 8);
      memset(servers_ptr, 0, sizeinfile + countservers + 8);
      if (servers_ptr == NULL)
        bail("Could not allocate enough memory for target file data");
      fill_mem(servers_ptr, ifp, 0);
      sizeservers = sizeinfile;
      tmpptr = servers_ptr;
      for (i = 0; i < countinfile; i++) {
        hydra_targets[i] = malloc(sizeof(hydra_target));
        memset(hydra_targets[i], 0, sizeof(hydra_target));
        hydra_targets[i]->target = tmpptr;
        while (*tmpptr != 0)
          tmpptr++;
        tmpptr++;
      }
    } else {
      countservers = hydra_brains.targets = 1;
      hydra_targets = malloc(sizeof(hydra_targets));
      hydra_targets[0] = malloc(sizeof(hydra_target));
      memset(hydra_targets[0], 0, sizeof(hydra_target));
      hydra_targets[0]->target = servers_ptr = hydra_options.server;
      sizeservers = strlen(hydra_options.server) + 1;
    }
    for (i = 0; i < hydra_brains.targets; i++) {
      hydra_targets[i]->login_ptr = login_ptr;
      hydra_targets[i]->pass_ptr = pass_ptr;
      if (hydra_options.loop_mode) {
        if (hydra_options.try_password_same_as_login)
          hydra_targets[i]->pass_state = 0;
        else
          if (hydra_options.try_null_password) {
            hydra_targets[i]->pass_ptr = empty_login;
            hydra_targets[i]->pass_state = 1;
          } else
            if (hydra_options.try_password_reverse_login)
              hydra_targets[i]->pass_state = 2;
            else
              hydra_targets[i]->pass_state = 3;
      }
    }
  }                             // END OF restore == 0 

  if (getenv("HYDRA_PROXY") && use_proxy == 0) {
    printf("[INFO] Using Connect Proxy: %s\n", getenv("HYDRA_PROXY"));
    use_proxy = 2;
  }
  if (use_proxy == 1)
    proxy_string = getenv("HYDRA_PROXY_HTTP");
  if (use_proxy == 2)
    proxy_string = getenv("HYDRA_PROXY");
  if (proxy_string != NULL && proxy_string[0] != 0) {
    if (strstr(proxy_string, "//") != NULL) {
      char *dslash = strstr(proxy_string, "://");
      if (dslash) {
        proxy_string[dslash-proxy_string] = 0;
        strncpy(proxy_string_type, proxy_string, sizeof(proxy_string_type));        
      }

      proxy_string=dslash;
      proxy_string += 3;
    }
    if (proxy_string[strlen(proxy_string) - 1] == '/')
      proxy_string[strlen(proxy_string) - 1] = 0;
    if ((tmpptr = index(proxy_string, ':')) == NULL)
      use_proxy = 0;
    else {
      *tmpptr = 0;
      tmpptr++;
      memset(&hints, 0, sizeof hints);
      if ((device = index(proxy_string, '%')) != NULL)
        *device++ = 0;
      if (getaddrinfo(proxy_string, NULL, &hints, &res) != 0) {
        fprintf(stderr, "[ERROR] could not resolve proxy address: %s\n", proxy_string);
        exit(-1);
      } else {
        for (p = res; p != NULL; p = p->ai_next) {
#ifdef AF_INET6
          if (p->ai_family == AF_INET6) {
            if (ipv6 == NULL)
              ipv6 = (struct sockaddr_in6 *) p->ai_addr;
          } else
#endif
          if (p->ai_family == AF_INET) {
            if (ipv4 == NULL)
              ipv4 = (struct sockaddr_in *) p->ai_addr;
          }
        }
        freeaddrinfo(res);
#ifdef AF_INET6
        if (ipv6 != NULL && (ipv4 == NULL || prefer_ipv6)) {
          proxy_string_ip[0] = 16;
          memcpy(proxy_string_ip + 1, (char *) &ipv6->sin6_addr, 16);
          if (device != NULL && strlen(device) <= 16)
            strcpy(proxy_string_ip + 17, device);
          if (memcmp(proxy_string_ip + 1, fe80, 2) == 0) {
            if (device == NULL) {
              fprintf(stderr, "[ERROR] The proxy address is a link local address, link local addresses require the interface being defined like this: fe80::1%%eth0\n");
              exit(-1);
            }
          }
        } else
#endif
        if (ipv4 != NULL) {
          proxy_string_ip[0] = 4;
          memcpy(proxy_string_ip + 1, (char *) &ipv4->sin_addr, 4);
        } else {
          fprintf(stderr, "[ERROR] Could not resolve proxy address: %s\n", proxy_string);
          exit(-1);
        }
      }
      proxy_string_port = atoi(tmpptr);
    }
    if (use_proxy == 0)
      fprintf(stderr, "[WARNING] invalid proxy definition. Syntax: \"HYDRA_PROXY=[connect|socks[4|5]]://1.2.3.4:3128/\".\n");
  } else
    use_proxy = 0;
  if (use_proxy > 0 && (tmpptr = getenv("HYDRA_PROXY_AUTH")) != NULL && tmpptr[0] != 0) {
    if (index(tmpptr, ':') == NULL) {
      fprintf(stderr, "[WARNING] invalid proxy authentication. Syntax: \"login:password\". Ignoring ...\n");
    } else {
      proxy_authentication = malloc(strlen(tmpptr) * 2 + 50);
      strcpy(proxy_authentication, tmpptr);
      if (hydra_strcasestr(proxy_string_type, "socks") == NULL)
        hydra_tobase64((unsigned char *) proxy_authentication, strlen(proxy_authentication), strlen(tmpptr) * 2 + 8);
    }
  }

  if (hydra_options.restore == 0) {
    if ((strcmp(hydra_options.service, "rsh") == 0) || (strcmp(hydra_options.service, "oracle-sid") == 0))
      math2 = hydra_brains.countlogin;
    else
      math2 = hydra_brains.countlogin * hydra_brains.countpass;

#ifdef HAVE_MATH_H
    if (hydra_options.bfg) {
      math2 = hydra_brains.countlogin * bf_get_pcount();
    }
#endif

    hydra_brains.todo = math2;
    math2 = math2 * hydra_brains.targets;
    hydra_brains.todo_all = math2;
    if (hydra_brains.todo_all == 0)
      bail("No login/password combination given!");
    if (hydra_brains.todo < hydra_options.tasks) {
      if (verbose && hydra_options.tasks != TASKS)
        printf("[VERBOSE] More tasks defined than login/pass pairs exist. Tasks reduced to %lu\n", hydra_brains.todo);
      hydra_options.tasks = hydra_brains.todo;
    }
  }
  if (hydra_options.max_use < hydra_brains.targets * hydra_options.tasks)
    hydra_options.max_use = hydra_brains.targets * hydra_options.tasks;
  if (hydra_options.max_use > MAXTASKS)
    hydra_options.max_use = MAXTASKS;
  if (hydra_options.max_use < hydra_brains.targets * hydra_options.tasks) {
    if ((hydra_options.tasks = hydra_options.max_use / hydra_brains.targets) == 0)
      hydra_options.tasks = 1;
    fprintf(stderr, "[WARNING] More tasks defined per server than allowed for maximal connections. Tasks reduced to %d.\n", hydra_options.tasks);
  } else {
    if (hydra_options.tasks > MAXTASKS) {
      fprintf(stderr, "[WARNING] reducing tasks to MAXTASKS (%d)\n", MAXTASKS);
      hydra_options.tasks = MAXTASKS;
    }
  }
  hydra_options.max_use = hydra_brains.targets * hydra_options.tasks;
  if (hydra_options.max_use > MAXTASKS)
    hydra_options.max_use = MAXTASKS;
  math2 = hydra_brains.todo_all / hydra_options.tasks;
  // set options (bits!) 
  options = 0;
  if (hydra_options.ssl)
    options = options | OPTION_SSL;
  if (hydra_options.colonfile != NULL)
    printf("[DATA] %d task%s, %d server%s, %lu login tr%s, ~%lu tr%s per task\n", hydra_options.tasks, hydra_options.tasks == 1 ? "" : "s", hydra_brains.targets, hydra_brains.targets == 1 ? "" : "s", hydra_brains.todo, hydra_brains.todo == 1 ? "y" : "ies", math2, math2 == 1 ? "y" : "ies");
  else
    printf("[DATA] %d task%s, %d server%s, %lu login tr%s (l:%lu/p:%lu), ~%lu tr%s per task\n", hydra_options.tasks, hydra_options.tasks == 1 ? "" : "s", hydra_brains.targets, hydra_brains.targets == 1 ? "" : "s", hydra_brains.todo,
           hydra_brains.todo == 1 ? "y" : "ies", (unsigned long int) hydra_brains.countlogin, (unsigned long int) hydra_brains.countpass, math2, math2 == 1 ? "y" : "ies");
  if (port < 1)
    if ((port = hydra_lookup_port(hydra_options.service)) < 1) {
      fprintf(stderr, "[ERROR] No valid port set or no default port available. Use the -s Option\n");
      exit(-1);
    }

  printf("[DATA] attacking service %s on port %d\n", hydra_options.service, port);

  if (hydra_options.outfile_ptr != NULL) {
    if ((hydra_brains.ofp = fopen(hydra_options.outfile_ptr, "a+")) == NULL) {
      perror("[ERROR] Error creating outputfile");
      exit(-1);
    }
    fprintf(hydra_brains.ofp, "# %s %s run at %s on %s %s (%s ", PROGRAM, VERSION, hydra_build_time(),
            hydra_options.server == NULL ? hydra_options.infile_ptr : hydra_options.server, hydra_options.service, prg);
    for (i = 1; i < argc; i++)
      fprintf(hydra_brains.ofp, " %s", argv[i]);
    fprintf(hydra_brains.ofp, ")\n"); 
  }
  
  // we have to flush all writeable buffered file pointers before forking 
  // set appropriate signals for mother 
  signal(SIGCHLD, killed_childs);
  signal(SIGTERM, kill_children);
#ifdef SIGBUS
  signal(SIGBUS, kill_children);
#endif
  if (debug == 0)
    signal(SIGSEGV, kill_children);
  signal(SIGHUP, kill_children);
  signal(SIGINT, kill_children);
  signal(SIGPIPE, SIG_IGN);
  if (verbose)
    printf("[VERBOSE] Resolving addresses ... ");
  if (debug)
    printf("\n");
  for (i = 0; i < countservers; i++) {
    if (debug)
      printf("[DEBUG] resolving %s\n", hydra_targets[i]->target);
    memset(&hints, 0, sizeof(hints));
    ipv4 = NULL;
#ifdef AF_INET6
    ipv6 = NULL;
    if ((device = index(hydra_targets[i]->target, '%')) != NULL)
      *device++ = 0;
#endif
    if (getaddrinfo(hydra_targets[i]->target, NULL, &hints, &res) != 0) {
      if (use_proxy == 0) {
        if (verbose)
          printf("[failed for %s] ", hydra_targets[i]->target);
        else
          fprintf(stderr, "[ERROR] could not resolve address: %s\n", hydra_targets[i]->target);
        hydra_targets[i]->done = 3;
        hydra_brains.finished++;
      }
    } else {
      for (p = res; p != NULL; p = p->ai_next) {
#ifdef AF_INET6
        if (p->ai_family == AF_INET6) {
          if (ipv6 == NULL)
            ipv6 = (struct sockaddr_in6 *) p->ai_addr;
        } else
#endif
        if (p->ai_family == AF_INET) {
          if (ipv4 == NULL)
            ipv4 = (struct sockaddr_in *) p->ai_addr;
        }
      }
#ifdef AF_INET6
      if (ipv6 != NULL && (ipv4 == NULL || prefer_ipv6)) {
        // IPV6 FIXME
        if ((strcmp(hydra_options.service, "socks5") == 0) || (strcmp(hydra_options.service, "sip") == 0)) {
          fprintf(stderr, "[ERROR] Target %s resolves to an IPv6 address, however module %s does not support this. Maybe try \"-4\" option. Sending in patches helps.\n",
                  hydra_targets[i]->target, hydra_options.service);
          hydra_targets[i]->done = 3;
          hydra_brains.finished++;
        } else {
          hydra_targets[i]->ip[0] = 16;
          memcpy(&hydra_targets[i]->ip[1], (char *) &ipv6->sin6_addr, 16);
          if (device != NULL && strlen(device) <= 16)
            strcpy(&hydra_targets[i]->ip[17], device);
          if (memcmp(&hydra_targets[i]->ip[17], fe80, 2) == 0) {
            if (device == NULL) {
              fprintf(stderr, "[ERROR] The target %s address is a link local address, link local addresses require the interface being defined like this: fe80::1%%eth0\n",
                      hydra_targets[i]->target);
              exit(-1);
            }
          }
        }
      } else
#endif
      if (ipv4 != NULL) {
        hydra_targets[i]->ip[0] = 4;
        memcpy(&hydra_targets[i]->ip[1], (char *) &ipv4->sin_addr, 4);
      } else {
        if (verbose)
          printf("[failed for %s] ", hydra_targets[i]->target);
        else
          fprintf(stderr, "[ERROR] Could not resolve proxy address: %s\n", hydra_targets[i]->target);
        hydra_targets[i]->done = 3;
        hydra_brains.finished++;
      }
      freeaddrinfo(res);
    }
  }
  if (verbose)
    printf("done\n");
  if (hydra_brains.targets == 0)
    bail("No server to scan!");

#ifndef SO_BINDTODEVICE
  if (device != NULL) {
    fprintf(stderr, "[ERROR] your operating system does not support SO_BINDTODEVICE or IP_FORCE_OUT_IFP, dunno how to bind the IPv6 address to the interface %s!\n", device);
  }
#endif

  if (hydra_options.restore == 0) {
    hydra_heads = malloc(sizeof(hydra_heads) * hydra_options.max_use);
    target_no = 0;
    for (i = 0; i < hydra_options.max_use; i++) {
      hydra_heads[i] = malloc(sizeof(hydra_head));
      memset(hydra_heads[i], 0, sizeof(hydra_head));
    }
  }

  // here we call the init function of the relevant service module
  // should we do the init centrally or should each child do that?
  // that depends largely on the number of targets and maximum tasks
  if (hydra_brains.targets == 1 || (hydra_brains.targets < 4 && hydra_options.tasks / hydra_brains.targets > 4 && hydra_brains.todo > 15))
    for (i = 0; i < hydra_brains.targets; i++)
      hydra_service_init(i);

  starttime = elapsed_status = elapsed_restore = time(NULL);
  fflush(stdout);
  fflush(stderr);
  fflush(hydra_brains.ofp);

  hydra_debug(0, "attack");
  process_restore = 1;

  // this is the big function which starts the attacking children, feeds login/password pairs, etc.! 
  while (exit_condition == 0) {
    FD_ZERO(&fdreadheads);
    for (head_no = 0, max_fd = 1; head_no < hydra_options.max_use; head_no++) {
      if (hydra_heads[head_no]->active > 0) {
        FD_SET(hydra_heads[head_no]->sp[0], &fdreadheads);
        if (max_fd < hydra_heads[head_no]->sp[0])
          max_fd = hydra_heads[head_no]->sp[0];
      }
    }
    my_select(max_fd + 1, &fdreadheads, NULL, NULL, 0, 200000);
    tmp_time = time(NULL);

    for (head_no = 0; head_no < hydra_options.max_use; head_no++) {
//      if (debug) printf("[DEBUG] head_no[%d] active %d\n", head_no, hydra_heads[head_no]->active);
      switch (hydra_heads[head_no]->active) {
      case -1:
        // disabled head, ignored
        break;
      case 0:
        if (hydra_heads[head_no]->redo) {
          hydra_spawn_head(head_no, hydra_heads[head_no]->target_no);
        } else {
          if (hydra_brains.targets > 1)
            hydra_heads[head_no]->target_no = hydra_select_target();
          if (debug)
            printf("[DEBUG] child %d got target %d selected\n", head_no, hydra_heads[head_no]->target_no);
          if (target_no < 0)
            hydra_kill_head(head_no, 0, 2);
          else
            hydra_spawn_head(head_no, hydra_heads[head_no]->target_no); // target_no is ignored if head->redo == 1
        }
        break;
      case 1:
        if (FD_ISSET(hydra_heads[head_no]->sp[0], &fdreadheads)) {
          readres = read_safe(hydra_heads[head_no]->sp[0], &rc, 1);
          if (readres > 0) {
            FD_CLR(hydra_heads[head_no]->sp[0], &fdreadheads);
            hydra_heads[head_no]->last_seen = tmp_time;
            if (debug)
              printf("[DEBUG] head_no[%d] read %c\n", head_no, rc);
            switch (rc) {
              // Valid Results:
              //  n - mother says to itself that child requests next login/password pair
              //  N - child requests next login/password pair
              //  Q - child reports that it is quitting
              //  C - child reports connect error (and is quitting)
              //  E - child reports protocol error (and is quitting)
              //  f - child reports that the username does not exist
              //  F - child reports that it found a valid login/password pair
              //        and requests next pair. Sends login/pw pair with next msg!
            case 'N':          // head wants next pair
              hydra_targets[hydra_heads[head_no]->target_no]->ok = 1;
              if (hydra_targets[hydra_heads[head_no]->target_no]->fail_count > 0)
                hydra_targets[hydra_heads[head_no]->target_no]->fail_count--;
              // no break here 
            case 'n':          // mother sends this to itself initially
              if (hydra_send_next_pair(hydra_heads[head_no]->target_no, head_no) == -1) {
                hydra_kill_head(head_no, 1, 2);
              }
              break;

            case 'F':          // valid password found
              hydra_brains.found++;
              if (hydra_options.exit_found) {   // option set says quit target after on valid login/pass pair is found 
                for (j = 0; j < hydra_options.max_use; j++)
                  if (hydra_heads[j]->active >= 0 && (hydra_heads[j]->target_no == target_no || hydra_options.exit_found == 2))
                    hydra_kill_head(j, 1, 2);   // kill all heads working on the target 
                if (hydra_targets[hydra_heads[head_no]->target_no]->done == 0) {
                  hydra_targets[hydra_heads[head_no]->target_no]->done = 1;   // mark target as done 
                  hydra_brains.finished++;
                  printf("[STATUS] attack finished for %s (valid pair found)\n", hydra_targets[hydra_heads[head_no]->target_no]->target);
                }
                if (hydra_options.exit_found == 2) {
                  for (j = 0; j < hydra_brains.targets; j++)
                    if (hydra_targets[j]->done == 0) {
                      hydra_targets[j]->done = 1;
                      hydra_brains.finished++;
                    }
                }
                continue;
              }
              // fall through
            case 'f':          // username identified as invalid
              hydra_targets[hydra_heads[head_no]->target_no]->ok = 1;
              if (hydra_targets[hydra_heads[head_no]->target_no]->fail_count > 0)
                hydra_targets[hydra_heads[head_no]->target_no]->fail_count--;
              memset(buf, 0, sizeof(buf));
              read_safe(hydra_heads[head_no]->sp[0], buf, MAXBUF);
              hydra_skip_user(hydra_heads[head_no]->target_no, buf);
              fck = write(hydra_heads[head_no]->sp[1], "n", 1); // small hack
              break;

              // we do not make a difference between 'C' and 'E' results - yet 
            case 'E':          // head reports protocol error
            case 'C':          // head reports connect error
              fck = write(hydra_heads[head_no]->sp[0], "Q", 1);
              if (debug) {
                printf("[ATTEMPT-ERROR] target %s - login \"%s\" - pass \"%s\" - child %d - %lu of %lu\n",
                       hydra_targets[hydra_heads[head_no]->target_no]->target, hydra_heads[head_no]->current_login_ptr, hydra_heads[head_no]->current_pass_ptr, head_no,
                       hydra_targets[hydra_heads[head_no]->target_no]->sent, hydra_brains.todo);
              }
              hydra_increase_fail_count(hydra_heads[head_no]->target_no, head_no);
              break;

            case 'Q':          // head reports its quitting
              fck = write(hydra_heads[head_no]->sp[0], "Q", 1);
              if (debug)
                printf("[DEBUG] child %d reported it quit\n", head_no);
              hydra_kill_head(head_no, 1, 0);
              break;

            default:
              fprintf(stderr, "[ERROR] child %d sent nonsense data, killing and restarting it!\n", head_no);
              hydra_increase_fail_count(hydra_heads[head_no]->target_no, head_no);
            }
          }
          if (readres == -1) {
            if (verbose)
              fprintf(stderr, "[WARNING] child %d seems to have died, restarting (this only happens if a module is bad) ... \n", head_no);
            hydra_increase_fail_count(hydra_heads[head_no]->target_no, head_no);
          }
        } else {
          if (hydra_heads[head_no]->last_seen + hydra_options.waittime > tmp_time) {
            // check if recover of timed-out head is necessary
            if (tmp_time > waittime + hydra_heads[head_no]->last_seen) {
              if (kill(hydra_heads[head_no]->pid, 0) < 0) {
                if (verbose)
                  fprintf(stderr, "[WARNING] child %d seems to be dead, restarting it ...\n", head_no);
                hydra_increase_fail_count(hydra_heads[head_no]->target_no, head_no);
              }
            }
            // if we do not get to hear anything for a longer time assume its dead
            if (tmp_time > waittime * 2 + hydra_heads[head_no]->last_seen) {
              if (verbose)
                fprintf(stderr, "[WARNING] timeout from child %d, restarting\n", head_no);
              hydra_increase_fail_count(hydra_heads[head_no]->target_no, head_no);
            }
          }
        }
        break;
      default:
        fprintf(stderr, "[ERROR] child %d in unknown state, restarting!\n", head_no);
        hydra_increase_fail_count(hydra_heads[head_no]->target_no, head_no);
      }
    }

    usleep(USLEEP_LOOP);
    (void) wait3(NULL, WNOHANG, NULL);
    // write restore file and report status 
    if (process_restore == 1 && time(NULL) - elapsed_restore > 299) {
      hydra_restore_write(0);
      elapsed_restore = time(NULL);
    }

    if (time(NULL) - elapsed_status > status_print) {
      elapsed_status = time(NULL);
      tmp_time = elapsed_status - starttime;
      if (tmp_time < 1)
        tmp_time = 1;
      tmp_time = hydra_brains.sent / tmp_time;
      if (tmp_time < 1)
        tmp_time = 1;
      if (status_print < 15 * 59)
        status_print = ((status_print + 1) * 2) - 1;
      if (status_print > 299 && (hydra_brains.todo_all - hydra_brains.sent) / tmp_time < 1500)
        status_print = 299;
      if (((hydra_brains.todo_all - hydra_brains.sent) / tmp_time) < 150)
        status_print = 59;
      k = 0;
      for (j = 0; j < hydra_options.max_use; j++)
        if (hydra_heads[j]->active >= 0)
          k++;
      printf("[STATUS] %.2f tries/min, %lu tries in %02lu:%02luh, %lu todo in %02lu:%02luh, %d active\n", (1.0 * hydra_brains.sent) / (((elapsed_status - starttime) * 1.0) / 60), // tries/min 
             hydra_brains.sent, // tries 
             (long unsigned int) ((elapsed_status - starttime) / 3600), // hours 
             (long unsigned int) (((elapsed_status - starttime) % 3600) / 60),  // minutes 
             hydra_brains.todo_all - hydra_brains.sent < 0 ? 1 : hydra_brains.todo_all - hydra_brains.sent, // left todo 
             (long unsigned int) (((double) hydra_brains.todo_all - hydra_brains.sent) / ((double) hydra_brains.sent / (elapsed_status - starttime))
             ) / 3600,          // hours 
             (((long unsigned int) (((double) hydra_brains.todo_all - hydra_brains.sent) / ((double) hydra_brains.sent / (elapsed_status - starttime))
               ) % 3600) / 60) + 1,      // min 
             k
        );
    }

    exit_condition = hydra_check_for_exit_condition();
  }
  process_restore = 0;
  if (debug)
    printf("[DEBUG] while loop left with %d\n", exit_condition);

  j = k = error = 0;
  for (i = 0; i < hydra_brains.targets; i++)
    switch (hydra_targets[i]->done) {
    case 3:
      k++;
      break;
    case 2:
      if (hydra_targets[i]->ok == 0)
        k++;
      else
        error++;
      break;
    case 1:
      break;
    case 0:
        if (hydra_targets[i]->ok == 0)
          k++;
        else
          j++;
      break;
    default:
      error++;
      fprintf(stderr, "[ERROR] illegal target result value (%d=>%d)\n", i, hydra_targets[i]->done);
    }

  for (i = 0; i < hydra_options.max_use; i++)
    if (hydra_heads[i]->active > 0 && hydra_heads[i]->pid > 0)
      hydra_kill_head(i, 1, 2);
  (void) wait3(NULL, WNOHANG, NULL);

  printf("%d of %d target%s%scompleted, %lu valid password%s found\n", hydra_brains.targets - j - k - error, hydra_brains.targets, hydra_brains.targets == 1 ? " " : "s ", hydra_brains.found > 0 ? "successfully " : "", hydra_brains.found, hydra_brains.found == 1 ? "" : "s");
  if (error == 0 && j == 0) {
    process_restore = 0;
    unlink(RESTOREFILE);
  } else {
    printf("[INFO] Writing restore file because %d server scan%s could not be completed\n", j + error, j + error == 1 ? "" : "s");
    hydra_restore_write(1);
  }
  if (error) {
    fprintf(stderr, "[ERROR] %d target%s disabled because of too many errors\n", error, error == 1 ? " was" : "s were");
    error = 1;
  }
  if (k) {
    fprintf(stderr, "[ERROR] %d target%s did not resolve or could not be connected\n", k, k == 1 ? "" : "s");
    error = 1;
  }
  if (j) {
    fprintf(stderr, "[ERROR] %d target%s did not complete\n", j, j == 1 ? "" : "s");
    error = 1;
  }

  // yeah we did it 
  printf("%s (%s) finished at %s\n", PROGRAM, RESOURCE, hydra_build_time());
  if (hydra_brains.ofp != NULL && hydra_brains.ofp != stdout)
    fclose(hydra_brains.ofp);

  fflush(NULL);
  if (error || j || exit_condition < 0)
    return -1;
  else
    return 0;
}
