common: prevent buffer overflow
[supercollider.git] / server / scsynth / Rendezvous.cpp
blob5f1703a13844f8d9353ad38806a7207a92bc24c6
1 /*
2 * Rendezvous.cpp
3 * SC3synth
5 * Created by C. Ramakrishnan on Wed Dec 18 2002.
6 * Illposed Software
8 * Avahi implementation (c) 2006 stefan kersten
9 * Howl implementation (c) 2005 2006 stefan kersten
13 SuperCollider real time audio synthesis system
14 Copyright (c) 2002 James McCartney. All rights reserved.
15 http://www.audiosynth.com
17 This program is free software; you can redistribute it and/or modify
18 it under the terms of the GNU General Public License as published by
19 the Free Software Foundation; either version 2 of the License, or
20 (at your option) any later version.
22 This program is distributed in the hope that it will be useful,
23 but WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 GNU General Public License for more details.
27 You should have received a copy of the GNU General Public License
28 along with this program; if not, write to the Free Software
29 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
32 #include "Rendezvous.h"
33 #include "SC_Export.h"
35 SC_DLLEXPORT_C int scprintf(const char *fmt, ...);
37 #ifndef __APPLE__
38 static const char* kSCRendezvousServiceName = "SuperCollider";
40 static const char* SCRendezvousProtocolString(SCRendezvousProtocol proto)
42 switch (proto) {
43 case kSCRendezvous_UDP: return "_osc._udp.";
44 case kSCRendezvous_TCP: return "_osc._tcp.";
45 default: return 0;
48 #endif
50 #if defined(__APPLE__) && !defined(SC_IPHONE)
51 #include <CoreServices/CoreServices.h>
53 void PublishPortToRendezvous(SCRendezvousProtocol protocol, short portNum)
55 scprintf("PublishPortToRendezvous %d %hu\n", protocol, portNum);
56 CFStringRef serviceType = 0;
57 switch (protocol)
59 case kSCRendezvous_UDP:
60 serviceType = CFSTR("_osc._udp.");
61 break;
62 case kSCRendezvous_TCP:
63 serviceType = CFSTR("_osc._tcp.");
64 break;
67 CFNetServiceRef netServiceRef =
68 CFNetServiceCreate(NULL, // use default allocator
69 CFSTR(""), // use default domain
70 serviceType,
71 CFSTR("SuperCollider"),
72 portNum);
73 // DEBUG
74 if (!netServiceRef)
76 scprintf("Couldn't create a Rendezvous net service.\n");
77 return;
80 CFNetServiceRegisterWithOptions(netServiceRef, 0, NULL); // don't care about the error
83 #elif HAVE_AVAHI
84 # include <avahi-client/client.h>
85 # include <avahi-client/publish.h>
86 # include <avahi-common/alternative.h>
87 # include <avahi-common/error.h>
88 # include <avahi-common/malloc.h>
89 # include <avahi-common/thread-watch.h>
90 # include <avahi-common/timeval.h>
91 # include <pthread.h>
92 # include <stdlib.h>
93 # include <stdio.h>
95 struct AvahiEntry
97 AvahiEntry* mNext;
98 SCRendezvousProtocol mProto;
99 short mPort;
100 bool mRegistered;
103 struct AvahiSession
105 AvahiSession();
106 ~AvahiSession();
108 void PublishPort(SCRendezvousProtocol proto, short port);
109 void CreateServices(AvahiClient* client);
110 void CreateServices() { CreateServices(mClient); }
111 void ResetServices();
112 void RenameService();
114 static void client_cb(AvahiClient* client, AvahiClientState state, void* data);
115 static void group_cb(AVAHI_GCC_UNUSED AvahiEntryGroup*, AvahiEntryGroupState state, void* data);
116 static void modify_cb(AVAHI_GCC_UNUSED AvahiTimeout*, void* data);
118 AvahiThreadedPoll* mPoll;
119 AvahiClient* mClient;
120 AvahiEntryGroup* mGroup;
121 AvahiEntry* mEntries;
122 pthread_mutex_t mMutex;
123 char* mServiceName;
126 struct AvahiSessionInstance
128 AvahiSessionInstance()
129 : mSession(0)
132 ~AvahiSessionInstance()
134 if (mSession) {
135 delete mSession;
138 AvahiSession* GetSession()
140 if (!mSession) {
141 mSession = new AvahiSession();
143 return mSession;
145 private:
146 AvahiSession* mSession;
149 static AvahiSessionInstance gAvahiSession;
151 AvahiSession::AvahiSession()
152 : mPoll(0),
153 mClient(0),
154 mGroup(0),
155 mEntries(0),
156 mServiceName(0)
158 int err;
160 pthread_mutex_init(&mMutex, 0);
161 mServiceName = avahi_strdup(kSCRendezvousServiceName);
163 mPoll = avahi_threaded_poll_new();
164 if (!mPoll) {
165 scprintf("Zeroconf: failed to create poll API\n");
166 return;
169 mClient = avahi_client_new(
170 avahi_threaded_poll_get(mPoll),
171 (AvahiClientFlags)0, client_cb, this, &err);
172 if (!mClient) {
173 scprintf("Zeroconf: failed to create client: %s\n", avahi_strerror(err));
174 avahi_threaded_poll_free(mPoll);
175 mPoll = 0;
176 return;
179 avahi_threaded_poll_start(mPoll);
182 AvahiSession::~AvahiSession()
184 if (mClient) {
185 avahi_threaded_poll_stop(mPoll);
186 avahi_client_free(mClient);
187 avahi_threaded_poll_free(mPoll);
188 AvahiEntry* entry = mEntries;
189 while (entry) {
190 AvahiEntry* next = entry->mNext;
191 delete entry;
192 entry = next;
195 free(mServiceName);
196 pthread_mutex_destroy(&mMutex);
199 void AvahiSession::client_cb(AvahiClient* client, AvahiClientState state, void* data)
201 AvahiSession* self = (AvahiSession*)data;
203 switch (state) {
204 case AVAHI_CLIENT_S_RUNNING:
205 self->CreateServices(client);
206 break;
207 case AVAHI_CLIENT_S_COLLISION:
208 self->ResetServices();
209 break;
210 case AVAHI_CLIENT_FAILURE:
211 scprintf("Zeroconf: client failure: %s\n",
212 avahi_strerror(avahi_client_errno(self->mClient)));
213 break;
214 case AVAHI_CLIENT_CONNECTING:
215 case AVAHI_CLIENT_S_REGISTERING:
216 break;
220 void AvahiSession::group_cb(AVAHI_GCC_UNUSED AvahiEntryGroup*, AvahiEntryGroupState state, void* data)
222 AvahiSession* self = (AvahiSession*)data;
224 switch (state) {
225 case AVAHI_ENTRY_GROUP_ESTABLISHED:
226 scprintf("Zeroconf: registered service '%s'\n", self->mServiceName);
227 break;
228 case AVAHI_ENTRY_GROUP_COLLISION: {
229 self->RenameService();
230 self->CreateServices();
231 break;
233 case AVAHI_ENTRY_GROUP_FAILURE:
234 case AVAHI_ENTRY_GROUP_UNCOMMITED:
235 case AVAHI_ENTRY_GROUP_REGISTERING:
236 break;
240 void AvahiSession::modify_cb(AVAHI_GCC_UNUSED AvahiTimeout* e, void* data)
242 AvahiSession* self = (AvahiSession*)data;
243 if (avahi_client_get_state(self->mClient) == AVAHI_CLIENT_S_RUNNING)
244 self->CreateServices();
247 void AvahiSession::PublishPort(SCRendezvousProtocol proto, short port)
249 if (!mClient) return;
251 AvahiEntry* entry = new AvahiEntry;
252 entry->mProto = proto;
253 entry->mPort = port;
254 entry->mRegistered = false;
256 pthread_mutex_lock(&mMutex);
257 entry->mNext = mEntries;
258 mEntries = entry;
259 pthread_mutex_unlock(&mMutex);
261 avahi_threaded_poll_lock(mPoll);
262 struct timeval tv;
263 avahi_threaded_poll_get(mPoll)->timeout_new(
264 avahi_threaded_poll_get(mPoll),
265 avahi_elapse_time(&tv, 0, 0),
266 modify_cb,
267 this);
268 avahi_threaded_poll_unlock(mPoll);
271 void AvahiSession::CreateServices(AvahiClient* client)
273 int err;
275 if (mGroup) {
276 avahi_entry_group_reset(mGroup);
277 } else {
278 mGroup = avahi_entry_group_new(client, group_cb, this);
279 if (!mGroup) {
280 scprintf("Zeroconf: failed to create entry group: %s\n",
281 avahi_strerror(avahi_client_errno(client)));
282 return;
286 pthread_mutex_lock(&mMutex);
287 AvahiEntry* entry = mEntries;
288 while (entry) {
289 const char* type = SCRendezvousProtocolString(entry->mProto);
290 err = avahi_entry_group_add_service(
291 mGroup, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
292 (AvahiPublishFlags)0, mServiceName, type,
293 NULL, NULL, entry->mPort,
294 NULL);
295 if (err == AVAHI_ERR_COLLISION) {
296 // BUG: shouldn't this actually be triggered in the entry
297 // group callback?
298 RenameService();
299 err = avahi_entry_group_add_service(
300 mGroup, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
301 (AvahiPublishFlags)0, mServiceName, type,
302 NULL, NULL, entry->mPort,
303 NULL);
305 if (err < 0) {
306 scprintf("Zeroconf: failed to register service '%s': %s\n",
307 mServiceName, avahi_strerror(err));
309 entry = entry->mNext;
311 pthread_mutex_unlock(&mMutex);
313 if (!avahi_entry_group_is_empty(mGroup)) {
314 err = avahi_entry_group_commit(mGroup);
315 if (err < 0) {
316 scprintf("Zeroconf: failed to commit entry group: %s\n",
317 avahi_strerror(err));
318 return;
323 void AvahiSession::ResetServices()
325 if (mGroup) avahi_entry_group_reset(mGroup);
328 void AvahiSession::RenameService()
330 char* name = avahi_alternative_service_name(mServiceName);
331 avahi_free(mServiceName);
332 mServiceName = name;
335 void PublishPortToRendezvous(SCRendezvousProtocol proto, short port)
337 gAvahiSession.GetSession()->PublishPort(proto, port);
340 #elif HAVE_HOWL
341 # include <howl.h>
342 # include <sys/poll.h>
343 # include <pthread.h>
345 struct HowlSession
347 HowlSession();
348 ~HowlSession();
350 void PublishPort(SCRendezvousProtocol protocol, short portNum);
351 void PublishPort(sw_discovery session, SCRendezvousProtocol protocol, short portNum);
353 sw_discovery mSession;
354 pthread_mutex_t mMutex;
357 struct HowlSessionInstance
359 HowlSessionInstance()
360 : mSession(0)
363 ~HowlSessionInstance()
365 if (mSession) {
366 delete mSession;
369 HowlSession* GetSession()
371 if (!mSession) {
372 mSession = new HowlSession();
374 return mSession;
376 private:
377 HowlSession* mSession;
380 static HowlSessionInstance gHowlSession;
382 HowlSession::HowlSession()
383 : mSession(0)
385 pthread_mutex_init(&mMutex, 0);
388 HowlSession::~HowlSession()
390 if (mSession) sw_discovery_fina(mSession);
391 pthread_mutex_destroy(&mMutex);
394 void HowlSession::PublishPort(SCRendezvousProtocol protocol, short portNum)
396 pthread_mutex_lock(&mMutex);
397 if (!mSession) sw_discovery_init(&mSession);
398 if (mSession) PublishPort(mSession, protocol, portNum);
399 pthread_mutex_unlock(&mMutex);
402 sw_result
403 howl_publish_reply_func(
404 sw_discovery,
405 sw_discovery_oid,
406 sw_discovery_publish_status,
407 sw_opaque
410 return SW_OKAY;
413 void HowlSession::PublishPort(sw_discovery session, SCRendezvousProtocol protocol, short portNum)
415 const char* serviceType = SCRendezvousProtocolString(protocol);
416 sw_discovery_oid oid;
418 sw_result res =
419 sw_discovery_publish(
420 session,
421 0, // interface
422 kSCRendezvousServiceName, // name
423 serviceType, // type
424 0, // domain (.local)
425 0, // host
426 portNum, // port
427 0, 0, // text records
428 howl_publish_reply_func, 0, // reply func
429 &oid // request id
432 scprintf(
433 "Zeroconf: publishing service %s on port %hu %s\n",
434 serviceType, portNum,
435 res == SW_OKAY ? "succeeded" : "failed"
439 void PublishPortToRendezvous(SCRendezvousProtocol protocol, short portNum)
441 gHowlSession.GetSession()->PublishPort(protocol, portNum);
444 #else // !__APPLE__ && !HAVE_AVAHI && !HAVE_HOWL
446 void PublishPortToRendezvous(SCRendezvousProtocol protocol, short portNum)
450 #endif // __APPLE__ || HAVE_AVAHI || HAVE_HOWL