sort.c

来自「此dns服务器是在mydns基础上改写」· C语言 代码 · 共 412 行

C
412
字号
/**************************************************************************************************	$Id: sort.c,v 1.22 2006/01/18 20:46:47 bboy Exp $	Copyright (C) 2002-2005  Don Moore <bboy@bboy.net>	This program is free software; you can redistribute it and/or modify	it under the terms of the GNU General Public License as published by	the Free Software Foundation; either version 2 of the License, or	(at Your option) any later version.	This program is distributed in the hope that it will be useful,	but WITHOUT ANY WARRANTY; without even the implied warranty of	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the	GNU General Public License for more details.	You should have received a copy of the GNU General Public License	along with this program; if not, write to the Free Software	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA**************************************************************************************************/#include "named.h"/* Make this nonzero to enable debugging for this source file */#define	DEBUG_SORT	1#define	RR_IS_RR(R)		((R)->rrtype == DNS_RRTYPE_RR)#define	RR_IS_ADDR(R) 	(RR_IS_RR((R)) && (RR_IS_A((R)) || RR_IS_AAAA((R))))#define	RR_IS_A(R)		(RR_IS_RR((R)) && (((MYDNS_RR *)(R)->rr)->type == DNS_QTYPE_A))#define	RR_IS_AAAA(R)	(RR_IS_RR((R)) && (((MYDNS_RR *)(R)->rr)->type == DNS_QTYPE_AAAA))#define	RR_IS_MX(R)		(RR_IS_RR((R)) && (((MYDNS_RR *)(R)->rr)->type == DNS_QTYPE_MX))#define	RR_IS_SRV(R)	(RR_IS_RR((R)) && (((MYDNS_RR *)(R)->rr)->type == DNS_QTYPE_SRV))#define	RAND(x)			((uint32_t)(((double)(x) + 1.0) * rand() / (RAND_MAX)))/**************************************************************************************************	SORTCMP	Comparison function; sorts records:		1. According to rr->sort_level		2. According to rr->sort1		3. According to rr->sort2		4. According to rr->id**************************************************************************************************/static intsortcmp(RR *rr1, RR *rr2){	if (rr1->sort_level != rr2->sort_level)		return (rr1->sort_level - rr2->sort_level);	if (rr1->sort1 != rr2->sort1)		return (rr1->sort1 - rr2->sort1);	if (rr1->sort2 != rr2->sort2)		return (rr1->sort2 - rr2->sort2);	return (rr1->id - rr2->id);}/*--- sortcmp() ---------------------------------------------------------------------------------*//**************************************************************************************************	RRSORT	Sorts the contents of an RR list according to the value(s) in 'sort'**************************************************************************************************/static RR *rrsort(RR *head, int (*compar)(RR *, RR *)){	RR	*low, *high, *current, *pivot, *temp;	int result;	if (!head)		return (NULL);	current = head;	do	{		current = current->next;		if (!current)			return (head);	} while (!(result = compar(head,current)));	if (result > 0)		pivot = current;	else		pivot = head;	low = high = NULL;	current = head;	while (current)	{		temp = current->next;		if (compar(pivot,current) < 0)		{			current->next = high;			high = current;		}		else		{			current->next = low;			low = current;		}		current = temp;	}	low  = rrsort(low, compar);	high = rrsort(high, compar);	current = temp = low;	while (1)	{		current = current->next;		if (!current)			break;		temp = current;	}	temp->next = high;	return low;}/*--- rrsort() ----------------------------------------------------------------------------------*//**************************************************************************************************	SORT_RRLIST	Sorts the specified RRLIST; reassigns tail node.**************************************************************************************************/static inline voidsort_rrlist(RRLIST *rrlist, int (*compar)(RR *, RR *)){	register RR *node;	/* Do the sort */	rrlist->head = rrsort(rrlist->head, compar);	/* Redetermine list tail */	for (node = rrlist->head; node; node = node->next)		if (!node->next)			rrlist->tail = node;}/*--- sort_rrlist() -----------------------------------------------------------------------------*//**************************************************************************************************	LOAD_BALANCE	Use the 'aux' value to weight multiple A nodes.**************************************************************************************************/static inline voidload_balance(TASK *t, RRLIST *rrlist, datasection_t section, int sort_level){	register RR	*node;											/* Current node */	register int order = 1;										/* Current order */#if DEBUG_ENABLED && DEBUG_SORT	Debug("%s: Load balancing A records in %s section", desctask(t), datasection_str[section]);#endif	/* Hosts with 'aux' values > 50000 are always listed last */	for (node = rrlist->head; node; node = node->next)		if (RR_IS_ADDR(node) && node->sort_level == sort_level && !node->sort1)			if (((MYDNS_RR *)node->rr)->aux >= 50000)				node->sort1 = 50000;	for (;;)	{		register int found = 0;									/* Number of records with this aux */		uint64_t	weights = 0;									/* Sum of aux */		register uint32_t rweight = 0;						/* Random aux */		/* Compute the sum of the weights for all nodes where 'sort1' == 0 */		for (node = rrlist->head; node; node = node->next)			if (RR_IS_ADDR(node) && node->sort_level == sort_level && !node->sort1)			{				found++;				weights += ((MYDNS_RR *)node->rr)->aux;			}		if (!found)			break;		/* Set 'sort1' to 'order' for the first node found where the running sum			value is greater than or equal to 'rweight' */		rweight = RAND(weights);		for (weights = 0, node = rrlist->head; node; node = node->next)			if (RR_IS_ADDR(node) && node->sort_level == sort_level && !node->sort1)			{				weights += ((MYDNS_RR *)node->rr)->aux;				if (weights >= rweight)				{					node->sort1 = 65535 - order++;					break;				}			}	}}/*--- load_balance() ----------------------------------------------------------------------------*//**************************************************************************************************	_SORT_A_RECS	If the request is for 'A' or 'AAAA' and there are multiple A or AAAA records, sort them.	Since this is an A or AAAA record, the answer section contains only addresses.	If any of the RR's have nonzero "aux" values, do load balancing, else do round robin.**************************************************************************************************/static inline void_sort_a_recs(TASK *t, RRLIST *rrlist, datasection_t section, int sort_level){	register RR *node;	register int nonzero_aux = 0;	register int count = 0;										/* Number of nodes at this level */	/* If any addresses have nonzero 'aux' values, do load balancing */	for (count = 0, node = rrlist->head; node; node = node->next)		if (RR_IS_ADDR(node) && node->sort_level == sort_level)		{			count++;			if (((MYDNS_RR *)node->rr)->aux)				nonzero_aux = 1;		}	if (count < 2)													/* Only one node here, don't bother */		return;	t->reply_cache_ok = 0;										/* Don't cache load-balanced replies */	if (nonzero_aux)	{		load_balance(t, rrlist, section, sort_level);	}	else /* Round robin - for address records, set 'sort' to a random number */	{#if DEBUG_ENABLED && DEBUG_SORT		Debug("%s: Sorting A records in %s section (round robin)", desctask(t), datasection_str[section]);#endif		for (node = rrlist->head; node; node = node->next)			if (RR_IS_ADDR(node) && node->sort_level == sort_level)				node->sort1 = RAND(4294967294U);	t->reply_cache_ok = 0;										/* Don't cache load-balanced replies */	}}/*--- _sort_a_recs() ----------------------------------------------------------------------------*//**************************************************************************************************	SORT_A_RECS	If the request is for 'A' or 'AAAA' and there are multiple A or AAAA records, sort them.	Since this is an A or AAAA record, the answer section contains only addresses.	If any of the RR's have nonzero "aux" values, do load balancing, else do round robin.**************************************************************************************************/voidsort_a_recs(TASK *t, RRLIST *rrlist, datasection_t section){	register RR *node;	/* Sort each sort level */	for (node = rrlist->head; node; node = node->next)		if (RR_IS_ADDR(node) && !node->sort2)			_sort_a_recs(t, rrlist, section, node->sort_level);	return (sort_rrlist(rrlist, sortcmp));}/*--- sort_a_recs() -----------------------------------------------------------------------------*//**************************************************************************************************	SORT_MX_RECS	When there are multiple equal-preference MX records, randomize them to help keep the load	equal.**************************************************************************************************/voidsort_mx_recs(TASK *t, RRLIST *rrlist, datasection_t section){	register RR *node;#if DEBUG_ENABLED && DEBUG_SORT	Debug("%s: Sorting MX records in %s section", desctask(t), datasection_str[section]);#endif	/* Set 'sort' to a random number */	for (node = rrlist->head; node; node = node->next)		if (RR_IS_MX(node))		{			node->sort1 = ((MYDNS_RR *)node->rr)->aux;			node->sort2 = RAND(4294967294U);		}	return (sort_rrlist(rrlist, sortcmp));}/*--- sort_mx_recs() ----------------------------------------------------------------------------*//**************************************************************************************************	SORT_SRV_PRIORITY	Sorts one record for a single specified priority.	After calling this function, 'sort2' should be 'weight' for the node affected.	Returns the number of nodes processed (0 means "done with this priority").**************************************************************************************************/static inline intsort_srv_priority(TASK *t, RRLIST *rrlist, datasection_t section, uint32_t priority,						int sort_level, int order){	register RR	*node;											/* Current node */	register int found = 0;										/* Number of records with this priority */	uint64_t	weights = 0;										/* Sum of weights */	register uint32_t rweight = 0;							/* Random weight */#if DEBUG_ENABLED && DEBUG_SORT	Debug("%s: Sorting SRV records in %s section with priority %u",			desctask(t), datasection_str[section], priority);#endif	/* Compute the sum of the weights for all nodes with this priority where 'sort2' == 0 */	for (node = rrlist->head; node; node = node->next)		if (RR_IS_SRV(node) && node->sort_level == sort_level && node->sort1 == priority && !node->sort2)		{			found++;			weights += ((MYDNS_RR *)node->rr)->srv_weight;		}	if (!found)		return (0);	/* Set 'sort2' to 'order' for the first node found at this priority where the running sum		value is greater than or equal to 'rweight' */	rweight = RAND(weights+1);	for (weights = 0, node = rrlist->head; node; node = node->next)		if (RR_IS_SRV(node) && node->sort_level == sort_level			 && node->sort1 == priority && !node->sort2)		{			weights += ((MYDNS_RR *)node->rr)->srv_weight;			if (weights >= rweight)			{				node->sort2 = order;				return (1);			}		}	return (1);}/*--- sort_srv_priority() -----------------------------------------------------------------------*//**************************************************************************************************	_SORT_SRV_RECS	Sorts SRV records at the specified sort level.	1. Sort by priority, lowest to highest.	2. Sort by weight; 0 means "almost never choose me", higher-than-zero yields		increased likelihood of being first.**************************************************************************************************/static inline void_sort_srv_recs(TASK *t, RRLIST *rrlist, datasection_t section, int sort_level){	register RR	*node;											/* Current node */	register int count;											/* Number of SRV nodes on this level */#if DEBUG_ENABLED && DEBUG_SORT	Debug("%s: Sorting SRV records in %s section", desctask(t), datasection_str[section]);#endif	/* Assign 'sort1' to the priority (aux) and 'sort2' to 0 if there's a zero weight, else random */	for (count = 0, node = rrlist->head; node; node = node->next)		if (RR_IS_SRV(node) && node->sort_level == sort_level)		{			count++;			node->sort1 = ((MYDNS_RR *)node->rr)->aux;			if (((MYDNS_RR *)node->rr)->srv_weight == 0)				node->sort2 = 0;			else	         node->sort2 = RAND(4294967294U);		}	if (count < 2)													/* Only one node here, don't bother */		return;	t->reply_cache_ok = 0;										/* Don't cache these replies */	/* Sort a first time, so that the list is ordered by priority/weight */	sort_rrlist(rrlist, sortcmp);	/* Reset 'sort2' to zero for each SRV */	for (node = rrlist->head; node; node = node->next)		if (RR_IS_SRV(node) && node->sort_level == sort_level)			node->sort2 = 0;	/* For each unique priority, sort by weight */	for (node = rrlist->head; node; node = node->next)		if (RR_IS_SRV(node) && node->sort_level == sort_level && !node->sort2)		{			register int priority = node->sort1;			register int order = 1;			while (sort_srv_priority(t, rrlist, section, priority, sort_level, order++))				/* DONOTHING */;		}}/*--- _sort_srv_recs() --------------------------------------------------------------------------*//**************************************************************************************************	SORT_SRV_RECS**************************************************************************************************/voidsort_srv_recs(TASK *t, RRLIST *rrlist, datasection_t section){	register RR	*node;											/* Current node */	/* Sort each sort level */	for (node = rrlist->head; node; )	{		/* _sort_srv_recs() will sort the list, so we need to reset the list each time */		if (RR_IS_SRV(node) && !node->sort2)		{			_sort_srv_recs(t, rrlist, section, node->sort_level);			node = rrlist->head;		}		else			node = node->next;	}}/*--- sort_srv_recs() ---------------------------------------------------------------------------*//* vi:set ts=3: */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?