Updated license
[anna.git] / source / comm / Socket.cpp
1 // ANNA - Anna is Not Nothingness Anymore
2 //
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
4 //
5 // https://bitbucket.org/testillano/anna
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
11 //     * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 //     * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 //     * Neither the name of Google Inc. nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 // Authors: eduardo.ramos.testillano@gmail.com
34 //          cisco.tierra@gmail.com
35
36
37 #include <sys/time.h>
38 #include <fcntl.h>
39 #include <strings.h>
40 #include <sys/types.h>
41 #include <poll.h>
42
43 #include <algorithm>
44
45 #include <anna/core/tracing/Logger.hpp>
46 #include <anna/core/tracing/TraceMethod.hpp>
47 #include <anna/core/DataBlock.hpp>
48
49 #include <anna/xml/Node.hpp>
50
51 #include <anna/comm/Socket.hpp>
52 #include <anna/comm/INetAddress.hpp>
53 #include <anna/comm/functions.hpp>
54 #include <anna/comm/Application.hpp>
55 #include <anna/comm/TransportFactory.hpp>
56 #include <anna/comm/ReceiverFactory.hpp>
57
58 using namespace std;
59 using namespace anna;
60 using namespace anna::comm;
61
62 Socket::Socket(const Domain::_v domain, const Type::_v type, TransportFactory* transportFactory) :
63   a_domain(domain),
64   a_type(type),
65   a_fd(-1),
66   a_isBound(false),
67   a_reuseMode(false),
68   a_category(0),
69   a_receiverFactory(NULL) {
70   if((a_transportFactory = transportFactory) == NULL) {
71     try {
72       comm::Application& app = comm::functions::getApp();
73       a_transportFactory = &app.comm::Application::getDefaultTransportFactory();
74     } catch(Exception& ex) {
75       ex.trace();
76     }
77   }
78 }
79
80 Socket::Socket(const INetAddress& localAddress, const Type::_v type, TransportFactory* transportFactory) :
81   a_domain(Domain::Inet),
82   a_type(type),
83   a_fd(-1),
84   a_localAccessPoint(localAddress),
85   a_isBound(false),
86   a_reuseMode(false),
87   a_category(0),
88   a_receiverFactory(NULL) {
89   if((a_transportFactory = transportFactory) == NULL) {
90     try {
91       comm::Application& app = comm::functions::getApp();
92       a_transportFactory = &app.comm::Application::getDefaultTransportFactory();
93     } catch(Exception& ex) {
94       ex.trace();
95     }
96   }
97 }
98
99 Socket::Socket(const std::string& path, const Type::_v type, TransportFactory* transportFactory) :
100   a_transportFactory(NULL),
101   a_domain(Domain::Unix),
102   a_type(type),
103   a_fd(-1),
104   a_localAccessPoint(path),
105   a_isBound(false),
106   a_reuseMode(false),
107   a_category(0),
108   a_receiverFactory(NULL) {
109   if((a_transportFactory = transportFactory) == NULL) {
110     try {
111       comm::Application& app = comm::functions::getApp();
112       a_transportFactory = &app.comm::Application::getDefaultTransportFactory();
113     } catch(Exception& ex) {
114       ex.trace();
115     }
116   }
117 }
118
119 Socket::~Socket() {
120   try {
121     close();
122   } catch(RuntimeException& ex) {
123     ex.trace();
124   }
125 }
126
127 bool Socket::support(const char* transportClassName) const
128 throw() {
129   if(a_transportFactory == NULL)
130     return false;
131
132   return anna_strcmp(a_transportFactory->getName().c_str(), transportClassName) == 0;
133 }
134
135 //---------------------------------------------------------------------------
136 // No hace falta que sea MT-safe porque siempre se invoca desde m�odo que
137 // ya tienen activa su propia zona de exclusin.
138 //---------------------------------------------------------------------------
139 void Socket::open()
140 throw(RuntimeException) {
141   if(a_fd != -1)
142     return;
143
144   if(a_transportFactory == NULL) {
145     std::string msg(asString());
146     msg += " | Transport factory was not especified";
147     throw RuntimeException(msg, ANNA_FILE_LOCATION);
148   }
149
150   anna_comm_socket_check(a_fd = socket((int) a_domain, (int) a_type, 0), "Cannot create the socket");
151   int value;
152   socklen_t len = sizeof(int);
153   anna_comm_socket_check(
154     getsockopt(a_fd, SOL_SOCKET, SO_REUSEADDR, &value, &len),
155     "Cannot obtain reuse mode"
156   );
157   a_reuseMode = (value == 1);
158
159   if(a_type == Type::Datagram) {
160     value = 1;
161     anna_comm_socket_check(
162       setsockopt(a_fd, SOL_SOCKET, SO_BROADCAST, &value, sizeof(int)),
163       "Cannot activate broadcast system"
164     );
165   }
166
167   LOGDEBUG(
168     string msg("comm::Socket::open | ");
169     msg += asString();
170     Logger::debug(msg, ANNA_FILE_LOCATION);
171   );
172 }
173
174 void Socket::close()
175 throw() {
176   if(a_fd == -1)
177     return;
178
179   Guard guard(*this, "comm::Socket::close");
180
181   if(a_fd != -1) {
182     a_isBound = false;
183     do_close();
184     a_fd = -1;
185   }
186
187   LOGDEBUG(
188     string msg("comm::Socket::close | ");
189     msg += asString();
190     Logger::debug(msg, ANNA_FILE_LOCATION);
191   );
192 }
193
194 bool Socket::setBlockingMode(const bool blockingMode)
195 throw(RuntimeException) {
196   int opts;
197   int result;
198   anna_socket_assert(a_fd == -1, "Socket is not opened");
199   anna_comm_socket_check(opts = fcntl(a_fd, F_GETFL), "Cannot obtain state bits (F_GETFL)");
200   result = !(opts & O_NONBLOCK);
201
202   if(blockingMode == false)
203     opts |= O_NONBLOCK;
204   else
205     opts &= ~O_NONBLOCK;
206
207   anna_comm_socket_check(fcntl(a_fd, F_SETFL, opts), "Cannot set state bits (F_SETFL)");
208   LOGDEBUG(
209     string msg("comm::Socket::setBlockingMode | Current: ");
210     msg += functions::asString(blockingMode);
211     msg += functions::asText(" | Previous: ", result != 0);
212     Logger::debug(msg, ANNA_FILE_LOCATION);
213   )
214   return (result != 0);
215 }
216
217 bool Socket::setReuseMode(const bool reuseMode)
218 throw(RuntimeException) {
219   anna_socket_assert(a_fd == -1, "Socket is not opened");
220
221   if(a_reuseMode == reuseMode)
222     return reuseMode;
223
224   bool result;
225   result = a_reuseMode;
226   int value = htons((a_reuseMode = reuseMode) == true);
227   anna_comm_socket_check(
228     setsockopt(a_fd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(int)),
229     "Cannot set the reuse mode"
230   );
231   return result;
232 }
233
234 void Socket::bind()
235 throw(RuntimeException) {
236   Guard guard(*this, "comm::Socket::bind");
237
238   if(a_fd == -1)
239     open();
240
241   anna_socket_assert(a_isBound == true, "Already attached");
242   setReuseMode(true);
243   sockaddr* s(NULL);
244   int len(0);
245   a_localAccessPoint.translate(*this, s, len);
246
247   if(do_bind(s, len) < 0) {
248     RuntimeException ex(asString(), errno, ANNA_FILE_LOCATION);
249     close();
250     throw ex;
251   }
252
253   a_isBound = true;
254   LOGDEBUG(
255     string msg("comm::Socket::bind | ");
256     msg += asString();
257     Logger::debug(msg, ANNA_FILE_LOCATION);
258   );
259 }
260
261 int Socket::do_bind(const struct sockaddr *addr, const int len)
262 throw(RuntimeException) {
263   return ::bind(a_fd, addr, len);
264 }
265
266 std::string Socket::asString() const
267 throw() {
268   std::string msg("comm::Socket { Domain: ");
269
270   if(this == NULL)
271     return msg += " <null> }";
272
273   switch(a_domain) {
274   case Domain::Unix: msg += "Unix"; break;
275   case Domain::Inet: msg += "Inet"; break;
276   default: msg += '<'; msg += functions::asString((int) a_domain); msg += '>'; break;
277   }
278
279   msg += " | Type: ";
280   msg += (a_type == Type::Stream) ? "Stream" : "Datagram";
281   msg += " | ";
282
283   if(a_transportFactory != NULL) {
284     msg += a_transportFactory->asString();
285     msg += " | ";
286   }
287
288   msg += "fd: ";
289   msg += functions::asString(a_fd);
290   msg += " | LocalPoint: ";
291   a_localAccessPoint.asString(msg);
292   msg += " | Bound: ";
293   msg += functions::asString(a_isBound);
294   msg += " | Opened: ";
295   msg += functions::asString(isOpened());
296
297   if(a_category != 0)
298     msg += functions::asText(" | Category: ", a_category);
299
300   if(a_receiverFactory != NULL) {
301     msg += " | ";
302     msg += a_receiverFactory->asString();
303   }
304
305   return msg += " }";
306 }
307
308 xml::Node* Socket::asXML(xml::Node* parent) const
309 throw(RuntimeException) {
310   const char* aux;
311   xml::Node* result = parent->createChild("comm.Socket");
312
313   switch(a_domain) {
314   case Domain::Unix: aux = "Unix"; break;
315   case Domain::Inet: aux = "Inet"; break;
316   default: aux = functions::asString((int) a_domain).c_str(); break;
317   }
318
319   result->createAttribute("Domain", aux);
320   result->createAttribute("Type", (a_type == Type::Stream) ? "Stream" : "Datagram");
321
322   if(a_transportFactory != NULL)
323     a_transportFactory->asXML(result);
324
325   result->createAttribute("fd", a_fd);
326   result->createAttribute("Bound", functions::asString(a_isBound));
327   result->createAttribute("Opened", functions::asString(isOpened()));
328   a_localAccessPoint.asXML("comm.LocalPoint", result);
329
330   if(a_fd != -1) {
331     int opts;
332
333     if((opts = fcntl(a_fd, F_GETFL)) != -1)
334       result->createAttribute("Mode", (opts & O_NONBLOCK) ? "Non-Blocking" : "Blocking");
335   }
336
337   if(a_category != 0)
338     result->createAttribute("Category", a_category);
339
340   if(a_receiverFactory != NULL)
341     a_receiverFactory->asXML(result);
342
343   return result;
344 }
345
346 const char* Socket::asText(const Socket::Notify::_v v)
347 throw() {
348   static const char* text [] = { "None", "ReceiveData", "Close", "Corrupt" };
349   return text [v];
350 }
351