OpenTTD Source  20241108-master-g80f628063a
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 <http://www.gnu.org/licenses/>.
6  */
7 
12 #include "../../stdafx.h"
13 #include "../../debug.h"
14 
15 #include "tcp.h"
16 
17 #include "../../safeguards.h"
18 
25  sock(s), writable(false)
26 {
27 }
28 
29 NetworkTCPSocketHandler::~NetworkTCPSocketHandler()
30 {
31  this->CloseSocket();
32 }
33 
40 {
41  if (this->sock != INVALID_SOCKET) closesocket(this->sock);
42  this->sock = INVALID_SOCKET;
43 }
44 
52 {
53  this->MarkClosed();
54  this->writable = false;
55 
56  this->packet_queue.clear();
57  this->packet_recv = nullptr;
58 
60 }
61 
68 void NetworkTCPSocketHandler::SendPacket(std::unique_ptr<Packet> &&packet)
69 {
70  assert(packet != nullptr);
71 
72  packet->PrepareToSend();
73  this->packet_queue.push_back(std::move(packet));
74 }
75 
87 {
88  /* We can not write to this socket!! */
89  if (!this->writable) return SPS_NONE_SENT;
90  if (!this->IsConnected()) return SPS_CLOSED;
91 
92  while (!this->packet_queue.empty()) {
93  Packet &p = *this->packet_queue.front();
94  ssize_t res = p.TransferOut<int>(send, this->sock, 0);
95  if (res == -1) {
97  if (!err.WouldBlock()) {
98  /* Something went wrong.. close client! */
99  if (!closing_down) {
100  Debug(net, 0, "Send failed: {}", err.AsString());
101  this->CloseConnection();
102  }
103  return SPS_CLOSED;
104  }
105  return SPS_PARTLY_SENT;
106  }
107  if (res == 0) {
108  /* Client/server has left us :( */
109  if (!closing_down) this->CloseConnection();
110  return SPS_CLOSED;
111  }
112 
113  /* Is this packet sent? */
114  if (p.RemainingBytesToTransfer() == 0) {
115  /* Go to the next packet */
116  this->packet_queue.pop_front();
117  } else {
118  return SPS_PARTLY_SENT;
119  }
120  }
121 
122  return SPS_ALL_SENT;
123 }
124 
129 std::unique_ptr<Packet> NetworkTCPSocketHandler::ReceivePacket()
130 {
131  ssize_t res;
132 
133  if (!this->IsConnected()) return nullptr;
134 
135  if (this->packet_recv == nullptr) {
136  this->packet_recv = std::make_unique<Packet>(this, TCP_MTU);
137  }
138 
139  Packet &p = *this->packet_recv.get();
140 
141  /* Read packet size */
142  if (!p.HasPacketSizeData()) {
143  while (p.RemainingBytesToTransfer() != 0) {
144  res = p.TransferIn<int>(recv, this->sock, 0);
145  if (res == -1) {
147  if (!err.WouldBlock()) {
148  /* Something went wrong... */
149  if (!err.IsConnectionReset()) Debug(net, 0, "Recv failed: {}", err.AsString());
150  this->CloseConnection();
151  return nullptr;
152  }
153  /* Connection would block, so stop for now */
154  return nullptr;
155  }
156  if (res == 0) {
157  /* Client/server has left */
158  this->CloseConnection();
159  return nullptr;
160  }
161  }
162 
163  /* Parse the size in the received packet and if not valid, close the connection. */
164  if (!p.ParsePacketSize()) {
165  this->CloseConnection();
166  return nullptr;
167  }
168  }
169 
170  /* Read rest of packet */
171  while (p.RemainingBytesToTransfer() != 0) {
172  res = p.TransferIn<int>(recv, this->sock, 0);
173  if (res == -1) {
175  if (!err.WouldBlock()) {
176  /* Something went wrong... */
177  if (!err.IsConnectionReset()) Debug(net, 0, "Recv failed: {}", err.AsString());
178  this->CloseConnection();
179  return nullptr;
180  }
181  /* Connection would block */
182  return nullptr;
183  }
184  if (res == 0) {
185  /* Client/server has left */
186  this->CloseConnection();
187  return nullptr;
188  }
189  }
190 
191  if (!p.PrepareToRead()) {
192  Debug(net, 0, "Invalid packet received (too small / decryption error)");
193  this->CloseConnection();
194  return nullptr;
195  }
196  return std::move(this->packet_recv);
197 }
198 
205 {
206  assert(this->sock != INVALID_SOCKET);
207 
208  fd_set read_fd, write_fd;
209  struct timeval tv;
210 
211  FD_ZERO(&read_fd);
212  FD_ZERO(&write_fd);
213 
214  FD_SET(this->sock, &read_fd);
215  FD_SET(this->sock, &write_fd);
216 
217  tv.tv_sec = tv.tv_usec = 0; // don't block at all.
218  if (select(FD_SETSIZE, &read_fd, &write_fd, nullptr, &tv) < 0) return false;
219 
220  this->writable = !!FD_ISSET(this->sock, &write_fd);
221  return FD_ISSET(this->sock, &read_fd) != 0;
222 }
Abstraction of a network error where all implementation details of the error codes are encapsulated i...
bool WouldBlock() const
Check whether this error describes that the operation would block.
bool IsConnectionReset() const
Check whether this error describes a connection reset.
const std::string & AsString() const
Get the string representation of the error message.
static NetworkError GetLast()
Get the last network error.
SocketHandler for all network sockets in OpenTTD.
Definition: core.h:43
void MarkClosed()
Mark the connection as closed.
Definition: core.h:66
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:33
virtual NetworkRecvStatus CloseConnection(bool error=true)
This will put this socket handler in a close state.
Definition: tcp.cpp:51
virtual std::unique_ptr< Packet > ReceivePacket()
Receives a packet for the given client.
Definition: tcp.cpp:129
bool IsConnected() const
Whether this socket is currently bound to a socket.
Definition: tcp.h:45
NetworkTCPSocketHandler(SOCKET s=INVALID_SOCKET)
Construct a socket handler for a TCP connection.
Definition: tcp.cpp:23
SOCKET sock
The socket currently connected to.
Definition: tcp.h:38
bool writable
Can we write to this socket?
Definition: tcp.h:39
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:68
SendPacketsState SendPackets(bool closing_down=false)
Sends all the buffered packets out for this client.
Definition: tcp.cpp:86
void CloseSocket()
Close the actual socket of the connection.
Definition: tcp.cpp:39
bool CanSendReceive()
Check whether this socket can send or receive something.
Definition: tcp.cpp:204
std::unique_ptr< Packet > packet_recv
Partially received packet.
Definition: tcp.h:34
static const size_t TCP_MTU
Number of bytes we can pack in a single TCP packet.
Definition: config.h:45
NetworkRecvStatus
Status of a network client; reasons why a client has quit.
Definition: core.h:23
@ NETWORK_RECV_STATUS_OKAY
Everything is okay.
Definition: core.h:24
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
Definition: debug.h:37
Internal entity of a packet.
Definition: packet.h:42
bool PrepareToRead()
Prepares the packet so it can be read.
Definition: packet.cpp:278
bool HasPacketSizeData() const
Check whether the packet, given the position of the "write" pointer, has read enough of the packet to...
Definition: packet.cpp:238
bool ParsePacketSize()
Reads the packet size from the raw packet and stores it in the packet->size.
Definition: packet.cpp:259
ssize_t TransferIn(F transfer_function, S source, Args &&... args)
Transfer data from the given function into the packet.
Definition: packet.h:174
size_t RemainingBytesToTransfer() const
Get the amount of bytes that are still available for the Transfer functions.
Definition: packet.cpp:447
ssize_t TransferOut(F transfer_function, D destination, Args &&... args)
Transfer data from the packet to the given function.
Definition: packet.h:139
Basic functions to receive and send TCP packets.
SendPacketsState
The states of sending the packets.
Definition: tcp.h:23
@ SPS_PARTLY_SENT
The packets are partly sent; there are more packets to be sent in the queue.
Definition: tcp.h:26
@ SPS_ALL_SENT
All packets in the queue are sent.
Definition: tcp.h:27
@ SPS_NONE_SENT
The buffer is still full, so no (parts of) packets could be sent.
Definition: tcp.h:25
@ SPS_CLOSED
The connection got closed.
Definition: tcp.h:24