00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
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
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 }