/* * Unix SMB/CIFS implementation. * Register _smb._tcp with avahi * * Copyright (C) Volker Lendecke 2009 * * 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 3 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, see . */ #include "includes.h" #include "smbd/smbd.h" #include #include #include #include #include struct avahi_state_struct { struct AvahiPoll *poll; AvahiClient *client; AvahiEntryGroup *entry_group; uint16_t port; }; static void *avahi_allocator_ctx = NULL; static void * avahi_allocator_malloc(size_t size) { return talloc_size(avahi_allocator_ctx, size); } static void avahi_allocator_free(void *p) { TALLOC_FREE(p); } static void * avahi_allocator_realloc(void *p, size_t size) { return talloc_realloc_size(avahi_allocator_ctx, p, size); } static void * avahi_allocator_calloc(size_t count, size_t size) { void *p = talloc_array_size(avahi_allocator_ctx, size, count); if (p) { memset(p, 0, size * count); } return p; } static const struct AvahiAllocator avahi_talloc_allocator = { &avahi_allocator_malloc, &avahi_allocator_free, &avahi_allocator_realloc, &avahi_allocator_calloc }; static void avahi_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState status, void *userdata) { struct avahi_state_struct *state = talloc_get_type_abort( userdata, struct avahi_state_struct); int error; switch (status) { case AVAHI_ENTRY_GROUP_ESTABLISHED: DBG_DEBUG("AVAHI_ENTRY_GROUP_ESTABLISHED\n"); break; case AVAHI_ENTRY_GROUP_FAILURE: error = avahi_client_errno(state->client); DBG_DEBUG("AVAHI_ENTRY_GROUP_FAILURE: %s\n", avahi_strerror(error)); break; case AVAHI_ENTRY_GROUP_COLLISION: DBG_DEBUG("AVAHI_ENTRY_GROUP_COLLISION\n"); break; case AVAHI_ENTRY_GROUP_UNCOMMITED: DBG_DEBUG("AVAHI_ENTRY_GROUP_UNCOMMITED\n"); break; case AVAHI_ENTRY_GROUP_REGISTERING: DBG_DEBUG("AVAHI_ENTRY_GROUP_REGISTERING\n"); break; } } static void avahi_client_callback(AvahiClient *c, AvahiClientState status, void *userdata) { struct avahi_state_struct *state = talloc_get_type_abort( userdata, struct avahi_state_struct); int error; switch (status) { case AVAHI_CLIENT_S_RUNNING: { int snum; int num_services = lp_numservices(); size_t dk = 0; AvahiStringList *adisk = NULL; AvahiStringList *adisk2 = NULL; AvahiStringList *dinfo = NULL; const char *hostname = NULL; enum mdns_name_values mdns_name = lp_mdns_name(); const char *model = NULL; DBG_DEBUG("AVAHI_CLIENT_S_RUNNING\n"); switch (mdns_name) { case MDNS_NAME_MDNS: hostname = avahi_client_get_host_name(c); break; case MDNS_NAME_NETBIOS: hostname = lp_netbios_name(); break; default: DBG_ERR("Unhandled mdns_name %d\n", mdns_name); return; } state->entry_group = avahi_entry_group_new( c, avahi_entry_group_callback, state); if (state->entry_group == NULL) { error = avahi_client_errno(c); DBG_DEBUG("avahi_entry_group_new failed: %s\n", avahi_strerror(error)); break; } error = avahi_entry_group_add_service( state->entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, hostname, "_smb._tcp", NULL, NULL, state->port, NULL); if (error != AVAHI_OK) { DBG_DEBUG("avahi_entry_group_add_service failed: %s\n", avahi_strerror(error)); avahi_entry_group_free(state->entry_group); state->entry_group = NULL; break; } for (snum = 0; snum < num_services; snum++) { if (lp_snum_ok(snum) && lp_parm_bool(snum, "fruit", "time machine", false)) { adisk2 = avahi_string_list_add_printf( adisk, "dk%zu=adVN=%s,adVF=0x82", dk++, lp_const_servicename(snum)); if (adisk2 == NULL) { DBG_DEBUG("avahi_string_list_add_printf" "failed: returned NULL\n"); avahi_string_list_free(adisk); avahi_entry_group_free(state->entry_group); state->entry_group = NULL; break; } adisk = adisk2; adisk2 = NULL; } } if (dk > 0) { adisk2 = avahi_string_list_add(adisk, "sys=adVF=0x100"); if (adisk2 == NULL) { DBG_DEBUG("avahi_string_list_add failed: " "returned NULL\n"); avahi_string_list_free(adisk); avahi_entry_group_free(state->entry_group); state->entry_group = NULL; break; } adisk = adisk2; adisk2 = NULL; error = avahi_entry_group_add_service_strlst( state->entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, hostname, "_adisk._tcp", NULL, NULL, 0, adisk); avahi_string_list_free(adisk); adisk = NULL; if (error != AVAHI_OK) { DBG_DEBUG("avahi_entry_group_add_service_strlst " "failed: %s\n", avahi_strerror(error)); avahi_entry_group_free(state->entry_group); state->entry_group = NULL; break; } } model = lp_parm_const_string(-1, "fruit", "model", "MacSamba"); dinfo = avahi_string_list_add_printf(NULL, "model=%s", model); if (dinfo == NULL) { DBG_DEBUG("avahi_string_list_add_printf" "failed: returned NULL\n"); avahi_entry_group_free(state->entry_group); state->entry_group = NULL; break; } error = avahi_entry_group_add_service_strlst( state->entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, hostname, "_device-info._tcp", NULL, NULL, 0, dinfo); avahi_string_list_free(dinfo); if (error != AVAHI_OK) { DBG_DEBUG("avahi_entry_group_add_service failed: %s\n", avahi_strerror(error)); avahi_entry_group_free(state->entry_group); state->entry_group = NULL; break; } error = avahi_entry_group_commit(state->entry_group); if (error != AVAHI_OK) { DBG_DEBUG("avahi_entry_group_commit failed: %s\n", avahi_strerror(error)); avahi_entry_group_free(state->entry_group); state->entry_group = NULL; break; } break; } case AVAHI_CLIENT_FAILURE: error = avahi_client_errno(c); DBG_DEBUG("AVAHI_CLIENT_FAILURE: %s\n", avahi_strerror(error)); if (error != AVAHI_ERR_DISCONNECTED) { break; } avahi_client_free(c); state->client = avahi_client_new(state->poll, AVAHI_CLIENT_NO_FAIL, avahi_client_callback, state, &error); if (state->client == NULL) { DBG_DEBUG("avahi_client_new failed: %s\n", avahi_strerror(error)); break; } break; case AVAHI_CLIENT_S_COLLISION: DBG_DEBUG("AVAHI_CLIENT_S_COLLISION\n"); break; case AVAHI_CLIENT_S_REGISTERING: DBG_DEBUG("AVAHI_CLIENT_S_REGISTERING\n"); break; case AVAHI_CLIENT_CONNECTING: DBG_DEBUG("AVAHI_CLIENT_CONNECTING\n"); break; } } void *avahi_start_register(TALLOC_CTX *mem_ctx, struct tevent_context *ev, uint16_t port) { struct avahi_state_struct *state; int error; avahi_allocator_ctx = talloc_new(mem_ctx); if (avahi_allocator_ctx == NULL) { return NULL; } avahi_set_allocator(&avahi_talloc_allocator); state = talloc(mem_ctx, struct avahi_state_struct); if (state == NULL) { return state; } state->port = port; state->poll = tevent_avahi_poll(state, ev); if (state->poll == NULL) { goto fail; } state->client = avahi_client_new(state->poll, AVAHI_CLIENT_NO_FAIL, avahi_client_callback, state, &error); if (state->client == NULL) { DBG_DEBUG("avahi_client_new failed: %s\n", avahi_strerror(error)); goto fail; } return state; fail: TALLOC_FREE(state); return NULL; }