Line data Source code
1 : // Snap Communicator -- classes to ease handling communication between processes
2 : // Copyright (c) 2012-2019 Made to Order Software Corp. All Rights Reserved
3 : //
4 : // This program is free software; you can redistribute it and/or modify
5 : // it under the terms of the GNU General Public License as published by
6 : // the Free Software Foundation; either version 2 of the License, or
7 : // (at your option) any later version.
8 : //
9 : // This program is distributed in the hope that it will be useful,
10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : // GNU General Public License for more details.
13 : //
14 : // You should have received a copy of the GNU General Public License
15 : // along with this program; if not, write to the Free Software
16 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 :
18 :
19 : // self
20 : //
21 : #include "eventdispatcher/utils.h"
22 :
23 : #include "eventdispatcher/exception.h"
24 :
25 :
26 : // snaplogger lib
27 : //
28 : #include <snaplogger/message.h>
29 :
30 :
31 : // snapdev lib
32 : //
33 : #include <snapdev/not_reached.h>
34 :
35 :
36 : // last include
37 : //
38 : #include <snapdev/poison.h>
39 :
40 :
41 :
42 : /** \file
43 : * \brief Various helper functions.
44 : *
45 : * These functions are useful for our event dispatcher environment.
46 : */
47 :
48 : namespace ed
49 : {
50 :
51 :
52 :
53 : /** \brief Get the current date.
54 : *
55 : * This function retrieves the current date and time with a precision
56 : * of microseconds.
57 : *
58 : * \todo
59 : * This is also defined in snap_child::get_current_date() so we should
60 : * unify that in some way...
61 : *
62 : * \return The time in microseconds.
63 : */
64 0 : std::int64_t get_current_date()
65 : {
66 : timeval tv;
67 0 : if(gettimeofday(&tv, nullptr) != 0)
68 : {
69 0 : int const err(errno);
70 : SNAP_LOG_FATAL
71 0 : << "gettimeofday() failed with errno: "
72 0 : << err
73 0 : << " ("
74 0 : << strerror(err)
75 0 : << ")";
76 0 : throw event_dispatcher_runtime_error("gettimeofday() failed");
77 : }
78 :
79 0 : return static_cast<int64_t>(tv.tv_sec) * static_cast<int64_t>(1000000)
80 0 : + static_cast<int64_t>(tv.tv_usec);
81 : }
82 :
83 :
84 :
85 : /** \brief Get the current date.
86 : *
87 : * This function retrieves the current date and time with a precision
88 : * of nanoseconds.
89 : *
90 : * \return The time in nanoseconds.
91 : */
92 0 : std::int64_t get_current_date_ns()
93 : {
94 : timespec ts;
95 0 : if(clock_gettime(CLOCK_REALTIME_COARSE, &ts) != 0)
96 : {
97 0 : int const err(errno);
98 : SNAP_LOG_FATAL
99 0 : << "clock_gettime() failed with errno: "
100 0 : << err
101 0 : << " ("
102 0 : << strerror(err)
103 0 : << ")";
104 0 : throw event_dispatcher_runtime_error("clock_gettime() failed");
105 : }
106 :
107 0 : return static_cast<int64_t>(ts.tv_sec) * static_cast<int64_t>(1000000000)
108 0 : + static_cast<int64_t>(ts.tv_nsec);
109 : }
110 :
111 :
112 :
113 :
114 :
115 :
116 :
117 :
118 :
119 : // ========================= HELPER FUNCTIONS TO DELETE ======================
120 :
121 :
122 :
123 : ///** \brief Check wether a string represents an IPv4 address.
124 : // *
125 : // * This function quickly checks whether the specified string defines a
126 : // * valid IPv4 address. It supports all classes (a.b.c.d, a.b.c., a.b, a)
127 : // * and all numbers can be in decimal, hexadecimal, or octal.
128 : // *
129 : // * \note
130 : // * The function can be called with a null pointer in which case it
131 : // * immediate returns false.
132 : // *
133 : // * \param[in] ip A pointer to a string holding an address.
134 : // *
135 : // * \return true if the \p ip string represents an IPv4 address.
136 : // *
137 : // * \sa snap_inet::inet_pton_v6()
138 : // */
139 : //bool is_ipv4(char const * ip)
140 : //{
141 : // if(ip == nullptr)
142 : // {
143 : // return false;
144 : // }
145 : //
146 : // // we must have (1) a number then (2) a dot or end of string
147 : // // with a maximum of 4 numbers and 3 dots
148 : // //
149 : // int64_t addr[4];
150 : // size_t pos(0);
151 : // for(;; ++ip, ++pos)
152 : // {
153 : // if(*ip < '0' || *ip > '9' || pos >= sizeof(addr) / sizeof(addr[0]))
154 : // {
155 : // // not a valid number
156 : // return false;
157 : // }
158 : // int64_t value(0);
159 : //
160 : // // number, may be decimal, octal, or hexadecimal
161 : // if(*ip == '0')
162 : // {
163 : // if(ip[1] == 'x' || ip[1] == 'X')
164 : // {
165 : // // expect hexadecimal
166 : // bool first(true);
167 : // for(ip += 2;; ++ip, first = false)
168 : // {
169 : // if(*ip >= '0' && *ip <= '9')
170 : // {
171 : // value = value * 16 + *ip - '0';
172 : // }
173 : // else if(*ip >= 'a' && *ip <= 'f')
174 : // {
175 : // value = value * 16 + *ip - 'a' + 10;
176 : // }
177 : // else if(*ip >= 'A' && *ip <= 'F')
178 : // {
179 : // value = value * 16 + *ip - 'A' + 10;
180 : // }
181 : // else
182 : // {
183 : // if(first)
184 : // {
185 : // // not even one digit, not good
186 : // return false;
187 : // }
188 : // // not valid hexadecimal, may be '.' or '\0' (tested below)
189 : // break;
190 : // }
191 : // if(value >= 0x100000000)
192 : // {
193 : // // too large even if we have no dots
194 : // return false;
195 : // }
196 : // }
197 : // }
198 : // else
199 : // {
200 : // // expect octal
201 : // for(++ip; *ip >= '0' && *ip <= '8'; ++ip)
202 : // {
203 : // value = value * 8 + *ip - '0';
204 : // if(value >= 0x100000000)
205 : // {
206 : // // too large even if we have no dots
207 : // return false;
208 : // }
209 : // }
210 : // }
211 : // }
212 : // else
213 : // {
214 : // // expect decimal
215 : // for(; *ip >= '0' && *ip <= '9'; ++ip)
216 : // {
217 : // value = value * 10 + *ip - '0';
218 : // if(value >= 0x100000000)
219 : // {
220 : // // too large even if we have no dots
221 : // return false;
222 : // }
223 : // }
224 : // }
225 : ////std::cerr << "value[" << pos << "] = " << value << "\n";
226 : // addr[pos] = value;
227 : // if(*ip != '.')
228 : // {
229 : // if(*ip != '\0')
230 : // {
231 : // return false;
232 : // }
233 : // ++pos;
234 : // break;
235 : // }
236 : // }
237 : //
238 : ////std::cerr << "pos = " << pos << "\n";
239 : // switch(pos)
240 : // {
241 : // case 1:
242 : // // one large value is considered valid for IPv4
243 : // // max. was already checked
244 : // return true;
245 : //
246 : // case 2:
247 : // return addr[0] < 256 && addr[1] < 0x1000000;
248 : //
249 : // case 3:
250 : // return addr[0] < 256 && addr[1] < 256 && addr[2] < 0x10000;
251 : //
252 : // case 4:
253 : // return addr[0] < 256 && addr[1] < 256 && addr[2] < 256 && addr[3] < 256;
254 : //
255 : // //case 0: (can happen on empty string)
256 : // default:
257 : // // no values, that is incorrect!?
258 : // return false;
259 : //
260 : // }
261 : //
262 : // snap::NOTREACHED();
263 : //}
264 : //
265 : //
266 : ///** \brief Check wether a string represents an IPv6 address.
267 : // *
268 : // * This function quickly checks whether the specified string defines a
269 : // * valid IPv6 address. It supports the IPv4 notation at times used
270 : // * inside an IPv6 notation.
271 : // *
272 : // * \note
273 : // * The function can be called with a null pointer in which case it
274 : // * immediate returns false.
275 : // *
276 : // * \param[in] ip A pointer to a string holding an address.
277 : // *
278 : // * \return true if the \p ip string represents an IPv6 address.
279 : // */
280 : //bool is_ipv6(char const * ip)
281 : //{
282 : // if(ip == nullptr)
283 : // {
284 : // return false;
285 : // }
286 : //
287 : // // an IPv6 is a set of 16 bit numbers separated by colon
288 : // // the last two numbers can represented in dot notation (ipv4 class a)
289 : // //
290 : // bool found_colon_colon(false);
291 : // int count(0);
292 : // if(*ip == ':'
293 : // && ip[1] == ':')
294 : // {
295 : // found_colon_colon = true;
296 : // ip += 2;
297 : // }
298 : // for(; *ip != '\0'; ++ip)
299 : // {
300 : // if(count >= 8)
301 : // {
302 : // return false;
303 : // }
304 : //
305 : // // all numbers are in hexadecimal
306 : // int value(0);
307 : // bool first(true);
308 : // for(;; ++ip, first = false)
309 : // {
310 : // if(*ip >= '0' && *ip <= '9')
311 : // {
312 : // value = value * 16 + *ip - '0';
313 : // }
314 : // else if(*ip >= 'a' && *ip <= 'f')
315 : // {
316 : // value = value * 16 + *ip - 'a' + 10;
317 : // }
318 : // else if(*ip >= 'A' && *ip <= 'F')
319 : // {
320 : // value = value * 16 + *ip - 'A' + 10;
321 : // }
322 : // else
323 : // {
324 : // if(first)
325 : // {
326 : // // not even one digit, not good
327 : // return false;
328 : // }
329 : // // not valid hexadecimal, may be ':' or '\0' (tested below)
330 : // break;
331 : // }
332 : // if(value >= 0x10000)
333 : // {
334 : // // too large, must be 16 bit numbers
335 : // return false;
336 : // }
337 : // }
338 : // ++count;
339 : ////std::cerr << count << ". value=" << value << " -> " << static_cast<int>(*ip) << "\n";
340 : // if(*ip == '\0')
341 : // {
342 : // break;
343 : // }
344 : //
345 : // // note: if we just found a '::' then here *ip == ':' still
346 : // if(*ip == '.')
347 : // {
348 : // // if we have a '.' we must end with an IPv4 and we either
349 : // // need found_colon_colon to be true or the count must be
350 : // // exactly 6 (1 "missing" colon)
351 : // //
352 : // if(!found_colon_colon
353 : // && count != 7) // we test with 7 because the first IPv4 number was already read
354 : // {
355 : // return false;
356 : // }
357 : // // also the value is 0 to 255 or it's an error too, but the
358 : // // problem here is that we need a decimal number and we just
359 : // // checked it as an hexadecimal...
360 : // //
361 : // if((value & 0x00f) >= 0x00a
362 : // || (value & 0x0f0) >= 0x0a0
363 : // || (value & 0xf00) >= 0xa00)
364 : // {
365 : // return false;
366 : // }
367 : // // transform back to a decimal number to verify the max.
368 : // //
369 : // value = (value & 0x00f) + (value & 0x0f0) / 16 * 10 + (value & 0xf00) / 256 * 100;
370 : // if(value > 255)
371 : // {
372 : // return false;
373 : // }
374 : // // now check the other numbers
375 : // int pos(1); // start at 1 since we already have 1 number checked
376 : // for(++ip; *ip != '\0'; ++ip, ++pos)
377 : // {
378 : // if(*ip < '0' || *ip > '9' || pos >= 4)
379 : // {
380 : // // not a valid number
381 : // return false;
382 : // }
383 : //
384 : // // only expect decimal in this case in class d (a.b.c.d)
385 : // value = 0;
386 : // for(; *ip >= '0' && *ip <= '9'; ++ip)
387 : // {
388 : // value = value * 10 + *ip - '0';
389 : // if(value > 255)
390 : // {
391 : // // too large
392 : // return false;
393 : // }
394 : // }
395 : //
396 : // if(*ip != '.')
397 : // {
398 : // if(*ip != '\0')
399 : // {
400 : // return false;
401 : // }
402 : // break;
403 : // }
404 : // }
405 : //
406 : // // we got a valid IPv4 at the end of IPv6 and we
407 : // // found the '\0' so we are all good...
408 : // //
409 : // return true;
410 : // }
411 : //
412 : // if(*ip != ':')
413 : // {
414 : // return false;
415 : // }
416 : //
417 : // // double colon?
418 : // if(ip[1] == ':')
419 : // {
420 : //#pragma GCC diagnostic push
421 : //#pragma GCC diagnostic ignored "-Wstrict-overflow"
422 : // if(!found_colon_colon && count < 6)
423 : //#pragma GCC diagnostic pop
424 : // {
425 : // // we can accept one '::'
426 : // ++ip;
427 : // found_colon_colon = true;
428 : // }
429 : // else
430 : // {
431 : // // a second :: is not valid for an IPv6
432 : // return false;
433 : // }
434 : // }
435 : // }
436 : //
437 : // return count == 8 || (count >= 1 && found_colon_colon);
438 : //}
439 : //
440 : //
441 : ///** \brief Retrieve an address and a port from a string.
442 : // *
443 : // * This function breaks up an address and a port number from a string.
444 : // *
445 : // * The address can either be an IPv4 address followed by a colon and
446 : // * the port number, or an IPv6 address written between square brackets
447 : // * ([::1]) followed by a colon and the port number. We also support
448 : // * just a port specification as in ":4040".
449 : // *
450 : // * Port numbers are limited to a number between 1 and 65535 inclusive.
451 : // * They can only be specified in base 10.
452 : // *
453 : // * The port is optional only if a \p default_port is provided (by
454 : // * default the \p default_port parameter is set to zero meaning that
455 : // * it is not specified.)
456 : // *
457 : // * If the addr_port string is empty, then the addr and port parameters
458 : // * are not modified, which means you want to define them with defaults
459 : // * before calling this function.
460 : // *
461 : // * \exception snapwebsites_exception_invalid_parameters
462 : // * If any parameter is considered invalid (albeit the validity of the
463 : // * address is not checked since it could be a fully qualified domain
464 : // * name) then this exception is raised.
465 : // *
466 : // * \param[in] addr_port The address and port pair.
467 : // * \param[in,out] addr The address part, without the square brackets for
468 : // * IPv6 addresses.
469 : // * \param[in,out] port The port number (1 to 65535 inclusive.)
470 : // * \param[in] protocol The protocol for the port (i.e. "tcp" or "udp")
471 : // */
472 : //void get_addr_port(QString const & addr_port, QString & addr, int & port, char const * protocol)
473 : //{
474 : // // if there is a colon, we may have a port or IPv6
475 : // //
476 : // int const p(addr_port.lastIndexOf(":"));
477 : // if(p != -1)
478 : // {
479 : // QString port_str;
480 : //
481 : // // if there is a ']' then we have an IPv6
482 : // //
483 : // int const bracket(addr_port.lastIndexOf("]"));
484 : // if(bracket != -1)
485 : // {
486 : // // we must have a starting '[' otherwise it is wrong
487 : // //
488 : // if(addr_port[0] != '[')
489 : // {
490 : // SNAP_LOG_FATAL("invalid address/port specification in \"")(addr_port)("\" (missing '[' at the start.)");
491 : // throw tcp_client_server_parameter_error("get_addr_port(): invalid [IPv6]:port specification, '[' missing.");
492 : // }
493 : //
494 : // // extract the address
495 : // //
496 : // addr = addr_port.mid(1, bracket - 1); // exclude the '[' and ']'
497 : //
498 : // // is there a port?
499 : // //
500 : // if(p == bracket + 1)
501 : // {
502 : // // IPv6 port specification is just after the ']'
503 : // //
504 : // port_str = addr_port.mid(p + 1); // ignore the ':'
505 : // }
506 : // else if(bracket != addr_port.length() - 1)
507 : // {
508 : // // the ']' is not at the very end when no port specified
509 : // //
510 : // SNAP_LOG_FATAL("invalid address/port specification in \"")(addr_port)("\" (']' is not at the end)");
511 : // throw tcp_client_server_parameter_error("get_addr_port(): invalid [IPv6]:port specification, ']' not at the end.");
512 : // }
513 : // }
514 : // else
515 : // {
516 : // // IPv4 port specification
517 : // //
518 : // if(p > 0)
519 : // {
520 : // // if p is zero, then we just had a port (:4040)
521 : // //
522 : // addr = addr_port.mid(0, p); // ignore the ':'
523 : // }
524 : // port_str = addr_port.mid(p + 1); // ignore the ':'
525 : // }
526 : //
527 : // // if port_str is still empty, we had an IPv6 without port
528 : // //
529 : // if(!port_str.isEmpty())
530 : // {
531 : // // first check whether the port is a number
532 : // //
533 : // bool ok(false);
534 : // port = port_str.toInt(&ok, 10); // force base 10
535 : // if(!ok)
536 : // {
537 : // // not a valid number, try to get it from /etc/services
538 : // //
539 : // struct servent const * s(getservbyname(port_str.toUtf8().data(), protocol));
540 : // if(s == nullptr)
541 : // {
542 : // SNAP_LOG_FATAL("invalid port specification in \"")(addr_port)("\", port not a decimal number nor a known service name.");
543 : // throw tcp_client_server_parameter_error("get_addr_port(): invalid addr:port specification, port number or name is not valid.");
544 : // }
545 : // port = s->s_port;
546 : // }
547 : // }
548 : // }
549 : // else if(!addr_port.isEmpty())
550 : // {
551 : // // just an IPv4 address specified, no port
552 : // //
553 : // addr = addr_port;
554 : // }
555 : //
556 : // // the address could end up being the empty string here
557 : // if(addr.isEmpty())
558 : // {
559 : // SNAP_LOG_FATAL("invalid address/port specification in \"")(addr_port)("\", address is empty.");
560 : // throw tcp_client_server_parameter_error("get_addr_port(): invalid addr:port specification, address is empty (this generally happens when a request is done with no default address).");
561 : // }
562 : //
563 : // // finally verify that the port is in range
564 : // if(port <= 0
565 : // || port > 65535)
566 : // {
567 : // SNAP_LOG_FATAL("invalid address/port specification in \"")(addr_port)("\", port out of bounds.");
568 : // throw tcp_client_server_parameter_error("get_addr_port(): invalid addr:port specification, port number is out of bounds (1 .. 65535).");
569 : // }
570 : //}
571 :
572 :
573 6 : } // namespace ed
574 : // vim: ts=4 sw=4 et
|