| 
									
										
										
										
											2020-01-18 09:38:21 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> | 
					
						
							|  |  |  |  * All rights reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Redistribution and use in source and binary forms, with or without | 
					
						
							|  |  |  |  * modification, are permitted provided that the following conditions are met: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 1. Redistributions of source code must retain the above copyright notice, this | 
					
						
							|  |  |  |  *    list of conditions and the following disclaimer. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | 
					
						
							|  |  |  |  *    this list of conditions and the following disclaimer in the documentation | 
					
						
							|  |  |  |  *    and/or other materials provided with the distribution. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | 
					
						
							|  |  |  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
					
						
							|  |  |  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 
					
						
							|  |  |  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | 
					
						
							|  |  |  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
					
						
							|  |  |  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | 
					
						
							|  |  |  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | 
					
						
							|  |  |  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | 
					
						
							|  |  |  |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
					
						
							|  |  |  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-14 22:29:06 +01:00
										 |  |  | #include <AK/ByteBuffer.h>
 | 
					
						
							| 
									
										
										
										
											2021-01-15 21:46:23 +01:00
										 |  |  | #include <AK/Debug.h>
 | 
					
						
							| 
									
										
										
										
											2020-02-06 15:04:03 +01:00
										 |  |  | #include <LibCore/Notifier.h>
 | 
					
						
							|  |  |  | #include <LibCore/Socket.h>
 | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  | #include <arpa/inet.h>
 | 
					
						
							| 
									
										
										
										
											2019-04-08 04:53:45 +02:00
										 |  |  | #include <errno.h>
 | 
					
						
							| 
									
										
										
										
											2019-07-26 22:39:16 +02:00
										 |  |  | #include <fcntl.h>
 | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  | #include <netdb.h>
 | 
					
						
							|  |  |  | #include <netinet/in.h>
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | #include <sys/socket.h>
 | 
					
						
							| 
									
										
										
										
											2019-07-26 22:39:16 +02:00
										 |  |  | #include <unistd.h>
 | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 12:34:39 +01:00
										 |  |  | namespace Core { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Socket::Socket(Type type, Object* parent) | 
					
						
							|  |  |  |     : IODevice(parent) | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  |     , m_type(type) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-12-16 18:16:15 +11:00
										 |  |  |     register_property( | 
					
						
							|  |  |  |         "source_address", [this] { return m_source_address.to_string(); }, | 
					
						
							|  |  |  |         [](auto&) { return false; }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     register_property( | 
					
						
							|  |  |  |         "destination_address", [this] { return m_destination_address.to_string(); }, | 
					
						
							|  |  |  |         [](auto&) { return false; }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     register_property( | 
					
						
							|  |  |  |         "source_port", [this] { return m_source_port; }, | 
					
						
							|  |  |  |         [](auto&) { return false; }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     register_property( | 
					
						
							|  |  |  |         "destination_port", [this] { return m_destination_port; }, | 
					
						
							|  |  |  |         [](auto&) { return false; }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     register_property( | 
					
						
							|  |  |  |         "connected", [this] { return m_connected; }, | 
					
						
							|  |  |  |         [](auto&) { return false; }); | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 12:34:39 +01:00
										 |  |  | Socket::~Socket() | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-08-04 22:00:42 +02:00
										 |  |  |     close(); | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 12:34:39 +01:00
										 |  |  | bool Socket::connect(const String& hostname, int port) | 
					
						
							| 
									
										
										
										
											2019-04-02 20:40:10 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     auto* hostent = gethostbyname(hostname.characters()); | 
					
						
							|  |  |  |     if (!hostent) { | 
					
						
							| 
									
										
										
										
											2021-01-11 13:04:22 +01:00
										 |  |  |         dbgln("Socket::connect: Unable to resolve '{}'", hostname); | 
					
						
							| 
									
										
										
										
											2019-04-02 20:40:10 +02:00
										 |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-03 21:17:35 +02:00
										 |  |  |     IPv4Address host_address((const u8*)hostent->h_addr_list[0]); | 
					
						
							| 
									
										
										
										
											2021-02-07 15:33:24 +03:30
										 |  |  |     dbgln_if(CSOCKET_DEBUG, "Socket::connect: Resolved '{}' to {}", hostname, host_address); | 
					
						
							| 
									
										
										
										
											2019-04-02 20:40:10 +02:00
										 |  |  |     return connect(host_address, port); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 12:34:39 +01:00
										 |  |  | void Socket::set_blocking(bool blocking) | 
					
						
							| 
									
										
										
										
											2019-07-16 18:00:08 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     int flags = fcntl(fd(), F_GETFL, 0); | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |     VERIFY(flags >= 0); | 
					
						
							| 
									
										
										
										
											2019-07-16 18:00:08 +02:00
										 |  |  |     if (blocking) | 
					
						
							| 
									
										
										
										
											2020-01-26 14:28:22 +01:00
										 |  |  |         flags = fcntl(fd(), F_SETFL, flags & ~O_NONBLOCK); | 
					
						
							| 
									
										
										
										
											2019-07-16 18:00:08 +02:00
										 |  |  |     else | 
					
						
							| 
									
										
										
										
											2020-01-26 14:28:22 +01:00
										 |  |  |         flags = fcntl(fd(), F_SETFL, flags | O_NONBLOCK); | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |     VERIFY(flags == 0); | 
					
						
							| 
									
										
										
										
											2019-07-16 18:00:08 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 12:34:39 +01:00
										 |  |  | bool Socket::connect(const SocketAddress& address, int port) | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |     VERIFY(!is_connected()); | 
					
						
							|  |  |  |     VERIFY(address.type() == SocketAddress::Type::IPv4); | 
					
						
							| 
									
										
										
										
											2021-02-07 15:33:24 +03:30
										 |  |  |     dbgln_if(CSOCKET_DEBUG, "{} connecting to {}...", *this, address); | 
					
						
							| 
									
										
										
										
											2019-07-13 19:42:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |     VERIFY(port > 0 && port <= 65535); | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     struct sockaddr_in addr; | 
					
						
							|  |  |  |     memset(&addr, 0, sizeof(addr)); | 
					
						
							|  |  |  |     auto ipv4_address = address.ipv4_address(); | 
					
						
							|  |  |  |     memcpy(&addr.sin_addr.s_addr, &ipv4_address, sizeof(IPv4Address)); | 
					
						
							|  |  |  |     addr.sin_family = AF_INET; | 
					
						
							|  |  |  |     addr.sin_port = htons(port); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-08 04:53:45 +02:00
										 |  |  |     m_destination_address = address; | 
					
						
							|  |  |  |     m_destination_port = port; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-11 19:44:15 +02:00
										 |  |  |     return common_connect((struct sockaddr*)&addr, sizeof(addr)); | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 12:34:39 +01:00
										 |  |  | bool Socket::connect(const SocketAddress& address) | 
					
						
							| 
									
										
										
										
											2019-07-13 19:42:03 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |     VERIFY(!is_connected()); | 
					
						
							|  |  |  |     VERIFY(address.type() == SocketAddress::Type::Local); | 
					
						
							| 
									
										
										
										
											2021-02-07 15:33:24 +03:30
										 |  |  |     dbgln_if(CSOCKET_DEBUG, "{} connecting to {}...", *this, address); | 
					
						
							| 
									
										
										
										
											2019-07-13 19:42:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     sockaddr_un saddr; | 
					
						
							|  |  |  |     saddr.sun_family = AF_LOCAL; | 
					
						
							| 
									
										
										
										
											2020-08-23 13:47:52 +02:00
										 |  |  |     auto dest_address = address.to_string(); | 
					
						
							| 
									
										
										
										
											2020-08-25 17:32:01 +03:00
										 |  |  |     bool fits = dest_address.copy_characters_to_buffer(saddr.sun_path, sizeof(saddr.sun_path)); | 
					
						
							|  |  |  |     if (!fits) { | 
					
						
							| 
									
										
										
										
											2020-08-23 13:47:52 +02:00
										 |  |  |         fprintf(stderr, "Core::Socket: Failed to connect() to %s: Path is too long!\n", dest_address.characters()); | 
					
						
							|  |  |  |         errno = EINVAL; | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-02-22 16:38:49 +01:00
										 |  |  |     m_destination_address = address; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-11 19:44:15 +02:00
										 |  |  |     return common_connect((const sockaddr*)&saddr, sizeof(saddr)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 12:34:39 +01:00
										 |  |  | bool Socket::common_connect(const struct sockaddr* addr, socklen_t addrlen) | 
					
						
							| 
									
										
										
										
											2019-09-11 19:44:15 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-04-19 12:57:13 +04:30
										 |  |  |     auto connected = [this] { | 
					
						
							| 
									
										
										
										
											2021-02-07 15:33:24 +03:30
										 |  |  |         dbgln_if(CSOCKET_DEBUG, "{} connected!", *this); | 
					
						
							| 
									
										
										
										
											2020-04-19 12:57:13 +04:30
										 |  |  |         if (!m_connected) { | 
					
						
							|  |  |  |             m_connected = true; | 
					
						
							|  |  |  |             ensure_read_notifier(); | 
					
						
							| 
									
										
										
										
											2020-05-18 20:16:52 +02:00
										 |  |  |             if (m_notifier) { | 
					
						
							|  |  |  |                 m_notifier->remove_from_parent(); | 
					
						
							|  |  |  |                 m_notifier = nullptr; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2020-04-19 12:57:13 +04:30
										 |  |  |             if (on_connected) | 
					
						
							|  |  |  |                 on_connected(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2019-09-11 19:44:15 +02:00
										 |  |  |     int rc = ::connect(fd(), addr, addrlen); | 
					
						
							| 
									
										
										
										
											2019-07-13 19:42:03 +02:00
										 |  |  |     if (rc < 0) { | 
					
						
							| 
									
										
										
										
											2019-09-11 19:44:15 +02:00
										 |  |  |         if (errno == EINPROGRESS) { | 
					
						
							| 
									
										
										
										
											2021-02-07 15:33:24 +03:30
										 |  |  |             dbgln_if(CSOCKET_DEBUG, "{} connection in progress (EINPROGRESS)", *this); | 
					
						
							| 
									
										
										
										
											2020-02-02 12:34:39 +01:00
										 |  |  |             m_notifier = Notifier::construct(fd(), Notifier::Event::Write, this); | 
					
						
							| 
									
										
										
										
											2020-04-19 12:57:13 +04:30
										 |  |  |             m_notifier->on_ready_to_write = move(connected); | 
					
						
							| 
									
										
										
										
											2019-09-11 19:44:15 +02:00
										 |  |  |             return true; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-02-22 16:38:49 +01:00
										 |  |  |         int saved_errno = errno; | 
					
						
							|  |  |  |         fprintf(stderr, "Core::Socket: Failed to connect() to %s: %s\n", destination_address().to_string().characters(), strerror(saved_errno)); | 
					
						
							|  |  |  |         errno = saved_errno; | 
					
						
							| 
									
										
										
										
											2019-07-13 19:42:03 +02:00
										 |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-02-07 15:33:24 +03:30
										 |  |  |     dbgln_if(CSOCKET_DEBUG, "{} connected ok!", *this); | 
					
						
							| 
									
										
										
										
											2020-04-19 12:57:13 +04:30
										 |  |  |     connected(); | 
					
						
							| 
									
										
										
										
											2019-07-13 19:42:03 +02:00
										 |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 12:34:39 +01:00
										 |  |  | ByteBuffer Socket::receive(int max_size) | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     auto buffer = read(max_size); | 
					
						
							| 
									
										
										
										
											2020-12-23 15:44:13 +01:00
										 |  |  |     if (eof()) | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  |         m_connected = false; | 
					
						
							|  |  |  |     return buffer; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-27 15:00:12 +02:00
										 |  |  | bool Socket::send(ReadonlyBytes data) | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-02-20 12:54:15 +01:00
										 |  |  |     ssize_t nsent = ::send(fd(), data.data(), data.size(), 0); | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  |     if (nsent < 0) { | 
					
						
							| 
									
										
										
										
											2019-08-17 11:07:15 +02:00
										 |  |  |         set_error(errno); | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |     VERIFY(static_cast<size_t>(nsent) == data.size()); | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-07-26 22:39:16 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 12:34:39 +01:00
										 |  |  | void Socket::did_update_fd(int fd) | 
					
						
							| 
									
										
										
										
											2019-07-27 10:48:43 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     if (fd < 0) { | 
					
						
							| 
									
										
										
										
											2020-06-08 14:29:37 +03:00
										 |  |  |         if (m_read_notifier) { | 
					
						
							| 
									
										
										
										
											2020-07-27 15:00:12 +02:00
										 |  |  |             m_read_notifier->remove_from_parent(); | 
					
						
							|  |  |  |             m_read_notifier = nullptr; | 
					
						
							| 
									
										
										
										
											2020-06-08 14:29:37 +03:00
										 |  |  |         } | 
					
						
							|  |  |  |         if (m_notifier) { | 
					
						
							|  |  |  |             m_notifier->remove_from_parent(); | 
					
						
							|  |  |  |             m_notifier = nullptr; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-07-27 10:48:43 +02:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-09-22 21:46:46 +02:00
										 |  |  |     if (m_connected) { | 
					
						
							|  |  |  |         ensure_read_notifier(); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         // I don't think it would be right if we updated the fd while not connected *but* while having a notifier..
 | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |         VERIFY(!m_read_notifier); | 
					
						
							| 
									
										
										
										
											2019-09-22 21:46:46 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 12:34:39 +01:00
										 |  |  | void Socket::ensure_read_notifier() | 
					
						
							| 
									
										
										
										
											2019-09-22 21:46:46 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |     VERIFY(m_connected); | 
					
						
							| 
									
										
										
										
											2020-02-02 12:34:39 +01:00
										 |  |  |     m_read_notifier = Notifier::construct(fd(), Notifier::Event::Read, this); | 
					
						
							| 
									
										
										
										
											2019-07-27 10:48:43 +02:00
										 |  |  |     m_read_notifier->on_ready_to_read = [this] { | 
					
						
							| 
									
										
										
										
											2020-07-06 23:11:55 +02:00
										 |  |  |         if (!can_read()) | 
					
						
							|  |  |  |             return; | 
					
						
							| 
									
										
										
										
											2019-07-27 10:48:43 +02:00
										 |  |  |         if (on_ready_to_read) | 
					
						
							|  |  |  |             on_ready_to_read(); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-02-02 12:34:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | } |