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