1 /******************************************************************************/
2 /* Mednafen - Multi-system Emulator */
3 /******************************************************************************/
4 /* Net_POSIX.cpp:
5 ** Copyright (C) 2012-2016 Mednafen Team
6 **
7 ** This program is free software; you can redistribute it and/or
8 ** modify it under the terms of the GNU General Public License
9 ** as published by the Free Software Foundation; either version 2
10 ** of the License, or (at your option) any later version.
11 **
12 ** This program is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ** GNU General Public License for more details.
16 **
17 ** You should have received a copy of the GNU General Public License
18 ** along with this program; if not, write to the Free Software Foundation, Inc.,
19 ** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21
22 #include "Net_POSIX.h"
23
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <netinet/tcp.h>
30 #include <arpa/inet.h>
31 #include <netdb.h>
32 #include <sys/time.h>
33 #include <poll.h>
34
35 #ifndef SOL_TCP
36 #define SOL_TCP IPPROTO_TCP
37 #endif
38
39 namespace Net
40 {
41
42 class POSIX_Connection : public Connection
43 {
44 public:
45
46 POSIX_Connection();
47 virtual ~POSIX_Connection() override;
48
49 virtual bool CanSend(int32 timeout = 0) override;
50 virtual bool CanReceive(int32 timeout = 0) override;
51
52 virtual uint32 Send(const void *data, uint32 len) override;
53
54 virtual uint32 Receive(void *data, uint32 len) override;
55
56 protected:
57
58 int fd = -1;
59 bool fully_established = false;
60 };
61
62 class POSIX_Client : public POSIX_Connection
63 {
64 public:
65 POSIX_Client(const char *host, unsigned int port);
66
67 virtual bool Established(int32 timeout = 0) override;
68 };
69
70 #if 0
71 class POSIX_Server : public POSIX_Connection
72 {
73 public:
74 POSIX_Server(unsigned int port);
75
76 virtual bool Established(bool wait = false) override;
77 };
78 #endif
79
POSIX_Client(const char * host,unsigned int port)80 POSIX_Client::POSIX_Client(const char *host, unsigned int port)
81 {
82 {
83 struct addrinfo hints;
84 struct addrinfo *result;
85 int rv;
86 char service[64];
87
88 fd = -1;
89
90 snprintf(service, sizeof(service), "%u", port);
91
92 memset(&hints, 0, sizeof(struct addrinfo));
93 hints.ai_family = AF_UNSPEC;
94 hints.ai_socktype = SOCK_STREAM;
95 #ifdef AI_ADDRCONFIG
96 hints.ai_flags = AI_ADDRCONFIG;
97 #else
98 hints.ai_flags = 0;
99 #endif
100 hints.ai_protocol = 0;
101
102 if((rv = getaddrinfo(host, service, &hints, &result)) != 0)
103 {
104 if(rv == EAI_SYSTEM)
105 {
106 ErrnoHolder ene(errno);
107 throw MDFN_Error(ene.Errno(), _("getaddrinfo() failed: %s"), ene.StrError());
108 }
109 else
110 throw MDFN_Error(0, _("getaddrinfo() failed: %s"), gai_strerror(rv));
111 }
112
113 for(int tryit = 0; tryit < 2; tryit++) // Quick hackish way to "sort" IPv4 ahead of everything else.
114 {
115 for(struct addrinfo *rp = result; rp != NULL; rp = rp->ai_next)
116 {
117 //printf("%u\n", rp->ai_family);
118
119 if(tryit == 0 && rp->ai_family != AF_INET)
120 continue;
121
122 fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
123 if(fd == -1)
124 {
125 ErrnoHolder ene(errno);
126
127 freeaddrinfo(result);
128
129 throw(MDFN_Error(ene.Errno(), _("socket() failed: %s"), ene.StrError()));
130 }
131
132 #ifdef SO_NOSIGPIPE
133 {
134 int opt = 1;
135
136 if(setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)) == -1)
137 {
138 ErrnoHolder ene(errno);
139
140 throw MDFN_Error(ene.Errno(), _("setsockopt() failed: %s"), ene.StrError());
141 }
142 }
143 #endif
144
145 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
146
147 TryConnectAgain:;
148 if(connect(fd, rp->ai_addr, rp->ai_addrlen) == -1)
149 {
150 if(errno == EINTR)
151 goto TryConnectAgain;
152 else if(errno != EINPROGRESS)
153 {
154 ErrnoHolder ene(errno);
155
156 freeaddrinfo(result);
157 close(fd);
158 fd = -1;
159
160 throw MDFN_Error(ene.Errno(), _("connect() failed: %s"), ene.StrError());
161 }
162 }
163 goto BreakOut;
164 }
165 }
166
167 BreakOut: ;
168
169 freeaddrinfo(result);
170 result = NULL;
171
172 if(fd == -1)
173 {
174 throw MDFN_Error(0, "BOOGA BOOGA");
175 }
176 }
177 }
178
Established(int32 timeout)179 bool POSIX_Client::Established(int32 timeout)
180 {
181 if(fully_established)
182 return true;
183
184 if(!CanSend(timeout))
185 return false;
186
187 {
188 int errc = 0;
189 socklen_t errc_len = sizeof(errc);
190
191 if(getsockopt(fd, SOL_SOCKET, SO_ERROR, &errc, &errc_len) == -1)
192 {
193 ErrnoHolder ene(errno);
194
195 throw MDFN_Error(ene.Errno(), _("getsockopt() failed: %s"), ene.StrError());
196 }
197 else if(errc)
198 {
199 ErrnoHolder ene(errc);
200
201 throw MDFN_Error(ene.Errno(), _("connect() failed: %s"), ene.StrError());
202 }
203 }
204
205 {
206 int tcpopt = 1;
207 if(setsockopt(fd, SOL_TCP, TCP_NODELAY, &tcpopt, sizeof(int)) == -1)
208 {
209 ErrnoHolder ene(errno);
210
211 throw MDFN_Error(ene.Errno(), _("setsockopt() failed: %s"), ene.StrError());
212 }
213 }
214
215 fully_established = true;
216
217 return true;
218 }
219
POSIX_Connection()220 POSIX_Connection::POSIX_Connection()
221 {
222
223 }
224
~POSIX_Connection()225 POSIX_Connection::~POSIX_Connection()
226 {
227 if(fd != -1)
228 {
229 //shutdown(fd, SHUT_RDWR); // TODO: investigate usage scenarios
230 close(fd);
231 fd = -1;
232 }
233 }
234
235 //
236 // Use poll() instead of select() so the code doesn't malfunction when
237 // exceeding the FD_SETSIZE ceiling(which can occur in some quasi-pathological Mednafen use cases, such as making and running with an M3U file
238 // that ultimately references thousands of files through CUE sheets).
239 //
CanSend(int32 timeout)240 bool POSIX_Connection::CanSend(int32 timeout)
241 {
242 int rv;
243 struct pollfd fds[1];
244
245 TryAgain:
246 memset(fds, 0, sizeof(fds));
247 fds[0].fd = fd;
248 fds[0].events = POLLOUT | POLLHUP | POLLERR;
249 rv = poll(fds, 1, ((timeout >= 0) ? (timeout + 500) / 1000 : -1));
250
251 if(rv == -1)
252 {
253 if(errno == EINTR)
254 {
255 timeout = 0;
256 goto TryAgain;
257 }
258
259 ErrnoHolder ene(errno);
260
261 throw MDFN_Error(ene.Errno(), _("poll() failed: %s"), ene.StrError());
262 }
263
264 return (bool)(fds[0].revents & (POLLOUT | POLLERR));
265 }
266
CanReceive(int32 timeout)267 bool POSIX_Connection::CanReceive(int32 timeout)
268 {
269 int rv;
270 struct pollfd fds[1];
271
272 TryAgain:
273 memset(fds, 0, sizeof(fds));
274 fds[0].fd = fd;
275 fds[0].events = POLLIN | POLLHUP | POLLERR;
276 rv = poll(fds, 1, ((timeout >= 0) ? (timeout + 500) / 1000 : -1));
277
278 if(rv == -1)
279 {
280 if(errno == EINTR)
281 {
282 timeout = 0;
283 goto TryAgain;
284 }
285
286 ErrnoHolder ene(errno);
287
288 throw MDFN_Error(ene.Errno(), _("poll() failed: %s"), ene.StrError());
289 }
290
291 return (bool)(fds[0].revents & (POLLIN | POLLHUP | POLLERR));
292 }
293
Send(const void * data,uint32 len)294 uint32 POSIX_Connection::Send(const void* data, uint32 len)
295 {
296 if(!fully_established)
297 throw MDFN_Error(0, _("Bug: Send() called when connection not fully established."));
298
299 ssize_t rv;
300
301 #ifdef MSG_NOSIGNAL
302 rv = send(fd, data, len, MSG_NOSIGNAL);
303 #else
304 rv = send(fd, data, len, 0);
305 #endif
306
307 if(rv < 0)
308 {
309 if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
310 {
311 ErrnoHolder ene(errno);
312
313 throw MDFN_Error(ene.Errno(), _("send() failed: %s"), ene.StrError());
314 }
315 return(0);
316 }
317
318 return rv;
319 }
320
Receive(void * data,uint32 len)321 uint32 POSIX_Connection::Receive(void* data, uint32 len)
322 {
323 if(!fully_established)
324 throw MDFN_Error(0, _("Bug: Receive() called when connection not fully established."));
325
326 ssize_t rv;
327
328 rv = recv(fd, data, len, 0);
329
330 if(rv < 0)
331 {
332 if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
333 {
334 ErrnoHolder ene(errno);
335
336 throw MDFN_Error(ene.Errno(), _("recv() failed: %s"), ene.StrError());
337 }
338 return(0);
339 }
340 else if(rv == 0)
341 {
342 throw MDFN_Error(0, _("recv() failed: peer has closed connection"));
343 }
344 return rv;
345 }
346
POSIX_Connect(const char * host,unsigned int port)347 std::unique_ptr<Connection> POSIX_Connect(const char* host, unsigned int port)
348 {
349 return std::unique_ptr<Connection>(new POSIX_Client(host, port));
350 }
351
352 #if 0
353 std::unique_ptr<Connection> POSIX_Accept(unsigned int port)
354 {
355 return new POSIX_Server(port);
356 }
357 #endif
358
359 }
360