Commit e6982f80 authored by Seblu's avatar Seblu
Browse files

fix race cond on connection

del sigpipe from connection
fix memleak from server
parent f0770771
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -80,6 +80,6 @@ Client::~Client() {}

static Client *login_fail(Connection &c) {
  std::cout << "CId " << c.getid() << ": Bad authentification.\n";
  c.sendln("Bad authentification.");
  c.sendln("KO");
  return 0;
}
+4 −1
Original line number Diff line number Diff line
@@ -179,7 +179,8 @@ void *Server::start_client(void *voidconn) {
  }

  // stop connexion
  conn->disconnect();
  try { conn->disconnect(); }
  catch (...) {}

  // Print closing connection
  std::cout << "CId " << conn->getid() << ": Closed.\n";
@@ -189,5 +190,7 @@ void *Server::start_client(void *voidconn) {
  S.threads_.erase(t);
  pthread_mutex_unlock(&S.threads_mutex_);

  delete conn;

  return 0;
}
+167 −176
Original line number Diff line number Diff line
@@ -51,7 +51,13 @@ Connection::Connection(int fd) : socket_fd_(fd), local_port_(-1), remote_port_(-

  if (socket_fd_ >= 0) {
    id_ = getconnid();
    setallinfo_();
    try {
      setlocalip_();
      setlocalport_();
      setremoteip_();
      setremoteport_();
    }
    catch (...) { }
  }
}

@@ -64,17 +70,50 @@ Connection::~Connection() {
  pthread_mutex_destroy(&w_mutex_);
}

/**
 * Disconnect socket
 */
void Connection::disconnect() {
  // lock connection mutex
  pthread_mutex_lock(&c_mutex_);

  if (socket_fd_ < 0) {
    pthread_mutex_unlock(&c_mutex_);
    throw Error(ERR_NET, "No connection established but trying to disconnect");
  }

  // lock all mutex
  pthread_mutex_lock(&r_mutex_);
  pthread_mutex_lock(&w_mutex_);

  disconnect_();

  pthread_mutex_unlock(&w_mutex_);
  pthread_mutex_unlock(&r_mutex_);
  pthread_mutex_unlock(&c_mutex_);
}

/**
 * Create connection with @param addr on port @param port
 */
void Connection::connect(const char *addr, int port) {
 if (socket_fd_ >= 0)
    throw Error(ERR_NET, "Connection already established but trying to connect");
  // check args
  if (addr == 0 || *addr == 0)
    throw Error(ERR_NET, "Trying to connect on a bad address");

 if (port <= 0)
    throw Error(ERR_NET, "Trying to Connect on a bad port number");
  if (port <= 0 || port >= 65536)
    throw Error(ERR_NET, "Trying to connect on a bad port number");

  // take connection mutex
  pthread_mutex_lock(&c_mutex_);

  // check already existant connection
  if (socket_fd_ >= 0) {
    pthread_mutex_unlock(&c_mutex_);
    throw Error(ERR_NET, "Connection already established but trying to connect");
  }

  // take read and write mutex
  pthread_mutex_lock(&r_mutex_);
  pthread_mutex_lock(&w_mutex_);

@@ -113,7 +152,18 @@ void Connection::connect(const char *addr, int port) {
  }

  // set infos
  setallinfo_();
  try {
    setlocalip_();
    setlocalport_();
    setremoteip_();
    setremoteport_();
  }
  catch (...) {
    pthread_mutex_unlock(&w_mutex_);
    pthread_mutex_unlock(&r_mutex_);
    pthread_mutex_unlock(&c_mutex_);
    throw;
  }

  pthread_mutex_unlock(&w_mutex_);
  pthread_mutex_unlock(&r_mutex_);
@@ -124,11 +174,21 @@ void Connection::connect(const char *addr, int port) {
 * Listen on @param port with queue size of max @param max
 */
void Connection::listen(int port, int max) {
  if (socket_fd_ >= 0)
    throw Error(ERR_NET, "Connection already established but trying to listen");
  // check arg
  if (port <= 0 || port >= 65536)
    throw Error(ERR_NET, "Trying to listen on a bad port number");
  if (max <= 0)
    throw Error(ERR_NET, "Trying to listen with bad wait queue size");

  // lock
  // take connection mutex
  pthread_mutex_lock(&c_mutex_);

  if (socket_fd_ >= 0) {
    pthread_mutex_unlock(&c_mutex_);
    throw Error(ERR_NET, "Connection already established but trying to listen");
  }

  // lock read and write mutex
  pthread_mutex_lock(&r_mutex_);
  pthread_mutex_lock(&w_mutex_);

@@ -168,67 +228,46 @@ void Connection::listen(int port, int max) {
  }

  // set all infos
  setallinfo_();

  try {
    setlocalip_();
    setlocalport_();
  }
  catch (...) {
    pthread_mutex_unlock(&w_mutex_);
    pthread_mutex_unlock(&r_mutex_);
    pthread_mutex_unlock(&c_mutex_);
    throw;
  }

/**
 * Disconnect socket
 */
void Connection::disconnect() {
  if (socket_fd_ < 0)
    throw Error(ERR_NET, "No connection established but trying to disconnect");

  // lock all mutex
  pthread_mutex_lock(&c_mutex_);
  pthread_mutex_lock(&r_mutex_);
  pthread_mutex_lock(&w_mutex_);

  disconnect_();

  pthread_mutex_unlock(&w_mutex_);
  pthread_mutex_unlock(&r_mutex_);
  pthread_mutex_unlock(&c_mutex_);
}

/**
 * Return the connection sate
 * Cannot be const, du to mutex locking
 *
 * @return see description
 */
bool Connection::connected() {
  // get connection mutex

  pthread_mutex_lock(&c_mutex_);

  bool ret = (socket_fd_ >= 0);

  pthread_mutex_unlock(&c_mutex_);

  return ret;
}

/**
 * Accept new connection on a listening socket
 *
 * @return null on error, else a new connection
 */
Connection *Connection::accept() {
  if (socket_fd_ < 0)
  // keep the socket fd
  pthread_mutex_lock(&c_mutex_);
  int socket_fd = socket_fd_;
  pthread_mutex_unlock(&c_mutex_);

  // test socket validity
  if (socket_fd < 0)
    throw Error(ERR_NET, "No connection established but trying to accept");

  struct sockaddr_in r_addr;
  socklen_t r_sin_size = sizeof r_addr;
  int r_fd;

  // accept is a read/write operation
  pthread_mutex_lock(&r_mutex_);
  pthread_mutex_lock(&w_mutex_);

  if ((r_fd = ::accept(socket_fd_, (struct sockaddr *) &r_addr, &r_sin_size)) == -1) {
  if ((r_fd = ::accept(socket_fd, (struct sockaddr *) &r_addr, &r_sin_size)) == -1) {
    pthread_mutex_unlock(&c_mutex_);
    throw Error(ERR_NET, (string) "accept: " + strerror(errno));
  }
@@ -243,51 +282,59 @@ Connection *Connection::accept() {
 * Send data on @param buf of size @param len on socket
 */
void Connection::send(const char* buf, size_t len) {
  if (socket_fd_ < 0)
    throw Error(ERR_NET, "No connection established but trying to send data");

  // lock mutex for operation
  pthread_mutex_lock(&c_mutex_);
  pthread_mutex_lock(&w_mutex_);
  int ret = ::send(socket_fd_, buf, len, 0);
  pthread_mutex_unlock(&w_mutex_);

  if (ret == -1 && errno == ECONNRESET)
    throw Error(ERR_NET, "Connection reset by peer");
  if (ret == -1)
    throw Error(ERR_NET, "send: " + (string) strerror(errno));
  // retreive socket_fd and free conn mutex
  int socket_fd = socket_fd_;
  pthread_mutex_unlock(&c_mutex_);

  // test socket validity
  if (socket_fd < 0) {
    pthread_mutex_unlock(&w_mutex_);
    throw Error(ERR_NET, "No connection established but trying to send data");
  }

/**
 * Send data on @param buf of size @param len followed by '\n' on socket
 */
void Connection::sendln(const char* buf, size_t len) {
  if (socket_fd_ < 0)
    throw Error(ERR_NET, "No connection established but trying to send line");
  // send is a write operation
  int ret = ::send(socket_fd_, buf, len, MSG_NOSIGNAL);

  pthread_mutex_lock(&w_mutex_);
  // write data
  int ret = ::send(socket_fd_, buf, len, 0);
  // treat error
  if (ret == -1) {
    pthread_mutex_unlock(&w_mutex_);
    if (errno == ECONNRESET)
    int errno__ = errno;
    pthread_mutex_lock(&c_mutex_);
    pthread_mutex_lock(&r_mutex_);
    disconnect_();
    pthread_mutex_unlock(&r_mutex_);
    pthread_mutex_unlock(&c_mutex_);
    pthread_mutex_unlock(&r_mutex_);
    if (errno__ == ECONNRESET)// || errno__ == EPIPE)
      throw Error(ERR_NET, "Connection reset by peer");
    throw Error(ERR_NET, "sendln: " + (string) strerror(errno));
    else
      throw Error(ERR_NET, "send: " + (string) strerror(errno__));
  }

  // write '\n'
  ret = ::send(socket_fd_, "\n", 1, 0);
  // release the mutex
  pthread_mutex_unlock(&w_mutex_);
  if (ret == -1)
    throw Error(ERR_NET, "sendln: " + (string) strerror(errno));
}

/**
 * Receive a line
 */
string Connection::recvln() {
  if (socket_fd_ < 0)
  // lock mutex for operation
  pthread_mutex_lock(&c_mutex_);
  pthread_mutex_lock(&r_mutex_);

  int socket_fd = socket_fd_;
  pthread_mutex_unlock(&c_mutex_);

  // test socket validity
  if (socket_fd < 0) {
    pthread_mutex_unlock(&r_mutex_);
    throw Error(ERR_NET, "No connection established but trying to receive line");
  }

  pthread_mutex_lock(&r_mutex_);
  do {
    // check EOL char
    size_t offset = rbuf_.find('\n');
@@ -298,26 +345,26 @@ string Connection::recvln() {
      return s;
    }

    // do poll fd here ?

    // read data
    static char local_buf[MAX_LINE_SIZE];
    int ret = ::recv(socket_fd_, local_buf, MAX_LINE_SIZE, 0);
    int ret = ::recv(socket_fd, local_buf, MAX_LINE_SIZE, 0);

    if (ret == 0) {
    if (ret == -1 || ret == 0) {
      pthread_mutex_lock(&c_mutex_);
      pthread_mutex_lock(&w_mutex_);
      disconnect_();
      pthread_mutex_unlock(&r_mutex_);
      pthread_mutex_unlock(&c_mutex_);
      pthread_mutex_unlock(&r_mutex_);
      throw Error(ERR_NET, "Connection reset by peer");
    }

    if (ret == -1) {
      *local_buf = 0;

      // unlock before error
      pthread_mutex_unlock(&r_mutex_);

      if (ret == 0)
	throw Error(ERR_NET, "Connection reset by peer");
      else
	throw Error(ERR_NET, (string) "recvln: " + strerror(errno));
    }
    else local_buf[ret] = 0;
    local_buf[ret] = 0;

    // add read data in buffer
    rbuf_ += local_buf;
@@ -327,10 +374,14 @@ string Connection::recvln() {
}

string Connection::getlocalip() {
  if (socket_fd_ < 0)
  // get local ip is a conn op
  pthread_mutex_lock(&c_mutex_);

  if (socket_fd_ < 0) {
    pthread_mutex_unlock(&c_mutex_);
    throw Error(ERR_NET, "No connection established but trying to get local ip");
  }

  pthread_mutex_lock(&c_mutex_);
  if (local_ip_.empty())
    setlocalip_();
  string ip = local_ip_;
@@ -339,10 +390,14 @@ string Connection::getlocalip() {
}

int Connection::getlocalport() {
  if (socket_fd_ < 0)
  // get local port is a conn op
  pthread_mutex_lock(&c_mutex_);

  if (socket_fd_ < 0) {
    pthread_mutex_unlock(&c_mutex_);
    throw Error(ERR_NET, "No connection established but trying to get local port");
  }

  pthread_mutex_lock(&c_mutex_);
  if (local_port_ == -1)
    setlocalport_();
  int port = local_port_;
@@ -351,10 +406,14 @@ int Connection::getlocalport() {
}

string Connection::getremoteip() {
  if (socket_fd_ < 0)
  // get remote ip is a conn op
  pthread_mutex_lock(&c_mutex_);

  if (socket_fd_ < 0) {
    pthread_mutex_unlock(&c_mutex_);
    throw Error(ERR_NET, "No connection established but trying to get remote ip");
  }

  pthread_mutex_lock(&c_mutex_);
  if (remote_ip_.empty())
    setremoteip_();
  string ip = remote_ip_;
@@ -363,10 +422,14 @@ string Connection::getremoteip() {
}

int Connection::getremoteport() {
  if (socket_fd_ < 0)
  // get remote port is a conn op
  pthread_mutex_lock(&c_mutex_);

  if (socket_fd_ < 0) {
    pthread_mutex_unlock(&c_mutex_);
    throw Error(ERR_NET, "No connection established but trying to get remote port");
  }

  pthread_mutex_lock(&c_mutex_);
  if (remote_port_ == -1)
    setremoteport_();
  int port = remote_port_;
@@ -379,89 +442,17 @@ unsigned long int Connection::getid() {
}

int Connection::getsocket() {
  if (socket_fd_ < 0)
    throw Error(ERR_NET, "No connection established but trying to get socket fd");

  // get socket is a conn op
  pthread_mutex_lock(&c_mutex_);
  int ret = socket_fd_;
  int socket_fd = socket_fd_;
  pthread_mutex_unlock(&c_mutex_);
  return ret;

  if (socket_fd < 0)
    throw Error(ERR_NET, "No connection established but trying to get socket fd");

  return socket_fd;
}

/*******************************************************************************
 ** Protected method
 ******************************************************************************/

/**
 * Reserve a socket
 * No mutex used !
 */
void Connection::socket_() {
  assert(socket_fd_ == -1);
  int yes = 1;

  if ((socket_fd_ = ::socket(AF_INET, SOCK_STREAM, 0)) == -1)
    throw Error(ERR_NET, (string) "Unable to open socket: " + strerror(errno));

  if (::setsockopt(socket_fd_, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
    throw Error(ERR_NET, (string) "Unable to set socket options: " + strerror(errno));
}

/**
 * Free a socket
 * No mutex used !
 */
void Connection::disconnect_() {
  assert(socket_fd_ >= 0);

  close(socket_fd_);
  socket_fd_ = -1;
}

/**
 * Set local ip
 * No mutex used !
 */
void Connection::setlocalip_() {
  struct sockaddr_in addr;
  socklen_t len = sizeof addr;

  ::getsockname(socket_fd_, (struct sockaddr*) &addr, &len);
  local_ip_ = inet_ntoa(addr.sin_addr);
}

/**
 * Set local port
 * No mutex used !
 */
void Connection::setlocalport_() {
  struct sockaddr_in addr;
  socklen_t len = sizeof addr;

  ::getsockname(socket_fd_, (struct sockaddr*) &addr, &len);
  local_port_ = ntohs(addr.sin_port);
}

/**
 * Set remote ip
 * No mutex used !
 */
void Connection::setremoteip_() {
  struct sockaddr_in addr;
  socklen_t len = sizeof addr;

  ::getpeername(socket_fd_, (struct sockaddr*) &addr, &len);
  remote_ip_ = inet_ntoa(addr.sin_addr);
}

/**
 * Set remote port
 * No mutex used !
 */
void Connection::setremoteport_() {
  struct sockaddr_in addr;
  socklen_t len = sizeof addr;

  ::getpeername(socket_fd_, (struct sockaddr*) &addr, &len);
  remote_port_ = ntohs(addr.sin_port);
}
+12 −14
Original line number Diff line number Diff line
@@ -2,17 +2,17 @@
  This file is part of SLS.
  Copyright (C) 2008 Sebastien LUTTRINGER <contact@seblu.net>

  SLS is free software; you can redistribute it and/or modify
  SLL 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; version 2 of the License.

  SLS is distributed in the hope that it will be useful,
  SLL 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 SLS; if not, write to the Free Software
  along with SLL; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

@@ -35,15 +35,14 @@ public:
  void listen(int port, int max);

  void disconnect();
  bool connected();
  inline bool connected();

  Connection *accept();

  // send methods
  inline void send(const string &data);
  void send(const char* buf, size_t len);
  inline void send(const string &data);
  inline void sendln(const string &data);
  void sendln(const char* buf, size_t len);

  // recv methods
  string recvln();
@@ -61,14 +60,13 @@ public:

  // protected methods
protected:
  void socket_();
  void disconnect_();

  inline void setallinfo_();
  void setlocalip_();
  void setlocalport_();
  void setremoteip_();
  void setremoteport_();
  inline void socket_();
  inline void disconnect_();

  inline void setlocalip_();
  inline void setlocalport_();
  inline void setremoteip_();
  inline void setremoteport_();

  // Protected datas
protected:
+101 −7
Original line number Diff line number Diff line
@@ -16,10 +16,35 @@
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

# include "error.hh"

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>

/*******************************************************************************
 ** Public method
 ******************************************************************************/

/**
 * Return the connection sate
 * Cannot be const, du to mutex locking
 *
 * @return see description
 */
bool Connection::connected() {
  // get connection mutex

  pthread_mutex_lock(&c_mutex_);

  bool ret = (socket_fd_ >= 0);

  pthread_mutex_unlock(&c_mutex_);

  return ret;
}

/**
 * Send @param data on socket
 */
@@ -31,7 +56,8 @@ void Connection::send(const string &data) {
 * Send @param data followed by '\n' on socket
 */
void Connection::sendln(const string &data) {
  sendln(data.c_str(), data.length());
  string tosend = data + "\n";
  send(tosend.c_str(), tosend.length());
}

/*******************************************************************************
@@ -39,11 +65,79 @@ void Connection::sendln(const string &data) {
 ******************************************************************************/

/**
 * Set all information about a connection
 * Free a socket
 * No mutex used !
 */
void Connection::setallinfo_() {
  setlocalip_();
  setlocalport_();
  setremoteip_();
  setremoteport_();
void Connection::disconnect_() {
  assert(socket_fd_ >= 0);

  close(socket_fd_);
  socket_fd_ = -1;
}

/**
 * Reserve a socket
 * No mutex used !
 */
void Connection::socket_() {
  assert(socket_fd_ == -1);
  int yes = 1;

  if ((socket_fd_ = ::socket(AF_INET, SOCK_STREAM, 0)) == -1)
    throw Error(ERR_NET, (string) "Unable to open socket: " + strerror(errno));

  if (::setsockopt(socket_fd_, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
    throw Error(ERR_NET, (string) "Unable to set socket options: " + strerror(errno));
}

/**
 * Set local ip
 * No mutex used !
 */
void Connection::setlocalip_() {
  struct sockaddr_in addr;
  socklen_t len = sizeof addr;

  if (::getsockname(socket_fd_, (struct sockaddr*) &addr, &len) != 0)
    throw Error(ERR_NET, (string) "Unable to set local ip: " + strerror(errno));
  local_ip_ = inet_ntoa(addr.sin_addr);
}

/**
 * Set local port
 * No mutex used !
 */
void Connection::setlocalport_() {
  struct sockaddr_in addr;
  socklen_t len = sizeof addr;

  if (::getsockname(socket_fd_, (struct sockaddr*) &addr, &len) != 0)
    throw Error(ERR_NET, (string) "Unable to set local port: " + strerror(errno));
  local_port_ = ntohs(addr.sin_port);
}

/**
 * Set remote ip
 * No mutex used !
 */
void Connection::setremoteip_() {
  struct sockaddr_in addr;
  socklen_t len = sizeof addr;

  if (::getpeername(socket_fd_, (struct sockaddr*) &addr, &len) != 0)
    throw Error(ERR_NET, (string) "Unable to set remote ip: " + strerror(errno));
  remote_ip_ = inet_ntoa(addr.sin_addr);
}

/**
 * Set remote port
 * No mutex used !
 */
void Connection::setremoteport_() {
  struct sockaddr_in addr;
  socklen_t len = sizeof addr;

  if (::getpeername(socket_fd_, (struct sockaddr*) &addr, &len) != 0)
    throw Error(ERR_NET, (string) "Unable to set remote port: " + strerror(errno));
  remote_port_ = ntohs(addr.sin_port);
}
Loading