825 lines
22 KiB
C
825 lines
22 KiB
C
/* status.c - Allow control of a running server using a socket
|
|
|
|
Copyright (C) 2000, 2001 Thomas Moestl
|
|
Copyright (C) 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2011 Paul A. Rombouts
|
|
|
|
This file is part of the pdnsd package.
|
|
|
|
pdnsd is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
pdnsd is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with pdnsd; see the file COPYING. If not, see
|
|
<http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#ifdef HAVE_ALLOCA_H
|
|
#include <alloca.h>
|
|
#endif
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/utsname.h>
|
|
#include <errno.h>
|
|
#include <pthread.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <stddef.h> /* for offsetof */
|
|
#include "ipvers.h"
|
|
#include "status.h"
|
|
#include "thread.h"
|
|
#include "cache.h"
|
|
#include "error.h"
|
|
#include "servers.h"
|
|
#include "dns_answer.h"
|
|
#include "helpers.h"
|
|
#include "conf-parser.h"
|
|
|
|
#if !defined(HAVE_ALLOCA) && !defined(alloca)
|
|
#define alloca malloc
|
|
#endif
|
|
|
|
|
|
char *sock_path=NULL;
|
|
int stat_sock;
|
|
|
|
|
|
/* Print an error to the socket */
|
|
static int print_serr(int rs, const char *msg)
|
|
{
|
|
uint16_t cmd;
|
|
|
|
DEBUG_MSG("Sending error message to control socket: '%s'\n",msg);
|
|
cmd=htons(1);
|
|
if(write(rs,&cmd,sizeof(cmd))!=sizeof(cmd) ||
|
|
write_all(rs,msg,strlen(msg))<0)
|
|
{
|
|
DEBUG_MSG("Error writing to control socket: %s\n",strerror(errno));
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* Print a success code to the socket */
|
|
static int print_succ(int rs)
|
|
{
|
|
uint16_t cmd;
|
|
|
|
cmd=htons(0);
|
|
if(write(rs,&cmd,sizeof(cmd))!=sizeof(cmd)) {
|
|
DEBUG_MSG("Error writing to control socket: %s\n"
|
|
"Failed to send success code.\n",strerror(errno));
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* Read a cmd short */
|
|
static int read_short(int fh, uint16_t *res)
|
|
{
|
|
uint16_t cmd;
|
|
|
|
if (read(fh,&cmd,sizeof(cmd))!=sizeof(cmd)) {
|
|
/* print_serr(fh,"Bad arg."); */
|
|
return 0;
|
|
}
|
|
*res= ntohs(cmd);
|
|
return 1;
|
|
}
|
|
|
|
/* Read a cmd long */
|
|
static int read_long(int fh, uint32_t *res)
|
|
{
|
|
uint32_t cmd;
|
|
|
|
if (read(fh,&cmd,sizeof(cmd))!=sizeof(cmd)) {
|
|
/* print_serr(fh,"Bad arg."); */
|
|
return 0;
|
|
}
|
|
*res= ntohl(cmd);
|
|
return 1;
|
|
}
|
|
|
|
/* Read a string preceded by a char count.
|
|
A buffer of the right size is allocated to hold the result.
|
|
A return value of 1 means success,
|
|
-1 means the result is undefined (*res is set to NULL),
|
|
0 means read or allocation error.
|
|
*/
|
|
static int read_allocstring(int fh, char **res, unsigned *len)
|
|
{
|
|
uint16_t count;
|
|
char *buf;
|
|
unsigned int nread;
|
|
|
|
if(!read_short(fh,&count)) return 0;
|
|
if(count==(uint16_t)(~0)) {*res=NULL; return -1;}
|
|
if(!(buf=malloc(count+1))) return 0;
|
|
nread=0;
|
|
while(nread<count) {
|
|
ssize_t m=read(fh,buf+nread,count-nread);
|
|
if(m<=0) {free(buf); return 0;}
|
|
nread+=m;
|
|
}
|
|
buf[count]=0;
|
|
*res=buf;
|
|
if(len) *len=count;
|
|
return 1;
|
|
}
|
|
|
|
/* Read a string preceded by a char count.
|
|
Place it in a buffer of size buflen and terminate with a null char.
|
|
A return value of 1 means success, -1 means not defined,
|
|
0 means error (read error, buffer too small).
|
|
*/
|
|
static int read_domain(int fh, char *buf, unsigned int buflen)
|
|
{
|
|
uint16_t count;
|
|
unsigned int nread;
|
|
|
|
if(!read_short(fh,&count)) return 0;
|
|
if(count==(uint16_t)(~0)) return -1;
|
|
if(count >=buflen) return 0;
|
|
nread=0;
|
|
while(nread<count) {
|
|
ssize_t m=read(fh,buf+nread,count-nread);
|
|
if(m<=0) return 0;
|
|
nread+=m;
|
|
}
|
|
buf[count]=0;
|
|
#if 0
|
|
if(count==0 || buf[count-1]!='.') {
|
|
if(count+1>=buflen) return 0;
|
|
buf[count]='.'; buf[count+1]=0;
|
|
}
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
static void *status_thread (void *p)
|
|
{
|
|
THREAD_SIGINIT;
|
|
/* (void)p; */ /* To inhibit "unused variable" warning */
|
|
|
|
if (!global.strict_suid) {
|
|
if (!run_as(global.run_as)) {
|
|
pdnsd_exit();
|
|
}
|
|
}
|
|
|
|
if (listen(stat_sock,5)==-1) {
|
|
log_warn("Error: could not listen on socket: %s.\nStatus readback will be impossible",strerror(errno));
|
|
goto exit_thread;
|
|
}
|
|
for(;;) {
|
|
struct sockaddr_un ra;
|
|
socklen_t res=sizeof(ra);
|
|
int rs;
|
|
if ((rs=accept(stat_sock,(struct sockaddr *)&ra,&res))!=-1) {
|
|
uint16_t cmd;
|
|
DEBUG_MSG("Status socket query pending.\n");
|
|
if (read_short(rs,&cmd)) {
|
|
/* Check magic number in command */
|
|
if((cmd & 0xff00) == CTL_CMDVERNR) {
|
|
const char *errmsg;
|
|
cmd &= 0xff;
|
|
switch(cmd) {
|
|
case CTL_STATS: {
|
|
struct utsname nm;
|
|
DEBUG_MSG("Received STATUS query.\n");
|
|
if(!print_succ(rs))
|
|
break;
|
|
uname(&nm);
|
|
if(fsprintf(rs,"pdnsd-%s running on %s.\n",VERSION,nm.nodename)<0 ||
|
|
report_cache_stat(rs)<0 ||
|
|
report_thread_stat(rs)<0 ||
|
|
report_conf_stat(rs)<0)
|
|
{
|
|
DEBUG_MSG("Error writing to control socket: %s\n"
|
|
"Failed to send status report.\n",strerror(errno));
|
|
}
|
|
}
|
|
break;
|
|
case CTL_SERVER: {
|
|
char *label,*dnsaddr;
|
|
int indx;
|
|
uint16_t cmd2;
|
|
DEBUG_MSG("Received SERVER command.\n");
|
|
if (read_allocstring(rs,&label,NULL)<=0) {
|
|
print_serr(rs,"Error reading server label.");
|
|
break;
|
|
}
|
|
if (!read_short(rs,&cmd2)) {
|
|
print_serr(rs,"Missing up|down|retest.");
|
|
goto free_label_break;
|
|
}
|
|
if(!read_allocstring(rs, &dnsaddr,NULL)) {
|
|
print_serr(rs,"Error reading DNS addresses.");
|
|
goto free_label_break;
|
|
}
|
|
/* Note by Paul Rombouts:
|
|
We are about to access server configuration data.
|
|
Now that the configuration can be changed during run time,
|
|
we should be using locks before accessing server config data, even if it
|
|
is read-only access.
|
|
However, as long as this is the only thread that calls reload_config_file()
|
|
it should be OK to read the server config without locks, but it is
|
|
something to keep in mind.
|
|
*/
|
|
{
|
|
char *endptr;
|
|
indx=strtol(label,&endptr,0);
|
|
if(!*endptr) {
|
|
if (indx<0 || indx>=DA_NEL(servers)) {
|
|
print_serr(rs,"Server index out of range.");
|
|
goto free_dnsaddr_label_break;
|
|
}
|
|
}
|
|
else {
|
|
if (!strcmp(label, "all"))
|
|
indx=-2; /* all servers */
|
|
else
|
|
indx=-1; /* compare names */
|
|
}
|
|
}
|
|
if(cmd2==CTL_S_UP || cmd2==CTL_S_DOWN || cmd2==CTL_S_RETEST) {
|
|
if(!dnsaddr) {
|
|
if (indx==-1) {
|
|
int i;
|
|
for (i=0;i<DA_NEL(servers);++i) {
|
|
char *servlabel=DA_INDEX(servers,i).label;
|
|
if (servlabel && !strcmp(servlabel,label))
|
|
goto found_label;
|
|
}
|
|
print_serr(rs,"Bad server label.");
|
|
goto free_dnsaddr_label_break;
|
|
found_label:;
|
|
}
|
|
if(mark_servers(indx,(indx==-1)?label:NULL,(cmd2==CTL_S_RETEST)?-1:(cmd2==CTL_S_UP))==0)
|
|
print_succ(rs);
|
|
else
|
|
print_serr(rs,"Could not start up or signal server status thread.");
|
|
}
|
|
else { /* Change server addresses */
|
|
if(indx==-2) {
|
|
print_serr(rs,"Can't use label \"all\" to change server addresses.");
|
|
goto free_dnsaddr_label_break;
|
|
}
|
|
if(indx==-1) {
|
|
int i;
|
|
for(i=0;i<DA_NEL(servers);++i) {
|
|
char *servlabel=DA_INDEX(servers,i).label;
|
|
if (servlabel && !strcmp(servlabel,label)) {
|
|
if(indx!=-1) {
|
|
print_serr(rs,"server label must be unique to change server addresses.");
|
|
goto free_dnsaddr_label_break;
|
|
}
|
|
indx=i;
|
|
}
|
|
}
|
|
if(indx==-1) {
|
|
print_serr(rs,"Bad server label.");
|
|
goto free_dnsaddr_label_break;
|
|
}
|
|
}
|
|
{
|
|
char *ipstr,*q=dnsaddr;
|
|
addr_array ar=NULL;
|
|
pdnsd_a addr;
|
|
int err;
|
|
for(;;) {
|
|
for(;;) {
|
|
if(!*q) goto change_servs;
|
|
if(*q!=',' && !isspace(*q)) break;
|
|
++q;
|
|
}
|
|
ipstr=q;
|
|
for(;;) {
|
|
++q;
|
|
if(!*q) break;
|
|
if(*q==',' || isspace(*q)) {*q++=0; break; }
|
|
}
|
|
if(!str2pdnsd_a(ipstr,&addr)) {
|
|
print_serr(rs,"Bad server ip");
|
|
goto free_ar;
|
|
}
|
|
if(!(ar=DA_GROW1(ar))) {
|
|
print_serr(rs,"Out of memory.");
|
|
goto free_dnsaddr_label_break;
|
|
}
|
|
DA_LAST(ar)=addr;
|
|
}
|
|
change_servs:
|
|
err=change_servers(indx,ar,(cmd2==CTL_S_RETEST)?-1:(cmd2==CTL_S_UP));
|
|
if(err==0)
|
|
print_succ(rs);
|
|
else
|
|
print_serr(rs,err==ETIMEDOUT?"Timed out while trying to gain access to server data.":
|
|
err==ENOMEM?"Out of memory.":
|
|
"Could not start up or signal server status thread.");
|
|
free_ar:
|
|
da_free(ar);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
print_serr(rs,"Bad command.");
|
|
|
|
free_dnsaddr_label_break:
|
|
free(dnsaddr);
|
|
free_label_break:
|
|
free(label);
|
|
}
|
|
break;
|
|
case CTL_RECORD: {
|
|
uint16_t cmd2;
|
|
unsigned char name[DNSNAMEBUFSIZE],buf[DNSNAMEBUFSIZE];
|
|
DEBUG_MSG("Received RECORD command.\n");
|
|
if (!read_short(rs,&cmd2))
|
|
goto incomplete_command;
|
|
if (read_domain(rs, charp buf, sizeof(buf))<=0)
|
|
goto incomplete_command;
|
|
if ((errmsg=parsestr2rhn(buf,sizeof(buf),name))!=NULL)
|
|
goto bad_domain_name;
|
|
switch (cmd2) {
|
|
case CTL_R_DELETE:
|
|
del_cache(name);
|
|
print_succ(rs);
|
|
break;
|
|
case CTL_R_INVAL:
|
|
invalidate_record(name);
|
|
print_succ(rs);
|
|
break;
|
|
default:
|
|
print_serr(rs,"Bad command.");
|
|
}
|
|
}
|
|
break;
|
|
case CTL_SOURCE: {
|
|
uint32_t ttl;
|
|
char *fn;
|
|
uint16_t servaliases,flags;
|
|
unsigned char buf[DNSNAMEBUFSIZE],owner[DNSNAMEBUFSIZE];
|
|
|
|
DEBUG_MSG("Received SOURCE command.\n");
|
|
if (read_allocstring(rs,&fn,NULL)<=0) {
|
|
print_serr(rs,"Bad filename name.");
|
|
break;
|
|
}
|
|
if (read_domain(rs, charp buf, sizeof(buf))<=0 ||
|
|
!read_long(rs,&ttl) ||
|
|
!read_short(rs,&servaliases) || /* serve aliases */
|
|
!read_short(rs,&flags)) /* caching flags */
|
|
{
|
|
print_serr(rs,"Malformed or incomplete command.");
|
|
goto free_fn;
|
|
}
|
|
if ((errmsg=parsestr2rhn(buf,sizeof(buf),owner))!=NULL) {
|
|
print_serr(rs,errmsg);
|
|
goto free_fn;
|
|
}
|
|
if (ttl < 0) {
|
|
print_serr(rs, "Bad TTL.");
|
|
goto free_fn;
|
|
}
|
|
if(flags&DF_NEGATIVE) {
|
|
print_serr(rs, "Bad cache flags.");
|
|
goto free_fn;
|
|
}
|
|
{
|
|
char *errmsg;
|
|
if (read_hosts(fn,owner,ttl,flags,servaliases,&errmsg))
|
|
print_succ(rs);
|
|
else {
|
|
print_serr(rs,errmsg?:"Out of memory.");
|
|
free(errmsg);
|
|
}
|
|
}
|
|
free_fn:
|
|
free(fn);
|
|
}
|
|
break;
|
|
case CTL_ADD: {
|
|
uint32_t ttl;
|
|
unsigned sz;
|
|
uint16_t tp,flags,nadr=0;
|
|
unsigned char name[DNSNAMEBUFSIZE],buf[DNSNAMEBUFSIZE],dbuf[2+DNSNAMEBUFSIZE];
|
|
size_t adrbufsz=0;
|
|
unsigned char *adrbuf=NULL;
|
|
|
|
DEBUG_MSG("Received ADD command.\n");
|
|
if (!read_short(rs,&tp))
|
|
goto incomplete_command;
|
|
if (read_domain(rs, charp buf, sizeof(buf))<=0)
|
|
goto incomplete_command;
|
|
if (!read_long(rs,&ttl))
|
|
goto incomplete_command;
|
|
if (!read_short(rs,&flags)) /* caching flags */
|
|
goto incomplete_command;
|
|
if ((errmsg=parsestr2rhn(buf,sizeof(buf),name))!=NULL)
|
|
goto bad_domain_name;
|
|
if (ttl < 0)
|
|
goto bad_ttl;
|
|
if(flags&DF_NEGATIVE)
|
|
goto bad_flags;
|
|
|
|
switch (tp) {
|
|
case T_A:
|
|
sz=sizeof(struct in_addr);
|
|
#if ALLOW_LOCAL_AAAA
|
|
goto read_adress_list;
|
|
case T_AAAA:
|
|
sz=sizeof(struct in6_addr);
|
|
read_adress_list:
|
|
#endif
|
|
if (!read_short(rs,&nadr))
|
|
goto incomplete_command;
|
|
if (!nadr)
|
|
goto bad_arg;
|
|
adrbufsz= nadr * (size_t)sz;
|
|
adrbuf= malloc(adrbufsz);
|
|
if(!adrbuf)
|
|
goto out_of_memory;
|
|
{
|
|
size_t nread=0;
|
|
while(nread<adrbufsz) {
|
|
ssize_t m=read(rs,adrbuf+nread,adrbufsz-nread);
|
|
if(m<=0) {free(adrbuf); goto bad_arg;}
|
|
nread += m;
|
|
}
|
|
}
|
|
break;
|
|
case T_CNAME:
|
|
case T_PTR:
|
|
case T_NS:
|
|
if (read_domain(rs, charp buf, sizeof(buf))<=0)
|
|
goto incomplete_command;
|
|
if ((errmsg=parsestr2rhn(buf,sizeof(buf),dbuf))!=NULL)
|
|
goto bad_domain_name;
|
|
sz=rhnlen(dbuf);
|
|
break;
|
|
case T_MX:
|
|
if (read(rs,dbuf,2)!=2)
|
|
goto bad_arg;
|
|
if (read_domain(rs, charp buf, sizeof(buf))<=0)
|
|
goto incomplete_command;
|
|
if ((errmsg=parsestr2rhn(buf,sizeof(buf),dbuf+2))!=NULL)
|
|
goto bad_domain_name;
|
|
sz=rhnlen(dbuf+2)+2;
|
|
break;
|
|
default:
|
|
goto bad_arg;
|
|
}
|
|
{
|
|
dns_cent_t cent;
|
|
|
|
if (!init_cent(¢, name, 0, 0, flags DBG1)) {
|
|
free(adrbuf);
|
|
goto out_of_memory;
|
|
}
|
|
if(adrbuf) {
|
|
unsigned char *adrp; int i;
|
|
for(adrp=adrbuf,i=0; i<nadr; adrp += sz,++i) {
|
|
if (!add_cent_rr(¢,tp,ttl,0,CF_LOCAL,sz,adrp DBG1)) {
|
|
free_cent(¢ DBG1);
|
|
free(adrbuf);
|
|
goto out_of_memory;
|
|
}
|
|
}
|
|
free(adrbuf);
|
|
}
|
|
else if (!add_cent_rr(¢,tp,ttl,0,CF_LOCAL,sz,dbuf DBG1)) {
|
|
free_cent(¢ DBG1);
|
|
goto out_of_memory;
|
|
}
|
|
|
|
if(cent.qname[0]==1 && cent.qname[1]=='*') {
|
|
/* Wild card record.
|
|
Set the DF_WILD flag for the name with '*.' removed. */
|
|
if(!set_cent_flags(¢.qname[2],DF_WILD)) {
|
|
print_serr(rs,
|
|
"Before defining records for a name with a wildcard"
|
|
" you must first define some records for the name"
|
|
" with '*.' removed.");
|
|
goto cleanup_cent;
|
|
}
|
|
}
|
|
|
|
add_cache(¢);
|
|
print_succ(rs);
|
|
cleanup_cent:
|
|
free_cent(¢ DBG1);
|
|
}
|
|
}
|
|
break;
|
|
case CTL_NEG: {
|
|
uint32_t ttl;
|
|
uint16_t tp;
|
|
unsigned char name[DNSNAMEBUFSIZE],buf[DNSNAMEBUFSIZE];
|
|
|
|
DEBUG_MSG("Received NEG command.\n");
|
|
if (read_domain(rs, charp buf, sizeof(buf))<=0)
|
|
goto incomplete_command;
|
|
if (!read_short(rs,&tp))
|
|
goto incomplete_command;
|
|
if (!read_long(rs,&ttl))
|
|
goto incomplete_command;
|
|
if ((errmsg=parsestr2rhn(buf,sizeof(buf),name))!=NULL) {
|
|
DEBUG_MSG("NEG: received bad domain name.\n");
|
|
goto bad_domain_name;
|
|
}
|
|
if (tp!=255 && PDNSD_NOT_CACHED_TYPE(tp)) {
|
|
DEBUG_MSG("NEG: received bad record type.\n");
|
|
print_serr(rs,"Bad record type.");
|
|
break;
|
|
}
|
|
if (ttl < 0)
|
|
goto bad_ttl;
|
|
{
|
|
dns_cent_t cent;
|
|
|
|
if (tp==255) {
|
|
if (!init_cent(¢, name, ttl, 0, DF_LOCAL|DF_NEGATIVE DBG1))
|
|
goto out_of_memory;
|
|
} else {
|
|
if (!init_cent(¢, name, 0, 0, 0 DBG1))
|
|
goto out_of_memory;
|
|
if (!add_cent_rrset_by_type(¢,tp,ttl,0,CF_LOCAL|CF_NEGATIVE DBG1)) {
|
|
free_cent(¢ DBG1);
|
|
goto out_of_memory;
|
|
}
|
|
}
|
|
add_cache(¢);
|
|
free_cent(¢ DBG1);
|
|
}
|
|
print_succ(rs);
|
|
}
|
|
break;
|
|
case CTL_CONFIG: {
|
|
char *fn,*errmsg;
|
|
DEBUG_MSG("Received CONFIG command.\n");
|
|
if (!read_allocstring(rs,&fn,NULL)) {
|
|
print_serr(rs,"Bad filename name.");
|
|
break;
|
|
}
|
|
if (reload_config_file(fn,&errmsg))
|
|
print_succ(rs);
|
|
else {
|
|
print_serr(rs,errmsg?:"Out of memory.");
|
|
free(errmsg);
|
|
}
|
|
free(fn);
|
|
}
|
|
break;
|
|
case CTL_INCLUDE: {
|
|
char *fn,*errmsg;
|
|
DEBUG_MSG("Received INCLUDE command.\n");
|
|
if (read_allocstring(rs,&fn,NULL)<=0) {
|
|
print_serr(rs,"Bad filename name.");
|
|
break;
|
|
}
|
|
if (read_config_file(fn,NULL,NULL,0,&errmsg))
|
|
print_succ(rs);
|
|
else {
|
|
print_serr(rs,errmsg?:"Out of memory.");
|
|
free(errmsg);
|
|
}
|
|
free(fn);
|
|
}
|
|
break;
|
|
case CTL_EVAL: {
|
|
char *str,*errmsg;
|
|
DEBUG_MSG("Received EVAL command.\n");
|
|
if (!read_allocstring(rs,&str,NULL)) {
|
|
print_serr(rs,"Bad input string.");
|
|
break;
|
|
}
|
|
if (confparse(NULL,str,NULL,NULL,0,&errmsg))
|
|
print_succ(rs);
|
|
else {
|
|
print_serr(rs,errmsg?:"Out of memory.");
|
|
free(errmsg);
|
|
}
|
|
free(str);
|
|
}
|
|
break;
|
|
case CTL_EMPTY: {
|
|
slist_array sla=NULL;
|
|
char *names; unsigned len;
|
|
|
|
DEBUG_MSG("Received EMPTY command.\n");
|
|
if (!read_allocstring(rs,&names,&len)) {
|
|
print_serr(rs,"Bad arguments.");
|
|
break;
|
|
}
|
|
if(names) {
|
|
char *p=names, *last=names+len;
|
|
|
|
while(p<last) {
|
|
int tp;
|
|
char *q;
|
|
slist_t *sl;
|
|
unsigned sz;
|
|
unsigned char rhn[DNSNAMEBUFSIZE];
|
|
|
|
if(*p=='-') {
|
|
tp=C_EXCLUDED;
|
|
++p;
|
|
}
|
|
else {
|
|
tp=C_INCLUDED;
|
|
if(*p=='+') ++p;
|
|
}
|
|
/* skip a possible leading dot. */
|
|
if(p+1<last && *p=='.' && *(p+1)) ++p;
|
|
q=p;
|
|
while(q<last && *q) ++q;
|
|
if ((errmsg=parsestr2rhn(ucharp p,q-p,rhn))!=NULL) {
|
|
DEBUG_MSG("EMPTY: received bad domain name: %s\n",p);
|
|
print_serr(rs,errmsg);
|
|
goto free_sla_names_break;
|
|
}
|
|
sz=rhnlen(rhn);
|
|
if (!(sla=DA_GROW1_F(sla,free_slist_domain))) {
|
|
print_serr(rs,"Out of memory.");
|
|
goto free_names_break;
|
|
}
|
|
sl=&DA_LAST(sla);
|
|
|
|
if (!(sl->domain=malloc(sz))) {
|
|
print_serr(rs,"Out of memory.");
|
|
goto free_sla_names_break;
|
|
}
|
|
memcpy(sl->domain,rhn,sz);
|
|
sl->exact=0;
|
|
sl->rule=tp;
|
|
p = q+1;
|
|
}
|
|
}
|
|
if(empty_cache(sla))
|
|
print_succ(rs);
|
|
else
|
|
print_serr(rs,"Could not lock the cache.");
|
|
free_sla_names_break:
|
|
free_slist_array(sla);
|
|
free_names_break:
|
|
free(names);
|
|
}
|
|
break;
|
|
case CTL_DUMP: {
|
|
int rv,exact=0;
|
|
unsigned char *nm=NULL;
|
|
char buf[DNSNAMEBUFSIZE];
|
|
unsigned char rhn[DNSNAMEBUFSIZE];
|
|
DEBUG_MSG("Received DUMP command.\n");
|
|
if (!(rv=read_domain(rs,buf,sizeof(buf)))) {
|
|
print_serr(rs,"Bad domain name.");
|
|
break;
|
|
}
|
|
if(rv>0) {
|
|
int sz;
|
|
exact=1; nm= ucharp buf; sz=sizeof(buf);
|
|
if(buf[0]=='.' && buf[1]) {
|
|
exact=0; ++nm; --sz;
|
|
}
|
|
if ((errmsg=parsestr2rhn(nm,sz,rhn))!=NULL)
|
|
goto bad_domain_name;
|
|
nm=rhn;
|
|
}
|
|
if(!print_succ(rs))
|
|
break;
|
|
if((rv=dump_cache(rs,nm,exact))<0 ||
|
|
(!rv && fsprintf(rs,"Could not find %s%s in the cache.\n",
|
|
exact?"":nm?"any entries matching ":"any entries",
|
|
nm?buf:"")<0))
|
|
{
|
|
DEBUG_MSG("Error writing to control socket: %s\n",strerror(errno));
|
|
}
|
|
}
|
|
break;
|
|
incomplete_command:
|
|
print_serr(rs,"Malformed or incomplete command.");
|
|
break;
|
|
bad_arg:
|
|
print_serr(rs,"Bad arg.");
|
|
break;
|
|
bad_domain_name:
|
|
print_serr(rs,errmsg);
|
|
break;
|
|
bad_ttl:
|
|
print_serr(rs, "Bad TTL.");
|
|
break;
|
|
bad_flags:
|
|
print_serr(rs, "Bad cache flags.");
|
|
break;
|
|
out_of_memory:
|
|
print_serr(rs,"Out of memory.");
|
|
break;
|
|
default:
|
|
print_serr(rs,"Unknown command.");
|
|
}
|
|
}
|
|
else {
|
|
DEBUG_MSG("Incorrect magic number in status-socket command code: %02x\n",cmd>>8);
|
|
print_serr(rs,"Command code contains incompatible version number.");
|
|
}
|
|
}
|
|
else {
|
|
DEBUG_MSG("short status-socket query\n");
|
|
print_serr(rs,"Command code missing or too short.");
|
|
}
|
|
close(rs);
|
|
usleep_r(100000); /* sleep some time. I do not want the query frequency to be too high. */
|
|
}
|
|
else if (errno!=EINTR) {
|
|
log_warn("Failed to accept connection on status socket: %s. "
|
|
"Status readback will be impossible",strerror(errno));
|
|
break;
|
|
}
|
|
}
|
|
|
|
exit_thread:
|
|
stat_pipe=0;
|
|
close(stat_sock);
|
|
statsock_thrid=main_thrid;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Initialize the status socket
|
|
*/
|
|
void init_stat_sock()
|
|
{
|
|
struct sockaddr_un *sa;
|
|
/* Should I include the terminating null byte in the calculation of the length parameter
|
|
for the socket address? The glibc info page "Details of Local Namespace" tells me I should not,
|
|
yet it is immediately followed by an example that contradicts that.
|
|
The SUN_LEN macro seems to be defined as
|
|
(offsetof(struct sockaddr_un, sun_path) + strlen(sa->sun_path)),
|
|
so I conclude it is not necessary to count the null byte, but it probably makes no
|
|
difference if you do.
|
|
*/
|
|
unsigned int sa_len = (offsetof(struct sockaddr_un, sun_path) + strlitlen("/pdnsd.status") + strlen(global.cache_dir));
|
|
|
|
sa=(struct sockaddr_un *)alloca(sa_len+1);
|
|
stpcpy(stpcpy(sa->sun_path,global.cache_dir),"/pdnsd.status");
|
|
|
|
if (unlink(sa->sun_path)!=0 && errno!=ENOENT) { /* Delete the socket */
|
|
log_warn("Failed to unlink %s: %s.\nStatus readback will be disabled",sa->sun_path, strerror(errno));
|
|
stat_pipe=0;
|
|
return;
|
|
}
|
|
if ((stat_sock=socket(PF_UNIX,SOCK_STREAM,0))==-1) {
|
|
log_warn("Failed to open socket: %s. Status readback will be impossible",strerror(errno));
|
|
stat_pipe=0;
|
|
return;
|
|
}
|
|
sa->sun_family=AF_UNIX;
|
|
#ifdef BSD44_SOCKA
|
|
sa->sun_len=SUN_LEN(sa);
|
|
#endif
|
|
/* Early initialization, so that umask can be used race-free. */
|
|
{
|
|
mode_t old_mask = umask((S_IRWXU|S_IRWXG|S_IRWXO)&(~global.ctl_perms));
|
|
if (bind(stat_sock,(struct sockaddr *)sa,sa_len)==-1) {
|
|
log_warn("Error: could not bind socket: %s.\nStatus readback will be impossible",strerror(errno));
|
|
close(stat_sock);
|
|
stat_pipe=0;
|
|
}
|
|
umask(old_mask);
|
|
}
|
|
|
|
if(stat_pipe) sock_path= strdup(sa->sun_path);
|
|
}
|
|
|
|
/*
|
|
* Start the status socket thread (see above)
|
|
*/
|
|
int start_stat_sock()
|
|
{
|
|
pthread_t st;
|
|
|
|
int rv=pthread_create(&st,&attr_detached,status_thread,NULL);
|
|
if (rv)
|
|
log_warn("Failed to start status thread. The status socket will be unuseable");
|
|
else {
|
|
statsock_thrid=st;
|
|
log_info(2,"Status thread started.");
|
|
}
|
|
return rv;
|
|
}
|