int avahi_netlink_work(AvahiNetlink *nl, int block) { ssize_t bytes; struct msghdr smsg; struct cmsghdr *cmsg; struct ucred *cred; struct iovec iov; struct nlmsghdr *p; char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; assert(nl); iov.iov_base = nl->buffer; iov.iov_len = nl->buffer_length; smsg.msg_name = NULL; smsg.msg_namelen = 0; smsg.msg_iov = &iov; smsg.msg_iovlen = 1; smsg.msg_control = cred_msg; smsg.msg_controllen = sizeof(cred_msg); smsg.msg_flags = (block ? 0 : MSG_DONTWAIT); if ((bytes = recvmsg(nl->fd, &smsg, 0)) < 0) { if (errno == EAGAIN || errno == EINTR) return 0; avahi_log_error(__FILE__": recvmsg() failed: %s", strerror(errno)); return -1; } cmsg = CMSG_FIRSTHDR(&smsg); if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS) { avahi_log_warn("No sender credentials received, ignoring data."); return -1; } cred = (struct ucred*) CMSG_DATA(cmsg); if (cred->pid != 0) return -1; p = (struct nlmsghdr *) nl->buffer; assert(nl->callback); for (; bytes > 0; p = NLMSG_NEXT(p, bytes)) { if (!NLMSG_OK(p, (size_t) bytes)) { avahi_log_warn(__FILE__": packet truncated"); return -1; } nl->callback(nl, p, nl->userdata); } return 0; }
void static_service_load(int in_chroot) { StaticServiceGroup *g, *n; glob_t globbuf; int globret; char **p; for (g = groups; g; g = n) { struct stat st; n = g->groups_next; if (stat(g->filename, &st) < 0) { if (errno == ENOENT) avahi_log_info("Service group file %s vanished, removing services.", g->filename); else avahi_log_warn("Failed to stat() file %s, ignoring: %s", g->filename, strerror(errno)); static_service_group_free(g); } else if (st.st_mtime != g->mtime) { avahi_log_info("Service group file %s changed, reloading.", g->filename); if (static_service_group_load(g) < 0) { avahi_log_warn("Failed to load service group file %s, removing service.", g->filename); static_service_group_free(g); } } } memset(&globbuf, 0, sizeof(globbuf)); if ((globret = glob(in_chroot ? "/services/*.service" : AVAHI_SERVICE_DIR "/*.service", GLOB_ERR, NULL, &globbuf)) != 0) switch (globret) { #ifdef GLOB_NOSPACE case GLOB_NOSPACE: avahi_log_error("Not enough memory to read service directory "AVAHI_SERVICE_DIR"."); break; #endif #ifdef GLOB_NOMATCH case GLOB_NOMATCH: avahi_log_info("No service file found in "AVAHI_SERVICE_DIR"."); break; #endif default: avahi_log_error("Failed to read "AVAHI_SERVICE_DIR"."); break; } else { for (p = globbuf.gl_pathv; *p; p++) load_file(*p); globfree(&globbuf); } }
DBusHandlerResult avahi_dbus_msg_record_browser_impl(DBusConnection *c, DBusMessage *m, void *userdata) { DBusError error; RecordBrowserInfo *i = userdata; assert(c); assert(m); assert(i); dbus_error_init(&error); avahi_log_debug(__FILE__": interface=%s, path=%s, member=%s", dbus_message_get_interface(m), dbus_message_get_path(m), dbus_message_get_member(m)); /* Introspection */ if (dbus_message_is_method_call(m, DBUS_INTERFACE_INTROSPECTABLE, "Introspect")) return avahi_dbus_handle_introspect(c, m, "org.freedesktop.Avahi.RecordBrowser.xml"); /* Access control */ if (strcmp(dbus_message_get_sender(m), i->client->name)) return avahi_dbus_respond_error(c, m, AVAHI_ERR_ACCESS_DENIED, NULL); if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_RECORD_BROWSER, "Free")) { if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) { avahi_log_warn("Error parsing RecordBrowser::Free message"); goto fail; } avahi_dbus_record_browser_free(i); return avahi_dbus_respond_ok(c, m); } if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_RECORD_BROWSER, "Start")) { if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) { avahi_log_warn("Error parsing RecordBrowser::Start message"); goto fail; } avahi_dbus_record_browser_start(i); return avahi_dbus_respond_ok(c, m); } avahi_log_warn("Missed message %s::%s()", dbus_message_get_interface(m), dbus_message_get_member(m)); fail: if (dbus_error_is_set(&error)) dbus_error_free(&error); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; }
int avahi_mdns_mcast_join_ipv4(int fd, const AvahiIPv4Address *a, int idx, int join) { #ifdef HAVE_STRUCT_IP_MREQN struct ip_mreqn mreq; #else struct ip_mreq mreq; #endif struct sockaddr_in sa; assert(fd >= 0); assert(idx >= 0); assert(a); memset(&mreq, 0, sizeof(mreq)); #ifdef HAVE_STRUCT_IP_MREQN mreq.imr_ifindex = idx; mreq.imr_address.s_addr = a->address; #else mreq.imr_interface.s_addr = a->address; #endif mdns_mcast_group_ipv4(&sa); mreq.imr_multiaddr = sa.sin_addr; /* Some network drivers have issues with dropping membership of * mcast groups when the iface is down, but don't allow rejoining * when it comes back up. This is an ugly workaround */ if (join) setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); if (setsockopt(fd, IPPROTO_IP, join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { avahi_log_warn("%s failed: %s", join ? "IP_ADD_MEMBERSHIP" : "IP_DROP_MEMBERSHIP", strerror(errno)); return -1; } return 0; }
int avahi_mcast_join_ipv6(int fd, const AvahiIPv6Address *a, int idx, int join, AvahiPublishProtocol proto) { struct ipv6_mreq mreq6; struct sockaddr_in6 sa6; assert(fd >= 0); assert(idx >= 0); assert(a); memset(&mreq6, 0, sizeof(mreq6)); /* send proto for 'sa6' to join appropiate multicast group */ mcast_group_ipv6 (&sa6, proto); mreq6.ipv6mr_multiaddr = sa6.sin6_addr; mreq6.ipv6mr_interface = idx; if (join) setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)); if (setsockopt(fd, IPPROTO_IPV6, join ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) { avahi_log_warn("%s failed: %s", join ? "IPV6_ADD_MEMBERSHIP" : "IPV6_DROP_MEMBERSHIP", strerror(errno)); return -1; } return 0; }
int avahi_netlink_work(AvahiNetlink *nl, int block) { ssize_t bytes; struct nlmsghdr *p; assert(nl); if ((bytes = recv(nl->fd, nl->buffer, nl->buffer_length, block ? 0 : MSG_DONTWAIT)) < 0) { if (errno == EAGAIN || errno == EINTR) return 0; avahi_log_error(__FILE__": recv() failed: %s", strerror(errno)); return -1; } p = (struct nlmsghdr *) nl->buffer; assert(nl->callback); for (; bytes > 0; p = NLMSG_NEXT(p, bytes)) { if (!NLMSG_OK(p, (size_t) bytes)) { avahi_log_warn(__FILE__": packet truncated"); return -1; } nl->callback(nl, p, nl->userdata); } return 0; }
static void set_one_rlimit(int resource, rlim_t limit, const char *name) { struct rlimit rl; rl.rlim_cur = rl.rlim_max = limit; if (setrlimit(resource, &rl) < 0) avahi_log_warn("setrlimit(%s, {%u, %u}) failed: %s", name, (unsigned) limit, (unsigned) limit, strerror(errno)); }
static void sender_timeout_callback(AvahiTimeEvent *e, void *userdata) { AvahiWideAreaLookup *l = userdata; struct timeval tv; assert(l); /* Try another DNS server after three retries */ if (l->n_send >= 3 && avahi_address_cmp(&l->engine->dns_servers[l->engine->current_dns_server], &l->dns_server_used) == 0) { next_dns_server(l->engine); if (avahi_address_cmp(&l->engine->dns_servers[l->engine->current_dns_server], &l->dns_server_used) == 0) /* There is no other DNS server, fail */ l->n_send = 1000; } if (l->n_send >= 6) { avahi_log_warn(__FILE__": Query timed out."); avahi_server_set_errno(l->engine->server, AVAHI_ERR_TIMEOUT); l->callback(l->engine, AVAHI_BROWSER_FAILURE, AVAHI_LOOKUP_RESULT_WIDE_AREA, NULL, l->userdata); lookup_stop(l); return; } assert(l->packet); send_to_dns_server(l, l->packet); l->n_send++; avahi_time_event_update(e, avahi_elapse_time(&tv, 1000, 0)); }
static AvahiSEntryGroup* add_dns_servers(AvahiServer *s, AvahiSEntryGroup* g, char **l) { char **p; assert(s); assert(l); if (!g) g = avahi_s_entry_group_new(s, NULL, NULL); assert(avahi_s_entry_group_is_empty(g)); for (p = l; *p; p++) { AvahiAddress a; if (!avahi_address_parse(*p, AVAHI_PROTO_UNSPEC, &a)) avahi_log_warn("Failed to parse address '%s', ignoring.", *p); else if (avahi_server_add_dns_server_address(s, g, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, NULL, AVAHI_DNS_SERVER_RESOLVE, &a, 53) < 0) { avahi_s_entry_group_free(g); avahi_log_error("Failed to add DNS server address: %s", avahi_strerror(avahi_server_errno(s))); return NULL; } } avahi_s_entry_group_commit(g); return g; }
static void client_work(AvahiWatch *watch, AVAHI_GCC_UNUSED int fd, AvahiWatchEvent events, void *userdata) { Client *c = userdata; assert(c); if ((events & AVAHI_WATCH_IN) && c->inbuf_length < sizeof(c->inbuf)) { ssize_t r; if ((r = read(c->fd, c->inbuf + c->inbuf_length, sizeof(c->inbuf) - c->inbuf_length)) <= 0) { if (r < 0) avahi_log_warn("read(): %s", strerror(errno)); client_free(c); return; } c->inbuf_length += r; assert(c->inbuf_length <= sizeof(c->inbuf)); handle_input(c); } if ((events & AVAHI_WATCH_OUT) && c->outbuf_length > 0) { ssize_t r; if ((r = write(c->fd, c->outbuf, c->outbuf_length)) < 0) { avahi_log_warn("write(): %s", strerror(errno)); client_free(c); return; } assert((size_t) r <= c->outbuf_length); c->outbuf_length -= r; if (c->outbuf_length) memmove(c->outbuf, c->outbuf + r, c->outbuf_length - r); if (c->outbuf_length == 0 && c->state == CLIENT_DEAD) { client_free(c); return; } } c->server->poll_api->watch_update( watch, (c->outbuf_length > 0 ? AVAHI_WATCH_OUT : 0) | (c->inbuf_length < sizeof(c->inbuf) ? AVAHI_WATCH_IN : 0)); }
static int ipv4_pktinfo(int fd) { int yes; #ifdef IP_PKTINFO yes = 1; if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) { avahi_log_warn("IP_PKTINFO failed: %s", strerror(errno)); return -1; } #else #ifdef IP_RECVINTERFACE yes = 1; if (setsockopt (fd, IPPROTO_IP, IP_RECVINTERFACE, &yes, sizeof(yes)) < 0) { avahi_log_warn("IP_RECVINTERFACE failed: %s", strerror(errno)); return -1; } #elif defined(IP_RECVIF) yes = 1; if (setsockopt (fd, IPPROTO_IP, IP_RECVIF, &yes, sizeof(yes)) < 0) { avahi_log_warn("IP_RECVIF failed: %s", strerror(errno)); return -1; } #endif #ifdef IP_RECVDSTADDR yes = 1; if (setsockopt (fd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)) < 0) { avahi_log_warn("IP_RECVDSTADDR failed: %s", strerror(errno)); return -1; } #endif #endif /* IP_PKTINFO */ #ifdef IP_RECVTTL yes = 1; if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) { avahi_log_warn("IP_RECVTTL failed: %s", strerror(errno)); return -1; } #endif return 0; }
int avahi_open_unicast_socket_ipv6(void) { struct sockaddr_in6 local; int fd = -1, yes; if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { avahi_log_warn("socket() failed: %s", strerror(errno)); goto fail; } yes = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) { avahi_log_warn("IPV6_V6ONLY failed: %s", strerror(errno)); goto fail; } memset(&local, 0, sizeof(local)); local.sin6_family = AF_INET6; if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) { avahi_log_warn("bind() failed: %s", strerror(errno)); goto fail; } if (ipv6_pktinfo(fd) < 0) goto fail; if (avahi_set_cloexec(fd) < 0) { avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno)); goto fail; } if (avahi_set_nonblock(fd) < 0) { avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno)); goto fail; } return fd; fail: if (fd >= 0) close(fd); return -1; }
static int reuseaddr(int fd) { int yes; yes = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { avahi_log_warn("SO_REUSEADDR failed: %s", strerror(errno)); return -1; } #ifdef SO_REUSEPORT yes = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)) < 0) { avahi_log_warn("SO_REUSEPORT failed: %s", strerror(errno)); return -1; } #endif return 0; }
int avahi_open_unicast_socket_ipv4(void) { struct sockaddr_in local; int fd = -1; if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { avahi_log_warn("socket() failed: %s", strerror(errno)); goto fail; } memset(&local, 0, sizeof(local)); local.sin_family = AF_INET; if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) { avahi_log_warn("bind() failed: %s", strerror(errno)); goto fail; } if (ipv4_pktinfo(fd) < 0) { goto fail; } if (avahi_set_cloexec(fd) < 0) { avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno)); goto fail; } if (avahi_set_nonblock(fd) < 0) { avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno)); goto fail; } return fd; fail: if (fd >= 0) close(fd); return -1; }
static int ipv6_pktinfo(int fd) { int yes; #ifdef IPV6_RECVPKTINFO yes = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &yes, sizeof(yes)) < 0) { avahi_log_warn("IPV6_RECVPKTINFO failed: %s", strerror(errno)); return -1; } #elif defined(IPV6_PKTINFO) yes = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) { avahi_log_warn("IPV6_PKTINFO failed: %s", strerror(errno)); return -1; } #endif #ifdef IPV6_RECVHOPS yes = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPS, &yes, sizeof(yes)) < 0) { avahi_log_warn("IPV6_RECVHOPS failed: %s", strerror(errno)); return -1; } #elif defined(IPV6_RECVHOPLIMIT) yes = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &yes, sizeof(yes)) < 0) { avahi_log_warn("IPV6_RECVHOPLIMIT failed: %s", strerror(errno)); return -1; } #elif defined(IPV6_HOPLIMIT) yes = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) { avahi_log_warn("IPV6_HOPLIMIT failed: %s", strerror(errno)); return -1; } #endif return 0; }
char *avahi_get_host_name(char *ret_s, size_t size) { assert(ret_s); assert(size > 0); if (gethostname(ret_s, size) >= 0) { ret_s[size-1] = 0; strip_bad_chars(ret_s); } else *ret_s = 0; if (strcmp(ret_s, "localhost") == 0 || strncmp(ret_s, "localhost.", 10) == 0) { *ret_s = 0; avahi_log_warn("System host name is set to 'localhost'. This is not a suitable mDNS host name, looking for alternatives."); } if (*ret_s == 0) { /* No hostname was set, so let's take the OS name */ #ifdef __linux__ /* Try LSB distribution name first */ if (load_lsb_distrib_id(ret_s, size) >= 0) { strip_bad_chars(ret_s); avahi_strdown(ret_s); } if (*ret_s == 0) #endif { /* Try uname() second */ struct utsname utsname; if (uname(&utsname) >= 0) { snprintf(ret_s, size, "%s", utsname.sysname); strip_bad_chars(ret_s); avahi_strdown(ret_s); } /* Give up */ if (*ret_s == 0) snprintf(ret_s, size, "unnamed"); } } if (size >= AVAHI_LABEL_MAX) ret_s[AVAHI_LABEL_MAX-1] = 0; return ret_s; }
static int bind_with_warn(int fd, const struct sockaddr *sa, socklen_t l) { assert(fd >= 0); assert(sa); assert(l > 0); if (bind(fd, sa, l) < 0) { if (errno != EADDRINUSE) { avahi_log_warn("bind() failed: %s", strerror(errno)); return -1; } avahi_log_warn("*** WARNING: Detected another %s mDNS stack running on this host. This makes mDNS unreliable and is thus not recommended. ***", sa->sa_family == AF_INET ? "IPv4" : "IPv6"); /* Try again, this time with SO_REUSEADDR set */ if (reuseaddr(fd) < 0) return -1; if (bind(fd, sa, l) < 0) { avahi_log_warn("bind() failed: %s", strerror(errno)); return -1; } } else { /* We enable SO_REUSEADDR afterwards, to make sure that the * user may run other mDNS implementations if he really * wants. */ if (reuseaddr(fd) < 0) return -1; } return 0; }
static void send_response_packet(AvahiResponseScheduler *s, AvahiResponseJob *rj) { AvahiDnsPacket *p; unsigned n; assert(s); assert(rj); if (!(p = avahi_dns_packet_new_response(s->interface->hardware->mtu, 1))) return; /* OOM */ n = 1; /* Put it in the packet. */ if (packet_add_response_job(s, p, rj)) { /* Try to fill up packet with more responses, if available */ while (s->jobs) { if (!packet_add_response_job(s, p, s->jobs)) break; n++; } } else { size_t size; avahi_dns_packet_free(p); /* OK, the packet was too small, so create one that fits */ size = avahi_record_get_estimate_size(rj->record) + AVAHI_DNS_PACKET_HEADER_SIZE; if (!(p = avahi_dns_packet_new_response(size + AVAHI_DNS_PACKET_EXTRA_SIZE, 1))) return; /* OOM */ if (!packet_add_response_job(s, p, rj)) { avahi_dns_packet_free(p); avahi_log_warn("Record too large, cannot send"); job_mark_done(s, rj); return; } } avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n); avahi_interface_send_packet(s->interface, p, AVAHI_MDNS); avahi_dns_packet_free(p); }
static void update_wide_area_servers(void) { AvahiAddress a[AVAHI_WIDE_AREA_SERVERS_MAX]; unsigned n = 0; char **p; if (!resolv_conf_name_servers) { avahi_server_set_wide_area_servers(avahi_server, NULL, 0); return; } for (p = resolv_conf_name_servers; *p && n < AVAHI_WIDE_AREA_SERVERS_MAX; p++) { if (!avahi_address_parse(*p, AVAHI_PROTO_UNSPEC, &a[n])) avahi_log_warn("Failed to parse address '%s', ignoring.", *p); else n++; } avahi_server_set_wide_area_servers(avahi_server, a, n); }
static void signal_callback(AvahiWatch *watch, AVAHI_GCC_UNUSED int fd, AVAHI_GCC_UNUSED AvahiWatchEvent event, AVAHI_GCC_UNUSED void *userdata) { int sig; const AvahiPoll *poll_api; assert(watch); assert(simple_poll_api); poll_api = avahi_simple_poll_get(simple_poll_api); if ((sig = daemon_signal_next()) <= 0) { avahi_log_error("daemon_signal_next() failed"); poll_api->watch_free(watch); return; } switch (sig) { case SIGINT: case SIGQUIT: case SIGTERM: avahi_log_info( "Got %s, quitting.", sig == SIGINT ? "SIGINT" : (sig == SIGQUIT ? "SIGQUIT" : "SIGTERM")); avahi_simple_poll_quit(simple_poll_api); break; case SIGHUP: avahi_log_info("Got SIGHUP, reloading."); reload_config(); break; case SIGUSR1: avahi_log_info("Got SIGUSR1, dumping record data."); avahi_server_dump(avahi_server, dump, NULL); break; default: avahi_log_warn("Got spurious signal, ignoring."); break; } }
static void update_browse_domains(void) { AvahiStringList *l; int n; char **p; if (!resolv_conf_search_domains) { avahi_server_set_browse_domains(avahi_server, NULL); return; } l = avahi_string_list_copy(config.server_config.browse_domains); for (p = resolv_conf_search_domains, n = 0; *p && n < BROWSE_DOMAINS_MAX; p++, n++) { if (!avahi_is_valid_domain_name(*p)) avahi_log_warn("'%s' is no valid domain name, ignoring.", *p); else l = avahi_string_list_add(l, *p); } l = filter_duplicate_domains(l); avahi_server_set_browse_domains(avahi_server, l); }
static void entry_group_callback(AvahiServer *s, AVAHI_GCC_UNUSED AvahiSEntryGroup *eg, AvahiEntryGroupState state, void* userdata) { StaticServiceGroup *g = userdata; assert(s); assert(g); switch (state) { case AVAHI_ENTRY_GROUP_COLLISION: { char *n; remove_static_service_group_from_server(g); n = avahi_alternative_service_name(g->chosen_name); avahi_free(g->chosen_name); g->chosen_name = n; avahi_log_notice("Service name conflict for \"%s\" (%s), retrying with \"%s\".", g->name, g->filename, g->chosen_name); add_static_service_group_to_server(g); break; } case AVAHI_ENTRY_GROUP_ESTABLISHED: avahi_log_info("Service \"%s\" (%s) successfully established.", g->chosen_name, g->filename); break; case AVAHI_ENTRY_GROUP_FAILURE: avahi_log_warn("Failed to publish service \"%s\" (%s): %s", g->chosen_name, g->filename, avahi_strerror(avahi_server_errno(s))); remove_static_service_group_from_server(g); break; case AVAHI_ENTRY_GROUP_UNCOMMITED: case AVAHI_ENTRY_GROUP_REGISTERING: ; } }
static void server_callback(AvahiServer *s, AvahiServerState state, void *userdata) { DaemonConfig *c = userdata; assert(s); assert(c); /* This function is possibly called before the global variable * avahi_server has been set, therefore we do it explicitly */ avahi_server = s; #ifdef HAVE_DBUS if (c->enable_dbus && state != AVAHI_SERVER_INVALID && state != AVAHI_SERVER_FAILURE) dbus_protocol_server_state_changed(state); #endif switch (state) { case AVAHI_SERVER_RUNNING: avahi_log_info("Server startup complete. Host name is %s. Local service cookie is %u.", avahi_server_get_host_name_fqdn(s), avahi_server_get_local_service_cookie(s)); sd_notifyf(0, "STATUS=Server startup complete. Host name is %s. Local service cookie is %u.", avahi_server_get_host_name_fqdn(s), avahi_server_get_local_service_cookie(s)); avahi_set_proc_title(argv0, "%s: running [%s]", argv0, avahi_server_get_host_name_fqdn(s)); static_service_add_to_server(); static_hosts_add_to_server(); remove_dns_server_entry_groups(); if (c->publish_resolv_conf && resolv_conf_name_servers && resolv_conf_name_servers[0]) resolv_conf_entry_group = add_dns_servers(s, resolv_conf_entry_group, resolv_conf_name_servers); if (c->publish_dns_servers && c->publish_dns_servers[0]) dns_servers_entry_group = add_dns_servers(s, dns_servers_entry_group, c->publish_dns_servers); simple_protocol_restart_queries(); break; case AVAHI_SERVER_COLLISION: { char *n; static_service_remove_from_server(); static_hosts_remove_from_server(); remove_dns_server_entry_groups(); n = avahi_alternative_host_name(avahi_server_get_host_name(s)); avahi_log_warn("Host name conflict, retrying with %s", n); sd_notifyf(0, "STATUS=Host name conflict, retrying with %s", n); avahi_set_proc_title(argv0, "%s: collision [%s]", argv0, n); avahi_server_set_host_name(s, n); avahi_free(n); break; } case AVAHI_SERVER_FAILURE: avahi_log_error("Server error: %s", avahi_strerror(avahi_server_errno(s))); sd_notifyf(0, "STATUS=Server error: %s", avahi_strerror(avahi_server_errno(s))); avahi_simple_poll_quit(simple_poll_api); break; case AVAHI_SERVER_REGISTERING: sd_notifyf(0, "STATUS=Registering host name %s", avahi_server_get_host_name_fqdn(s)); avahi_set_proc_title(argv0, "%s: registering [%s]", argv0, avahi_server_get_host_name_fqdn(s)); static_service_remove_from_server(); static_hosts_remove_from_server(); remove_dns_server_entry_groups(); break; case AVAHI_SERVER_INVALID: break; } }
static int load_resolv_conf(void) { int ret = -1; FILE *f; int i = 0, j = 0; avahi_strfreev(resolv_conf_name_servers); resolv_conf_name_servers = NULL; avahi_strfreev(resolv_conf_search_domains); resolv_conf_search_domains = NULL; #ifdef ENABLE_CHROOT f = avahi_chroot_helper_get_file(RESOLV_CONF); #else f = fopen(RESOLV_CONF, "r"); #endif if (!f) { avahi_log_warn("Failed to open "RESOLV_CONF": %s", strerror(errno)); goto finish; } resolv_conf_name_servers = avahi_new0(char*, AVAHI_WIDE_AREA_SERVERS_MAX+1); resolv_conf_search_domains = avahi_new0(char*, BROWSE_DOMAINS_MAX+1); while (!feof(f)) { char ln[128]; char *p; if (!(fgets(ln, sizeof(ln), f))) break; ln[strcspn(ln, "\r\n#")] = 0; p = ln + strspn(ln, "\t "); if ((has_prefix(p, "nameserver ") || has_prefix(p, "nameserver\t")) && i < AVAHI_WIDE_AREA_SERVERS_MAX) { p += 10; p += strspn(p, "\t "); p[strcspn(p, "\t ")] = 0; resolv_conf_name_servers[i++] = avahi_strdup(p); } if ((has_prefix(p, "search ") || has_prefix(p, "search\t") || has_prefix(p, "domain ") || has_prefix(p, "domain\t"))) { p += 6; while (j < BROWSE_DOMAINS_MAX) { size_t k; p += strspn(p, "\t "); k = strcspn(p, "\t "); if (k > 0) { resolv_conf_search_domains[j++] = avahi_strndup(p, k); p += k; } if (!*p) break; } } } ret = 0; finish: if (ret != 0) { avahi_strfreev(resolv_conf_name_servers); resolv_conf_name_servers = NULL; avahi_strfreev(resolv_conf_search_domains); resolv_conf_search_domains = NULL; } if (f) fclose(f); return ret; }
int main(int argc, char *argv[]) { int r = 255; int wrote_pid_file = 0; avahi_set_log_function(log_function); init_rand_seed(); avahi_server_config_init(&config.server_config); config.command = DAEMON_RUN; config.daemonize = 0; config.config_file = NULL; #ifdef HAVE_DBUS config.enable_dbus = 1; config.fail_on_missing_dbus = 1; config.n_clients_max = 0; config.n_objects_per_client_max = 0; config.n_entries_per_entry_group_max = 0; #endif config.drop_root = 1; config.set_rlimits = 1; #ifdef ENABLE_CHROOT config.use_chroot = 1; #endif config.modify_proc_title = 1; config.disable_user_service_publishing = 0; config.publish_dns_servers = NULL; config.publish_resolv_conf = 0; config.use_syslog = 0; config.debug = 0; config.rlimit_as_set = 0; config.rlimit_core_set = 0; config.rlimit_data_set = 0; config.rlimit_fsize_set = 0; config.rlimit_nofile_set = 0; config.rlimit_stack_set = 0; #ifdef RLIMIT_NPROC config.rlimit_nproc_set = 0; #endif if ((argv0 = strrchr(argv[0], '/'))) argv0 = avahi_strdup(argv0 + 1); else argv0 = avahi_strdup(argv[0]); daemon_pid_file_ident = (const char *) argv0; daemon_log_ident = (char*) argv0; daemon_pid_file_proc = pid_file_proc; if (parse_command_line(&config, argc, argv) < 0) goto finish; if (config.modify_proc_title) avahi_init_proc_title(argc, argv); #ifdef ENABLE_CHROOT config.use_chroot = config.use_chroot && config.drop_root; #endif if (config.command == DAEMON_HELP) { help(stdout); r = 0; } else if (config.command == DAEMON_VERSION) { printf("%s "PACKAGE_VERSION"\n", argv0); r = 0; } else if (config.command == DAEMON_KILL) { if (daemon_pid_file_kill_wait(SIGTERM, 5) < 0) { avahi_log_warn("Failed to kill daemon: %s", strerror(errno)); goto finish; } r = 0; } else if (config.command == DAEMON_RELOAD) { if (daemon_pid_file_kill(SIGHUP) < 0) { avahi_log_warn("Failed to kill daemon: %s", strerror(errno)); goto finish; } r = 0; } else if (config.command == DAEMON_CHECK) r = (daemon_pid_file_is_running() >= 0) ? 0 : 1; else if (config.command == DAEMON_RUN) { pid_t pid; if (getuid() != 0 && config.drop_root) { avahi_log_error("This program is intended to be run as root."); goto finish; } if ((pid = daemon_pid_file_is_running()) >= 0) { avahi_log_error("Daemon already running on PID %u", pid); goto finish; } if (load_config_file(&config) < 0) goto finish; if (config.daemonize) { daemon_retval_init(); if ((pid = daemon_fork()) < 0) goto finish; else if (pid != 0) { int ret; /** Parent **/ if ((ret = daemon_retval_wait(20)) < 0) { avahi_log_error("Could not receive return value from daemon process."); goto finish; } r = ret; goto finish; } /* Child */ } if (config.use_syslog || config.daemonize) daemon_log_use = DAEMON_LOG_SYSLOG; if (sd_listen_fds(0) <= 0) if (daemon_close_all(-1) < 0) avahi_log_warn("Failed to close all remaining file descriptors: %s", strerror(errno)); daemon_reset_sigs(-1); daemon_unblock_sigs(-1); if (make_runtime_dir() < 0) goto finish; if (config.drop_root) { #ifdef ENABLE_CHROOT if (config.use_chroot) if (avahi_caps_reduce() < 0) goto finish; #endif if (drop_root() < 0) goto finish; #ifdef ENABLE_CHROOT if (config.use_chroot) if (avahi_caps_reduce2() < 0) goto finish; #endif } if (daemon_pid_file_create() < 0) { avahi_log_error("Failed to create PID file: %s", strerror(errno)); if (config.daemonize) daemon_retval_send(1); goto finish; } else wrote_pid_file = 1; if (config.set_rlimits) enforce_rlimits(); chdir("/"); #ifdef ENABLE_CHROOT if (config.drop_root && config.use_chroot) if (avahi_chroot_helper_start(argv0) < 0) { avahi_log_error("failed to start chroot() helper daemon."); goto finish; } #endif avahi_log_info("%s "PACKAGE_VERSION" starting up.", argv0); sd_notifyf(0, "STATUS=%s "PACKAGE_VERSION" starting up.", argv0); avahi_set_proc_title(argv0, "%s: starting up", argv0); if (run_server(&config) == 0) r = 0; avahi_log_info("%s "PACKAGE_VERSION" exiting.", argv0); sd_notifyf(0, "STATUS=%s "PACKAGE_VERSION" exiting.", argv0); } finish: if (config.daemonize) daemon_retval_done(); avahi_server_config_free(&config.server_config); avahi_free(config.config_file); avahi_strfreev(config.publish_dns_servers); avahi_strfreev(resolv_conf_name_servers); avahi_strfreev(resolv_conf_search_domains); if (wrote_pid_file) { #ifdef ENABLE_CHROOT avahi_chroot_helper_unlink(pid_file_proc()); #else daemon_pid_file_remove(); #endif } #ifdef ENABLE_CHROOT avahi_chroot_helper_shutdown(); #endif avahi_free(argv0); return r; }
static int run_server(DaemonConfig *c) { int r = -1; int error; const AvahiPoll *poll_api = NULL; AvahiWatch *sig_watch = NULL; int retval_is_sent = 0; #ifdef HAVE_INOTIFY AvahiWatch *inotify_watch = NULL; #endif #ifdef HAVE_KQUEUE int i; AvahiWatch *kqueue_watch = NULL; #endif assert(c); ignore_signal(SIGPIPE); if (!(nss_support = avahi_nss_support())) avahi_log_warn("WARNING: No NSS support for mDNS detected, consider installing nss-mdns!"); if (!(simple_poll_api = avahi_simple_poll_new())) { avahi_log_error("Failed to create main loop object."); goto finish; } poll_api = avahi_simple_poll_get(simple_poll_api); if (daemon_signal_init(SIGINT, SIGHUP, SIGTERM, SIGUSR1, 0) < 0) { avahi_log_error("Could not register signal handlers (%s).", strerror(errno)); goto finish; } if (!(sig_watch = poll_api->watch_new(poll_api, daemon_signal_fd(), AVAHI_WATCH_IN, signal_callback, simple_poll_api))) { avahi_log_error( "Failed to create signal watcher"); goto finish; } if (simple_protocol_setup(poll_api) < 0) goto finish; #ifdef HAVE_DBUS if (c->enable_dbus) { if (dbus_protocol_setup(poll_api, config.disable_user_service_publishing, config.n_clients_max, config.n_objects_per_client_max, config.n_entries_per_entry_group_max, !c->fail_on_missing_dbus #ifdef ENABLE_CHROOT && !config.use_chroot #endif ) < 0) { avahi_log_warn("WARNING: Failed to contact D-Bus daemon."); if (c->fail_on_missing_dbus) goto finish; } } #endif #ifdef ENABLE_CHROOT if (config.drop_root && config.use_chroot) { if (chroot(AVAHI_CONFIG_DIR) < 0) { avahi_log_error("Failed to chroot(): %s", strerror(errno)); goto finish; } avahi_log_info("Successfully called chroot()."); chdir("/"); if (avahi_caps_drop_all() < 0) { avahi_log_error("Failed to drop capabilities."); goto finish; } avahi_log_info("Successfully dropped remaining capabilities."); } #endif #ifdef HAVE_INOTIFY if ((inotify_fd = inotify_init()) < 0) avahi_log_warn( "Failed to initialize inotify: %s", strerror(errno)); else { add_inotify_watches(); if (!(inotify_watch = poll_api->watch_new(poll_api, inotify_fd, AVAHI_WATCH_IN, inotify_callback, NULL))) { avahi_log_error( "Failed to create inotify watcher"); goto finish; } } #endif #ifdef HAVE_KQUEUE if ((kq = kqueue()) < 0) avahi_log_warn( "Failed to initialize kqueue: %s", strerror(errno)); else { add_kqueue_watches(); if (!(kqueue_watch = poll_api->watch_new(poll_api, kq, AVAHI_WATCH_IN, kqueue_callback, NULL))) { avahi_log_error( "Failed to create kqueue watcher"); goto finish; } } #endif load_resolv_conf(); #ifdef ENABLE_CHROOT static_service_load(config.use_chroot); static_hosts_load(config.use_chroot); #else static_service_load(0); static_hosts_load(0); #endif if (!(avahi_server = avahi_server_new(poll_api, &c->server_config, server_callback, c, &error))) { avahi_log_error("Failed to create server: %s", avahi_strerror(error)); goto finish; } update_wide_area_servers(); update_browse_domains(); if (c->daemonize) { daemon_retval_send(0); retval_is_sent = 1; } for (;;) { if ((r = avahi_simple_poll_iterate(simple_poll_api, -1)) < 0) { /* We handle signals through an FD, so let's continue */ if (errno == EINTR) continue; avahi_log_error("poll(): %s", strerror(errno)); goto finish; } else if (r > 0) /* Quit */ break; } r = 0; finish: static_service_remove_from_server(); static_service_free_all(); static_hosts_remove_from_server(); static_hosts_free_all(); remove_dns_server_entry_groups(); simple_protocol_shutdown(); #ifdef HAVE_DBUS if (c->enable_dbus) dbus_protocol_shutdown(); #endif if (avahi_server) { avahi_server_free(avahi_server); avahi_server = NULL; } daemon_signal_done(); if (sig_watch) poll_api->watch_free(sig_watch); #ifdef HAVE_INOTIFY if (inotify_watch) poll_api->watch_free(inotify_watch); if (inotify_fd >= 0) close(inotify_fd); #endif #ifdef HAVE_KQUEUE if (kqueue_watch) poll_api->watch_free(kqueue_watch); if (kq >= 0) close(kq); for (i = 0; i < num_kfds; i++) { if (kfds[i] >= 0) close(kfds[i]); } #endif if (simple_poll_api) { avahi_simple_poll_free(simple_poll_api); simple_poll_api = NULL; } if (!retval_is_sent && c->daemonize) daemon_retval_send(1); return r; }
DBusHandlerResult avahi_dbus_msg_entry_group_impl(DBusConnection *c, DBusMessage *m, void *userdata) { DBusError error; EntryGroupInfo *i = userdata; assert(c); assert(m); assert(i); dbus_error_init(&error); avahi_log_debug(__FILE__": interface=%s, path=%s, member=%s", dbus_message_get_interface(m), dbus_message_get_path(m), dbus_message_get_member(m)); /* Introspection */ if (dbus_message_is_method_call(m, DBUS_INTERFACE_INTROSPECTABLE, "Introspect")) return avahi_dbus_handle_introspect(c, m, "org.freedesktop.Avahi.EntryGroup.xml"); /* Access control */ if (strcmp(dbus_message_get_sender(m), i->client->name)) return avahi_dbus_respond_error(c, m, AVAHI_ERR_ACCESS_DENIED, NULL); if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "Free")) { if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) { avahi_log_warn("Error parsing EntryGroup::Free message"); goto fail; } avahi_dbus_entry_group_free(i); return avahi_dbus_respond_ok(c, m); } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "Commit")) { if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) { avahi_log_warn("Error parsing EntryGroup::Commit message"); goto fail; } if (avahi_s_entry_group_commit(i->entry_group) < 0) return avahi_dbus_respond_error(c, m, avahi_server_errno(avahi_server), NULL); return avahi_dbus_respond_ok(c, m); } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "Reset")) { if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) { avahi_log_warn("Error parsing EntryGroup::Reset message"); goto fail; } avahi_s_entry_group_reset(i->entry_group); i->n_entries = 0; return avahi_dbus_respond_ok(c, m); } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "IsEmpty")) { if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) { avahi_log_warn("Error parsing EntryGroup::IsEmpty message"); goto fail; } return avahi_dbus_respond_boolean(c, m, !!avahi_s_entry_group_is_empty(i->entry_group)); } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "GetState")) { AvahiEntryGroupState state; if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) { avahi_log_warn("Error parsing EntryGroup::GetState message"); goto fail; } state = avahi_s_entry_group_get_state(i->entry_group); return avahi_dbus_respond_int32(c, m, (int32_t) state); } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "AddService")) { int32_t interface, protocol; uint32_t flags; char *type, *name, *domain, *host; uint16_t port; AvahiStringList *strlst = NULL; if (!dbus_message_get_args( m, &error, DBUS_TYPE_INT32, &interface, DBUS_TYPE_INT32, &protocol, DBUS_TYPE_UINT32, &flags, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &type, DBUS_TYPE_STRING, &domain, DBUS_TYPE_STRING, &host, DBUS_TYPE_UINT16, &port, DBUS_TYPE_INVALID) || !type || !name || avahi_dbus_read_strlst(m, 8, &strlst) < 0) { avahi_log_warn("Error parsing EntryGroup::AddService message"); goto fail; } if (!(flags & AVAHI_PUBLISH_UPDATE) && i->n_entries >= server->n_entries_per_entry_group_max) { avahi_string_list_free(strlst); return avahi_dbus_respond_error(c, m, AVAHI_ERR_TOO_MANY_ENTRIES, NULL); } if (domain && !*domain) domain = NULL; if (host && !*host) host = NULL; if (avahi_server_add_service_strlst(avahi_server, i->entry_group, (AvahiIfIndex) interface, (AvahiProtocol) protocol, (AvahiPublishFlags) flags, name, type, domain, host, port, strlst) < 0) { avahi_string_list_free(strlst); return avahi_dbus_respond_error(c, m, avahi_server_errno(avahi_server), NULL); } if (!(flags & AVAHI_PUBLISH_UPDATE)) i->n_entries ++; avahi_string_list_free(strlst); return avahi_dbus_respond_ok(c, m); } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "AddServiceSubtype")) { int32_t interface, protocol; uint32_t flags; char *type, *name, *domain, *subtype; if (!dbus_message_get_args( m, &error, DBUS_TYPE_INT32, &interface, DBUS_TYPE_INT32, &protocol, DBUS_TYPE_UINT32, &flags, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &type, DBUS_TYPE_STRING, &domain, DBUS_TYPE_STRING, &subtype, DBUS_TYPE_INVALID) || !type || !name || !subtype) { avahi_log_warn("Error parsing EntryGroup::AddServiceSubtype message"); goto fail; } if (!(flags & AVAHI_PUBLISH_UPDATE) && i->n_entries >= server->n_entries_per_entry_group_max) return avahi_dbus_respond_error(c, m, AVAHI_ERR_TOO_MANY_ENTRIES, NULL); if (domain && !*domain) domain = NULL; if (avahi_server_add_service_subtype(avahi_server, i->entry_group, (AvahiIfIndex) interface, (AvahiProtocol) protocol, (AvahiPublishFlags) flags, name, type, domain, subtype) < 0) return avahi_dbus_respond_error(c, m, avahi_server_errno(avahi_server), NULL); if (!(flags & AVAHI_PUBLISH_UPDATE)) i->n_entries ++; return avahi_dbus_respond_ok(c, m); } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "UpdateServiceTxt")) { int32_t interface, protocol; uint32_t flags; char *type, *name, *domain; AvahiStringList *strlst; if (!dbus_message_get_args( m, &error, DBUS_TYPE_INT32, &interface, DBUS_TYPE_INT32, &protocol, DBUS_TYPE_UINT32, &flags, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &type, DBUS_TYPE_STRING, &domain, DBUS_TYPE_INVALID) || !type || !name || avahi_dbus_read_strlst(m, 6, &strlst)) { avahi_log_warn("Error parsing EntryGroup::UpdateServiceTxt message"); goto fail; } if (domain && !*domain) domain = NULL; if (avahi_server_update_service_txt_strlst(avahi_server, i->entry_group, (AvahiIfIndex) interface, (AvahiProtocol) protocol, (AvahiPublishFlags) flags, name, type, domain, strlst) < 0) { avahi_string_list_free(strlst); return avahi_dbus_respond_error(c, m, avahi_server_errno(avahi_server), NULL); } avahi_string_list_free(strlst); return avahi_dbus_respond_ok(c, m); } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "AddAddress")) { int32_t interface, protocol; uint32_t flags; char *name, *address; AvahiAddress a; if (!dbus_message_get_args( m, &error, DBUS_TYPE_INT32, &interface, DBUS_TYPE_INT32, &protocol, DBUS_TYPE_UINT32, &flags, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID) || !name || !address) { avahi_log_warn("Error parsing EntryGroup::AddAddress message"); goto fail; } if (!(flags & AVAHI_PUBLISH_UPDATE) && i->n_entries >= server->n_entries_per_entry_group_max) return avahi_dbus_respond_error(c, m, AVAHI_ERR_TOO_MANY_ENTRIES, NULL); if (!(avahi_address_parse(address, AVAHI_PROTO_UNSPEC, &a))) return avahi_dbus_respond_error(c, m, AVAHI_ERR_INVALID_ADDRESS, NULL); if (avahi_server_add_address(avahi_server, i->entry_group, (AvahiIfIndex) interface, (AvahiProtocol) protocol, (AvahiPublishFlags) flags, name, &a) < 0) return avahi_dbus_respond_error(c, m, avahi_server_errno(avahi_server), NULL); if (!(flags & AVAHI_PUBLISH_UPDATE)) i->n_entries ++; return avahi_dbus_respond_ok(c, m); } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "AddRecord")) { int32_t interface, protocol; uint32_t flags, ttl, size; uint16_t clazz, type; char *name; void *rdata; AvahiRecord *r; if (!dbus_message_get_args( m, &error, DBUS_TYPE_INT32, &interface, DBUS_TYPE_INT32, &protocol, DBUS_TYPE_UINT32, &flags, DBUS_TYPE_STRING, &name, DBUS_TYPE_UINT16, &clazz, DBUS_TYPE_UINT16, &type, DBUS_TYPE_UINT32, &ttl, DBUS_TYPE_INVALID) || !name || avahi_dbus_read_rdata (m, 7, &rdata, &size)) { avahi_log_warn("Error parsing EntryGroup::AddRecord message"); goto fail; } if (!(flags & AVAHI_PUBLISH_UPDATE) && i->n_entries >= server->n_entries_per_entry_group_max) return avahi_dbus_respond_error(c, m, AVAHI_ERR_TOO_MANY_ENTRIES, NULL); if (!avahi_is_valid_domain_name (name)) return avahi_dbus_respond_error(c, m, AVAHI_ERR_INVALID_DOMAIN_NAME, NULL); if (!(r = avahi_record_new_full (name, clazz, type, ttl))) return avahi_dbus_respond_error(c, m, AVAHI_ERR_NO_MEMORY, NULL); if (avahi_rdata_parse (r, rdata, size) < 0) { avahi_record_unref (r); return avahi_dbus_respond_error(c, m, AVAHI_ERR_INVALID_RDATA, NULL); } if (avahi_server_add(avahi_server, i->entry_group, (AvahiIfIndex) interface, (AvahiProtocol) protocol, (AvahiPublishFlags) flags, r) < 0) { avahi_record_unref (r); return avahi_dbus_respond_error(c, m, avahi_server_errno(avahi_server), NULL); } if (!(flags & AVAHI_PUBLISH_UPDATE)) i->n_entries ++; avahi_record_unref (r); return avahi_dbus_respond_ok(c, m); } avahi_log_warn("Missed message %s::%s()", dbus_message_get_interface(m), dbus_message_get_member(m)); fail: if (dbus_error_is_set(&error)) dbus_error_free(&error); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; }
static void handle_packet(AvahiWideAreaLookupEngine *e, AvahiDnsPacket *p) { AvahiWideAreaLookup *l = NULL; int i, r; AvahiBrowserEvent final_event = AVAHI_BROWSER_ALL_FOR_NOW; assert(e); assert(p); /* Some superficial validity tests */ if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) { avahi_log_warn(__FILE__": Ignoring invalid response for wide area datagram."); goto finish; } /* Look for the lookup that issued this query */ if (!(l = find_lookup(e, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID))) || l->dead) goto finish; /* Check whether this a packet indicating a failure */ if ((r = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & 15) != 0 || avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0) { avahi_server_set_errno(e->server, r == 0 ? AVAHI_ERR_NOT_FOUND : map_dns_error(r)); /* Tell the user about the failure */ final_event = AVAHI_BROWSER_FAILURE; /* We go on here, since some of the records contained in the reply might be interesting in some way */ } /* Skip over the question */ for (i = (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); i > 0; i--) { AvahiKey *k; if (!(k = avahi_dns_packet_consume_key(p, NULL))) { avahi_log_warn(__FILE__": Wide area response packet too short or invalid while reading question key. (Maybe a UTF-8 problem?)"); avahi_server_set_errno(e->server, AVAHI_ERR_INVALID_PACKET); final_event = AVAHI_BROWSER_FAILURE; goto finish; } avahi_key_unref(k); } /* Process responses */ for (i = (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) + (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) + (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); i > 0; i--) { AvahiRecord *rr; if (!(rr = avahi_dns_packet_consume_record(p, NULL))) { avahi_log_warn(__FILE__": Wide area response packet too short or invalid while reading response record. (Maybe a UTF-8 problem?)"); avahi_server_set_errno(e->server, AVAHI_ERR_INVALID_PACKET); final_event = AVAHI_BROWSER_FAILURE; goto finish; } add_to_cache(e, rr); avahi_record_unref(rr); } finish: if (l && !l->dead) { if (l->callback) l->callback(e, final_event, AVAHI_LOOKUP_RESULT_WIDE_AREA, NULL, l->userdata); lookup_stop(l); } }
static void elapse_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* data) { AvahiProbeJob *pj = data, *next; AvahiProbeScheduler *s; AvahiDnsPacket *p; unsigned n; assert(pj); s = pj->scheduler; if (pj->done) { /* Lets remove it from the history */ job_free(s, pj); return; } if (!(p = avahi_dns_packet_new_query(s->interface->hardware->mtu))) return; /* OOM */ n = 1; /* Add the import probe */ if (!packet_add_probe_query(s, p, pj)) { size_t size; AvahiKey *k; int b; avahi_dns_packet_free(p); /* The probe didn't fit in the package, so let's allocate a larger one */ size = avahi_key_get_estimate_size(pj->record->key) + avahi_record_get_estimate_size(pj->record) + AVAHI_DNS_PACKET_HEADER_SIZE; if (size > AVAHI_DNS_PACKET_SIZE_MAX) size = AVAHI_DNS_PACKET_SIZE_MAX; if (!(p = avahi_dns_packet_new_query(size))) return; /* OOM */ if (!(k = avahi_key_new(pj->record->key->name, pj->record->key->clazz, AVAHI_DNS_TYPE_ANY))) { avahi_dns_packet_free(p); return; /* OOM */ } b = avahi_dns_packet_append_key(p, k, 0) && avahi_dns_packet_append_record(p, pj->record, 0, 0); avahi_key_unref(k); if (b) { avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, 1); avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, 1); avahi_interface_send_packet(s->interface, p); } else avahi_log_warn("Probe record too large, cannot send"); avahi_dns_packet_free(p); job_mark_done(s, pj); return; } /* Try to fill up packet with more probes, if available */ for (pj = s->jobs; pj; pj = pj->jobs_next) { if (pj->chosen) continue; if (!packet_add_probe_query(s, p, pj)) break; n++; } avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, n); n = 0; /* Now add the chosen records to the authorative section */ for (pj = s->jobs; pj; pj = next) { next = pj->jobs_next; if (!pj->chosen) continue; if (!avahi_dns_packet_append_record(p, pj->record, 0, 0)) { /* avahi_log_warn("Bad probe size estimate!"); */ /* Unmark all following jobs */ for (; pj; pj = pj->jobs_next) pj->chosen = 0; break; } job_mark_done(s, pj); n ++; } avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, n); /* Send it now */ avahi_interface_send_packet(s->interface, p); avahi_dns_packet_free(p); }
int simple_protocol_setup(const AvahiPoll *poll_api) { struct sockaddr_un sa; mode_t u; int n; assert(!server); server = avahi_new(Server, 1); server->poll_api = poll_api; server->remove_socket = 0; server->fd = -1; server->n_clients = 0; AVAHI_LLIST_HEAD_INIT(Client, server->clients); server->watch = NULL; u = umask(0000); if ((n = sd_listen_fds(1)) < 0) { avahi_log_warn("Failed to acquire systemd file descriptors: %s", strerror(-n)); goto fail; } if (n > 1) { avahi_log_warn("Too many systemd file descriptors passed."); goto fail; } if (n == 1) { int r; if ((r = sd_is_socket(SD_LISTEN_FDS_START, AF_LOCAL, SOCK_STREAM, 1)) < 0) { avahi_log_warn("Passed systemd file descriptor is of wrong type: %s", strerror(-r)); goto fail; } server->fd = SD_LISTEN_FDS_START; } else { if ((server->fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) { avahi_log_warn("socket(AF_LOCAL, SOCK_STREAM, 0): %s", strerror(errno)); goto fail; } memset(&sa, 0, sizeof(sa)); sa.sun_family = AF_LOCAL; strncpy(sa.sun_path, AVAHI_SOCKET, sizeof(sa.sun_path)-1); /* We simply remove existing UNIX sockets under this name. The Avahi daemon makes sure that it runs only once on a host, therefore sockets that already exist are stale and may be removed without any ill effects */ unlink(AVAHI_SOCKET); if (bind(server->fd, (struct sockaddr*) &sa, sizeof(sa)) < 0) { avahi_log_warn("bind(): %s", strerror(errno)); goto fail; } server->remove_socket = 1; if (listen(server->fd, SOMAXCONN) < 0) { avahi_log_warn("listen(): %s", strerror(errno)); goto fail; } } umask(u); server->watch = poll_api->watch_new(poll_api, server->fd, AVAHI_WATCH_IN, server_work, server); return 0; fail: umask(u); simple_protocol_shutdown(); return -1; }