OpenTTD Source 20260311-master-g511d3794ce
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
9
10#include "../../stdafx.h"
11#include "../../debug.h"
12
13#include "tcp.h"
14
15#include "../../safeguards.h"
16
22
29{
30 if (this->sock != INVALID_SOCKET) closesocket(this->sock);
31 this->sock = INVALID_SOCKET;
32}
33
41{
42 this->MarkClosed();
43 this->writable = false;
44
45 this->packet_queue.clear();
46 this->packet_recv = nullptr;
47
49}
50
57void NetworkTCPSocketHandler::SendPacket(std::unique_ptr<Packet> &&packet)
58{
59 assert(packet != nullptr);
60
61 packet->PrepareToSend();
62 this->packet_queue.push_back(std::move(packet));
63}
64
76{
77 /* We can not write to this socket!! */
78 if (!this->writable) return SPS_NONE_SENT;
79 if (!this->IsConnected()) return SPS_CLOSED;
80
81 while (!this->packet_queue.empty()) {
82 Packet &p = *this->packet_queue.front();
83 ssize_t res = p.TransferOut(SocketSender{this->sock});
84 if (res == -1) {
86 if (!err.WouldBlock()) {
87 /* Something went wrong.. close client! */
88 if (!closing_down) {
89 Debug(net, 0, "Send failed: {}", err.AsString());
90 this->CloseConnection();
91 }
92 return SPS_CLOSED;
93 }
94 return SPS_PARTLY_SENT;
95 }
96 if (res == 0) {
97 /* Client/server has left us :( */
98 if (!closing_down) this->CloseConnection();
99 return SPS_CLOSED;
100 }
101
102 /* Is this packet sent? */
103 if (p.RemainingBytesToTransfer() == 0) {
104 /* Go to the next packet */
105 this->packet_queue.pop_front();
106 } else {
107 return SPS_PARTLY_SENT;
108 }
109 }
110
111 return SPS_ALL_SENT;
112}
113
119{
120 ssize_t res;
121
122 if (!this->IsConnected()) return nullptr;
123
124 if (this->packet_recv == nullptr) {
125 this->packet_recv = std::make_unique<Packet>(this, TCP_MTU);
126 }
127
128 Packet &p = *this->packet_recv.get();
129
130 /* Read packet size */
131 if (!p.HasPacketSizeData()) {
132 while (p.RemainingBytesToTransfer() != 0) {
133 res = p.TransferIn(SocketReceiver{this->sock});
134 if (res == -1) {
136 if (!err.WouldBlock()) {
137 /* Something went wrong... */
138 if (!err.IsConnectionReset()) Debug(net, 0, "Recv failed: {}", err.AsString());
139 this->CloseConnection();
140 return nullptr;
141 }
142 /* Connection would block, so stop for now */
143 return nullptr;
144 }
145 if (res == 0) {
146 /* Client/server has left */
147 this->CloseConnection();
148 return nullptr;
149 }
150 }
151
152 /* Parse the size in the received packet and if not valid, close the connection. */
153 if (!p.ParsePacketSize()) {
154 this->CloseConnection();
155 return nullptr;
156 }
157 }
158
159 /* Read rest of packet */
160 while (p.RemainingBytesToTransfer() != 0) {
161 res = p.TransferIn(SocketReceiver{this->sock});
162 if (res == -1) {
164 if (!err.WouldBlock()) {
165 /* Something went wrong... */
166 if (!err.IsConnectionReset()) Debug(net, 0, "Recv failed: {}", err.AsString());
167 this->CloseConnection();
168 return nullptr;
169 }
170 /* Connection would block */
171 return nullptr;
172 }
173 if (res == 0) {
174 /* Client/server has left */
175 this->CloseConnection();
176 return nullptr;
177 }
178 }
179
180 if (!p.PrepareToRead()) {
181 Debug(net, 0, "Invalid packet received (too small / decryption error)");
182 this->CloseConnection();
183 return nullptr;
184 }
185 return std::move(this->packet_recv);
186}
187
194{
195 assert(this->sock != INVALID_SOCKET);
196
197 fd_set read_fd, write_fd;
198 struct timeval tv;
199
200 FD_ZERO(&read_fd);
201 FD_ZERO(&write_fd);
202
203 FD_SET(this->sock, &read_fd);
204 FD_SET(this->sock, &write_fd);
205
206 tv.tv_sec = tv.tv_usec = 0; // don't block at all.
207 if (select(FD_SETSIZE, &read_fd, &write_fd, nullptr, &tv) < 0) return false;
208
209 this->writable = !!FD_ISSET(this->sock, &write_fd);
210 return FD_ISSET(this->sock, &read_fd) != 0;
211}
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:40
virtual std::unique_ptr< Packet > ReceivePacket()
Receives a packet for the given client.
Definition tcp.cpp:118
~NetworkTCPSocketHandler() override
Close the socket.
Definition tcp.cpp:18
bool IsConnected() const
Whether this socket is currently bound to a socket.
Definition tcp.h:42
SOCKET sock
The socket currently connected to.
Definition tcp.h:35
bool writable
Can we write to this socket?
Definition tcp.h:36
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:57
SendPacketsState SendPackets(bool closing_down=false)
Sends all the buffered packets out for this client.
Definition tcp.cpp:75
void CloseSocket()
Close the actual socket of the connection.
Definition tcp.cpp:28
bool CanSendReceive()
Check whether this socket can send or receive something.
Definition tcp.cpp:193
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
Functions related to debugging.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
A number of safeguards to prevent using unsafe methods.
Definition of base types and functions in a cross-platform compatible way.
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:174
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:145
Helper for Packet::TransferIn that reads data from a socket.
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