1/*-
2 * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD$");
30
31#include <sys/param.h>
32
33#include <assert.h>
34#include <netdb.h>
35#include <nsswitch.h>
36#include <stdlib.h>
37#include <string.h>
38
39#include "../debug.h"
40#include "services.h"
41
42static int services_marshal_func(struct servent *, char *, size_t *);
43static int services_lookup_func(const char *, size_t, char **, size_t *);
44static void *services_mp_init_func(void);
45static int services_mp_lookup_func(char **, size_t *, void *);
46static void services_mp_destroy_func(void *);
47
48static int
49services_marshal_func(struct servent *serv, char *buffer, size_t *buffer_size)
50{
51	struct servent	new_serv;
52	size_t	desired_size;
53	char	**alias;
54	char	*p;
55	size_t	size;
56	size_t	aliases_size;
57
58	TRACE_IN(services_marshal_func);
59	desired_size = ALIGNBYTES + sizeof(struct servent) + sizeof(char *);
60	if (serv->s_name != NULL)
61		desired_size += strlen(serv->s_name) + 1;
62	if (serv->s_proto != NULL)
63		desired_size += strlen(serv->s_proto) + 1;
64
65	aliases_size = 0;
66	if (serv->s_aliases != NULL) {
67		for (alias = serv->s_aliases; *alias; ++alias) {
68			desired_size += strlen(*alias) + 1;
69			++aliases_size;
70		}
71
72		desired_size += ALIGNBYTES + sizeof(char *) *
73		    (aliases_size + 1);
74	}
75
76	if ((*buffer_size < desired_size) || (buffer == NULL)) {
77		*buffer_size = desired_size;
78		TRACE_OUT(services_marshal_func);
79		return (NS_RETURN);
80	}
81
82	memcpy(&new_serv, serv, sizeof(struct servent));
83	memset(buffer, 0, desired_size);
84
85	*buffer_size = desired_size;
86	p = buffer + sizeof(struct servent) + sizeof(char *);
87	memcpy(buffer + sizeof(struct servent), &p, sizeof(char *));
88	p = (char *)ALIGN(p);
89
90	if (new_serv.s_name != NULL) {
91		size = strlen(new_serv.s_name);
92		memcpy(p, new_serv.s_name, size);
93		new_serv.s_name = p;
94		p += size + 1;
95	}
96
97	if (new_serv.s_proto != NULL) {
98		size = strlen(new_serv.s_proto);
99		memcpy(p, new_serv.s_proto, size);
100		new_serv.s_proto = p;
101		p += size + 1;
102	}
103
104	if (new_serv.s_aliases != NULL) {
105		p = (char *)ALIGN(p);
106		memcpy(p, new_serv.s_aliases, sizeof(char *) * aliases_size);
107		new_serv.s_aliases = (char **)p;
108		p += sizeof(char *) * (aliases_size + 1);
109
110		for (alias = new_serv.s_aliases; *alias; ++alias) {
111			size = strlen(*alias);
112			memcpy(p, *alias, size);
113			*alias = p;
114			p += size + 1;
115		}
116	}
117
118	memcpy(buffer, &new_serv, sizeof(struct servent));
119	TRACE_OUT(services_marshal_func);
120	return (NS_SUCCESS);
121}
122
123static int
124services_lookup_func(const char *key, size_t key_size, char **buffer,
125	size_t *buffer_size)
126{
127	enum nss_lookup_type lookup_type;
128	char	*name = NULL;
129	char	*proto = NULL;
130	size_t	size, size2;
131	int	port;
132
133	struct servent *result;
134
135	TRACE_IN(services_lookup_func);
136
137	assert(buffer != NULL);
138	assert(buffer_size != NULL);
139
140	if (key_size < sizeof(enum nss_lookup_type)) {
141		TRACE_OUT(passwd_lookup_func);
142		return (NS_UNAVAIL);
143	}
144	memcpy(&lookup_type, key, sizeof(enum nss_lookup_type));
145
146	switch (lookup_type) {
147	case nss_lt_name:
148		size = key_size - sizeof(enum nss_lookup_type);
149		name = calloc(1, size + 1);
150		assert(name != NULL);
151		memcpy(name, key + sizeof(enum nss_lookup_type), size);
152
153		size2 = strlen(name) + 1;
154
155		if (size2 < size)
156			proto = name + size2;
157		else
158			proto = NULL;
159		break;
160	case nss_lt_id:
161		if (key_size < sizeof(enum nss_lookup_type) +
162			sizeof(int)) {
163			TRACE_OUT(passwd_lookup_func);
164			return (NS_UNAVAIL);
165		}
166
167		memcpy(&port, key + sizeof(enum nss_lookup_type),
168			sizeof(int));
169
170		size = key_size - sizeof(enum nss_lookup_type) - sizeof(int);
171		if (size > 0) {
172			proto = calloc(1, size + 1);
173			assert(proto != NULL);
174			memcpy(proto, key + sizeof(enum nss_lookup_type) +
175				sizeof(int), size);
176		}
177		break;
178	default:
179		TRACE_OUT(passwd_lookup_func);
180		return (NS_UNAVAIL);
181	}
182
183	switch (lookup_type) {
184	case nss_lt_name:
185		result = getservbyname(name, proto);
186		free(name);
187		break;
188	case nss_lt_id:
189		result = getservbyport(port, proto);
190		free(proto);
191		break;
192	default:
193		/* SHOULD NOT BE REACHED */
194		break;
195	}
196
197	if (result != NULL) {
198		services_marshal_func(result, NULL, buffer_size);
199		*buffer = malloc(*buffer_size);
200		assert(*buffer != NULL);
201		services_marshal_func(result, *buffer, buffer_size);
202	}
203
204	TRACE_OUT(services_lookup_func);
205	return (result == NULL ? NS_NOTFOUND : NS_SUCCESS);
206}
207
208static void *
209services_mp_init_func(void)
210{
211	TRACE_IN(services_mp_init_func);
212	setservent(0);
213	TRACE_OUT(services_mp_init_func);
214
215	return (NULL);
216}
217
218static int
219services_mp_lookup_func(char **buffer, size_t *buffer_size, void *mdata)
220{
221	struct servent *result;
222
223	TRACE_IN(services_mp_lookup_func);
224	result = getservent();
225	if (result != NULL) {
226		services_marshal_func(result, NULL, buffer_size);
227		*buffer = malloc(*buffer_size);
228		assert(*buffer != NULL);
229		services_marshal_func(result, *buffer, buffer_size);
230	}
231
232	TRACE_OUT(services_mp_lookup_func);
233	return (result == NULL ? NS_NOTFOUND : NS_SUCCESS);
234}
235
236static void
237services_mp_destroy_func(void *mdata)
238{
239	TRACE_IN(services_mp_destroy_func);
240	TRACE_OUT(services_mp_destroy_func);
241}
242
243struct agent *
244init_services_agent(void)
245{
246	struct common_agent	*retval;
247	TRACE_IN(init_services_agent);
248
249	retval = calloc(1, sizeof(*retval));
250	assert(retval != NULL);
251
252	retval->parent.name = strdup("services");
253	assert(retval->parent.name != NULL);
254
255	retval->parent.type = COMMON_AGENT;
256	retval->lookup_func = services_lookup_func;
257
258	TRACE_OUT(init_services_agent);
259	return ((struct agent *)retval);
260}
261
262struct agent *
263init_services_mp_agent(void)
264{
265	struct multipart_agent	*retval;
266
267	TRACE_IN(init_services_mp_agent);
268	retval = calloc(1,
269		sizeof(*retval));
270	assert(retval != NULL);
271
272	retval->parent.name = strdup("services");
273	retval->parent.type = MULTIPART_AGENT;
274	retval->mp_init_func = services_mp_init_func;
275	retval->mp_lookup_func = services_mp_lookup_func;
276	retval->mp_destroy_func = services_mp_destroy_func;
277	assert(retval->parent.name != NULL);
278
279	TRACE_OUT(init_services_mp_agent);
280	return ((struct agent *)retval);
281}
282