dns.cpp

00001 /*
00002   Copyright (c) 2005-2006 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 #else
00017 #include "config.h"
00018 #endif
00019 
00020 #include "dns.h"
00021 
00022 #include <sys/types.h>
00023 
00024 #ifndef WIN32
00025 #include <netinet/in.h>
00026 #include <arpa/nameser.h>
00027 #include <resolv.h>
00028 #include <netdb.h>
00029 #include <arpa/inet.h>
00030 #include <sys/socket.h>
00031 #include <sys/un.h>
00032 #include <unistd.h>
00033 #else
00034 #include <winsock.h>
00035 #endif
00036 
00037 #include <sstream>
00038 
00039 #define SRV_COST    (RRFIXEDSZ+0)
00040 #define SRV_WEIGHT  (RRFIXEDSZ+2)
00041 #define SRV_PORT    (RRFIXEDSZ+4)
00042 #define SRV_SERVER  (RRFIXEDSZ+6)
00043 #define SRV_FIXEDSZ (RRFIXEDSZ+6)
00044 
00045 #ifndef T_SRV
00046 #define T_SRV 33
00047 #endif
00048 
00049 #ifndef C_IN
00050 #define C_IN 1
00051 #endif
00052 
00053 #ifndef INVALID_SOCKET
00054 #define INVALID_SOCKET -1
00055 #endif
00056 
00057 #define XMPP_PORT 5222
00058 
00059 namespace gloox
00060 {
00061 
00062 #if !defined( HAVE_RES_QUERYDOMAIN ) || !defined( HAVE_DN_SKIPNAME ) || !defined( HAVE_RES_QUERY )
00063   int DNS::connect( const std::string& domain, const LogSink& logInstance )
00064   {
00065     logInstance.log( LogLevelWarning, LogAreaClassDns,
00066                      "note: gloox does not support SRV records on this platform." );
00067 
00068     return DNS::connect( domain, XMPP_PORT, logInstance );
00069   }
00070 #else
00071   DNS::HostMap DNS::resolve( const std::string& domain )
00072   {
00073     std::string service = "xmpp-client";
00074     std::string proto = "tcp";
00075 
00076     return resolve( service, proto, domain );
00077   }
00078 
00079   DNS::HostMap DNS::resolve( const std::string& service, const std::string& proto,
00080                              const std::string& domain )
00081   {
00082     buffer srvbuf;
00083     bool error = false;
00084 
00085     const std::string dname = "_" +  service + "._" + proto;
00086 
00087     if( !domain.empty() )
00088       srvbuf.len = res_querydomain( dname.c_str(), const_cast<char*>( domain.c_str() ),
00089                                     C_IN, T_SRV, srvbuf.buf, NS_PACKETSZ );
00090     else
00091       srvbuf.len = res_query( dname.c_str(), C_IN, T_SRV, srvbuf.buf, NS_PACKETSZ );
00092 
00093     if( srvbuf.len < 0 )
00094       return defaultHostMap( service, proto, domain );
00095 
00096     HEADER* hdr = (HEADER*)srvbuf.buf;
00097     unsigned char* here = srvbuf.buf + NS_HFIXEDSZ;
00098 
00099     if( ( hdr->tc ) || ( srvbuf.len < NS_HFIXEDSZ ) )
00100       error = true;
00101 
00102     if( hdr->rcode >= 1 && hdr->rcode <= 5 )
00103       error = true;
00104 
00105     if( ntohs( hdr->ancount ) == 0 )
00106       error = true;
00107 
00108     if( ntohs( hdr->ancount ) > NS_PACKETSZ )
00109       error = true;
00110 
00111     int cnt;
00112     for( cnt = ntohs( hdr->qdcount ); cnt>0; --cnt )
00113     {
00114       int strlen = dn_skipname( here, srvbuf.buf + srvbuf.len );
00115       here += strlen + NS_QFIXEDSZ;
00116     }
00117 
00118     unsigned char *srv[NS_PACKETSZ];
00119     int srvnum = 0;
00120     for( cnt = ntohs( hdr->ancount ); cnt>0; --cnt )
00121     {
00122       int strlen = dn_skipname( here, srvbuf.buf + srvbuf.len );
00123       here += strlen;
00124       srv[srvnum++] = here;
00125       here += SRV_FIXEDSZ;
00126       here += dn_skipname( here, srvbuf.buf + srvbuf.len );
00127     }
00128 
00129     if( error )
00130     {
00131       return defaultHostMap( service, proto, domain );
00132     }
00133 
00134     // (q)sort here
00135 
00136     HostMap servers;
00137     for( cnt=0; cnt<srvnum; ++cnt )
00138     {
00139       name srvname;
00140 
00141       if( ns_name_ntop( srv[cnt] + SRV_SERVER, (char*)srvname, NS_MAXDNAME ) < 0 )
00142         printf( "handle this error!\n" );
00143 
00144       servers[(char*)srvname] = ns_get16( srv[cnt] + SRV_PORT );
00145     }
00146 
00147     return servers;
00148   }
00149 
00150   DNS::HostMap DNS::defaultHostMap( const std::string& service, const std::string& proto,
00151                                const std::string& domain )
00152   {
00153     HostMap server;
00154     struct servent *servent;
00155 
00156     if( ( servent = getservbyname( service.c_str(), proto.c_str() ) ) == 0 )
00157     {
00158       server[domain] = 0;
00159       return server;
00160     }
00161 
00162     if( !domain.empty() )
00163       server[domain] = ntohs( servent->s_port );
00164 
00165     return server;
00166   }
00167 
00168   int DNS::connect( const std::string& domain, const LogSink& logInstance )
00169   {
00170     HostMap hosts = resolve( domain );
00171     if( hosts.size() == 0 )
00172       return -DNS_NO_HOSTS_FOUND;
00173 
00174     struct protoent* prot;
00175     if( ( prot = getprotobyname( "tcp" ) ) == 0)
00176       return -DNS_COULD_NOT_RESOLVE;
00177 
00178     int fd;
00179     if( ( fd = socket( PF_INET, SOCK_STREAM, prot->p_proto ) ) == -1 )
00180       return -DNS_COULD_NOT_RESOLVE;
00181 
00182     struct hostent *h;
00183     struct sockaddr_in target;
00184     target.sin_family = AF_INET;
00185     int ret = 0;
00186     HostMap::const_iterator it = hosts.begin();
00187     for( ; it != hosts.end(); ++it )
00188     {
00189       int port;
00190       if( (*it).second == 0 )
00191         port = XMPP_PORT;
00192       else
00193         port = (*it).second;
00194 
00195       target.sin_port = htons( port );
00196       if( ( h = gethostbyname( (*it).first.c_str() ) ) == 0 )
00197       {
00198         ret = -DNS_COULD_NOT_RESOLVE;
00199         continue;
00200       }
00201 
00202       in_addr *addr = (in_addr*)malloc( sizeof( in_addr ) );
00203       memcpy( addr, h->h_addr, sizeof( in_addr ) );
00204       char *tmp = inet_ntoa( *addr );
00205       free( addr );
00206       std::ostringstream oss;
00207       oss << "resolved " << (*it).first.c_str() <<  " to: " << tmp << ":" << port;
00208       logInstance.log( LogLevelDebug, LogAreaClassDns, oss.str() );
00209 
00210       if( inet_aton( tmp, &(target.sin_addr) ) == 0 )
00211         continue;
00212 
00213       memset( target.sin_zero, '\0', 8 );
00214       if( ::connect( fd, (struct sockaddr *)&target, sizeof( struct sockaddr ) ) == 0 )
00215         return fd;
00216 
00217       close( fd );
00218     }
00219     if( ret )
00220       return ret;
00221 
00222     return -DNS_COULD_NOT_CONNECT;
00223   }
00224 #endif
00225 
00226   int DNS::connect( const std::string& domain, int port, const LogSink& logInstance )
00227   {
00228 #ifdef WIN32
00229     WSADATA wsaData;
00230     if( WSAStartup( MAKEWORD( 1, 1 ), &wsaData ) != 0 )
00231       return -DNS_COULD_NOT_RESOLVE;
00232 #endif
00233 
00234     struct protoent* prot;
00235     if( ( prot = getprotobyname( "tcp" ) ) == 0 )
00236     {
00237       cleanup();
00238       return -DNS_COULD_NOT_RESOLVE;
00239     }
00240 
00241     int fd;
00242     if( ( fd = socket( PF_INET, SOCK_STREAM, prot->p_proto ) ) == -1 )
00243     {
00244       cleanup();
00245       return -DNS_COULD_NOT_CONNECT;
00246     }
00247 
00248     struct hostent *h;
00249     if( ( h = gethostbyname( domain.c_str() ) ) == 0 )
00250     {
00251       cleanup();
00252       return -DNS_COULD_NOT_RESOLVE;
00253     }
00254 
00255     struct sockaddr_in target;
00256     target.sin_family = AF_INET;
00257     target.sin_port = htons( port );
00258 
00259     if( h->h_length != sizeof( struct in_addr ) )
00260     {
00261       cleanup();
00262       return -DNS_COULD_NOT_RESOLVE;
00263     }
00264     else
00265     {
00266       memcpy( &target.sin_addr, h->h_addr, sizeof( struct in_addr ) );
00267     }
00268 
00269     std::ostringstream oss;
00270     oss << "resolved " << domain.c_str() << " to: " << inet_ntoa( target.sin_addr );
00271     logInstance.log( LogLevelDebug, LogAreaClassDns, oss.str() );
00272 
00273     memset( target.sin_zero, '\0', 8 );
00274     if( ::connect( fd, (struct sockaddr *)&target, sizeof( struct sockaddr ) ) == 0 )
00275       return fd;
00276 
00277 #ifndef WIN32
00278     close( fd );
00279 #else
00280     closesocket( fd );
00281     cleanup();
00282 #endif
00283     return -DNS_COULD_NOT_CONNECT;
00284   }
00285 
00286   void DNS::cleanup()
00287   {
00288 #ifdef WIN32
00289     WSACleanup();
00290 #endif
00291   }
00292 }

Generated on Sun Sep 24 21:57:31 2006 for gloox by  doxygen 1.4.7