comparison common/sockets.cpp @ 0:1158cffe5a5e

Initial import
author David Demelier <markand@malikania.fr>
date Mon, 08 Feb 2016 16:43:14 +0100
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:1158cffe5a5e
1 /*
2 * sockets.cpp -- portable C++ socket wrappers
3 *
4 * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #define TIMEOUT_MSG "operation timeout"
20
21 #include <algorithm>
22 #include <atomic>
23 #include <cstring>
24 #include <mutex>
25
26 #include "sockets.h"
27
28 namespace irccd {
29
30 namespace net {
31
32 /*
33 * Portable constants
34 * ------------------------------------------------------------------
35 */
36
37 /* {{{ Constants */
38
39 #if defined(_WIN32)
40
41 const Handle Invalid{INVALID_SOCKET};
42 const int Failure{SOCKET_ERROR};
43
44 #else
45
46 const Handle Invalid{-1};
47 const int Failure{-1};
48
49 #endif
50
51 /* }}} */
52
53 /*
54 * Portable functions
55 * ------------------------------------------------------------------
56 */
57
58 /* {{{ Functions */
59
60 #if defined(_WIN32)
61
62 namespace {
63
64 static std::mutex s_mutex;
65 static std::atomic<bool> s_initialized{false};
66
67 } // !namespace
68
69 #endif // !_WIN32
70
71 void init() noexcept
72 {
73 #if defined(_WIN32)
74 std::lock_guard<std::mutex> lock(s_mutex);
75
76 if (!s_initialized) {
77 s_initialized = true;
78
79 WSADATA wsa;
80 WSAStartup(MAKEWORD(2, 2), &wsa);
81
82 /*
83 * If SOCKET_WSA_NO_INIT is not set then the user
84 * must also call finish himself.
85 */
86 #if !defined(SOCKET_NO_AUTO_INIT)
87 atexit(finish);
88 #endif
89 }
90 #endif
91 }
92
93 void finish() noexcept
94 {
95 #if defined(_WIN32)
96 WSACleanup();
97 #endif
98 }
99
100 std::string error(int errn)
101 {
102 #if defined(_WIN32)
103 LPSTR str = nullptr;
104 std::string errmsg = "Unknown error";
105
106 FormatMessageA(
107 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
108 NULL,
109 errn,
110 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
111 (LPSTR)&str, 0, NULL);
112
113
114 if (str) {
115 errmsg = std::string(str);
116 LocalFree(str);
117 }
118
119 return errmsg;
120 #else
121 return strerror(errn);
122 #endif
123 }
124
125 std::string error()
126 {
127 #if defined(_WIN32)
128 return error(WSAGetLastError());
129 #else
130 return error(errno);
131 #endif
132 }
133
134 /* }}} */
135
136 /*
137 * SSL stuff
138 * ------------------------------------------------------------------
139 */
140
141 /* {{{ SSL initialization */
142
143 #if !defined(SOCKET_NO_SSL)
144
145 namespace ssl {
146
147 namespace {
148
149 std::mutex mutex;
150 std::atomic<bool> initialized{false};
151
152 } // !namespace
153
154 void finish() noexcept
155 {
156 ERR_free_strings();
157 }
158
159 void init() noexcept
160 {
161 std::lock_guard<std::mutex> lock{mutex};
162
163 if (!initialized) {
164 initialized = true;
165
166 SSL_library_init();
167 SSL_load_error_strings();
168 OpenSSL_add_all_algorithms();
169
170 #if !defined(SOCKET_NO_AUTO_SSL_INIT)
171 atexit(finish);
172 #endif // SOCKET_NO_AUTO_SSL_INIT
173 }
174 }
175
176 } // !ssl
177
178 #endif // SOCKET_NO_SSL
179
180 /* }}} */
181
182 /*
183 * Error class
184 * ------------------------------------------------------------------
185 */
186
187 /* {{{ Error */
188
189 Error::Error(Code code, std::string function)
190 : m_code{code}
191 , m_function{std::move(function)}
192 , m_error{error()}
193 {
194 }
195
196 Error::Error(Code code, std::string function, int n)
197 : m_code{code}
198 , m_function{std::move(function)}
199 , m_error{error(n)}
200 {
201 }
202
203 Error::Error(Code code, std::string function, std::string error)
204 : m_code{code}
205 , m_function{std::move(function)}
206 , m_error{std::move(error)}
207 {
208 }
209
210 /* }}} */
211
212 /*
213 * Predefine addressed to be used
214 * ------------------------------------------------------------------
215 */
216
217 /* {{{ Addresses */
218
219 namespace address {
220
221 /* Default domain */
222 int Ip::m_default{AF_INET};
223
224 Ip::Ip(Type domain) noexcept
225 : m_domain(static_cast<int>(domain))
226 {
227 assert(m_domain == AF_INET6 || m_domain == AF_INET);
228
229 if (m_domain == AF_INET6) {
230 std::memset(&m_sin6, 0, sizeof (sockaddr_in6));
231 } else {
232 std::memset(&m_sin, 0, sizeof (sockaddr_in));
233 }
234 }
235
236 Ip::Ip(const std::string &host, int port, Type domain)
237 : m_domain(static_cast<int>(domain))
238 {
239 assert(m_domain == AF_INET6 || m_domain == AF_INET);
240
241 if (host == "*") {
242 if (m_domain == AF_INET6) {
243 std::memset(&m_sin6, 0, sizeof (sockaddr_in6));
244
245 m_length = sizeof (sockaddr_in6);
246 m_sin6.sin6_addr = in6addr_any;
247 m_sin6.sin6_family = AF_INET6;
248 m_sin6.sin6_port = htons(port);
249 } else {
250 std::memset(&m_sin, 0, sizeof (sockaddr_in));
251
252 m_length = sizeof (sockaddr_in);
253 m_sin.sin_addr.s_addr = INADDR_ANY;
254 m_sin.sin_family = AF_INET;
255 m_sin.sin_port = htons(port);
256 }
257 } else {
258 addrinfo hints, *res;
259
260 std::memset(&hints, 0, sizeof (addrinfo));
261 hints.ai_family = domain;
262
263 auto error = getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, &res);
264 if (error != 0) {
265 throw Error{Error::System, "getaddrinfo", gai_strerror(error)};
266 }
267
268 if (m_domain == AF_INET6) {
269 std::memcpy(&m_sin6, res->ai_addr, res->ai_addrlen);
270 } else {
271 std::memcpy(&m_sin, res->ai_addr, res->ai_addrlen);
272 }
273
274 m_length = res->ai_addrlen;
275 freeaddrinfo(res);
276 }
277 }
278
279 Ip::Ip(const sockaddr_storage *ss, socklen_t length) noexcept
280 : m_length{length}
281 , m_domain{ss->ss_family}
282 {
283 assert(ss->ss_family == AF_INET6 || ss->ss_family == AF_INET);
284
285 if (ss->ss_family == AF_INET6) {
286 std::memcpy(&m_sin6, ss, length);
287 } else if (ss->ss_family == AF_INET) {
288 std::memcpy(&m_sin, ss, length);
289 }
290 }
291
292 #if !defined(_WIN32)
293
294 Local::Local() noexcept
295 {
296 std::memset(&m_sun, 0, sizeof (sockaddr_un));
297 }
298
299 Local::Local(std::string path, bool rm) noexcept
300 : m_path{std::move(path)}
301 {
302 /* Silently remove the file even if it fails */
303 if (rm) {
304 ::remove(m_path.c_str());
305 }
306
307 /* Copy the path */
308 std::memset(m_sun.sun_path, 0, sizeof (m_sun.sun_path));
309 std::strncpy(m_sun.sun_path, m_path.c_str(), sizeof (m_sun.sun_path) - 1);
310
311 /* Set the parameters */
312 m_sun.sun_family = AF_LOCAL;
313 }
314
315 Local::Local(const sockaddr_storage *ss, socklen_t length) noexcept
316 {
317 assert(ss->ss_family == AF_LOCAL);
318
319 if (ss->ss_family == AF_LOCAL) {
320 std::memcpy(&m_sun, ss, length);
321 m_path = reinterpret_cast<const sockaddr_un &>(m_sun).sun_path;
322 }
323 }
324
325 #endif // !_WIN32
326
327 } // !address
328
329 /* }}} */
330
331 /*
332 * Select
333 * ------------------------------------------------------------------
334 */
335
336 /* {{{ Select */
337
338 std::vector<ListenerStatus> Select::wait(const ListenerTable &table, int ms)
339 {
340 timeval maxwait, *towait;
341 fd_set readset;
342 fd_set writeset;
343
344 FD_ZERO(&readset);
345 FD_ZERO(&writeset);
346
347 Handle max = 0;
348
349 for (const auto &pair : table) {
350 if ((pair.second & Condition::Readable) == Condition::Readable) {
351 FD_SET(pair.first, &readset);
352 }
353 if ((pair.second & Condition::Writable) == Condition::Writable) {
354 FD_SET(pair.first, &writeset);
355 }
356
357 if (pair.first > max) {
358 max = pair.first;
359 }
360 }
361
362 maxwait.tv_sec = 0;
363 maxwait.tv_usec = ms * 1000;
364
365 // Set to nullptr for infinite timeout.
366 towait = (ms < 0) ? nullptr : &maxwait;
367
368 auto error = ::select(max + 1, &readset, &writeset, nullptr, towait);
369 if (error == Failure) {
370 throw Error{Error::System, "select"};
371 }
372 if (error == 0) {
373 throw Error{Error::Timeout, "select", TIMEOUT_MSG};
374 }
375
376 std::vector<ListenerStatus> sockets;
377
378 for (const auto &pair : table) {
379 if (FD_ISSET(pair.first, &readset)) {
380 sockets.push_back(ListenerStatus{pair.first, Condition::Readable});
381 }
382 if (FD_ISSET(pair.first, &writeset)) {
383 sockets.push_back(ListenerStatus{pair.first, Condition::Writable});
384 }
385 }
386
387 return sockets;
388 }
389
390 /* }}} */
391
392 /*
393 * Poll
394 * ------------------------------------------------------------------
395 */
396
397 /* {{{ Poll */
398
399 /*
400 * Poll implementation
401 * ------------------------------------------------------------------
402 */
403
404 #if defined(SOCKET_HAVE_POLL)
405
406 #if defined(_WIN32)
407 # define poll WSAPoll
408 #endif
409
410 short Poll::toPoll(Condition condition) const noexcept
411 {
412 short result(0);
413
414 if ((condition & Condition::Readable) == Condition::Readable) {
415 result |= POLLIN;
416 }
417 if ((condition & Condition::Writable) == Condition::Writable) {
418 result |= POLLOUT;
419 }
420
421 return result;
422 }
423
424 Condition Poll::toCondition(short &event) const noexcept
425 {
426 Condition condition{Condition::None};
427
428 /*
429 * Poll implementations mark the socket differently regarding
430 * the disconnection of a socket.
431 *
432 * At least, even if POLLHUP or POLLIN is set, recv() always
433 * return 0 so we mark the socket as readable.
434 */
435 if ((event & POLLIN) || (event & POLLHUP)) {
436 condition |= Condition::Readable;
437 }
438 if (event & POLLOUT) {
439 condition |= Condition::Writable;
440 }
441
442 /* Reset event for safety */
443 event = 0;
444
445 return condition;
446 }
447
448 void Poll::set(const ListenerTable &, Handle h, Condition condition, bool add)
449 {
450 if (add) {
451 m_fds.push_back(pollfd{h, toPoll(condition), 0});
452 } else {
453 auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const pollfd &pfd) {
454 return pfd.fd == h;
455 });
456
457 it->events |= toPoll(condition);
458 }
459 }
460
461 void Poll::unset(const ListenerTable &, Handle h, Condition condition, bool remove)
462 {
463 auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const pollfd &pfd) {
464 return pfd.fd == h;
465 });
466
467 if (remove) {
468 m_fds.erase(it);
469 } else {
470 it->events &= ~(toPoll(condition));
471 }
472 }
473
474 std::vector<ListenerStatus> Poll::wait(const ListenerTable &, int ms)
475 {
476 auto result = poll(m_fds.data(), m_fds.size(), ms);
477 if (result == 0) {
478 throw Error{Error::Timeout, "select", TIMEOUT_MSG};
479 }
480 if (result < 0) {
481 throw Error{Error::System, "poll"};
482 }
483
484 std::vector<ListenerStatus> sockets;
485 for (auto &fd : m_fds) {
486 if (fd.revents != 0) {
487 sockets.push_back(ListenerStatus{fd.fd, toCondition(fd.revents)});
488 }
489 }
490
491 return sockets;
492 }
493
494 #endif // !SOCKET_HAVE_POLL
495
496 /* }}} */
497
498 /*
499 * Epoll implementation
500 * ------------------------------------------------------------------
501 */
502
503 /* {{{ Epoll */
504
505 #if defined(SOCKET_HAVE_EPOLL)
506
507 uint32_t Epoll::toEpoll(Condition condition) const noexcept
508 {
509 uint32_t events = 0;
510
511 if ((condition & Condition::Readable) == Condition::Readable) {
512 events |= EPOLLIN;
513 }
514 if ((condition & Condition::Writable) == Condition::Writable) {
515 events |= EPOLLOUT;
516 }
517
518 return events;
519 }
520
521 Condition Epoll::toCondition(uint32_t events) const noexcept
522 {
523 Condition condition{Condition::None};
524
525 if ((events & EPOLLIN) || (events & EPOLLHUP)) {
526 condition |= Condition::Readable;
527 }
528 if (events & EPOLLOUT) {
529 condition |= Condition::Writable;
530 }
531
532 return condition;
533 }
534
535 void Epoll::update(Handle h, int op, int eflags)
536 {
537 epoll_event ev;
538
539 std::memset(&ev, 0, sizeof (epoll_event));
540
541 ev.events = eflags;
542 ev.data.fd = h;
543
544 if (epoll_ctl(m_handle, op, h, &ev) < 0) {
545 throw Error{Error::System, "epoll_ctl"};
546 }
547 }
548
549 Epoll::Epoll()
550 : m_handle{epoll_create1(0)}
551 {
552 if (m_handle < 0) {
553 throw Error{Error::System, "epoll_create"};
554 }
555 }
556
557 Epoll::~Epoll()
558 {
559 close(m_handle);
560 }
561
562 /*
563 * For set and unset, we need to apply the whole flags required, so if the socket
564 * was set to Connection::Readable and user add Connection::Writable, we must
565 * place both.
566 */
567 void Epoll::set(const ListenerTable &table, Handle sc, Condition condition, bool add)
568 {
569 if (add) {
570 update(sc, EPOLL_CTL_ADD, toEpoll(condition));
571 m_events.resize(m_events.size() + 1);
572 } else {
573 update(sc, EPOLL_CTL_MOD, toEpoll(table.at(sc) | condition));
574 }
575 }
576
577 /*
578 * Unset is a bit complicated case because Listener tells us which
579 * flag to remove but to update epoll descriptor we need to pass
580 * the effective flags that we want to be applied.
581 *
582 * So we put the same flags that are currently effective and remove the
583 * requested one.
584 */
585 void Epoll::unset(const ListenerTable &table, Handle sc, Condition condition, bool remove)
586 {
587 if (remove) {
588 update(sc, EPOLL_CTL_DEL, 0);
589 m_events.resize(m_events.size() - 1);
590 } else {
591 update(sc, EPOLL_CTL_MOD, toEpoll(table.at(sc) & ~(condition)));
592 }
593 }
594
595 std::vector<ListenerStatus> Epoll::wait(const ListenerTable &, int ms)
596 {
597 int ret = epoll_wait(m_handle, m_events.data(), m_events.size(), ms);
598 std::vector<ListenerStatus> result;
599
600 if (ret == 0) {
601 throw Error{Error::Timeout, "epoll_wait", TIMEOUT_MSG};
602 }
603 if (ret < 0) {
604 throw Error{Error::System, "epoll_wait"};
605 }
606
607 for (int i = 0; i < ret; ++i) {
608 result.push_back(ListenerStatus{m_events[i].data.fd, toCondition(m_events[i].events)});
609 }
610
611 return result;
612 }
613
614 #endif // !SOCKET_HAVE_EPOLL
615
616 /* }}} */
617
618 /*
619 * Kqueue implementation
620 * ------------------------------------------------------------------
621 */
622
623 /* {{{ Kqueue */
624
625 #if defined(SOCKET_HAVE_KQUEUE)
626
627 Kqueue::Kqueue()
628 : m_handle(kqueue())
629 {
630 if (m_handle < 0) {
631 throw Error{Error::System, "kqueue"};
632 }
633 }
634
635 Kqueue::~Kqueue()
636 {
637 close(m_handle);
638 }
639
640 void Kqueue::update(Handle h, int filter, int kflags)
641 {
642 struct kevent ev;
643
644 EV_SET(&ev, h, filter, kflags, 0, 0, nullptr);
645
646 if (kevent(m_handle, &ev, 1, nullptr, 0, nullptr) < 0) {
647 throw Error{Error::System, "kevent"};
648 }
649 }
650
651 void Kqueue::set(const ListenerTable &, Handle h, Condition condition, bool add)
652 {
653 if ((condition & Condition::Readable) == Condition::Readable) {
654 update(h, EVFILT_READ, EV_ADD | EV_ENABLE);
655 }
656 if ((condition & Condition::Writable) == Condition::Writable) {
657 update(h, EVFILT_WRITE, EV_ADD | EV_ENABLE);
658 }
659
660 if (add) {
661 m_result.resize(m_result.size() + 1);
662 }
663 }
664
665 void Kqueue::unset(const ListenerTable &, Handle h, Condition condition, bool remove)
666 {
667 if ((condition & Condition::Readable) == Condition::Readable) {
668 update(h, EVFILT_READ, EV_DELETE);
669 }
670 if ((condition & Condition::Writable) == Condition::Writable) {
671 update(h, EVFILT_WRITE, EV_DELETE);
672 }
673
674 if (remove) {
675 m_result.resize(m_result.size() - 1);
676 }
677 }
678
679 std::vector<ListenerStatus> Kqueue::wait(const ListenerTable &, int ms)
680 {
681 std::vector<ListenerStatus> sockets;
682 timespec ts = { 0, 0 };
683 timespec *pts = (ms <= 0) ? nullptr : &ts;
684
685 ts.tv_sec = ms / 1000;
686 ts.tv_nsec = (ms % 1000) * 1000000;
687
688 int nevents = kevent(m_handle, nullptr, 0, &m_result[0], m_result.capacity(), pts);
689
690 if (nevents == 0) {
691 throw Error{Error::Timeout, "kevent", TIMEOUT_MSG};
692 }
693 if (nevents < 0) {
694 throw Error{Error::System, "kevent"};
695 }
696
697 for (int i = 0; i < nevents; ++i) {
698 sockets.push_back(ListenerStatus{
699 static_cast<Handle>(m_result[i].ident),
700 m_result[i].filter == EVFILT_READ ? Condition::Readable : Condition::Writable
701 });
702 }
703
704 return sockets;
705 }
706
707 #endif // !SOCKET_HAVE_KQUEUE
708
709 /* }}} */
710
711 } // !net
712
713 } // !irccd