1 /* distcache, Distributed Session Caching technology
2  * Copyright (C) 2000-2003  Geoff Thorpe, and Cryptographic Appliances, Inc.
3  * Copyright (C) 2004       The Distcache.org project
4  *
5  * This library is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU Lesser General Public License as published by the Free
7  * Software Foundation; using version 2.1 of the License. The copyright holders
8  * may elect to allow the application of later versions of the License to this
9  * software, please contact the author (geoff@distcache.org) if you wish us to
10  * review any later version released by the Free Software Foundation.
11  *
12  * This library is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this library; if not, write to the Free Software Foundation, Inc.,
19  * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21 
22 #include "swamp.h"
23 
24 /*************/
25 /* Constants */
26 /*************/
27 
28 /* Do we do reference count debugging on dist_pattern? */
29 /* #define REF_DEBUG */
30 
31 /* Maximum number of servers we store in a list */
32 #define MAX_SERVERS 1000 /* XXX arbitary */
33 
34 /* Maximum number of server distribution patterns to allow. */
35 #define MAX_DISTRIBUTE_PATTERNS 1000 /* XXX arbitary */
36 
37 /******************************/
38 /* Un-opaque our opaque types */
39 /******************************/
40 
41 /* This type represents the pattern and order of servers to swamp */
42 struct st_dist_pattern {
43 	NAL_ADDRESS *items[MAX_SERVERS];
44 	unsigned int num;
45 	unsigned int idx[MAX_DISTRIBUTE_PATTERNS];
46 	unsigned int period;
47 	unsigned int references;
48 	unsigned int start_idx;
49 };
50 
51 /**************************/
52 /* dist_pattern functions */
53 /**************************/
54 
55 /* A function used in the distribute_cursor code to "get" a reference to an
56  * existing pattern. */
dist_pattern_up(dist_pattern * p)57 void dist_pattern_up(dist_pattern *p)
58 {
59 	(p->references)++;
60 #ifdef REF_DEBUG
61 	printf("REF_DEBUG: dist_pattern_up(%08x), ref count now %u\n",
62 			(unsigned int)p, p->references);
63 #endif
64 }
65 
66 /* Create/initialise a distribution_pattern structure ready for use */
dist_pattern_new(void)67 dist_pattern *dist_pattern_new(void)
68 {
69 	dist_pattern *p = SYS_malloc(dist_pattern, 1);
70 	if(!p)
71 		return NULL;
72 	p->num = 0;
73 	p->period = 0;
74 	p->references = 1;
75 	p->start_idx = 0;
76 #ifdef REF_DEBUG
77 	printf("REF_DEBUG: dist_pattern_new(%08x), ref count now %u\n",
78 			(unsigned int)p, p->references);
79 #endif
80 	return p;
81 }
82 
dist_pattern_free(dist_pattern * p)83 void dist_pattern_free(dist_pattern *p)
84 {
85 	(p->references)--;
86 #ifdef REF_DEBUG
87 	printf("REF_DEBUG: dist_pattern_free(%08x), ref count now %u\n",
88 			(unsigned int)p, p->references);
89 #endif
90 	if(p->references < 0) {
91 		assert(NULL == "shouldn't happen!");
92 		abort();
93 	} else if(p->references == 0)
94 		SYS_free(dist_pattern, p);
95 }
96 
dist_pattern_get_start_idx(dist_pattern * p)97 unsigned int dist_pattern_get_start_idx(dist_pattern *p)
98 {
99 	unsigned int toret = p->start_idx++;
100 	if(p->start_idx >= p->period)
101 		p->start_idx = 0;
102 	return toret;
103 }
104 
105 /* Return the number of entires in the distribution pattern structure */
dist_pattern_period(dist_pattern * dist)106 unsigned int dist_pattern_period(dist_pattern *dist)
107 {
108 	return dist->period;
109 }
110 
111 /* Return the number of servers in the distribution pattern structure */
dist_pattern_num(dist_pattern * dist)112 unsigned int dist_pattern_num(dist_pattern *dist)
113 {
114 	return dist->num;
115 }
116 
117 /* Return the address of the server corresponding to the "idx"th entry in the
118  * distribution pattern, or NULL for error */
dist_pattern_get(const dist_pattern * dist,unsigned int idx)119 const NAL_ADDRESS *dist_pattern_get(const dist_pattern *dist,
120 					unsigned int idx)
121 {
122 	if(idx >= dist->period)
123 		return NULL;
124 	return dist->items[dist->idx[idx]];
125 }
126 
127 /* Parses an "<hostname>:<port>" string address and places it on the top of a
128  * server_list stack */
dist_pattern_push_address(dist_pattern * dist,const char * address)129 int dist_pattern_push_address(dist_pattern *dist, const char *address)
130 {
131 	NAL_ADDRESS *sa;
132 
133 	if((dist->num >= MAX_SERVERS) || ((sa = NAL_ADDRESS_new()) == NULL))
134 		return 0;
135 	if(!NAL_ADDRESS_create(sa, address, SWAMP_BUFFER_SIZE)) {
136 		NAL_ADDRESS_free(sa);
137 		return 0;
138 	}
139 	/* Parsing went OK, add this server address to our list */
140 	dist->items[dist->num++] = sa;
141 	return 1;
142 }
143 
dist_pattern_push(dist_pattern * dist,unsigned int val)144 static dist_pattern_error_t dist_pattern_push(dist_pattern *dist,
145 					unsigned int val)
146 {
147 	/* Is there room in the array? */
148 	if(dist->period >= MAX_DISTRIBUTE_PATTERNS)
149 		return ERR_DIST_PAT_ARRAY_FULL;
150 
151 	/* 'val' must between 1 and (dist->num) */
152 	if(!val || (val > dist->num))
153 		return ERR_DIST_PAT_VALUE_OUT_OF_RANGE;
154 
155 	/* Everything checks out - place the server number at the end of the
156 	 * array. Note that we reduce the value by one as internally, servers
157 	 * are numbered from 0 onwards. */
158 	dist->idx[dist->period++] = val - 1;
159 
160 	return ERR_DIST_PAT_OKAY;
161 }
162 
163 /* Parse a distribution pattern string. The format is a bunch of values
164  * representing server numbers delimited by spaces and/or commas. e.g.
165  *     "1, 2, 3, 4,5 6 7 8,"
166  *
167  * Values are expected to between "1" and the number of servers. If something
168  * fails, a suitable error code is returned (see also
169  * dist_pattern_error_string()).
170  *
171  * An empty string is valid - a canonical pattern is generated from the server
172  * list (if there 5 servers, a pattern of "1,2,3,4,5" is assumed). */
dist_pattern_parse(dist_pattern * dist,const char * dist_str)173 dist_pattern_error_t dist_pattern_parse(dist_pattern *dist,
174 				const char *dist_str)
175 {
176 	char *res;
177 	unsigned long val;
178 	tokeniser_t tok;
179 	dist_pattern_error_t ret = ERR_DIST_PAT_OKAY;
180 
181 	assert(dist);
182 
183 	/* If 'dist_str' is NULL, generate a canonical pattern */
184 	if(!dist_str) {
185 		unsigned int loop = 0;
186 		while(loop < dist->num) {
187 			ret = dist_pattern_push(dist, ++loop);
188 			if(ret != ERR_DIST_PAT_OKAY)
189 				return ret;
190 		}
191 		return ret;
192 	}
193 
194 	/* Now process the (non-NULL) 'dist_str' */
195 	init_tokeniser(&tok, dist_str, ", ");
196 	while((res = do_tokenising(&tok)) != NULL) {
197 		if(!int_substrtoul(res, &val, ", ")) {
198 			ret = ERR_DIST_PAT_INVALID_SYNTAX;
199 			break;
200 		}
201 		ret = dist_pattern_push(dist, val);
202 		if(ret != ERR_DIST_PAT_OKAY)
203 			break;
204 	}
205 	free_tokeniser(&tok);
206 	return ret;
207 }
208 
209 /* Return a string briefly describing the problem reported by "err". N.B.
210  * this will also return something for "ERR_DIST_PAT_OKAY" which could confuse
211  * people; e.g. "error with distribute pattern: no error".
212  *
213  * This function is guaranteed to return a valid string (even if it is empty).
214  */
dist_pattern_error_string(dist_pattern_error_t err)215 const char *dist_pattern_error_string(dist_pattern_error_t err)
216 {
217 	switch(err) {
218 	case ERR_DIST_PAT_OKAY:
219 		return "no error";
220 	case ERR_DIST_PAT_VALUE_OUT_OF_RANGE:
221 		return "value out of range";
222 	case ERR_DIST_PAT_INVALID_SYNTAX:
223 		return "invalid syntax";
224 	case ERR_DIST_PAT_ARRAY_FULL:
225 		return "distribute pattern array is full";
226 	case ERR_DIST_PAT_INTERNAL_PROBLEM:
227 		return "internal error (a bug?)";
228 	}
229 	/* Return something */
230 	return "";
231 }
232