From 6aa332b56e7dcbf1d0d20731f9fd4ee07a9bfc2e Mon Sep 17 00:00:00 2001 From: btsimonh Date: Sun, 30 Oct 2022 06:24:16 +0000 Subject: [PATCH] add driver SSDP. This is NOT started by default. run cmd "startdriver SSDP" - and you device will appear in windows networking.... We COULD in the future 'hear' notify messages from other OBK devices, so that all devices could know about each other... or even send an MSEARCH. --- src/driver/drv_main.c | 2 + src/driver/drv_ssdp.c | 432 ++++++++++++++++++++++++++++++++++++++++++ src/driver/drv_ssdp.h | 7 + 3 files changed, 441 insertions(+) create mode 100644 src/driver/drv_ssdp.c create mode 100644 src/driver/drv_ssdp.h diff --git a/src/driver/drv_main.c b/src/driver/drv_main.c index bf8b8c68b..df902dde0 100644 --- a/src/driver/drv_main.c +++ b/src/driver/drv_main.c @@ -12,6 +12,7 @@ #include "drv_ntp.h" #include "../httpserver/new_http.h" #include "drv_public.h" +#include "drv_ssdp.h" const char* sensor_mqttNames[OBK_NUM_MEASUREMENTS] = { "voltage", @@ -80,6 +81,7 @@ static driver_t g_drivers[] = { { "IR", DRV_IR_Init, NULL, NULL, DRV_IR_RunFrame, NULL, NULL, false }, { "DGR", DRV_DGR_Init, DRV_DGR_RunEverySecond, NULL, DRV_DGR_RunQuickTick, DRV_DGR_Shutdown, DRV_DGR_OnChannelChanged, false }, { "DDP", DRV_DDP_Init, NULL, NULL, DRV_DDP_RunFrame, DRV_DDP_Shutdown, NULL, false }, + { "SSDP", DRV_SSDP_Init, DRV_SSDP_RunEverySecond, NULL, DRV_SSDP_RunQuickTick, DRV_SSDP_Shutdown, NULL, false }, #endif { "SM2135", SM2135_Init, SM2135_RunFrame, NULL, NULL, NULL, SM2135_OnChannelChanged, false }, { "BP5758D", BP5758D_Init, BP5758D_RunFrame, NULL, NULL, NULL, BP5758D_OnChannelChanged, false }, diff --git a/src/driver/drv_ssdp.c b/src/driver/drv_ssdp.c new file mode 100644 index 000000000..92e287d13 --- /dev/null +++ b/src/driver/drv_ssdp.c @@ -0,0 +1,432 @@ +//////////////////////////////////////////////////////// +// +// OpenBeken SSDP support +// listens on multicast 239.255.255.250:1900 +// responds to ANY M-SEARCH with a valid response. +// add an HTTP service routine for /ssdp.xml +// which returns the SSDP Description +// +// Result - start this driver and your device is visible in Windows +// 'Other Devices' in network. +// double click a device to go to the device webpage +// +// copyright Simon Hailes & OpenBeken +///////////////////////////////////////////////////////// + +#include "../new_common.h" +#include "../new_pins.h" +#include "../new_cfg.h" +// Commands register, execution API and cmd tokenizer +#include "../cmnds/cmd_public.h" +#include "../logging/logging.h" +#include "../devicegroups/deviceGroups_public.h" +#include "lwip/sockets.h" +#include "lwip/ip_addr.h" +#include "lwip/inet.h" +#include "../obk_config.h" +#include "../httpserver/new_http.h" +#include "common_math.h" + +static const char* ssdp_group = "239.255.255.250"; +static int ssdp_port = 1900; + +static int g_ssdp_socket_receive = -1; + +static char g_ssdp_uuid[40] = "e427ce1a-3e80-43d0-ad6f-89ec42e46363"; + +static void DRV_SSDP_Send_Notify(); +static int ssdp_timercount = 0; + +const char *HAL_GetMyIPString(); + +// allocated at first use, freed if stopped +static char *advert_message = NULL; +static char *udp_msgbuf = NULL; +#define UDP_MSGBUF_LEN 500 +static char *notify_message = NULL; +static char *http_message = NULL; +int http_message_len = 0; + + +/////////////////////////////// +// private functions, only used by the public functions... + +static void DRV_SSDP_CreateSocket_Receive() { + + struct sockaddr_in addr; + struct ip_mreq mreq; + int flag = 1; + int broadcast = 1; + int iResult = 1; + + // create what looks like an ordinary UDP socket + // + g_ssdp_socket_receive = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (g_ssdp_socket_receive < 0) { + g_ssdp_socket_receive = -1; + addLogAdv(LOG_INFO, LOG_FEATURE_HTTP,"DRV_SSDP_CreateSocket_Receive: failed to do socket\n"); + return ; + } + + if(broadcast) + { + + iResult = setsockopt(g_ssdp_socket_receive, SOL_SOCKET, SO_BROADCAST, (char *)&flag, sizeof(flag)); + if (iResult != 0) + { + addLogAdv(LOG_INFO, LOG_FEATURE_HTTP,"DRV_SSDP_CreateSocket_Receive: failed to do setsockopt SO_BROADCAST\n"); + close(g_ssdp_socket_receive); + g_ssdp_socket_receive = -1; + return ; + } + } + else{ + // allow multiple sockets to use the same PORT number + // + if ( + setsockopt( + g_ssdp_socket_receive, SOL_SOCKET, SO_REUSEADDR, (char*) &flag, sizeof(flag) + ) < 0 + ){ + addLogAdv(LOG_INFO, LOG_FEATURE_HTTP,"DRV_SSDP_CreateSocket_Receive: failed to do setsockopt SO_REUSEADDR\n"); + close(g_ssdp_socket_receive); + g_ssdp_socket_receive = -1; + return ; + } + } + + // set up destination address + // + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); // differs from sender + addr.sin_port = htons(ssdp_port); + + // bind to receive address + // + if (bind(g_ssdp_socket_receive, (struct sockaddr*) &addr, sizeof(addr)) < 0) { + addLogAdv(LOG_INFO, LOG_FEATURE_HTTP,"DRV_DGR_CreateSocket_Receive: failed to do bind\n"); + close(g_ssdp_socket_receive); + g_ssdp_socket_receive = -1; + return ; + } + +/* if(broadcast) + { + + } + else*/ + { + + // use setsockopt() to request that the kernel join a multicast group + // + mreq.imr_multiaddr.s_addr = inet_addr(ssdp_group); + //mreq.imr_interface.s_addr = htonl(INADDR_ANY); + mreq.imr_interface.s_addr = htonl(INADDR_ANY);//inet_addr(MY_CAPTURE_IP); + ///mreq.imr_interface.s_addr = inet_addr("192.168.0.122"); + iResult = setsockopt( + g_ssdp_socket_receive, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &mreq, sizeof(mreq) + ); + if ( + iResult < 0 + ){ + addLogAdv(LOG_INFO, LOG_FEATURE_HTTP,"DRV_SSDP_CreateSocket_Receive: failed to do setsockopt IP_ADD_MEMBERSHIP %i\n",iResult); + close(g_ssdp_socket_receive); + g_ssdp_socket_receive = -1; + return ; + } + } + + lwip_fcntl(g_ssdp_socket_receive, F_SETFL,O_NONBLOCK); + + addLogAdv(LOG_INFO, LOG_FEATURE_HTTP,"DRV_SSDP_CreateSocket_Receive: Socket created, waiting for packets\n"); +} + + + +static const char message_template[] = +"HTTP/1.1 200 OK\r\n" \ +"CACHE-CONTROL: max-age=1800\r\n" \ +"EXT:\r\n" \ +"LOCATION: http://%s:80/ssdp.xml\r\n" \ +"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" \ +"01-NLS: %s\r\n" \ +"ST: upnp:rootdevice\r\n" \ +"SERVER: OpenBk\r\n" \ +"USN: %s::upnp:rootdevice\r\n" \ +"BOOTID.UPNP.ORG: 0\r\n" \ +"CONFIGID.UPNP.ORG: 1\r\n" \ +"\r\n\r\n" \ +/* like e427ce1a-3e80-43d0-ad6f-89ec42e46363*/ \ +/*"last-seen: 1477147409.432466\r\n"*/ \ +/*"DATE: Sat, 22 Oct 2016 14:44:26 GMT\r\n"*/ \ +; + + +static void DRV_SSDP_Send_Advert_To(struct sockaddr_in *addr) { + int nbytes; + const char *myip = HAL_GetMyIPString(); + + if (g_ssdp_socket_receive <= 0) { + addLogAdv(LOG_INFO, LOG_FEATURE_HTTP,"DRV_SSDP_Send_Advert_To: no socket"); + return ; + } + + if (!advert_message){ + advert_message = (char *)malloc(strlen(message_template) + 100); + } + + sprintf(advert_message, message_template, + myip, + g_ssdp_uuid, + g_ssdp_uuid); + + // set up destination address + // + nbytes = sendto( + g_ssdp_socket_receive, + (const char*) advert_message, + strlen(advert_message), + 0, + (struct sockaddr*) addr, + sizeof(struct sockaddr) + ); + + addLogAdv(LOG_INFO, LOG_FEATURE_HTTP,"DRV_SSDP_Send_Advert_To: sent message"); +} + + + + + +static const char notify_template[] = +"NOTIFY * HTTP/1.1\r\n" \ +"HOST: 239.255.255.250:1900\r\n" \ +"CACHE-CONTROL: max-age=1800\r\n" \ +"LOCATION: http://%s:80/ssdp.xml'\r\n" \ +"NTS: ssdp:alive\r\n" \ +"NT: upnp:rootdevice\r\n" \ +"USN: uuid:%s::upnp:rootdevice\r\n" \ +"\r\n\r\n" \ +; + + + +static void DRV_SSDP_Send_Notify() { + int nbytes; + struct sockaddr_in multicastaddr; + + if (g_ssdp_socket_receive <= 0){ + addLogAdv(LOG_ERROR, LOG_FEATURE_HTTP,"DRV_SSDP_Send_Notify: no socket"); + return; + } + // set up destination address + // + memset(&multicastaddr, 0, sizeof(multicastaddr)); + multicastaddr.sin_family = AF_INET; + multicastaddr.sin_addr.s_addr = inet_addr(ssdp_group); + multicastaddr.sin_port = htons(ssdp_port); + + const char *myip = HAL_GetMyIPString(); + + if (!notify_message){ + notify_message = (char *)malloc(strlen(notify_template) + 100); + } + + sprintf(notify_message, notify_template, myip, g_ssdp_uuid); + + // set up destination address + // + nbytes = sendto( + g_ssdp_socket_receive, + (const char*) notify_message, + strlen(notify_message), + 0, + (struct sockaddr*) &multicastaddr, + sizeof(multicastaddr) + ); + + if (nbytes <= 0){ + addLogAdv(LOG_INFO, LOG_FEATURE_HTTP,"#### ERROR ##### DRV_SSDP_Send_Notify: sent message %d bytes", nbytes); + } else { + addLogAdv(LOG_EXTRADEBUG, LOG_FEATURE_HTTP,"DRV_SSDP_Send_Notify: sent message %d bytes", nbytes); + } +} + + +static const char *http_reply = +"\r\n" \ +" \r\n" \ +" 1\r\n" \ +" 0\r\n" \ +" \r\n" \ +" \r\n" \ +" urn:schemas-upnp-org:device:Basic:1\r\n" \ +" %s\r\n" \ +" OpenBeken\r\n" \ +" https://github.com/openshwprojects/OpenBK7231T_App\r\n" \ +" OpenBeken\r\n" \ +" %s\r\n" \ +" 1\r\n" \ +" https://github.com/openshwprojects/OpenBK7231T_App\r\n" \ +" 1\r\n" \ +" uuid:%s\r\n" \ +" \r\n" \ +" \r\n" \ +" http://%s:80\r\n" \ +" urn:schemas-upnp-org:service:openbk:1\r\n" \ +" urn:upnp-org:serviceId:1" \ +" /\r\n" \ +" \r\n" \ +" /ssdp.xml\r\n" \ +" \r\n" \ +" \r\n" \ +" http://%s:80/\r\n" \ +" \r\n" \ +"\r\n" +; + +static int DRV_SSDP_Service_Http(http_request_t* request){ + const char *myip = HAL_GetMyIPString(); + const char *freindly_name = CFG_GetDeviceName(); + + if (!http_message){ + http_message_len = + strlen(http_reply) + + strlen(freindly_name) + + strlen(PLATFORM_MCU_NAME) + + strlen(g_ssdp_uuid) + + strlen(myip) + + strlen(myip) + + 40; + http_message = (char *)malloc(http_message_len); + } + + addLogAdv(LOG_INFO, LOG_FEATURE_HTTP,"###################### DRV_SSDP_Service_Http"); + + snprintf(http_message, http_message_len, http_reply, + freindly_name, + PLATFORM_MCU_NAME, + g_ssdp_uuid, + myip, + myip); + + http_setup(request, "application/xml"); + poststr(request, http_message); + poststr(request, NULL); + return 0; +} +// end private functions +/////////////////////////////// + + +/////////////////////////////////////////////// +// public functions, only used in drv_main + +void DRV_SSDP_Init() +{ + // like "e427ce1a-3e80-43d0-ad6f-89ec42e46363"; + sprintf(g_ssdp_uuid, "%08x-%04x-%04x-%04x-%04x%08x", + (unsigned int)rand(), + (unsigned int)rand()&0xffff, + (unsigned int)rand()&0xffff, + (unsigned int)rand()&0xffff, + (unsigned int)rand()&0xffff, + (unsigned int)rand() + ); + + DRV_SSDP_CreateSocket_Receive(); + HTTP_RegisterCallback("/ssdp.xml", HTTP_GET, DRV_SSDP_Service_Http); + //CMD_RegisterCommand("SSDPNotify", "", CMD_SSDP_Notify, "qqq", NULL); +} + +void DRV_SSDP_RunEverySecond() { + ssdp_timercount++; + if (ssdp_timercount >= 30){ + // multicast a notify + DRV_SSDP_Send_Notify(); + ssdp_timercount = 0; + } +} + +void DRV_SSDP_RunQuickTick() { + + if (g_ssdp_socket_receive <= 0) { + return ; + } + // now just enter a read-print loop + // + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + + if (!udp_msgbuf){ + udp_msgbuf = (char *)malloc(UDP_MSGBUF_LEN); + } + + socklen_t addrlen = sizeof(addr); + int nbytes = recvfrom( + g_ssdp_socket_receive, + udp_msgbuf, + UDP_MSGBUF_LEN, + 0, + (struct sockaddr *) &addr, + &addrlen + ); + if (nbytes <= 0) { + //addLogAdv(LOG_INFO, LOG_FEATURE_HTTP,"nothing\n"); + return ; + } + // just so we can terminate for print + if (nbytes >= UDP_MSGBUF_LEN){ + nbytes = UDP_MSGBUF_LEN-1; + } + addLogAdv(LOG_EXTRADEBUG, LOG_FEATURE_HTTP,"Received %i bytes from %s",nbytes,inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr)); + udp_msgbuf[nbytes] = 0; + addLogAdv(LOG_EXTRADEBUG, LOG_FEATURE_HTTP,"data: %s",udp_msgbuf); + udp_msgbuf[nbytes] = '\0'; + + /* we may get: + M-SEARCH * HTTP/1.1 + HOST:239.255.255.250:1900 + ST:upnp:rootdevice + MX:2 + MAN:"ssdp:discover" + */ + + // if search, then respond + // we SHOULD be a little more specific!!! + if (!strncmp(udp_msgbuf, "M-SEARCH", 8)){ + // reply with our advert to the sender + addLogAdv(LOG_EXTRADEBUG, LOG_FEATURE_HTTP,"Is MSEARCH - responding"); + DRV_SSDP_Send_Advert_To(&addr); + } +} + + +void DRV_SSDP_Shutdown() +{ + if(g_ssdp_socket_receive>=0) { + close(g_ssdp_socket_receive); + g_ssdp_socket_receive = -1; + } + + if (advert_message){ + free(advert_message); + advert_message = NULL; + } + if (udp_msgbuf){ + free(udp_msgbuf); + udp_msgbuf = NULL; + } + if (notify_message) { + free(notify_message); + notify_message = NULL; + } + if (http_message) { + free(http_message); + http_message = NULL; + } +} + +// end public +/////////////////////////////////////////////// diff --git a/src/driver/drv_ssdp.h b/src/driver/drv_ssdp.h new file mode 100644 index 000000000..7796cc0fb --- /dev/null +++ b/src/driver/drv_ssdp.h @@ -0,0 +1,7 @@ + + + +void DRV_SSDP_RunEverySecond(); +void DRV_SSDP_RunQuickTick(); +void DRV_SSDP_Shutdown(); +void DRV_SSDP_Init(); \ No newline at end of file