gloox  1.0.1
linklocalclient.cpp
1 /*
2  Copyright (c) 2012 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 #include "linklocalclient.h"
14 
15 #ifdef HAVE_MDNS
16 
17 #include "gloox.h"
18 #include "tag.h"
19 #include "util.h"
20 #include "connectiontcpclient.h"
21 
22 #include <cstdio>
23 
24 #if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ )
25 # include <arpa/inet.h>
26 #endif
27 
28 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
29 # include <winsock.h>
30 #elif defined( _WIN32_WCE )
31 # include <winsock2.h>
32 #endif
33 
34 namespace gloox
35 {
36 
37  namespace LinkLocal
38  {
39 
40  Client::Client( const JID& jid )
41  : gloox::Client( jid, EmptyString ), m_qRef( 0 ), m_rRef( 0 ), m_currentRef( 0 ),
42  m_interface( 0 ), m_port( 0 ), m_streamSent( false )
43  {
44  }
45 
47  {
48  }
49 
51  {
52  return ClientBase::connect( false );
53  }
54 
55  bool Client::connect( const std::string& service, const std::string& type,
56  const std::string& domain, int interface )
57  {
58  m_interface = interface;
59  return resolve( service, type, domain );
60  }
61 
63  {
65  return ClientBase::recv( timeout );
66  else
67  {
68  if( !m_currentRef )
69  return ConnNoError;
70 
71  struct timeval tv;
72 
73  fd_set fds;
74  FD_ZERO( &fds );
75  // the following causes a C4127 warning in VC++ Express 2008 and possibly other versions.
76  // however, the reason for the warning can't be fixed in gloox.
77  FD_SET( DNSServiceRefSockFD( m_currentRef ), &fds );
78 
79  tv.tv_sec = timeout / 1000000;
80  tv.tv_usec = timeout % 1000000;
81 
82  if( select( FD_SETSIZE, &fds, 0, 0, timeout == -1 ? 0 : &tv ) > 0 )
83  {
84  if( FD_ISSET( DNSServiceRefSockFD( m_currentRef ), &fds ) != 0 )
85  DNSServiceProcessResult( m_currentRef );
86  }
87 
88  return ConnNoError;
89  }
90  }
91 
92 
93  bool Client::resolve( const std::string& service, const std::string& type,
94  const std::string& domain )
95  {
96  m_to = service;
97  m_rRef = 0;
98  DNSServiceErrorType e = DNSServiceResolve( &m_rRef, 0, m_interface, service.c_str(), type.c_str(),
99  domain.c_str(), &handleResolveReply, this );
100  if( e != kDNSServiceErr_NoError )
101  {
102  DNSServiceRefDeallocate( m_rRef );
103  m_rRef = 0;
104  return false;
105  }
106  m_currentRef = m_rRef;
107 
108  return true;
109  }
110 
111  void Client::handleResolveReply( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
112  DNSServiceErrorType errorCode, const char* fullname, const char* hosttarget,
113  uint16_t port, uint16_t txtLen, const unsigned char* txtRecord, void* context )
114  {
115  if( !context || errorCode != kDNSServiceErr_NoError )
116  return;
117 
118  // printf("Client::handleResolveReply susccessful, querying %s\n", hosttarget );
119 
120  static_cast<Client*>( context )->query( hosttarget, ntohs( port ) );
121  }
122 
123  bool Client::query( const std::string& hostname, int port )
124  {
125  m_port = port;
126  m_qRef = 0;
127  DNSServiceErrorType e = DNSServiceQueryRecord( &m_qRef, 0, m_interface, hostname.c_str(), kDNSServiceType_A,
128  kDNSServiceClass_IN, &handleQueryReply, this );
129  if( e != kDNSServiceErr_NoError )
130  {
131  // printf( "Client::query() failed\n" );
132  DNSServiceRefDeallocate( m_qRef );
133  m_qRef = 0;
134  return false;
135  }
136  m_currentRef = m_qRef;
137 
138  return true;
139  }
140 
141  void Client::handleQueryReply( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
142  DNSServiceErrorType errorCode, const char *fullname, uint16_t rrtype,
143  uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl,
144  void *context )
145  {
146  // printf("Client::handleQueryReply returned\n" );
147 
148  if( !context || errorCode != kDNSServiceErr_NoError )
149  return;
150 
151  const unsigned char* rd = static_cast<const unsigned char*>( rdata );
152  std::string addr = util::int2string( rd[0] );
153  addr += '.';
154  addr += util::int2string( rd[1] );
155  addr += '.';
156  addr += util::int2string( rd[2] );
157  addr += '.';
158  addr += util::int2string( rd[3] );
159  // printf( "host %s is at %s\n", fullname, addr.c_str() );
160  static_cast<Client*>( context )->handleQuery( addr );
161  }
162 
163  void Client::handleQuery( const std::string& addr )
164  {
165  if( m_rRef )
166  {
167  DNSServiceRefDeallocate( m_rRef );
168  m_rRef = 0;
169  }
170 
171  ConnectionTCPClient* connection = new ConnectionTCPClient( this, logInstance(), addr, m_port );
172  // printf( "LinkLocal::Client: connecting to %s:%d\n", addr.c_str(), m_port );
173  ConnectionError e = connection->connect();
174  if( e != ConnNoError )
175  {
176  // printf( "connection error: %d\n", e );
177  delete connection;
178  }
179  }
180 
181  void Client::handleConnect( const ConnectionBase* connection )
182  {
183  if( m_qRef )
184  {
185  DNSServiceRefDeallocate( m_qRef );
186  m_qRef = 0;
187  m_currentRef = 0;
188  }
189 
190  // printf( "LinkLocal::Client::handleConnect()!!!\n" );
191  ConnectionBase* cb = const_cast<ConnectionBase*>( connection );
193  gloox::Client::connect( false );
194  sendStart( m_to );
195  }
196 
197  void Client::handleStartNode( const Tag* start )
198  {
199  // printf( "LinkLocal::Client::handleStartNode()\n" );
200  if( start && !m_streamSent )
201  sendStart( start->findAttribute( "from" ) );
202  }
203 
204  void Client::sendStart( const std::string& to )
205  {
206  m_streamSent = true;
207  std::string s = "<?xml version='1.0' encoding='UTF-8'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' to='";
208  s += to;
209  s += "' from='";
210  s += m_jid.full().c_str();
211  s += "' version='1.0'>";
212  send( s );
213  }
214 
215  }
216 
217 }
218 
219 #endif // HAVE_MDNS