1/*-
2 * service.c
3 *
4 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
5 *
6 * Copyright (c) 2001-2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 * $Id: service.c,v 1.1 2004/01/13 19:32:36 max Exp $
31 * $FreeBSD$
32 */
33
34#include <sys/uio.h>
35#include <netinet/in.h>
36#include <arpa/inet.h>
37#define L2CAP_SOCKET_CHECKED
38#include <bluetooth.h>
39#include <errno.h>
40#include <string.h>
41#include <unistd.h>
42
43#include <sdp-int.h>
44#include <sdp.h>
45
46static int32_t sdp_receive_error_pdu(sdp_session_p ss);
47
48int32_t
49sdp_register_service(void *xss, uint16_t uuid, bdaddr_p const bdaddr,
50		uint8_t const *data, uint32_t datalen, uint32_t *handle)
51{
52	sdp_session_p	ss = (sdp_session_p) xss;
53	struct iovec	iov[4];
54	sdp_pdu_t	pdu;
55	int32_t		len;
56
57	if (ss == NULL)
58		return (-1);
59	if (bdaddr == NULL || data == NULL ||
60	    datalen == 0 || !(ss->flags & SDP_SESSION_LOCAL)) {
61		ss->error = EINVAL;
62		return (-1);
63	}
64	if (sizeof(pdu)+sizeof(uuid)+sizeof(*bdaddr)+datalen > SDP_LOCAL_MTU) {
65		ss->error = EMSGSIZE;
66		return (-1);
67	}
68
69	pdu.pid = SDP_PDU_SERVICE_REGISTER_REQUEST;
70	pdu.tid = htons(++ss->tid);
71	pdu.len = htons(sizeof(uuid) + sizeof(*bdaddr) + datalen);
72
73	uuid = htons(uuid);
74
75	iov[0].iov_base = (void *) &pdu;
76	iov[0].iov_len = sizeof(pdu);
77
78	iov[1].iov_base = (void *) &uuid;
79	iov[1].iov_len = sizeof(uuid);
80
81	iov[2].iov_base = (void *) bdaddr;
82	iov[2].iov_len = sizeof(*bdaddr);
83
84	iov[3].iov_base = (void *) data;
85	iov[3].iov_len = datalen;
86
87	do {
88		len = writev(ss->s, iov, sizeof(iov)/sizeof(iov[0]));
89	} while (len < 0 && errno == EINTR);
90
91	if (len < 0) {
92		ss->error = errno;
93		return (-1);
94	}
95
96	len = sdp_receive_error_pdu(ss);
97	if (len < 0)
98		return (-1);
99	if (len != sizeof(pdu) + sizeof(uint16_t) + sizeof(uint32_t)) {
100		ss->error = EIO;
101		return (-1);
102	}
103
104	if (handle != NULL) {
105		*handle  = (uint32_t) ss->rsp[--len];
106		*handle |= (uint32_t) ss->rsp[--len] << 8;
107		*handle |= (uint32_t) ss->rsp[--len] << 16;
108		*handle |= (uint32_t) ss->rsp[--len] << 24;
109	}
110
111	return (0);
112}
113
114int32_t
115sdp_unregister_service(void *xss, uint32_t handle)
116{
117	sdp_session_p	ss = (sdp_session_p) xss;
118	struct iovec	iov[2];
119	sdp_pdu_t	pdu;
120	int32_t		len;
121
122	if (ss == NULL)
123		return (-1);
124	if (!(ss->flags & SDP_SESSION_LOCAL)) {
125		ss->error = EINVAL;
126		return (-1);
127	}
128	if (sizeof(pdu) + sizeof(handle) > SDP_LOCAL_MTU) {
129		ss->error = EMSGSIZE;
130		return (-1);
131	}
132
133	pdu.pid = SDP_PDU_SERVICE_UNREGISTER_REQUEST;
134	pdu.tid = htons(++ss->tid);
135	pdu.len = htons(sizeof(handle));
136
137	handle = htonl(handle);
138
139	iov[0].iov_base = (void *) &pdu;
140	iov[0].iov_len = sizeof(pdu);
141
142	iov[1].iov_base = (void *) &handle;
143	iov[1].iov_len = sizeof(handle);
144
145	do {
146		len = writev(ss->s, iov, sizeof(iov)/sizeof(iov[0]));
147	} while (len < 0 && errno == EINTR);
148
149	if (len < 0) {
150		ss->error = errno;
151		return (-1);
152	}
153
154	return ((sdp_receive_error_pdu(ss) < 0)? -1 : 0);
155}
156
157int32_t
158sdp_change_service(void *xss, uint32_t handle,
159		uint8_t const *data, uint32_t datalen)
160{
161	sdp_session_p	ss = (sdp_session_p) xss;
162	struct iovec	iov[3];
163	sdp_pdu_t	pdu;
164	int32_t		len;
165
166	if (ss == NULL)
167		return (-1);
168	if (data == NULL || datalen == 0 || !(ss->flags & SDP_SESSION_LOCAL)) {
169		ss->error = EINVAL;
170		return (-1);
171	}
172	if (sizeof(pdu) + sizeof(handle) + datalen > SDP_LOCAL_MTU) {
173		ss->error = EMSGSIZE;
174		return (-1);
175	}
176
177	pdu.pid = SDP_PDU_SERVICE_CHANGE_REQUEST;
178	pdu.tid = htons(++ss->tid);
179	pdu.len = htons(sizeof(handle) + datalen);
180
181	handle = htons(handle);
182
183	iov[0].iov_base = (void *) &pdu;
184	iov[0].iov_len = sizeof(pdu);
185
186	iov[1].iov_base = (void *) &handle;
187	iov[1].iov_len = sizeof(handle);
188
189	iov[2].iov_base = (void *) data;
190	iov[2].iov_len = datalen;
191
192	do {
193		len = writev(ss->s, iov, sizeof(iov)/sizeof(iov[0]));
194	} while (len < 0 && errno == EINTR);
195
196	if (len < 0) {
197		ss->error = errno;
198		return (-1);
199	}
200
201	return ((sdp_receive_error_pdu(ss) < 0)? -1 : 0);
202}
203
204static int32_t
205sdp_receive_error_pdu(sdp_session_p ss)
206{
207	sdp_pdu_p	pdu;
208	int32_t		len;
209	uint16_t	error;
210
211	do {
212		len = read(ss->s, ss->rsp, ss->rsp_e - ss->rsp);
213	} while (len < 0 && errno == EINTR);
214
215	if (len < 0) {
216		ss->error = errno;
217		return (-1);
218	}
219
220	pdu = (sdp_pdu_p) ss->rsp;
221	pdu->tid = ntohs(pdu->tid);
222	pdu->len = ntohs(pdu->len);
223
224	if (pdu->pid != SDP_PDU_ERROR_RESPONSE || pdu->tid != ss->tid ||
225	    pdu->len < 2 || pdu->len != len - sizeof(*pdu)) {
226		ss->error = EIO;
227		return (-1);
228	}
229
230	error  = (uint16_t) ss->rsp[sizeof(pdu)] << 8;
231	error |= (uint16_t) ss->rsp[sizeof(pdu) + 1];
232
233	if (error != 0) {
234		ss->error = EIO;
235		return (-1);
236	}
237
238	return (len);
239}
240
241