gloox
1.0
|
00001 /* 00002 Copyright (c) 2005-2009 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 #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 // mingw 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 // (q)sort here 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& /*service*/, const std::string& /*proto*/, 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 //FIXME do we need to handle this? How? Can it actually happen at all? 00288 // printf( "could not get numeric hostname"); 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 // Do not return an error. We'll fall back to IPPROTO_TCP. 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 }