xref: /illumos-gate/usr/src/lib/libmlrpc/common/ndr_svc.c (revision 66582b606a8194f7f3ba5b3a3a6dca5b0d346361)
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  */
46 static ndr_handle_t *ndr_handle_list;
47 static mutex_t ndr_handle_lock;
48 
49 /*
50  * Table of registered services.
51  */
52 #define	NDR_MAX_SERVICES	32
53 static 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  */
64 int
65 ndr_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 
92 void
93 ndr_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 
103 ndr_stub_table_t *
104 ndr_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 
116 ndr_service_t *
117 ndr_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 
136 ndr_service_t *
137 ndr_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  */
195 ndr_hdid_t *
196 ndr_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  */
231 void
232 ndr_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  */
265 ndr_handle_t *
266 ndr_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  */
293 void
294 ndr_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  */
324 void
325 ndr_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  */
338 int
339 ndr_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 
385 void
386 ndr_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 
403 ndr_binding_t *
404 ndr_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 
418 ndr_binding_t *
419 ndr_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  */
439 ssize_t
440 ndr_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