示例#1
0
/* Mark a set of servers up or down or schedule uptests.
 * If i>=0 only the server section with index i is scanned,
 * if i<0 all sections are scanned.
 * Only sections matching label are actually set. A NULL label matches
 * any section.
 * up=1 or up=0 means mark server up or down, up=-1 means retest.
 *
 * A non-zero return value indicates an error.
 */
int mark_servers(int i, char *label, int up)
{
	int retval=0,n,signal_test;

	pthread_mutex_lock(&servers_lock);

	signal_test=0;
	n=DA_NEL(servers);
	if(i>=0) {
		/* just one section */
		if(i<n) n=i+1;
	}
	else {
		i=0; /* scan all sections */
	}
	for(;i<n;++i) {
		servparm_t *sp=&DA_INDEX(servers,i);
		if(!label || (sp->label && !strcmp(sp->label,label))) {
			int j,m=DA_NEL(sp->atup_a);

			/* If a section with undiscovered root servers is marked up, signal a test. */
			if(m && sp->rootserver>1 && up>0) signal_test=1;

			for(j=0;j<m;++j) {
				atup_t *at=&DA_INDEX(sp->atup_a,j);
				if(up>=0) {
					at->is_up=up;
					at->i_ts=time(NULL);
				}
				else if(at->i_ts) {
					/* A test may take a while, and we don't want to hold
					   up the calling thread.
					   Instead we set the timestamp to zero and signal
					   a condition which should wake up the server test thread.
					*/
					at->i_ts=0;
					signal_test=1;
				}
			}
		}
	}
	if(signal_test) {
		if(pthread_equal(servstat_thrid,main_thrid))
			retval=start_servstat_thread();
		else {
			retest_flag=1;
			retval=pthread_cond_signal(&server_test_cond);
		}
	}

	pthread_mutex_unlock(&servers_lock);
	return retval;
}
示例#2
0
static void free_server_data(servparm_array sa)
{
	int i,n=DA_NEL(sa);
	for(i=0;i<n;++i)
		free_servparm(&DA_INDEX(sa,i));
	da_free(sa);
}
示例#3
0
void free_slist_array(slist_array sla)
{
	int j,m=DA_NEL(sla);
	for(j=0;j<m;++j)
		free(DA_INDEX(sla,j).domain);
	da_free(sla);

}
示例#4
0
static void free_zones(zone_array za)
{
	int i,n=DA_NEL(za);
	for(i=0;i<n;++i)
		free(DA_INDEX(za,i));

	da_free(za);
}
示例#5
0
/* Internal server test. Call with locks applied.
   May test a single server ip or several collectively.
 */
static void retest(int i, int j)
{
  time_t s_ts;
  servparm_t *srv=&DA_INDEX(servers,i);
  int nsrvs=DA_NEL(srv->atup_a);

  if(!nsrvs) return;
  if(j>=0) {
    if(j<nsrvs) nsrvs=j+1;  /* test just one */
  }
  else {
    j=0;        /* test a range of servers */
  }

  if(!scheme_ok(srv)) {
    s_ts=time(NULL);

    for(;j<nsrvs;++j) {
      atup_t *at=&DA_INDEX(srv->atup_a,j);
      at->is_up=0;
      at->i_ts=s_ts;
    }
  }
  else if(srv->uptest==C_NONE) {
    s_ts=time(NULL);

    for(;j<nsrvs;++j) {
	DA_INDEX(srv->atup_a,j).i_ts=s_ts;
    }
  }
  else if(srv->uptest==C_QUERY || (srv->uptest==C_PING && is_inaddr_any(&srv->ping_a))) {  /* test each ip address separately */
    for(;j<nsrvs;++j) {
	atup_t *at=&DA_INDEX(srv->atup_a,j);
	s_ts=time(NULL);
	at->is_up=uptest(srv,j);
	if(signal_interrupt)
	  break;
	at->i_ts=s_ts;
    }
  }
  else {  /* test ip addresses collectively */
    int res;

    s_ts=time(NULL);
    res=uptest(srv,j);
    for(;j<nsrvs;++j) {
      atup_t *at=&DA_INDEX(srv->atup_a,j);
      at->is_up=res;
      if(signal_interrupt && srv->uptest==C_PING)
	continue;
      at->i_ts=s_ts;
    }
  }
}
示例#6
0
/*
 * This can be used to mark a server (or a list of nadr servers) up (up=1) or down (up=0),
 * or to schedule an immediate retest (up=-1).
 * We can't always use indices to identify a server, because we allow run-time
 * configuration of server addresses, so the servers are identified by their IP addresses.
 */ 
void sched_server_test(pdnsd_a *sa, int nadr, int up)
{
	int k,signal_test;
	
	pthread_mutex_lock(&servers_lock);

	signal_test=0;
	/* This obviously isn't very efficient, but nadr should be small
	   and anything else would introduce considerable overhead */
	for(k=0;k<nadr;++k) {
		pdnsd_a *sak= &sa[k];
		int i,n=DA_NEL(servers);
		for(i=0;i<n;++i) {
			servparm_t *sp=&DA_INDEX(servers,i);
			int j,m=DA_NEL(sp->atup_a);
			for(j=0;j<m;++j) {
				atup_t *at=&DA_INDEX(sp->atup_a,j);
				if(equiv_inaddr2(sak,&at->a)) {
					if(up>=0) {
						at->is_up=up;
						at->i_ts=time(NULL);
						DEBUG_PDNSDA_MSG("Marked server %s %s.\n",PDNSDA2STR(sak),up?"up":"down");
					} 
					else if(at->i_ts) {
						/* A test may take a while, and we don't want to hold
						   up the calling thread.
						   Instead we set the timestamp to zero and signal
						   a condition which should wake up the server test thread.
						*/
						at->i_ts=0;
						signal_test=1;
					}
				}
			}
		}
	}
	if(signal_test) pthread_cond_signal(&server_test_cond);

	pthread_mutex_unlock(&servers_lock);
}
示例#7
0
/*
 * Test called by the dns query handlers to handle interval=onquery cases.
 */
void test_onquery()
{
	int i,n,signal_test;
	
	pthread_mutex_lock(&servers_lock);
	schm[0] = '\0';
	signal_test=0;
	n=DA_NEL(servers);
	for (i=0;i<n;++i) {
		servparm_t *sp=&DA_INDEX(servers,i);
		if (sp->interval==-1) {
			if(sp->rootserver<=1)
				retest(i,-1);
			else {
				/* We leave root-server discovery to the server status thread */
				int j,m=DA_NEL(sp->atup_a);
				for(j=0;j<m;++j)
					DA_INDEX(sp->atup_a,j).i_ts=0;
				signal_test=1;
			}
		}
	}

	if(signal_test) {
		int rv;
		if(pthread_equal(servstat_thrid,main_thrid))
			start_servstat_thread();
		else {
			retest_flag=1;
			if((rv=pthread_cond_signal(&server_test_cond))) {
				DEBUG_MSG("test_onquery(): couldn't signal server status thread: %s\n",strerror(rv));
			}
		}
	}

	pthread_mutex_unlock(&servers_lock);
}
示例#8
0
/*
 * Read a configuration file, saving the results in a (separate) global section and servers array,
 * and the cache.
 *
 * char *nm should contain the name of the file to read. If it is NULL, the name of the config file
 *          read during startup is used.
 *
 * globparm_t *global should point to a struct which will be used to store the data of the
 *                    global section(s). If it is NULL, no global sections are allowed in the
 *		      file.
 *
 * servparm_array *servers should point to a dynamic array which will be grown to store the data
 *                         of the server sections. If it is NULL, no server sections are allowed
 *			   in the file.
 *
 * char **errstr is used to return a possible error message.
 *               In case of failure, *errstr will refer to a newly allocated string.
 *
 * read_config_file returns 1 on success, 0 on failure.
 */
int read_config_file(const char *nm, globparm_t *global, servparm_array *servers, int includedepth, char **errstr)
{
	int retval=0;
	const char *conftype= (global?"config":"include");
	FILE *in;

	if (nm==NULL)
		nm=conf_file;

	if (!(in=fopen(nm,"r"))) {
		if(asprintf(errstr,"Error: Could not open %s file %s: %s",conftype,nm,strerror(errno))<0)
			*errstr=NULL;
		return 0;
	}
	if(global || servers) {
		/* Check restrictions on ownership and permissions of config file. */
		int fd=fileno(in);
		struct stat sb;

		/* Note by Paul Rombouts: I am using fstat() instead of stat() here to
		   prevent a possible exploitable race condition */
		if (fd==-1 || fstat(fd,&sb)!=0) {
			if(asprintf(errstr,
				    "Error: Could not stat %s file %s: %s",
				    conftype,nm,strerror(errno))<0)
				*errstr=NULL;
			goto close_file;
		}
	}

	retval=confparse(in,NULL,global,servers,includedepth,errstr);
close_file:
	if(fclose(in) && retval) {
		if(asprintf(errstr,"Error: Could not close %s file %s: %s",
			    conftype,nm,strerror(errno))<0)
			*errstr=NULL;
		return 0;
	}
	if(retval && servers && !DA_NEL(*servers)) {
		if(asprintf(errstr,"Error: no server sections defined in config file %s",nm)<0)
			*errstr=NULL;
		return 0;
	}
	return retval;
}
示例#9
0
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(&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(&cent,tp,ttl,0,CF_LOCAL,sz,adrp  DBG1)) {
									free_cent(&cent  DBG1);
									free(adrbuf);
									goto out_of_memory;
								}
							}
							free(adrbuf);
						}
						else if (!add_cent_rr(&cent,tp,ttl,0,CF_LOCAL,sz,dbuf  DBG1)) {
							free_cent(&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(&cent.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(&cent);
						print_succ(rs);
					cleanup_cent:
						free_cent(&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(&cent, name, ttl, 0, DF_LOCAL|DF_NEGATIVE  DBG1))
								goto out_of_memory;
						} else {
							if (!init_cent(&cent, name, 0, 0, 0  DBG1))
								goto out_of_memory;
							if (!add_cent_rrset_by_type(&cent,tp,ttl,0,CF_LOCAL|CF_NEGATIVE  DBG1)) {
								free_cent(&cent  DBG1);
								goto out_of_memory;
							}
						}
						add_cache(&cent);
						free_cent(&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 {
示例#10
0
/*
 * Re-Read the configuration file.
 * Return 1 on success, 0 on failure.
 * In case of failure, the old configuration will be unchanged (although the cache may not) and
 * **errstr will refer to a newly allocated string containing an error message.
 */
int reload_config_file(const char *nm, char **errstr)
{
	globparm_t global_new;
	servparm_array servers_new;

	global_new=global;
	global_new.cache_dir=NULL;
	global_new.pidfile=NULL;
	global_new.scheme_file=NULL;
	global_new.deleg_only_zones=NULL;
	global_new.onquery=0;
	servers_new=NULL;
	if(read_config_file(nm,&global_new,&servers_new,0,errstr)) {
		if(global_new.cache_dir && strcmp(global_new.cache_dir,global.cache_dir)) {
			*errstr=strdup("Cannot reload config file: the specified cache_dir directory has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
		if(global_new.pidfile && (!global.pidfile || strcmp(global_new.pidfile,global.pidfile))) {
			*errstr=strdup("Cannot reload config file: the specified pid_file has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
		if(global_new.scheme_file && strcmp(global_new.scheme_file,global.scheme_file)) {
			*errstr=strdup("Cannot reload config file: the specified scheme_file has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
		if(global_new.port!=global.port) {
			*errstr=strdup("Cannot reload config file: the specified server_port has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
		if(!ADDR_EQUIV(&global_new.a,&global.a)) {
			*errstr=strdup("Cannot reload config file: the specified interface address (server_ip) has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
#ifdef ENABLE_IPV6
		if(!IN6_ARE_ADDR_EQUAL(&global_new.ipv4_6_prefix,&global.ipv4_6_prefix)) {
			*errstr=strdup("Cannot reload config file: the specified ipv4_6_prefix has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
#endif
		if(strcmp(global_new.run_as,global.run_as)) {
			*errstr=strdup("Cannot reload config file: the specified run_as id has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
		if(global_new.daemon!=global.daemon) {
			*errstr=strdup("Cannot reload config file: the daemon option has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
		if(global_new.debug!=global.debug) {
			*errstr=strdup("Cannot reload config file: the debug option has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
		if(global_new.stat_pipe!=global.stat_pipe) {
			*errstr=strdup("Cannot reload config file: the status_ctl option has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
		if(global_new.notcp!=global.notcp) {
			*errstr=strdup("Cannot reload config file: the tcp_server option has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
		if(global_new.strict_suid!=global.strict_suid) {
			*errstr=strdup("Cannot reload config file: the strict_setuid option has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
		if(global_new.ctl_perms!=global.ctl_perms) {
			*errstr=strdup("Cannot reload config file: the specified ctl_perms has changed.\n"
				       "Try restarting pdnsd instead.");
			goto cleanup_return;
		}
		if(ping_isocket==-1
#ifdef ENABLE_IPV6
		   && ping6_isocket==-1
#endif
		  ) {
			int i,n=DA_NEL(servers_new);
			for (i=0;i<n;++i) {
				if (DA_INDEX(servers_new,i).uptest==C_PING) {
					if(asprintf(errstr,"Cannot reload config file: the ping socket is not initialized"
						    " and the new config contains uptest=ping in server section %i.\n"
						    "Try restarting pdnsd instead.",i)<0)
						*errstr=NULL;
					goto cleanup_return;
				}
			}
		}

		/* we need exclusive access to the server data to make the changes */
		/* Wait at most 60 seconds to obtain a lock. */
		if(!exclusive_lock_server_data(60)) {
			*errstr=strdup("Cannot reload config file: Timed out while waiting for access to config data.");
			goto cleanup_return;
		}
		free(global_new.cache_dir); global_new.cache_dir=global.cache_dir;
		free(global_new.pidfile); global_new.pidfile=global.pidfile;
		free(global_new.scheme_file); global_new.scheme_file=global.scheme_file;
		free_zones(global.deleg_only_zones);
		global=global_new;

		free_server_data(servers);
		servers=servers_new;
		/* schedule a retest to check which servers are up,
		   and free the lock. */
		exclusive_unlock_server_data(1);

		return 1;
	}

 cleanup_return:
	free(global_new.cache_dir);
	free(global_new.pidfile);
	free(global_new.scheme_file);
	free_zones(global_new.deleg_only_zones);
	free_server_data(servers_new);
	return 0;
}
示例#11
0
/*
  Change addresses of servers during runtime.
  i is the number of the server section to change.
  ar should point to an array of IP addresses (may be NULL).
  up=1 or up=0 means mark server up or down afterwards,
  up=-1 means retest.

  A non-zero return value indicates an error.
*/
int change_servers(int i, addr_array ar, int up)
{
	int retval=0,j,change,signal_test;
	int n;
	servparm_t *sp;

	pthread_mutex_lock(&servers_lock);

	signal_test=0;
	change=0;
	n=DA_NEL(ar);
	sp=&DA_INDEX(servers,i);
	if(n != DA_NEL(sp->atup_a) || sp->rootserver>1)
		change=1;
	else {
		int j;
		for(j=0;j<n;++j)
			if(!same_inaddr2(&DA_INDEX(ar,j),&DA_INDEX(sp->atup_a,j).a)) {
				change=1;
				break;
			}
	}
	if(change) {
		/* we need exclusive access to the server data to make the changes */
		struct timeval now;
		struct timespec timeout;
		atup_array ata;

		if(server_status_ping>0 && !pthread_equal(servstat_thrid,main_thrid)) {
			int err;
			/* Try to interrupt server status thread to prevent delays. */
			DEBUG_MSG("Sending server status thread an interrupt signal.\n");
			if((err=pthread_kill(servstat_thrid,statusintsig))) {
				DEBUG_MSG("pthread_kill failed: %s\n",strerror(err));
			}
		}

		DEBUG_MSG("Changing IPs of server section #%d\n",i);
		gettimeofday(&now,NULL);
		timeout.tv_sec = now.tv_sec + 60;     /* time out after 60 seconds */
		timeout.tv_nsec = now.tv_usec * 1000;
		while (server_data_users>0) {
			if(pthread_cond_timedwait(&server_data_cond, &servers_lock, &timeout) == ETIMEDOUT) {
				retval=ETIMEDOUT;
				goto unlock_mutex;
			}
		}

		ata= DA_CREATE(atup_array, n);
		if(!ata) {
			log_warn("Out of memory in change_servers().");
			retval=ENOMEM;
			goto unlock_mutex;
		}
		da_free(sp->atup_a);
		sp->atup_a=ata;
		/* Stop trying to discover rootservers
		   if we set the addresses using this routine. */
		if(sp->rootserver>1) sp->rootserver=1;
	}

	for(j=0; j<n; ++j) {
		atup_t *at = &DA_INDEX(sp->atup_a,j);
		if(change) {
			SET_PDNSD_A2(&at->a, &DA_INDEX(ar,j));
			at->is_up=sp->preset;
		}
		if(up>=0) {
			at->is_up=up;
			at->i_ts=time(NULL);
		}
		else if(change || at->i_ts) {
			/* A test may take a while, and we don't want to hold
			   up the calling thread.
			   Instead we set the timestamp to zero and signal
			   a condition which should wake up the server test thread.
			*/
			at->i_ts=0;
			signal_test=1;
		}
	}

	if(signal_test) {
		if(pthread_equal(servstat_thrid,main_thrid))
			retval=start_servstat_thread();
		else {
			retest_flag=1;
			retval=pthread_cond_signal(&server_test_cond);
		}
	}

 unlock_mutex:
	pthread_mutex_unlock(&servers_lock);
	return retval;
}
示例#12
0
/*
 * Refresh the server status by pinging or testing the interface in the given interval.
 * Note that you may get inaccuracies in the dimension of the ping timeout or the runtime
 * of your uptest command if you have uptest=ping or uptest=exec for at least one server.
 * This happens when all the uptests for the first n servers take more time than the inteval
 * of n+1 (or 0 when n+1>servnum). I do not think that these delays are critical, so I did
 * not to anything about that (because that may also be costly).
 */
void *servstat_thread(void *p)
{
	struct sigaction action;
	int keep_testing;

	/* (void)p; */  /* To inhibit "unused variable" warning */

	THREAD_SIGINIT;

	pthread_mutex_lock(&servers_lock);
	/* servstat_thrid=pthread_self(); */

	signal_interrupt=0;
	action.sa_handler = sigint_handler;
	sigemptyset(&action.sa_mask);
	action.sa_flags = 0;
	if(sigaction(statusintsig, &action, NULL) == 0) {
		sigset_t smask;
		sigemptyset(&smask);
		sigaddset(&smask, statusintsig); 
		pthread_sigmask(SIG_UNBLOCK,&smask,NULL);
	}
	else {
		log_warn("Cannot install signal handler for server status thread: %s\n",strerror(errno));
	}

	for(;;) {
		do {
			int i,n;
			keep_testing=0;
			retest_flag=0;
			schm[0] = '\0';
			n=DA_NEL(servers);
			for (i=0;i<n;++i) {
				servparm_t *sp=&DA_INDEX(servers,i);
				int j,m;
				if(sp->rootserver==2) {
					/* First get addresses of root servers. */
					addr2_array adrs;
					int l, one_up=0;

					if(!scheme_ok(sp)) {
						time_t now=time(NULL);
						m=DA_NEL(sp->atup_a);
						for(j=0;j<m;++j)
							DA_INDEX(sp->atup_a,j).i_ts=now;
					} else if(sp->uptest==C_PING || sp->uptest==C_QUERY) {
						/* Skip ping or query tests until after discovery. */
						if(sp->interval>0)
							one_up= DA_NEL(sp->atup_a);
						else {
							time_t now=time(NULL);
							m=DA_NEL(sp->atup_a);
							for(j=0;j<m;++j) {
								atup_t *at=&DA_INDEX(sp->atup_a,j);
								if(at->is_up || at->i_ts==0)
									one_up=1;
								at->i_ts=now;
							}
						}
					}
					else {
						retest(i,-1);

						m=DA_NEL(sp->atup_a);
						for(j=0;j<m;++j) {
							if(DA_INDEX(sp->atup_a,j).is_up) {
								one_up=1;
								break;
							}
						}
					}

					if(!one_up) {
						if (needs_intermittent_testing(sp)) keep_testing=1;
						continue;
					}

					DEBUG_MSG("Attempting to discover root servers for server section #%d.\n",i);
					adrs=resolv_rootserver_addrs(sp->atup_a,sp->port,sp->timeout);
					l= DA_NEL(adrs);
					if(l>0) {
						struct timeval now;
						struct timespec timeout;
						atup_array ata;
						DEBUG_MSG("Filling server section #%d with %d root server addresses.\n",i,l);
						gettimeofday(&now,NULL);
						timeout.tv_sec = now.tv_sec + 60;     /* time out after 60 seconds */
						timeout.tv_nsec = now.tv_usec * 1000;
						while (server_data_users>0) {
							if(pthread_cond_timedwait(&server_data_cond, &servers_lock, &timeout) == ETIMEDOUT) {
								DEBUG_MSG("Timed out while waiting for exclusive access to server data"
									  " to set root server addresses of server section #%d\n",i);
								da_free(adrs);
								keep_testing=1;
								continue;
							}
						}
						ata = DA_CREATE(atup_array, l);
						if(!ata) {
							log_warn("Out of memory in servstat_thread() while discovering root servers.");
							da_free(adrs);
							keep_testing=1;
							continue;
						}
						for(j=0; j<l; ++j) {
							atup_t *at = &DA_INDEX(ata,j);
							at->a = DA_INDEX(adrs,j);
							at->is_up=sp->preset;
							at->i_ts= sp->interval<0 ? time(NULL): 0;
						}
						da_free(sp->atup_a);
						sp->atup_a=ata;
						da_free(adrs);
						/* Successfully set IP addresses for this server section. */
						sp->rootserver=1;
					}
					else {
						DEBUG_MSG("Failed to discover root servers in servstat_thread() (server section #%d).\n",i);
						if(adrs) da_free(adrs);
						if(DA_NEL(sp->atup_a)) keep_testing=1;
						continue;
					}
				}

				if (needs_testing(sp)) keep_testing=1;
				m=DA_NEL(sp->atup_a);
				for(j=0;j<m;++j)
					if(DA_INDEX(sp->atup_a,j).i_ts)
						goto individual_tests;
				/* Test collectively */
				if(!signal_interrupt) retest(i,-1);
				continue;

			individual_tests:
				for(j=0; !signal_interrupt && j<m; ++j) {
					time_t ts=DA_INDEX(sp->atup_a,j).i_ts, now;

					if (ts==0 /* Always test servers with timestamp 0 */ ||
					    (needs_intermittent_testing(sp) &&
					     ((now=time(NULL))-ts>sp->interval ||
					      ts>now /* kluge for clock skew */)))
					{ 
						retest(i,j);
					}
				}
			}
		} while(!signal_interrupt && retest_flag);

		signal_interrupt=0;

		/* Break the loop and exit the thread if it is no longer needed. */
		if(!keep_testing) break;

		{
			struct timeval now;
			struct timespec timeout;
			time_t minwait;
			int i,n,retval;

			gettimeofday(&now,NULL);
			minwait=3600; /* Check at least once every hour. */
			n=DA_NEL(servers);
			for (i=0;i<n;++i) {
				servparm_t *sp=&DA_INDEX(servers,i);
				int j,m=DA_NEL(sp->atup_a);
				for(j=0;j<m;++j) {
					time_t ts= DA_INDEX(sp->atup_a,j).i_ts;
					if(ts==0) {
						/* Test servers with timestamp 0 without delay */
						if(minwait > 0) minwait=0;
					}
					else if(needs_intermittent_testing(sp)) {
						time_t wait= ts + sp->interval - now.tv_sec;
						if(wait < minwait) minwait=wait;
					}
				}
			}
			timeout.tv_sec = now.tv_sec;
			if(minwait>0)
				timeout.tv_sec += minwait;
			timeout.tv_nsec = now.tv_usec * 1000 + 500000000;  /* wait at least half a second. */
			if(timeout.tv_nsec>=1000000000) {
				timeout.tv_nsec -= 1000000000;
				++timeout.tv_sec;
			}
			/* While we wait for a server_test_cond condition or a timeout
			   the servers_lock mutex is unlocked, so other threads can access
			   server data
			*/
			retval=pthread_cond_timedwait(&server_test_cond, &servers_lock, &timeout);
			DEBUG_MSG("Server status thread woke up (%s signal).\n",
				  retval==0?"test condition":retval==ETIMEDOUT?"timer":retval==EINTR?"interrupt":"error");
		}
	}

	/* server status thread no longer needed. */
	servstat_thrid=main_thrid;
	pthread_mutex_unlock(&servers_lock);
	DEBUG_MSG("Server status thread exiting.\n");
	return NULL;
}
示例#13
0
文件: conff.c 项目: LazyZhu/pdnsd-1
/*
 * Read a configuration file, saving the results in a (separate) global section and servers array,
 * and the cache.
 *
 * char *nm should contain the name of the file to read. If it is NULL, the name of the config file
 *          read during startup is used.
 *
 * globparm_t *global should point to a struct which will be used to store the data of the
 *                    global section(s). If it is NULL, no global sections are allowed in the
 *		      file.
 *
 * servparm_array *servers should point to a dynamic array which will be grown to store the data
 *                         of the server sections. If it is NULL, no server sections are allowed
 *			   in the file.
 *
 * char **errstr is used to return a possible error message.
 *               In case of failure, *errstr will refer to a newly allocated string.
 *
 * read_config_file returns 1 on success, 0 on failure.
 */
int read_config_file(const char *nm, globparm_t *global, servparm_array *servers, int includedepth, char **errstr)
{
	int retval=0;
	const char *conftype= (global?"config":"include");
	FILE *in;

	if (nm==NULL)
		nm=conf_file;

	if (!(in=fopen(nm,"r"))) {
		if(asprintf(errstr,"Error: Could not open %s file %s: %s",conftype,nm,strerror(errno))<0)
			*errstr=NULL;
		return 0;
	}
	if(global || servers) {
		/* Check restrictions on ownership and permissions of config file. */
		int fd=fileno(in);
		struct stat sb;

		/* Note by Paul Rombouts: I am using fstat() instead of stat() here to
		   prevent a possible exploitable race condition */
		if (fd==-1 || fstat(fd,&sb)!=0) {
			if(asprintf(errstr,
				    "Error: Could not stat %s file %s: %s",
				    conftype,nm,strerror(errno))<0)
				*errstr=NULL;
			goto close_file;
		}
		else if (sb.st_uid!=init_uid) {
			/* Note by Paul Rombouts:
			   Perhaps we should use getpwuid_r() instead of getpwuid(), which is not necessarily thread safe.
			   As long as getpwuid() is only used by only one thread, it should be OK,
			   but it is something to keep in mind.
			*/
			struct passwd *pws;
			char owner[24],user[24];
			if((pws=getpwuid(sb.st_uid)))
				strncp(owner,pws->pw_name,sizeof(owner));
			else
				sprintf(owner,"%i",sb.st_uid);
			if((pws=getpwuid(init_uid)))
				strncp(user,pws->pw_name,sizeof(user));
			else
				sprintf(user,"%i",init_uid);
			if(asprintf(errstr,
				    "Error: %s file %s is owned by '%s', but pdnsd was started as user '%s'.",
				    conftype,nm,owner,user)<0)
				*errstr=NULL;
			goto close_file;
		}
		else if ((sb.st_mode&(S_IWGRP|S_IWOTH))) {
			if(asprintf(errstr,
				    "Error: Bad %s file permissions: file %s must be only writeable by the user.",
				    conftype,nm)<0)
				*errstr=NULL;
			goto close_file;
		}
	}

	retval=confparse(in,NULL,global,servers,includedepth,errstr);
close_file:
	if(fclose(in) && retval) {
		if(asprintf(errstr,"Error: Could not close %s file %s: %s",
			    conftype,nm,strerror(errno))<0)
			*errstr=NULL;
		return 0;
	}
	if(retval && servers && !DA_NEL(*servers)) {
		if(asprintf(errstr,"Error: no server sections defined in config file %s",nm)<0)
			*errstr=NULL;
		return 0;
	}
	return retval;
}