OpenTTD Source 20260108-master-g8ba1860eaa
tcp.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
10#include "../../stdafx.h"
11#include "../../debug.h"
12
13#include "tcp.h"
14
15#include "../../safeguards.h"
16
17NetworkTCPSocketHandler::~NetworkTCPSocketHandler()
18{
19 this->CloseSocket();
20}
21
28{
29 if (this->sock != INVALID_SOCKET) closesocket(this->sock);
30 this->sock = INVALID_SOCKET;
31}
32
40{
41 this->MarkClosed();
42 this->writable = false;
43
44 this->packet_queue.clear();
45 this->packet_recv = nullptr;
46
48}
49
56void NetworkTCPSocketHandler::SendPacket(std::unique_ptr<Packet> &&packet)
57{
58 assert(packet != nullptr);
59
60 packet->PrepareToSend();
61 this->packet_queue.push_back(std::move(packet));
62}
63
75{
76 /* We can not write to this socket!! */
77 if (!this->writable) return SPS_NONE_SENT;
78 if (!this->IsConnected()) return SPS_CLOSED;
79
80 while (!this->packet_queue.empty()) {
81 Packet &p = *this->packet_queue.front();
82 ssize_t res = p.TransferOut(SocketSender{this->sock});
83 if (res == -1) {
85 if (!err.WouldBlock()) {
86 /* Something went wrong.. close client! */
87 if (!closing_down) {
88 Debug(net, 0, "Send failed: {}", err.AsString());
89 this->CloseConnection();
90 }
91 return SPS_CLOSED;
92 }
93 return SPS_PARTLY_SENT;
94 }
95 if (res == 0) {
96 /* Client/server has left us :( */
97 if (!closing_down) this->CloseConnection();
98 return SPS_CLOSED;
99 }
100
101 /* Is this packet sent? */
102 if (p.RemainingBytesToTransfer() == 0) {
103 /* Go to the next packet */
104 this->packet_queue.pop_front();
105 } else {
106 return SPS_PARTLY_SENT;
107 }
108 }
109
110 return SPS_ALL_SENT;
111}
112
118{
119 ssize_t res;
120
121 if (!this->IsConnected()) return nullptr;
122
123 if (this->packet_recv == nullptr) {
124 this->packet_recv = std::make_unique<Packet>(this, TCP_MTU);
125 }
126
127 Packet &p = *this->packet_recv.get();
128
129 /* Read packet size */
130 if (!p.HasPacketSizeData()) {
131 while (p.RemainingBytesToTransfer() != 0) {
132 res = p.TransferIn(SocketReceiver{this->sock});
133 if (res == -1) {
135 if (!err.WouldBlock()) {
136 /* Something went wrong... */
137 if (!err.IsConnectionReset()) Debug(net, 0, "Recv failed: {}", err.AsString());
138 this->CloseConnection();
139 return nullptr;
140 }
141 /* Connection would block, so stop for now */
142 return nullptr;
143 }
144 if (res == 0) {
145 /* Client/server has left */
146 this->CloseConnection();
147 return nullptr;
148 }
149 }
150
151 /* Parse the size in the received packet and if not valid, close the connection. */
152 if (!p.ParsePacketSize()) {
153 this->CloseConnection();
154 return nullptr;
155 }
156 }
157
158 /* Read rest of packet */
159 while (p.RemainingBytesToTransfer() != 0) {
160 res = p.TransferIn(SocketReceiver{this->sock});
161 if (res == -1) {
163 if (!err.WouldBlock()) {
164 /* Something went wrong... */
165 if (!err.IsConnectionReset()) Debug(net, 0, "Recv failed: {}", err.AsString());
166 this->CloseConnection();
167 return nullptr;
168 }
169 /* Connection would block */
170 return nullptr;
171 }
172 if (res == 0) {
173 /* Client/server has left */
174 this->CloseConnection();
175 return nullptr;
176 }
177 }
178
179 if (!p.PrepareToRead()) {
180 Debug(net, 0, "Invalid packet received (too small / decryption error)");
181 this->CloseConnection();
182 return nullptr;
183 }
184 return std::move(this->packet_recv);
185}
186
193{
194 assert(this->sock != INVALID_SOCKET);
195
196 fd_set read_fd, write_fd;
197 struct timeval tv;
198
199 FD_ZERO(&read_fd);
200 FD_ZERO(&write_fd);
201
202 FD_SET(this->sock, &read_fd);
203 FD_SET(this->sock, &write_fd);
204
205 tv.tv_sec = tv.tv_usec = 0; // don't block at all.
206 if (select(FD_SETSIZE, &read_fd, &write_fd, nullptr, &tv) < 0) return false;
207
208 this->writable = !!FD_ISSET(this->sock, &write_fd);
209 return FD_ISSET(this->sock, &read_fd) != 0;
210}
Abstraction of a network error where all implementation details of the error codes are encapsulated i...
std::string_view AsString() const
Get the string representation of the error message.
bool WouldBlock() const
Check whether this error describes that the operation would block.
bool IsConnectionReset() const
Check whether this error describes a connection reset.
static NetworkError GetLast()
Get the last network error.
void MarkClosed()
Mark the connection as closed.
Definition core.h:64
std::deque< std::unique_ptr< Packet > > packet_queue
Packets that are awaiting delivery. Cannot be std::queue as that does not have a clear() function.
Definition tcp.h:31
virtual NetworkRecvStatus CloseConnection(bool error=true)
This will put this socket handler in a close state.
Definition tcp.cpp:39
virtual std::unique_ptr< Packet > ReceivePacket()
Receives a packet for the given client.
Definition tcp.cpp:117
bool IsConnected() const
Whether this socket is currently bound to a socket.
Definition tcp.h:43
SOCKET sock
The socket currently connected to.
Definition tcp.h:36
bool writable
Can we write to this socket?
Definition tcp.h:37
virtual void SendPacket(std::unique_ptr< Packet > &&packet)
This function puts the packet in the send-queue and it is send as soon as possible.
Definition tcp.cpp:56
SendPacketsState SendPackets(bool closing_down=false)
Sends all the buffered packets out for this client.
Definition tcp.cpp:74
void CloseSocket()
Close the actual socket of the connection.
Definition tcp.cpp:27
bool CanSendReceive()
Check whether this socket can send or receive something.
Definition tcp.cpp:192
std::unique_ptr< Packet > packet_recv
Partially received packet.
Definition tcp.h:32
static const size_t TCP_MTU
Number of bytes we can pack in a single TCP packet.
Definition config.h:43
NetworkRecvStatus
Status of a network client; reasons why a client has quit.
Definition core.h:21
@ NETWORK_RECV_STATUS_OKAY
Everything is okay.
Definition core.h:22
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
Internal entity of a packet.
Definition packet.h:41
bool PrepareToRead()
Prepares the packet so it can be read.
Definition packet.cpp:276
ssize_t TransferIn(F transfer_function)
Transfer data from the given function into the packet.
Definition packet.h:155
bool HasPacketSizeData() const
Check whether the packet, given the position of the "write" pointer, has read enough of the packet to...
Definition packet.cpp:236
bool ParsePacketSize()
Reads the packet size from the raw packet and stores it in the packet->size.
Definition packet.cpp:257
size_t RemainingBytesToTransfer() const
Get the amount of bytes that are still available for the Transfer functions.
Definition packet.cpp:445
ssize_t TransferOut(F transfer_function)
Transfer data from the packet to the given function.
Definition packet.h:126
IPv4 addresses should be 4 bytes.
Basic functions to receive and send TCP packets.
SendPacketsState
The states of sending the packets.
Definition tcp.h:21
@ SPS_PARTLY_SENT
The packets are partly sent; there are more packets to be sent in the queue.
Definition tcp.h:24
@ SPS_ALL_SENT
All packets in the queue are sent.
Definition tcp.h:25
@ SPS_NONE_SENT
The buffer is still full, so no (parts of) packets could be sent.
Definition tcp.h:23
@ SPS_CLOSED
The connection got closed.
Definition tcp.h:22