1/*	$OpenBSD: name2id.c,v 1.4 2015/01/22 17:42:09 reyk Exp $	*/
2
3/*
4 * Copyright (c) 2004, 2005 Henning Brauer <henning@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
15 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
16 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20#include <sys/queue.h>
21
22#include <stdlib.h>
23#include <string.h>
24#include <errno.h>
25
26#include "relayd.h"
27
28#define	IDVAL_MAX	50000
29
30struct n2id_label {
31	TAILQ_ENTRY(n2id_label)	 entry;
32	char			*name;
33	u_int16_t		 id;
34	int			 ref;
35};
36
37TAILQ_HEAD(n2id_labels, n2id_label);
38
39u_int16_t	 _name2id(struct n2id_labels *, const char *);
40const char	*_id2name(struct n2id_labels *, u_int16_t);
41void		 _unref(struct n2id_labels *, u_int16_t);
42void		 _ref(struct n2id_labels *, u_int16_t);
43
44struct n2id_labels	relay_labels = TAILQ_HEAD_INITIALIZER(relay_labels);
45struct n2id_labels	relay_tags = TAILQ_HEAD_INITIALIZER(relay_tags);
46
47u_int16_t
48tag_name2id(const char *name)
49{
50	return (_name2id(&relay_tags, name));
51}
52
53const char *
54tag_id2name(u_int16_t id)
55{
56	return (_id2name(&relay_tags, id));
57}
58
59void
60tag_unref(u_int16_t id)
61{
62	_unref(&relay_tags, id);
63}
64
65void
66tag_ref(u_int16_t id)
67{
68	_ref(&relay_tags, id);
69}
70
71u_int16_t
72label_name2id(const char *name)
73{
74	return (_name2id(&relay_labels, name));
75}
76
77const char *
78label_id2name(u_int16_t id)
79{
80	return (_id2name(&relay_labels, id));
81}
82
83void
84label_unref(u_int16_t id)
85{
86	_unref(&relay_labels, id);
87}
88
89void
90label_ref(u_int16_t id)
91{
92	_ref(&relay_labels, id);
93}
94
95u_int16_t
96_name2id(struct n2id_labels *head, const char *name)
97{
98	struct n2id_label	*label, *p = NULL;
99	u_int16_t		 new_id = 1;
100
101	if (!name[0]) {
102		errno = EINVAL;
103		return (0);
104	}
105
106	TAILQ_FOREACH(label, head, entry)
107		if (strcmp(name, label->name) == 0) {
108			label->ref++;
109			return (label->id);
110		}
111
112	/*
113	 * to avoid fragmentation, we do a linear search from the beginning
114	 * and take the first free slot we find. if there is none or the list
115	 * is empty, append a new entry at the end.
116	 */
117
118	if (!TAILQ_EMPTY(head))
119		for (p = TAILQ_FIRST(head); p != NULL &&
120		    p->id == new_id; p = TAILQ_NEXT(p, entry))
121			new_id = p->id + 1;
122
123	if (new_id > IDVAL_MAX) {
124		errno = ERANGE;
125		return (0);
126	}
127
128	if ((label = calloc(1, sizeof(struct n2id_label))) == NULL)
129		return (0);
130	if ((label->name = strdup(name)) == NULL) {
131		free(label);
132		return (0);
133	}
134	label->id = new_id;
135	label->ref++;
136
137	if (p != NULL)	/* insert new entry before p */
138		TAILQ_INSERT_BEFORE(p, label, entry);
139	else		/* either list empty or no free slot in between */
140		TAILQ_INSERT_TAIL(head, label, entry);
141
142	return (label->id);
143}
144
145const char *
146_id2name(struct n2id_labels *head, u_int16_t id)
147{
148	struct n2id_label	*label;
149
150	if (id == 0)
151		return ("");
152
153	TAILQ_FOREACH(label, head, entry)
154		if (label->id == id)
155			return (label->name);
156
157	return ("");
158}
159
160void
161_unref(struct n2id_labels *head, u_int16_t id)
162{
163	struct n2id_label	*p, *next;
164
165	if (id == 0)
166		return;
167
168	for (p = TAILQ_FIRST(head); p != NULL; p = next) {
169		next = TAILQ_NEXT(p, entry);
170		if (id == p->id) {
171			if (--p->ref == 0) {
172				TAILQ_REMOVE(head, p, entry);
173				free(p->name);
174				free(p);
175			}
176			break;
177		}
178	}
179}
180
181void
182_ref(struct n2id_labels *head, u_int16_t id)
183{
184	struct n2id_label	*label;
185
186	if (id == 0)
187		return;
188
189	TAILQ_FOREACH(label, head, entry)
190		if (label->id == id) {
191			++label->ref;
192			break;
193		}
194}
195