Fawkes API Fawkes Development Version
socket.cpp
1
2/***************************************************************************
3 * socket.h - Fawkes socket base class
4 *
5 * Created: Thu Nov 09 14:30:56 2006
6 * Copyright 2006 Tim Niemueller [www.niemueller.de]
7 *
8 ****************************************************************************/
9
10/* This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version. A runtime exception applies to
14 * this software (see LICENSE.GPL_WRE file mentioned below for details).
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Library General Public License for more details.
20 *
21 * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
22 */
23
24#include <core/exceptions/system.h>
25#include <netcomm/socket/socket.h>
26#include <utils/misc/string_conversions.h>
27#include <utils/time/time.h>
28
29#ifndef _GNU_SOURCE
30# define _GNU_SOURCE
31#endif
32
33#include <sys/socket.h>
34#include <sys/types.h>
35
36#include <cstdlib>
37#include <cstring>
38#include <errno.h>
39#include <fcntl.h>
40#include <netdb.h>
41#include <string>
42#include <unistd.h>
43// include <linux/in.h>
44#include <arpa/inet.h>
45#include <netinet/in.h>
46#include <netinet/in_systm.h>
47#include <netinet/ip.h>
48
49#include <cstdio>
50#include <poll.h>
51
52// Until this is integrated from linux/in.h to netinet/in.h
53#ifdef __linux__
54# ifndef IP_MTU
55# define IP_MTU 14
56# endif
57#endif // __linux__
58#ifdef __FreeBSD__
59# include <net/if.h>
60# include <sys/ioctl.h>
61#endif
62
63namespace fawkes {
64
65/** @class SocketException netcomm/socket/socket.h
66 * Socket exception.
67 * Thrown if an exception occurs in a socket. If the error was caused by
68 * a system call that sets errno this is given to the exception.
69 * @ingroup NetComm
70 */
71
72/** Constructor.
73 * @param format format for reason that caused the exception.
74 */
75SocketException::SocketException(const char *format, ...) : Exception()
76{
77 va_list va;
78 va_start(va, format);
79 append_va(format, va);
80 va_end(va);
81}
82
83/** Constructor.
84 * @param msg reason of the exception
85 * @param _errno error number (errno returned by a syscall)
86 */
87SocketException::SocketException(int _errno, const char *msg) : Exception(_errno, "%s", msg)
88{
89}
90
91/** @class Socket netcomm/socket/socket.h
92 * Socket base class.
93 * This is the base class for all sockets. You cannot use it directly
94 * but you have to use one of the derivatives like StreamSocket or
95 * DatagramSocket.
96 * @ingroup NetComm
97 * @author Tim Niemueller
98 */
99
100/** @var Socket::addr_type
101 * Address type/family of socket.
102 */
103/** @var Socket::sock_fd
104 * Socket file descriptor.
105 */
106/** @var Socket::timeout
107 * Timeout in seconds for various operations. If the timeout is non-zero
108 * the socket is initialized non-blocking and operations are aborted after
109 * timeout seconds have passed.
110 */
111/** @var Socket::client_addr
112 * Client address, set if connected.
113 */
114/** @var Socket::client_addr_len
115 * length in bytes of client address.
116 */
117
118/** Data can be read. */
119const short Socket::POLL_IN = POLLIN;
120
121/** Writing will not block. */
122const short Socket::POLL_OUT = POLLOUT;
123
124/** There is urgent data to read (e.g., out-of-band data on TCP socket;
125 * pseudo-terminal master in packet mode has seen state change in slave).
126 */
127const short Socket::POLL_PRI = POLLPRI;
128
129/** Stream socket peer closed connection, or shut down writing half of
130 * connection. The _GNU_SOURCE feature test macro must be defined
131 * in order to obtain this definition (since Linux 2.6.17).
132 */
133#ifdef POLLRDHUP
134const short Socket::POLL_RDHUP = POLLRDHUP;
135#else
136const short Socket::POLL_RDHUP = 0;
137#endif
138
139/** Error condition. */
140const short Socket::POLL_ERR = POLLERR;
141
142/** Hang up. */
143const short Socket::POLL_HUP = POLLHUP;
144
145/** Invalid request. */
146const short Socket::POLL_NVAL = POLLNVAL;
147
148/** Constructor similar to syscall.
149 * This creates a new socket. This is a plain pass-through constructor
150 * to the socket() syscall. In most cases this should only be used by
151 * a derivate.
152 * @param addr_type Specify IPv4 or IPv6
153 * @param sock_type socket type, either TCP or UDP
154 * @param timeout See Socket::timeout.
155 * @exception SocketException thrown if socket cannot be opened, check errno for cause
156 */
157Socket::Socket(AddrType addr_type, SocketType sock_type, float timeout)
158: addr_type(addr_type),
159 sock_fd(-1),
160 timeout(timeout),
161 client_addr(NULL),
162 client_addr_len(0),
163 socket_protocol_(0)
164{
165 if (addr_type == IPv4) {
166 socket_addr_family_ = AF_INET;
167 } else if (addr_type == IPv6) {
168 socket_addr_family_ = AF_INET6;
169 } else {
170 throw SocketException("Unknown address type");
171 }
172 if (sock_type == TCP) {
173 socket_type_ = SOCK_STREAM;
174 } else if (sock_type == UDP) {
175 socket_type_ = SOCK_DGRAM;
176 } else {
177 throw SocketException("Unknown socket type");
178 }
179}
180
181/** IPv4 Constructor.
182 * This creates a new socket. This is a plain pass-through constructor
183 * to the socket() syscall. In most cases this should only be used by
184 * a derivate.
185 * @param sock_type socket type, either TCP or UDP
186 * @param timeout See Socket::timeout.
187 * @exception SocketException thrown if socket cannot be opened, check errno for cause
188 */
189Socket::Socket(SocketType sock_type, float timeout)
190: sock_fd(-1),
191 timeout(timeout),
192 client_addr(NULL),
193 client_addr_len(0),
194 socket_addr_family_(-1),
195 socket_protocol_(0)
196{
197 if (sock_type == TCP) {
198 socket_type_ = SOCK_STREAM;
199 } else if (sock_type == UDP) {
200 socket_type_ = SOCK_DGRAM;
201 } else {
202 throw SocketException("Unknown socket type");
203 }
204}
205
206/** Constructor.
207 * Plain constructor. The socket will not be opened. This may only be called by
208 * sub-classes and you must ensure that the socket file descriptor is initialized
209 * properly.
210 */
212: sock_fd(-1),
213 timeout(0.f),
214 client_addr(NULL),
215 client_addr_len(0),
216 socket_addr_family_(0),
217 socket_type_(0),
218 socket_protocol_(0)
219{
220}
221
222/** Copy constructor.
223 * @param socket socket to copy
224 */
226{
227 if (socket.client_addr != NULL) {
228 if (socket.client_addr_len > sizeof(struct ::sockaddr_storage)) {
229 throw SocketException("Invalid client socket address length");
230 }
231 client_addr = (struct ::sockaddr_storage *)malloc(sizeof(struct ::sockaddr_storage));
232 client_addr_len = sizeof(struct ::sockaddr_storage);
233 memcpy(client_addr, socket.client_addr, client_addr_len);
234 } else {
235 client_addr = NULL;
236 client_addr_len = 0;
237 }
238 timeout = socket.timeout;
239 sock_fd = dup(socket.sock_fd);
240 socket_addr_family_ = socket.socket_addr_family_;
241 socket_type_ = socket.socket_type_;
242 socket_protocol_ = socket.socket_protocol_;
243}
244
245/** Copy constructor.
246 * @param socket socket to copy
247 * @return reference to this instance
248 */
249Socket &
251{
252 close();
253 if (client_addr != NULL) {
254 free(client_addr);
255 client_addr = NULL;
256 }
257
258 if (socket.client_addr != NULL) {
259 if (socket.client_addr_len > sizeof(struct ::sockaddr_storage)) {
260 throw SocketException("Invalid client socket address length");
261 }
262 client_addr = (struct ::sockaddr_storage *)malloc(sizeof(struct ::sockaddr_storage));
263 client_addr_len = sizeof(struct ::sockaddr_storage);
264 memcpy(client_addr, socket.client_addr, client_addr_len);
265 } else {
266 client_addr = NULL;
267 client_addr_len = 0;
268 }
269 timeout = socket.timeout;
270 sock_fd = dup(socket.sock_fd);
271 socket_addr_family_ = socket.socket_addr_family_;
272 socket_type_ = socket.socket_type_;
273 socket_protocol_ = socket.socket_protocol_;
274
275 return *this;
276}
277
278void
279Socket::create()
280{
281 if (sock_fd != -1)
282 return;
283 if (socket_addr_family_ == -1) {
284 throw UnknownTypeException("Invalid socket address family, wrong constructor called?");
285 }
286
287 if ((sock_fd = socket(socket_addr_family_, socket_type_, socket_protocol_)) == -1) {
288 throw SocketException(errno, "Could not open socket");
289 }
290
291 if (timeout > 0.f) {
292 // set to non-blocking
293 if (fcntl(sock_fd, F_SETFL, O_NONBLOCK) == -1) {
294 throw SocketException(errno, "Could not set socket to non-blocking");
295 }
296 }
297}
298
299/** Destructor. */
301{
302 close();
303 if (client_addr != NULL) {
304 free(client_addr);
305 client_addr = NULL;
306 }
307}
308
309/** Close socket. */
310void
312{
313 if (sock_fd != -1) {
315 sock_fd = -1;
316 }
317}
318
319/** Connect socket.
320 * If called for a stream socket this will connect to the remote address. If
321 * you call this on a datagram socket you will tune in to a specific sender and
322 * receiver.
323 * @param addr_port struct containing address and port to connect to
324 * @exception SocketException thrown if socket cannot connect, check errno for cause
325 */
326void
327Socket::connect(const struct ::sockaddr_storage &addr_port)
328{
329 connect((const struct sockaddr *)&addr_port, sizeof(::sockaddr_storage));
330}
331
332/** Connect socket.
333 * If called for a stream socket this will connect to the remote address. If
334 * you call this on a datagram socket you will tune in to a specific sender and
335 * receiver.
336 * @param addr_port struct containing address and port to connect to
337 * @param struct_size size of addr_port struct
338 * @exception SocketException thrown if socket cannot connect, check errno for cause
339 */
340void
341Socket::connect(const struct sockaddr *addr_port, socklen_t struct_size)
342{
343 if (sock_fd != -1)
344 throw SocketException("Socket already initialized and connected");
345 socket_addr_family_ = addr_port->sa_family;
346
347 create();
348
349 if (timeout == 0.f) {
350 if (::connect(sock_fd, addr_port, struct_size) < 0) {
351 throw SocketException(errno, "Could not connect");
352 }
353 } else {
354 struct timeval start, now;
355 gettimeofday(&start, NULL);
356 do {
357 if (::connect(sock_fd, addr_port, struct_size) < 0) {
358 if ((errno != EINPROGRESS) && (errno != EALREADY)) {
359 throw SocketException(errno, "Could not connect");
360 }
361 }
362 gettimeofday(&now, NULL);
363 } while (time_diff_sec(now, start) < timeout);
364 }
365}
366
367/** Connect socket.
368 * If called for a stream socket this will connect to the remote address. If
369 * you call this on a datagram socket you will tune in to a specific sender and
370 * receiver.
371 * @param hostname hostname or textual represenation of IP address to connect to
372 * @param port port to connect to
373 * @exception SocketException thrown if socket cannot connect, check errno for cause
374 */
375void
376Socket::connect(const char *hostname, unsigned short int port)
377{
378 if (sock_fd != -1)
379 throw SocketException("Socket already initialized and connected");
380
381 struct addrinfo hints, *servinfo, *p;
382 int rv;
383
384 std::string tried_endpoints;
385
386 std::string port_s = StringConversions::to_string((unsigned int)port);
387
388 memset(&hints, 0, sizeof(hints));
389 hints.ai_family = AF_UNSPEC;
390 hints.ai_socktype = socket_type_;
391 if ((rv = getaddrinfo(hostname, port_s.c_str(), &hints, &servinfo)) != 0) {
392 throw SocketException("getaddrinfo for %s:%s failed: %s",
393 hostname,
394 port_s.c_str(),
395 gai_strerror(rv));
396 }
397
398 for (p = servinfo; p != NULL; p = p->ai_next) {
399 bool failed = false;
400 std::string what;
401 int lerrno = 0;
402 if ((sock_fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
403 what = "socket";
404 lerrno = errno;
405 failed = true;
406 }
407
408 if (!failed) {
409 if (::connect(sock_fd, p->ai_addr, p->ai_addrlen) == -1) {
410 what = "connect";
411 lerrno = errno;
413 sock_fd = -1;
414 failed = true;
415 }
416 }
417
418 if (failed) {
419 if (p->ai_family == AF_INET) {
420 char tmp[INET_ADDRSTRLEN];
421 if (inet_ntop(
422 p->ai_family, &((struct sockaddr_in *)p->ai_addr)->sin_addr, tmp, INET_ADDRSTRLEN)
423 != NULL) {
424 tried_endpoints +=
425 std::string(" IPv4:") + tmp + ":" + port_s + "|" + what + "|" + strerror(lerrno);
426 } else {
427 tried_endpoints +=
428 std::string(" IPv4:FAIL") + tmp + ":" + port_s + "|" + what + "|" + strerror(lerrno);
429 }
430 } else if (p->ai_family == AF_INET6) {
431 char tmp[INET6_ADDRSTRLEN];
432 if (inet_ntop(
433 p->ai_family, &((struct sockaddr_in6 *)p->ai_addr)->sin6_addr, tmp, INET6_ADDRSTRLEN)
434 != NULL) {
435 tried_endpoints +=
436 std::string(" IPv6:[") + tmp + "]:" + port_s + "|" + what + "|" + strerror(lerrno);
437 } else {
438 tried_endpoints +=
439 std::string(" IPv6:FAIL") + tmp + ":" + port_s + "|" + what + "|" + strerror(lerrno);
440 }
441 } else {
442 tried_endpoints += std::string(" UNKNOWN_AF:") + port_s;
443 }
444
445 continue;
446 } else {
447 // Connected succesfully!
448 break;
449 }
450 }
451
452 freeaddrinfo(servinfo);
453
454 if (p == NULL || sock_fd == -1) {
455 throw SocketException("Failed to connect to any endpoint (tried:%s)", tried_endpoints.c_str());
456 }
457}
458
459/** Bind socket.
460 * Can only be called on stream sockets.
461 * @param port port to bind
462 * @exception SocketException thrown if socket cannot bind, check errno for cause
463 */
464void
465Socket::bind(const unsigned short int port)
466{
467 if (sock_fd != -1)
468 throw SocketException("Socket already initialized and connected");
469 create();
470
471 switch (addr_type) {
472 case IPv4: {
473 struct ::sockaddr_in host;
474 memset(&host, 0, sizeof(host));
475
476 host.sin_family = AF_INET;
477 host.sin_addr.s_addr = INADDR_ANY;
478 host.sin_port = htons(port);
479
480 int reuse = 1;
481 if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) {
482 throw SocketException(errno, "Could not set SO_REUSEADDR");
483 }
484
485 if (::bind(sock_fd, (struct sockaddr *)&host, sizeof(host)) < 0) {
486 throw SocketException(errno, "Could not bind to port");
487 }
488 } break;
489 case IPv6: {
490 struct ::sockaddr_in6 host;
491 memset(&host, 0, sizeof(host));
492
493 host.sin6_family = AF_INET6;
494 host.sin6_port = htons(port);
495
496 int on = 1;
497 if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
498 throw SocketException(errno, "Could not set SO_REUSEADDR");
499 }
500 if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1) {
501 throw SocketException(errno, "Could not set IPV6_V6ONLY");
502 }
503
504 if (::bind(sock_fd, (struct sockaddr *)&host, sizeof(host)) < 0) {
505 throw SocketException(errno, "Could not bind to port");
506 }
507 } break;
508 default: throw SocketException("Address type not specified for socket, cannot bind");
509 }
510}
511
512/** Bind socket to a specific address.
513 * @param port port to bind
514 * @param ipaddr textual IP address of a local interface to bind to, must match the address
515 * type passed to the constructor.
516 * @exception SocketException thrown if socket cannot bind, check errno for cause
517 */
518void
519Socket::bind(const unsigned short int port, const char *ipaddr)
520{
521 if (sock_fd != -1)
522 throw SocketException("Socket already initialized and connected");
523 create();
524
525 switch (addr_type) {
526 case IPv4: {
527 struct ::sockaddr_in host;
528 memset(&host, 0, sizeof(host));
529
530 host.sin_family = AF_INET;
531 host.sin_port = htons(port);
532
533 if (inet_pton(AF_INET, ipaddr, &host.sin_addr) <= 0) {
534 throw SocketException("bind(IPv4): failed to parse IP address '%s'", ipaddr);
535 }
536
537 int reuse = 1;
538 if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) {
539 throw SocketException(errno, "Could not set SO_REUSEADDR");
540 }
541
542 if (::bind(sock_fd, (struct sockaddr *)&host, sizeof(host)) < 0) {
543 throw SocketException(errno, "Could not bind to port");
544 }
545 } break;
546 case IPv6: {
547 struct ::sockaddr_in6 host;
548 memset(&host, 0, sizeof(host));
549
550 host.sin6_family = AF_INET6;
551 host.sin6_port = htons(port);
552
553 if (inet_pton(AF_INET6, ipaddr, &host.sin6_addr) <= 0) {
554 throw SocketException("bind(IPv6): failed to parse IP address '%s'", ipaddr);
555 }
556
557 int on = 1;
558 if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
559 throw SocketException(errno, "Could not set SO_REUSEADDR");
560 }
561 if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1) {
562 throw SocketException(errno, "Could not set IPV6_V6ONLY");
563 }
564
565 if (::bind(sock_fd, (struct sockaddr *)&host, sizeof(host)) < 0) {
566 throw SocketException(errno, "Could not bind to port");
567 }
568 } break;
569 default: throw SocketException("Address type not specified for socket, cannot bind");
570 }
571}
572
573/** Listen on socket.
574 * This waits for new connections on a bound socket. The backlog is the maximum
575 * number of connections waiting for being accepted.
576 * @param backlog maximum number of waiting connections
577 * @exception SocketException thrown if socket cannot listen, check errno for cause
578 * @see bind()
579 * @see accept()
580 */
581void
582Socket::listen(int backlog)
583{
584 if (sock_fd == -1) {
585 throw SocketException("Socket not initialized, call bind() or connect()");
586 }
587
588 if (::listen(sock_fd, backlog)) {
589 throw SocketException(errno, "Cannot listen on socket");
590 }
591}
592
593/** Accept connection.
594 * Accepts a connection waiting in the queue.
595 * @return new socket used to communicate with the remote part
596 * @exception SocketException thrown if socket cannot accept, check errno for cause
597 */
598Socket *
600{
601 if (sock_fd == -1) {
602 throw SocketException("Socket not initialized, call bind() or connect()");
603 }
604
605 struct ::sockaddr_in tmp_client_addr;
606 unsigned int tmp_client_addr_len = sizeof(struct ::sockaddr_in);
607
608 int a_sock_fd = -1;
609
610 a_sock_fd = ::accept(sock_fd, (sockaddr *)&tmp_client_addr, &tmp_client_addr_len);
611 if (a_sock_fd == -1) {
612 if (errno != EWOULDBLOCK) {
613 throw SocketException(errno, "Could not accept connection");
614 } else {
615 return NULL;
616 }
617 }
618
619 // Does not work, evaluated at compile time, thus need clone()
620 //typeof_(this) s = new typeof_(*this);
621 //s->timeout = timeout;
622
623 Socket *s = clone();
624 s->sock_fd = a_sock_fd;
625
626 if (s->client_addr != NULL) {
627 free(s->client_addr);
628 }
629 /*
630 struct ::sockaddr_in *tmp_client_addr_alloc = (struct ::sockaddr_in *)malloc(sizeof(struct ::sockaddr_in));
631 memcpy(tmp_client_addr_alloc, &tmp_client_addr, sizeof(struct ::sockaddr_in));
632 s->client_addr = tmp_client_addr_alloc;
633 s->client_addr_len = tmp_client_addr_len;
634 */
635
636 return s;
637}
638
639/** Check if data is available.
640 * Use this to check if data is available on the socket for reading.
641 * @return true, if data can be read, false otherwise
642 */
643bool
645{
646 if (sock_fd == -1)
647 return false;
648
649 fd_set rfds;
650 struct timeval tv;
651 int retval = 1;
652
653 FD_ZERO(&rfds);
654 FD_SET(sock_fd, &rfds);
655 tv.tv_sec = 0;
656 tv.tv_usec = 0;
657
658 retval = select(sock_fd + 1, &rfds, NULL, NULL, &tv);
659 if (retval < 0) {
660 perror("select() failed");
661 }
662
663 return (retval > 0);
664}
665
666/** Wait for some event on socket.
667 * @param timeout timeout in miliseconds to wait. A negative value means to
668 * wait forever until an event occurs, zero means just check, don't wait.
669 * @param what what to wait for, a bitwise OR'ed combination of POLL_IN,
670 * POLL_OUT and POLL_PRI.
671 * @return Returns a flag value. Use bit-wise AND with the POLL_* constants
672 * in this class.
673 * @exception InterruptedException thrown, if poll is interrupted by a signal
674 * @exception SocketException thrown for any other error the poll() syscall can cause,
675 * see Exception::errno() for the cause of the error.
676 * @see Socket::POLL_IN
677 * @see Socket::POLL_OUT
678 * @see Socket::POLL_PRI
679 * @see Socket::POLL_RDHUP
680 * @see Socket::POLL_ERR
681 * @see Socket::POLL_HUP
682 * @see Socket::POLL_NVAL
683 */
684short
685Socket::poll(int timeout, short what)
686{
687 if (sock_fd == -1) {
688 return POLL_ERR;
689 }
690
691 struct pollfd pfd;
692 pfd.fd = sock_fd;
693 pfd.events = what;
694 pfd.revents = 0;
695 if (::poll(&pfd, 1, timeout) == -1) {
696 if (errno == EINTR) {
697 throw InterruptedException();
698 } else {
699 throw SocketException(errno, "poll() failed");
700 }
701 } else {
702 return pfd.revents;
703 }
704}
705
706/** Write to the socket.
707 * Write to the socket. This method can only be used on streams.
708 * @param buf buffer to write
709 * @param count number of bytes to write from buf
710 * @exception SocketException if the data could not be written or if a timeout occured.
711 */
712void
713Socket::write(const void *buf, size_t count)
714{
715 if (sock_fd == -1) {
716 throw SocketException("Socket not initialized, call bind() or connect()");
717 }
718
719 int retval = 0;
720 unsigned int bytes_written = 0;
721 struct timeval start, now;
722
723 gettimeofday(&start, NULL);
724
725 do {
726 retval = ::write(sock_fd, (char *)buf + bytes_written, count - bytes_written);
727 if (retval == -1) {
728 if (errno != EAGAIN) {
729 throw SocketException(errno, "Could not write data");
730 } else {
731 // just to meet loop condition
732 retval = 0;
733 }
734 } else {
735 bytes_written += retval;
736 // reset timeout
737 gettimeofday(&start, NULL);
738 }
739 gettimeofday(&now, NULL);
740 usleep(0);
741 } while ((bytes_written < count) && (time_diff_sec(now, start) < timeout));
742
743 if (bytes_written < count) {
744 throw SocketException("Write timeout");
745 }
746}
747
748/** Read from socket.
749 * Read from the socket. This method can only be used on streams.
750 * @param buf buffer to write from
751 * @param count length of buffer, number of bytes to write to stream
752 * @param read_all setting this to true causes a call to read() loop until exactly
753 * count bytes have been read, if false it will return after the first successful read
754 * with the number of bytes available then.
755 * @return number of bytes read.
756 * @see write
757 * @exception SocketException thrown for any error during reading
758 */
759size_t
760Socket::read(void *buf, size_t count, bool read_all)
761{
762 if (sock_fd == -1) {
763 throw SocketException("Socket not initialized, call bind() or connect()");
764 }
765
766 int retval = 0;
767 unsigned int bytes_read = 0;
768
769 if (timeout > 0) {
770 struct timeval start, now;
771
772 gettimeofday(&start, NULL);
773
774 if (read_all) {
775 do {
776 retval = ::read(sock_fd, (char *)buf + bytes_read, count - bytes_read);
777 if (retval == -1) {
778 if (errno != EAGAIN) {
779 throw SocketException(errno, "Could not read data");
780 } else {
781 // just to meet loop condition
782 retval = 0;
783 }
784 } else {
785 bytes_read += retval;
786 // reset timeout
787 gettimeofday(&start, NULL);
788 }
789 gettimeofday(&now, NULL);
790 usleep(0);
791 } while ((bytes_read < count) && (time_diff_sec(now, start) < timeout));
792 } else {
793 do {
794 retval = ::read(sock_fd, (char *)buf, count);
795 if ((retval == -1) && (errno != EAGAIN)) {
796 throw SocketException(errno, "Could not read data");
797 } else {
798 bytes_read = retval;
799 }
800 usleep(0);
801 } while (retval < 0);
802 }
803 } else {
804 if (read_all) {
805 do {
806 retval = ::read(sock_fd, (char *)buf + bytes_read, count - bytes_read);
807 if (retval == -1) {
808 throw SocketException(errno, "Could not read data");
809 } else if (retval == 0) {
810 throw SocketException("Could not read any data");
811 } else {
812 bytes_read += retval;
813 }
814 usleep(0);
815 } while (bytes_read < count);
816 } else {
817 do {
818 retval = ::read(sock_fd, (char *)buf, count);
819 if ((retval == -1) && (errno != EAGAIN)) {
820 throw SocketException(errno, "Could not read data");
821 } else {
822 bytes_read = retval;
823 }
824 usleep(0);
825 } while (retval < 0);
826 }
827 }
828
829 if (read_all && (bytes_read < count)) {
830 throw SocketException("Read timeout");
831 }
832
833 return bytes_read;
834}
835
836/** Write to the socket.
837 * Write to the socket. This method can be used on streams or on datagram
838 * sockets which have been tuned to a specific receiver by using connect().
839 * For streams usage of write() is recommended as it is the more intuitive
840 * way to deal with a stream.
841 * @param buf buffer to write
842 * @param buf_len length of buffer, number of bytes to write to stream
843 * @see write
844 */
845void
846Socket::send(void *buf, size_t buf_len)
847{
848 try {
849 write(buf, buf_len);
850 } catch (SocketException &e) {
851 throw;
852 }
853}
854
855/** Read from socket.
856 * Read from the socket. This method can only be used on streams. Usage of
857 * read() is recommended.
858 * @param buf buffer to read data into
859 * @param buf_len length of buffer, number of bytes to read from stream
860 * @return number of bytes read
861 * @exception SocketException thrown if an error occurs or the other side
862 * has closed the connection.
863 */
864size_t
865Socket::recv(void *buf, size_t buf_len)
866{
867 if (sock_fd == -1) {
868 throw SocketException("Socket not initialized, call bind() or connect()");
869 }
870
871 ssize_t rv;
872 if ((rv = ::recv(sock_fd, buf, buf_len, 0)) == -1) {
873 throw SocketException(errno, "recv() failed");
874 } else if (rv == 0) {
875 throw SocketException("Other side closed the connection");
876 }
877 return rv;
878}
879
880/** Send message.
881 * @param buf buffer with data to send
882 * @param buf_len length of buffer, all data will be send.
883 * @param addr addr to send data to.
884 * @param addr_len length of address
885 */
886void
887Socket::send(void *buf, size_t buf_len, const struct sockaddr *addr, socklen_t addr_len)
888{
889 if (sock_fd == -1) {
890 throw SocketException("Socket not initialized, call bind() or connect()");
891 }
892
893 int retval = 0;
894 unsigned int bytes_written = 0;
895 struct timeval start, now;
896
897 gettimeofday(&start, NULL);
898
899 do {
900 retval =
901 ::sendto(sock_fd, (char *)buf + bytes_written, buf_len - bytes_written, 0, addr, addr_len);
902 if (retval == -1) {
903 if (errno != EAGAIN) {
904 throw SocketException(errno, "Could not read data");
905 } else {
906 // just to meet loop condition
907 retval = 0;
908 }
909 } else {
910 bytes_written += retval;
911 // reset timeout
912 gettimeofday(&start, NULL);
913 }
914 gettimeofday(&now, NULL);
915 usleep(0);
916 } while ((bytes_written < buf_len) && (time_diff_sec(now, start) < timeout));
917
918 if (bytes_written < buf_len) {
919 throw SocketException("Write timeout");
920 }
921}
922
923/** Receive data.
924 * This will use recvfrom() to read data from the socket and returns the
925 * number of bytes actually read. It will not wait until the requested
926 * number of bytes has been read. Use read() if you need this.
927 * @param buf buffer that read data shall be stored in.
928 * @param buf_len length of buffer and number of bytes to be read
929 * @param addr return parameter, contains address of sender
930 * @param addr_len initially has to contain size of address, on return
931 * contains the actual bytes used.
932 * @return number of bytes received
933 */
934size_t
935Socket::recv(void *buf, size_t buf_len, struct sockaddr *addr, socklen_t *addr_len)
936{
937 if (sock_fd == -1) {
938 throw SocketException("Socket not initialized, call bind() or connect()");
939 }
940
941 ssize_t rv = 0;
942
943 if ((rv = ::recvfrom(sock_fd, buf, buf_len, 0, addr, addr_len)) == -1) {
944 throw SocketException(errno, "recvfrom() failed");
945 } else if (rv == 0) {
946 throw SocketException("Peer has closed the connection");
947 } else {
948 return rv;
949 }
950}
951
952/** Is socket listening for connections?
953 * @return true if socket is listening for incoming connections, false otherwise
954 */
955bool
957{
958 if (sock_fd == -1)
959 return false;
960
961 int i = 0;
962 unsigned int len = sizeof(i);
963 if (getsockopt(sock_fd, SOL_SOCKET, SO_ACCEPTCONN, &i, &len) == -1) {
964 throw SocketException(errno, "Socket::listening(): getsockopt failed");
965 }
966 return (i == 1);
967}
968
969/** Maximum Transfer Unit (MTU) of socket.
970 * Note that this can only be retrieved of connected sockets!
971 * @return MTU in bytes
972 */
973unsigned int
975{
976 if (sock_fd == -1) {
977 throw SocketException("Socket not initialized, call bind() or connect()");
978 }
979
980 int m = 0;
981
982#ifdef __linux__
983 unsigned int len = sizeof(m);
984 if (getsockopt(sock_fd, IPPROTO_IP, IP_MTU, &m, &len) == -1) {
985 throw SocketException(errno, "Socket::mtu(): getsockopt failed");
986 }
987
988 if (m < 0) {
989 throw SocketException("MTU < 0");
990 }
991#elif defined __FreeBSD__
992 struct ifreq ifr;
993 if (ioctl(sock_fd, SIOCGIFMTU, &ifr) != -1)
994 m = ifr.ifr_mtu;
995#endif
996
997 return m;
998}
999
1000} // end namespace fawkes
Base class for exceptions in Fawkes.
Definition: exception.h:36
void append_va(const char *format, va_list va) noexcept
Append messages to the message list.
Definition: exception.cpp:353
The current system call has been interrupted (for instance by a signal).
Definition: system.h:39
Socket exception.
Definition: socket.h:57
SocketException(int _errno, const char *msg)
Constructor.
Definition: socket.cpp:87
Socket base class.
Definition: socket.h:64
virtual Socket * clone()=0
Clone socket.
virtual bool available()
Check if data is available.
Definition: socket.cpp:644
virtual void bind(const unsigned short int port)
Bind socket.
Definition: socket.cpp:465
static const short POLL_RDHUP
Stream socket peer closed connection, or shut down writing half of connection.
Definition: socket.h:69
int sock_fd
Socket file descriptor.
Definition: socket.h:137
static const short POLL_NVAL
Invalid request.
Definition: socket.h:72
Socket()
Constructor.
Definition: socket.cpp:211
AddrType
Address type specification.
Definition: socket.h:75
@ IPv4
IPv4.
Definition: socket.h:77
@ IPv6
IPv6.
Definition: socket.h:78
virtual unsigned int mtu()
Maximum Transfer Unit (MTU) of socket.
Definition: socket.cpp:974
virtual void connect(const char *hostname, const unsigned short int port)
Connect socket.
Definition: socket.cpp:376
virtual size_t recv(void *buf, size_t buf_len)
Read from socket.
Definition: socket.cpp:865
SocketType
Socket type.
Definition: socket.h:82
@ TCP
TCP stream socket.
Definition: socket.h:83
@ UDP
UDP datagram socket.
Definition: socket.h:84
virtual size_t read(void *buf, size_t count, bool read_all=true)
Read from socket.
Definition: socket.cpp:760
static const short POLL_HUP
Hang up.
Definition: socket.h:71
virtual bool listening()
Is socket listening for connections?
Definition: socket.cpp:956
Socket & operator=(Socket &socket)
Copy constructor.
Definition: socket.cpp:250
static const short POLL_IN
Data can be read.
Definition: socket.h:66
virtual void send(void *buf, size_t buf_len)
Write to the socket.
Definition: socket.cpp:846
AddrType addr_type
Address type/family of socket.
Definition: socket.h:136
static const short POLL_OUT
Writing will not block.
Definition: socket.h:67
virtual short poll(int timeout=-1, short what=POLL_IN|POLL_HUP|POLL_PRI|POLL_RDHUP)
Wait for some event on socket.
Definition: socket.cpp:685
static const short POLL_PRI
There is urgent data to read (e.g., out-of-band data on TCP socket; pseudo-terminal master in packet ...
Definition: socket.h:68
virtual ~Socket()
Destructor.
Definition: socket.cpp:300
float timeout
Timeout in seconds for various operations.
Definition: socket.h:138
struct::sockaddr_storage * client_addr
Client address, set if connected.
Definition: socket.h:139
virtual Socket * accept()
Accept connection.
Definition: socket.cpp:599
static const short POLL_ERR
Error condition.
Definition: socket.h:70
virtual void listen(int backlog=1)
Listen on socket.
Definition: socket.cpp:582
unsigned int client_addr_len
length in bytes of client address.
Definition: socket.h:140
virtual void close()
Close socket.
Definition: socket.cpp:311
virtual void write(const void *buf, size_t count)
Write to the socket.
Definition: socket.cpp:713
static std::string to_string(unsigned int i)
Convert unsigned int value to a string.
Fawkes library namespace.
double time_diff_sec(const timeval &a, const timeval &b)
Calculate time difference of two time structs.
Definition: time.h:41