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