/* libobby - Network text editing library * Copyright (C) 2005-2006 0x539 dev group * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include "zeroconf_howl.hpp" namespace obby { zeroconf_howl::zeroconf_howl() { if (sw_discovery_init(&m_session) != SW_OKAY) throw std::runtime_error("sw_discovery_init() failed."); if (sw_discovery_salt(m_session, &m_salt) != SW_OKAY) throw std::runtime_error("sw_discovery_salt() failed."); } zeroconf_howl::~zeroconf_howl() { unpublish_all(); sw_discovery_fina(m_session); } void zeroconf_howl::publish(const std::string& name, unsigned int port) { sw_discovery_oid oid; sw_result result; /* This publishes a record for other users of this library within the * default domain (.local) */ if((result = sw_discovery_publish(m_session, 0, name.c_str(), "_lobby._tcp.", NULL, NULL, port, NULL, 0, &zeroconf_howl::handle_publish_reply, static_cast(this), &oid)) != SW_OKAY) { std::stringstream stream; stream << "sw_discovery_publish(...) failed: " << result; throw std::runtime_error(stream.str()); } else { m_published[name] = oid; } } void zeroconf_howl::unpublish(const std::string& name) { if(!m_published[name]) { std::stringstream stream; stream << "unpublish not possible for \"" << name << "\""; throw std::runtime_error(stream.str()); } else { sw_discovery_cancel(m_session, m_published[name]); } m_published.erase(name); } void zeroconf_howl::unpublish_all() { std::map::iterator i; for(i = m_published.begin(); i != m_published.end(); ++i) sw_discovery_cancel(m_session, i->second); m_published.clear(); } void zeroconf_howl::discover() { sw_discovery_oid oid; sw_result result; if ((result = sw_discovery_browse(m_session, 0, "_lobby._tcp", NULL, &zeroconf_howl::handle_browse_reply, static_cast(this), &oid)) != SW_OKAY) { std::stringstream stream; stream << "discover failed: " << result; throw std::runtime_error(stream.str()); } } void zeroconf_howl::select() { sw_discovery_run(m_session); } void zeroconf_howl::select(unsigned int msecs) { sw_ulong ms = msecs; sw_salt_step(m_salt, &ms); } sw_result zeroconf_howl::handle_publish_reply(sw_discovery discovery, sw_discovery_oid oid, sw_discovery_publish_status status, sw_opaque extra) { if (status != SW_OKAY) { std::stringstream stream; stream << "publish failed: " << status; throw std::runtime_error(stream.str()); } return SW_OKAY; } sw_result zeroconf_howl::handle_browse_reply(sw_discovery discovery, sw_discovery_oid oid, sw_discovery_browse_status status, sw_uint32 interface_index, sw_const_string name, sw_const_string type, sw_const_string domain, sw_opaque extra) { sw_discovery session = static_cast(extra)->m_session; switch(status) { case SW_DISCOVERY_BROWSE_INVALID: { throw std::runtime_error( "sw_discovery failed within the callback"); break; } case SW_DISCOVERY_BROWSE_ADD_SERVICE: { sw_result result; if ((result = sw_discovery_resolve(session, interface_index, name, type, domain, &zeroconf_howl::handle_resolve_reply, extra, &oid)) != SW_OKAY) { std::stringstream stream; stream << "resolve failed: " << result; throw std::runtime_error(stream.str()); } break; } case SW_DISCOVERY_BROWSE_REMOVE_SERVICE: { static_cast( extra)->leave_event().emit(name); break; } } return SW_OKAY; } sw_result zeroconf_howl::handle_resolve_reply(sw_discovery discovery, sw_discovery_oid oid, sw_uint32 interface_index, sw_const_string name, sw_const_string type, sw_const_string domain, sw_ipv4_address address, sw_port port, sw_octets text_record, sw_ulong text_record_len, sw_opaque extra) { // At least newer revisions of OS X emit 0.0.0.0 as a second pseudo // entry when discovering the local host as the IPv6 address gets // parsed as a IPv4 address anywhere within Howl which does not yet // support service discovery on IPv6. uint32_t ip(sw_ipv4_address_saddr(address)); if(ip != 0) static_cast(extra)->discover_event().emit( name, net6::ipv4_address::create_from_address( ip, port)); return SW_OKAY; } }