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