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

Generated on Tue May 1 14:20:20 2007 for gloox by  doxygen 1.5.1