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 
8 /**
9  * @file tcp.cpp Basic functions to receive and send TCP packets.
10  */
11 
12 #include "../../stdafx.h"
13 #include "../../debug.h"
14 
15 #include "tcp.h"
16 
17 #include "../../safeguards.h"
18 
19 /**
20  * Construct a socket handler for a TCP connection.
21  * @param s The just opened TCP connection.
22  */
NetworkTCPSocketHandler(SOCKET s)23 NetworkTCPSocketHandler::NetworkTCPSocketHandler(SOCKET s) :
24 		NetworkSocketHandler(),
25 		packet_queue(nullptr), packet_recv(nullptr),
26 		sock(s), writable(false)
27 {
28 }
29 
~NetworkTCPSocketHandler()30 NetworkTCPSocketHandler::~NetworkTCPSocketHandler()
31 {
32 	this->EmptyPacketQueue();
33 	this->CloseSocket();
34 }
35 
36 /**
37  * Free all pending and partially received packets.
38  */
EmptyPacketQueue()39 void NetworkTCPSocketHandler::EmptyPacketQueue()
40 {
41 	while (this->packet_queue != nullptr) {
42 		delete Packet::PopFromQueue(&this->packet_queue);
43 	}
44 	delete this->packet_recv;
45 	this->packet_recv = nullptr;
46 }
47 
48 /**
49  * Close the actual socket of the connection.
50  * Please make sure CloseConnection is called before CloseSocket, as
51  * otherwise not all resources might be released.
52  */
CloseSocket()53 void NetworkTCPSocketHandler::CloseSocket()
54 {
55 	if (this->sock != INVALID_SOCKET) closesocket(this->sock);
56 	this->sock = INVALID_SOCKET;
57 }
58 
59 /**
60  * This will put this socket handler in a close state. It will not
61  * actually close the OS socket; use CloseSocket for this.
62  * @param error Whether we quit under an error condition or not.
63  * @return new status of the connection.
64  */
CloseConnection(bool error)65 NetworkRecvStatus NetworkTCPSocketHandler::CloseConnection(bool error)
66 {
67 	this->MarkClosed();
68 	this->writable = false;
69 
70 	this->EmptyPacketQueue();
71 
72 	return NETWORK_RECV_STATUS_OKAY;
73 }
74 
75 /**
76  * This function puts the packet in the send-queue and it is send as
77  * soon as possible. This is the next tick, or maybe one tick later
78  * if the OS-network-buffer is full)
79  * @param packet the packet to send
80  */
SendPacket(Packet * packet)81 void NetworkTCPSocketHandler::SendPacket(Packet *packet)
82 {
83 	assert(packet != nullptr);
84 
85 	packet->PrepareToSend();
86 	Packet::AddToQueue(&this->packet_queue, packet);
87 }
88 
89 /**
90  * Sends all the buffered packets out for this client. It stops when:
91  *   1) all packets are send (queue is empty)
92  *   2) the OS reports back that it can not send any more
93  *      data right now (full network-buffer, it happens ;))
94  *   3) sending took too long
95  * @param closing_down Whether we are closing down the connection.
96  * @return \c true if a (part of a) packet could be sent and
97  *         the connection is not closed yet.
98  */
SendPackets(bool closing_down)99 SendPacketsState NetworkTCPSocketHandler::SendPackets(bool closing_down)
100 {
101 	ssize_t res;
102 	Packet *p;
103 
104 	/* We can not write to this socket!! */
105 	if (!this->writable) return SPS_NONE_SENT;
106 	if (!this->IsConnected()) return SPS_CLOSED;
107 
108 	while ((p = this->packet_queue) != nullptr) {
109 		res = p->TransferOut<int>(send, this->sock, 0);
110 		if (res == -1) {
111 			NetworkError err = NetworkError::GetLast();
112 			if (!err.WouldBlock()) {
113 				/* Something went wrong.. close client! */
114 				if (!closing_down) {
115 					Debug(net, 0, "Send failed: {}", err.AsString());
116 					this->CloseConnection();
117 				}
118 				return SPS_CLOSED;
119 			}
120 			return SPS_PARTLY_SENT;
121 		}
122 		if (res == 0) {
123 			/* Client/server has left us :( */
124 			if (!closing_down) this->CloseConnection();
125 			return SPS_CLOSED;
126 		}
127 
128 		/* Is this packet sent? */
129 		if (p->RemainingBytesToTransfer() == 0) {
130 			/* Go to the next packet */
131 			delete Packet::PopFromQueue(&this->packet_queue);
132 		} else {
133 			return SPS_PARTLY_SENT;
134 		}
135 	}
136 
137 	return SPS_ALL_SENT;
138 }
139 
140 /**
141  * Receives a packet for the given client
142  * @return The received packet (or nullptr when it didn't receive one)
143  */
ReceivePacket()144 Packet *NetworkTCPSocketHandler::ReceivePacket()
145 {
146 	ssize_t res;
147 
148 	if (!this->IsConnected()) return nullptr;
149 
150 	if (this->packet_recv == nullptr) {
151 		this->packet_recv = new Packet(this, TCP_MTU);
152 	}
153 
154 	Packet *p = this->packet_recv;
155 
156 	/* Read packet size */
157 	if (!p->HasPacketSizeData()) {
158 		while (p->RemainingBytesToTransfer() != 0) {
159 			res = p->TransferIn<int>(recv, this->sock, 0);
160 			if (res == -1) {
161 				NetworkError err = NetworkError::GetLast();
162 				if (!err.WouldBlock()) {
163 					/* Something went wrong... */
164 					if (!err.IsConnectionReset()) Debug(net, 0, "Recv failed: {}", err.AsString());
165 					this->CloseConnection();
166 					return nullptr;
167 				}
168 				/* Connection would block, so stop for now */
169 				return nullptr;
170 			}
171 			if (res == 0) {
172 				/* Client/server has left */
173 				this->CloseConnection();
174 				return nullptr;
175 			}
176 		}
177 
178 		/* Parse the size in the received packet and if not valid, close the connection. */
179 		if (!p->ParsePacketSize()) {
180 			this->CloseConnection();
181 			return nullptr;
182 		}
183 	}
184 
185 	/* Read rest of packet */
186 	while (p->RemainingBytesToTransfer() != 0) {
187 		res = p->TransferIn<int>(recv, this->sock, 0);
188 		if (res == -1) {
189 			NetworkError err = NetworkError::GetLast();
190 			if (!err.WouldBlock()) {
191 				/* Something went wrong... */
192 				if (!err.IsConnectionReset()) Debug(net, 0, "Recv failed: {}", err.AsString());
193 				this->CloseConnection();
194 				return nullptr;
195 			}
196 			/* Connection would block */
197 			return nullptr;
198 		}
199 		if (res == 0) {
200 			/* Client/server has left */
201 			this->CloseConnection();
202 			return nullptr;
203 		}
204 	}
205 
206 	/* Prepare for receiving a new packet */
207 	this->packet_recv = nullptr;
208 
209 	p->PrepareToRead();
210 	return p;
211 }
212 
213 /**
214  * Check whether this socket can send or receive something.
215  * @return \c true when there is something to receive.
216  * @note Sets #writable if more data can be sent.
217  */
CanSendReceive()218 bool NetworkTCPSocketHandler::CanSendReceive()
219 {
220 	fd_set read_fd, write_fd;
221 	struct timeval tv;
222 
223 	FD_ZERO(&read_fd);
224 	FD_ZERO(&write_fd);
225 
226 	FD_SET(this->sock, &read_fd);
227 	FD_SET(this->sock, &write_fd);
228 
229 	tv.tv_sec = tv.tv_usec = 0; // don't block at all.
230 	if (select(FD_SETSIZE, &read_fd, &write_fd, nullptr, &tv) < 0) return false;
231 
232 	this->writable = !!FD_ISSET(this->sock, &write_fd);
233 	return FD_ISSET(this->sock, &read_fd) != 0;
234 }
235