/*
       IDA Pro remote debugger server
*/

#include <pro.h>
#include <fpro.h>
#include <signal.h>

static bool verbose = false;

#ifdef __NT__
#ifdef __AMD64__
#define SYSTEM "Windows64"
#else
#define SYSTEM "Windows32"
#endif
#define DEBUGGER_ID    DEBUGGER_ID_X86_IA32_WIN32_USER
#define socklen_t int
#include <winsock2.h>
#endif

#ifdef __LINUX__
#define SYSTEM "Linux"
#define DEBUGGER_ID    DEBUGGER_ID_X86_IA32_LINUX_USER
#ifdef LIBWRAP
extern "C" const char *check_connection(int);
#endif // LIBWRAP
#endif // __LINUX__

//--------------------------------------------------------------------------
static FILE *channels[16] = { 0 };

static void close_all_channels(void)
{
  for ( int i=0; i < qnumber(channels); i++ )
    if ( channels[i] != NULL )
      qfclose(channels[i]);
  memset(channels, 0, sizeof(channels));
}

static int find_free_channel(void)
{
  for ( int i=0; i < qnumber(channels); i++ )
    if ( channels[i] == NULL )
      return i;
  return -1;
}

//--------------------------------------------------------------------------
#define REMOTE_DEBUGGER
#define DEBUGGER_SERVER
#include "idarpc.cpp"

static SOCKET listen_socket = -1;
//--------------------------------------------------------------------------
void neterr(const char *module)
{
  int code = get_network_error();
  fprintf(stderr, "%s: %s\n", module, winerr(code));
  exit(1);
}

//--------------------------------------------------------------------------
static void NT_CDECL shutdown_gracefully(int signum)
{
  fprintf(stderr, "got signal #%d\n", signum);
  if ( listen_socket != -1 ) closesocket(listen_socket);
  if ( rpc_socket != -1 ) closesocket(rpc_socket);
  _exit(1);
}

//--------------------------------------------------------------------------
// debugger remote server
int main(int argc, char *argv[])
{
  int port_number = DEBUGGER_PORT_NUMBER;
  const char *password = NULL;
  printf("IDA " SYSTEM " remote debug server. Version 1.%d. Copyright Datarescue 2004-2005\n", IDD_INTERFACE_VERSION);
  while ( argc > 1 && (argv[1][0] == '-' || argv[1][0] == '/'))
  {
    switch ( argv[1][1] )
    {
      case 'p':
        port_number = atoi(&argv[1][2]);
        break;
      case 'P':
        password = argv[1] + 2;
        break;
      case 'v':
        verbose = true;
        break;
      default:
        error("usage: ida_remote [switches]\n"
              "  -p...  port number\n"
              "  -P...  password\n"
              "  -v     verbose\n");
    }
    argv++;
    argc--;
  }
  signal(SIGINT, shutdown_gracefully);
  signal(SIGSEGV, shutdown_gracefully);
//  signal(SIGPIPE, SIG_IGN);
  init_sockets();
  listen_socket = socket(AF_INET, SOCK_STREAM, 0);
  if ( listen_socket == -1 )
    neterr("socket");

  setup_socket(listen_socket);

  struct sockaddr_in sa;
  memset(&sa, 0, sizeof(sa));
  sa.sin_family = AF_INET;
  sa.sin_port   = htons(short(port_number));
  if ( bind(listen_socket, (sockaddr *)&sa, sizeof(sa)) == SOCKET_ERROR )
    neterr("bind");
  if ( listen(listen_socket, SOMAXCONN) == SOCKET_ERROR )
    neterr("listen");
  printf("Listening to port #%u...\n", port_number);

  poll_debug_events = false;
  has_pending_event = false;

  while ( true )
  {
    sockaddr_in sa;
    socklen_t salen = sizeof(sa);
    rpc_socket = accept(listen_socket, (sockaddr *)&sa, &salen);
    if ( rpc_socket == -1 )
      neterr("accept");
#if defined(__LINUX__) && defined(LIBWRAP)
    const char *p;
    if((p = check_connection(rpc_socket)) != NULL) {
      fprintf(stderr,
              "ida-server CONNECTION REFUSED from %s (tcp_wrappers)\n", p);
      shutdown(rpc_socket, 2);
      close(rpc_socket);
      continue;
    }
#endif // defined(__LINUX__) && defined(LIBWRAP)

    printf("=========================================================\n"
           "Accepting incoming connection...\n");

    string open = prepare_rpc_packet(RPC_OPEN);
    append_long(open, IDD_INTERFACE_VERSION);
    append_long(open, DEBUGGER_ID);
    append_long(open, sizeof(ea_t));
    rpc_packet_t *rp = process_request(open, true);
    const uchar *answer = (uchar *)(rp+1);
    const uchar *end = answer + rp->length;
    bool send_response = true;
    bool ok = extract_long(&answer, end);
    if ( !ok )
    {
      printf("Incompatible IDA Pro version\n");
      send_response = false;
    }
    else if ( password != NULL )
    {
      char *pass = extract_str(&answer, end);
      if ( strcmp(pass, password) != '\0' )
      {
        printf("Bad password\n");
        ok = false;
      }
    }
    qfree(rp);

    if ( send_response )
    {
      open = prepare_rpc_packet(RPC_OK);
      append_long(open, ok);
      send_request(open);

      if ( ok )
      {
        string cmd;
        qfree(process_request(cmd, true));
      }
    }

    printf("Closing incoming connection...\n");
    closesocket(rpc_socket);
    close_all_channels();
    rpc_socket = -1;
    network_error_code = 0;
  }
}

