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