Bond's TCP Library  1.0
Bond's TCP Client/Server Library
tcpserver.cpp
1 #include <fcntl.h>
2 #include <sys/types.h>
3 #include <sys/ioctl.h>
4 #include <linux/if_link.h>
5 #include <netdb.h>
6 #include <ifaddrs.h>
7 #include <sys/socket.h>
8 #include <sys/epoll.h>
9 #include <arpa/inet.h>
10 #include <netinet/ip.h>
11 #include <unistd.h>
12 #include "tcpserver.h"
13 
14 namespace tcp {
15 
16 using namespace std;
17 
19  if (listening())
20  stop();
21 }
22 
23 void Server::start(in_port_t port, char *bindaddress, bool useSSL, int backlog)
24 {
25  start(port,string(bindaddress),useSSL,backlog);
26 }
27 
28 void Server::start(in_port_t port, string bindaddress, bool useSSL, int backlog)
29 {
30  mtx.lock();
31  useSSL_ = useSSL;
32  memset(&addr_,0,sizeof(addr_));
33  if ((bindaddress == "") || (bindaddress == "0.0.0.0") || (bindaddress == "::")) {
34  if (domain() == AF_INET) {
35  reinterpret_cast<struct sockaddr_in*>(&addr_)->sin_addr.s_addr = INADDR_ANY;
36  }
37  if (domain() == AF_INET6) {
38  reinterpret_cast<struct sockaddr_in6*>(&addr_)->sin6_addr = IN6ADDR_ANY_INIT;
39  }
40  } else {
41  if (!findifaddr(bindaddress,reinterpret_cast<struct sockaddr*>(&addr_))) {
42  error("Interface " + string(bindaddress) + " not found");
43  }
44  }
45  if (domain() == AF_INET) {
46  addr_.ss_family = AF_INET;
47  reinterpret_cast<struct sockaddr_in*>(&addr_)->sin_port = htons(port);
48  }
49  if (domain() == AF_INET6) {
50  addr_.ss_family = AF_INET6;
51  reinterpret_cast<struct sockaddr_in6*>(&addr_)->sin6_port = htons(port);
52  }
53 
54  int enable = 1;
55  if (setsockopt(socket(), SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0)
56  error("setsockopt","Server could not set socket option SO_REUSEADDR");
57 
58  if (bindToAddress((struct sockaddr*)&addr_,(domain() == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)))) {
59  startListening(backlog);
60  }
61  mtx.unlock();
62 }
63 
65 {
66  if (listening()) {
67  log("Sending disconnect to all sessions");
68  mtx.lock();
69  std::map<int,Session*>::iterator it;
70  for (it = sessions.begin();it != sessions.end();++it) {
71  it->second->disconnect();
72  }
73  mtx.unlock();
74  }
75  disconnect();
76 }
77 
78 void Server::handleEvents(uint32_t events) {
79  if (listening() && (events & EPOLLIN)) {
80  acceptConnection();
81  }
82 }
83 
85  struct ifaddrs *list, *item;
86  string family;
87  char host[NI_MAXHOST];
88 
89  if (getifaddrs(&list) == 0) {
90  item = list;
91  while (item != nullptr) {
92  int f = item->ifa_addr->sa_family;
93  if ((f == AF_INET) || (f == AF_INET6)) {
94  switch (f) {
95  case AF_INET: family.assign("AF_INET"); break;
96  case AF_INET6: family.assign("AF_INET6"); break;
97  default: family.assign("");
98  };
99  int res = getnameinfo(item->ifa_addr,(f == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6),host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
100  if (res != 0) {
101  error("printifaddrs",gai_strerror(res));
102  return false;
103  }
104  cout << family << " " << item->ifa_name << " address: " << host << endl;
105  }
106  item = item->ifa_next;
107  }
108  } else {
109  error("printifaddrs",strerror(errno));
110  return false;
111  }
112  freeifaddrs(list);
113  return true;
114 }
115 
116 bool Server::findifaddr(const string ifname, sockaddr *addr) {
117  struct ifaddrs *list, *item;
118  int f;
119  string family;
120  char host[NI_MAXHOST];
121  bool result = false;
122 
123  if (getifaddrs(&list) == 0) {
124  item = list;
125  while (item != nullptr) {
126  f = item->ifa_addr->sa_family;
127  if (getnameinfo(item->ifa_addr,(f == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6),host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) != 0) {
128  host[0] = 0;
129  }
130  if (f == domain() && ((ifname.compare(item->ifa_name) == 0) || (ifname.compare(host) == 0))) {
131  if (f == AF_INET) {
132  *(sockaddr_in*)addr = *(sockaddr_in*)item->ifa_addr;
133  result = true;
134  break;
135  }
136  if (f == AF_INET6) {
137  *(sockaddr_in6*)addr = *(sockaddr_in6*)item->ifa_addr;
138  result = true;
139  break;
140  }
141  }
142  item = item->ifa_next;
143  }
144  } else {
145  error("findifaddrs",strerror(errno));
146  return false;
147  }
148  freeifaddrs(list);
149  return result;
150 }
151 
152 bool Server::bindToAddress(sockaddr *addr, socklen_t len) {
153  if (::bind(socket(),addr,len) == -1) {
154  error("bind",strerror(errno));
155  return false;
156  } else {
157  char ip[INET6_ADDRSTRLEN];
158  memset(&ip,0,INET6_ADDRSTRLEN);
159  if (addr->sa_family == AF_INET) {
160  struct sockaddr_in * a = reinterpret_cast<struct sockaddr_in*>(addr);
161  string msg;
162  if (a->sin_addr.s_addr == INADDR_ANY) {
163  msg = "Server bound to any IP4";
164  } else {
165  inet_ntop(AF_INET,&a->sin_addr,ip,INET_ADDRSTRLEN);
166  msg = "Server bound to " + string(ip);
167  }
168  msg += " on port " + to_string(ntohs(a->sin_port));
169  log(msg);
170  } else {
171  struct sockaddr_in6 * a = reinterpret_cast<struct sockaddr_in6*>(addr);
172  inet_ntop(AF_INET6,&a->sin6_addr,ip,INET6_ADDRSTRLEN);
173  string msg;
174  if (strcmp(ip,"::") == 0) {
175  msg = "Server bound to any IP6";
176  } else {
177  msg = "Server bound to " + string(ip);
178  }
179  msg += " on port " + to_string(ntohs(a->sin6_port));
180  log(msg);
181  }
182  return true;
183  }
184 }
185 
186 bool Server::startListening(int backlog) {
187  if (listen(socket(),backlog) == -1) {
188  error("listen",strerror(errno));
189  return false;
190  } else {
191  state_ = SocketState::LISTENING;
192  log("Server started listening");
193  return true;
194  }
195 }
196 
197 bool Server::acceptConnection() {
198  struct sockaddr_in peer_addr;
199  socklen_t peer_addr_len = sizeof(struct sockaddr_in);
200  mtx.lock();
201  int conn_sock = ::accept(socket(),(struct sockaddr *) &peer_addr, &peer_addr_len);
202  if (conn_sock == -1) {
203  error("accept",strerror(errno));
204  mtx.unlock();
205  return false;
206  } else {
207  // Delete any existing sessions with the same socket handle
208  Session* session = sessions[conn_sock];
209  sessions.erase(conn_sock);
210  if (session != nullptr) {
211  warning("A session with socket handle " + to_string(conn_sock) + " already exists. Deleting it.");
212  delete session;
213  }
214  // Start a new session and accept it
215  session = createSession(conn_sock,peer_addr);
216  sessions[conn_sock] = session;
217  session->accepted();
218  mtx.unlock();
219  return session->connected();
220  }
221 }
222 
223 /* Session */
224 
226  mtx.lock();
227  server_.sessions.erase(socket());
228  mtx.unlock();
229 }
230 
231 void Session::connectionMessage(string action)
232 {
233  char ip[INET6_ADDRSTRLEN];
234  memset(&ip,0,INET6_ADDRSTRLEN);
235  inet_ntop(domain(),&addr_,ip,domain() == AF_INET ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN);
236  string msg("Connection from ");
237  if (domain() == AF_INET) {
238  msg += string(ip);
239  } else {
240  msg += "[" + string(ip) + "]";
241  }
242  msg += ":" + to_string(port_) + " " + action;
243  log(msg);
244 }
245 
249  mtx.lock();
250  connectionMessage("accepted");
251  if (server().useSSL_) {
252  ssl_ = createSSL(server().ctx());
253  ssl_->setfd(socket());
254  if (ssl_->accept())
255  state_ = SocketState::CONNECTED;
256  else
257  disconnected();
258  } else {
259  state_ = SocketState::CONNECTED;
260  }
261  mtx.unlock();
262 }
263 
268  if (ssl_) {
269  mtx.lock();
270  ssl_->shutdown();
271  printSSLErrors();
272  mtx.unlock();
273  }
274  disconnected();
275 }
276 
280  if (connected()) {
281  mtx.lock();
282  if (ssl_) {
283  delete ssl_;
284  ssl_ = nullptr;
285  printSSLErrors();
286  }
287  state_ = SocketState::DISCONNECTED;
288  connectionMessage("disconnected");
289  delete this;
290  mtx.unlock();
291  }
292 }
293 
294 }
tcp::printSSLErrors
void printSSLErrors()
This method logs openSSL errors to cerr.
Definition: tcpssl.cpp:56
tcpserver.h
Provides basic tcp server functionality.
tcp::Session::disconnect
void disconnect() override
Shutdown the socket and close the session.
Definition: tcpserver.cpp:267
tcp::Server::start
void start(in_port_t port, string bindaddress, bool useSSL=false, int backlog=64)
Start up the server.
Definition: tcpserver.cpp:28
tcp::Server::findifaddr
bool findifaddr(const string ifname, sockaddr *addr)
Returns an interface address from an interface name and domain.
Definition: tcpserver.cpp:116
tcp::error
void error(string msg)
Send an error message to the log stream.
Definition: tcpsocket.cpp:18
tcp::warning
void warning(string msg)
Send a warning message to the log stream.
Definition: tcpsocket.cpp:20
tcp::Server::~Server
virtual ~Server()
Destroy the server instance.
Definition: tcpserver.cpp:18
tcp::Session::~Session
virtual ~Session()
The destructor is protected and is called by the disconnect() or disconnected() methods.
Definition: tcpserver.cpp:225
tcp::Server::printifaddrs
static bool printifaddrs()
Print a list of interface addresses to cout.
Definition: tcpserver.cpp:84
tcp::Server::handleEvents
void handleEvents(uint32_t events) override
Called by the EPoll class when the listening socket recieves an event from the OS.
Definition: tcpserver.cpp:78
tcp::Server::stop
void stop()
Stop the server.
Definition: tcpserver.cpp:64
tcp::Session::disconnected
void disconnected() override
Called when a tcp connection is dropped.
Definition: tcpserver.cpp:279
tcp::log
void log(string msg)
Send an log message to the log stream.
Definition: tcpsocket.cpp:22
tcp
A tcp client/server library for linux that supports openSSL and EPoll.
Definition: tcpclient.cpp:5
tcp::Session::accepted
virtual void accepted()
Called by the Server::acceptConnection after a connection has been accepted.
Definition: tcpserver.cpp:248