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