1/*-
2 * service.c
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
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 */
32
33#include <sys/uio.h>
34#include <netinet/in.h>
35#include <arpa/inet.h>
36#define L2CAP_SOCKET_CHECKED
37#include <bluetooth.h>
38#include <errno.h>
39#include <string.h>
40#include <unistd.h>
41
42#include <sdp-int.h>
43#include <sdp.h>
44
45static int32_t sdp_receive_error_pdu(sdp_session_p ss);
46
47int32_t
48sdp_register_service(void *xss, uint16_t uuid, bdaddr_p const bdaddr,
49		uint8_t const *data, uint32_t datalen, uint32_t *handle)
50{
51	sdp_session_p	ss = (sdp_session_p) xss;
52	struct iovec	iov[4];
53	sdp_pdu_t	pdu;
54	int32_t		len;
55
56	if (ss == NULL)
57		return (-1);
58	if (bdaddr == NULL || data == NULL ||
59	    datalen == 0 || !(ss->flags & SDP_SESSION_LOCAL)) {
60		ss->error = EINVAL;
61		return (-1);
62	}
63	if (sizeof(pdu)+sizeof(uuid)+sizeof(*bdaddr)+datalen > SDP_LOCAL_MTU) {
64		ss->error = EMSGSIZE;
65		return (-1);
66	}
67
68	pdu.pid = SDP_PDU_SERVICE_REGISTER_REQUEST;
69	pdu.tid = htons(++ss->tid);
70	pdu.len = htons(sizeof(uuid) + sizeof(*bdaddr) + datalen);
71
72	uuid = htons(uuid);
73
74	iov[0].iov_base = (void *) &pdu;
75	iov[0].iov_len = sizeof(pdu);
76
77	iov[1].iov_base = (void *) &uuid;
78	iov[1].iov_len = sizeof(uuid);
79
80	iov[2].iov_base = (void *) bdaddr;
81	iov[2].iov_len = sizeof(*bdaddr);
82
83	iov[3].iov_base = (void *) data;
84	iov[3].iov_len = datalen;
85
86	do {
87		len = writev(ss->s, iov, sizeof(iov)/sizeof(iov[0]));
88	} while (len < 0 && errno == EINTR);
89
90	if (len < 0) {
91		ss->error = errno;
92		return (-1);
93	}
94
95	len = sdp_receive_error_pdu(ss);
96	if (len < 0)
97		return (-1);
98	if (len != sizeof(pdu) + sizeof(uint16_t) + sizeof(uint32_t)) {
99		ss->error = EIO;
100		return (-1);
101	}
102
103	if (handle != NULL) {
104		*handle  = (uint32_t) ss->rsp[--len];
105		*handle |= (uint32_t) ss->rsp[--len] << 8;
106		*handle |= (uint32_t) ss->rsp[--len] << 16;
107		*handle |= (uint32_t) ss->rsp[--len] << 24;
108	}
109
110	return (0);
111}
112
113int32_t
114sdp_unregister_service(void *xss, uint32_t handle)
115{
116	sdp_session_p	ss = (sdp_session_p) xss;
117	struct iovec	iov[2];
118	sdp_pdu_t	pdu;
119	int32_t		len;
120
121	if (ss == NULL)
122		return (-1);
123	if (!(ss->flags & SDP_SESSION_LOCAL)) {
124		ss->error = EINVAL;
125		return (-1);
126	}
127	if (sizeof(pdu) + sizeof(handle) > SDP_LOCAL_MTU) {
128		ss->error = EMSGSIZE;
129		return (-1);
130	}
131
132	pdu.pid = SDP_PDU_SERVICE_UNREGISTER_REQUEST;
133	pdu.tid = htons(++ss->tid);
134	pdu.len = htons(sizeof(handle));
135
136	handle = htonl(handle);
137
138	iov[0].iov_base = (void *) &pdu;
139	iov[0].iov_len = sizeof(pdu);
140
141	iov[1].iov_base = (void *) &handle;
142	iov[1].iov_len = sizeof(handle);
143
144	do {
145		len = writev(ss->s, iov, sizeof(iov)/sizeof(iov[0]));
146	} while (len < 0 && errno == EINTR);
147
148	if (len < 0) {
149		ss->error = errno;
150		return (-1);
151	}
152
153	return ((sdp_receive_error_pdu(ss) < 0)? -1 : 0);
154}
155
156int32_t
157sdp_change_service(void *xss, uint32_t handle,
158		uint8_t const *data, uint32_t datalen)
159{
160	sdp_session_p	ss = (sdp_session_p) xss;
161	struct iovec	iov[3];
162	sdp_pdu_t	pdu;
163	int32_t		len;
164
165	if (ss == NULL)
166		return (-1);
167	if (data == NULL || datalen == 0 || !(ss->flags & SDP_SESSION_LOCAL)) {
168		ss->error = EINVAL;
169		return (-1);
170	}
171	if (sizeof(pdu) + sizeof(handle) + datalen > SDP_LOCAL_MTU) {
172		ss->error = EMSGSIZE;
173		return (-1);
174	}
175
176	pdu.pid = SDP_PDU_SERVICE_CHANGE_REQUEST;
177	pdu.tid = htons(++ss->tid);
178	pdu.len = htons(sizeof(handle) + datalen);
179
180	handle = htons(handle);
181
182	iov[0].iov_base = (void *) &pdu;
183	iov[0].iov_len = sizeof(pdu);
184
185	iov[1].iov_base = (void *) &handle;
186	iov[1].iov_len = sizeof(handle);
187
188	iov[2].iov_base = (void *) data;
189	iov[2].iov_len = datalen;
190
191	do {
192		len = writev(ss->s, iov, sizeof(iov)/sizeof(iov[0]));
193	} while (len < 0 && errno == EINTR);
194
195	if (len < 0) {
196		ss->error = errno;
197		return (-1);
198	}
199
200	return ((sdp_receive_error_pdu(ss) < 0)? -1 : 0);
201}
202
203static int32_t
204sdp_receive_error_pdu(sdp_session_p ss)
205{
206	sdp_pdu_p	pdu;
207	int32_t		len;
208	uint16_t	error;
209
210	do {
211		len = read(ss->s, ss->rsp, ss->rsp_e - ss->rsp);
212	} while (len < 0 && errno == EINTR);
213
214	if (len < 0) {
215		ss->error = errno;
216		return (-1);
217	}
218
219	pdu = (sdp_pdu_p) ss->rsp;
220	pdu->tid = ntohs(pdu->tid);
221	pdu->len = ntohs(pdu->len);
222
223	if (pdu->pid != SDP_PDU_ERROR_RESPONSE || pdu->tid != ss->tid ||
224	    pdu->len < 2 || pdu->len != len - sizeof(*pdu)) {
225		ss->error = EIO;
226		return (-1);
227	}
228
229	error  = (uint16_t) ss->rsp[sizeof(pdu)] << 8;
230	error |= (uint16_t) ss->rsp[sizeof(pdu) + 1];
231
232	if (error != 0) {
233		ss->error = EIO;
234		return (-1);
235	}
236
237	return (len);
238}
239
240