1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
|
/*
Affix - Bluetooth Protocol Stack for Linux
Copyright (C) 2001,2002 Nokia Corporation
Author: Dmitry Kasatkin <dmitry.kasatkin@nokia.com>
Original Author: Guruprasad Krishnamurthy <kgprasad@hotmail.com>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
$Id: utils.c,v 1.55 2003/04/15 10:47:06 kds Exp $
SDP client utilities to perform GIAC, connect to an SDP server,
and to initialize the SDP machinery
Fixes:
Dmitry Kasatkin : SDPServerConnection changed to complex type
*/
#include <affix/config.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <affix/sdp.h>
#include <affix/sdpclt.h>
#include <affix/sdpsrv.h>
#include "utils.h"
#include "cstate.h"
#include "attr.h"
static int sdp_up = 0;
static int sdp_flags = 0;
static pid_t sdp_pid = 0;
sdp_mode_t sdp_mode;
char *sdp_errlist(int err)
{
switch (err) {
case SDP_ERR_SDP_VERSION:
return "Invalid/Unsupported SDP version";
case SDP_ERR_SERVICE_RECORD_HANDLE:
return "Invalid Service Record Handle";
case SDP_ERR_REQUEST_SYNTAX:
return "Invalid request syntax";
case SDP_ERR_PDU_SIZE:
return "Invalid PDU Size";
case SDP_ERR_CONT_STATE:
return "Invalid Continuation State";
case SDP_ERR_RESOURCES:
return "Insufficient Resources to satisfy Request";
case SDP_ERR_INVALID_ARG:
return "Invalid Arguments";
case SDP_ERR_NOT_EXIST:
return "Attribute/Service does not exist";
case SDP_ERR_SYNTAX:
return "Invalid Data Synax";
case SDP_ERR_INTERNAL:
return "Internal Error";
case SDP_ERR_SERVER:
return "Unspecified Server Error";
default:
return "Unknown error";
}
}
void _sdp_error(char *buf, int err)
{
if (err < 0)
sprintf(buf, "System error: %s (%d)", strerror(errno), errno);
else if (err > 0)
sprintf(buf, "SDP error: %s (%d)", sdp_errlist(err), err);
else
sprintf(buf, "No error (0)\n");
}
char *sdp_error(int err)
{
static unsigned char buf[80][2];
static int num = 0;
num = 1 - num; /* switch buf */
_sdp_error(buf[num], err);
return buf[num];
}
int sdp_init(int flags)
{
int err = 0;
if (!sdp_up) {
if (flags & SDP_SVC_SERVER)
sdp_mode = SDP_SERVER;
else
sdp_mode = SDP_CLIENT;
sdp_flags = flags;
sdp_up = 1;
}
return err;
}
void sdp_cleanup(void)
{
if (sdp_up) {
sdp_up = 0;
sdp_flags = 0;
}
}
int sdp_connect(struct sockaddr_affix *saddr)
{
int status = 0;
int clientSockFd;
uint16_t port;
if (saddr->family == PF_INET) {
/*
** Code for a local SDP server connection
*/
struct sockaddr_in *inAddr = (struct sockaddr_in*)saddr;
DBPRT("Attempt to open a TCP socket, local client\n");
clientSockFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (clientSockFd < 0)
return clientSockFd;
port = inAddr->sin_port;
inAddr->sin_port = SDP_TCP_PORT;
DBPRT("TCP fd : %d\n", clientSockFd);
DBPRT("Local addr : %x\n", inAddr->sin_addr.s_addr);
status = connect(clientSockFd, (struct sockaddr *)inAddr, sizeof(*inAddr));
if (status < 0) {
close(clientSockFd);
BTERROR("TCP connect error:%s", strerror(errno));
} else {
DBPRT("Connected\n");
}
inAddr->sin_port = port;
} else if (saddr->family == PF_AFFIX) {
/*
** Create a BT socket for ourselves
*/
DBPRT("Attempt to open BT connection\n");
DBPRT("BT addr str : %s\n", bda2str(&saddr->bda));
clientSockFd = socket(PF_AFFIX, SOCK_SEQPACKET, BTPROTO_L2CAP);
if (clientSockFd < 0)
return clientSockFd;
port = saddr->port;
saddr->port = SDP_PSM;
DBPRT("Connecting\n");
status = connect(clientSockFd, (struct sockaddr*)saddr, sizeof(*saddr));
if (status < 0) {
close(clientSockFd);
DBPRT("connect error:%s", strerror(errno));
} else {
DBPRT("Connect success, mtu: %d\n", l2cap_getmtu(clientSockFd));
}
//printf("Connect success, mtu: %d\n", l2cap_getmtu(clientSockFd));
saddr->port = port;
} else
return SDP_ERR_INVALID_ARG;
return status ? status : clientSockFd;
}
int sdp_connect_local(void)
{
struct sockaddr_in sa;
int err;
if (sdp_flags & SDP_SVC_PROVIDER) {
sdp_pid = affix_pidof("btsdp", PIDOF_SINGLE, 0);
if (sdp_pid == 0) {
/* start server */
sdp_pid = fork();
if (sdp_pid < 0) {
BTERROR("fork() failed\n");
return -1;
}
if (!sdp_pid) {
/* child */
err = execlp("btsdp", "btsdp", "-D", NULL);
if (err)
BTERROR("Unable to run btsdp\n");
} else {
struct sockaddr_in in;
int fd;
socklen_t len;
time_t stm, tm;
/* wait for server goes up */
in.sin_family = AF_INET;
in.sin_port = SDP_TCP_PORT;
in.sin_addr.s_addr = inet_addr("127.0.0.1");
len = sizeof(in);
fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd < 0)
return -1;
stm = time(NULL);
for (;;) {
err = connect(fd, (struct sockaddr *)&in, len);
if (!err)
break;
tm = time(NULL);
if (difftime(tm, stm) > 2) {
close(fd);
return -1;
}
}
close(fd);
}
}
}
/* now try to connect */
sa.sin_family = PF_INET;
inet_aton("127.0.0.1", &sa.sin_addr);
return sdp_connect((struct sockaddr_affix*)&sa);
}
/*
** Close an existing connection to the SDP server on a said
** socket descriptor.
*/
void sdp_close(int fd)
{
if (fd >= 0)
close(fd);
}
/*
** A simple function which returns the time of day in
** seconds. Used for updating the service db state
** attribute of the service record of the SDP server
*/
long sdp_get_time(void)
{
static struct timeval tm;
/*
** To handle failure in gettimeofday, so an old
** value is returned and service does not fail
*/
gettimeofday(&tm, NULL);
return tm.tv_sec;
}
/*
** Generate unique transaction identifiers
*/
uint16_t sdp_gen_trans(void)
{
uint16_t j;
j=1+(int) (100.0*rand()/(RAND_MAX+1.0));
return j;
}
/*
** A generic send request, wait for response method. Uses
** select system call to block for response or timeout
*/
int sdp_send_req_w4_rsp(
int srvHandle,
char *requestBuffer,
char *responseBuffer,
int requestSize,
int *responseSize
)
{
int status = 0;
int bytesSent = 0;
int bytesRecvd = 0;
sdp_hdr_t *pduRequestHeader;
sdp_hdr_t *pduResponseHeader;
fd_set readFds;
struct timeval timeout;
pduRequestHeader = (sdp_hdr_t *)requestBuffer;
pduResponseHeader = (sdp_hdr_t *)responseBuffer;
/*
** Send the request, wait for response and if
** not error, set the appropriate values
** and return
*/
status = send(srvHandle, requestBuffer, requestSize, 0);
if (status < 0) {
BTERROR("Error sending data:%s", strerror(errno));
return status;
}
bytesSent = status;
if (bytesSent != requestSize) {
DBPRT("Attempt to send : %d sent : %d\n", requestSize, bytesSent);
return SDP_ERR_INTERNAL;
}
DBPRT("Bytes sent to handle : %d is : %d\n", srvHandle, bytesSent);
BTDUMP(requestBuffer, bytesSent);
DBPRT("Waiting for response\n");
timeout.tv_sec = SDP_TIMEOUT;
timeout.tv_usec = 0;
FD_ZERO(&readFds);
FD_SET(srvHandle, &readFds);
status = select(srvHandle+1, &readFds, NULL, NULL, &timeout);
if (status < 0)
return status;
if (status == 0) {
BTERROR("Client timed out\n");
errno = ETIME;
return -1;
}
DBPRT("Trying to read\n");
status = recv(srvHandle, responseBuffer, SDP_RSP_BUF_SIZE, 0);
if (status < 0)
return status;
if (status == 0) {
errno = ECONNRESET;
return -1;
}
bytesRecvd = status;
DBPRT("Read : %d\n", bytesRecvd);
BTDUMP(responseBuffer, bytesRecvd);
/*
** Process the response
*/
pduResponseHeader = (sdp_hdr_t *)responseBuffer;
if (pduRequestHeader->transactionId != pduResponseHeader->transactionId)
return SDP_ERR_INTERNAL;
*responseSize = bytesRecvd;
return 0;
}
union sockaddr_sdp {
struct sockaddr_affix bt;
struct sockaddr_in in;
};
int sdp_getmtu(int fd)
{
union sockaddr_sdp caddr;
socklen_t len = sizeof(caddr);
int mtu;
getsockname(fd, (struct sockaddr*)&caddr, &len);
if (caddr.bt.family == PF_AFFIX)
mtu = l2cap_getmtu(fd);
else {
len = sizeof(int);
getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &mtu, &len);
DBPRT("TCP MTU: %d\n", mtu);
}
return mtu;
}
/* ------------------------------------------------------- */
/*
** This version is for use by SDP server when creating new
** service records
*/
sdpsvc_t *sdp_create_svc(void)
{
sdpsvc_t *svcRec;
svcRec = (sdpsvc_t*)malloc(sizeof(sdpsvc_t));
if (!svcRec)
return NULL;
memset(svcRec, 0, sizeof(sdpsvc_t));
svcRec->serviceRecordHandle = 0xffffffff;
svcRec->fd = -1;
return svcRec;
}
/*
** Free the contents of a service record
*/
void sdp_free_svc(sdpsvc_t *svcRec)
{
if (!svcRec)
return;
if (svcRec->pdu.data)
free(svcRec->pdu.data);
s_list_destroy(&svcRec->targetPattern);
sdp_free_seq(&svcRec->attributeList);
free(svcRec);
}
void sdp_free_svclist(slist_t **svcList)
{
slist_t *list;
sdpsvc_t *svcRec;
for (list = *svcList; list; list = s_list_next(list)) {
svcRec = s_list_data(list);
if (!svcRec)
continue;
sdp_free_svc(svcRec);
}
s_list_free(svcList);
}
|