dns.cpp

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

Generated on Mon Jan 16 16:19:54 2006 for gloox by  doxygen 1.4.6