]> git.mxchange.org Git - simgear.git/blob - simgear/io/raw_socket.cxx
Asynchronous host lookups+caching, attempt #2
[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 <map>
61
62 #include <simgear/debug/logstream.hxx>
63 #include <simgear/structure/exception.hxx>
64 #include <simgear/threads/SGThread.hxx>
65
66 namespace {
67
68
69
70 class Resolver : public SGThread
71 {
72 public:
73     static Resolver* instance()
74     {
75         if (!static_instance) {
76             static_instance = new Resolver;
77             atexit(&Resolver::cleanup);
78             static_instance->start();
79         }
80         
81         return static_instance;
82     }
83     
84     static void cleanup()
85     {
86         static_instance->cancel();
87     }
88     
89     Resolver()
90     {
91     // take the lock initially, thread will wait upon it once running
92         _lock.lock();
93     }
94     
95     simgear::IPAddress* lookup(const string& host)
96     {
97         simgear::IPAddress* result = NULL;
98         _lock.lock();
99         AddressCache::iterator it = _cache.find(host);
100         if (it == _cache.end()) {
101             _cache[host] = NULL; // mark as needing looked up
102             _wait.signal(); // if the thread was sleeping, poke it
103         } else {
104             result = it->second;
105         }
106         _lock.unlock();
107         return result;
108     }
109     
110     simgear::IPAddress* lookupSync(const string& host)
111     {
112         simgear::IPAddress* result = NULL;
113         _lock.lock();
114         AddressCache::iterator it = _cache.find(host);
115         if (it == _cache.end()) {
116             _lock.unlock();
117             result = new simgear::IPAddress;
118             bool ok = lookupHost(host.c_str(), *result);
119             _lock.lock();
120             if (ok) {
121                 _cache[host] = result; // mark as needing looked up
122             } else {
123                 delete result;
124                 result = NULL;
125             }
126         } else { // found in cache, easy
127             result = it->second;
128         }
129         _lock.unlock();
130         return result;
131     }
132 protected:
133     /**
134      * run method waits on a condition (_wait), and when awoken,
135      * finds any unresolved entries in _cache, resolves them, and goes
136      * back to sleep.
137      */
138     virtual void run()
139     {
140         while (true) {
141             _wait.wait(_lock);
142             AddressCache::iterator it;
143             
144             for (it = _cache.begin(); it != _cache.end(); ++it) {
145                 if (it->second == NULL) {
146                     string h = it->first;
147                     
148                     _lock.unlock();
149                     simgear::IPAddress* addr = new simgear::IPAddress;
150                 // may take seconds or even minutes!
151                     lookupHost(h.c_str(), *addr);
152                     _lock.lock();
153                 
154                 // cahce may have changed while we had the lock released -
155                 // so iterators may be invalid: restart the traversal
156                     it = _cache.begin();
157                     _cache[h] = addr;
158                 } // of found un-resolved entry
159             } // of un-resolved address iteration 
160         } // of thread run loop
161     }
162 private:
163     static Resolver* static_instance;
164     
165     /**
166      * The actual synchronous, blocking host lookup function
167      * do *not* call this with any locks (mutexs) held, since depending
168      * on local system configuration / network availability, it
169      * may block for seconds or minutes.
170      */
171     bool lookupHost(const char* host, simgear::IPAddress& addr)
172     {
173       struct addrinfo hints;
174       memset(&hints, 0, sizeof(struct addrinfo));
175       hints.ai_family = AF_INET;
176       bool ok = false;
177       
178       struct addrinfo* result0 = NULL;
179       int err = getaddrinfo(host, NULL, &hints, &result0);
180       if (err) {
181         SG_LOG(SG_IO, SG_WARN, "getaddrinfo failed for '" << host << "' : " << gai_strerror(err));
182         return false;
183       } else {
184           struct addrinfo* result;
185           for (result = result0; result != NULL; result = result->ai_next) {
186               if (result->ai_family != AF_INET) { // only accept IP4 for the moment
187                   continue;
188               }
189
190               if (result->ai_addrlen != addr.getAddrLen()) {
191                   SG_LOG(SG_IO, SG_ALERT, "mismatch in socket address sizes: got " <<
192                       result->ai_addrlen << ", expected " << addr.getAddrLen());
193                   continue;
194               }
195
196               memcpy(addr.getAddr(), result->ai_addr, result->ai_addrlen);
197               ok = true;
198               break;
199           } // of getaddrinfo results iteration
200       } // of getaddrinfo succeeded
201
202       freeaddrinfo(result0);
203       return ok;
204     }
205     
206     SGMutex _lock;
207     SGPthreadCond _wait;
208     typedef std::map<string, simgear::IPAddress*> AddressCache;
209     AddressCache _cache;
210 };
211
212 Resolver* Resolver::static_instance = NULL;
213  
214 } // of anonymous namespace
215
216 namespace simgear
217 {
218                                                                                        
219 IPAddress::IPAddress ( const char* host, int port )
220 {
221   set ( host, port ) ;
222 }
223
224 IPAddress::IPAddress( const IPAddress& other ) :
225   addr(NULL)
226 {
227   if (other.addr) {
228     addr = (struct sockaddr_in*) malloc(sizeof(struct sockaddr_in));
229     memcpy(addr, other.addr, sizeof(struct sockaddr_in));
230   }
231 }
232
233 const IPAddress& IPAddress::operator=(const IPAddress& other)
234 {
235   if (addr) {
236     free(addr);
237     addr = NULL;
238   }
239
240   if (other.addr) {
241     addr = (struct sockaddr_in*) malloc(sizeof(struct sockaddr_in));
242     memcpy(addr, other.addr, sizeof(struct sockaddr_in));
243   }
244
245   return *this;
246 }
247
248 void IPAddress::set ( const char* host, int port )
249 {
250   addr = (struct sockaddr_in*) malloc(sizeof(struct sockaddr_in));
251   memset(addr, 0, sizeof(struct sockaddr_in));
252
253   addr->sin_family = AF_INET ;
254   addr->sin_port = htons (port);
255
256   /* Convert a string specifying a host name or one of a few symbolic
257   ** names to a numeric IP address.  This usually calls getaddrinfo()
258   ** to do the work; the names "" and "<broadcast>" are special.
259   */
260
261   if (!host || host[0] == '\0') {
262     addr->sin_addr.s_addr = INADDR_ANY;
263     return;
264   }
265   
266   if (strcmp(host, "<broadcast>") == 0) {
267     addr->sin_addr.s_addr = INADDR_BROADCAST;
268     return;
269   }
270   
271 // check the cache
272   IPAddress* cached = Resolver::instance()->lookupSync(host);
273   if (cached) {
274       memcpy(addr, cached->getAddr(), cached->getAddrLen());
275   }
276   
277   addr->sin_port = htons (port); // fix up port after getaddrinfo
278 }
279
280 IPAddress::~IPAddress()
281 {
282   if (addr) {
283     free (addr);
284   }
285 }
286
287 bool IPAddress::lookupNonblocking(const char* host, IPAddress& addr)
288 {    
289     IPAddress* cached = Resolver::instance()->lookup(host);
290     if (!cached) {
291         return false;
292     }
293     
294     addr = *cached;
295     return true;
296 }
297
298 /* Create a string object representing an IP address.
299    This is always a string of the form 'dd.dd.dd.dd' (with variable
300    size numbers). */
301
302 const char* IPAddress::getHost () const
303 {
304   static char buf [32];
305         long x = ntohl(addr->sin_addr.s_addr);
306         sprintf(buf, "%d.%d.%d.%d",
307                 (int) (x>>24) & 0xff, (int) (x>>16) & 0xff,
308                 (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff );
309   return buf;
310 }
311
312 unsigned int IPAddress::getIP () const 
313
314         return addr->sin_addr.s_addr; 
315 }
316
317 unsigned int IPAddress::getPort() const
318 {
319   return ntohs(addr->sin_port);
320 }
321
322 void IPAddress::setPort(int port)
323 {
324     addr->sin_port = htons(port);
325 }
326
327 unsigned int IPAddress::getFamily () const 
328
329         return addr->sin_family; 
330 }
331
332 const char* IPAddress::getLocalHost ()
333 {
334   //gethostbyname(gethostname())
335
336   char buf[256];
337   memset(buf, 0, sizeof(buf));
338   gethostname(buf, sizeof(buf)-1);
339   const hostent *hp = gethostbyname(buf);
340
341   if (hp && *hp->h_addr_list)
342   {
343     in_addr     addr = *((in_addr*)*hp->h_addr_list);
344     const char* host = inet_ntoa(addr);
345
346     if ( host )
347       return host ;
348   }
349
350   return "127.0.0.1" ;
351 }
352
353
354 bool IPAddress::getBroadcast () const
355 {
356   return (addr->sin_addr.s_addr == INADDR_BROADCAST);
357 }
358
359 unsigned int IPAddress::getAddrLen() const
360 {
361     return sizeof(struct sockaddr_in);
362 }
363
364 struct sockaddr* IPAddress::getAddr() const
365 {
366     if (addr == NULL) {
367         addr = (struct sockaddr_in*) malloc(sizeof(struct sockaddr_in));
368         memset(addr, 0, sizeof(struct sockaddr_in));
369     }
370     
371     return (struct sockaddr*) addr;
372 }
373
374 Socket::Socket ()
375 {
376   handle = -1 ;
377 }
378
379
380 Socket::~Socket ()
381 {
382   close () ;
383 }
384
385
386 void Socket::setHandle (int _handle)
387 {
388   close () ;
389   handle = _handle ;
390 }
391
392
393 bool Socket::open ( bool stream )
394 {
395   close () ;
396   handle = ::socket ( AF_INET, (stream? SOCK_STREAM: SOCK_DGRAM), 0 ) ;
397
398   // Jan 26, 2010: Patch to avoid the problem of the socket resource not
399   // yet being available if the program is restarted quickly after being
400   // killed.
401   //
402   // Reference: http://www.unixguide.net/network/socketfaq/4.5.shtml
403   // --
404   // Also required for joining multicast domains
405   if ( stream ) {
406     int opt_boolean = 1;
407 #if defined(_WIN32) || defined(__CYGWIN__)
408     setsockopt( handle, SOL_SOCKET, SO_REUSEADDR, (char *)&opt_boolean,
409                 sizeof(opt_boolean) );
410 #else
411     setsockopt( handle, SOL_SOCKET, SO_REUSEADDR, &opt_boolean,
412                 sizeof(opt_boolean) );
413 #endif
414   }
415
416   return (handle != -1);
417 }
418
419
420 void Socket::setBlocking ( bool blocking )
421 {
422   assert ( handle != -1 ) ;
423
424 #if defined(WINSOCK)
425     u_long nblocking = blocking? 0: 1;
426   ::ioctlsocket(handle, FIONBIO, &nblocking);
427 #else
428
429   int delay_flag = ::fcntl (handle, F_GETFL, 0);
430
431   if (blocking)
432     delay_flag &= (~O_NDELAY);
433   else
434     delay_flag |= O_NDELAY;
435
436   ::fcntl (handle, F_SETFL, delay_flag);
437 #endif
438 }
439
440
441 void Socket::setBroadcast ( bool broadcast )
442 {
443   assert ( handle != -1 ) ;
444   int result;
445   if ( broadcast ) {
446       int one = 1;
447 #if defined(_WIN32) || defined(__CYGWIN__)
448       result = ::setsockopt( handle, SOL_SOCKET, SO_BROADCAST, (char*)&one, sizeof(one) );
449 #else
450       result = ::setsockopt( handle, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one) );
451 #endif
452   } else {
453       result = ::setsockopt( handle, SOL_SOCKET, SO_BROADCAST, NULL, 0 );
454   }
455   
456   if ( result < 0 ) {
457       throw sg_exception("Socket::setBroadcast failed");
458   }
459 }
460
461
462 int Socket::bind ( const char* host, int port )
463 {
464   int result;
465   assert ( handle != -1 ) ;
466   IPAddress addr ( host, port ) ;
467
468 #if !defined(WINSOCK)
469   if( (result = ::bind(handle, addr.getAddr(), addr.getAddrLen() ) ) < 0 ) {
470     SG_LOG(SG_IO, SG_ALERT, "bind(" << addr.getHost() << ":" << addr.getPort() << ") failed. Errno " << errno << " (" << strerror(errno) << ")");
471     return result;
472   }
473 #endif
474
475   // 224.0.0.0 - 239.255.255.255 are multicast   
476   // Usage of 239.x.x.x is recommended for local scope
477   // Reference: http://tools.ietf.org/html/rfc5771
478   if( ntohl(addr.getIP()) >= 0xe0000000 && ntohl(addr.getIP()) <= 0xefffffff ) {
479
480 #if defined(WINSOCK)
481     struct sockaddr_in a;
482     a.sin_addr.S_un.S_addr = INADDR_ANY;
483     a.sin_family = AF_INET;
484     a.sin_port = htons(port);
485       
486     if( (result = ::bind(handle,(const sockaddr*)&a,sizeof(a))) < 0 ) {
487       SG_LOG(SG_IO, SG_ALERT, "bind(any:" << port << ") failed. Errno " << errno << " (" << strerror(errno) << ")");
488       return result;
489     }
490 #endif
491
492     struct ip_mreq mreq;
493     mreq.imr_multiaddr.s_addr = addr.getIP();
494     mreq.imr_interface.s_addr = htonl(INADDR_ANY);
495     if( (result=::setsockopt(getHandle(), IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&mreq, sizeof(mreq))) != 0 ) {
496       SG_LOG(SG_IO, SG_ALERT, "setsockopt(IP_ADD_MEMBERSHIP) failed. Errno " << errno << " (" << strerror(errno) << ")");
497       return result;
498     }
499   }
500 #if defined(WINSOCK)
501   else if( (result = ::bind(handle,addr.getAddr(), addr.getAddrLen())) < 0 ) {
502     SG_LOG(SG_IO, SG_ALERT, "bind(" << host << ":" << port << ") failed. Errno " << errno << " (" << strerror(errno) << ")");
503     return result;
504   }
505 #endif
506
507   return 0;
508 }
509
510
511 int Socket::listen ( int backlog )
512 {
513   assert ( handle != -1 ) ;
514   return ::listen(handle,backlog);
515 }
516
517
518 int Socket::accept ( IPAddress* addr )
519 {
520   assert ( handle != -1 ) ;
521
522   if ( addr == NULL )
523   {
524     return ::accept(handle,NULL,NULL);
525   }
526   else
527   {
528     socklen_t addr_len = addr->getAddrLen(); ;
529     return ::accept(handle, addr->getAddr(), &addr_len);
530   }
531 }
532
533
534 int Socket::connect ( const char* host, int port )
535 {
536   IPAddress addr ( host, port ) ;
537   return connect ( &addr );
538 }
539
540
541 int Socket::connect ( IPAddress* addr )
542 {
543   assert ( handle != -1 ) ;
544   if ( addr->getBroadcast() ) {
545       setBroadcast( true );
546   }
547   return ::connect(handle, addr->getAddr(), addr->getAddrLen() );
548 }
549
550
551 int Socket::send (const void * buffer, int size, int flags)
552 {
553   assert ( handle != -1 ) ;
554   return ::send (handle, (const char*)buffer, size, flags);
555 }
556
557
558 int Socket::sendto ( const void * buffer, int size,
559                         int flags, const IPAddress* to )
560 {
561   assert ( handle != -1 ) ;
562   return ::sendto(handle,(const char*)buffer,size,flags,
563                          (const sockaddr*) to->getAddr(), to->getAddrLen());
564 }
565
566
567 int Socket::recv (void * buffer, int size, int flags)
568 {
569   assert ( handle != -1 ) ;
570   return ::recv (handle, (char*)buffer, size, flags);
571 }
572
573
574 int Socket::recvfrom ( void * buffer, int size,
575                           int flags, IPAddress* from )
576 {
577   assert ( handle != -1 ) ;
578   socklen_t fromlen = (socklen_t) from->getAddrLen() ;
579   return ::recvfrom(handle,(char*)buffer,size,flags, from->getAddr(),&fromlen);
580 }
581
582
583 void Socket::close (void)
584 {
585   if ( handle != -1 )
586   {
587 #if defined(WINSOCK)
588     ::closesocket( handle );
589 #else
590     ::close( handle );
591 #endif
592     handle = -1 ;
593   }
594 }
595
596
597 bool Socket::isNonBlockingError ()
598 {
599 #if defined(WINSOCK)
600   int wsa_errno = WSAGetLastError();
601   if ( wsa_errno != 0 )
602   {
603     WSASetLastError(0);
604         SG_LOG(SG_IO, SG_WARN, "isNonBlockingError: WSAGetLastError():" << wsa_errno);
605     switch (wsa_errno) {
606     case WSAEWOULDBLOCK: // always == NET_EAGAIN?
607     case WSAEALREADY:
608     case WSAEINPROGRESS:
609       return true;
610     }
611   }
612   return false;
613 #else
614   switch (errno) {
615   case EWOULDBLOCK: // always == NET_EAGAIN?
616   case EALREADY:
617   case EINPROGRESS:
618     return true;
619   }
620   return false;
621
622 #endif
623 }
624
625
626 //////////////////////////////////////////////////////////////////////
627 //
628 //      modified version by os
629 //
630 //////////////////////////////////////////////////////////////////////
631 int Socket::select ( Socket** reads, Socket** writes, int timeout )
632 {
633   fd_set r,w;
634   int   retval;
635   
636   FD_ZERO (&r);
637   FD_ZERO (&w);
638
639   int i, k ;
640   int num = 0 ;
641
642   if ( reads )
643   {
644     for ( i=0; reads[i]; i++ )
645     {
646       int fd = reads[i]->getHandle();
647       FD_SET (fd, &r);
648       num++;
649     }
650   }
651
652   if ( writes )
653   {
654     for ( i=0; writes[i]; i++ )
655     {
656       int fd = writes[i]->getHandle();
657       FD_SET (fd, &w);
658       num++;
659     }
660   }
661
662   if (!num)
663     return num ;
664
665   /* Set up the timeout */
666   struct timeval tv ;
667   tv.tv_sec = timeout/1000;
668   tv.tv_usec = (timeout%1000)*1000;
669
670   // It bothers me that select()'s first argument does not appear to
671   // work as advertised... [it hangs like this if called with
672   // anything less than FD_SETSIZE, which seems wasteful?]
673   
674   // Note: we ignore the 'exception' fd_set - I have never had a
675   // need to use it.  The name is somewhat misleading - the only
676   // thing I have ever seen it used for is to detect urgent data -
677   // which is an unportable feature anyway.
678
679   retval = ::select (FD_SETSIZE, &r, &w, 0, &tv);
680
681   //remove sockets that had no activity
682
683   num = 0 ;
684
685   if ( reads )
686   {
687     for ( k=i=0; reads[i]; i++ )
688     {
689       int fd = reads[i]->getHandle();
690       if ( FD_ISSET (fd, &r) )
691       {
692         reads[k++] = reads[i];
693         num++;
694       }
695     }
696     reads[k] = NULL ;
697   }
698
699   if ( writes )
700   {
701     for ( k=i=0; writes[i]; i++ )
702     {
703       int fd = writes[i]->getHandle();
704       if ( FD_ISSET (fd, &w) )
705       {
706         writes[k++] = writes[i];
707         num++;
708       }
709     }
710     writes[k] = NULL ;
711   }
712
713   if (retval == 0) // timeout
714     return (-2);
715   if (retval == -1)// error
716     return (-1);
717
718   return num ;
719 }
720
721
722 /* Init/Exit functions */
723
724 static void netExit ( void )
725 {
726 #if defined(WINSOCK)
727         /* Clean up windows networking */
728         if ( WSACleanup() == SOCKET_ERROR ) {
729                 if ( WSAGetLastError() == WSAEINPROGRESS ) {
730                         WSACancelBlockingCall();
731                         WSACleanup();
732                 }
733         }
734 #endif
735 }
736
737 int Socket::initSockets()
738 {
739 #if defined(WINSOCK)
740         /* Start up the windows networking */
741         WORD version_wanted = MAKEWORD(1,1);
742         WSADATA wsaData;
743
744         if ( WSAStartup(version_wanted, &wsaData) != 0 ) {
745                 throw sg_exception("WinSock initialization failed");
746         }
747 #endif
748
749   atexit( netExit ) ;
750         return(0);
751 }
752
753
754 } // of namespace simgear
755