]> git.mxchange.org Git - simgear.git/blob - simgear/io/raw_socket.cxx
c07165809f8002f2241ef30c14377db05ff0cc2e
[simgear.git] / simgear / io / raw_socket.cxx
1 /*
2      simgear::Socket, adapted from PLIB Socket by James Turner
3      Copyright (C) 2010  James Turner
4      
5      PLIB - A Suite of Portable Game Libraries
6      Copyright (C) 1998,2002  Steve Baker
7  
8      This library is free software; you can redistribute it and/or
9      modify it under the terms of the GNU Library General Public
10      License as published by the Free Software Foundation; either
11      version 2 of the License, or (at your option) any later version.
12  
13      This library is distributed in the hope that it will be useful,
14      but WITHOUT ANY WARRANTY; without even the implied warranty of
15      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16      Library General Public License for more details.
17  
18      You should have received a copy of the GNU Library General Public
19      License along with this library; if not, write to the Free Software
20      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #  include <simgear_config.h>
25 #endif
26
27 #include <simgear/compiler.h>
28 #include "raw_socket.hxx"
29
30 #if defined(_WIN32) && !defined(__CYGWIN__)
31 # define WINSOCK // use winsock convetions, otherwise standard POSIX
32 #endif
33
34 #include <cstdlib>
35 #include <ctime>
36 #include <cstring>
37 #include <cassert>
38 #include <cstdio> // for snprintf
39 #include <errno.h>
40
41 #if defined(WINSOCK)
42 #  include <winsock2.h>
43 #  include <ws2tcpip.h>
44 #  include <stdarg.h>
45 #else
46 #  include <sys/types.h>
47 #  include <sys/socket.h>
48 #  include <netinet/in.h>
49 #  include <arpa/inet.h>
50 #  include <sys/time.h>  
51 #  include <unistd.h>
52 #  include <netdb.h>
53 #  include <fcntl.h>
54 #endif
55
56 #if defined(_MSC_VER) && !defined(socklen_t)
57 #define socklen_t int
58 #endif
59
60 #include <simgear/debug/logstream.hxx>
61 #include <simgear/structure/exception.hxx>
62
63 namespace simgear
64 {
65                                                                                        
66 IPAddress::IPAddress ( const char* host, int port )
67 {
68   set ( host, port ) ;
69 }
70
71 IPAddress::IPAddress( const IPAddress& other ) :
72   addr(NULL)
73 {
74   if (other.addr) {
75     addr = (struct sockaddr_in*) malloc(sizeof(struct sockaddr_in));
76     memcpy(addr, other.addr, sizeof(struct sockaddr_in));
77   }
78 }
79
80 const IPAddress& IPAddress::operator=(const IPAddress& other)
81 {
82   if (addr) {
83     free(addr);
84     addr = NULL;
85   }
86
87   if (other.addr) {
88     addr = (struct sockaddr_in*) malloc(sizeof(struct sockaddr_in));
89     memcpy(addr, other.addr, sizeof(struct sockaddr_in));
90   }
91
92   return *this;
93 }
94
95 void IPAddress::set ( const char* host, int port )
96 {
97   addr = (struct sockaddr_in*) malloc(sizeof(struct sockaddr_in));
98   memset(addr, 0, sizeof(struct sockaddr_in));
99
100   addr->sin_family = AF_INET ;
101   addr->sin_port = htons (port);
102
103   /* Convert a string specifying a host name or one of a few symbolic
104   ** names to a numeric IP address.  This usually calls getaddrinfo()
105   ** to do the work; the names "" and "<broadcast>" are special.
106   */
107
108   if (!host || host[0] == '\0') {
109     addr->sin_addr.s_addr = INADDR_ANY;
110     return;
111   }
112   
113   if (strcmp(host, "<broadcast>") == 0) {
114     addr->sin_addr.s_addr = INADDR_BROADCAST;
115     return;
116   }
117   
118   struct addrinfo hints;
119   memset(&hints, 0, sizeof(struct addrinfo));
120   hints.ai_family = AF_INET;
121   
122   struct addrinfo* result = NULL;
123   int err = getaddrinfo(host, NULL, NULL /* no hints */, &result);
124   if (err) {
125     SG_LOG(SG_IO, SG_WARN, "getaddrinfo failed for '" << host << "' : " << gai_strerror(err));
126   } else if (result->ai_addrlen != getAddrLen()) {
127     SG_LOG(SG_IO, SG_ALERT, "mismatch in socket address sizes: got " <<
128         result->ai_addrlen << ", expected " << getAddrLen());
129     SG_LOG(SG_IO, SG_ALERT, "family:" << result->ai_family);
130   } else {
131     memcpy(addr, result->ai_addr, result->ai_addrlen);
132   }
133   
134   freeaddrinfo(result);
135   addr->sin_port = htons (port); // fix up port after getaddrinfo
136 }
137
138 IPAddress::~IPAddress()
139 {
140   if (addr) {
141     free (addr);
142   }
143 }
144
145 /* Create a string object representing an IP address.
146    This is always a string of the form 'dd.dd.dd.dd' (with variable
147    size numbers). */
148
149 const char* IPAddress::getHost () const
150 {
151   static char buf [32];
152         long x = ntohl(addr->sin_addr.s_addr);
153         sprintf(buf, "%d.%d.%d.%d",
154                 (int) (x>>24) & 0xff, (int) (x>>16) & 0xff,
155                 (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff );
156   return buf;
157 }
158
159 unsigned int IPAddress::getIP () const 
160
161         return addr->sin_addr.s_addr; 
162 }
163
164 unsigned int IPAddress::getPort() const
165 {
166   return ntohs(addr->sin_port);
167 }
168
169 unsigned int IPAddress::getFamily () const 
170
171         return addr->sin_family; 
172 }
173
174 const char* IPAddress::getLocalHost ()
175 {
176   //gethostbyname(gethostname())
177
178   char buf[256];
179   memset(buf, 0, sizeof(buf));
180   gethostname(buf, sizeof(buf)-1);
181   const hostent *hp = gethostbyname(buf);
182
183   if (hp && *hp->h_addr_list)
184   {
185     in_addr     addr = *((in_addr*)*hp->h_addr_list);
186     const char* host = inet_ntoa(addr);
187
188     if ( host )
189       return host ;
190   }
191
192   return "127.0.0.1" ;
193 }
194
195
196 bool IPAddress::getBroadcast () const
197 {
198   return (addr->sin_addr.s_addr == INADDR_BROADCAST);
199 }
200
201 unsigned int IPAddress::getAddrLen() const
202 {
203     return sizeof(struct sockaddr_in);
204 }
205
206 struct sockaddr* IPAddress::getAddr() const
207 {
208     return (struct sockaddr*) addr;
209 }
210
211 Socket::Socket ()
212 {
213   handle = -1 ;
214 }
215
216
217 Socket::~Socket ()
218 {
219   close () ;
220 }
221
222
223 void Socket::setHandle (int _handle)
224 {
225   close () ;
226   handle = _handle ;
227 }
228
229
230 bool Socket::open ( bool stream )
231 {
232   close () ;
233   handle = ::socket ( AF_INET, (stream? SOCK_STREAM: SOCK_DGRAM), 0 ) ;
234
235   // Jan 26, 2010: Patch to avoid the problem of the socket resource not
236   // yet being available if the program is restarted quickly after being
237   // killed.
238   //
239   // Reference: http://www.unixguide.net/network/socketfaq/4.5.shtml
240   // --
241   // Also required for joining multicast domains
242   if ( stream ) {
243     int opt_boolean = 1;
244 #if defined(_WIN32) || defined(__CYGWIN__)
245     setsockopt( handle, SOL_SOCKET, SO_REUSEADDR, (char *)&opt_boolean,
246                 sizeof(opt_boolean) );
247 #else
248     setsockopt( handle, SOL_SOCKET, SO_REUSEADDR, &opt_boolean,
249                 sizeof(opt_boolean) );
250 #endif
251   }
252
253   return (handle != -1);
254 }
255
256
257 void Socket::setBlocking ( bool blocking )
258 {
259   assert ( handle != -1 ) ;
260
261 #if defined(WINSOCK)
262     u_long nblocking = blocking? 0: 1;
263   ::ioctlsocket(handle, FIONBIO, &nblocking);
264 #else
265
266   int delay_flag = ::fcntl (handle, F_GETFL, 0);
267
268   if (blocking)
269     delay_flag &= (~O_NDELAY);
270   else
271     delay_flag |= O_NDELAY;
272
273   ::fcntl (handle, F_SETFL, delay_flag);
274 #endif
275 }
276
277
278 void Socket::setBroadcast ( bool broadcast )
279 {
280   assert ( handle != -1 ) ;
281   int result;
282   if ( broadcast ) {
283       int one = 1;
284 #if defined(_WIN32) || defined(__CYGWIN__)
285       result = ::setsockopt( handle, SOL_SOCKET, SO_BROADCAST, (char*)&one, sizeof(one) );
286 #else
287       result = ::setsockopt( handle, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one) );
288 #endif
289   } else {
290       result = ::setsockopt( handle, SOL_SOCKET, SO_BROADCAST, NULL, 0 );
291   }
292   
293   if ( result < 0 ) {
294       throw sg_exception("Socket::setBroadcast failed");
295   }
296 }
297
298
299 int Socket::bind ( const char* host, int port )
300 {
301   int result;
302   assert ( handle != -1 ) ;
303   IPAddress addr ( host, port ) ;
304
305 #if !defined(WINSOCK)
306   if( (result = ::bind(handle, addr.getAddr(), addr.getAddrLen() ) ) < 0 ) {
307     SG_LOG(SG_IO, SG_ALERT, "bind(" << addr.getHost() << ":" << addr.getPort() << ") failed. Errno " << errno << " (" << strerror(errno) << ")");
308     return result;
309   }
310 #endif
311
312   // 224.0.0.0 - 239.255.255.255 are multicast   
313   // Usage of 239.x.x.x is recommended for local scope
314   // Reference: http://tools.ietf.org/html/rfc5771
315   if( ntohl(addr.getIP()) >= 0xe0000000 && ntohl(addr.getIP()) <= 0xefffffff ) {
316
317 #if defined(WINSOCK)
318     struct sockaddr_in a;
319     a.sin_addr.S_un.S_addr = INADDR_ANY;
320     a.sin_family = AF_INET;
321     a.sin_port = htons(port);
322       
323     if( (result = ::bind(handle,(const sockaddr*)&a,sizeof(a))) < 0 ) {
324       SG_LOG(SG_IO, SG_ALERT, "bind(any:" << port << ") failed. Errno " << errno << " (" << strerror(errno) << ")");
325       return result;
326     }
327 #endif
328
329     struct ip_mreq mreq;
330     mreq.imr_multiaddr.s_addr = addr.getIP();
331     mreq.imr_interface.s_addr = htonl(INADDR_ANY);
332     if( (result=::setsockopt(getHandle(), IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&mreq, sizeof(mreq))) != 0 ) {
333       SG_LOG(SG_IO, SG_ALERT, "setsockopt(IP_ADD_MEMBERSHIP) failed. Errno " << errno << " (" << strerror(errno) << ")");
334       return result;
335     }
336   }
337 #if defined(WINSOCK)
338   else if( (result = ::bind(handle,addr.getAddr(), addr.getAddrLen())) < 0 ) {
339     SG_LOG(SG_IO, SG_ALERT, "bind(" << host << ":" << port << ") failed. Errno " << errno << " (" << strerror(errno) << ")");
340     return result;
341   }
342 #endif
343
344   return 0;
345 }
346
347
348 int Socket::listen ( int backlog )
349 {
350   assert ( handle != -1 ) ;
351   return ::listen(handle,backlog);
352 }
353
354
355 int Socket::accept ( IPAddress* addr )
356 {
357   assert ( handle != -1 ) ;
358
359   if ( addr == NULL )
360   {
361     return ::accept(handle,NULL,NULL);
362   }
363   else
364   {
365     socklen_t addr_len = addr->getAddrLen(); ;
366     return ::accept(handle, addr->getAddr(), &addr_len);
367   }
368 }
369
370
371 int Socket::connect ( const char* host, int port )
372 {
373   IPAddress addr ( host, port ) ;
374   return connect ( &addr );
375 }
376
377
378 int Socket::connect ( IPAddress* addr )
379 {
380   assert ( handle != -1 ) ;
381   if ( addr->getBroadcast() ) {
382       setBroadcast( true );
383   }
384   return ::connect(handle, addr->getAddr(), addr->getAddrLen() );
385 }
386
387
388 int Socket::send (const void * buffer, int size, int flags)
389 {
390   assert ( handle != -1 ) ;
391   return ::send (handle, (const char*)buffer, size, flags);
392 }
393
394
395 int Socket::sendto ( const void * buffer, int size,
396                         int flags, const IPAddress* to )
397 {
398   assert ( handle != -1 ) ;
399   return ::sendto(handle,(const char*)buffer,size,flags,
400                          (const sockaddr*) to->getAddr(), to->getAddrLen());
401 }
402
403
404 int Socket::recv (void * buffer, int size, int flags)
405 {
406   assert ( handle != -1 ) ;
407   return ::recv (handle, (char*)buffer, size, flags);
408 }
409
410
411 int Socket::recvfrom ( void * buffer, int size,
412                           int flags, IPAddress* from )
413 {
414   assert ( handle != -1 ) ;
415   socklen_t fromlen = (socklen_t) from->getAddrLen() ;
416   return ::recvfrom(handle,(char*)buffer,size,flags, from->getAddr(),&fromlen);
417 }
418
419
420 void Socket::close (void)
421 {
422   if ( handle != -1 )
423   {
424 #if defined(WINSOCK)
425     ::closesocket( handle );
426 #else
427     ::close( handle );
428 #endif
429     handle = -1 ;
430   }
431 }
432
433
434 bool Socket::isNonBlockingError ()
435 {
436 #if defined(WINSOCK)
437   int wsa_errno = WSAGetLastError();
438   if ( wsa_errno != 0 )
439   {
440     WSASetLastError(0);
441         SG_LOG(SG_IO, SG_WARN, "isNonBlockingError: WSAGetLastError():" << wsa_errno);
442     switch (wsa_errno) {
443     case WSAEWOULDBLOCK: // always == NET_EAGAIN?
444     case WSAEALREADY:
445     case WSAEINPROGRESS:
446       return true;
447     }
448   }
449   return false;
450 #else
451   switch (errno) {
452   case EWOULDBLOCK: // always == NET_EAGAIN?
453   case EALREADY:
454   case EINPROGRESS:
455     return true;
456   }
457   return false;
458
459 #endif
460 }
461
462
463 //////////////////////////////////////////////////////////////////////
464 //
465 //      modified version by os
466 //
467 //////////////////////////////////////////////////////////////////////
468 int Socket::select ( Socket** reads, Socket** writes, int timeout )
469 {
470   fd_set r,w;
471   int   retval;
472   
473   FD_ZERO (&r);
474   FD_ZERO (&w);
475
476   int i, k ;
477   int num = 0 ;
478
479   if ( reads )
480   {
481     for ( i=0; reads[i]; i++ )
482     {
483       int fd = reads[i]->getHandle();
484       FD_SET (fd, &r);
485       num++;
486     }
487   }
488
489   if ( writes )
490   {
491     for ( i=0; writes[i]; i++ )
492     {
493       int fd = writes[i]->getHandle();
494       FD_SET (fd, &w);
495       num++;
496     }
497   }
498
499   if (!num)
500     return num ;
501
502   /* Set up the timeout */
503   struct timeval tv ;
504   tv.tv_sec = timeout/1000;
505   tv.tv_usec = (timeout%1000)*1000;
506
507   // It bothers me that select()'s first argument does not appear to
508   // work as advertised... [it hangs like this if called with
509   // anything less than FD_SETSIZE, which seems wasteful?]
510   
511   // Note: we ignore the 'exception' fd_set - I have never had a
512   // need to use it.  The name is somewhat misleading - the only
513   // thing I have ever seen it used for is to detect urgent data -
514   // which is an unportable feature anyway.
515
516   retval = ::select (FD_SETSIZE, &r, &w, 0, &tv);
517
518   //remove sockets that had no activity
519
520   num = 0 ;
521
522   if ( reads )
523   {
524     for ( k=i=0; reads[i]; i++ )
525     {
526       int fd = reads[i]->getHandle();
527       if ( FD_ISSET (fd, &r) )
528       {
529         reads[k++] = reads[i];
530         num++;
531       }
532     }
533     reads[k] = NULL ;
534   }
535
536   if ( writes )
537   {
538     for ( k=i=0; writes[i]; i++ )
539     {
540       int fd = writes[i]->getHandle();
541       if ( FD_ISSET (fd, &w) )
542       {
543         writes[k++] = writes[i];
544         num++;
545       }
546     }
547     writes[k] = NULL ;
548   }
549
550   if (retval == 0) // timeout
551     return (-2);
552   if (retval == -1)// error
553     return (-1);
554
555   return num ;
556 }
557
558
559 /* Init/Exit functions */
560
561 static void netExit ( void )
562 {
563 #if defined(WINSOCK)
564         /* Clean up windows networking */
565         if ( WSACleanup() == SOCKET_ERROR ) {
566                 if ( WSAGetLastError() == WSAEINPROGRESS ) {
567                         WSACancelBlockingCall();
568                         WSACleanup();
569                 }
570         }
571 #endif
572 }
573
574 int Socket::initSockets()
575 {
576 #if defined(WINSOCK)
577         /* Start up the windows networking */
578         WORD version_wanted = MAKEWORD(1,1);
579         WSADATA wsaData;
580
581         if ( WSAStartup(version_wanted, &wsaData) != 0 ) {
582                 throw sg_exception("WinSock initialization failed");
583         }
584 #endif
585
586   atexit( netExit ) ;
587         return(0);
588 }
589
590
591 } // of namespace simgear
592