| 
									
										
										
										
											2019-04-10 17:35:43 +02:00
										 |  |  | #include <LibCore/CNotifier.h>
 | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  | #include <LibCore/CSocket.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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-10 20:22:23 +02:00
										 |  |  | CSocket::CSocket(Type type, CObject* parent) | 
					
						
							|  |  |  |     : CIODevice(parent) | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  |     , m_type(type) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-10 20:22:23 +02:00
										 |  |  | CSocket::~CSocket() | 
					
						
							| 
									
										
										
										
											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
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-10 20:22:23 +02:00
										 |  |  | bool CSocket::connect(const String& hostname, int port) | 
					
						
							| 
									
										
										
										
											2019-04-02 20:40:10 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     auto* hostent = gethostbyname(hostname.characters()); | 
					
						
							|  |  |  |     if (!hostent) { | 
					
						
							| 
									
										
										
										
											2019-07-14 11:02:40 +02:00
										 |  |  |         dbg() << "CSocket::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]); | 
					
						
							| 
									
										
										
										
											2019-07-14 11:02:40 +02:00
										 |  |  |     dbg() << "CSocket::connect: Resolved '" << hostname << "' to " << host_address; | 
					
						
							| 
									
										
										
										
											2019-04-02 20:40:10 +02:00
										 |  |  |     return connect(host_address, port); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-16 18:00:08 +02:00
										 |  |  | void CSocket::set_blocking(bool blocking) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int flags = fcntl(fd(), F_GETFL, 0); | 
					
						
							|  |  |  |     ASSERT(flags >= 0); | 
					
						
							|  |  |  |     if (blocking) | 
					
						
							|  |  |  |         flags = fcntl(fd(), F_SETFL, flags | O_NONBLOCK); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         flags = fcntl(fd(), F_SETFL, flags & O_NONBLOCK); | 
					
						
							|  |  |  |     ASSERT(flags >= 0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-10 20:22:23 +02:00
										 |  |  | bool CSocket::connect(const CSocketAddress& address, int port) | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     ASSERT(!is_connected()); | 
					
						
							| 
									
										
										
										
											2019-04-10 20:22:23 +02:00
										 |  |  |     ASSERT(address.type() == CSocketAddress::Type::IPv4); | 
					
						
							| 
									
										
										
										
											2019-07-14 11:02:40 +02:00
										 |  |  |     dbg() << *this << " connecting to " << address << "..."; | 
					
						
							| 
									
										
										
										
											2019-07-13 19:42:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  |     ASSERT(port > 0 && port <= 65535); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-13 19:42:03 +02:00
										 |  |  | bool CSocket::connect(const CSocketAddress& address) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     ASSERT(!is_connected()); | 
					
						
							|  |  |  |     ASSERT(address.type() == CSocketAddress::Type::Local); | 
					
						
							| 
									
										
										
										
											2019-07-14 11:02:40 +02:00
										 |  |  |     dbg() << *this << " connecting to " << address << "..."; | 
					
						
							| 
									
										
										
										
											2019-07-13 19:42:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     sockaddr_un saddr; | 
					
						
							|  |  |  |     saddr.sun_family = AF_LOCAL; | 
					
						
							|  |  |  |     strcpy(saddr.sun_path, address.to_string().characters()); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-11 19:44:15 +02:00
										 |  |  |     return common_connect((const sockaddr*)&saddr, sizeof(saddr)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool CSocket::common_connect(const struct sockaddr* addr, socklen_t addrlen) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     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) { | 
					
						
							|  |  |  |             dbg() << *this << " connection in progress (EINPROGRESS)"; | 
					
						
							| 
									
										
										
										
											2019-09-21 15:18:12 +02:00
										 |  |  |             m_notifier = CNotifier::construct(fd(), CNotifier::Event::Write, this); | 
					
						
							| 
									
										
										
										
											2019-09-11 19:44:15 +02:00
										 |  |  |             m_notifier->on_ready_to_write = [this] { | 
					
						
							|  |  |  |                 dbg() << *this << " connected!"; | 
					
						
							|  |  |  |                 m_connected = true; | 
					
						
							| 
									
										
										
										
											2019-09-22 21:46:46 +02:00
										 |  |  |                 ensure_read_notifier(); | 
					
						
							| 
									
										
										
										
											2019-09-11 19:44:15 +02:00
										 |  |  |                 m_notifier->set_event_mask(CNotifier::Event::None); | 
					
						
							|  |  |  |                 if (on_connected) | 
					
						
							|  |  |  |                     on_connected(); | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         perror("CSocket::common_connect: connect"); | 
					
						
							| 
									
										
										
										
											2019-07-13 19:42:03 +02:00
										 |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-09-11 19:44:15 +02:00
										 |  |  |     dbg() << *this << " connected ok!"; | 
					
						
							| 
									
										
										
										
											2019-07-13 19:42:03 +02:00
										 |  |  |     m_connected = true; | 
					
						
							| 
									
										
										
										
											2019-09-22 21:46:46 +02:00
										 |  |  |     ensure_read_notifier(); | 
					
						
							| 
									
										
										
										
											2019-07-16 13:17:23 +02:00
										 |  |  |     if (on_connected) | 
					
						
							|  |  |  |         on_connected(); | 
					
						
							| 
									
										
										
										
											2019-07-13 19:42:03 +02:00
										 |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-10 20:22:23 +02:00
										 |  |  | ByteBuffer CSocket::receive(int max_size) | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     auto buffer = read(max_size); | 
					
						
							|  |  |  |     if (eof()) { | 
					
						
							| 
									
										
										
										
											2019-07-14 11:02:40 +02:00
										 |  |  |         dbg() << *this << " connection appears to have closed in receive()."; | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  |         m_connected = false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return buffer; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-10 20:22:23 +02:00
										 |  |  | bool CSocket::send(const ByteBuffer& data) | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-09-30 08:57:01 +02:00
										 |  |  |     int 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; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     ASSERT(nsent == data.size()); | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-07-26 22:39:16 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-27 10:48:43 +02:00
										 |  |  | void CSocket::did_update_fd(int fd) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (fd < 0) { | 
					
						
							|  |  |  |         m_read_notifier = nullptr; | 
					
						
							|  |  |  |         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..
 | 
					
						
							|  |  |  |         ASSERT(!m_read_notifier); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CSocket::ensure_read_notifier() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     ASSERT(m_connected); | 
					
						
							|  |  |  |     m_read_notifier = CNotifier::construct(fd(), CNotifier::Event::Read, this); | 
					
						
							| 
									
										
										
										
											2019-07-27 10:48:43 +02:00
										 |  |  |     m_read_notifier->on_ready_to_read = [this] { | 
					
						
							|  |  |  |         if (on_ready_to_read) | 
					
						
							|  |  |  |             on_ready_to_read(); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | } |