/* 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 */ #include "config.h" #include "half_duplex.h" #include "opt.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_FILIO_H #include #endif half_duplex_handler::half_duplex_handler(event_queue& q, int fd, bool first_read) : fd_handler (q, fd, first_read ? watch_read : watch_write), state(0), receive_goal(0) { } half_duplex_handler::~half_duplex_handler() { int old_fd = fd(); if (old_fd >= 0) { unwatch(); close(old_fd); } } bool half_duplex_handler::on_timeout(ticks_t) { error(ETIMEDOUT); return false; } bool half_duplex_handler::on_activity(activity act) { switch (act) { case activity_read: return on_activity_read(); case activity_write: return on_activity_write(); case activity_error: error(get_error()); return false; case activity_read_write: fprintf(stderr, "%s: half-duplex connection on %d is ready for " "reading and writing\n", opt_program, fd()); exit(EXIT_FAILURE); }; abort(); } bool half_duplex_handler::on_activity_read() { unsigned current_size = receive_buffer.size(); unsigned to_read; // Determine the number of bytes to read. if (receive_goal > 0) { to_read = receive_goal - current_size; } else { int pending; int result = ioctl(fd(), FIONREAD, &pending); if (result == -1) { error(errno); return false; } to_read = pending; } // Grow the receive buffer as necessary and fill it with available // data. receive_buffer.resize(current_size + to_read); int result = read(fd(), &receive_buffer[current_size], to_read); // Now process the data. if (result > 0) { if (static_cast(result) == to_read) { // We got all the data we wanted. return invoke_ready(); } else { // We received less data than expected. We have to shrink the // string. receive_buffer.resize(current_size + result); if (!receive_goal) { // There was less data than exepcted, but it's still enough if // we weren't given an exact number of bytes. return invoke_ready(); } else { // We have to wait for more data. return true; } } } else if (result == 0) { // Peer closed the connection. error(0); return false; } else { // result == -1 // We encountered an error. int err = errno; switch (err) { case EWOULDBLOCK: case EINTR: // Non-permanent errors, ignore them. return true; default: error(err); return false; } } abort(); } bool half_duplex_handler::on_activity_write() { unsigned to_write = send_buffer.size() - send_offset; int result = write(fd(), &send_buffer[send_offset], to_write); if (result > 0) { send_offset += result; if (static_cast(result) == to_write) { // The send operation is complete. return invoke_ready(); } else { // There still is more data to send. return true; } } else if (result == 0) { // Peer closed the connection. error(0); return false; } else { // We encountered an error. int err = errno; switch (err) { case EWOULDBLOCK: case EINTR: // Non-permanent errors, ignore them. return true; default: error(err); return false; } } abort(); } void half_duplex_handler::request_data(unsigned count) { receive_buffer.clear(); receive_goal = count; watch(watch_read); stop = false; } int half_duplex_handler::get_error() { int error = 0; socklen_t len = sizeof(error); errno = 0; if (getsockopt(fd(), SOL_SOCKET, SO_ERROR, &error, &len) >= 0) { // Non-Solaris case: error is overwritten with the error code, // Nothing to do. } else { // Solaris case: errno has the error code. error = errno; } return error; } // arch-tag: 4b315ce8-4614-4c9d-bf96-377b8f74fd86