sbin_PROGRAMS = afpd
afpd_SOURCES = \
- afp_avahi.c \
afp_config.c \
afp_dsi.c \
- afp_mdns.c \
afp_options.c \
afp_util.c \
- afp_zeroconf.c \
afprun.c \
afs.c \
appl.c \
afpd_LDADD = \
$(top_builddir)/libatalk/libatalk.la \
- @LIBGCRYPT_LIBS@ @QUOTA_LIBS@ @WRAP_LIBS@ @LIBADD_DL@ @ACL_LIBS@ @ZEROCONF_LIBS@ @PTHREAD_LIBS@ @GSSAPI_LIBS@ @KRB5_LIBS@ @MYSQL_LIBS@
+ @LIBGCRYPT_LIBS@ @QUOTA_LIBS@ @WRAP_LIBS@ @LIBADD_DL@ @ACL_LIBS@ @PTHREAD_LIBS@ @GSSAPI_LIBS@ @KRB5_LIBS@ @MYSQL_LIBS@
afpd_LDFLAGS = -export-dynamic
afpd_CFLAGS = \
- @ZEROCONF_CFLAGS@ @GSSAPI_CFLAGS@ @KRB5_CFLAGS@ @PTHREAD_CFLAGS@\
+ @GSSAPI_CFLAGS@ @KRB5_CFLAGS@ @PTHREAD_CFLAGS@\
-DAPPLCNAME \
-DSERVERTEXT=\"$(SERVERTEXT)/\" \
-D_PATH_AFPDPWFILE=\"$(pkgconfdir)/afppasswd\" \
noinst_HEADERS = auth.h afp_config.h desktop.h directory.h fce_api_internal.h file.h \
filedir.h fork.h icon.h mangle.h misc.h status.h switch.h \
uam_auth.h uid.h unix.h volume.h hash.h acls.h acl_mappings.h extattrs.h \
- dircache.h afp_zeroconf.h afp_avahi.h afp_mdns.h afpstats.h afpstats_obj.h
+ dircache.h afpstats_obj.h
+++ /dev/null
-/*
- * Author: Daniel S. Haischt <me@daniel.stefan.haischt.name>
- * Purpose: Avahi based Zeroconf support
- * Docs: http://avahi.org/download/doxygen/
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#ifdef HAVE_AVAHI
-
-#include <unistd.h>
-#include <time.h>
-
-#include <avahi-common/strlst.h>
-
-#include <atalk/logger.h>
-#include <atalk/util.h>
-#include <atalk/dsi.h>
-#include <atalk/unicode.h>
-#include <atalk/netatalk_conf.h>
-
-#include "afp_zeroconf.h"
-#include "afp_avahi.h"
-
-/*****************************************************************
- * Global variables
- *****************************************************************/
-struct context *ctx = NULL;
-
-/*****************************************************************
- * Private functions
- *****************************************************************/
-
-static void publish_reply(AvahiEntryGroup *g,
- AvahiEntryGroupState state,
- void *userdata);
-
-/*
- * This function tries to register the AFP DNS
- * SRV service type.
- */
-static void register_stuff(void) {
- uint port;
- const struct vol *volume;
- DSI *dsi;
- char name[MAXINSTANCENAMELEN+1];
- AvahiStringList *strlist = NULL;
- AvahiStringList *strlist2 = NULL;
- char tmpname[256];
-
- assert(ctx->client);
-
- if (!ctx->group) {
- if (!(ctx->group = avahi_entry_group_new(ctx->client, publish_reply, ctx))) {
- LOG(log_error, logtype_afpd, "Failed to create entry group: %s",
- avahi_strerror(avahi_client_errno(ctx->client)));
- goto fail;
- }
- }
-
- if (avahi_entry_group_is_empty(ctx->group)) {
- /* Register our service */
-
- /* Build AFP volumes list */
- int i = 0;
- strlist = avahi_string_list_add_printf(strlist, "sys=waMa=0,adVF=0x100");
-
- for (volume = getvolumes(); volume; volume = volume->v_next) {
-
- if (convert_string(CH_UCS2, CH_UTF8_MAC, volume->v_u8mname, -1, tmpname, 255) <= 0) {
- LOG ( log_error, logtype_afpd, "Could not set Zeroconf volume name for TimeMachine");
- goto fail;
- }
-
- if (volume->v_flags & AFPVOL_TM) {
- if (volume->v_uuid) {
- LOG(log_info, logtype_afpd, "Registering volume '%s' with UUID: '%s' for TimeMachine",
- volume->v_localname, volume->v_uuid);
- strlist = avahi_string_list_add_printf(strlist, "dk%u=adVN=%s,adVF=0xa1,adVU=%s",
- i++, tmpname, volume->v_uuid);
- } else {
- LOG(log_warning, logtype_afpd, "Registering volume '%s' for TimeMachine. But UUID is invalid.",
- volume->v_localname);
- strlist = avahi_string_list_add_printf(strlist, "dk%u=adVN=%s,adVF=0xa1",
- i++, tmpname);
- }
- }
- }
-
- /* AFP server */
- for (dsi = ctx->obj->dsi; dsi; dsi = dsi->next) {
- port = getip_port((struct sockaddr *)&dsi->server);
-
- LOG(log_info, logtype_afpd, "hostname: %s", ctx->obj->options.hostname);
-
- if (convert_string(ctx->obj->options.unixcharset,
- CH_UTF8,
- ctx->obj->options.hostname,
- -1,
- name,
- MAXINSTANCENAMELEN) <= 0) {
- LOG(log_error, logtype_afpd, "Could not set Zeroconf instance name: %s", ctx->obj->options.hostname);
- goto fail;
- }
- if ((dsi->bonjourname = strdup(name)) == NULL) {
- LOG(log_error, logtype_afpd, "Could not set Zeroconf instance name");
- goto fail;
-
- }
- LOG(log_info, logtype_afpd, "Registering server '%s' with Bonjour",
- dsi->bonjourname);
-
- if (avahi_entry_group_add_service(ctx->group,
- AVAHI_IF_UNSPEC,
- AVAHI_PROTO_UNSPEC,
- 0,
- dsi->bonjourname,
- AFP_DNS_SERVICE_TYPE,
- NULL,
- NULL,
- port,
- NULL) < 0) {
- LOG(log_error, logtype_afpd, "Failed to add service: %s",
- avahi_strerror(avahi_client_errno(ctx->client)));
- goto fail;
- }
-
- if (i && avahi_entry_group_add_service_strlst(ctx->group,
- AVAHI_IF_UNSPEC,
- AVAHI_PROTO_UNSPEC,
- 0,
- dsi->bonjourname,
- ADISK_SERVICE_TYPE,
- NULL,
- NULL,
- 9, /* discard */
- strlist) < 0) {
- LOG(log_error, logtype_afpd, "Failed to add service: %s",
- avahi_strerror(avahi_client_errno(ctx->client)));
- goto fail;
- } /* if */
-
- if (ctx->obj->options.mimicmodel) {
- strlist2 = avahi_string_list_add_printf(strlist2, "model=%s", ctx->obj->options.mimicmodel);
- if (avahi_entry_group_add_service_strlst(ctx->group,
- AVAHI_IF_UNSPEC,
- AVAHI_PROTO_UNSPEC,
- 0,
- dsi->bonjourname,
- DEV_INFO_SERVICE_TYPE,
- NULL,
- NULL,
- 0,
- strlist2) < 0) {
- LOG(log_error, logtype_afpd, "Failed to add service: %s",
- avahi_strerror(avahi_client_errno(ctx->client)));
- goto fail;
- }
- } /* if (config->obj.options.mimicmodel) */
-
- } /* for config*/
-
- if (avahi_entry_group_commit(ctx->group) < 0) {
- LOG(log_error, logtype_afpd, "Failed to commit entry group: %s",
- avahi_strerror(avahi_client_errno(ctx->client)));
- goto fail;
- }
-
- } /* if avahi_entry_group_is_empty*/
-
- return;
-
-fail:
- time(NULL);
-// avahi_threaded_poll_quit(ctx->threaded_poll);
-}
-
-/* Called when publishing of service data completes */
-static void publish_reply(AvahiEntryGroup *g,
- AvahiEntryGroupState state,
- AVAHI_GCC_UNUSED void *userdata)
-{
- assert(ctx->group == NULL || g == ctx->group);
-
- switch (state) {
-
- case AVAHI_ENTRY_GROUP_ESTABLISHED :
- /* The entry group has been established successfully */
- LOG(log_debug, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_ESTABLISHED");
- break;
-
- case AVAHI_ENTRY_GROUP_COLLISION:
- /* With multiple names there's no way to know which one collided */
- LOG(log_error, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_COLLISION",
- avahi_strerror(avahi_client_errno(ctx->client)));
- avahi_threaded_poll_quit(ctx->threaded_poll);
- break;
-
- case AVAHI_ENTRY_GROUP_FAILURE:
- LOG(log_error, logtype_afpd, "Failed to register service: %s",
- avahi_strerror(avahi_client_errno(ctx->client)));
- avahi_threaded_poll_quit(ctx->threaded_poll);
- break;
-
- case AVAHI_ENTRY_GROUP_UNCOMMITED:
- break;
- case AVAHI_ENTRY_GROUP_REGISTERING:
- break;
- }
-}
-
-static void client_callback(AvahiClient *client,
- AvahiClientState state,
- void *userdata)
-{
- ctx->client = client;
-
- switch (state) {
- case AVAHI_CLIENT_S_RUNNING:
- /* The server has startup successfully and registered its host
- * name on the network, so it's time to create our services */
- if (!ctx->group)
- register_stuff();
- break;
-
- case AVAHI_CLIENT_S_COLLISION:
- if (ctx->group)
- avahi_entry_group_reset(ctx->group);
- break;
-
- case AVAHI_CLIENT_FAILURE: {
- if (avahi_client_errno(client) == AVAHI_ERR_DISCONNECTED) {
- int error;
-
- avahi_client_free(ctx->client);
- ctx->client = NULL;
- ctx->group = NULL;
-
- /* Reconnect to the server */
- if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
- AVAHI_CLIENT_NO_FAIL,
- client_callback,
- ctx,
- &error))) {
-
- LOG(log_error, logtype_afpd, "Failed to contact server: %s",
- avahi_strerror(error));
-
- avahi_threaded_poll_quit(ctx->threaded_poll);
- }
-
- } else {
- LOG(log_error, logtype_afpd, "Client failure: %s",
- avahi_strerror(avahi_client_errno(client)));
- avahi_threaded_poll_quit(ctx->threaded_poll);
- }
- break;
- }
-
- case AVAHI_CLIENT_S_REGISTERING:
- break;
- case AVAHI_CLIENT_CONNECTING:
- break;
- }
-}
-
-/************************************************************************
- * Public funcions
- ************************************************************************/
-
-/*
- * Tries to setup the Zeroconf thread and any
- * neccessary config setting.
- */
-void av_zeroconf_register(const AFPObj *obj) {
- int error;
-
- /* initialize the struct that holds our config settings. */
- if (ctx) {
- LOG(log_debug, logtype_afpd, "Resetting zeroconf records");
- avahi_entry_group_reset(ctx->group);
- } else {
- ctx = calloc(1, sizeof(struct context));
- ctx->obj = obj;
- assert(ctx);
- }
-
-/* first of all we need to initialize our threading env */
- if (!(ctx->threaded_poll = avahi_threaded_poll_new())) {
- goto fail;
- }
-
-/* now we need to acquire a client */
- if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
- AVAHI_CLIENT_NO_FAIL,
- client_callback,
- NULL,
- &error))) {
- LOG(log_error, logtype_afpd, "Failed to create client object: %s",
- avahi_strerror(error));
- goto fail;
- }
-
- if (avahi_threaded_poll_start(ctx->threaded_poll) < 0) {
- LOG(log_error, logtype_afpd, "Failed to create thread: %s",
- avahi_strerror(avahi_client_errno(ctx->client)));
- goto fail;
- } else {
- LOG(log_info, logtype_afpd, "Successfully started avahi loop.");
- }
-
- ctx->thread_running = 1;
- return;
-
-fail:
- av_zeroconf_unregister();
-
- return;
-}
-
-/*
- * Tries to shutdown this loop impl.
- * Call this function from inside this thread.
- */
-int av_zeroconf_unregister() {
- LOG(log_error, logtype_afpd, "av_zeroconf_unregister");
-
- if (ctx) {
- LOG(log_error, logtype_afpd, "av_zeroconf_unregister: avahi_threaded_poll_stop");
- if (ctx->threaded_poll)
- avahi_threaded_poll_stop(ctx->threaded_poll);
- LOG(log_error, logtype_afpd, "av_zeroconf_unregister: avahi_client_free");
- if (ctx->client)
- avahi_client_free(ctx->client);
- LOG(log_error, logtype_afpd, "av_zeroconf_unregister: avahi_threaded_poll_free");
- if (ctx->threaded_poll)
- avahi_threaded_poll_free(ctx->threaded_poll);
- free(ctx);
- ctx = NULL;
- }
- return 0;
-}
-
-#endif /* USE_AVAHI */
-
+++ /dev/null
-/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
-/*
- * Author: Daniel S. Haischt <me@daniel.stefan.haischt.name>
- * Purpose: Avahi based Zeroconf support
- * Docs: http://avahi.org/download/doxygen/
- *
- */
-
-#ifndef AFPD_AVAHI_H
-#define AFPD_AVAHI_H
-
-#include <stdlib.h>
-#include <assert.h>
-#include <string.h>
-
-#include <avahi-client/client.h>
-#include <avahi-client/publish.h>
-#include <avahi-common/alternative.h>
-#include <avahi-common/thread-watch.h>
-#include <avahi-common/malloc.h>
-#include <avahi-common/error.h>
-
-#include <atalk/logger.h>
-
-#include "afp_config.h"
-
-struct context {
- /* Avahi stuff */
- int thread_running;
- AvahiThreadedPoll *threaded_poll;
- AvahiClient *client;
- AvahiEntryGroup *group;
- /* Netatalk stuff */
- const AFPObj *obj;
-};
-
-/* prototype definitions */
-void av_zeroconf_register(const AFPObj *obj);
-int av_zeroconf_unregister(void);
-
-#endif /* AFPD_AVAHI_H */
#include "uam_auth.h"
#include "status.h"
#include "volume.h"
-#include "afp_zeroconf.h"
/*!
* Free and cleanup config and DSI
if (!dsi) {
/* Master afpd reloading config */
auth_unload();
- if (! (obj->options.flags & OPTION_NOZEROCONF)) {
- zeroconf_deregister();
- }
}
unload_volumes(obj);
acl_ldap_readconfig(obj->iniconfig);
#endif /* HAVE_LDAP */
- /* Now register with zeroconf, we also need the volumes for that */
- if (! (obj->options.flags & OPTION_NOZEROCONF)) {
- load_volumes(obj, lv_all);
- zeroconf_register(obj);
- }
-
if ((r = atalk_iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "fce listener", NULL))) {
LOG(log_note, logtype_afpd, "Adding FCE listener: %s", r);
fce_add_udp_socket(r);
+++ /dev/null
-/*
- * Author: Lee Essen <lee.essen@nowonline.co.uk>
- * Based on: avahi support from Daniel S. Haischt <me@daniel.stefan.haischt.name>
- * Purpose: mdns based Zeroconf support
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#ifdef HAVE_MDNS
-
-#include <unistd.h>
-#include <time.h>
-#include <pthread.h>
-#include <poll.h>
-
-#include <atalk/logger.h>
-#include <atalk/util.h>
-#include <atalk/dsi.h>
-#include <atalk/unicode.h>
-#include <atalk/netatalk_conf.h>
-
-#include "afp_zeroconf.h"
-#include "afp_mdns.h"
-
-/*
- * We'll store all the DNSServiceRef's here so that we can
- * deallocate them later
- */
-static DNSServiceRef *svc_refs = NULL;
-static int svc_ref_count = 0;
-static pthread_t poller;
-
-/*
- * Its easier to use asprintf to set the TXT record values
- */
-
-int TXTRecordPrintf(TXTRecordRef * rec, const char * key, const char * fmt, ... )
-{
- int ret = 0;
- char *str;
- va_list ap;
- va_start( ap, fmt );
-
- if( 0 > vasprintf(&str, fmt, ap ) ) {
- va_end(ap);
- return -1;
- }
- va_end(ap);
-
- if( kDNSServiceErr_NoError != TXTRecordSetValue(rec, key, strlen(str), str) ) {
- ret = -1;
- }
-
- free(str);
- return ret;
-}
-
-int TXTRecordKeyPrintf(TXTRecordRef * rec, const char * key_fmt, int key_var, const char * fmt, ...)
-{
- int ret = 0;
- char *key = NULL, *str = NULL;
- va_list ap;
-
- if( 0 > asprintf(&key, key_fmt, key_var))
- return -1;
-
- va_start( ap, fmt );
- if( 0 > vasprintf(&str, fmt, ap )) {
- va_end(ap);
- ret = -1;
- goto exit;
- }
- va_end(ap);
-
- if( kDNSServiceErr_NoError != TXTRecordSetValue(rec, key, strlen(str), str) ) {
- ret = -1;
- goto exit;
- }
-
-exit:
- if (str)
- free(str);
- if (key)
- free(key);
- return ret;
-}
-
-static struct pollfd *fds;
-
-/*
- * This is the thread that polls the filehandles
- */
-static void *polling_thread(void *arg) {
- // First we loop through getting the filehandles and adding them to our poll, we
- // need to allocate our pollfd's
- DNSServiceErrorType error;
- fds = calloc(svc_ref_count, sizeof(struct pollfd));
- assert(fds);
-
- for(int i=0; i < svc_ref_count; i++) {
- int fd = DNSServiceRefSockFD(svc_refs[i]);
- fds[i].fd = fd;
- fds[i].events = POLLIN;
- }
-
- // Now we can poll and process the results...
- while(poll(fds, svc_ref_count, -1) > 0) {
- for(int i=0; i < svc_ref_count; i++) {
- if(fds[i].revents & POLLIN) {
- error = DNSServiceProcessResult(svc_refs[i]);
- }
- }
- }
- return(NULL);
-}
-
-/*
- * This is the callback for the service register function ... actually there isn't a lot
- * we can do if we get problems, so we don't really need to do anything other than report
- * the issue.
- */
-static void RegisterReply(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode,
- const char *name, const char *regtype, const char *domain, void *context)
-{
- if (errorCode != kDNSServiceErr_NoError) {
- LOG(log_error, logtype_afpd, "Failed to register mDNS service: %s%s%s: code=%d",
- name, regtype, domain, errorCode);
- }
-}
-
-/*
- * This function unregisters anything we have already
- * registered and frees associated memory
- */
-static void unregister_stuff() {
- pthread_cancel(poller);
-
- for (int i = 0; i < svc_ref_count; i++)
- close(fds[i].fd);
- free(fds);
- fds = NULL;
-
- if(svc_refs) {
- for(int i=0; i < svc_ref_count; i++) {
- DNSServiceRefDeallocate(svc_refs[i]);
- }
- free(svc_refs);
- svc_refs = NULL;
- svc_ref_count = 0;
- }
-}
-
-/*
- * This function tries to register the AFP DNS
- * SRV service type.
- */
-static void register_stuff(const AFPObj *obj) {
- uint port;
- const struct vol *volume;
- DSI *dsi;
- char name[MAXINSTANCENAMELEN+1];
- DNSServiceErrorType error;
- TXTRecordRef txt_adisk;
- TXTRecordRef txt_devinfo;
- char tmpname[256];
-
- // If we had already registered, then we will unregister and re-register
- if(svc_refs) unregister_stuff();
-
- /* Register our service, prepare the TXT record */
- TXTRecordCreate(&txt_adisk, 0, NULL);
- if( 0 > TXTRecordPrintf(&txt_adisk, "sys", "waMa=0,adVF=0x100") ) {
- LOG ( log_error, logtype_afpd, "Could not create Zeroconf TXTRecord for sys");
- goto fail;
- }
-
- /* Build AFP volumes list */
- int i = 0;
-
- for (volume = getvolumes(); volume; volume = volume->v_next) {
-
- if (convert_string(CH_UCS2, CH_UTF8_MAC, volume->v_u8mname, -1, tmpname, 255) <= 0) {
- LOG ( log_error, logtype_afpd, "Could not set Zeroconf volume name for TimeMachine");
- goto fail;
- }
-
- if (volume->v_flags & AFPVOL_TM) {
- if (volume->v_uuid) {
- LOG(log_info, logtype_afpd, "Registering volume '%s' with UUID: '%s' for TimeMachine",
- volume->v_localname, volume->v_uuid);
- if( 0 > TXTRecordKeyPrintf(&txt_adisk, "dk%u", i++, "adVN=%s,adVF=0xa1,adVU=%s",
- tmpname, volume->v_uuid) ) {
- LOG ( log_error, logtype_afpd, "Could not set Zeroconf TXTRecord for dk%u", i);
- goto fail;
- }
- } else {
- LOG(log_warning, logtype_afpd, "Registering volume '%s' for TimeMachine. But UUID is invalid.",
- volume->v_localname);
- if( 0 > TXTRecordKeyPrintf(&txt_adisk, "dk%u", i++, "adVN=%s,adVF=0xa1", tmpname) ) {
- LOG ( log_error, logtype_afpd, "Could not set Zeroconf TXTRecord for dk%u", i);
- goto fail;
- }
- }
- }
- }
-
- // Now we can count the configs so we know how many service
- // records to allocate
- for (dsi = obj->dsi; dsi; dsi = dsi->next) {
- svc_ref_count++; // AFP_DNS_SERVICE_TYPE
- if (i) svc_ref_count++; // ADISK_SERVICE_TYPE
- if (obj->options.mimicmodel) svc_ref_count++; // DEV_INFO_SERVICE_TYPE
- }
-
- // Allocate the memory to store our service refs
- svc_refs = calloc(svc_ref_count, sizeof(DNSServiceRef));
- assert(svc_refs);
- svc_ref_count = 0;
-
- /* AFP server */
- for (dsi = obj->dsi; dsi; dsi = dsi->next) {
-
- port = getip_port((struct sockaddr *)&dsi->server);
-
- if (convert_string(obj->options.unixcharset,
- CH_UTF8,
- obj->options.hostname,
- -1,
- name,
- MAXINSTANCENAMELEN) <= 0) {
- LOG(log_error, logtype_afpd, "Could not set Zeroconf instance name");
- goto fail;
- }
- if ((dsi->bonjourname = strdup(name)) == NULL) {
- LOG(log_error, logtype_afpd, "Could not set Zeroconf instance name");
- goto fail;
-
- }
- LOG(log_info, logtype_afpd, "Registering server '%s' with Bonjour",
- dsi->bonjourname);
-
- error = DNSServiceRegister(&svc_refs[svc_ref_count++],
- 0, // no flags
- 0, // all network interfaces
- dsi->bonjourname,
- AFP_DNS_SERVICE_TYPE,
- "", // default domains
- NULL, // default host name
- htons(port),
- 0, // length of TXT
- NULL, // no TXT
- RegisterReply, // callback
- NULL); // no context
- if(error != kDNSServiceErr_NoError) {
- LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
- AFP_DNS_SERVICE_TYPE, error);
- goto fail;
- }
-
- if(i) {
- error = DNSServiceRegister(&svc_refs[svc_ref_count++],
- 0, // no flags
- 0, // all network interfaces
- dsi->bonjourname,
- ADISK_SERVICE_TYPE,
- "", // default domains
- NULL, // default host name
- htons(port),
- TXTRecordGetLength(&txt_adisk),
- TXTRecordGetBytesPtr(&txt_adisk),
- RegisterReply, // callback
- NULL); // no context
- if(error != kDNSServiceErr_NoError) {
- LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
- ADISK_SERVICE_TYPE, error);
- goto fail;
- }
- }
-
- if (obj->options.mimicmodel) {
- LOG(log_info, logtype_afpd, "Registering server '%s' with model '%s'",
- dsi->bonjourname, obj->options.mimicmodel);
- TXTRecordCreate(&txt_devinfo, 0, NULL);
- if( 0 > TXTRecordPrintf(&txt_devinfo, "model", obj->options.mimicmodel) ) {
- LOG ( log_error, logtype_afpd, "Could not create Zeroconf TXTRecord for model");
- goto fail;
- }
-
- error = DNSServiceRegister(&svc_refs[svc_ref_count++],
- 0, // no flags
- 0, // all network interfaces
- dsi->bonjourname,
- DEV_INFO_SERVICE_TYPE,
- "", // default domains
- NULL, // default host name
- /*
- * We would probably use port 0 zero, but we can't, from man DNSServiceRegister:
- * "A value of 0 for a port is passed to register placeholder services.
- * Place holder services are not found when browsing, but other
- * clients cannot register with the same name as the placeholder service."
- * We therefor use port 9 which is used by the adisk service type.
- */
- htons(9),
- TXTRecordGetLength(&txt_devinfo),
- TXTRecordGetBytesPtr(&txt_devinfo),
- RegisterReply, // callback
- NULL); // no context
- TXTRecordDeallocate(&txt_devinfo);
- if(error != kDNSServiceErr_NoError) {
- LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
- DEV_INFO_SERVICE_TYPE, error);
- goto fail;
- }
- } /* if (config->obj.options.mimicmodel) */
- } /* for config*/
-
- /*
- * Now we can create the thread that will poll for the results
- * and handle the calling of the callbacks
- */
- if(pthread_create(&poller, NULL, polling_thread, NULL) != 0) {
- LOG(log_error, logtype_afpd, "Unable to start mDNS polling thread");
- goto fail;
- }
-
-fail:
- TXTRecordDeallocate(&txt_adisk);
- return;
-}
-
-/************************************************************************
- * Public funcions
- ************************************************************************/
-
-/*
- * Tries to setup the Zeroconf thread and any
- * neccessary config setting.
- */
-void md_zeroconf_register(const AFPObj *obj) {
- int error;
-
- register_stuff(obj);
- return;
-}
-
-/*
- * Tries to shutdown this loop impl.
- * Call this function from inside this thread.
- */
-int md_zeroconf_unregister() {
- unregister_stuff();
- return 0;
-}
-
-#endif /* USE_MDNS */
-
+++ /dev/null
-/*
- * Author: Lee Essen <lee.essen@nowonline.co.uk>
- * Based on: avahi support from Daniel S. Haischt <me@daniel.stefan.haischt.name>
- * Purpose: mdns based Zeroconf support
- */
-
-#ifndef AFPD_MDNS_H
-#define AFPD_MDNS_H
-
-#include <stdlib.h>
-#include <assert.h>
-#include <string.h>
-
-#include <dns_sd.h>
-
-#include <atalk/logger.h>
-
-#include "afp_config.h"
-
-/* prototype definitions */
-void md_zeroconf_register(const AFPObj *obj);
-int md_zeroconf_unregister(void);
-
-#endif /* AFPD_MDNS_H */
+++ /dev/null
-/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
-/*
- * Author: Daniel S. Haischt <me@daniel.stefan.haischt.name>
- * Purpose: Zeroconf facade, that abstracts access to a
- * particular Zeroconf implementation
- * Doc: http://www.dns-sd.org/
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "afp_zeroconf.h"
-#include "afp_config.h"
-
-#ifdef HAVE_MDNS
-#include "afp_mdns.h"
-#elif defined (HAVE_AVAHI)
-#include "afp_avahi.h"
-#endif
-
-
-/*
- * Functions (actually they are just facades)
- */
-void zeroconf_register(const AFPObj *configs)
-{
-#if defined (HAVE_MDNS)
- LOG(log_debug, logtype_afpd, "Attempting to register with mDNS using mDNSResponder");
-
- md_zeroconf_register(configs);
-#elif defined (HAVE_AVAHI)
- LOG(log_debug, logtype_afpd, "Attempting to register with mDNS using Avahi");
-
- av_zeroconf_register(configs);
-#endif
-}
-
-void zeroconf_deregister(void)
-{
-#if defined (HAVE_MDNS)
- LOG(log_debug, logtype_afpd, "Attempting to de-register mDNS using mDNSResponder");
- md_zeroconf_unregister();
-#elif defined (HAVE_AVAHI)
- LOG(log_debug, logtype_afpd, "Attempting to de-register mDNS using Avahi");
- av_zeroconf_unregister();
-#endif
-}
+++ /dev/null
-/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
-/*
- * Author: Daniel S. Haischt <me@daniel.stefan.haischt.name>
- * Purpose: Zeroconf facade, that abstracts access to a
- * particular Zeroconf implementation
- * Doc: http://www.dns-sd.org/
- *
- */
-
-#ifndef AFPD_ZEROCONF_H
-#define AFPD_ZEROCONF_H
-
-#include "afp_config.h"
-
-#define AFP_DNS_SERVICE_TYPE "_afpovertcp._tcp"
-#define ADISK_SERVICE_TYPE "_adisk._tcp"
-#define DEV_INFO_SERVICE_TYPE "_device-info._tcp"
-
-#define MAXINSTANCENAMELEN 63
-
-/*
- * Prototype Definitions
- */
-
-/*
- * registers service with a particular Zerconf implemenation.
- */
-void zeroconf_register(const AFPObj *obj);
-
-/*
- * de-registers the ntpd service with a particular Zerconf implemenation.
- */
-void zeroconf_deregister(void);
-
-#endif /* AFPD_ZEROCONF_H */
#include "status.h"
#include "fork.h"
#include "uam_auth.h"
-#include "afp_zeroconf.h"
#include "afpstats.h"
#define ASEV_THRESHHOLD 10
sbin_PROGRAMS = netatalk
-netatalk_SOURCES = netatalk.c
+netatalk_SOURCES = netatalk.c afp_avahi.c afp_mdns.c afp_zeroconf.c
netatalk_CFLAGS = \
+ @ZEROCONF_CFLAGS@ \
-D_PATH_CONFDIR=\"$(pkgconfdir)/\" \
-D_PATH_STATEDIR='"$(localstatedir)/netatalk/"' \
-D_PATH_AFPD=\"$(sbindir)/afpd\" \
netatalk_LDADD = \
@MYSQL_LIBS@ \
+ @ZEROCONF_LIBS@ \
$(top_builddir)/libatalk/libatalk.la
netatalk_LDFLAGS =
netatalk_CFLAGS += @LIBEVENT_CFLAGS@
netatalk_LDFLAGS += @LIBEVENT_LDFLAGS@ -levent
endif
+
+noinst_HEADERS = afp_zeroconf.h afp_avahi.h afp_mdns.h
--- /dev/null
+/*
+ * Author: Daniel S. Haischt <me@daniel.stefan.haischt.name>
+ * Purpose: Avahi based Zeroconf support
+ * Docs: http://avahi.org/download/doxygen/
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_AVAHI
+
+#include <unistd.h>
+#include <time.h>
+
+#include <avahi-common/strlst.h>
+
+#include <atalk/logger.h>
+#include <atalk/util.h>
+#include <atalk/dsi.h>
+#include <atalk/unicode.h>
+#include <atalk/netatalk_conf.h>
+
+#include "afp_zeroconf.h"
+#include "afp_avahi.h"
+
+/*****************************************************************
+ * Global variables
+ *****************************************************************/
+struct context *ctx = NULL;
+
+/*****************************************************************
+ * Private functions
+ *****************************************************************/
+
+static void publish_reply(AvahiEntryGroup *g,
+ AvahiEntryGroupState state,
+ void *userdata);
+
+/*
+ * This function tries to register the AFP DNS
+ * SRV service type.
+ */
+static void register_stuff(void) {
+ uint port;
+ const struct vol *volume;
+ char name[MAXINSTANCENAMELEN+1];
+ AvahiStringList *strlist = NULL;
+ AvahiStringList *strlist2 = NULL;
+ char tmpname[256];
+
+ assert(ctx->client);
+
+ if (!ctx->group) {
+ if (!(ctx->group = avahi_entry_group_new(ctx->client, publish_reply, ctx))) {
+ LOG(log_error, logtype_afpd, "Failed to create entry group: %s",
+ avahi_strerror(avahi_client_errno(ctx->client)));
+ goto fail;
+ }
+ }
+
+ if (avahi_entry_group_is_empty(ctx->group)) {
+ /* Register our service */
+
+ /* Build AFP volumes list */
+ int i = 0;
+ strlist = avahi_string_list_add_printf(strlist, "sys=waMa=0,adVF=0x100");
+
+ for (volume = getvolumes(); volume; volume = volume->v_next) {
+
+ if (convert_string(CH_UCS2, CH_UTF8_MAC, volume->v_u8mname, -1, tmpname, 255) <= 0) {
+ LOG ( log_error, logtype_afpd, "Could not set Zeroconf volume name for TimeMachine");
+ goto fail;
+ }
+
+ if (volume->v_flags & AFPVOL_TM) {
+ if (volume->v_uuid) {
+ LOG(log_info, logtype_afpd, "Registering volume '%s' with UUID: '%s' for TimeMachine",
+ volume->v_localname, volume->v_uuid);
+ strlist = avahi_string_list_add_printf(strlist, "dk%u=adVN=%s,adVF=0xa1,adVU=%s",
+ i++, tmpname, volume->v_uuid);
+ } else {
+ LOG(log_warning, logtype_afpd, "Registering volume '%s' for TimeMachine. But UUID is invalid.",
+ volume->v_localname);
+ strlist = avahi_string_list_add_printf(strlist, "dk%u=adVN=%s,adVF=0xa1",
+ i++, tmpname);
+ }
+ }
+ }
+
+ port = atoi(ctx->obj->options.port);
+
+ LOG(log_info, logtype_afpd, "hostname: %s", ctx->obj->options.hostname);
+
+ if (convert_string(ctx->obj->options.unixcharset,
+ CH_UTF8,
+ ctx->obj->options.hostname,
+ -1,
+ name,
+ MAXINSTANCENAMELEN) <= 0) {
+ LOG(log_error, logtype_afpd, "Could not set Zeroconf instance name: %s", ctx->obj->options.hostname);
+ goto fail;
+ }
+
+ LOG(log_info, logtype_afpd, "Registering server '%s' with Bonjour", name);
+
+ if (avahi_entry_group_add_service(ctx->group,
+ AVAHI_IF_UNSPEC,
+ AVAHI_PROTO_UNSPEC,
+ 0,
+ name,
+ AFP_DNS_SERVICE_TYPE,
+ NULL,
+ NULL,
+ port,
+ NULL) < 0) {
+ LOG(log_error, logtype_afpd, "Failed to add service: %s",
+ avahi_strerror(avahi_client_errno(ctx->client)));
+ goto fail;
+ }
+
+ if (i && avahi_entry_group_add_service_strlst(ctx->group,
+ AVAHI_IF_UNSPEC,
+ AVAHI_PROTO_UNSPEC,
+ 0,
+ name,
+ ADISK_SERVICE_TYPE,
+ NULL,
+ NULL,
+ 9, /* discard */
+ strlist) < 0) {
+ LOG(log_error, logtype_afpd, "Failed to add service: %s",
+ avahi_strerror(avahi_client_errno(ctx->client)));
+ goto fail;
+ } /* if */
+
+ if (ctx->obj->options.mimicmodel) {
+ strlist2 = avahi_string_list_add_printf(strlist2, "model=%s", ctx->obj->options.mimicmodel);
+ if (avahi_entry_group_add_service_strlst(ctx->group,
+ AVAHI_IF_UNSPEC,
+ AVAHI_PROTO_UNSPEC,
+ 0,
+ name,
+ DEV_INFO_SERVICE_TYPE,
+ NULL,
+ NULL,
+ 0,
+ strlist2) < 0) {
+ LOG(log_error, logtype_afpd, "Failed to add service: %s",
+ avahi_strerror(avahi_client_errno(ctx->client)));
+ goto fail;
+ }
+ } /* if (config->obj.options.mimicmodel) */
+
+ if (avahi_entry_group_commit(ctx->group) < 0) {
+ LOG(log_error, logtype_afpd, "Failed to commit entry group: %s",
+ avahi_strerror(avahi_client_errno(ctx->client)));
+ goto fail;
+ }
+
+ } /* if avahi_entry_group_is_empty*/
+
+ return;
+
+fail:
+ time(NULL);
+// avahi_threaded_poll_quit(ctx->threaded_poll);
+}
+
+/* Called when publishing of service data completes */
+static void publish_reply(AvahiEntryGroup *g,
+ AvahiEntryGroupState state,
+ AVAHI_GCC_UNUSED void *userdata)
+{
+ assert(ctx->group == NULL || g == ctx->group);
+
+ switch (state) {
+
+ case AVAHI_ENTRY_GROUP_ESTABLISHED :
+ /* The entry group has been established successfully */
+ LOG(log_debug, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_ESTABLISHED");
+ break;
+
+ case AVAHI_ENTRY_GROUP_COLLISION:
+ /* With multiple names there's no way to know which one collided */
+ LOG(log_error, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_COLLISION",
+ avahi_strerror(avahi_client_errno(ctx->client)));
+ avahi_threaded_poll_quit(ctx->threaded_poll);
+ break;
+
+ case AVAHI_ENTRY_GROUP_FAILURE:
+ LOG(log_error, logtype_afpd, "Failed to register service: %s",
+ avahi_strerror(avahi_client_errno(ctx->client)));
+ avahi_threaded_poll_quit(ctx->threaded_poll);
+ break;
+
+ case AVAHI_ENTRY_GROUP_UNCOMMITED:
+ break;
+ case AVAHI_ENTRY_GROUP_REGISTERING:
+ break;
+ }
+}
+
+static void client_callback(AvahiClient *client,
+ AvahiClientState state,
+ void *userdata)
+{
+ ctx->client = client;
+
+ switch (state) {
+ case AVAHI_CLIENT_S_RUNNING:
+ /* The server has startup successfully and registered its host
+ * name on the network, so it's time to create our services */
+ if (!ctx->group)
+ register_stuff();
+ break;
+
+ case AVAHI_CLIENT_S_COLLISION:
+ if (ctx->group)
+ avahi_entry_group_reset(ctx->group);
+ break;
+
+ case AVAHI_CLIENT_FAILURE: {
+ if (avahi_client_errno(client) == AVAHI_ERR_DISCONNECTED) {
+ int error;
+
+ avahi_client_free(ctx->client);
+ ctx->client = NULL;
+ ctx->group = NULL;
+
+ /* Reconnect to the server */
+ if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
+ AVAHI_CLIENT_NO_FAIL,
+ client_callback,
+ ctx,
+ &error))) {
+
+ LOG(log_error, logtype_afpd, "Failed to contact server: %s",
+ avahi_strerror(error));
+
+ avahi_threaded_poll_quit(ctx->threaded_poll);
+ }
+
+ } else {
+ LOG(log_error, logtype_afpd, "Client failure: %s",
+ avahi_strerror(avahi_client_errno(client)));
+ avahi_threaded_poll_quit(ctx->threaded_poll);
+ }
+ break;
+ }
+
+ case AVAHI_CLIENT_S_REGISTERING:
+ break;
+ case AVAHI_CLIENT_CONNECTING:
+ break;
+ }
+}
+
+/************************************************************************
+ * Public funcions
+ ************************************************************************/
+
+/*
+ * Tries to setup the Zeroconf thread and any
+ * neccessary config setting.
+ */
+void av_zeroconf_register(const AFPObj *obj) {
+ int error;
+
+ /* initialize the struct that holds our config settings. */
+ if (ctx) {
+ LOG(log_debug, logtype_afpd, "Resetting zeroconf records");
+ avahi_entry_group_reset(ctx->group);
+ } else {
+ ctx = calloc(1, sizeof(struct context));
+ ctx->obj = obj;
+ assert(ctx);
+ }
+
+/* first of all we need to initialize our threading env */
+ if (!(ctx->threaded_poll = avahi_threaded_poll_new())) {
+ goto fail;
+ }
+
+/* now we need to acquire a client */
+ if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
+ AVAHI_CLIENT_NO_FAIL,
+ client_callback,
+ NULL,
+ &error))) {
+ LOG(log_error, logtype_afpd, "Failed to create client object: %s",
+ avahi_strerror(error));
+ goto fail;
+ }
+
+ if (avahi_threaded_poll_start(ctx->threaded_poll) < 0) {
+ LOG(log_error, logtype_afpd, "Failed to create thread: %s",
+ avahi_strerror(avahi_client_errno(ctx->client)));
+ goto fail;
+ } else {
+ LOG(log_info, logtype_afpd, "Successfully started avahi loop.");
+ }
+
+ ctx->thread_running = 1;
+ return;
+
+fail:
+ av_zeroconf_unregister();
+
+ return;
+}
+
+/*
+ * Tries to shutdown this loop impl.
+ * Call this function from inside this thread.
+ */
+int av_zeroconf_unregister() {
+ LOG(log_error, logtype_afpd, "av_zeroconf_unregister");
+
+ if (ctx) {
+ LOG(log_error, logtype_afpd, "av_zeroconf_unregister: avahi_threaded_poll_stop");
+ if (ctx->threaded_poll)
+ avahi_threaded_poll_stop(ctx->threaded_poll);
+ LOG(log_error, logtype_afpd, "av_zeroconf_unregister: avahi_client_free");
+ if (ctx->client)
+ avahi_client_free(ctx->client);
+ LOG(log_error, logtype_afpd, "av_zeroconf_unregister: avahi_threaded_poll_free");
+ if (ctx->threaded_poll)
+ avahi_threaded_poll_free(ctx->threaded_poll);
+ free(ctx);
+ ctx = NULL;
+ }
+ return 0;
+}
+
+#endif /* USE_AVAHI */
+
--- /dev/null
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
+/*
+ * Author: Daniel S. Haischt <me@daniel.stefan.haischt.name>
+ * Purpose: Avahi based Zeroconf support
+ * Docs: http://avahi.org/download/doxygen/
+ *
+ */
+
+#ifndef AFPD_AVAHI_H
+#define AFPD_AVAHI_H
+
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include <avahi-client/client.h>
+#include <avahi-client/publish.h>
+#include <avahi-common/alternative.h>
+#include <avahi-common/thread-watch.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+
+#include <atalk/logger.h>
+
+struct context {
+ /* Avahi stuff */
+ int thread_running;
+ AvahiThreadedPoll *threaded_poll;
+ AvahiClient *client;
+ AvahiEntryGroup *group;
+ /* Netatalk stuff */
+ const AFPObj *obj;
+};
+
+/* prototype definitions */
+void av_zeroconf_register(const AFPObj *obj);
+int av_zeroconf_unregister(void);
+
+#endif /* AFPD_AVAHI_H */
--- /dev/null
+/*
+ * Author: Lee Essen <lee.essen@nowonline.co.uk>
+ * Based on: avahi support from Daniel S. Haischt <me@daniel.stefan.haischt.name>
+ * Purpose: mdns based Zeroconf support
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_MDNS
+
+#include <unistd.h>
+#include <time.h>
+#include <pthread.h>
+#include <poll.h>
+
+#include <atalk/logger.h>
+#include <atalk/util.h>
+#include <atalk/dsi.h>
+#include <atalk/unicode.h>
+#include <atalk/netatalk_conf.h>
+
+#include "afp_zeroconf.h"
+#include "afp_mdns.h"
+
+/*
+ * We'll store all the DNSServiceRef's here so that we can
+ * deallocate them later
+ */
+static DNSServiceRef *svc_refs = NULL;
+static int svc_ref_count = 0;
+static pthread_t poller;
+
+/*
+ * Its easier to use asprintf to set the TXT record values
+ */
+
+int TXTRecordPrintf(TXTRecordRef * rec, const char * key, const char * fmt, ... )
+{
+ int ret = 0;
+ char *str;
+ va_list ap;
+ va_start( ap, fmt );
+
+ if( 0 > vasprintf(&str, fmt, ap ) ) {
+ va_end(ap);
+ return -1;
+ }
+ va_end(ap);
+
+ if( kDNSServiceErr_NoError != TXTRecordSetValue(rec, key, strlen(str), str) ) {
+ ret = -1;
+ }
+
+ free(str);
+ return ret;
+}
+
+int TXTRecordKeyPrintf(TXTRecordRef * rec, const char * key_fmt, int key_var, const char * fmt, ...)
+{
+ int ret = 0;
+ char *key = NULL, *str = NULL;
+ va_list ap;
+
+ if( 0 > asprintf(&key, key_fmt, key_var))
+ return -1;
+
+ va_start( ap, fmt );
+ if( 0 > vasprintf(&str, fmt, ap )) {
+ va_end(ap);
+ ret = -1;
+ goto exit;
+ }
+ va_end(ap);
+
+ if( kDNSServiceErr_NoError != TXTRecordSetValue(rec, key, strlen(str), str) ) {
+ ret = -1;
+ goto exit;
+ }
+
+exit:
+ if (str)
+ free(str);
+ if (key)
+ free(key);
+ return ret;
+}
+
+static struct pollfd *fds;
+
+/*
+ * This is the thread that polls the filehandles
+ */
+static void *polling_thread(void *arg) {
+ // First we loop through getting the filehandles and adding them to our poll, we
+ // need to allocate our pollfd's
+ DNSServiceErrorType error;
+ fds = calloc(svc_ref_count, sizeof(struct pollfd));
+ assert(fds);
+
+ for(int i=0; i < svc_ref_count; i++) {
+ int fd = DNSServiceRefSockFD(svc_refs[i]);
+ fds[i].fd = fd;
+ fds[i].events = POLLIN;
+ }
+
+ // Now we can poll and process the results...
+ while(poll(fds, svc_ref_count, -1) > 0) {
+ for(int i=0; i < svc_ref_count; i++) {
+ if(fds[i].revents & POLLIN) {
+ error = DNSServiceProcessResult(svc_refs[i]);
+ }
+ }
+ }
+ return(NULL);
+}
+
+/*
+ * This is the callback for the service register function ... actually there isn't a lot
+ * we can do if we get problems, so we don't really need to do anything other than report
+ * the issue.
+ */
+static void RegisterReply(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode,
+ const char *name, const char *regtype, const char *domain, void *context)
+{
+ if (errorCode != kDNSServiceErr_NoError) {
+ LOG(log_error, logtype_afpd, "Failed to register mDNS service: %s%s%s: code=%d",
+ name, regtype, domain, errorCode);
+ }
+}
+
+/*
+ * This function unregisters anything we have already
+ * registered and frees associated memory
+ */
+static void unregister_stuff() {
+ pthread_cancel(poller);
+
+ for (int i = 0; i < svc_ref_count; i++)
+ close(fds[i].fd);
+ free(fds);
+ fds = NULL;
+
+ if(svc_refs) {
+ for(int i=0; i < svc_ref_count; i++) {
+ DNSServiceRefDeallocate(svc_refs[i]);
+ }
+ free(svc_refs);
+ svc_refs = NULL;
+ svc_ref_count = 0;
+ }
+}
+
+/*
+ * This function tries to register the AFP DNS
+ * SRV service type.
+ */
+static void register_stuff(const AFPObj *obj) {
+ uint port;
+ const struct vol *volume;
+ DSI *dsi;
+ char name[MAXINSTANCENAMELEN+1];
+ DNSServiceErrorType error;
+ TXTRecordRef txt_adisk;
+ TXTRecordRef txt_devinfo;
+ char tmpname[256];
+
+ // If we had already registered, then we will unregister and re-register
+ if(svc_refs) unregister_stuff();
+
+ /* Register our service, prepare the TXT record */
+ TXTRecordCreate(&txt_adisk, 0, NULL);
+ if( 0 > TXTRecordPrintf(&txt_adisk, "sys", "waMa=0,adVF=0x100") ) {
+ LOG ( log_error, logtype_afpd, "Could not create Zeroconf TXTRecord for sys");
+ goto fail;
+ }
+
+ /* Build AFP volumes list */
+ int i = 0;
+
+ for (volume = getvolumes(); volume; volume = volume->v_next) {
+
+ if (convert_string(CH_UCS2, CH_UTF8_MAC, volume->v_u8mname, -1, tmpname, 255) <= 0) {
+ LOG ( log_error, logtype_afpd, "Could not set Zeroconf volume name for TimeMachine");
+ goto fail;
+ }
+
+ if (volume->v_flags & AFPVOL_TM) {
+ if (volume->v_uuid) {
+ LOG(log_info, logtype_afpd, "Registering volume '%s' with UUID: '%s' for TimeMachine",
+ volume->v_localname, volume->v_uuid);
+ if( 0 > TXTRecordKeyPrintf(&txt_adisk, "dk%u", i++, "adVN=%s,adVF=0xa1,adVU=%s",
+ tmpname, volume->v_uuid) ) {
+ LOG ( log_error, logtype_afpd, "Could not set Zeroconf TXTRecord for dk%u", i);
+ goto fail;
+ }
+ } else {
+ LOG(log_warning, logtype_afpd, "Registering volume '%s' for TimeMachine. But UUID is invalid.",
+ volume->v_localname);
+ if( 0 > TXTRecordKeyPrintf(&txt_adisk, "dk%u", i++, "adVN=%s,adVF=0xa1", tmpname) ) {
+ LOG ( log_error, logtype_afpd, "Could not set Zeroconf TXTRecord for dk%u", i);
+ goto fail;
+ }
+ }
+ }
+ }
+
+ /* AFP_DNS_SERVICE_TYPE */
+ svc_ref_count = 1;
+ if (i) {
+ /* ADISK_SERVICE_TYPE */
+ svc_ref_count++;
+ }
+ if (obj->options.mimicmodel) {
+ /* DEV_INFO_SERVICE_TYPE */
+ svc_ref_count++;
+ }
+
+ // Allocate the memory to store our service refs
+ svc_refs = calloc(svc_ref_count, sizeof(DNSServiceRef));
+ assert(svc_refs);
+ svc_ref_count = 0;
+
+ port = atoi(obj->options.port);
+
+ if (convert_string(obj->options.unixcharset,
+ CH_UTF8,
+ obj->options.hostname,
+ -1,
+ name,
+ MAXINSTANCENAMELEN) <= 0) {
+ LOG(log_error, logtype_afpd, "Could not set Zeroconf instance name");
+ goto fail;
+ }
+ LOG(log_info, logtype_afpd, "Registering server '%s' with Bonjour",
+ dsi->bonjourname);
+
+ error = DNSServiceRegister(&svc_refs[svc_ref_count++],
+ 0, // no flags
+ 0, // all network interfaces
+ name,
+ AFP_DNS_SERVICE_TYPE,
+ "", // default domains
+ NULL, // default host name
+ htons(port),
+ 0, // length of TXT
+ NULL, // no TXT
+ RegisterReply, // callback
+ NULL); // no context
+ if (error != kDNSServiceErr_NoError) {
+ LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
+ AFP_DNS_SERVICE_TYPE, error);
+ goto fail;
+ }
+
+ if (i) {
+ error = DNSServiceRegister(&svc_refs[svc_ref_count++],
+ 0, // no flags
+ 0, // all network interfaces
+ name,
+ ADISK_SERVICE_TYPE,
+ "", // default domains
+ NULL, // default host name
+ htons(port),
+ TXTRecordGetLength(&txt_adisk),
+ TXTRecordGetBytesPtr(&txt_adisk),
+ RegisterReply, // callback
+ NULL); // no context
+ if (error != kDNSServiceErr_NoError) {
+ LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
+ ADISK_SERVICE_TYPE, error);
+ goto fail;
+ }
+ }
+
+ if (obj->options.mimicmodel) {
+ LOG(log_info, logtype_afpd, "Registering server '%s' with model '%s'",
+ dsi->bonjourname, obj->options.mimicmodel);
+ TXTRecordCreate(&txt_devinfo, 0, NULL);
+ if ( 0 > TXTRecordPrintf(&txt_devinfo, "model", obj->options.mimicmodel) ) {
+ LOG ( log_error, logtype_afpd, "Could not create Zeroconf TXTRecord for model");
+ goto fail;
+ }
+
+ error = DNSServiceRegister(&svc_refs[svc_ref_count++],
+ 0, // no flags
+ 0, // all network interfaces
+ name,
+ DEV_INFO_SERVICE_TYPE,
+ "", // default domains
+ NULL, // default host name
+ /*
+ * We would probably use port 0 zero, but we can't, from man DNSServiceRegister:
+ * "A value of 0 for a port is passed to register placeholder services.
+ * Place holder services are not found when browsing, but other
+ * clients cannot register with the same name as the placeholder service."
+ * We therefor use port 9 which is used by the adisk service type.
+ */
+ htons(9),
+ TXTRecordGetLength(&txt_devinfo),
+ TXTRecordGetBytesPtr(&txt_devinfo),
+ RegisterReply, // callback
+ NULL); // no context
+ TXTRecordDeallocate(&txt_devinfo);
+ if (error != kDNSServiceErr_NoError) {
+ LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
+ DEV_INFO_SERVICE_TYPE, error);
+ goto fail;
+ }
+ } /* if (config->obj.options.mimicmodel) */
+
+ /*
+ * Now we can create the thread that will poll for the results
+ * and handle the calling of the callbacks
+ */
+ if(pthread_create(&poller, NULL, polling_thread, NULL) != 0) {
+ LOG(log_error, logtype_afpd, "Unable to start mDNS polling thread");
+ goto fail;
+ }
+
+fail:
+ TXTRecordDeallocate(&txt_adisk);
+ return;
+}
+
+/************************************************************************
+ * Public funcions
+ ************************************************************************/
+
+/*
+ * Tries to setup the Zeroconf thread and any
+ * neccessary config setting.
+ */
+void md_zeroconf_register(const AFPObj *obj) {
+ int error;
+
+ register_stuff(obj);
+ return;
+}
+
+/*
+ * Tries to shutdown this loop impl.
+ * Call this function from inside this thread.
+ */
+int md_zeroconf_unregister() {
+ unregister_stuff();
+ return 0;
+}
+
+#endif /* USE_MDNS */
+
--- /dev/null
+/*
+ * Author: Lee Essen <lee.essen@nowonline.co.uk>
+ * Based on: avahi support from Daniel S. Haischt <me@daniel.stefan.haischt.name>
+ * Purpose: mdns based Zeroconf support
+ */
+
+#ifndef AFPD_MDNS_H
+#define AFPD_MDNS_H
+
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include <dns_sd.h>
+
+#include <atalk/logger.h>
+
+/* prototype definitions */
+void md_zeroconf_register(const AFPObj *obj);
+int md_zeroconf_unregister(void);
+
+#endif /* AFPD_MDNS_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
+/*
+ * Author: Daniel S. Haischt <me@daniel.stefan.haischt.name>
+ * Purpose: Zeroconf facade, that abstracts access to a
+ * particular Zeroconf implementation
+ * Doc: http://www.dns-sd.org/
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "afp_zeroconf.h"
+
+#ifdef HAVE_MDNS
+#include "afp_mdns.h"
+#elif defined (HAVE_AVAHI)
+#include "afp_avahi.h"
+#endif
+
+
+/*
+ * Functions (actually they are just facades)
+ */
+void zeroconf_register(const AFPObj *configs)
+{
+#if defined (HAVE_MDNS)
+ LOG(log_debug, logtype_afpd, "Attempting to register with mDNS using mDNSResponder");
+
+ md_zeroconf_register(configs);
+#elif defined (HAVE_AVAHI)
+ LOG(log_debug, logtype_afpd, "Attempting to register with mDNS using Avahi");
+
+ av_zeroconf_register(configs);
+#endif
+}
+
+void zeroconf_deregister(void)
+{
+#if defined (HAVE_MDNS)
+ LOG(log_debug, logtype_afpd, "Attempting to de-register mDNS using mDNSResponder");
+ md_zeroconf_unregister();
+#elif defined (HAVE_AVAHI)
+ LOG(log_debug, logtype_afpd, "Attempting to de-register mDNS using Avahi");
+ av_zeroconf_unregister();
+#endif
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
+/*
+ * Author: Daniel S. Haischt <me@daniel.stefan.haischt.name>
+ * Purpose: Zeroconf facade, that abstracts access to a
+ * particular Zeroconf implementation
+ * Doc: http://www.dns-sd.org/
+ *
+ */
+
+#ifndef AFPD_ZEROCONF_H
+#define AFPD_ZEROCONF_H
+
+#include <atalk/globals.h>
+
+#define AFP_DNS_SERVICE_TYPE "_afpovertcp._tcp"
+#define ADISK_SERVICE_TYPE "_adisk._tcp"
+#define DEV_INFO_SERVICE_TYPE "_device-info._tcp"
+
+#define MAXINSTANCENAMELEN 63
+
+/*
+ * Prototype Definitions
+ */
+
+/*
+ * registers service with a particular Zerconf implemenation.
+ */
+void zeroconf_register(const AFPObj *obj);
+
+/*
+ * de-registers the ntpd service with a particular Zerconf implemenation.
+ */
+void zeroconf_deregister(void);
+
+#endif /* AFPD_ZEROCONF_H */
#include <atalk/netatalk_conf.h>
#include <atalk/bstrlib.h>
#include <atalk/bstradd.h>
+#include "afp_zeroconf.h"
#include <event2/event.h>
}
#endif
+ /* Now register with zeroconf, we also need the volumes for that */
+ if (! (obj.options.flags & OPTION_NOZEROCONF)) {
+ LOG(log_note, logtype_default, "Registering with Zeroconf");
+ zeroconf_register(&obj);
+ LOG(log_note, logtype_default, "done");
+ }
/* run the event loop */
ret = event_base_dispatch(base);