1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 *
25 * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
26 */
27
28#include <uuid/uuid.h>
29#include <ctype.h>
30#include <synch.h>
31#include <stdio.h>
32#include <unistd.h>
33#include <string.h>
34#include <strings.h>
35#include <assert.h>
36
37#include <libmlrpc.h>
38
39
40/*
41 * Global list of allocated handles.  Handles are used in various
42 * server-side RPC functions: typically, issued when a service is
43 * opened and obsoleted when it is closed.  Clients should treat
44 * handles as opaque data.
45 */
46static ndr_handle_t *ndr_handle_list;
47static mutex_t ndr_handle_lock;
48
49/*
50 * Table of registered services.
51 */
52#define	NDR_MAX_SERVICES	32
53static ndr_service_t *ndr_services[NDR_MAX_SERVICES];
54
55/*
56 * Register a service.
57 *
58 * Returns:
59 *	0	Success
60 *	-1	Duplicate service
61 *	-2	Duplicate name
62 *	-3	Table overflow
63 */
64int
65ndr_svc_register(ndr_service_t *svc)
66{
67	ndr_service_t 	*p;
68	int		free_slot = -1;
69	int		i;
70
71	for (i = 0; i < NDR_MAX_SERVICES; i++) {
72		if ((p = ndr_services[i]) == NULL) {
73			if (free_slot < 0)
74				free_slot = i;
75			continue;
76		}
77
78		if (p == svc)
79			return (-1);
80
81		if (strcasecmp(p->name, svc->name) == 0)
82			return (-2);
83	}
84
85	if (free_slot < 0)
86		return (-3);
87
88	ndr_services[free_slot] = svc;
89	return (0);
90}
91
92void
93ndr_svc_unregister(ndr_service_t *svc)
94{
95	int i;
96
97	for (i = 0; i < NDR_MAX_SERVICES; i++) {
98		if (ndr_services[i] == svc)
99			ndr_services[i] = NULL;
100	}
101}
102
103ndr_stub_table_t *
104ndr_svc_find_stub(ndr_service_t *svc, int opnum)
105{
106	ndr_stub_table_t *ste;
107
108	for (ste = svc->stub_table; ste->func; ste++) {
109		if (ste->opnum == opnum)
110			return (ste);
111	}
112
113	return (NULL);
114}
115
116ndr_service_t *
117ndr_svc_lookup_name(const char *name)
118{
119	ndr_service_t 	*svc;
120	int			i;
121
122	for (i = 0; i < NDR_MAX_SERVICES; i++) {
123		if ((svc = ndr_services[i]) == NULL)
124			continue;
125
126		if (strcasecmp(name, svc->name) != 0)
127			continue;
128
129		ndo_printf(0, 0, "%s %s", svc->name, svc->desc);
130		return (svc);
131	}
132
133	return (NULL);
134}
135
136ndr_service_t *
137ndr_svc_lookup_uuid(ndr_uuid_t *as_uuid, int as_vers,
138    ndr_uuid_t *ts_uuid, int ts_vers)
139{
140	ndr_service_t *svc;
141	char abstract_syntax[UUID_PRINTABLE_STRING_LENGTH];
142	char transfer_syntax[UUID_PRINTABLE_STRING_LENGTH];
143	int i;
144
145	if (as_uuid)
146		ndr_uuid_unparse(as_uuid, abstract_syntax);
147
148	if (ts_uuid)
149		ndr_uuid_unparse(ts_uuid, transfer_syntax);
150
151	for (i = 0; i < NDR_MAX_SERVICES; i++) {
152		if ((svc = ndr_services[i]) == NULL)
153			continue;
154
155		if (as_uuid) {
156			if (svc->abstract_syntax_uuid == 0)
157				continue;
158
159			if (svc->abstract_syntax_version != as_vers)
160				continue;
161
162			if (strcasecmp(abstract_syntax,
163			    svc->abstract_syntax_uuid))
164				continue;
165		}
166
167		if (ts_uuid) {
168			if (svc->transfer_syntax_uuid == 0)
169				continue;
170
171			if (svc->transfer_syntax_version != ts_vers)
172				continue;
173
174			if (strcasecmp(transfer_syntax,
175			    svc->transfer_syntax_uuid))
176				continue;
177		}
178
179		ndo_printf(0, 0, "%s %s", svc->name, svc->desc);
180		return (svc);
181	}
182
183	ndo_printf(0, 0, "ndr_svc_lookup_uuid: unknown service");
184	ndo_printf(0, 0, "abstract=%s v%d, transfer=%s v%d",
185	    abstract_syntax, as_vers, transfer_syntax, ts_vers);
186	return (NULL);
187}
188
189/*
190 * Allocate a handle for use with the server-side RPC functions.
191 *
192 * An arbitrary caller context can be associated with the handle
193 * via data; it will not be dereferenced by the handle API.
194 */
195ndr_hdid_t *
196ndr_hdalloc(const ndr_xa_t *xa, const void *data)
197{
198	static ndr_hdid_t id;
199	ndr_handle_t *hd;
200	uuid_t uu;
201
202	if ((hd = malloc(sizeof (ndr_handle_t))) == NULL)
203		return (NULL);
204
205	if (id.data2 == 0) {
206		uuid_generate_random(uu);
207		bcopy(uu, &id.data2, sizeof (uuid_t));
208		id.data1 = 0;
209		id.data2 = 0;
210	}
211
212	++id.data2;
213
214	bcopy(&id, &hd->nh_id, sizeof (ndr_hdid_t));
215	hd->nh_pipe = xa->pipe;
216	hd->nh_svc = xa->binding->service;
217	hd->nh_data = (void *)data;
218	hd->nh_data_free = NULL;
219
220	(void) mutex_lock(&ndr_handle_lock);
221	hd->nh_next = ndr_handle_list;
222	ndr_handle_list = hd;
223	(void) mutex_unlock(&ndr_handle_lock);
224
225	return (&hd->nh_id);
226}
227
228/*
229 * Remove a handle from the global list and free it.
230 */
231void
232ndr_hdfree(const ndr_xa_t *xa, const ndr_hdid_t *id)
233{
234	ndr_service_t *svc = xa->binding->service;
235	ndr_handle_t *hd;
236	ndr_handle_t **pphd;
237
238	assert(id);
239
240	(void) mutex_lock(&ndr_handle_lock);
241	pphd = &ndr_handle_list;
242
243	while (*pphd) {
244		hd = *pphd;
245
246		if (bcmp(&hd->nh_id, id, sizeof (ndr_hdid_t)) == 0) {
247			if (hd->nh_svc == svc) {
248				*pphd = hd->nh_next;
249				free(hd);
250			}
251			break;
252		}
253
254		pphd = &(*pphd)->nh_next;
255	}
256
257	(void) mutex_unlock(&ndr_handle_lock);
258}
259
260/*
261 * Lookup a handle by id.  If the handle is in the list and it matches
262 * the specified service, a pointer to it is returned.  Otherwise a null
263 * pointer is returned.
264 */
265ndr_handle_t *
266ndr_hdlookup(const ndr_xa_t *xa, const ndr_hdid_t *id)
267{
268	ndr_service_t *svc = xa->binding->service;
269	ndr_handle_t *hd;
270
271	assert(id);
272	(void) mutex_lock(&ndr_handle_lock);
273	hd = ndr_handle_list;
274
275	while (hd) {
276		if (bcmp(&hd->nh_id, id, sizeof (ndr_hdid_t)) == 0) {
277			if (hd->nh_svc != svc)
278				break;
279			(void) mutex_unlock(&ndr_handle_lock);
280			return (hd);
281		}
282
283		hd = hd->nh_next;
284	}
285
286	(void) mutex_unlock(&ndr_handle_lock);
287	return (NULL);
288}
289
290/*
291 * Called when a pipe is closed to release any associated handles.
292 */
293void
294ndr_hdclose(ndr_pipe_t *pipe)
295{
296	ndr_handle_t *hd;
297	ndr_handle_t **pphd;
298
299	(void) mutex_lock(&ndr_handle_lock);
300	pphd = &ndr_handle_list;
301
302	while (*pphd) {
303		hd = *pphd;
304
305		if (hd->nh_pipe == pipe) {
306			*pphd = hd->nh_next;
307
308			if (hd->nh_data_free)
309				(*hd->nh_data_free)(hd->nh_data);
310
311			free(hd);
312			continue;
313		}
314
315		pphd = &(*pphd)->nh_next;
316	}
317
318	(void) mutex_unlock(&ndr_handle_lock);
319}
320
321/*
322 * Convert a UUID to a string.
323 */
324void
325ndr_uuid_unparse(ndr_uuid_t *uuid, char *out)
326{
327	(void) sprintf(out, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
328	    uuid->data1, uuid->data2, uuid->data3,
329	    uuid->data4[0], uuid->data4[1],
330	    uuid->data4[2], uuid->data4[3],
331	    uuid->data4[4], uuid->data4[5],
332	    uuid->data4[6], uuid->data4[7]);
333}
334
335/*
336 * Convert a string to a UUID.
337 */
338int
339ndr_uuid_parse(char *in, ndr_uuid_t *uuid)
340{
341	char 		*p = in;
342	char 		*q;
343	char		buf[4];
344	int		i;
345
346	if (strlen(in) != UUID_PRINTABLE_STRING_LENGTH - 1)
347		return (-1);
348
349	uuid->data1 = strtoul(p, &p, 16);
350	if (*p != '-')
351		return (-1);
352	p++;
353
354	uuid->data2 = strtol(p, &p, 16);
355	if (*p != '-')
356		return (-1);
357	p++;
358
359	uuid->data3 = strtol(p, &p, 16);
360	if (*p != '-')
361		return (-1);
362	p++;
363
364	for (i = 0; i < 8; i++) {
365		if (*p ==  '-')
366			p++;
367
368		if (p[0] == 0 || p[1] == 0)
369			return (-1);
370
371		buf[0] = *p++;
372		buf[1] = *p++;
373		buf[2] = 0;
374		uuid->data4[i] = strtol(buf, &q, 16);
375		if (*q != 0)
376			return (-1);
377	}
378
379	if (*p != 0)
380		return (-1);
381
382	return (0);
383}
384
385void
386ndr_svc_binding_pool_init(ndr_binding_t **headpp, ndr_binding_t pool[],
387    int n_pool)
388{
389	ndr_binding_t	*head = NULL;
390	int		ix;
391
392	for (ix = n_pool - 1; ix >= 0; ix--) {
393		pool[ix].next = head;
394		pool[ix].service = NULL;
395		pool[ix].p_cont_id = 0xffff;
396		pool[ix].instance_specific = 0;
397		head = &pool[ix];
398	}
399
400	*headpp = head;
401}
402
403ndr_binding_t *
404ndr_svc_find_binding(ndr_xa_t *mxa, ndr_p_context_id_t p_cont_id)
405{
406	ndr_binding_t *mbind;
407
408	for (mbind = mxa->binding_list; mbind; mbind = mbind->next) {
409		if (mbind->service != NULL &&
410		    mbind->which_side == NDR_BIND_SIDE_SERVER &&
411		    mbind->p_cont_id == p_cont_id)
412			break;
413	}
414
415	return (mbind);
416}
417
418ndr_binding_t *
419ndr_svc_new_binding(ndr_xa_t *mxa)
420{
421	ndr_binding_t *mbind;
422
423	for (mbind = mxa->binding_list; mbind; mbind = mbind->next) {
424		if (mbind->service == NULL)
425			break;
426	}
427
428	return (mbind);
429}
430
431/*
432 * Move bytes between a buffer and a uio structure.
433 * The transfer direction is controlled by rw:
434 *	UIO_READ:  transfer from buf to uio
435 *	UIO_WRITE: transfer from uio to buf
436 *
437 * Returns the number of bytes moved.
438 */
439ssize_t
440ndr_uiomove(caddr_t buf, size_t buflen, enum uio_rw rw, struct uio *uio)
441{
442	struct iovec *iov;
443	int reading = (rw == UIO_READ);
444	size_t nbytes;
445	size_t nxfer = 0;
446
447	assert(rw == UIO_READ || rw == UIO_WRITE);
448
449	while (buflen && uio->uio_resid && uio->uio_iovcnt) {
450		iov = uio->uio_iov;
451		if ((nbytes = iov->iov_len) == 0) {
452			uio->uio_iov++;
453			uio->uio_iovcnt--;
454			continue;
455		}
456
457		if (nbytes > buflen)
458			nbytes = buflen;
459
460		if (reading)
461			bcopy(buf, iov->iov_base, nbytes);
462		else
463			bcopy(iov->iov_base, buf, nbytes);
464
465		iov->iov_base += nbytes;
466		iov->iov_len -= nbytes;
467		uio->uio_resid -= nbytes;
468		uio->uio_offset += nbytes;
469		buf += nbytes;
470		buflen -= nbytes;
471		nxfer += nbytes;
472	}
473
474	return (nxfer);
475}
476