diff --git a/sll/trunk/connection.cc b/sll/trunk/connection.cc new file mode 100644 index 0000000000000000000000000000000000000000..aa25541436ba4d09eea44f71074ab1e97a540c09 --- /dev/null +++ b/sll/trunk/connection.cc @@ -0,0 +1,467 @@ +/* + This file is part of SLS. + Copyright (C) 2008 Sebastien LUTTRINGER + + SLS 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, + 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 + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "sls.hh" +#include "connection.hh" +#include "error.hh" + +#include +#include +#include +#include +#include +#include + +/******************************************************************************* + ** Class method + ******************************************************************************/ +unsigned long int Connection::getconnid() { + static unsigned long int id = 1; + return id++; +} + +/******************************************************************************* + ** Public method + ******************************************************************************/ + +/** + * Constructor + * + * @param fd socket of the connection (-1) is not exist + */ +Connection::Connection(int fd) : socket_fd_(fd), local_port_(-1), remote_port_(-1), id_(0) { + pthread_mutex_init(&c_mutex_, 0); + pthread_mutex_init(&r_mutex_, 0); + pthread_mutex_init(&w_mutex_, 0); + + if (socket_fd_ >= 0) { + id_ = getconnid(); + setallinfo_(); + } +} + +/** + * Destructor + */ +Connection::~Connection() { + pthread_mutex_destroy(&c_mutex_); + pthread_mutex_destroy(&r_mutex_); + pthread_mutex_destroy(&w_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"); + + if (port <= 0) + throw Error(ERR_NET, "Trying to Connect on a bad port number"); + + pthread_mutex_lock(&c_mutex_); + pthread_mutex_lock(&r_mutex_); + pthread_mutex_lock(&w_mutex_); + + // retrieve remote host info + struct hostent *h = gethostbyname(addr); + if (h == 0) { + pthread_mutex_unlock(&w_mutex_); + pthread_mutex_unlock(&r_mutex_); + pthread_mutex_unlock(&c_mutex_); + throw Error(ERR_NET, (string) "Unable to resolve: " + addr + ": " + hstrerror(h_errno)); + } + + // create socket + try { socket_(); } + catch (...) { + pthread_mutex_unlock(&w_mutex_); + pthread_mutex_unlock(&r_mutex_); + pthread_mutex_unlock(&c_mutex_); + throw; + } + + // fill sockaddr + struct sockaddr_in daddr; + daddr.sin_family = AF_INET; + daddr.sin_port = htons(port); + daddr.sin_addr = *((struct in_addr *) h->h_addr); + memset(daddr.sin_zero, '\0', sizeof daddr.sin_zero); + + // connect + if (::connect(socket_fd_, (struct sockaddr *) &daddr, sizeof daddr) == -1) { + disconnect_(); + pthread_mutex_unlock(&w_mutex_); + pthread_mutex_unlock(&r_mutex_); + pthread_mutex_unlock(&c_mutex_); + throw Error(ERR_NET, (string) "Unable to connect to " + addr + ": " + hstrerror(h_errno)); + } + + // set infos + setallinfo_(); + + pthread_mutex_unlock(&w_mutex_); + pthread_mutex_unlock(&r_mutex_); + pthread_mutex_unlock(&c_mutex_); +} + +/** + * 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"); + + // lock + pthread_mutex_lock(&c_mutex_); + pthread_mutex_lock(&r_mutex_); + pthread_mutex_lock(&w_mutex_); + + // create socket + try { socket_(); } + catch (...) { + pthread_mutex_unlock(&w_mutex_); + pthread_mutex_unlock(&r_mutex_); + pthread_mutex_unlock(&c_mutex_); + throw; + } + + // fill sockaddr + struct sockaddr_in saddr; + + saddr.sin_family = AF_INET; + saddr.sin_port = htons(port); + saddr.sin_addr.s_addr = INADDR_ANY; + memset(saddr.sin_zero, '\0', sizeof saddr.sin_zero); + + // bind on socket + if (bind(socket_fd_, (struct sockaddr *)&saddr, sizeof saddr) == -1) { + disconnect_(); + pthread_mutex_unlock(&w_mutex_); + pthread_mutex_unlock(&r_mutex_); + pthread_mutex_unlock(&c_mutex_); + throw Error(ERR_NET, (string) "Unable to bind: " + strerror(errno)); + } + + // listen on socket + if (::listen(socket_fd_, max) == -1) { + disconnect_(); + pthread_mutex_unlock(&w_mutex_); + pthread_mutex_unlock(&r_mutex_); + pthread_mutex_unlock(&c_mutex_); + throw Error(ERR_NET, (string) "Unable to listen: " + strerror(errno)); + } + + // set all infos + setallinfo_(); + + pthread_mutex_unlock(&w_mutex_); + pthread_mutex_unlock(&r_mutex_); + pthread_mutex_unlock(&c_mutex_); +} + +/** + * 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) + 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; + + pthread_mutex_lock(&r_mutex_); + pthread_mutex_lock(&w_mutex_); + + 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)); + } + + pthread_mutex_unlock(&r_mutex_); + pthread_mutex_unlock(&w_mutex_); + + return new Connection(r_fd); +} + +/** + * 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"); + + 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)); +} + +/** + * 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"); + + pthread_mutex_lock(&w_mutex_); + // write data + int ret = ::send(socket_fd_, buf, len, 0); + if (ret == -1) { + pthread_mutex_unlock(&w_mutex_); + if (errno == ECONNRESET) + throw Error(ERR_NET, "Connection reset by peer"); + throw Error(ERR_NET, "sendln: " + (string) strerror(errno)); + } + + // write '\n' + ret = ::send(socket_fd_, "\n", 1, 0); + 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) + 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'); + if (offset != string::npos) { + string s = rbuf_.substr(0, offset); + rbuf_.erase(0, offset + 1); + pthread_mutex_unlock(&r_mutex_); + 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); + + if (ret == 0) { + 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_); + + throw Error(ERR_NET, (string) "recvln: " + strerror(errno)); + } + else local_buf[ret] = 0; + + // add read data in buffer + rbuf_ += local_buf; + + } while (1); + assert(1); +} + +string Connection::getlocalip() { + if (socket_fd_ < 0) + 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_; + pthread_mutex_unlock(&c_mutex_); + return ip; +} + +int Connection::getlocalport() { + if (socket_fd_ < 0) + 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_; + pthread_mutex_unlock(&c_mutex_); + return port; +} + +string Connection::getremoteip() { + if (socket_fd_ < 0) + 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_; + pthread_mutex_unlock(&c_mutex_); + return ip; +} + +int Connection::getremoteport() { + if (socket_fd_ < 0) + 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_; + pthread_mutex_unlock(&c_mutex_); + return port; +} + +unsigned long int Connection::getid() { + return id_; +} + +int Connection::getsocket() { + if (socket_fd_ < 0) + throw Error(ERR_NET, "No connection established but trying to get socket fd"); + + pthread_mutex_lock(&c_mutex_); + int ret = socket_fd_; + pthread_mutex_unlock(&c_mutex_); + return ret; +} + +/******************************************************************************* + ** 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); +} diff --git a/sll/trunk/connection.hh b/sll/trunk/connection.hh new file mode 100644 index 0000000000000000000000000000000000000000..194cd85543b552c10938f6f7ee24bf3914f14143 --- /dev/null +++ b/sll/trunk/connection.hh @@ -0,0 +1,96 @@ +/* + This file is part of SLS. + Copyright (C) 2008 Sebastien LUTTRINGER + + SLS 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, + 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 + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef CONNECTION_HH +# define CONNECTION_HH + +# include + +class Connection { + // class methods +public: + unsigned long int getconnid(); + + // public methods +public: + Connection(int fd = -1); + virtual ~Connection(); + + void connect(const char *addr, int port); + void listen(int port, int max); + + void disconnect(); + bool connected(); + + Connection *accept(); + + // send methods + inline void send(const string &data); + void send(const char* buf, size_t len); + inline void sendln(const string &data); + void sendln(const char* buf, size_t len); + + // recv methods + string recvln(); + + // info methods + string getlocalip(); + int getlocalport(); + + string getremoteip(); + int getremoteport(); + + unsigned long int getid(); + + int getsocket(); + + // protected methods +protected: + void socket_(); + void disconnect_(); + + inline void setallinfo_(); + void setlocalip_(); + void setlocalport_(); + void setremoteip_(); + void setremoteport_(); + + // Protected datas +protected: + int socket_fd_; // connection socket + + // storage of info about connection + int local_port_; + int remote_port_; + + string local_ip_; + string remote_ip_; + + unsigned long int id_; + + string rbuf_; // read buffer + string wbuf_; // write buffer + + pthread_mutex_t c_mutex_; // connection mutex + pthread_mutex_t r_mutex_; // read mutex + pthread_mutex_t w_mutex_; // write mutex +}; + +# include "connection.hxx" + +#endif diff --git a/sll/trunk/connection.hxx b/sll/trunk/connection.hxx new file mode 100644 index 0000000000000000000000000000000000000000..92b5bf707be7ffcd518a966ccdffef9199538145 --- /dev/null +++ b/sll/trunk/connection.hxx @@ -0,0 +1,49 @@ +/* + This file is part of SLS. + Copyright (C) 2008 Sebastien LUTTRINGER + + SLS 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, + 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 + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/******************************************************************************* + ** Public method + ******************************************************************************/ + +/** + * Send @param data on socket + */ +void Connection::send(const string &data) { + send(data.c_str(), data.length()); +} + +/** + * Send @param data followed by '\n' on socket + */ +void Connection::sendln(const string &data) { + sendln(data.c_str(), data.length()); +} + +/******************************************************************************* + ** Protected method + ******************************************************************************/ + +/** + * Set all information about a connection + */ +void Connection::setallinfo_() { + setlocalip_(); + setlocalport_(); + setremoteip_(); + setremoteport_(); +}