00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 #include "config.h"
00015
00016 #include "gloox.h"
00017 #include "dns.h"
00018 #include "util.h"
00019
00020 #ifndef _WIN32_WCE
00021 # include <sys/types.h>
00022 #endif
00023
00024 #include <stdio.h>
00025
00026 #if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ )
00027 # include <netinet/in.h>
00028 # include <arpa/nameser.h>
00029 # include <resolv.h>
00030 # include <netdb.h>
00031 # include <arpa/inet.h>
00032 # include <sys/socket.h>
00033 # include <sys/un.h>
00034 # include <unistd.h>
00035 # include <errno.h>
00036 #endif
00037
00038 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
00039 # include <winsock.h>
00040 #elif defined( _WIN32_WCE )
00041 # include <winsock2.h>
00042 #endif
00043
00044 #ifdef HAVE_WINDNS_H
00045 # include <windns.h>
00046 #endif
00047
00048 #define SRV_COST (RRFIXEDSZ+0)
00049 #define SRV_WEIGHT (RRFIXEDSZ+2)
00050 #define SRV_PORT (RRFIXEDSZ+4)
00051 #define SRV_SERVER (RRFIXEDSZ+6)
00052 #define SRV_FIXEDSZ (RRFIXEDSZ+6)
00053
00054 #ifndef T_SRV
00055 # define T_SRV 33
00056 #endif
00057
00058
00059 #ifndef DNS_TYPE_SRV
00060 # define DNS_TYPE_SRV 33
00061 #endif
00062
00063 #ifndef NS_CMPRSFLGS
00064 # define NS_CMPRSFLGS 0xc0
00065 #endif
00066
00067 #ifndef C_IN
00068 # define C_IN 1
00069 #endif
00070
00071 #ifndef INVALID_SOCKET
00072 # define INVALID_SOCKET -1
00073 #endif
00074
00075 #define XMPP_PORT 5222
00076
00077 namespace gloox
00078 {
00079
00080 #if defined( HAVE_RES_QUERYDOMAIN ) && defined( HAVE_DN_SKIPNAME ) && defined( HAVE_RES_QUERY )
00081 DNS::HostMap DNS::resolve( const std::string& service, const std::string& proto,
00082 const std::string& domain, const LogSink& logInstance )
00083 {
00084 buffer srvbuf;
00085 bool error = false;
00086
00087 const std::string dname = "_" + service + "._" + proto;
00088
00089 if( !domain.empty() )
00090 srvbuf.len = res_querydomain( dname.c_str(), const_cast<char*>( domain.c_str() ),
00091 C_IN, T_SRV, srvbuf.buf, NS_PACKETSZ );
00092 else
00093 srvbuf.len = res_query( dname.c_str(), C_IN, T_SRV, srvbuf.buf, NS_PACKETSZ );
00094
00095 if( srvbuf.len < 0 )
00096 return defaultHostMap( domain, logInstance );
00097
00098 HEADER* hdr = (HEADER*)srvbuf.buf;
00099 unsigned char* here = srvbuf.buf + NS_HFIXEDSZ;
00100
00101 if( ( hdr->tc ) || ( srvbuf.len < NS_HFIXEDSZ ) )
00102 error = true;
00103
00104 if( hdr->rcode >= 1 && hdr->rcode <= 5 )
00105 error = true;
00106
00107 if( ntohs( hdr->ancount ) == 0 )
00108 error = true;
00109
00110 if( ntohs( hdr->ancount ) > NS_PACKETSZ )
00111 error = true;
00112
00113 int cnt;
00114 for( cnt = ntohs( hdr->qdcount ); cnt > 0; --cnt )
00115 {
00116 int strlen = dn_skipname( here, srvbuf.buf + srvbuf.len );
00117 here += strlen + NS_QFIXEDSZ;
00118 }
00119
00120 unsigned char* srv[NS_PACKETSZ];
00121 int srvnum = 0;
00122 for( cnt = ntohs( hdr->ancount ); cnt > 0; --cnt )
00123 {
00124 int strlen = dn_skipname( here, srvbuf.buf + srvbuf.len );
00125 here += strlen;
00126 srv[srvnum++] = here;
00127 here += SRV_FIXEDSZ;
00128 here += dn_skipname( here, srvbuf.buf + srvbuf.len );
00129 }
00130
00131 if( error )
00132 {
00133 return defaultHostMap( domain, logInstance );
00134 }
00135
00136
00137
00138 HostMap servers;
00139 for( cnt = 0; cnt < srvnum; ++cnt )
00140 {
00141 char srvname[NS_MAXDNAME];
00142 srvname[0] = '\0';
00143
00144 if( dn_expand( srvbuf.buf, srvbuf.buf + NS_PACKETSZ,
00145 srv[cnt] + SRV_SERVER, srvname, NS_MAXDNAME ) < 0
00146 || !(*srvname) )
00147 continue;
00148
00149 unsigned char* c = srv[cnt] + SRV_PORT;
00150 servers.insert( std::make_pair( (char*)srvname, ntohs( c[1] << 8 | c[0] ) ) );
00151 }
00152
00153 if( !servers.size() )
00154 return defaultHostMap( domain, logInstance );
00155
00156 return servers;
00157 }
00158
00159 #elif defined( _WIN32 ) && defined( HAVE_WINDNS_H )
00160 DNS::HostMap DNS::resolve( const std::string& service, const std::string& proto,
00161 const std::string& domain, const LogSink& logInstance )
00162 {
00163 const std::string dname = "_" + service + "._" + proto + "." + domain;
00164 bool error = false;
00165
00166 DNS::HostMap servers;
00167 DNS_RECORD* pRecord = NULL;
00168 DNS_STATUS status = DnsQuery_UTF8( dname.c_str(), DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &pRecord, NULL );
00169 if( status == ERROR_SUCCESS )
00170 {
00171 DNS_RECORD* pRec = pRecord;
00172 do
00173 {
00174 if( pRec->wType == DNS_TYPE_SRV )
00175 {
00176 servers[pRec->Data.SRV.pNameTarget] = pRec->Data.SRV.wPort;
00177 }
00178 pRec = pRec->pNext;
00179 }
00180 while( pRec != NULL );
00181 DnsRecordListFree( pRecord, DnsFreeRecordList );
00182 }
00183 else
00184 {
00185 logInstance.warn( LogAreaClassDns, "DnsQuery_UTF8() failed: " + util::int2string( status ) );
00186 error = true;
00187 }
00188
00189 if( error || !servers.size() )
00190 {
00191 servers = defaultHostMap( domain, logInstance );
00192 }
00193
00194 return servers;
00195 }
00196
00197 #else
00198 DNS::HostMap DNS::resolve( const std::string& , const std::string& ,
00199 const std::string& domain, const LogSink& logInstance )
00200 {
00201 logInstance.warn( LogAreaClassDns, "Notice: gloox does not support SRV "
00202 "records on this platform. Using A records instead." );
00203 return defaultHostMap( domain, logInstance );
00204 }
00205 #endif
00206
00207 DNS::HostMap DNS::defaultHostMap( const std::string& domain, const LogSink& logInstance )
00208 {
00209 HostMap server;
00210
00211 logInstance.warn( LogAreaClassDns, "Notice: no SRV record found for "
00212 + domain + ", using default port." );
00213
00214 if( !domain.empty() )
00215 server[domain] = XMPP_PORT;
00216
00217 return server;
00218 }
00219
00220 #ifdef HAVE_GETADDRINFO
00221 void DNS::resolve( struct addrinfo** res, const std::string& service, const std::string& proto,
00222 const std::string& domain, const LogSink& logInstance )
00223 {
00224 logInstance.dbg( LogAreaClassDns, "Resolving: _" + service + "._" + proto + "." + domain );
00225 struct addrinfo hints;
00226 if( proto == "tcp" )
00227 hints.ai_socktype = SOCK_STREAM;
00228 else if( proto == "udp" )
00229 hints.ai_socktype = SOCK_DGRAM;
00230 else
00231 {
00232 logInstance.err( LogAreaClassDns, "Unknown/Invalid protocol: " + proto );
00233 }
00234 memset( &hints, '\0', sizeof( hints ) );
00235 hints.ai_flags = AI_ADDRCONFIG | AI_CANONNAME;
00236 hints.ai_socktype = SOCK_STREAM;
00237 int e = getaddrinfo( domain.c_str(), service.c_str(), &hints, res );
00238 if( e )
00239 logInstance.err( LogAreaClassDns, "getaddrinfo() failed" );
00240 }
00241
00242 int DNS::connect( const std::string& host, const LogSink& logInstance )
00243 {
00244 struct addrinfo* results = 0;
00245
00246 resolve( &results, host, logInstance );
00247 if( !results )
00248 {
00249 logInstance.err( LogAreaClassDns, "host not found: " + host );
00250 return -ConnDnsError;
00251 }
00252
00253 struct addrinfo* runp = results;
00254 while( runp )
00255 {
00256 int fd = DNS::connect( runp, logInstance );
00257 if( fd >= 0 )
00258 return fd;
00259
00260 runp = runp->ai_next;
00261 }
00262
00263 freeaddrinfo( results );
00264
00265 return -ConnConnectionRefused;
00266 }
00267
00268 int DNS::connect( struct addrinfo* res, const LogSink& logInstance )
00269 {
00270 if( !res )
00271 return -1;
00272
00273 int fd = getSocket( res->ai_family, res->ai_socktype, res->ai_protocol, logInstance );
00274 if( fd < 0 )
00275 return fd;
00276
00277 if( ::connect( fd, res->ai_addr, res->ai_addrlen ) == 0 )
00278 {
00279 char ip[NI_MAXHOST];
00280 char port[NI_MAXSERV];
00281
00282 if( getnameinfo( res->ai_addr, sizeof( sockaddr ),
00283 ip, sizeof( ip ),
00284 port, sizeof( port ),
00285 NI_NUMERICHOST | NI_NUMERICSERV ) )
00286 {
00287
00288
00289 }
00290
00291 if( res->ai_canonname )
00292 logInstance.dbg( LogAreaClassDns, "Connecting to " + std::string( res->ai_canonname )
00293 + " (" + ip + "), port " + port );
00294 else
00295 logInstance.dbg( LogAreaClassDns, "Connecting to " + ip + ":" + port );
00296
00297 return fd;
00298 }
00299
00300 std::string message = "connect() failed. "
00301 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
00302 "WSAGetLastError: " + util::int2string( ::WSAGetLastError() );
00303 #else
00304 "errno: " + util::int2string( errno );
00305 #endif
00306 logInstance.dbg( LogAreaClassDns, message );
00307
00308 closeSocket( fd, logInstance );
00309 return -ConnConnectionRefused;
00310 }
00311
00312 #else
00313
00314 int DNS::connect( const std::string& host, const LogSink& logInstance )
00315 {
00316 HostMap hosts = resolve( host, logInstance );
00317 if( hosts.size() == 0 )
00318 return -ConnDnsError;
00319
00320 HostMap::const_iterator it = hosts.begin();
00321 for( ; it != hosts.end(); ++it )
00322 {
00323 int fd = DNS::connect( (*it).first, (*it).second, logInstance );
00324 if( fd >= 0 )
00325 return fd;
00326 }
00327
00328 return -ConnConnectionRefused;
00329 }
00330 #endif
00331
00332 int DNS::getSocket( const LogSink& logInstance )
00333 {
00334 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
00335 WSADATA wsaData;
00336 if( WSAStartup( MAKEWORD( 1, 1 ), &wsaData ) != 0 )
00337 {
00338 logInstance.dbg( LogAreaClassDns, "WSAStartup() failed. WSAGetLastError: "
00339 + util::int2string( ::WSAGetLastError() ) );
00340 return -ConnDnsError;
00341 }
00342 #endif
00343
00344 int protocol = IPPROTO_TCP;
00345 struct protoent* prot;
00346 if( ( prot = getprotobyname( "tcp" ) ) != 0 )
00347 {
00348 protocol = prot->p_proto;
00349 }
00350 else
00351 {
00352 std::string message = "getprotobyname( \"tcp\" ) failed. "
00353 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
00354 "WSAGetLastError: " + util::int2string( ::WSAGetLastError() )
00355 #else
00356 "errno: " + util::int2string( errno );
00357 #endif
00358 + ". Falling back to IPPROTO_TCP: " + util::int2string( IPPROTO_TCP );
00359 logInstance.dbg( LogAreaClassDns, message );
00360
00361
00362 }
00363
00364 return getSocket( PF_INET, SOCK_STREAM, protocol, logInstance );
00365 }
00366
00367 int DNS::getSocket( int af, int socktype, int proto, const LogSink& logInstance )
00368 {
00369 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
00370 SOCKET fd;
00371 #else
00372 int fd;
00373 #endif
00374 if( ( fd = socket( af, socktype, proto ) ) == INVALID_SOCKET )
00375 {
00376 std::string message = "getSocket( "
00377 + util::int2string( af ) + ", "
00378 + util::int2string( socktype ) + ", "
00379 + util::int2string( proto )
00380 + " ) failed. "
00381 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
00382 "WSAGetLastError: " + util::int2string( ::WSAGetLastError() );
00383 #else
00384 "errno: " + util::int2string( errno );
00385 #endif
00386 logInstance.dbg( LogAreaClassDns, message );
00387
00388 cleanup( logInstance );
00389 return -ConnConnectionRefused;
00390 }
00391
00392 #ifdef HAVE_SETSOCKOPT
00393 int timeout = 5000;
00394 setsockopt( fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof( timeout ) );
00395 setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (char*)&timeout, sizeof( timeout ) );
00396 #endif
00397
00398 return (int)fd;
00399 }
00400
00401 int DNS::connect( const std::string& host, int port, const LogSink& logInstance )
00402 {
00403 int fd = getSocket( logInstance );
00404 if( fd < 0 )
00405 return fd;
00406
00407 struct hostent* h;
00408 if( ( h = gethostbyname( host.c_str() ) ) == 0 )
00409 {
00410 logInstance.dbg( LogAreaClassDns, "gethostbyname() failed for " + host + "." );
00411 cleanup( logInstance );
00412 return -ConnDnsError;
00413 }
00414
00415 struct sockaddr_in target;
00416 target.sin_family = AF_INET;
00417 target.sin_port = htons( static_cast<unsigned short int>( port ) );
00418
00419 if( h->h_length != sizeof( struct in_addr ) )
00420 {
00421 logInstance.dbg( LogAreaClassDns, "gethostbyname() returned unexpected structure." );
00422 cleanup( logInstance );
00423 return -ConnDnsError;
00424 }
00425 else
00426 {
00427 memcpy( &target.sin_addr, h->h_addr, sizeof( struct in_addr ) );
00428 }
00429
00430 logInstance.dbg( LogAreaClassDns, "Connecting to " + host
00431 + " (" + inet_ntoa( target.sin_addr ) + ":" + util::int2string( port ) + ")" );
00432
00433 memset( target.sin_zero, '\0', 8 );
00434 if( ::connect( fd, (struct sockaddr *)&target, sizeof( struct sockaddr ) ) == 0 )
00435 {
00436 logInstance.dbg( LogAreaClassDns, "Connected to " + host + " ("
00437 + inet_ntoa( target.sin_addr ) + ":" + util::int2string( port ) + ")" );
00438 return fd;
00439 }
00440
00441 std::string message = "Connection to " + host + " ("
00442 + inet_ntoa( target.sin_addr ) + ":" + util::int2string( port ) + ") failed. "
00443 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
00444 "WSAGetLastError: " + util::int2string( ::WSAGetLastError() );
00445 #else
00446 "errno: " + util::int2string( errno );
00447 #endif
00448 logInstance.dbg( LogAreaClassDns, message );
00449
00450 closeSocket( fd, logInstance );
00451 return -ConnConnectionRefused;
00452 }
00453
00454 void DNS::closeSocket( int fd, const LogSink& logInstance )
00455 {
00456 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
00457 int result = closesocket( fd );
00458 #else
00459 int result = close( fd );
00460 #endif
00461
00462 if( result != 0 )
00463 {
00464 std::string message = "closeSocket() failed. "
00465 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
00466 "WSAGetLastError: " + util::int2string( ::WSAGetLastError() );
00467 #else
00468 "errno: " + util::int2string( errno );
00469 #endif
00470 logInstance.dbg( LogAreaClassDns, message );
00471 }
00472 }
00473
00474 void DNS::cleanup( const LogSink& logInstance )
00475 {
00476 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
00477 if( WSACleanup() != 0 )
00478 {
00479 logInstance.dbg( LogAreaClassDns, "WSACleanup() failed. WSAGetLastError: "
00480 + util::int2string( ::WSAGetLastError() ) );
00481 }
00482 #else
00483 (void)logInstance;
00484 #endif
00485 }
00486
00487 }