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