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