00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
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
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
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.warn( LogAreaClassDns, "notice: gloox does not support SRV "
00192 "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.warn( LogAreaClassDns, "notice: no SRV record found for "
00202 + domain + ", using default port." );
00203
00204 if( !domain.empty() )
00205 server[domain] = XMPP_PORT;
00206
00207 return server;
00208 }
00209
00210 #ifdef HAVE_GETADDRINFO
00211 void DNS::resolve( struct addrinfo** res, const std::string& service, const std::string& proto,
00212 const std::string& domain, const LogSink& logInstance )
00213 {
00214 logInstance.dbg( LogAreaClassDns, "Resolving: _" + service + "._" + proto + "." + domain );
00215 struct addrinfo hints;
00216 if( proto == "tcp" )
00217 hints.ai_socktype = SOCK_STREAM;
00218 else if( proto == "udp" )
00219 hints.ai_socktype = SOCK_DGRAM;
00220 else
00221 {
00222 logInstance.err( LogAreaClassDns, "Unknown/Invalid protocol: " + proto );
00223 }
00224 memset( &hints, '\0', sizeof( hints ) );
00225 hints.ai_flags = AI_ADDRCONFIG | AI_CANONNAME;
00226 hints.ai_socktype = SOCK_STREAM;
00227 int e = getaddrinfo( domain.c_str(), service.c_str(), &hints, res );
00228 if( e )
00229 logInstance.err( LogAreaClassDns, "getaddrinfo() failed" );
00230 }
00231
00232 int DNS::connect( const std::string& host, const LogSink& logInstance )
00233 {
00234 struct addrinfo* results = 0;
00235
00236 resolve( &results, host, logInstance );
00237 if( !results )
00238 {
00239 logInstance.err( LogAreaClassDns, "host not found: " + host );
00240 return -ConnDnsError;
00241 }
00242
00243 struct addrinfo* runp = results;
00244 while( runp )
00245 {
00246 int fd = DNS::connect( runp, logInstance );
00247 if( fd >= 0 )
00248 return fd;
00249
00250 runp = runp->ai_next;
00251 }
00252
00253 freeaddrinfo( results );
00254
00255 return -ConnConnectionRefused;
00256 }
00257
00258 int DNS::connect( struct addrinfo* res, const LogSink& logInstance )
00259 {
00260 if( !res )
00261 return -1;
00262
00263 int fd = getSocket( res->ai_family, res->ai_socktype, res->ai_protocol );
00264 if( fd < 0 )
00265 return fd;
00266
00267 if( ::connect( fd, res->ai_addr, res->ai_addrlen ) == 0 )
00268 {
00269 char ip[NI_MAXHOST];
00270 char port[NI_MAXSERV];
00271
00272 if( getnameinfo( res->ai_addr, sizeof( sockaddr ),
00273 ip, sizeof( ip ),
00274 port, sizeof( port ),
00275 NI_NUMERICHOST | NI_NUMERICSERV ) )
00276 {
00277 printf( "could not get numeric hostname");
00278 }
00279
00280 std::ostringstream oss;
00281 oss << "Connecting to ";
00282 if( res->ai_canonname )
00283 {
00284 oss << res->ai_canonname;
00285 oss << " (" << ip << "), port " << port;
00286 }
00287 else
00288 {
00289 oss << ip << ":" << port;
00290 }
00291 logInstance.dbg( LogAreaClassDns, oss.str() );
00292 return fd;
00293 }
00294
00295 closeSocket( fd );
00296 return -ConnConnectionRefused;
00297 }
00298
00299 #else
00300
00301 int DNS::connect( const std::string& host, const LogSink& logInstance )
00302 {
00303 HostMap hosts = resolve( host, logInstance );
00304 if( hosts.size() == 0 )
00305 return -ConnDnsError;
00306
00307 HostMap::const_iterator it = hosts.begin();
00308 for( ; it != hosts.end(); ++it )
00309 {
00310 int fd = DNS::connect( (*it).first, (*it).second, logInstance );
00311 if( fd >= 0 )
00312 return fd;
00313 }
00314
00315 return -ConnConnectionRefused;
00316 }
00317 #endif
00318
00319 int DNS::getSocket()
00320 {
00321 #ifdef _WIN32
00322 WSADATA wsaData;
00323 if( WSAStartup( MAKEWORD( 1, 1 ), &wsaData ) != 0 )
00324 return -ConnDnsError;
00325 #endif
00326
00327 struct protoent* prot;
00328 if( ( prot = getprotobyname( "tcp" ) ) == 0 )
00329 {
00330 cleanup();
00331 return -ConnDnsError;
00332 }
00333 return getSocket( PF_INET, SOCK_STREAM, prot->p_proto );
00334 }
00335
00336 int DNS::getSocket( int af, int socktype, int proto )
00337 {
00338 int fd;
00339 if( ( fd = socket( af, socktype, proto ) ) == -1 )
00340 {
00341 cleanup();
00342 return -ConnConnectionRefused;
00343 }
00344
00345 #ifdef HAVE_SETSOCKOPT
00346 int timeout = 5000;
00347 setsockopt( fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof( timeout ) );
00348 setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (char*)&timeout, sizeof( timeout ) );
00349 #endif
00350
00351 return fd;
00352 }
00353
00354 int DNS::connect( const std::string& host, unsigned short port, const LogSink& logInstance )
00355 {
00356 int fd = getSocket();
00357 if( fd < 0 )
00358 return fd;
00359
00360 struct hostent* h;
00361 if( ( h = gethostbyname( host.c_str() ) ) == 0 )
00362 {
00363 cleanup();
00364 return -ConnDnsError;
00365 }
00366
00367 struct sockaddr_in target;
00368 target.sin_family = AF_INET;
00369 target.sin_port = htons( port );
00370
00371 if( h->h_length != sizeof( struct in_addr ) )
00372 {
00373 cleanup();
00374 return -ConnDnsError;
00375 }
00376 else
00377 {
00378 memcpy( &target.sin_addr, h->h_addr, sizeof( struct in_addr ) );
00379 }
00380
00381 #ifndef _WIN32_WCE
00382 std::ostringstream oss;
00383 #endif
00384
00385 memset( target.sin_zero, '\0', 8 );
00386 if( ::connect( fd, (struct sockaddr *)&target, sizeof( struct sockaddr ) ) == 0 )
00387 {
00388 #ifndef _WIN32_WCE
00389 oss << "connecting to " << host.c_str()
00390 << " (" << inet_ntoa( target.sin_addr ) << ":" << port << ")";
00391 logInstance.dbg( LogAreaClassDns, oss.str() );
00392 #endif
00393 return fd;
00394 }
00395
00396 #ifndef _WIN32_WCE
00397 oss << "connection to " << host.c_str()
00398 << " (" << inet_ntoa( target.sin_addr ) << ":" << port << ") failed";
00399 logInstance.dbg( LogAreaClassDns, oss.str() );
00400 #endif
00401
00402 closeSocket( fd );
00403 return -ConnConnectionRefused;
00404 }
00405
00406 void DNS::closeSocket( int fd )
00407 {
00408 #ifndef _WIN32
00409 close( fd );
00410 #else
00411 closesocket( fd );
00412 #endif
00413 }
00414
00415 void DNS::cleanup()
00416 {
00417 #ifdef _WIN32
00418 WSACleanup();
00419 #endif
00420 }
00421
00422 }