Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | Related Pages

dns.cpp

00001 /*
00002   Copyright (c) 2005-2008 by Jakob Schroeter <js@camaya.net>
00003   This file is part of the gloox library. http://camaya.net/gloox
00004 
00005   This software is distributed under a license. The full license
00006   agreement can be found in the file LICENSE in this distribution.
00007   This software may not be copied, modified, sold or distributed
00008   other than expressed in the named license agreement.
00009 
00010   This software is distributed without any warranty.
00011 */
00012 
00013 
00014 #ifdef _WIN32
00015 # include "../config.h.win"
00016 #elif defined( _WIN32_WCE )
00017 # include "../config.h.win"
00018 #else
00019 # include "config.h"
00020 #endif
00021 
00022 #include "gloox.h"
00023 #include "dns.h"
00024 
00025 #ifndef _WIN32_WCE
00026 # include <sys/types.h>
00027 # include <sstream>
00028 #endif
00029 
00030 #include <stdio.h>
00031 
00032 #if !defined( _WIN32 ) && !defined( _WIN32_WCE )
00033 # include <netinet/in.h>
00034 # include <arpa/nameser.h>
00035 # include <resolv.h>
00036 # include <netdb.h>
00037 # include <arpa/inet.h>
00038 # include <sys/socket.h>
00039 # include <sys/un.h>
00040 # include <unistd.h>
00041 #endif
00042 
00043 #ifdef _WIN32
00044 # include <winsock.h>
00045 #elif defined( _WIN32_WCE )
00046 # include <winsock2.h>
00047 #endif
00048 
00049 #ifdef HAVE_WINDNS_H
00050 # include <windns.h>
00051 #endif
00052 
00053 #define SRV_COST    (RRFIXEDSZ+0)
00054 #define SRV_WEIGHT  (RRFIXEDSZ+2)
00055 #define SRV_PORT    (RRFIXEDSZ+4)
00056 #define SRV_SERVER  (RRFIXEDSZ+6)
00057 #define SRV_FIXEDSZ (RRFIXEDSZ+6)
00058 
00059 #ifndef T_SRV
00060 # define T_SRV 33
00061 #endif
00062 
00063 // mingw
00064 #ifndef DNS_TYPE_SRV
00065 # define DNS_TYPE_SRV 33
00066 #endif
00067 
00068 #ifndef C_IN
00069 # define C_IN 1
00070 #endif
00071 
00072 #ifndef INVALID_SOCKET
00073 # define INVALID_SOCKET -1
00074 #endif
00075 
00076 #define XMPP_PORT 5222
00077 
00078 namespace gloox
00079 {
00080 
00081 #if defined( HAVE_RES_QUERYDOMAIN ) && defined( HAVE_DN_SKIPNAME ) && defined( HAVE_RES_QUERY )
00082   DNS::HostMap DNS::resolve( const std::string& service, const std::string& proto,
00083                              const std::string& domain, const LogSink& logInstance )
00084   {
00085     buffer srvbuf;
00086     bool error = false;
00087 
00088     const std::string dname = "_" +  service + "._" + proto;
00089 
00090     if( !domain.empty() )
00091       srvbuf.len = res_querydomain( dname.c_str(), const_cast<char*>( domain.c_str() ),
00092                                     C_IN, T_SRV, srvbuf.buf, NS_PACKETSZ );
00093     else
00094       srvbuf.len = res_query( dname.c_str(), C_IN, T_SRV, srvbuf.buf, NS_PACKETSZ );
00095 
00096     if( srvbuf.len < 0 )
00097       return defaultHostMap( domain, logInstance );
00098 
00099     HEADER* hdr = (HEADER*)srvbuf.buf;
00100     unsigned char* here = srvbuf.buf + NS_HFIXEDSZ;
00101 
00102     if( ( hdr->tc ) || ( srvbuf.len < NS_HFIXEDSZ ) )
00103       error = true;
00104 
00105     if( hdr->rcode >= 1 && hdr->rcode <= 5 )
00106       error = true;
00107 
00108     if( ntohs( hdr->ancount ) == 0 )
00109       error = true;
00110 
00111     if( ntohs( hdr->ancount ) > NS_PACKETSZ )
00112       error = true;
00113 
00114     int cnt;
00115     for( cnt = ntohs( hdr->qdcount ); cnt>0; --cnt )
00116     {
00117       int strlen = dn_skipname( here, srvbuf.buf + srvbuf.len );
00118       here += strlen + NS_QFIXEDSZ;
00119     }
00120 
00121     unsigned char* srv[NS_PACKETSZ];
00122     int srvnum = 0;
00123     for( cnt = ntohs( hdr->ancount ); cnt>0; --cnt )
00124     {
00125       int strlen = dn_skipname( here, srvbuf.buf + srvbuf.len );
00126       here += strlen;
00127       srv[srvnum++] = here;
00128       here += SRV_FIXEDSZ;
00129       here += dn_skipname( here, srvbuf.buf + srvbuf.len );
00130     }
00131 
00132     if( error )
00133     {
00134       return defaultHostMap( domain, logInstance );
00135     }
00136 
00137     // (q)sort here
00138 
00139     HostMap servers;
00140     for( cnt=0; cnt<srvnum; ++cnt )
00141     {
00142       name srvname;
00143 
00144       if( ns_name_ntop( srv[cnt] + SRV_SERVER, (char*)srvname, NS_MAXDNAME ) < 0 )
00145         printf( "handle this error!\n" );
00146 
00147       servers[(char*)srvname] = ns_get16( srv[cnt] + SRV_PORT );
00148     }
00149 
00150     return servers;
00151   }
00152 
00153 #elif defined( _WIN32 ) && defined( HAVE_WINDNS_H )
00154   DNS::HostMap DNS::resolve( const std::string& service, const std::string& proto,
00155                              const std::string& domain, const LogSink& logInstance )
00156   {
00157     const std::string dname = "_" +  service + "._" + proto + "." + domain;
00158     bool error = false;
00159 
00160     DNS::HostMap servers;
00161     DNS_RECORD* pRecord;
00162     if( DnsQuery( dname.c_str(), DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &pRecord, NULL ) == ERROR_SUCCESS )
00163     {
00164       DNS_RECORD* pRec = pRecord;
00165       do
00166       {
00167         if( pRec->wType == DNS_TYPE_SRV )
00168         {
00169           servers[pRec->Data.SRV.pNameTarget] = pRec->Data.SRV.wPort;
00170         }
00171         pRec = pRec->pNext;
00172       }
00173       while( pRec != NULL );
00174       DnsRecordListFree( pRecord, DnsFreeRecordList );
00175     }
00176     else
00177       error = true;
00178 
00179     if( error || !servers.size() )
00180     {
00181       servers = defaultHostMap( domain, logInstance );
00182     }
00183 
00184     return servers;
00185   }
00186 
00187 #else
00188   DNS::HostMap DNS::resolve( const std::string& service, const std::string& proto,
00189                              const std::string& domain, const LogSink& logInstance )
00190   {
00191     logInstance.warn( LogAreaClassDns, "notice: gloox does not support SRV "
00192                         "records on this platform. Using A records instead." );
00193     return defaultHostMap( domain, logInstance );
00194   }
00195 #endif
00196 
00197   DNS::HostMap DNS::defaultHostMap( const std::string& domain, const LogSink& logInstance )
00198   {
00199     HostMap server;
00200 
00201     logInstance.warn( LogAreaClassDns, "notice: no SRV record found for "
00202                                           + domain + ", using default port." );
00203 
00204     if( !domain.empty() )
00205       server[domain] = XMPP_PORT;
00206 
00207     return server;
00208   }
00209 
00210 #ifdef HAVE_GETADDRINFO
00211   void DNS::resolve( struct addrinfo** res, const std::string& service, const std::string& proto,
00212                      const std::string& domain, const LogSink& logInstance )
00213   {
00214     logInstance.dbg( LogAreaClassDns, "Resolving: _" +  service + "._" + proto + "." + domain );
00215     struct addrinfo hints;
00216     if( proto == "tcp" )
00217       hints.ai_socktype = SOCK_STREAM;
00218     else if( proto == "udp" )
00219       hints.ai_socktype = SOCK_DGRAM;
00220     else
00221     {
00222       logInstance.err( LogAreaClassDns, "Unknown/Invalid protocol: " + proto );
00223     }
00224     memset( &hints, '\0', sizeof( hints ) );
00225     hints.ai_flags = AI_ADDRCONFIG | AI_CANONNAME;
00226     hints.ai_socktype = SOCK_STREAM;
00227     int e = getaddrinfo( domain.c_str(), service.c_str(), &hints, res );
00228     if( e )
00229       logInstance.err( LogAreaClassDns, "getaddrinfo() failed" );
00230   }
00231 
00232   int DNS::connect( const std::string& host, const LogSink& logInstance )
00233   {
00234     struct addrinfo* results = 0;
00235 
00236     resolve( &results, host, logInstance );
00237     if( !results )
00238     {
00239       logInstance.err( LogAreaClassDns, "host not found: " + host );
00240       return -ConnDnsError;
00241     }
00242 
00243     struct addrinfo* runp = results;
00244     while( runp )
00245     {
00246       int fd = DNS::connect( runp, logInstance );
00247       if( fd >= 0 )
00248         return fd;
00249 
00250       runp = runp->ai_next;
00251     }
00252 
00253     freeaddrinfo( results );
00254 
00255     return -ConnConnectionRefused;
00256   }
00257 
00258   int DNS::connect( struct addrinfo* res, const LogSink& logInstance )
00259   {
00260     if( !res )
00261       return -1;
00262 
00263     int fd = getSocket( res->ai_family, res->ai_socktype, res->ai_protocol );
00264     if( fd < 0 )
00265       return fd;
00266 
00267     if( ::connect( fd, res->ai_addr, res->ai_addrlen ) == 0 )
00268     {
00269       char ip[NI_MAXHOST];
00270       char port[NI_MAXSERV];
00271 
00272       if( getnameinfo( res->ai_addr, sizeof( sockaddr ),
00273                        ip, sizeof( ip ),
00274                        port, sizeof( port ),
00275                        NI_NUMERICHOST | NI_NUMERICSERV ) )
00276       {
00277         printf( "could not get numeric hostname");
00278       }
00279 
00280       std::ostringstream oss;
00281       oss << "Connecting to ";
00282       if( res->ai_canonname )
00283       {
00284         oss << res->ai_canonname;
00285         oss << " (" << ip << "), port " << port;
00286       }
00287       else
00288       {
00289         oss << ip << ":" << port;
00290       }
00291       logInstance.dbg( LogAreaClassDns, oss.str() );
00292       return fd;
00293     }
00294 
00295     closeSocket( fd );
00296     return -ConnConnectionRefused;
00297   }
00298 
00299 #else
00300 
00301   int DNS::connect( const std::string& host, const LogSink& logInstance )
00302   {
00303     HostMap hosts = resolve( host, logInstance );
00304     if( hosts.size() == 0 )
00305       return -ConnDnsError;
00306 
00307     HostMap::const_iterator it = hosts.begin();
00308     for( ; it != hosts.end(); ++it )
00309     {
00310       int fd = DNS::connect( (*it).first, (*it).second, logInstance );
00311       if( fd >= 0 )
00312         return fd;
00313     }
00314 
00315     return -ConnConnectionRefused;
00316   }
00317 #endif
00318 
00319   int DNS::getSocket()
00320   {
00321 #ifdef _WIN32
00322     WSADATA wsaData;
00323     if( WSAStartup( MAKEWORD( 1, 1 ), &wsaData ) != 0 )
00324       return -ConnDnsError;
00325 #endif
00326 
00327     struct protoent* prot;
00328     if( ( prot = getprotobyname( "tcp" ) ) == 0 )
00329     {
00330       cleanup();
00331       return -ConnDnsError;
00332     }
00333     return getSocket( PF_INET, SOCK_STREAM, prot->p_proto );
00334   }
00335 
00336   int DNS::getSocket( int af, int socktype, int proto )
00337   {
00338     int fd;
00339     if( ( fd = socket( af, socktype, proto ) ) == -1 )
00340     {
00341       cleanup();
00342       return -ConnConnectionRefused;
00343     }
00344 
00345 #ifdef HAVE_SETSOCKOPT
00346     int timeout = 5000;
00347     setsockopt( fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof( timeout ) );
00348     setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (char*)&timeout, sizeof( timeout ) );
00349 #endif
00350 
00351     return fd;
00352   }
00353 
00354   int DNS::connect( const std::string& host, unsigned short port, const LogSink& logInstance )
00355   {
00356     int fd = getSocket();
00357     if( fd < 0 )
00358       return fd;
00359 
00360     struct hostent* h;
00361     if( ( h = gethostbyname( host.c_str() ) ) == 0 )
00362     {
00363       cleanup();
00364       return -ConnDnsError;
00365     }
00366 
00367     struct sockaddr_in target;
00368     target.sin_family = AF_INET;
00369     target.sin_port = htons( port );
00370 
00371     if( h->h_length != sizeof( struct in_addr ) )
00372     {
00373       cleanup();
00374       return -ConnDnsError;
00375     }
00376     else
00377     {
00378       memcpy( &target.sin_addr, h->h_addr, sizeof( struct in_addr ) );
00379     }
00380 
00381 #ifndef _WIN32_WCE
00382     std::ostringstream oss;
00383 #endif
00384 
00385     memset( target.sin_zero, '\0', 8 );
00386     if( ::connect( fd, (struct sockaddr *)&target, sizeof( struct sockaddr ) ) == 0 )
00387     {
00388 #ifndef _WIN32_WCE
00389       oss << "connecting to " << host.c_str()
00390           << " (" << inet_ntoa( target.sin_addr ) << ":" << port << ")";
00391       logInstance.dbg( LogAreaClassDns, oss.str() );
00392 #endif
00393       return fd;
00394     }
00395 
00396 #ifndef _WIN32_WCE
00397     oss << "connection to " << host.c_str()
00398          << " (" << inet_ntoa( target.sin_addr ) << ":" << port << ") failed";
00399     logInstance.dbg( LogAreaClassDns, oss.str() );
00400 #endif
00401 
00402     closeSocket( fd );
00403     return -ConnConnectionRefused;
00404   }
00405 
00406   void DNS::closeSocket( int fd )
00407   {
00408 #ifndef _WIN32
00409     close( fd );
00410 #else
00411     closesocket( fd );
00412 #endif
00413   }
00414 
00415   void DNS::cleanup()
00416   {
00417 #ifdef _WIN32
00418     WSACleanup();
00419 #endif
00420   }
00421 
00422 }

Generated on Sun Oct 12 16:25:15 2008 for gloox by  doxygen 1.4.1