DBusHandlerResult message_handler(DBusConnection *connection, DBusMessage *message, void *user_data) { char *method = (char *)dbus_message_get_member(message); DBusMessage *reply = NULL; if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect")) { /* string length: "%s" provides space for termination zero */ if (!introspection_xml && (introspection_xml = whine_malloc(strlen(introspection_xml_template) + strlen(daemon->dbus_name)))) sprintf(introspection_xml, introspection_xml_template, daemon->dbus_name); if (introspection_xml) { reply = dbus_message_new_method_return(message); dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection_xml, DBUS_TYPE_INVALID); } } else if (strcmp(method, "GetVersion") == 0) { char *v = VERSION; reply = dbus_message_new_method_return(message); dbus_message_append_args(reply, DBUS_TYPE_STRING, &v, DBUS_TYPE_INVALID); } else if (strcmp(method, "SetServers") == 0) { my_syslog(LOG_INFO, _("setting upstream servers from DBus")); dbus_read_servers(message); check_servers(); } else if (strcmp(method, "SetServersEx") == 0) { my_syslog(LOG_INFO, _("setting upstream servers from DBus")); reply = dbus_read_servers_ex(message); check_servers(); } else if (strcmp(method, "ClearCache") == 0) clear_cache_and_reload(dnsmasq_time()); else return (DBUS_HANDLER_RESULT_NOT_YET_HANDLED); method = user_data; /* no warning */ /* If no reply or no error, return nothing */ if (!reply) reply = dbus_message_new_method_return(message); if (reply) { dbus_connection_send (connection, reply, NULL); dbus_message_unref (reply); } return (DBUS_HANDLER_RESULT_HANDLED); }
/* initialisation for dhcp-hostdir. Set inotify watch for each directory, and read pre-existing files */ void set_dhcp_inotify(void) { struct hostsfile *ah; for (ah = daemon->inotify_hosts; ah; ah = ah->next) { DIR *dir_stream = NULL; struct dirent *ent; struct stat buf; if (stat(ah->fname, &buf) == -1 || !(S_ISDIR(buf.st_mode))) { my_syslog(LOG_ERR, _("bad directory in dhcp-hostsdir %s"), ah->fname); continue; } if (!(ah->flags & AH_WD_DONE)) { ah->wd = inotify_add_watch(daemon->inotifyfd, ah->fname, IN_CLOSE_WRITE | IN_MOVED_TO); ah->flags |= AH_WD_DONE; } /* Read contents of dir _after_ calling add_watch, in the ho[e of avoiding a race which misses files being added as we start */ if (ah->wd == -1 || !(dir_stream = opendir(ah->fname))) { my_syslog(LOG_ERR, _("failed to create inotify for %s"), ah->fname); continue; } while ((ent = readdir(dir_stream))) { size_t lendir = strlen(ah->fname); size_t lenfile = strlen(ent->d_name); char *path; /* ignore emacs backups and dotfiles */ if (lenfile == 0 || ent->d_name[lenfile - 1] == '~' || (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') || ent->d_name[0] == '.') continue; if ((path = whine_malloc(lendir + lenfile + 2))) { strcpy(path, ah->fname); strcat(path, "/"); strcat(path, ent->d_name); /* ignore non-regular files */ if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode)) option_read_hostsfile(path); free(path); } } } }
static dbus_bool_t add_watch(DBusWatch *watch, void *data) { struct watch *w; for (w = daemon->watches; w; w = w->next) if (w->watch == watch) return TRUE; if (!(w = whine_malloc(sizeof(struct watch)))) return FALSE; w->watch = watch; w->next = daemon->watches; daemon->watches = w; w = data; /* no warning */ return TRUE; }
struct blockdata *blockdata_alloc(char *data, size_t len) { struct blockdata *block, *ret = NULL; struct blockdata **prev = &ret; size_t blen; while (len > 0) { if (keyblock_free) { block = keyblock_free; keyblock_free = block->next; blockdata_count++; } else if ((block = whine_malloc(sizeof(struct blockdata)))) { blockdata_count++; if (blockdata_hwm < blockdata_count) blockdata_hwm = blockdata_count; } if (!block) { /* failed to alloc, free partial chain */ blockdata_free(ret); return NULL; } blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len; memcpy(block->key, data, blen); data += blen; len -= blen; *prev = block; prev = &block->next; block->next = NULL; } return ret; }
static void check_for_dhcp_inotify(struct inotify_event *in, time_t now) { struct hostsfile *ah; /* ignore emacs backups and dotfiles */ if (in->len == 0 || in->name[in->len - 1] == '~' || (in->name[0] == '#' && in->name[in->len - 1] == '#') || in->name[0] == '.') return; for (ah = daemon->inotify_hosts; ah; ah = ah->next) if (ah->wd == in->wd) { size_t lendir = strlen(ah->fname); char *path; if ((path = whine_malloc(lendir + in->len + 2))) { strcpy(path, ah->fname); strcat(path, "/"); strcat(path, in->name); if (option_read_hostsfile(path)) { /* Propogate the consequences of loading a new dhcp-host */ dhcp_update_configs(daemon->dhcp_conf); lease_update_from_configs(); lease_update_file(now); lease_update_dns(1); } free(path); } return; } }
static void dbus_read_servers(DBusMessage *message) { struct server *serv, *tmp, **up; DBusMessageIter iter; union mysockaddr addr, source_addr; char *domain; dbus_message_iter_init(message, &iter); for (serv = daemon->servers; serv; serv = serv->next) if (serv->flags & SERV_FROM_DBUS) serv->flags |= SERV_MARK; while (1) { int skip = 0; if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32) { u32 a; dbus_message_iter_get_basic(&iter, &a); dbus_message_iter_next (&iter); #ifdef HAVE_SOCKADDR_SA_LEN source_addr.in.sin_len = addr.in.sin_len = sizeof(struct sockaddr_in); #endif addr.in.sin_addr.s_addr = ntohl(a); source_addr.in.sin_family = addr.in.sin_family = AF_INET; addr.in.sin_port = htons(NAMESERVER_PORT); source_addr.in.sin_addr.s_addr = INADDR_ANY; source_addr.in.sin_port = htons(daemon->query_port); } else if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_BYTE) { unsigned char p[sizeof(struct in6_addr)]; unsigned int i; skip = 1; for(i = 0; i < sizeof(struct in6_addr); i++) { dbus_message_iter_get_basic(&iter, &p[i]); dbus_message_iter_next (&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BYTE) break; } #ifndef HAVE_IPV6 my_syslog(LOG_WARNING, _("attempt to set an IPv6 server address via DBus - no IPv6 support")); #else if (i == sizeof(struct in6_addr)-1) { memcpy(&addr.in6.sin6_addr, p, sizeof(struct in6_addr)); #ifdef HAVE_SOCKADDR_SA_LEN source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(struct sockaddr_in6); #endif source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6; addr.in6.sin6_port = htons(NAMESERVER_PORT); source_addr.in6.sin6_flowinfo = addr.in6.sin6_flowinfo = 0; source_addr.in6.sin6_scope_id = addr.in6.sin6_scope_id = 0; source_addr.in6.sin6_addr = in6addr_any; source_addr.in6.sin6_port = htons(daemon->query_port); skip = 0; } #endif } else break; do { if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) { dbus_message_iter_get_basic(&iter, &domain); dbus_message_iter_next (&iter); } else domain = NULL; if (!skip) { for (serv = daemon->servers; serv; serv = serv->next) if ((serv->flags & SERV_FROM_DBUS) && (serv->flags & SERV_MARK)) { if (!(serv->flags & SERV_HAS_DOMAIN) && !domain) { serv->flags &= ~SERV_MARK; break; } if ((serv->flags & SERV_HAS_DOMAIN) && domain && hostname_isequal(domain, serv->domain)) { serv->flags &= ~SERV_MARK; break; } } if (!serv && (serv = whine_malloc(sizeof (struct server)))) { memset(serv, 0, sizeof(struct server)); if (domain) serv->domain = whine_malloc(strlen(domain)+1); if (domain && !serv->domain) { free(serv); serv = NULL; } else { serv->next = daemon->servers; daemon->servers = serv; serv->flags = SERV_FROM_DBUS; if (domain) { strcpy(serv->domain, domain); serv->flags |= SERV_HAS_DOMAIN; } } } if (serv) { if (source_addr.in.sin_family == AF_INET && addr.in.sin_addr.s_addr == 0 && serv->domain) serv->flags |= SERV_NO_ADDR; else { serv->flags &= ~SERV_NO_ADDR; serv->addr = addr; serv->source_addr = source_addr; } } } } while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING); } for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp) { tmp = serv->next; if (serv->flags & SERV_MARK) { server_gone(serv); *up = serv->next; free(serv); } else up = &serv->next; } }
/* The daemon forks before calling this: it should deal with one connection, blocking as neccessary, and then return. Note, need to be a bit careful about resources for debug mode, when the fork is suppressed: that's done by the caller. */ unsigned char *tcp_request(int confd, time_t now, struct in_addr local_addr, struct in_addr netmask) { int size = 0; size_t m; unsigned short qtype, gotname; unsigned char c1, c2; /* Max TCP packet + slop */ unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ); HEADER *header; struct server *last_server; while (1) { if (!packet || !read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) || !(size = c1 << 8 | c2) || !read_write(confd, packet, size, 1)) return packet; if (size < (int)sizeof(HEADER)) continue; header = (HEADER *)packet; if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype))) { union mysockaddr peer_addr; socklen_t peer_len = sizeof(union mysockaddr); if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) != -1) { char types[20]; querystr(types, qtype); if (peer_addr.sa.sa_family == AF_INET) log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, (struct all_addr *)&peer_addr.in.sin_addr, types); #ifdef HAVE_IPV6 else log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, (struct all_addr *)&peer_addr.in6.sin6_addr, types); #endif } } /* m > 0 if answered from cache */ m = answer_request(header, ((char *) header) + 65536, (unsigned int)size, local_addr, netmask, now); /* Do this by steam now we're not in the select() loop */ check_log_writer(NULL); if (m == 0) { unsigned short flags = 0; struct all_addr *addrp = NULL; int type = 0; char *domain = NULL; if (gotname) flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain); if (type != 0 || (daemon->options & OPT_ORDER) || !daemon->last_server) last_server = daemon->servers; else last_server = daemon->last_server; if (!flags && last_server) { struct server *firstsendto = NULL; unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff); /* Loop round available servers until we succeed in connecting to one. Note that this code subtley ensures that consecutive queries on this connection which can go to the same server, do so. */ while (1) { if (!firstsendto) firstsendto = last_server; else { if (!(last_server = last_server->next)) last_server = daemon->servers; if (last_server == firstsendto) break; } /* server for wrong domain */ if (type != (last_server->flags & SERV_TYPE) || (type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain))) continue; if ((last_server->tcpfd == -1) && (last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) != -1 && (!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, last_server->mark, 1) || connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1)) { close(last_server->tcpfd); last_server->tcpfd = -1; } if (last_server->tcpfd == -1) continue; c1 = size >> 8; c2 = size; if (!read_write(last_server->tcpfd, &c1, 1, 0) || !read_write(last_server->tcpfd, &c2, 1, 0) || !read_write(last_server->tcpfd, packet, size, 0) || !read_write(last_server->tcpfd, &c1, 1, 1) || !read_write(last_server->tcpfd, &c2, 1, 1)) { close(last_server->tcpfd); last_server->tcpfd = -1; continue; } m = (c1 << 8) | c2; if (!read_write(last_server->tcpfd, packet, m, 1)) return packet; if (!gotname) strcpy(daemon->namebuff, "query"); if (last_server->addr.sa.sa_family == AF_INET) log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff, (struct all_addr *)&last_server->addr.in.sin_addr, NULL); #ifdef HAVE_IPV6 else log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, (struct all_addr *)&last_server->addr.in6.sin6_addr, NULL); #endif /* There's no point in updating the cache, since this process will exit and lose the information after a few queries. We make this call for the alias and bogus-nxdomain side-effects. */ /* If the crc of the question section doesn't match the crc we sent, then someone might be attempting to insert bogus values into the cache by sending replies containing questions and bogus answers. */ if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff)) m = process_reply(header, now, last_server, (unsigned int)m); break; } } /* In case of local answer or no connections made. */ if (m == 0) m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl); } check_log_writer(NULL); c1 = m>>8; c2 = m; if (!read_write(confd, &c1, 1, 0) || !read_write(confd, &c2, 1, 0) || !read_write(confd, packet, m, 0)) return packet; }
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix) { char *packet = daemon->packet, *namebuff = daemon->namebuff; struct tftp_file *file; struct tftp_transfer *t; uid_t uid = geteuid(); struct stat statbuf; int fd = -1; /* trick to ban moving out of the subtree */ if (prefix && strstr(namebuff, "/../")) goto perm; if ((fd = open(namebuff, O_RDONLY)) == -1) { if (errno == ENOENT) { *len = tftp_err(ERR_FNF, packet, _("file %s not found"), namebuff); return NULL; } else if (errno == EACCES) goto perm; else goto oops; } /* stat the file descriptor to avoid stat->open races */ if (fstat(fd, &statbuf) == -1) goto oops; /* running as root, must be world-readable */ if (uid == 0) { if (!(statbuf.st_mode & S_IROTH)) goto perm; } /* in secure mode, must be owned by user running dnsmasq */ else if (option_bool(OPT_TFTP_SECURE) && uid != statbuf.st_uid) goto perm; /* If we're doing many transfers from the same file, only open it once this saves lots of file descriptors when mass-booting a big cluster, for instance. Be conservative and only share when inode and name match this keeps error messages sane. */ for (t = daemon->tftp_trans; t; t = t->next) if (t->file->dev == statbuf.st_dev && t->file->inode == statbuf.st_ino && strcmp(t->file->filename, namebuff) == 0) { close(fd); t->file->refcount++; return t->file; } if (!(file = whine_malloc(sizeof(struct tftp_file) + strlen(namebuff) + 1))) { errno = ENOMEM; goto oops; } file->fd = fd; file->size = statbuf.st_size; file->dev = statbuf.st_dev; file->inode = statbuf.st_ino; file->refcount = 1; strcpy(file->filename, namebuff); return file; perm: errno = EACCES; *len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff); if (fd != -1) close(fd); return NULL; oops: *len = tftp_err_oops(packet, namebuff); if (fd != -1) close(fd); return NULL; }
void tftp_request(struct listener *listen, time_t now) { ssize_t len; char *packet = daemon->packet; char *filename, *mode, *p, *end, *opt; union mysockaddr addr, peer; struct msghdr msg; struct iovec iov; struct ifreq ifr; int is_err = 1, if_index = 0, mtu = 0; struct iname *tmp; struct tftp_transfer *transfer; int port = daemon->start_tftp_port; /* may be zero to use ephemeral port */ #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) int mtuflag = IP_PMTUDISC_DONT; #endif char namebuff[IF_NAMESIZE]; char *name = NULL; char *prefix = daemon->tftp_prefix; struct tftp_prefix *pref; union all_addr addra; /* Can always get recvd interface for IPv6 */ int check_dest = !option_bool(OPT_NOWILD) || listen->family == AF_INET6; union { struct cmsghdr align; /* this ensures alignment */ char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; #if defined(HAVE_LINUX_NETWORK) char control[CMSG_SPACE(sizeof(struct in_pktinfo))]; #elif defined(HAVE_SOLARIS_NETWORK) char control[CMSG_SPACE(sizeof(unsigned int))]; #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) char control[CMSG_SPACE(sizeof(struct sockaddr_dl))]; #endif } control_u; msg.msg_controllen = sizeof(control_u); msg.msg_control = control_u.control; msg.msg_flags = 0; msg.msg_name = &peer; msg.msg_namelen = sizeof(peer); msg.msg_iov = &iov; msg.msg_iovlen = 1; iov.iov_base = packet; iov.iov_len = daemon->packet_buff_sz; /* we overwrote the buffer... */ daemon->srv_save = NULL; if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2) return; /* Can always get recvd interface for IPv6 */ if (!check_dest) { if (listen->iface) { addr = listen->iface->addr; name = listen->iface->name; mtu = listen->iface->mtu; if (daemon->tftp_mtu != 0 && daemon->tftp_mtu < mtu) mtu = daemon->tftp_mtu; } else { /* we're listening on an address that doesn't appear on an interface, ask the kernel what the socket is bound to */ socklen_t tcp_len = sizeof(union mysockaddr); if (getsockname(listen->tftpfd, (struct sockaddr *)&addr, &tcp_len) == -1) return; } } else { struct cmsghdr *cmptr; if (msg.msg_controllen < sizeof(struct cmsghdr)) return; addr.sa.sa_family = listen->family; #if defined(HAVE_LINUX_NETWORK) if (listen->family == AF_INET) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO) { union { unsigned char *c; struct in_pktinfo *p; } p; p.c = CMSG_DATA(cmptr); addr.in.sin_addr = p.p->ipi_spec_dst; if_index = p.p->ipi_ifindex; } #elif defined(HAVE_SOLARIS_NETWORK) if (listen->family == AF_INET) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) { union { unsigned char *c; struct in_addr *a; unsigned int *i; } p; p.c = CMSG_DATA(cmptr); if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR) addr.in.sin_addr = *(p.a); else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) if_index = *(p.i); } #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) if (listen->family == AF_INET) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) { union { unsigned char *c; struct in_addr *a; struct sockaddr_dl *s; } p; p.c = CMSG_DATA(cmptr); if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR) addr.in.sin_addr = *(p.a); else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) if_index = p.s->sdl_index; } #endif if (listen->family == AF_INET6) { for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo) { union { unsigned char *c; struct in6_pktinfo *p; } p; p.c = CMSG_DATA(cmptr); addr.in6.sin6_addr = p.p->ipi6_addr; if_index = p.p->ipi6_ifindex; } } if (!indextoname(listen->tftpfd, if_index, namebuff)) return; name = namebuff; addra.addr4 = addr.in.sin_addr; if (listen->family == AF_INET6) addra.addr6 = addr.in6.sin6_addr; if (daemon->tftp_interfaces) { /* dedicated tftp interface list */ for (tmp = daemon->tftp_interfaces; tmp; tmp = tmp->next) if (tmp->name && wildcard_match(tmp->name, name)) break; if (!tmp) return; } else { /* Do the same as DHCP */ if (!iface_check(listen->family, &addra, name, NULL)) { if (!option_bool(OPT_CLEVERBIND)) enumerate_interfaces(0); if (!loopback_exception(listen->tftpfd, listen->family, &addra, name) && !label_exception(if_index, listen->family, &addra)) return; } #ifdef HAVE_DHCP /* allowed interfaces are the same as for DHCP */ for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) if (tmp->name && wildcard_match(tmp->name, name)) return; #endif } safe_strncpy(ifr.ifr_name, name, IF_NAMESIZE); if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1) { mtu = ifr.ifr_mtu; if (daemon->tftp_mtu != 0 && daemon->tftp_mtu < mtu) mtu = daemon->tftp_mtu; } } /* Failed to get interface mtu - can use configured value. */ if (mtu == 0) mtu = daemon->tftp_mtu; if (name) { /* check for per-interface prefix */ for (pref = daemon->if_prefix; pref; pref = pref->next) if (strcmp(pref->interface, name) == 0) prefix = pref->prefix; } if (listen->family == AF_INET) { addr.in.sin_port = htons(port); #ifdef HAVE_SOCKADDR_SA_LEN addr.in.sin_len = sizeof(addr.in); #endif } else { addr.in6.sin6_port = htons(port); addr.in6.sin6_flowinfo = 0; addr.in6.sin6_scope_id = 0; #ifdef HAVE_SOCKADDR_SA_LEN addr.in6.sin6_len = sizeof(addr.in6); #endif } if (!(transfer = whine_malloc(sizeof(struct tftp_transfer)))) return; if ((transfer->sockfd = socket(listen->family, SOCK_DGRAM, 0)) == -1) { free(transfer); return; } transfer->peer = peer; transfer->timeout = now + 2; transfer->backoff = 1; transfer->block = 1; transfer->blocksize = 512; transfer->offset = 0; transfer->file = NULL; transfer->opt_blocksize = transfer->opt_transize = 0; transfer->netascii = transfer->carrylf = 0; prettyprint_addr(&peer, daemon->addrbuff); /* if we have a nailed-down range, iterate until we find a free one. */ while (1) { if (bind(transfer->sockfd, &addr.sa, sa_len(&addr)) == -1 || #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) setsockopt(transfer->sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 || #endif !fix_fd(transfer->sockfd)) { if (errno == EADDRINUSE && daemon->start_tftp_port != 0) { if (++port <= daemon->end_tftp_port) { if (listen->family == AF_INET) addr.in.sin_port = htons(port); else addr.in6.sin6_port = htons(port); continue; } my_syslog(MS_TFTP | LOG_ERR, _("unable to get free port for TFTP")); } free_transfer(transfer); return; } break; } p = packet + 2; end = packet + len; if (ntohs(*((unsigned short *)packet)) != OP_RRQ || !(filename = next(&p, end)) || !(mode = next(&p, end)) || (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0)) { len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff); is_err = 1; } else { if (strcasecmp(mode, "netascii") == 0) transfer->netascii = 1; while ((opt = next(&p, end))) { if (strcasecmp(opt, "blksize") == 0) { if ((opt = next(&p, end)) && !option_bool(OPT_TFTP_NOBLOCK)) { /* 32 bytes for IP, UDP and TFTP headers, 52 bytes for IPv6 */ int overhead = (listen->family == AF_INET) ? 32 : 52; transfer->blocksize = atoi(opt); if (transfer->blocksize < 1) transfer->blocksize = 1; if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4) transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4; if (mtu != 0 && transfer->blocksize > (unsigned)mtu - overhead) transfer->blocksize = (unsigned)mtu - overhead; transfer->opt_blocksize = 1; transfer->block = 0; } } else if (strcasecmp(opt, "tsize") == 0 && next(&p, end) && !transfer->netascii) { transfer->opt_transize = 1; transfer->block = 0; } } /* cope with backslashes from windows boxen. */ for (p = filename; *p; p++) if (*p == '\\') *p = '/'; else if (option_bool(OPT_TFTP_LC)) *p = tolower(*p); strcpy(daemon->namebuff, "/"); if (prefix) { if (prefix[0] == '/') daemon->namebuff[0] = 0; strncat(daemon->namebuff, prefix, (MAXDNAME-1) - strlen(daemon->namebuff)); if (prefix[strlen(prefix)-1] != '/') strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff)); if (option_bool(OPT_TFTP_APREF_IP)) { size_t oldlen = strlen(daemon->namebuff); struct stat statbuf; strncat(daemon->namebuff, daemon->addrbuff, (MAXDNAME-1) - strlen(daemon->namebuff)); strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff)); /* remove unique-directory if it doesn't exist */ if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode)) daemon->namebuff[oldlen] = 0; } if (option_bool(OPT_TFTP_APREF_MAC)) { unsigned char *macaddr = NULL; unsigned char macbuf[DHCP_CHADDR_MAX]; #ifdef HAVE_DHCP if (daemon->dhcp && peer.sa.sa_family == AF_INET) { /* Check if the client IP is in our lease database */ struct dhcp_lease *lease = lease_find_by_addr(peer.in.sin_addr); if (lease && lease->hwaddr_type == ARPHRD_ETHER && lease->hwaddr_len == ETHER_ADDR_LEN) macaddr = lease->hwaddr; } #endif /* If no luck, try to find in ARP table. This only works if client is in same (V)LAN */ if (!macaddr && find_mac(&peer, macbuf, 1, now) > 0) macaddr = macbuf; if (macaddr) { size_t oldlen = strlen(daemon->namebuff); struct stat statbuf; snprintf(daemon->namebuff + oldlen, (MAXDNAME-1) - oldlen, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x/", macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]); /* remove unique-directory if it doesn't exist */ if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode)) daemon->namebuff[oldlen] = 0; } } /* Absolute pathnames OK if they match prefix */ if (filename[0] == '/') { if (strstr(filename, daemon->namebuff) == filename) daemon->namebuff[0] = 0; else filename++; } } else if (filename[0] == '/') daemon->namebuff[0] = 0; strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff)); /* check permissions and open file */ if ((transfer->file = check_tftp_fileperm(&len, prefix))) { if ((len = get_block(packet, transfer)) == -1) len = tftp_err_oops(packet, daemon->namebuff); else is_err = 0; } } while (sendto(transfer->sockfd, packet, len, 0, (struct sockaddr *)&peer, sa_len(&peer)) == -1 && errno == EINTR); if (is_err) free_transfer(transfer); else { transfer->next = daemon->tftp_trans; daemon->tftp_trans = transfer; } }
void slaac_add_addrs(struct dhcp_lease *lease, time_t now) { struct slaac_address *slaac, *old, **up; struct dhcp_context *context; if (!(lease->flags & LEASE_HAVE_HWADDR) || lease->last_interface == 0 || !lease->hostname) return ; old = lease->slaac_address; lease->slaac_address = NULL; for (context = daemon->ra_contexts; context; context = context->next) if ((context->flags & CONTEXT_RA_NAME) && lease->last_interface == context->if_index) { struct in6_addr addr = context->start6; if (lease->hwaddr_len == 6 && (lease->hwaddr_type == ARPHRD_ETHER || lease->hwaddr_type == ARPHRD_IEEE802)) { /* convert MAC address to EUI-64 */ memcpy(&addr.s6_addr[8], lease->hwaddr, 3); memcpy(&addr.s6_addr[13], &lease->hwaddr[3], 3); addr.s6_addr[11] = 0xff; addr.s6_addr[12] = 0xfe; } #if defined(ARPHRD_EUI64) else if (lease->hwaddr_len == 8 && lease->hwaddr_type == ARPHRD_EUI64) memcpy(&addr.s6_addr[8], lease->hwaddr, 8); #endif #if defined(ARPHRD_IEEE1394) && defined(ARPHRD_EUI64) else if (lease->clid_len == 9 && lease->clid[0] == ARPHRD_EUI64 && lease->hwaddr_type == ARPHRD_IEEE1394) /* firewire has EUI-64 identifier as clid */ memcpy(&addr.s6_addr[8], &lease->clid[1], 8); #endif else continue; addr.s6_addr[8] ^= 0x02; /* check if we already have this one */ for (up = &old, slaac = old; slaac; slaac = slaac->next) { if (IN6_ARE_ADDR_EQUAL(&addr, &slaac->addr)) { *up = slaac->next; break; } up = &slaac->next; } /* No, make new one */ if (!slaac && (slaac = whine_malloc(sizeof(struct slaac_address)))) { slaac->ping_time = now; slaac->backoff = 1; slaac->addr = addr; slaac->local = context->local6; /* Do RA's to prod it */ ra_start_unsolicted(now, context); } if (slaac) { slaac->next = lease->slaac_address; lease->slaac_address = slaac; } } /* Free any no reused */ for (; old; old = slaac) { slaac = old->next; free(old); } }
void tftp_request(struct listener *listen, time_t now) { ssize_t len; char *packet = daemon->packet; char *filename, *mode, *p, *end, *opt; struct sockaddr_in addr, peer; struct msghdr msg; struct cmsghdr *cmptr; struct iovec iov; struct ifreq ifr; int is_err = 1, if_index = 0; struct iname *tmp; struct tftp_transfer *transfer; int port = daemon->start_tftp_port; /* may be zero to use ephemeral port */ #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) int mtu = IP_PMTUDISC_DONT; #endif union { struct cmsghdr align; /* this ensures alignment */ #if defined(HAVE_LINUX_NETWORK) char control[CMSG_SPACE(sizeof(struct in_pktinfo))]; #elif defined(HAVE_SOLARIS_NETWORK) char control[CMSG_SPACE(sizeof(unsigned int))]; #else char control[CMSG_SPACE(sizeof(struct sockaddr_dl))]; #endif } control_u; msg.msg_controllen = sizeof(control_u); msg.msg_control = control_u.control; msg.msg_flags = 0; msg.msg_name = &peer; msg.msg_namelen = sizeof(peer); msg.msg_iov = &iov; msg.msg_iovlen = 1; iov.iov_base = packet; iov.iov_len = daemon->packet_buff_sz; /* we overwrote the buffer... */ daemon->srv_save = NULL; if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2) return; if (daemon->options & OPT_NOWILD) addr = listen->iface->addr.in; else { addr.sin_addr.s_addr = 0; #if defined(HAVE_LINUX_NETWORK) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO) { addr.sin_addr = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst; if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex; } if (!(ifr.ifr_ifindex = if_index) || ioctl(listen->tftpfd, SIOCGIFNAME, &ifr) == -1) return; #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR) addr.sin_addr = *((struct in_addr *)CMSG_DATA(cmptr)); else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) #ifdef HAVE_SOLARIS_NETWORK if_index = *((unsigned int *)CMSG_DATA(cmptr)); #else if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index; #endif if (if_index == 0 || !if_indextoname(if_index, ifr.ifr_name)) return; #endif if (addr.sin_addr.s_addr == 0) return; if (!iface_check(AF_INET, (struct all_addr *)&addr.sin_addr, &ifr, &if_index)) return; /* allowed interfaces are the same as for DHCP */ for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0)) return; } addr.sin_port = htons(port); addr.sin_family = AF_INET; #ifdef HAVE_SOCKADDR_SA_LEN addr.sin_len = sizeof(addr); #endif if (!(transfer = whine_malloc(sizeof(struct tftp_transfer)))) return; if ((transfer->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { free(transfer); return; } transfer->peer = peer; transfer->timeout = now + 2; transfer->backoff = 1; transfer->block = 1; transfer->blocksize = 512; transfer->offset = 0; transfer->file = NULL; transfer->opt_blocksize = transfer->opt_transize = 0; transfer->netascii = transfer->carrylf = 0; /* if we have a nailed-down range, iterate until we find a free one. */ while (1) { if (bind(transfer->sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1 || #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) setsockopt(transfer->sockfd, SOL_IP, IP_MTU_DISCOVER, &mtu, sizeof(mtu)) == -1 || #endif !fix_fd(transfer->sockfd)) { if (errno == EADDRINUSE && daemon->start_tftp_port != 0) { if (++port <= daemon->end_tftp_port) { addr.sin_port = htons(port); continue; } my_syslog(LOG_ERR, _("unable to get free port for TFTP")); } free_transfer(transfer); return; } break; } p = packet + 2; end = packet + len; if (ntohs(*((unsigned short *)packet)) != OP_RRQ || !(filename = next(&p, end)) || !(mode = next(&p, end)) || (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0)) len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), inet_ntoa(peer.sin_addr)); else { if (strcasecmp(mode, "netascii") == 0) transfer->netascii = 1; while ((opt = next(&p, end))) { if (strcasecmp(opt, "blksize") == 0) { if ((opt = next(&p, end)) && !(daemon->options & OPT_TFTP_NOBLOCK)) { transfer->blocksize = atoi(opt); if (transfer->blocksize < 1) transfer->blocksize = 1; if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4) transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4; transfer->opt_blocksize = 1; transfer->block = 0; } } else if (strcasecmp(opt, "tsize") == 0 && next(&p, end) && !transfer->netascii) { transfer->opt_transize = 1; transfer->block = 0; } } strcpy(daemon->namebuff, "/"); if (daemon->tftp_prefix) { if (daemon->tftp_prefix[0] == '/') daemon->namebuff[0] = 0; strncat(daemon->namebuff, daemon->tftp_prefix, (MAXDNAME-1) - strlen(daemon->namebuff)); if (daemon->tftp_prefix[strlen(daemon->tftp_prefix)-1] != '/') strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff)); if (daemon->options & OPT_TFTP_APREF) { size_t oldlen = strlen(daemon->namebuff); struct stat statbuf; strncat(daemon->namebuff, inet_ntoa(peer.sin_addr), (MAXDNAME-1) - strlen(daemon->namebuff)); strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff)); /* remove unique-directory if it doesn't exist */ if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode)) daemon->namebuff[oldlen] = 0; } /* Absolute pathnames OK if they match prefix */ if (filename[0] == '/') { if (strstr(filename, daemon->namebuff) == filename) daemon->namebuff[0] = 0; else filename++; } } else if (filename[0] == '/') daemon->namebuff[0] = 0; strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff)); /* check permissions and open file */ if ((transfer->file = check_tftp_fileperm(&len))) { if ((len = get_block(packet, transfer)) == -1) len = tftp_err_oops(packet, daemon->namebuff); else is_err = 0; } } while (sendto(transfer->sockfd, packet, len, 0, (struct sockaddr *)&peer, sizeof(peer)) == -1 && errno == EINTR); if (is_err) free_transfer(transfer); else { my_syslog(LOG_INFO, _("TFTP sent %s to %s"), daemon->namebuff, inet_ntoa(peer.sin_addr)); transfer->next = daemon->tftp_trans; daemon->tftp_trans = transfer; } }
int inotify_check(time_t now) { int hit = 0; struct hostsfile *ah; while (1) { int rc; char *p; struct resolvc *res; struct inotify_event *in; while ((rc = read(daemon->inotifyfd, inotify_buffer, INOTIFY_SZ)) == -1 && errno == EINTR); if (rc <= 0) break; for (p = inotify_buffer; rc - (p - inotify_buffer) >= (int)sizeof(struct inotify_event); p += sizeof(struct inotify_event) + in->len) { in = (struct inotify_event*)p; for (res = daemon->resolv_files; res; res = res->next) if (res->wd == in->wd && in->len != 0 && strcmp(res->file, in->name) == 0) hit = 1; /* ignore emacs backups and dotfiles */ if (in->len == 0 || in->name[in->len - 1] == '~' || (in->name[0] == '#' && in->name[in->len - 1] == '#') || in->name[0] == '.') continue; for (ah = daemon->dynamic_dirs; ah; ah = ah->next) if (ah->wd == in->wd) { size_t lendir = strlen(ah->fname); char *path; if ((path = whine_malloc(lendir + in->len + 2))) { strcpy(path, ah->fname); strcat(path, "/"); strcat(path, in->name); my_syslog(LOG_INFO, _("inotify, new or changed file %s"), path); if (ah->flags & AH_HOSTS) { read_hostsfile(path, ah->index, 0, NULL, 0); #ifdef HAVE_DHCP if (daemon->dhcp || daemon->doing_dhcp6) { /* Propogate the consequences of loading a new dhcp-host */ dhcp_update_configs(daemon->dhcp_conf); lease_update_from_configs(); lease_update_file(now); lease_update_dns(1); } #endif } #ifdef HAVE_DHCP else if (ah->flags & AH_DHCP_HST) { if (option_read_dynfile(path, AH_DHCP_HST)) { /* Propogate the consequences of loading a new dhcp-host */ dhcp_update_configs(daemon->dhcp_conf); lease_update_from_configs(); lease_update_file(now); lease_update_dns(1); } } else if (ah->flags & AH_DHCP_OPT) option_read_dynfile(path, AH_DHCP_OPT); #endif free(path); } } } } return hit; }
void tftp_request(struct listener *listen, time_t now) { ssize_t len; char *packet = daemon->packet; char *filename, *mode, *p, *end, *opt; union mysockaddr addr, peer; struct msghdr msg; struct iovec iov; struct ifreq ifr; int is_err = 1, if_index = 0, mtu = 0, special = 0; #ifdef HAVE_DHCP struct iname *tmp; #endif struct tftp_transfer *transfer; int port = daemon->start_tftp_port; /* may be zero to use ephemeral port */ #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) int mtuflag = IP_PMTUDISC_DONT; #endif char namebuff[IF_NAMESIZE]; char pretty_addr[ADDRSTRLEN]; char *name; char *prefix = daemon->tftp_prefix; struct tftp_prefix *pref; struct interface_list *ir; union { struct cmsghdr align; /* this ensures alignment */ #ifdef HAVE_IPV6 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; #endif #if defined(HAVE_LINUX_NETWORK) char control[CMSG_SPACE(sizeof(struct in_pktinfo))]; #elif defined(HAVE_SOLARIS_NETWORK) char control[CMSG_SPACE(sizeof(unsigned int))]; #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) char control[CMSG_SPACE(sizeof(struct sockaddr_dl))]; #endif } control_u; msg.msg_controllen = sizeof(control_u); msg.msg_control = control_u.control; msg.msg_flags = 0; msg.msg_name = &peer; msg.msg_namelen = sizeof(peer); msg.msg_iov = &iov; msg.msg_iovlen = 1; iov.iov_base = packet; iov.iov_len = daemon->packet_buff_sz; /* we overwrote the buffer... */ daemon->srv_save = NULL; if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2) return; if (option_bool(OPT_NOWILD)) { addr = listen->iface->addr; mtu = listen->iface->mtu; name = listen->iface->name; } else { struct cmsghdr *cmptr; int check; struct interface_list *ir; if (msg.msg_controllen < sizeof(struct cmsghdr)) return; addr.sa.sa_family = listen->family; #if defined(HAVE_LINUX_NETWORK) if (listen->family == AF_INET) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO) { union { unsigned char *c; struct in_pktinfo *p; } p; p.c = CMSG_DATA(cmptr); addr.in.sin_addr = p.p->ipi_spec_dst; if_index = p.p->ipi_ifindex; } #elif defined(HAVE_SOLARIS_NETWORK) if (listen->family == AF_INET) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) { union { unsigned char *c; struct in_addr *a; unsigned int *i; } p; p.c = CMSG_DATA(cmptr); if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR) addr.in.sin_addr = *(p.a); else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) if_index = *(p.i); } #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) if (listen->family == AF_INET) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) { union { unsigned char *c; struct in_addr *a; struct sockaddr_dl *s; } p; p.c = CMSG_DATA(cmptr); if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR) addr.in.sin_addr = *(p.a); else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) if_index = p.s->sdl_index; } #endif #ifdef HAVE_IPV6 if (listen->family == AF_INET6) { for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == daemon->v6pktinfo) { union { unsigned char *c; struct in6_pktinfo *p; } p; p.c = CMSG_DATA(cmptr); addr.in6.sin6_addr = p.p->ipi6_addr; if_index = p.p->ipi6_ifindex; } } #endif if (!indextoname(listen->tftpfd, if_index, namebuff)) return; name = namebuff; #ifdef HAVE_IPV6 if (listen->family == AF_INET6) check = iface_check(AF_INET6, (struct all_addr *)&addr.in6.sin6_addr, name, &if_index); else #endif check = iface_check(AF_INET, (struct all_addr *)&addr.in.sin_addr, name, &if_index); /* wierd TFTP service override */ for (ir = daemon->tftp_interfaces; ir; ir = ir->next) if (strcmp(ir->interface, name) == 0) break; if (!ir) { if (!daemon->tftp_unlimited || !check) return; #ifdef HAVE_DHCP /* allowed interfaces are the same as for DHCP */ for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) if (tmp->name && (strcmp(tmp->name, name) == 0)) return; #endif } strncpy(ifr.ifr_name, name, IF_NAMESIZE); if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1) mtu = ifr.ifr_mtu; } /* check for per-interface prefix */ for (pref = daemon->if_prefix; pref; pref = pref->next) if (strcmp(pref->interface, name) == 0) prefix = pref->prefix; /* wierd TFTP interfaces disable special options. */ for (ir = daemon->tftp_interfaces; ir; ir = ir->next) if (strcmp(ir->interface, name) == 0) special = 1; #ifdef HAVE_SOCKADDR_SA_LEN addr.sa.sa_len = sa_len(&addr); #endif if (listen->family == AF_INET) addr.in.sin_port = htons(port); #ifdef HAVE_IPV6 else { addr.in6.sin6_port = htons(port); addr.in6.sin6_flowinfo = 0; } #endif if (!(transfer = whine_malloc(sizeof(struct tftp_transfer)))) return; if ((transfer->sockfd = socket(listen->family, SOCK_DGRAM, 0)) == -1) { free(transfer); return; } transfer->peer = peer; transfer->timeout = now + 2; transfer->backoff = 1; transfer->block = 1; transfer->blocksize = 512; transfer->offset = 0; transfer->file = NULL; transfer->opt_blocksize = transfer->opt_transize = 0; transfer->netascii = transfer->carrylf = 0; prettyprint_addr(&peer, pretty_addr); /* if we have a nailed-down range, iterate until we find a free one. */ while (1) { if (bind(transfer->sockfd, &addr.sa, sizeof(addr)) == -1 || #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) setsockopt(transfer->sockfd, SOL_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 || #endif !fix_fd(transfer->sockfd)) { if (errno == EADDRINUSE && daemon->start_tftp_port != 0) { if (++port <= daemon->end_tftp_port) { if (listen->family == AF_INET) addr.in.sin_port = htons(port); #ifdef HAVE_IPV6 else addr.in6.sin6_port = htons(port); #endif continue; } my_syslog(MS_TFTP | LOG_ERR, _("unable to get free port for TFTP")); } free_transfer(transfer); return; } break; } p = packet + 2; end = packet + len; if (ntohs(*((unsigned short *)packet)) != OP_RRQ || !(filename = next(&p, end)) || !(mode = next(&p, end)) || (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0)) len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), pretty_addr); else { if (strcasecmp(mode, "netascii") == 0) transfer->netascii = 1; while ((opt = next(&p, end))) { if (strcasecmp(opt, "blksize") == 0) { if ((opt = next(&p, end)) && (special || !option_bool(OPT_TFTP_NOBLOCK))) { transfer->blocksize = atoi(opt); if (transfer->blocksize < 1) transfer->blocksize = 1; if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4) transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4; /* 32 bytes for IP, UDP and TFTP headers */ if (mtu != 0 && transfer->blocksize > (unsigned)mtu - 32) transfer->blocksize = (unsigned)mtu - 32; transfer->opt_blocksize = 1; transfer->block = 0; } } else if (strcasecmp(opt, "tsize") == 0 && next(&p, end) && !transfer->netascii) { transfer->opt_transize = 1; transfer->block = 0; } } /* cope with backslashes from windows boxen. */ while ((p = strchr(filename, '\\'))) *p = '/'; strcpy(daemon->namebuff, "/"); if (prefix) { if (prefix[0] == '/') daemon->namebuff[0] = 0; strncat(daemon->namebuff, prefix, (MAXDNAME-1) - strlen(daemon->namebuff)); if (prefix[strlen(prefix)-1] != '/') strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff)); if (!special && option_bool(OPT_TFTP_APREF)) { size_t oldlen = strlen(daemon->namebuff); struct stat statbuf; strncat(daemon->namebuff, pretty_addr, (MAXDNAME-1) - strlen(daemon->namebuff)); strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff)); /* remove unique-directory if it doesn't exist */ if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode)) daemon->namebuff[oldlen] = 0; } /* Absolute pathnames OK if they match prefix */ if (filename[0] == '/') { if (strstr(filename, daemon->namebuff) == filename) daemon->namebuff[0] = 0; else filename++; } } else if (filename[0] == '/') daemon->namebuff[0] = 0; strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff)); /* check permissions and open file */ if ((transfer->file = check_tftp_fileperm(&len, prefix, special))) { if ((len = get_block(packet, transfer)) == -1) len = tftp_err_oops(packet, daemon->namebuff); else is_err = 0; } } while (sendto(transfer->sockfd, packet, len, 0, (struct sockaddr *)&peer, sizeof(peer)) == -1 && errno == EINTR); if (is_err) free_transfer(transfer); else { transfer->next = daemon->tftp_trans; daemon->tftp_trans = transfer; } }
static void add_update_server(union mysockaddr *addr, union mysockaddr *source_addr, const char *interface, const char *domain) { struct server *serv; /* See if there is a suitable candidate, and unmark */ for (serv = daemon->servers; serv; serv = serv->next) if ((serv->flags & SERV_FROM_DBUS) && (serv->flags & SERV_MARK)) { if (domain) { if (!(serv->flags & SERV_HAS_DOMAIN) || !hostname_isequal(domain, serv->domain)) continue; } else { if (serv->flags & SERV_HAS_DOMAIN) continue; } serv->flags &= ~SERV_MARK; break; } if (!serv && (serv = whine_malloc(sizeof (struct server)))) { /* Not found, create a new one. */ memset(serv, 0, sizeof(struct server)); if (domain && !(serv->domain = whine_malloc(strlen(domain)+1))) { free(serv); serv = NULL; } else { serv->next = daemon->servers; daemon->servers = serv; serv->flags = SERV_FROM_DBUS; if (domain) { strcpy(serv->domain, domain); serv->flags |= SERV_HAS_DOMAIN; } } } if (serv) { if (interface) strcpy(serv->interface, interface); else serv->interface[0] = 0; if (source_addr->in.sin_family == AF_INET && addr->in.sin_addr.s_addr == 0 && serv->domain) serv->flags |= SERV_NO_ADDR; else { serv->flags &= ~SERV_NO_ADDR; serv->addr = *addr; serv->source_addr = *source_addr; } } }
static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings) { DBusMessageIter iter, array_iter, string_iter; DBusMessage *error = NULL; const char *addr_err; char *dup = NULL; if (!dbus_message_iter_init(message, &iter)) { return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, "Failed to initialize dbus message iter"); } /* check that the message contains an array of arrays */ if ((dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) || (dbus_message_iter_get_element_type(&iter) != (strings ? DBUS_TYPE_STRING : DBUS_TYPE_ARRAY))) { return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, strings ? "Expected array of string" : "Expected array of string arrays"); } mark_servers(SERV_FROM_DBUS); /* array_iter points to each "as" element in the outer array */ dbus_message_iter_recurse(&iter, &array_iter); while (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_INVALID) { const char *str = NULL; union mysockaddr addr, source_addr; int flags = 0; char interface[IF_NAMESIZE]; char *str_addr, *str_domain = NULL; if (strings) { dbus_message_iter_get_basic(&array_iter, &str); if (!str || !strlen (str)) { error = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, "Empty string"); break; } /* dup the string because it gets modified during parsing */ if (dup) free(dup); if (!(dup = str_domain = whine_malloc(strlen(str)+1))) break; strcpy(str_domain, str); /* point to address part of old string for error message */ if ((str_addr = strrchr(str, '/'))) str = str_addr+1; if ((str_addr = strrchr(str_domain, '/'))) { if (*str_domain != '/' || str_addr == str_domain) { error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS, "No domain terminator '%s'", str); break; } *str_addr++ = 0; str_domain++; } else { str_addr = str_domain; str_domain = NULL; } } else { /* check the types of the struct and its elements */ if ((dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_ARRAY) || (dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_STRING)) { error = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, "Expected inner array of strings"); break; } /* string_iter points to each "s" element in the inner array */ dbus_message_iter_recurse(&array_iter, &string_iter); if (dbus_message_iter_get_arg_type(&string_iter) != DBUS_TYPE_STRING) { /* no IP address given */ error = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, "Expected IP address"); break; } dbus_message_iter_get_basic(&string_iter, &str); if (!str || !strlen (str)) { error = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, "Empty IP address"); break; } /* dup the string because it gets modified during parsing */ if (dup) free(dup); if (!(dup = str_addr = whine_malloc(strlen(str)+1))) break; strcpy(str_addr, str); } memset(&addr, 0, sizeof(addr)); memset(&source_addr, 0, sizeof(source_addr)); memset(&interface, 0, sizeof(interface)); /* parse the IP address */ if ((addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, &flags))) { error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS, "Invalid IP address '%s': %s", str, addr_err); break; } /* 0.0.0.0 for server address == NULL, for Dbus */ if (addr.in.sin_family == AF_INET && addr.in.sin_addr.s_addr == 0) flags |= SERV_NO_ADDR; if (strings) { char *p; do { if (str_domain) { if ((p = strchr(str_domain, '/'))) *p++ = 0; } else p = NULL; add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain); } while ((str_domain = p)); } else { /* jump past the address to the domain list (if any) */ dbus_message_iter_next (&string_iter); /* parse domains and add each server/domain pair to the list */ do { str = NULL; if (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING) dbus_message_iter_get_basic(&string_iter, &str); dbus_message_iter_next (&string_iter); add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str); } while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING); } /* jump to next element in outer array */ dbus_message_iter_next(&array_iter); } cleanup_servers(); if (dup) free(dup); return error; }
DBusHandlerResult message_handler(DBusConnection *connection, DBusMessage *message, void *user_data) { char *method = (char *)dbus_message_get_member(message); DBusMessage *reply = NULL; int clear_cache = 0, new_servers = 0; if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect")) { /* string length: "%s" provides space for termination zero */ if (!introspection_xml && (introspection_xml = whine_malloc(strlen(introspection_xml_template) + strlen(daemon->dbus_name)))) sprintf(introspection_xml, introspection_xml_template, daemon->dbus_name); if (introspection_xml) { reply = dbus_message_new_method_return(message); dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection_xml, DBUS_TYPE_INVALID); } } else if (strcmp(method, "GetVersion") == 0) { char *v = VERSION; reply = dbus_message_new_method_return(message); dbus_message_append_args(reply, DBUS_TYPE_STRING, &v, DBUS_TYPE_INVALID); } #ifdef HAVE_LOOP else if (strcmp(method, "GetLoopServers") == 0) { reply = dbus_reply_server_loop(message); } #endif else if (strcmp(method, "SetServers") == 0) { dbus_read_servers(message); new_servers = 1; } else if (strcmp(method, "SetServersEx") == 0) { reply = dbus_read_servers_ex(message, 0); new_servers = 1; } else if (strcmp(method, "SetDomainServers") == 0) { reply = dbus_read_servers_ex(message, 1); new_servers = 1; } else if (strcmp(method, "SetFilterWin2KOption") == 0) { reply = dbus_set_bool(message, OPT_FILTER, "filterwin2k"); } else if (strcmp(method, "SetBogusPrivOption") == 0) { reply = dbus_set_bool(message, OPT_BOGUSPRIV, "bogus-priv"); } else if (strcmp(method, "ClearCache") == 0) clear_cache = 1; else return (DBUS_HANDLER_RESULT_NOT_YET_HANDLED); if (new_servers) { my_syslog(LOG_INFO, _("setting upstream servers from DBus")); check_servers(); if (option_bool(OPT_RELOAD)) clear_cache = 1; } if (clear_cache) clear_cache_and_reload(dnsmasq_time()); method = user_data; /* no warning */ /* If no reply or no error, return nothing */ if (!reply) reply = dbus_message_new_method_return(message); if (reply) { dbus_connection_send (connection, reply, NULL); dbus_message_unref (reply); } return (DBUS_HANDLER_RESULT_HANDLED); }
/* initialisation for dynamic-dir. Set inotify watch for each directory, and read pre-existing files */ void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz) { struct hostsfile *ah; for (ah = daemon->dynamic_dirs; ah; ah = ah->next) { DIR *dir_stream = NULL; struct dirent *ent; struct stat buf; if (!(ah->flags & flag)) continue; if (stat(ah->fname, &buf) == -1 || !(S_ISDIR(buf.st_mode))) { my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"), ah->fname, strerror(errno)); continue; } if (!(ah->flags & AH_WD_DONE)) { ah->wd = inotify_add_watch(daemon->inotifyfd, ah->fname, IN_CLOSE_WRITE | IN_MOVED_TO); ah->flags |= AH_WD_DONE; } /* Read contents of dir _after_ calling add_watch, in the hope of avoiding a race which misses files being added as we start */ if (ah->wd == -1 || !(dir_stream = opendir(ah->fname))) { my_syslog(LOG_ERR, _("failed to create inotify for %s: %s"), ah->fname, strerror(errno)); continue; } while ((ent = readdir(dir_stream))) { size_t lendir = strlen(ah->fname); size_t lenfile = strlen(ent->d_name); char *path; /* ignore emacs backups and dotfiles */ if (lenfile == 0 || ent->d_name[lenfile - 1] == '~' || (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') || ent->d_name[0] == '.') continue; if ((path = whine_malloc(lendir + lenfile + 2))) { strcpy(path, ah->fname); strcat(path, "/"); strcat(path, ent->d_name); /* ignore non-regular files */ if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode)) { if (ah->flags & AH_HOSTS) total_size = read_hostsfile(path, ah->index, total_size, rhash, revhashsz); #ifdef HAVE_DHCP else if (ah->flags & (AH_DHCP_HST | AH_DHCP_OPT)) option_read_dynfile(path, ah->flags); #endif } free(path); } } closedir(dir_stream); } }