Serenity Operating System
at portability 202 lines 6.0 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <AK/ByteBuffer.h> 28#include <LibCore/Notifier.h> 29#include <LibCore/Socket.h> 30#include <arpa/inet.h> 31#include <errno.h> 32#include <fcntl.h> 33#include <netdb.h> 34#include <netinet/in.h> 35#include <stdio.h> 36#include <stdlib.h> 37#include <sys/socket.h> 38#include <unistd.h> 39 40//#define CSOCKET_DEBUG 41 42namespace Core { 43 44Socket::Socket(Type type, Object* parent) 45 : IODevice(parent) 46 , m_type(type) 47{ 48} 49 50Socket::~Socket() 51{ 52 close(); 53} 54 55bool Socket::connect(const String& hostname, int port) 56{ 57 auto* hostent = gethostbyname(hostname.characters()); 58 if (!hostent) { 59 dbg() << "Socket::connect: Unable to resolve '" << hostname << "'"; 60 return false; 61 } 62 63 IPv4Address host_address((const u8*)hostent->h_addr_list[0]); 64#ifdef CSOCKET_DEBUG 65 dbg() << "Socket::connect: Resolved '" << hostname << "' to " << host_address; 66#endif 67 return connect(host_address, port); 68} 69 70void Socket::set_blocking(bool blocking) 71{ 72 int flags = fcntl(fd(), F_GETFL, 0); 73 ASSERT(flags >= 0); 74 if (blocking) 75 flags = fcntl(fd(), F_SETFL, flags & ~O_NONBLOCK); 76 else 77 flags = fcntl(fd(), F_SETFL, flags | O_NONBLOCK); 78 ASSERT(flags == 0); 79} 80 81bool Socket::connect(const SocketAddress& address, int port) 82{ 83 ASSERT(!is_connected()); 84 ASSERT(address.type() == SocketAddress::Type::IPv4); 85#ifdef CSOCKET_DEBUG 86 dbg() << *this << " connecting to " << address << "..."; 87#endif 88 89 ASSERT(port > 0 && port <= 65535); 90 91 struct sockaddr_in addr; 92 memset(&addr, 0, sizeof(addr)); 93 auto ipv4_address = address.ipv4_address(); 94 memcpy(&addr.sin_addr.s_addr, &ipv4_address, sizeof(IPv4Address)); 95 addr.sin_family = AF_INET; 96 addr.sin_port = htons(port); 97 98 m_destination_address = address; 99 m_destination_port = port; 100 101 return common_connect((struct sockaddr*)&addr, sizeof(addr)); 102} 103 104bool Socket::connect(const SocketAddress& address) 105{ 106 ASSERT(!is_connected()); 107 ASSERT(address.type() == SocketAddress::Type::Local); 108#ifdef CSOCKET_DEBUG 109 dbg() << *this << " connecting to " << address << "..."; 110#endif 111 112 sockaddr_un saddr; 113 saddr.sun_family = AF_LOCAL; 114 strcpy(saddr.sun_path, address.to_string().characters()); 115 116 m_destination_address = address; 117 118 return common_connect((const sockaddr*)&saddr, sizeof(saddr)); 119} 120 121bool Socket::common_connect(const struct sockaddr* addr, socklen_t addrlen) 122{ 123 int rc = ::connect(fd(), addr, addrlen); 124 if (rc < 0) { 125 if (errno == EINPROGRESS) { 126#ifdef CSOCKET_DEBUG 127 dbg() << *this << " connection in progress (EINPROGRESS)"; 128#endif 129 m_notifier = Notifier::construct(fd(), Notifier::Event::Write, this); 130 m_notifier->on_ready_to_write = [this] { 131#ifdef CSOCKET_DEBUG 132 dbg() << *this << " connected!"; 133#endif 134 m_connected = true; 135 ensure_read_notifier(); 136 m_notifier->set_event_mask(Notifier::Event::None); 137 if (on_connected) 138 on_connected(); 139 }; 140 return true; 141 } 142 int saved_errno = errno; 143 fprintf(stderr, "Core::Socket: Failed to connect() to %s: %s\n", destination_address().to_string().characters(), strerror(saved_errno)); 144 errno = saved_errno; 145 return false; 146 } 147#ifdef CSOCKET_DEBUG 148 dbg() << *this << " connected ok!"; 149#endif 150 m_connected = true; 151 ensure_read_notifier(); 152 if (on_connected) 153 on_connected(); 154 return true; 155} 156 157ByteBuffer Socket::receive(int max_size) 158{ 159 auto buffer = read(max_size); 160 if (eof()) { 161 dbg() << *this << " connection appears to have closed in receive()."; 162 m_connected = false; 163 } 164 return buffer; 165} 166 167bool Socket::send(const ByteBuffer& data) 168{ 169 ssize_t nsent = ::send(fd(), data.data(), data.size(), 0); 170 if (nsent < 0) { 171 set_error(errno); 172 return false; 173 } 174 ASSERT(static_cast<size_t>(nsent) == data.size()); 175 return true; 176} 177 178void Socket::did_update_fd(int fd) 179{ 180 if (fd < 0) { 181 m_read_notifier = nullptr; 182 return; 183 } 184 if (m_connected) { 185 ensure_read_notifier(); 186 } else { 187 // I don't think it would be right if we updated the fd while not connected *but* while having a notifier.. 188 ASSERT(!m_read_notifier); 189 } 190} 191 192void Socket::ensure_read_notifier() 193{ 194 ASSERT(m_connected); 195 m_read_notifier = Notifier::construct(fd(), Notifier::Event::Read, this); 196 m_read_notifier->on_ready_to_read = [this] { 197 if (on_ready_to_read) 198 on_ready_to_read(); 199 }; 200} 201 202}