/* doscan - Denial Of Service Capable Auditing of Networks       -*- C++ -*-
 * Copyright (C) 2003 Florian Weimer
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef SCAN_TCP_H
#define SCAN_TCP_H

#include "event_queue.h"
#include "ipv4.h"

#include <string>

class tcp_client_handler : public event_queue::fd_handler {
  tcp_client_handler();

  ipv4_t the_host;
  unsigned short the_port;

public:
  // Opens a new TCP connection to the specified host/port.
  // on_activity() is invoked as soon as the connection has been
  // established, with state == 0.  on_activity() should check for
  // errors (using get_error()) and invoke watch() to react upon the
  // correct descriptor activity.

  tcp_client_handler(event_queue&, ipv4_t host, unsigned short port);
  virtual ~tcp_client_handler();

  // Returns information about the connection.

  ipv4_t host();
  unsigned short port();

  // Creates non-blocking socket and invoke connect().  Returns -1 on
  // error.

  static int make_connection(ipv4_t host, unsigned short port);

  // Returns the pending error code for fd, or 0 if no error.  Might
  // change errno.  It's recommended to invoke this routine
  // unconditionally after connect() because error signaling is not
  // portable (e.g. Solaris does not set POLLERR).

  static int get_error(int fd);

  // The same, but works on the fd() descriptor.

  int get_error();
};

class tcp_half_duplex_handler : public tcp_client_handler {
  tcp_half_duplex_handler();

  int next_state;
  bool next_is_read, stop;
  std::string data;
  unsigned offset;

  // Override routines inherited from fd_handler.

  virtual bool on_timeout(ticks_t);
  virtual bool on_activity(activity);

  bool on_activity_read(activity);
  bool on_activity_write(activity);

public:

  // Open a half-duplex TCP connection to host:port.  When called for
  // the first time (with state == 0), ready() should check for
  // pending connect() errors using get_error().
  //
  // If the connect attempt fails, ready() or send_ready() are never
  // called.

  tcp_half_duplex_handler(event_queue&, ipv4_t host, unsigned short port);

  // ready() is called when the requested amount of data has been
  // received, or a send operation has completed and some data has
  // been received.
  //
  //  An implementation in a derived class should call request_data(),
  //  request_more_data() // or send().  If it does not, the
  //  connection is closed.

  virtual void ready(int state, int error, const std::string& data) = 0;

  // request_data() should be called by ready() to request more data.
  // If count bytes are received, ready() is invoked again, with the
  // given state.  (If count is zero, all the data in the TCP socket
  // buffer is read.)

  void request_data(int new_state, unsigned count = 0);

  // request_more_data() is like request_data(), only the data is
  // appended to the existing buffer.

  void request_more_data(int new_state, unsigned count = 0);

  // send() should be called by ready() to send the specified data.
  // After completion, ready() is invoked with the new state.

  void send(int new_state, const std::string& data);

  // Closes the current connection and tries to establish a new one.

  void reconnect(int new_state, bool first_read);
};

// tcp_handler

inline ipv4_t
tcp_client_handler::host()
{
  return the_host;
}

inline unsigned short
tcp_client_handler::port()
{
  return the_port;
}

inline int
tcp_client_handler::get_error()
{
  return get_error(fd());
}

#endif // SCAN_TCP_H
