gloox  0.9.9.12
dns.cpp
1 /*
2  Copyright (c) 2005-2008 by Jakob Schroeter <js@camaya.net>
3  This file is part of the gloox library. http://camaya.net/gloox
4 
5  This software is distributed under a license. The full license
6  agreement can be found in the file LICENSE in this distribution.
7  This software may not be copied, modified, sold or distributed
8  other than expressed in the named license agreement.
9 
10  This software is distributed without any warranty.
11 */
12 
13 
14 #ifdef _WIN32
15 # include "../config.h.win"
16 #elif defined( _WIN32_WCE )
17 # include "../config.h.win"
18 #else
19 # include "config.h"
20 #endif
21 
22 #include "gloox.h"
23 #include "dns.h"
24 
25 #ifndef _WIN32_WCE
26 # include <sys/types.h>
27 # include <sstream>
28 #endif
29 
30 #include <stdio.h>
31 
32 #if !defined( _WIN32 ) && !defined( _WIN32_WCE )
33 # include <netinet/in.h>
34 # include <arpa/nameser.h>
35 # include <resolv.h>
36 # include <netdb.h>
37 # include <arpa/inet.h>
38 # include <sys/socket.h>
39 # include <sys/un.h>
40 # include <unistd.h>
41 #endif
42 
43 #ifdef _WIN32
44 # include <winsock.h>
45 #elif defined( _WIN32_WCE )
46 # include <winsock2.h>
47 #endif
48 
49 #ifdef HAVE_WINDNS_H
50 # include <windns.h>
51 #endif
52 
53 #define SRV_COST (RRFIXEDSZ+0)
54 #define SRV_WEIGHT (RRFIXEDSZ+2)
55 #define SRV_PORT (RRFIXEDSZ+4)
56 #define SRV_SERVER (RRFIXEDSZ+6)
57 #define SRV_FIXEDSZ (RRFIXEDSZ+6)
58 
59 #ifndef T_SRV
60 # define T_SRV 33
61 #endif
62 
63 // mingw
64 #ifndef DNS_TYPE_SRV
65 # define DNS_TYPE_SRV 33
66 #endif
67 
68 #ifndef C_IN
69 # define C_IN 1
70 #endif
71 
72 #ifndef INVALID_SOCKET
73 # define INVALID_SOCKET -1
74 #endif
75 
76 #define XMPP_PORT 5222
77 
78 namespace gloox
79 {
80 
81 #if defined( HAVE_RES_QUERYDOMAIN ) && defined( HAVE_DN_SKIPNAME ) && defined( HAVE_RES_QUERY )
82  DNS::HostMap DNS::resolve( const std::string& service, const std::string& proto,
83  const std::string& domain, const LogSink& logInstance )
84  {
85  buffer srvbuf;
86  bool error = false;
87 
88  const std::string dname = "_" + service + "._" + proto;
89 
90  if( !domain.empty() )
91  srvbuf.len = res_querydomain( dname.c_str(), const_cast<char*>( domain.c_str() ),
92  C_IN, T_SRV, srvbuf.buf, NS_PACKETSZ );
93  else
94  srvbuf.len = res_query( dname.c_str(), C_IN, T_SRV, srvbuf.buf, NS_PACKETSZ );
95 
96  if( srvbuf.len < 0 )
97  return defaultHostMap( domain, logInstance );
98 
99  HEADER* hdr = (HEADER*)srvbuf.buf;
100  unsigned char* here = srvbuf.buf + NS_HFIXEDSZ;
101 
102  if( ( hdr->tc ) || ( srvbuf.len < NS_HFIXEDSZ ) )
103  error = true;
104 
105  if( hdr->rcode >= 1 && hdr->rcode <= 5 )
106  error = true;
107 
108  if( ntohs( hdr->ancount ) == 0 )
109  error = true;
110 
111  if( ntohs( hdr->ancount ) > NS_PACKETSZ )
112  error = true;
113 
114  int cnt;
115  for( cnt = ntohs( hdr->qdcount ); cnt>0; --cnt )
116  {
117  int strlen = dn_skipname( here, srvbuf.buf + srvbuf.len );
118  here += strlen + NS_QFIXEDSZ;
119  }
120 
121  unsigned char *srv[NS_PACKETSZ];
122  int srvnum = 0;
123  for( cnt = ntohs( hdr->ancount ); cnt>0; --cnt )
124  {
125  int strlen = dn_skipname( here, srvbuf.buf + srvbuf.len );
126  here += strlen;
127  srv[srvnum++] = here;
128  here += SRV_FIXEDSZ;
129  here += dn_skipname( here, srvbuf.buf + srvbuf.len );
130  }
131 
132  if( error )
133  {
134  return defaultHostMap( domain, logInstance );
135  }
136 
137  // (q)sort here
138 
139  HostMap servers;
140  for( cnt=0; cnt<srvnum; ++cnt )
141  {
142  name srvname;
143 
144  if( ns_name_ntop( srv[cnt] + SRV_SERVER, (char*)srvname, NS_MAXDNAME ) < 0 )
145  printf( "handle this error!\n" );
146 
147  unsigned char* c = srv[cnt] + SRV_PORT;
148 
149  servers.insert( std::make_pair( (char*)srvname, ntohs( c[1] << 8 | c[0] ) ) );
150  }
151 
152  return servers;
153  }
154 
155 #elif defined( _WIN32 ) && defined( HAVE_WINDNS_H )
156  DNS::HostMap DNS::resolve( const std::string& service, const std::string& proto,
157  const std::string& domain, const LogSink& logInstance )
158  {
159  const std::string dname = "_" + service + "._" + proto + "." + domain;
160  bool error = false;
161 
162  DNS::HostMap servers;
163  DNS_RECORD *pRecord;
164  if( DnsQuery( dname.c_str(), DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &pRecord, NULL ) == ERROR_SUCCESS )
165  {
166  DNS_RECORD *pRec = pRecord;
167  do
168  {
169  if( pRec->wType == DNS_TYPE_SRV )
170  {
171  servers[pRec->Data.SRV.pNameTarget] = pRec->Data.SRV.wPort;
172  }
173  pRec = pRec->pNext;
174  }
175  while( pRec != NULL );
176  DnsRecordListFree( pRecord, DnsFreeRecordList );
177  }
178  else
179  error = true;
180 
181  if( error || !servers.size() )
182  {
183  servers = defaultHostMap( domain, logInstance );
184  }
185 
186  return servers;
187  }
188 
189 #else
190  DNS::HostMap DNS::resolve( const std::string& /*service*/, const std::string& /*proto*/,
191  const std::string& domain, const LogSink& logInstance )
192  {
193  logInstance.log( LogLevelWarning, LogAreaClassDns,
194  "notice: gloox does not support SRV records on this platform. Using A records instead." );
195  return defaultHostMap( domain, logInstance );
196  }
197 #endif
198 
199  DNS::HostMap DNS::defaultHostMap( const std::string& domain, const LogSink& logInstance )
200  {
201  HostMap server;
202 
203  logInstance.log( LogLevelWarning, LogAreaClassDns, "notice: no SRV record found for " + domain
204  + ", using default port." );
205 
206  if( !domain.empty() )
207  server[domain] = XMPP_PORT;
208 
209  return server;
210  }
211 
212  int DNS::connect( const std::string& domain, const LogSink& logInstance )
213  {
214  HostMap hosts = resolve( domain, logInstance );
215  if( hosts.size() == 0 )
216  return -ConnDnsError;
217 
218  HostMap::const_iterator it = hosts.begin();
219  for( ; it != hosts.end(); ++it )
220  {
221  int fd = DNS::connect( (*it).first, (*it).second, logInstance );
222  if( fd >= 0 )
223  return fd;
224  }
225 
226  return -ConnConnectionRefused;
227  }
228 
230  {
231 #ifdef _WIN32
232  WSADATA wsaData;
233  if( WSAStartup( MAKEWORD( 1, 1 ), &wsaData ) != 0 )
234  return -ConnDnsError;
235 #endif
236 
237  struct protoent* prot;
238  if( ( prot = getprotobyname( "tcp" ) ) == 0 )
239  {
240  cleanup();
241  return -ConnDnsError;
242  }
243 
244  int fd;
245  if( ( fd = socket( PF_INET, SOCK_STREAM, prot->p_proto ) ) == INVALID_SOCKET )
246  {
247  cleanup();
248  return -ConnConnectionRefused;
249  }
250 
251 #ifdef HAVE_SETSOCKOPT
252  int val = 1;
253  setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (char*)&val, sizeof( val ) );
254 #endif
255 
256  return fd;
257  }
258 
259  int DNS::connect( const std::string& domain, unsigned short port, const LogSink& logInstance )
260  {
261  int fd = getSocket();
262  if( fd < 0 )
263  return fd;
264 
265  struct hostent *h;
266  if( ( h = gethostbyname( domain.c_str() ) ) == 0 )
267  {
268  cleanup();
269  return -ConnDnsError;
270  }
271 
272  struct sockaddr_in target;
273  target.sin_family = AF_INET;
274  target.sin_port = htons( port );
275 
276  if( h->h_length != sizeof( struct in_addr ) )
277  {
278  cleanup();
279  return -ConnDnsError;
280  }
281  else
282  {
283  memcpy( &target.sin_addr, h->h_addr, sizeof( struct in_addr ) );
284  }
285 
286 #ifndef _WIN32_WCE
287  std::ostringstream oss;
288 #endif
289 
290  memset( target.sin_zero, '\0', 8 );
291  if( ::connect( fd, (struct sockaddr *)&target, sizeof( struct sockaddr ) ) == 0 )
292  {
293 #ifndef _WIN32_WCE
294  oss << "connecting to " << domain.c_str()
295  << " (" << inet_ntoa( target.sin_addr ) << ":" << port << ")";
296  logInstance.log( LogLevelDebug, LogAreaClassDns, oss.str() );
297 #endif
298  return fd;
299  }
300 
301 #ifndef _WIN32_WCE
302  oss << "connection to " << domain.c_str()
303  << " (" << inet_ntoa( target.sin_addr ) << ":" << port << ") failed";
304  logInstance.log( LogLevelDebug, LogAreaClassDns, oss.str() );
305 #endif
306 
307  closeSocket( fd );
308  return -ConnConnectionRefused;
309  }
310 
311  void DNS::closeSocket( int fd )
312  {
313 #ifndef _WIN32
314  close( fd );
315 #else
316  closesocket( fd );
317 #endif
318  }
319 
320  void DNS::cleanup()
321  {
322 #ifdef _WIN32
323  WSACleanup();
324 #endif
325  }
326 
327 }