/* * Copyright (c) 2002, 2003, 2004 Niels Provos * All rights reserved. * * 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 * 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 #include #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef timeout_pending #undef timeout_initialized #include #include "honeyd.h" #include "tcp.h" #include "log.h" #include "hooks.h" #include "util.h" struct callback cb_tcp = { cmd_tcp_read, cmd_tcp_write, cmd_tcp_eread, cmd_tcp_connect_cb }; void tcp_increase_buf(u_char **pbuf, u_int *psize, u_int maxsize) { u_int size = *psize; u_char *p; if (size >= maxsize) return; size *= 2; if (size >= maxsize) size = maxsize; p = realloc(*pbuf, size); if (p == NULL) return; *psize = size; *pbuf = p; } void tcp_drain_payload(struct tcp_con *con, u_int len) { if (len == 0 || con->payload == NULL) return; if (len >= con->plen) { con->plen = 0; con->poff = 0; goto out; } memmove(con->payload, con->payload + len, con->plen - len); con->plen -= len; con->poff -= len; out: cmd_trigger_read(&con->cmd, con->psize - con->plen); } int tcp_add_readbuf(struct tcp_con *con, u_char *dat, u_int datlen) { int space; hooks_dispatch(IP_PROTO_TCP, HD_INCOMING_STREAM, &con->conhdr, dat, datlen); if (con->cmd_pfd == -1) return (datlen); space = con->rsize - con->rlen; if (space < datlen) { tcp_increase_buf(&con->readbuf, &con->rsize, TCP_MAX_SIZE); space = con->rsize - con->rlen; if (space < datlen) datlen = space; } memcpy(con->readbuf + con->rlen, dat, datlen); con->rlen += datlen; cmd_trigger_write(&con->cmd, con->rlen); return (datlen); } void cmd_tcp_eread(int fd, short which, void *arg) { extern FILE *honeyd_servicefp; struct tcp_con *con = arg; char line[1024]; int nread; struct command *cmd = &con->cmd; TRACE(fd, nread = read(fd, line, sizeof(line))); if (nread <= 0) { if (cmd->fdwantclose) { /* Stdin is already closed */ cmd_free(&con->cmd); tcp_sendfin(con); } else { /* Now stdin will takes us down */ cmd->fdwantclose = 1; } return; } if (nread == sizeof(line)) nread--; line[nread] = '\0'; honeyd_log_service(honeyd_servicefp, IP_PROTO_TCP, &con->conhdr, line); TRACE(cmd->peread.ev_fd, event_add(&cmd->peread, NULL)); } void cmd_tcp_read(int fd, short which, void *arg) { struct tcp_con *con = arg; int len, space; struct command *cmd = &con->cmd; space = con->psize - con->plen; if (space <= 0) return; TRACE(fd, len = read(fd, con->payload + con->plen, space)); if (len == -1) { if (errno == EINTR || errno == EAGAIN) goto again; cmd_free(&con->cmd); tcp_sendfin(con); return; } else if (len == 0) { if (cmd->perrfd != -1 && !cmd->fdwantclose) { cmd->fdwantclose = 1; return; } cmd_free(&con->cmd); tcp_sendfin(con); return; } con->plen += len; if (con->plen == con->psize) tcp_increase_buf(&con->payload, &con->psize, TCP_MAX_SIZE); /* XXX - Trigger write */ tcp_senddata(con, TH_ACK); again: cmd_trigger_read(&con->cmd, con->psize - con->plen); } void cmd_tcp_write(int fd, short which, void *arg) { struct tcp_con *con = arg; int len; TRACE(fd, len = write(fd, con->readbuf, con->rlen)); if (len == -1) { if (errno == EINTR || errno == EAGAIN) goto again; cmd_free(&con->cmd); return; } else if (len == 0) { cmd_free(&con->cmd); return; } memmove(con->readbuf, con->readbuf + len, con->rlen - len); con->rlen -= len; /* Shut down the connection if we received a FIN and sent all data */ if (con->rlen == 0 && con->cmd.fdgotfin) TRACE(con->cmd_pfd, shutdown(con->cmd_pfd, SHUT_WR)); again: cmd_trigger_write(&con->cmd, con->rlen); } void cmd_tcp_connect_cb(int fd, short which, void *arg) { struct tcp_con *con = arg; int error = 0; socklen_t errsz = sizeof(error); /* Everything is ready */ cmd_ready_fd(&con->cmd, &cb_tcp, con); if (which == EV_TIMEOUT) goto out; /* Check if the connection completed */ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &errsz) == -1) { warn("%s: getsockopt for %d", __FUNCTION__, fd); goto out; } if (error) { warnx("%s: getsockopt: %s", __FUNCTION__, strerror(error)); goto out; } cmd_trigger_read(&con->cmd, con->psize - con->plen); cmd_trigger_write(&con->cmd, con->rlen); return; out: /* Connection failed, bring this down gracefully */ cmd_free(&con->cmd); tcp_sendfin(con); }