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