dns.cpp

00001 /*
00002   Copyright (c) 2005-2007 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.log( LogLevelWarning, LogAreaClassDns,
00192                     "notice: gloox does not support SRV 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.log( LogLevelWarning, LogAreaClassDns, "notice: no SRV record found for " + domain
00202                                                        + ", using default port." );
00203 
00204     if( !domain.empty() )
00205       server[domain] = XMPP_PORT;
00206 
00207     return server;
00208   }
00209 
00210   int DNS::connect( const std::string& domain, const LogSink& logInstance )
00211   {
00212     HostMap hosts = resolve( domain, logInstance );
00213     if( hosts.size() == 0 )
00214       return -ConnDnsError;
00215 
00216     HostMap::const_iterator it = hosts.begin();
00217     for( ; it != hosts.end(); ++it )
00218     {
00219       int fd = DNS::connect( (*it).first, (*it).second, logInstance );
00220       if( fd >= 0 )
00221         return fd;
00222     }
00223 
00224     return -ConnConnectionRefused;
00225   }
00226 
00227   int DNS::getSocket()
00228   {
00229 #ifdef _WIN32
00230     WSADATA wsaData;
00231     if( WSAStartup( MAKEWORD( 1, 1 ), &wsaData ) != 0 )
00232       return -ConnDnsError;
00233 #endif
00234 
00235     struct protoent* prot;
00236     if( ( prot = getprotobyname( "tcp" ) ) == 0 )
00237     {
00238       cleanup();
00239       return -ConnDnsError;
00240     }
00241 
00242     int fd;
00243     if( ( fd = socket( PF_INET, SOCK_STREAM, prot->p_proto ) ) == -1 )
00244     {
00245       cleanup();
00246       return -ConnConnectionRefused;
00247     }
00248 
00249 #ifdef HAVE_SETSOCKOPT
00250     int val = 1;
00251     setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (char*)&val, sizeof( val ) );
00252 #endif
00253 
00254     return fd;
00255   }
00256 
00257   int DNS::connect( const std::string& domain, unsigned short port, const LogSink& logInstance )
00258   {
00259     int fd = getSocket();
00260     if( fd < 0 )
00261       return fd;
00262 
00263     struct hostent *h;
00264     if( ( h = gethostbyname( domain.c_str() ) ) == 0 )
00265     {
00266       cleanup();
00267       return -ConnDnsError;
00268     }
00269 
00270     struct sockaddr_in target;
00271     target.sin_family = AF_INET;
00272     target.sin_port = htons( port );
00273 
00274     if( h->h_length != sizeof( struct in_addr ) )
00275     {
00276       cleanup();
00277       return -ConnDnsError;
00278     }
00279     else
00280     {
00281       memcpy( &target.sin_addr, h->h_addr, sizeof( struct in_addr ) );
00282     }
00283 
00284 #ifndef _WIN32_WCE
00285     std::ostringstream oss;
00286 #endif
00287 
00288     memset( target.sin_zero, '\0', 8 );
00289     if( ::connect( fd, (struct sockaddr *)&target, sizeof( struct sockaddr ) ) == 0 )
00290     {
00291 #ifndef _WIN32_WCE
00292       oss << "connecting to " << domain.c_str()
00293           << " (" << inet_ntoa( target.sin_addr ) << ":" << port << ")";
00294       logInstance.log( LogLevelDebug, LogAreaClassDns, oss.str() );
00295 #endif
00296       return fd;
00297     }
00298 
00299 #ifndef _WIN32_WCE
00300     oss << "connection to " << domain.c_str()
00301          << " (" << inet_ntoa( target.sin_addr ) << ":" << port << ") failed";
00302     logInstance.log( LogLevelDebug, LogAreaClassDns, oss.str() );
00303 #endif
00304 
00305     closeSocket( fd );
00306     return -ConnConnectionRefused;
00307   }
00308 
00309   void DNS::closeSocket( int fd )
00310   {
00311 #ifndef _WIN32
00312     close( fd );
00313 #else
00314     closesocket( fd );
00315 #endif
00316   }
00317 
00318   void DNS::cleanup()
00319   {
00320 #ifdef _WIN32
00321     WSACleanup();
00322 #endif
00323   }
00324 
00325 }

Generated on Sat Nov 10 08:50:27 2007 for gloox by  doxygen 1.5.3-20071008