1 //
2 // anyRemote
3 // a wi-fi or bluetooth remote for your PC.
4 //
5 // Copyright (C) 2006-2016 Mikhail Fedotov <anyremote@mail.ru>
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 3 of the License, or
10 // (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 //
21
22 #include <ctype.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <math.h>
26 #include <netdb.h>
27 #include <netinet/in.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/ioctl.h>
32 #include <sys/socket.h>
33 #include <sys/stat.h>
34 #include <sys/time.h>
35 #include <sys/types.h>
36 #include <sys/un.h>
37 #include <termios.h>
38 #include <unistd.h>
39
40 #ifdef USE_BLUEZ
41 #include <bluetooth/bluetooth.h>
42 #include <bluetooth/rfcomm.h>
43 #include <bluetooth/sdp.h>
44 #include <bluetooth/sdp_lib.h>
45 #endif
46
47 #ifdef USE_BT_FBSD
48 #include <bluetooth.h>
49 #include <sdp.h>
50 #include <err.h>
51 #endif
52
53 #include "pr_btspp.h"
54 #include "common.h"
55 #include "utils.h"
56 #include "sys_util.h"
57 #include "conf.h"
58 #include "lib_wrapper.h"
59 #include "dispatcher.h"
60 #include "security.h"
61
62 extern char tmp[MAXMAXLEN];
63 extern boolean_t stillRun;
64
65 typedef struct _BtsppConnection_ {
66 int fileDescriptor;
67 int serverFileDescriptor;
68 #ifdef USE_BLUEZ
69 sdp_session_t *session;
70 sdp_record_t *record;
71 #endif
72 #ifdef USE_BT_FBSD
73 void *session;
74 uint32_t record;
75 #endif
76 } _BtsppConnection;
77
78 //
79 // Support SDP
80 //
81
82 #ifdef USE_BLUEZ
83
sdpRegister(ConnectInfo * connInfo)84 static boolean_t sdpRegister(ConnectInfo* connInfo)
85 {
86 _BtsppConnection* cn = (_BtsppConnection*) connInfo->connectionData;
87 if (!cn) {
88 return BOOL_NO;
89 }
90 boolean_t ret = BOOL_YES;
91
92 uint8_t svc_uuid_int[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xab, 0xcd };
93 uint8_t rfcomm_channel = connInfo->port;
94 const char *svc_dsc = "Bluetooth remote control";
95 const char *service_prov = "anyRemote";
96
97 uuid_t root_uuid, l2cap_uuid, rfcomm_uuid, svc_uuid,
98 svc_class_uuid;
99 sdp_list_t *l2cap_list = 0,
100 *rfcomm_list = 0,
101 *root_list = 0,
102 *proto_list = 0,
103 *access_proto_list = 0,
104 *svc_class_list = 0,
105 *profile_list = 0;
106 sdp_data_t *channel = 0;
107 sdp_profile_desc_t profile;
108 cn->record = sdp_record_alloc();
109
110 // set the general service ID
111 sdp_uuid128_create( &svc_uuid, &svc_uuid_int );
112 sdp_set_service_id( cn->record, svc_uuid );
113
114 // set the service class
115 sdp_uuid16_create(&svc_class_uuid, SERIAL_PORT_SVCLASS_ID);
116 svc_class_list = sdp_list_append(0, &svc_class_uuid);
117 sdp_set_service_classes(cn->record, svc_class_list);
118
119 // set the Bluetooth profile information
120 sdp_uuid16_create(&profile.uuid, SERIAL_PORT_PROFILE_ID);
121 profile.version = 0x0100;
122 profile_list = sdp_list_append(0, &profile);
123 sdp_set_profile_descs(cn->record, profile_list);
124
125 // make the service record publicly browsable
126 sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
127 root_list = sdp_list_append(0, &root_uuid);
128 sdp_set_browse_groups(cn->record, root_list );
129
130 // set l2cap information
131 sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
132 l2cap_list = sdp_list_append( 0, &l2cap_uuid );
133 proto_list = sdp_list_append( 0, l2cap_list );
134
135 // register the RFCOMM channel for RFCOMM sockets
136 sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
137 channel = sdp_data_alloc(SDP_UINT8, &rfcomm_channel);
138 rfcomm_list = sdp_list_append( 0, &rfcomm_uuid );
139 sdp_list_append( rfcomm_list, channel );
140 sdp_list_append( proto_list, rfcomm_list );
141
142 access_proto_list = sdp_list_append( 0, proto_list );
143 sdp_set_access_protos( cn->record, access_proto_list );
144
145 // set the name, provider, and description
146 char *sn = getServiceName();
147 sdp_set_info_attr(cn->record, sn, service_prov, svc_dsc);
148 free(sn);
149
150 // connect to the local SDP server, register the service record,
151 // and disconnect
152 cn->session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
153 if (!cn->session || !cn->record) {
154 ERROR2("can not connect to SDP server %s (%d)",
155 strerror(sdp_get_error(cn->session)), sdp_get_error(cn->session));
156 ret = BOOL_NO;
157 }
158 if (cn->session && cn->record &&
159 sdp_record_register(cn->session, cn->record, 0) == -1) {
160 ERROR2("can not register SDP service %s (%d)",
161 strerror(sdp_get_error(cn->session)), sdp_get_error(cn->session));
162 ret = BOOL_NO;
163 }
164
165 // cleanup
166 sdp_data_free( channel );
167 sdp_list_free( l2cap_list, 0 );
168 sdp_list_free( proto_list, 0 );
169 sdp_list_free( rfcomm_list, 0 );
170 sdp_list_free( root_list, 0 );
171 sdp_list_free( access_proto_list, 0 );
172 sdp_list_free( svc_class_list, 0 );
173 sdp_list_free( profile_list, 0 );
174
175 return ret;
176 }
177 #endif
178
179 #ifdef USE_BT_FBSD
sdpRegister(ConnectInfo * connInfo)180 static boolean_t sdpRegister(ConnectInfo* connInfo)
181 {
182 _BtsppConnection* cn = (_BtsppConnection*) connInfo->connectionData;
183 if (!cn) {
184 return BOOL_NO;
185 }
186 boolean_t ret = BOOL_YES;
187
188 int channel,service;
189 bdaddr_t bt_addr_any;
190 sdp_lan_profile_t lan;
191
192 channel = connInfo->port;
193 service = SDP_SERVICE_CLASS_SERIAL_PORT;
194
195 cn->session = sdp_open_local(NULL);
196 if (cn->session == NULL) {
197 errx(1, "Unable to create local SDP session");
198 ret = BOOL_NO;
199 }
200 if (sdp_error(cn->session) != 0) {
201 errx(1, "Unable to open local SDP session. %s (%d)",
202 strerror(sdp_error(cn->session)), sdp_error(cn->session));
203 ret = BOOL_NO;
204 }
205 memset(&lan, 0, sizeof(lan));
206 lan.server_channel = channel;
207
208 memcpy(&bt_addr_any, NG_HCI_BDADDR_ANY, sizeof(bt_addr_any));
209
210 if (sdp_register_service(cn->session, service, &bt_addr_any,
211 (void *)&lan, sizeof(lan), &(cn->record)) != 0) {
212 errx(1, "Unable to register LAN service with "
213 "local SDP daemon. %s (%d)",
214 strerror(sdp_error(cn->session)), sdp_error(cn->session));
215 ret = BOOL_NO;
216 }
217
218 return ret;
219 }
220 #endif
221
sdpDeregister(_BtsppConnection * cn)222 static void sdpDeregister(_BtsppConnection* cn)
223 {
224 if (!cn) return;
225
226 logger(L_DBG, "Deregister SDP service");
227 #ifdef USE_BLUEZ
228 if (cn->session != NULL) {
229 sdp_record_unregister(cn->session, cn->record);
230 sdp_close(cn->session);
231 cn->session = NULL;
232 //sdp_record_free(cn->record);
233 }
234 #endif
235 #ifdef USE_BT_FBSD
236 if (cn->session != NULL) {
237 sdp_unregister_service(cn->session, cn->record);
238 sdp_close(cn->session);
239 sdp_close(cn->session);
240 cn->session = NULL;
241 }
242 #endif
243 }
244
btsppFD(ConnectInfo * conn)245 int btsppFD(ConnectInfo* conn)
246 {
247 _BtsppConnection* cn = (_BtsppConnection*) conn->connectionData;
248 if (!cn) {
249 return -1;
250 }
251 return (conn->state == PEER_WAIT_ACCEPT ||
252 conn->state == PEER_WAIT_LISTEN ? cn->serverFileDescriptor : cn->fileDescriptor);
253 }
254
btsppOpenInternal(ConnectInfo * connInfo)255 static int btsppOpenInternal(ConnectInfo* connInfo)
256 {
257 #ifdef USE_BLUEZ
258 struct sockaddr_rc bt_addr = { 0 };
259 #endif
260 #ifdef USE_BT_FBSD
261 struct sockaddr_rfcomm bt_addr;
262 #endif
263
264 struct sockaddr* socketaddr = NULL;
265
266 int addFamily = AF_UNSPEC;
267 int proto = 0;
268 size_t sz;
269
270 if (connInfo->connectionData && ((_BtsppConnection*) connInfo->connectionData)->serverFileDescriptor > 0) {
271 logger(L_ERR, "BTSPP socket was already opened");
272 return 1;
273 }
274
275 if (connInfo->connectionData) {
276 free(connInfo->connectionData);
277 }
278
279 connInfo->connectionData = (_BtsppConnection*) malloc(sizeof(_BtsppConnection));
280 _BtsppConnection* cn = (_BtsppConnection*) connInfo->connectionData;
281
282 cn->serverFileDescriptor = -1;
283 cn->fileDescriptor = -1;
284
285 #ifdef USE_BLUEZ
286 cn->session = NULL;
287 cn->record = NULL;
288
289 addFamily = AF_BLUETOOTH;
290 proto = BTPROTO_RFCOMM;
291 #endif
292
293 #ifdef USE_BT_FBSD
294 cn->session = NULL;
295 cn->record = 0;
296
297 addFamily = PF_BLUETOOTH;
298 proto = BLUETOOTH_PROTO_RFCOMM;
299 #endif
300
301 if (addFamily == AF_UNSPEC) { // no BT support
302 return -1;
303 }
304
305 if ((cn->serverFileDescriptor = socket(addFamily, SOCK_STREAM|SOCK_CLOEXEC, proto)) < 0) {
306 logger(L_ERR, "opening BT/RFCOMM socket");
307 errnoDebug("opening BT/RFCOMM socket",errno); // testing debug
308
309 printf("ERROR: opening BT/RFCOMM socket\n");
310 cn->serverFileDescriptor = -1;
311 return -1;
312 }
313 DEBUG2("[DS]: btsppOpenInternal srv descriptor %d", cn->serverFileDescriptor);
314
315 int optval = 1;
316 setsockopt(cn->serverFileDescriptor, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
317
318 /*
319 // Set non-blocking mode
320 if (-1 == (oldflags = fcntl(sportfd, F_GETFL, 0))) {
321 oldflags = 0;
322 }
323 fcntl(sportfd, F_SETFL, oldflags | O_NONBLOCK);
324 */
325
326
327 #ifdef USE_BLUEZ
328
329 //memset((void *) &bt_addr, 0, sizeof(bt_addr));
330 sz = sizeof(bt_addr);
331
332 // bind socket to the specified port of the first available local bluetooth adapter
333 bt_addr.rc_family = AF_BLUETOOTH;
334 bt_addr.rc_bdaddr = *BDADDR_ANY;
335 bt_addr.rc_channel = (uint8_t) connInfo->port;
336
337 // try to do the same manually
338 //char tmpstring[512];
339 //sprintf(tmpstring, "sdptool add --channel=%i SP", connInfo->port);
340 //sprintf(tmpstring, "sdptool add --channel=%i SP;sdptool setattr `sdptool search --bdaddr local SP|grep \"Service RecHandle\"|tail -1|cut -f 3 -d \" \"` 0x100 anyRemote", connInfo->port);
341 //sprintf(tmpstring, "bash -c \'A=`sdptool search --bdaddr local SP|grep \"Service Name\"|grep anyRemote|wc -l`; if [ \"x$A\" == \"x0\" ]; then sdptool add --channel=%i SP;sdptool setattr `sdptool search --bdaddr local SP|grep \"Service RecHandle\"|tail -1|cut -f 3 -d \" \"` 0x100 anyRemote; fi\'", connInfo->port);
342 //system(tmpstring);
343
344 if (sdpRegister(connInfo) == BOOL_YES) {
345 sprintf(tmp, "[DS]: registered SP for channel %i", connInfo->port);
346 logger(L_INF, tmp);
347 } else {
348
349 size_t sz;
350 char* btDaemon = executeCommandPipe("ps -eo args|grep bluetoothd|grep -v grep", &sz);
351 if (btDaemon) {
352 if (!strstr(btDaemon,"bluetoothd")) {
353 ERROR2("[DS]: ERROR: bluetoothd daemon is not started");
354 writeToFrontEnd("Bluetooth connection will not work. bluetoothd daemon is not started");
355 printf("ERROR: bluetoothd daemon is not started\n");
356 } else if (!strstr(btDaemon,"-C")) {
357 ERROR2("[DS]: It needs to run bluetoothd daemon with -C option");
358 writeToFrontEnd("Bluetooth connection will not work. It needs to run bluetoothd daemon with -C option");
359 printf("ERROR: Bluetooth connection will not work. It needs to run bluetoothd daemon with -C option\n");
360 } else {
361 ERROR2("[DS]: Not enough permissions to register SDP service");
362 writeToFrontEnd("Not enough permissions to register SDP service");
363 printf("ERROR: Not enough permissions to register SDP service\n");
364 }
365 } else {
366 ERROR2("[DS]: ERROR: bluetoothd daemon is not started");
367 writeToFrontEnd("Bluetooth connection will not work. bluetoothd daemon is not started");
368 printf("ERROR: bluetoothd daemon is not started\n");
369 }
370
371
372 /* } else {
373 char* sdp = executeCommandPipe("sdptool search --bdaddr local SP", &sz);
374 if (sdp) {
375 if (strstr(sdp,"Permission denied")) {
376 ERROR2("[DS]: Not enough permissions to run sdptool search command");
377 writeToFrontEnd("Not enough permissions to run sdptool search command");
378 } else {
379 printf("TEST %s", sdp);
380 char* ptrptr = NULL;
381 char* tok = strtok_r(sdp,"\\n", &ptrptr);
382
383 while (tok) {
384 printf("TEST %s", tok);
385 tok = strtok_r(NULL,"\\n", &ptrptr);
386
387 }
388 }
389 }
390 }
391 }*/
392 }
393
394 socketaddr=(struct sockaddr *)&bt_addr;
395 #endif
396
397 #ifdef USE_BT_FBSD
398 memset(&bt_addr, 0, sizeof(bt_addr));
399 sz = sizeof(bt_addr);
400
401 bt_addr.rfcomm_len = sizeof(bt_addr);
402 bt_addr.rfcomm_family = AF_BLUETOOTH;
403 bt_addr.rfcomm_channel = (uint8_t) connInfo->port;
404 if (sdpRegister(connInfo) == BOOL_YES) {
405 sprintf(tmp, "registered SP for channel %i", connInfo->port);
406 logger(L_INF, tmp);
407 }
408
409 socketaddr=(struct sockaddr *)&bt_addr;
410 #endif
411
412 if (bind(cn->serverFileDescriptor, (struct sockaddr *) socketaddr, sz) < 0) {
413 logger(L_ERR, "on binding");
414 printf("ERROR: on binding %d->%s\n", errno, strerror(errno));
415 return -1;
416 }
417
418 return 1;
419 }
420
421
btsppOpen(ConnectInfo * connInfo)422 int btsppOpen(ConnectInfo* connInfo)
423 {
424 DEBUG2("[DS]: btsppOpen %d", connInfo->mode);
425 if (connInfo->mode != SERVER_BT) {
426 DEBUG2("[DS]: btsppOpen wrong mode");
427 }
428 if (btsppOpenInternal(connInfo) < 0) {
429 return EXIT_NOK;
430 }
431 connInfo->state = PEER_WAIT_LISTEN;
432
433 // make work with bluez 5.x
434 btsppListen(connInfo);
435
436 return EXIT_OK;
437 }
438
btsppClose(ConnectInfo * connInfo,int final)439 void btsppClose(ConnectInfo* connInfo, int final)
440 {
441 if (final) {
442 logger(L_INF, "btsppClose");
443 }
444
445 _BtsppConnection* cn = (_BtsppConnection*) connInfo->connectionData;
446 if (!cn) return;
447
448 if (cn->fileDescriptor >= 0) {
449 if (final) {
450 logger(L_INF, "btsppClose close socket");
451 }
452 close(cn->fileDescriptor);
453 cn->fileDescriptor = -1;
454 }
455 if (cn->serverFileDescriptor >= 0) {
456 if (final) {
457 logger(L_INF, "closeSocketPort close server socket");
458 }
459 close(cn->serverFileDescriptor);
460 cn->serverFileDescriptor = -1;
461 }
462
463 if (final) {
464 sdpDeregister(cn);
465 }
466
467 free(cn);
468 connInfo->connectionData = NULL;
469 connInfo->state = PEER_DISCONNECTED;
470 }
471
btsppReset(ConnectInfo * conn)472 void btsppReset(ConnectInfo* conn)
473 {
474 _BtsppConnection* cn = (_BtsppConnection*) conn->connectionData;
475 if (cn) {
476 if (cn->fileDescriptor >= 0) {
477 close(cn->fileDescriptor);
478 cn->fileDescriptor = -1;
479 }
480 conn->state = PEER_WAIT_ACCEPT;
481 } else {
482 conn->state = PEER_DISCONNECTED; // should not happens
483 }
484 }
485
486 //
487 // Setup peer
488 //
btsppListen(ConnectInfo * conn)489 int btsppListen(ConnectInfo* conn)
490 {
491 logger(L_INF, "[DS]: btsppListen");
492
493 _BtsppConnection* cn = (_BtsppConnection*) conn->connectionData;
494 if (!cn) {
495 return -1;
496 }
497 INFO2("[DS]: btsppListen: fd %d", cn->serverFileDescriptor);
498 int ret = listen(cn->serverFileDescriptor,0);
499 if (ret >= 0) {
500 conn->state = PEER_WAIT_ACCEPT;
501 }
502 INFO2("[DS]: btsppListen ret=%d",ret);
503 return (ret < 0 ? -1 : 1);
504 }
505
506 //
507 // Wait for incoming connection
508 //
btsppAccept(ConnectInfo * connInfo)509 int btsppAccept(ConnectInfo* connInfo)
510 {
511 logger(L_INF, "[DS]: btsppAccept");
512
513 _BtsppConnection* cn = (_BtsppConnection*) connInfo->connectionData;
514 if (!cn) return -1;
515
516 logger(L_INF, "[DS]: BTSPP server mode: Waiting connection");
517 int cnt;
518 socklen_t sz;
519
520 struct sockaddr* socketaddr = NULL;
521
522 #ifdef USE_BLUEZ
523 struct sockaddr_rc bt_addr;
524 bdaddr_t ba;
525 #endif
526 #ifdef USE_BT_FBSD
527 struct sockaddr_rfcomm bt_addr;
528 #endif
529
530 logger(L_INF, "btsppAccept");
531 cnt = 0;
532
533 #if defined(USE_BLUEZ) || defined(USE_BT_FBSD)
534 socketaddr=(struct sockaddr *)&bt_addr;
535 sz = sizeof(bt_addr);
536 #endif
537
538 while (stillRun) {
539
540 INFO2("btsppAccept: fd %d", cn->serverFileDescriptor);
541
542 cn->fileDescriptor = accept(cn->serverFileDescriptor, (struct sockaddr *) socketaddr, &sz);
543
544 if (cn->fileDescriptor < 0 && errno == EAGAIN) {
545
546 if (cnt >= 60) { // Print to log every minute
547 logger(L_INF, "btsppAccept: waiting for connection");
548 //printf(".");
549 cnt = 0;
550 }
551 fflush(stdout);
552
553 sleep(1);
554 cnt++;
555
556 continue;
557 }
558
559 if (cn->fileDescriptor < 0) {
560 logger(L_ERR, "btsppAccept: on accept");
561 return -1;
562 }
563 INFO2("[DS]: btsppAccept fd=%d",cn->fileDescriptor);
564
565 char* btAddress = NULL;
566 #ifdef USE_BLUEZ
567 baswap(&ba, &bt_addr.rc_bdaddr);
568 btAddress = batostr(&ba);
569 #endif
570 #ifdef USE_BT_FBSD
571 btAddress = strdup(bt_ntoa(&bt_addr.rfcomm_bdaddr, NULL));
572 #endif
573
574 if (!isAllowed(btAddress)) {
575 INFO2("btsppAccept: host %s is not in the list of accepted host, close connection", (btAddress ? btAddress : "NULL"));
576 write(cn->fileDescriptor,CMD_STR_DISCONNECT,strlen(CMD_STR_DISCONNECT));
577
578 close(cn->fileDescriptor);
579 cn->fileDescriptor = -1;
580 connInfo->state = PEER_DISCONNECTED;
581
582 free(btAddress);
583
584 return -1;
585 }
586
587 if (getUsePassword() && !getBemused()) {
588 logger(L_DBG,"[DS]: btsppAccept: Do password verification");
589
590 int ret = EXIT_OK;
591
592 int i=0;
593 for ( ; i<3; i++) {
594
595 ret = verifyPassword(cn->fileDescriptor);
596
597 if (ret == EXIT_OK) { // got it
598 break;
599
600 }
601 }
602
603 if (ret != EXIT_OK) {
604
605 if (ret == EXIT_NOK) {
606 write(cn->fileDescriptor,CMD_STR_DISCONNECT,strlen(CMD_STR_DISCONNECT));
607 }
608
609 close(cn->fileDescriptor);
610 cn->fileDescriptor = -1;
611 connInfo->state = PEER_DISCONNECTED;
612
613 free(btAddress);
614
615 return -1;
616 }
617 logger(L_DBG,"[DS]: socketAccept: Password verification OK");
618 }
619
620 logger(L_INF, "btsppAccept: accepted");
621 connInfo->state = PEER_CONNECTED;
622
623 freeBtAddress();
624
625 sprintf(tmp, "btsppAccept: remote BT address is %s", (btAddress ? btAddress : "NULL"));
626 setBtAddress(btAddress);
627 logger(L_INF, tmp);
628
629 // force to detect cover size. need to do that before (Connect) or syncPeer() handling
630 getClientSize(connInfo->id, cn->fileDescriptor);
631
632 break;
633 }
634 return 1;
635 }
636
btsppWrite(ConnectInfo * connInfo,dMessage * msg)637 int btsppWrite(ConnectInfo* connInfo, dMessage* msg)
638 {
639 _BtsppConnection* cn = (_BtsppConnection*) connInfo->connectionData;
640 if (!cn) {
641 logger(L_DBG,"[DS]: btsppWrite() no connection data");
642 return EXIT_NOK;
643 }
644
645 const char* command = msg->value;
646 int count = msg->size;
647
648 //logger(L_DBG, "btsppWrite");
649 if (!command || count <= 0) {
650 return EXIT_OK;
651 }
652
653 if (strcmp("End();",command) == 0) { // used only for WEB/CMXML
654 return EXIT_OK;
655 }
656
657 // send command
658 if (cn->fileDescriptor >= 0 && count > 0) {
659
660 memset(tmp, 0, MAXMAXLEN);
661 strcat(tmp, "btsppWrite ");
662
663 int logSz = (count > 256 ? 255 : count);
664
665 // it is possible to get binary data here
666 memcpy(tmp, command, logSz); // Do not dump long commands
667 tmp[logSz] = '\0';
668 logger(L_DBG, tmp);
669
670 sprintf(tmp, "btsppWrite %d bytes", count);
671 logger(L_INF, tmp);
672
673 int n = write(cn->fileDescriptor,command,count);
674 if (n < 0) {
675 logger(L_ERR, "error writing to BTSPP socket");
676 return EXIT_NOK;
677 }
678 return EXIT_OK;
679 } else {
680 logger(L_ERR, "error writing to socket: already closed");
681 }
682 return EXIT_NOK;
683 }
684