]> git.mxchange.org Git - simgear.git/blob - simgear/io/raw_socket.cxx
ecc8671fa8a9b76c0fbb5de2ca3b8dcb803081c6
[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* result = NULL;
119   int err = getaddrinfo(host, NULL, NULL /* no hints */, &result);
120   if (err) {
121     SG_LOG(SG_IO, SG_WARN, "getaddrinfo failed for '" << host << "' : " << gai_strerror(err));
122   } else if (result->ai_addrlen != getAddrLen()) {
123     SG_LOG(SG_IO, SG_ALERT, "mismatch in socket address sizes");
124   } else {
125     memcpy(addr, result->ai_addr, result->ai_addrlen);
126   }
127   
128   freeaddrinfo(result);
129   addr->sin_port = htons (port); // fix up port after getaddrinfo
130 }
131
132 IPAddress::~IPAddress()
133 {
134   if (addr) {
135     free (addr);
136   }
137 }
138
139 /* Create a string object representing an IP address.
140    This is always a string of the form 'dd.dd.dd.dd' (with variable
141    size numbers). */
142
143 const char* IPAddress::getHost () const
144 {
145   static char buf [32];
146         long x = ntohl(addr->sin_addr.s_addr);
147         sprintf(buf, "%d.%d.%d.%d",
148                 (int) (x>>24) & 0xff, (int) (x>>16) & 0xff,
149                 (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff );
150   return buf;
151 }
152
153 unsigned int IPAddress::getIP () const 
154
155         return addr->sin_addr.s_addr; 
156 }
157
158 unsigned int IPAddress::getPort() const
159 {
160   return ntohs(addr->sin_port);
161 }
162
163 unsigned int IPAddress::getFamily () const 
164
165         return addr->sin_family; 
166 }
167
168 const char* IPAddress::getLocalHost ()
169 {
170   //gethostbyname(gethostname())
171
172   char buf[256];
173   memset(buf, 0, sizeof(buf));
174   gethostname(buf, sizeof(buf)-1);
175   const hostent *hp = gethostbyname(buf);
176
177   if (hp && *hp->h_addr_list)
178   {
179     in_addr     addr = *((in_addr*)*hp->h_addr_list);
180     const char* host = inet_ntoa(addr);
181
182     if ( host )
183       return host ;
184   }
185
186   return "127.0.0.1" ;
187 }
188
189
190 bool IPAddress::getBroadcast () const
191 {
192   return (addr->sin_addr.s_addr == INADDR_BROADCAST);
193 }
194
195 unsigned int IPAddress::getAddrLen() const
196 {
197     return sizeof(struct sockaddr_in);
198 }
199
200 struct sockaddr* IPAddress::getAddr() const
201 {
202     return (struct sockaddr*) addr;
203 }
204
205 Socket::Socket ()
206 {
207   handle = -1 ;
208 }
209
210
211 Socket::~Socket ()
212 {
213   close () ;
214 }
215
216
217 void Socket::setHandle (int _handle)
218 {
219   close () ;
220   handle = _handle ;
221 }
222
223
224 bool Socket::open ( bool stream )
225 {
226   close () ;
227   handle = ::socket ( AF_INET, (stream? SOCK_STREAM: SOCK_DGRAM), 0 ) ;
228
229   // Jan 26, 2010: Patch to avoid the problem of the socket resource not
230   // yet being available if the program is restarted quickly after being
231   // killed.
232   //
233   // Reference: http://www.unixguide.net/network/socketfaq/4.5.shtml
234   // --
235   // Also required for joining multicast domains
236   if ( stream ) {
237     int opt_boolean = 1;
238 #if defined(_WIN32) || defined(__CYGWIN__)
239     setsockopt( handle, SOL_SOCKET, SO_REUSEADDR, (char *)&opt_boolean,
240                 sizeof(opt_boolean) );
241 #else
242     setsockopt( handle, SOL_SOCKET, SO_REUSEADDR, &opt_boolean,
243                 sizeof(opt_boolean) );
244 #endif
245   }
246
247   return (handle != -1);
248 }
249
250
251 void Socket::setBlocking ( bool blocking )
252 {
253   assert ( handle != -1 ) ;
254
255 #if defined(WINSOCK)
256     u_long nblocking = blocking? 0: 1;
257   ::ioctlsocket(handle, FIONBIO, &nblocking);
258 #else
259
260   int delay_flag = ::fcntl (handle, F_GETFL, 0);
261
262   if (blocking)
263     delay_flag &= (~O_NDELAY);
264   else
265     delay_flag |= O_NDELAY;
266
267   ::fcntl (handle, F_SETFL, delay_flag);
268 #endif
269 }
270
271
272 void Socket::setBroadcast ( bool broadcast )
273 {
274   assert ( handle != -1 ) ;
275   int result;
276   if ( broadcast ) {
277       int one = 1;
278 #if defined(_WIN32) || defined(__CYGWIN__)
279       result = ::setsockopt( handle, SOL_SOCKET, SO_BROADCAST, (char*)&one, sizeof(one) );
280 #else
281       result = ::setsockopt( handle, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one) );
282 #endif
283   } else {
284       result = ::setsockopt( handle, SOL_SOCKET, SO_BROADCAST, NULL, 0 );
285   }
286   
287   if ( result < 0 ) {
288       throw sg_exception("Socket::setBroadcast failed");
289   }
290 }
291
292
293 int Socket::bind ( const char* host, int port )
294 {
295   int result;
296   assert ( handle != -1 ) ;
297   IPAddress addr ( host, port ) ;
298
299 #if !defined(WINSOCK)
300   if( (result = ::bind(handle, addr.getAddr(), addr.getAddrLen() ) ) < 0 ) {
301     SG_LOG(SG_IO, SG_ALERT, "bind(" << addr.getHost() << ":" << addr.getPort() << ") failed. Errno " << errno << " (" << strerror(errno) << ")");
302     return result;
303   }
304 #endif
305
306   // 224.0.0.0 - 239.255.255.255 are multicast   
307   // Usage of 239.x.x.x is recommended for local scope
308   // Reference: http://tools.ietf.org/html/rfc5771
309   if( ntohl(addr.getIP()) >= 0xe0000000 && ntohl(addr.getIP()) <= 0xefffffff ) {
310
311 #if defined(WINSOCK)
312     struct sockaddr_in a;
313     a.sin_addr.S_un.S_addr = INADDR_ANY;
314     a.sin_family = AF_INET;
315     a.sin_port = htons(port);
316       
317     if( (result = ::bind(handle,(const sockaddr*)&a,sizeof(a))) < 0 ) {
318       SG_LOG(SG_IO, SG_ALERT, "bind(any:" << port << ") failed. Errno " << errno << " (" << strerror(errno) << ")");
319       return result;
320     }
321 #endif
322
323     struct ip_mreq mreq;
324     mreq.imr_multiaddr.s_addr = addr.getIP();
325     mreq.imr_interface.s_addr = htonl(INADDR_ANY);
326     if( (result=::setsockopt(getHandle(), IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&mreq, sizeof(mreq))) != 0 ) {
327       SG_LOG(SG_IO, SG_ALERT, "setsockopt(IP_ADD_MEMBERSHIP) failed. Errno " << errno << " (" << strerror(errno) << ")");
328       return result;
329     }
330   }
331 #if defined(WINSOCK)
332   else if( (result = ::bind(handle,addr->getAddr(), addr->getAddrLen())) < 0 ) {
333     SG_LOG(SG_IO, SG_ALERT, "bind(" << host << ":" << port << ") failed. Errno " << errno << " (" << strerror(errno) << ")");
334     return result;
335   }
336 #endif
337
338   return 0;
339 }
340
341
342 int Socket::listen ( int backlog )
343 {
344   assert ( handle != -1 ) ;
345   return ::listen(handle,backlog);
346 }
347
348
349 int Socket::accept ( IPAddress* addr )
350 {
351   assert ( handle != -1 ) ;
352
353   if ( addr == NULL )
354   {
355     return ::accept(handle,NULL,NULL);
356   }
357   else
358   {
359     socklen_t addr_len = addr->getAddrLen(); ;
360     return ::accept(handle, addr->getAddr(), &addr_len);
361   }
362 }
363
364
365 int Socket::connect ( const char* host, int port )
366 {
367   IPAddress addr ( host, port ) ;
368   return connect ( &addr );
369 }
370
371
372 int Socket::connect ( IPAddress* addr )
373 {
374   assert ( handle != -1 ) ;
375   if ( addr->getBroadcast() ) {
376       setBroadcast( true );
377   }
378   return ::connect(handle, addr->getAddr(), addr->getAddrLen() );
379 }
380
381
382 int Socket::send (const void * buffer, int size, int flags)
383 {
384   assert ( handle != -1 ) ;
385   return ::send (handle, (const char*)buffer, size, flags);
386 }
387
388
389 int Socket::sendto ( const void * buffer, int size,
390                         int flags, const IPAddress* to )
391 {
392   assert ( handle != -1 ) ;
393   return ::sendto(handle,(const char*)buffer,size,flags,
394                          (const sockaddr*) to->getAddr(), to->getAddrLen());
395 }
396
397
398 int Socket::recv (void * buffer, int size, int flags)
399 {
400   assert ( handle != -1 ) ;
401   return ::recv (handle, (char*)buffer, size, flags);
402 }
403
404
405 int Socket::recvfrom ( void * buffer, int size,
406                           int flags, IPAddress* from )
407 {
408   assert ( handle != -1 ) ;
409   socklen_t fromlen = (socklen_t) from->getAddrLen() ;
410   return ::recvfrom(handle,(char*)buffer,size,flags, from->getAddr(),&fromlen);
411 }
412
413
414 void Socket::close (void)
415 {
416   if ( handle != -1 )
417   {
418 #if defined(WINSOCK)
419     ::closesocket( handle );
420 #else
421     ::close( handle );
422 #endif
423     handle = -1 ;
424   }
425 }
426
427
428 bool Socket::isNonBlockingError ()
429 {
430 #if defined(WINSOCK)
431   int wsa_errno = WSAGetLastError();
432   if ( wsa_errno != 0 )
433   {
434     WSASetLastError(0);
435         SG_LOG(SG_IO, SG_WARN, "isNonBlockingError: WSAGetLastError():" << wsa_errno);
436     switch (wsa_errno) {
437     case WSAEWOULDBLOCK: // always == NET_EAGAIN?
438     case WSAEALREADY:
439     case WSAEINPROGRESS:
440       return true;
441     }
442   }
443   return false;
444 #else
445   switch (errno) {
446   case EWOULDBLOCK: // always == NET_EAGAIN?
447   case EALREADY:
448   case EINPROGRESS:
449     return true;
450   }
451   return false;
452
453 #endif
454 }
455
456
457 //////////////////////////////////////////////////////////////////////
458 //
459 //      modified version by os
460 //
461 //////////////////////////////////////////////////////////////////////
462 int Socket::select ( Socket** reads, Socket** writes, int timeout )
463 {
464   fd_set r,w;
465   int   retval;
466   
467   FD_ZERO (&r);
468   FD_ZERO (&w);
469
470   int i, k ;
471   int num = 0 ;
472
473   if ( reads )
474   {
475     for ( i=0; reads[i]; i++ )
476     {
477       int fd = reads[i]->getHandle();
478       FD_SET (fd, &r);
479       num++;
480     }
481   }
482
483   if ( writes )
484   {
485     for ( i=0; writes[i]; i++ )
486     {
487       int fd = writes[i]->getHandle();
488       FD_SET (fd, &w);
489       num++;
490     }
491   }
492
493   if (!num)
494     return num ;
495
496   /* Set up the timeout */
497   struct timeval tv ;
498   tv.tv_sec = timeout/1000;
499   tv.tv_usec = (timeout%1000)*1000;
500
501   // It bothers me that select()'s first argument does not appear to
502   // work as advertised... [it hangs like this if called with
503   // anything less than FD_SETSIZE, which seems wasteful?]
504   
505   // Note: we ignore the 'exception' fd_set - I have never had a
506   // need to use it.  The name is somewhat misleading - the only
507   // thing I have ever seen it used for is to detect urgent data -
508   // which is an unportable feature anyway.
509
510   retval = ::select (FD_SETSIZE, &r, &w, 0, &tv);
511
512   //remove sockets that had no activity
513
514   num = 0 ;
515
516   if ( reads )
517   {
518     for ( k=i=0; reads[i]; i++ )
519     {
520       int fd = reads[i]->getHandle();
521       if ( FD_ISSET (fd, &r) )
522       {
523         reads[k++] = reads[i];
524         num++;
525       }
526     }
527     reads[k] = NULL ;
528   }
529
530   if ( writes )
531   {
532     for ( k=i=0; writes[i]; i++ )
533     {
534       int fd = writes[i]->getHandle();
535       if ( FD_ISSET (fd, &w) )
536       {
537         writes[k++] = writes[i];
538         num++;
539       }
540     }
541     writes[k] = NULL ;
542   }
543
544   if (retval == 0) // timeout
545     return (-2);
546   if (retval == -1)// error
547     return (-1);
548
549   return num ;
550 }
551
552
553 /* Init/Exit functions */
554
555 static void netExit ( void )
556 {
557 #if defined(WINSOCK)
558         /* Clean up windows networking */
559         if ( WSACleanup() == SOCKET_ERROR ) {
560                 if ( WSAGetLastError() == WSAEINPROGRESS ) {
561                         WSACancelBlockingCall();
562                         WSACleanup();
563                 }
564         }
565 #endif
566 }
567
568 int Socket::initSockets()
569 {
570 #if defined(WINSOCK)
571         /* Start up the windows networking */
572         WORD version_wanted = MAKEWORD(1,1);
573         WSADATA wsaData;
574
575         if ( WSAStartup(version_wanted, &wsaData) != 0 ) {
576                 throw sg_exception("WinSock initialization failed");
577         }
578 #endif
579
580   atexit( netExit ) ;
581         return(0);
582 }
583
584
585 } // of namespace simgear
586