gloox  1.0.1
linklocalmanager.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 "linklocalmanager.h"
14 
15 #ifdef HAVE_MDNS
16 
17 #include "linklocalhandler.h"
18 #include "connectiontcpclient.h"
19 #include "jid.h"
20 #include "util.h"
21 
22 #include <cstdio>
23 #include <unistd.h>
24 
25 #if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ )
26 # include <arpa/inet.h>
27 #endif
28 
29 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
30 # include <winsock.h>
31 #elif defined( _WIN32_WCE )
32 # include <winsock2.h>
33 #endif
34 
35 #define LINKLOCAL_SERVICE_PORT 5562
36 
37 const std::string LINKLOCAL_SERVICE_TYPE = "_presence._tcp";
38 
39 namespace gloox
40 {
41 
42  namespace LinkLocal
43  {
44 
45  Manager::Manager( const std::string& user, ConnectionHandler* connHandler, const LogSink &logInstance )
46  : m_publishRef( 0 ), m_browseRef( 0 ), m_user( user ), m_interface( 0 ), m_port( 0 ),
47  m_logInstance( logInstance ), m_browseFd( 0 ), m_server( connHandler, m_logInstance, EmptyString, LINKLOCAL_SERVICE_PORT ),
48  m_linkLocalHandler( 0 ), m_connectionHandler( connHandler )
49  {
50 
51  setPort( LINKLOCAL_SERVICE_PORT ); // does more than just setting m_port
52  addTXTData( "node", GLOOX_CAPS_NODE );
53  }
54 
56  {
58  stopBrowsing();
59  }
60 
61  void Manager::addTXTData( const std::string& key, const std::string& value )
62  {
63  if( value.empty() || key.empty() || key == "txtvers" )
64  return;
65 
66  m_txtData[key] = value;
67  }
68 
69  void Manager::removeTXTData( const std::string& key )
70  {
71  m_txtData.erase( key );
72  }
73 
75  {
76  if( m_publishRef )
78 
79  m_server.connect();
80 
81  std::string txtRecord;
82  txtRecord += (char)9; // length of mandatory txtvers=1
83  txtRecord += "txtvers=1"; // this is here because it SHOULD be the first entry
84  StringMap::const_iterator it = m_txtData.begin();
85  for( ; it != m_txtData.end(); ++it )
86  {
87  txtRecord += (char)( (*it).first.length() + (*it).second.length() + 1 );
88  txtRecord += (*it).first;
89  txtRecord += '=';
90  txtRecord += (*it).second;
91  }
92 
93  std::string service = m_user + "@";
94  if( m_host.empty() )
95  {
96  char host[65];
97  gethostname( host, 65 );
98  service += host;
99  }
100  else
101  service += m_host;
102 
103  /*DNSServiceErrorType e =*/ DNSServiceRegister( &m_publishRef,
104  0, // flags
105  m_interface, // interface, 0 = any, -1 = local only
106  service.c_str(), // service name, 0 = local computer name
107  LINKLOCAL_SERVICE_TYPE.c_str(), // service type
108  m_domain.c_str(), // domain, 0 = default domain(s)
109  m_host.c_str(), // host, 0 = default host name(s)
110  htons( m_port ), // port
111  (short unsigned int)txtRecord.length(), // TXT record length
112  (const void*)txtRecord.c_str(), // TXT record
113  0, // callback
114  0 ); // context
115  }
116 
118  {
119  if( m_publishRef )
120  {
121  DNSServiceRefDeallocate( m_publishRef );
122  m_publishRef = 0;
123  }
124  }
125 
127  {
128  if( !m_linkLocalHandler )
129  return false;
130 
131  if( m_browseRef )
132  stopBrowsing();
133 
134  DNSServiceErrorType e = DNSServiceBrowse( &m_browseRef,
135  0, // flags, currently ignored
136  m_interface, // interface, 0 = any, -1 = local only
137  LINKLOCAL_SERVICE_TYPE.c_str(), // service type
138  m_domain.c_str(), // domain, 0 = default domain(s)
139  &handleBrowseReply, // callback
140  this ); // context
141  if ( e != kDNSServiceErr_NoError )
142  return false;
143 
144  return true;
145  }
146 
148  {
149  if( m_browseRef )
150  {
151  DNSServiceRefDeallocate( m_browseRef );
152  m_browseRef = 0;
153  }
154  }
155 
156  void Manager::recv( int timeout )
157  {
158  if( !m_browseRef )
159  return;
160 
161  struct timeval tv;
162 
163  fd_set fds;
164  FD_ZERO( &fds );
165  // the following causes a C4127 warning in VC++ Express 2008 and possibly other versions.
166  // however, the reason for the warning can't be fixed in gloox.
167  FD_SET( DNSServiceRefSockFD( m_browseRef ), &fds );
168 
169  tv.tv_sec = timeout / 1000000;
170  tv.tv_usec = timeout % 1000000;
171 
172  if( select( FD_SETSIZE, &fds, 0, 0, timeout == -1 ? 0 : &tv ) > 0 )
173  {
174  if( FD_ISSET( DNSServiceRefSockFD( m_browseRef ), &fds ) != 0 )
175  DNSServiceProcessResult( m_browseRef );
176  }
177 
178  m_server.recv( timeout );
179  }
180 
181 
182  void Manager::handleBrowseReply( DNSServiceRef /*sdRef*/, DNSServiceFlags flags, uint32_t interfaceIndex,
183  DNSServiceErrorType errorCode, const char* serviceName, const char* regtype,
184  const char* replyDomain, void* context )
185  {
186  if( !context || errorCode != kDNSServiceErr_NoError )
187  return;
188 
189  Flag f = ( flags & kDNSServiceFlagsAdd ) == kDNSServiceFlagsAdd
190  ? AddService
191  : RemoveService;
192 
193  Manager* m = static_cast<Manager*>( context );
194  m->handleBrowse( f, serviceName, regtype, replyDomain, interfaceIndex, ( flags & kDNSServiceFlagsMoreComing ) == kDNSServiceFlagsMoreComing );
195 
196  }
197 
198  void Manager::handleBrowse( Flag flag, const std::string& service, const std::string& regtype, const std::string& domain, int interface, bool moreComing )
199  {
200  Service s( flag, service, regtype, domain, interface );
201  m_tmpServices.push_back( s );
202 
203 // switch( flag )
204 // {
205 // case AddService:
206 // {
207 // m_services.push_back( s );
208 // break;
209 // }
210 // case RemoveService:
211 // {
212 // ServiceList::iterator it = m_services.begin();
213 // for( ; it != m_services.end(); ++it )
214 // {
215 // if( (*it)->service == service && (*it)->regtype == regtype && (*it)->domain == domain )
216 // {
217 // m_services.erase( it );
218 // }
219 // }
220 // break;
221 // }
222 // }
223 
224  if( !moreComing )
225  {
226  m_linkLocalHandler->handleBrowseReply( m_tmpServices );
227  m_tmpServices.clear();
228  }
229  }
230 
231 
232 // const StringMap Manager::decodeTXT( const std::string& txt )
233 // {
234 // StringMap result;
235 // if( txt.empty() )
236 // return result;
237 //
238 // std::string::const_iterator it = txt.begin();
239 // while( it < txt.end() )
240 // {
241 // int len = (int)(*it);
242 // std::string tmp( ++it, it + len + 1 );
243 // it += len;
244 // size_t pos = tmp.find( '=' );
245 // result.insert( std::make_pair( tmp.substr( 0, pos ), tmp.substr( pos + 1 ) ) );
246 // }
247 //
248 // return result;
249 // }
250 
251  }
252 
253 }
254 
255 #endif // HAVE_MDNS