|
| 1 | +/* SPDX-License-Identifier: GPL-3.0-or-later */ |
| 2 | +/* Copyright © 2016-2024 Byteduck */ |
| 3 | + |
| 4 | +#include "IPSocket.h" |
| 5 | +#include "UDPSocket.h" |
| 6 | +#include "../api/in.h" |
| 7 | +#include "../kstd/KLog.h" |
| 8 | +#include "../tasking/PollBlocker.h" |
| 9 | +#include "../filesystem/FileDescriptor.h" |
| 10 | + |
| 11 | +IPSocket::IPSocket(Socket::Type type, int protocol): Socket(Domain::Inet, type, protocol) { |
| 12 | + |
| 13 | +} |
| 14 | + |
| 15 | +ResultRet<kstd::Arc<IPSocket>> IPSocket::make(Socket::Type type, int protocol) { |
| 16 | + switch (type) { |
| 17 | + case Type::Dgram: |
| 18 | + return kstd::static_pointer_cast<IPSocket>(TRY(UDPSocket::make())); |
| 19 | + default: |
| 20 | + return Result(EINVAL); |
| 21 | + } |
| 22 | +} |
| 23 | + |
| 24 | +Result IPSocket::bind(SafePointer<sockaddr> addr_ptr, socklen_t addrlen) { |
| 25 | + if (m_bound || addrlen != sizeof(sockaddr_in)) |
| 26 | + return Result(set_error(EINVAL)); |
| 27 | + |
| 28 | + auto addr = addr_ptr.as<sockaddr_in>().get(); |
| 29 | + if (addr.sin_family != AF_INET) |
| 30 | + return Result(set_error(EINVAL)); |
| 31 | + |
| 32 | + m_port = from_big_endian(addr.sin_port); |
| 33 | + m_addr = IPv4Address(from_big_endian(addr.sin_addr.s_addr)); |
| 34 | + |
| 35 | + return do_bind(); |
| 36 | +} |
| 37 | + |
| 38 | +ssize_t IPSocket::recvfrom(FileDescriptor& fd, SafePointer<uint8_t> buf, size_t len, int flags, SafePointer<sockaddr> src_addr, SafePointer<socklen_t> addrlen) { |
| 39 | + m_receive_queue_lock.acquire(); |
| 40 | + |
| 41 | + // Block until we have a packet to read |
| 42 | + while (m_receive_queue.empty()) { |
| 43 | + if (fd.nonblock()) { |
| 44 | + m_receive_queue_lock.release(); |
| 45 | + return -EAGAIN; |
| 46 | + } |
| 47 | + |
| 48 | + update_blocker(); |
| 49 | + m_receive_queue_lock.release(); |
| 50 | + TaskManager::current_thread()->block(m_receive_blocker); |
| 51 | + m_receive_queue_lock.acquire(); |
| 52 | + } |
| 53 | + |
| 54 | + // Read our packet |
| 55 | + auto* packet = m_receive_queue.pop_front(); |
| 56 | + update_blocker(); |
| 57 | + m_receive_queue_lock.release(); |
| 58 | + auto res = do_recv(packet, buf, len); |
| 59 | + kfree(packet); |
| 60 | + return res; |
| 61 | +} |
| 62 | + |
| 63 | +Result IPSocket::recv_packet(const void* buf, size_t len) { |
| 64 | + LOCK(m_receive_queue_lock); |
| 65 | + |
| 66 | + if (m_receive_queue.size() == m_receive_queue.capacity()) { |
| 67 | + KLog::warn("IPSocket", "Dropping packet because receive queue is full"); |
| 68 | + return Result(ENOSPC); |
| 69 | + } |
| 70 | + |
| 71 | + auto* src_pkt = (const IPv4Packet*) buf; |
| 72 | + auto* new_pkt = (IPv4Packet*) kmalloc(len); |
| 73 | + memcpy(new_pkt, src_pkt, len); |
| 74 | + |
| 75 | + m_receive_queue.push_back(new_pkt); |
| 76 | + update_blocker(); |
| 77 | + |
| 78 | + return Result(SUCCESS); |
| 79 | +} |
| 80 | + |
| 81 | +bool IPSocket::can_read(const FileDescriptor& fd) { |
| 82 | + return !m_receive_queue.empty(); |
| 83 | +} |
| 84 | + |
| 85 | +void IPSocket::update_blocker() { |
| 86 | + m_receive_blocker.set_ready(!m_receive_queue.empty()); |
| 87 | +} |
0 commit comments