From d53b39a2807fb410f03f0caa888464377130c2fe Mon Sep 17 00:00:00 2001 From: Omar Kallel <omar.kallel@pivasoftware.com> Date: Fri, 24 Jan 2020 14:38:17 +0100 Subject: [PATCH] miniupnpc: periodic desvoery, download description files, corresponding ubus --- net/miniupnpc/Makefile | 13 +- net/miniupnpc/files/periodic_ssdp.init | 23 + .../patches/400-periodic_discovery_ubus.patch | 486 ++++++++++++++++++ 3 files changed, 521 insertions(+), 1 deletion(-) create mode 100644 net/miniupnpc/files/periodic_ssdp.init create mode 100644 net/miniupnpc/patches/400-periodic_discovery_ubus.patch diff --git a/net/miniupnpc/Makefile b/net/miniupnpc/Makefile index a458b29486..029eab271f 100644 --- a/net/miniupnpc/Makefile +++ b/net/miniupnpc/Makefile @@ -36,7 +36,7 @@ define Package/miniupnpc CATEGORY:=Network SUBMENU:=Firewall TITLE+= client - DEPENDS:=+libminiupnpc + DEPENDS:=+libminiupnpc +libubox +libubus +libjson-c +libblobmsg-json endef define Package/libminiupnpc @@ -51,6 +51,13 @@ CMAKE_OPTIONS += -DUPNPC_BUILD_TESTS=OFF TARGET_CFLAGS += $(FPIC) TARGET_LDFLAGS += -Wl,--gc-sections,--as-needed +USE_LOCAL=$(shell ls ./src/ 2>/dev/null >/dev/null && echo 1) +ifneq ($(USE_LOCAL),) +define Build/Prepare + $(CP) ./src/* $(PKG_BUILD_DIR)/ +endef +endif + define Build/InstallDev $(INSTALL_DIR) $(1)/usr/include/miniupnpc $(CP) \ @@ -63,9 +70,13 @@ define Build/InstallDev endef define Package/miniupnpc/install + echo "Helllo install" $(INSTALL_DIR) $(1)/usr/bin $(INSTALL_BIN) $(PKG_BUILD_DIR)/upnpc-shared $(1)/usr/bin/upnpc $(INSTALL_BIN) $(PKG_BUILD_DIR)/listdevices $(1)/usr/bin/ + $(INSTALL_BIN) $(PKG_BUILD_DIR)/periodic_ssdp $(1)/usr/bin/ + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) ./files/periodic_ssdp.init $(1)/etc/init.d/periodic_ssdp endef define Package/libminiupnpc/install diff --git a/net/miniupnpc/files/periodic_ssdp.init b/net/miniupnpc/files/periodic_ssdp.init new file mode 100644 index 0000000000..a2b80f5af7 --- /dev/null +++ b/net/miniupnpc/files/periodic_ssdp.init @@ -0,0 +1,23 @@ +#!/bin/sh /etc/rc.common + +START=21 +STOP=89 + +USE_PROCD=1 +NAME=periodic_ssdp + +start_service() { + procd_open_instance + procd_set_param command "/usr/bin/periodic_ssdp" + procd_set_param respawn + procd_close_instance +} + +stop() { + service_stop /usr/bin/periodic_ssdp +} + +reload_service() { + stop + start +} \ No newline at end of file diff --git a/net/miniupnpc/patches/400-periodic_discovery_ubus.patch b/net/miniupnpc/patches/400-periodic_discovery_ubus.patch new file mode 100644 index 0000000000..8420ca5671 --- /dev/null +++ b/net/miniupnpc/patches/400-periodic_discovery_ubus.patch @@ -0,0 +1,486 @@ +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -147,6 +147,9 @@ + add_executable (listdevices listdevices.c) + target_link_libraries (listdevices PRIVATE libminiupnpc-shared) + target_include_directories(listdevices PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) ++ ++ add_executable (periodic_ssdp periodic_ssdp.c upnp_ubus.c) ++ target_link_libraries (periodic_ssdp PRIVATE libminiupnpc-shared ubox ubus json-c blobmsg_json) + endif () + endif () + +--- a/periodic_ssdp.c ++++ b/periodic_ssdp.c +@@ -0,0 +1,95 @@ ++/* ++* 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. ++* ++* Copyright (C) 2019 iopsys Software Solutions AB ++* Author: Omar Kallel <omar.kallel@pivasoftware.com> ++*/ ++ ++#include <stdio.h> ++#include "upnp_ubus.h" ++#include "miniupnpc.h" ++#include <libubox/uloop.h> ++#include <sys/types.h> ++#include <sys/stat.h> ++#include <unistd.h> ++ ++ ++LIST_HEAD(descriptions_list); ++ ++static void ssdp_discover_devices(struct uloop_timeout *timeout); ++ ++static struct uloop_timeout ssdp_timer = { .cb = ssdp_discover_devices }; ++ ++static void discover_upnp_connected_devices(){ ++ char **urlparams, *desc_name = NULL, *filepath = NULL, *download_cmd = NULL; ++ struct UPNPDev *dev; ++ int error; ++ size_t length; ++ int is_device_desc; ++ ++ /* ++ * Discover devices ++ */ ++ devlist = upnpDiscoverAll(2000, NULL, NULL, 0, 0, 2, &error); ++ ++ /* ++ * Download description files ++ */ ++ check_create_dir("/etc/upnpc"); ++ check_create_dir("/etc/upnpc/descriptions"); ++ ++ if(devlist) { ++ for(dev = devlist; dev != NULL; dev = dev->pNext) { ++ urlparams= strsplit(strstr(dev->descURL, "://")?strstr(dev->descURL, "://")+3:dev->descURL, "/", &length); ++ asprintf(&desc_name, "%s_%s", urlparams[0], urlparams[length-1]); ++ asprintf(&filepath, "/etc/upnpc/descriptions/%s", desc_name); ++ asprintf(&download_cmd, "wget -q -O %s %s", filepath, dev->descURL); ++ if(!is_desc_elt_exit_in_list(dev->descURL)) { ++ if(strstr(dev->st, ":rootdevice") != NULL || strstr(dev->usn, ":device:") != NULL) ++ is_device_desc = 1; ++ else ++ is_device_desc = 0; ++ add_desc_to_descriptions_list(filepath, dev->descURL, is_device_desc); ++ } ++ if (access(filepath, F_OK)) { ++ system(download_cmd); ++ } ++ ++ FREE(desc_name); ++ FREE(filepath); ++ FREE(download_cmd); ++ } ++ } ++ uloop_timeout_set(&ssdp_timer, 10000); ++} ++ ++static void ssdp_discover_devices(struct uloop_timeout *timeout) { ++ discover_upnp_connected_devices(); ++} ++ ++int main() { ++ struct ubus_context *ctx = NULL; ++ const char *ubus_socket = NULL; ++ int ret; ++ ++ uloop_init(); ++ discover_upnp_connected_devices(); ++ ++ ctx = ubus_connect(ubus_socket); ++ if (!ctx) { ++ fprintf(stderr, "Failed to connect to ubus\n"); ++ return -1; ++ } ++ ubus_add_uloop(ctx); ++ ++ ret = ubus_add_object(ctx, &upnpc_object); ++ if (ret) ++ fprintf(stderr, "Failed to add 'usp' ubus object: %s\n", ubus_strerror(ret)); ++ ++ uloop_run(); ++ uloop_done(); ++ return 0; ++} +--- a/upnp_ubus.c ++++ b/upnp_ubus.c +@@ -0,0 +1,300 @@ ++/* ++* 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. ++* ++* Copyright (C) 2019 iopsys Software Solutions AB ++* Author: Omar Kallel <omar.kallel@pivasoftware.com> ++* ++*/ ++ ++#include <stdio.h> ++#include "miniupnpc.h" ++#include "upnp_ubus.h" ++#include "minixml.h" ++#include "igd_desc_parse.h" ++ ++struct UPNPDev * devlist = NULL; ++ ++const struct blobmsg_policy upnp_dev_policy[0] = { ++ //[0] = { .name = "path", .type = BLOBMSG_TYPE_STRING } ++}; ++ ++struct ubus_method upnpc_methods[] = { ++ UBUS_METHOD("discovery", upnp_discovery_res, upnp_dev_policy), ++ UBUS_METHOD("description", upnp_description_res, upnp_dev_policy), ++}; ++ ++struct ubus_object_type upnpc_type = UBUS_OBJECT_TYPE("upnpc", upnpc_methods); ++ ++struct ubus_object upnpc_object = { ++ .name = "upnpc", ++ .type = &upnpc_type, ++ .methods = upnpc_methods, ++ .n_methods = ARRAY_SIZE(upnpc_methods), ++}; ++ ++void xmplparsestartelt(void * d, const char * name, int l) ++{ ++ struct xml_parse_strct *xmldata = (struct xml_parse_strct *)d; ++ struct xml_parse_list_elt *xmlelt = NULL, *parentelt = NULL; ++ ++ xmldata->node_name = (char *)malloc(l*sizeof(char)); ++ memcpy(xmldata->node_name, name, l); ++ xmldata->node_name[l] = '\0'; ++ ++ if(memcmp(name, "service", l) == 0 || memcmp(name, "device", l) == 0) { ++ if(!list_empty(xmldata->xml_elts)) { ++ parentelt = (struct xml_parse_list_elt *)xmldata->xml_elts->prev; ++ } ++ xmlelt = calloc(1, sizeof(struct xml_parse_list_elt)); ++ list_add_tail(&xmlelt->list, xmldata->xml_elts); ++ xmlelt->nodename = (char *)malloc(l*sizeof(char)); ++ memcpy(xmlelt->nodename, name, l); ++ xmlelt->nodename[l] = '\0'; ++ xmlelt->eltobj = json_object_new_object(); ++ if (memcmp(name, "device", l) == 0) { ++ xmlelt->elt_type = DEVICE; ++ } else if (memcmp(name, "service", l) == 0) { ++ xmlelt->elt_type = SERVICE; ++ } ++ if(parentelt) { ++ json_object_object_add(xmlelt->eltobj, "parent_dev", json_object_new_string(parentelt->udn)); ++ } else { ++ json_object_object_add(xmlelt->eltobj, "parent_dev", json_object_new_string("")); ++ } ++ xmlelt->udn = NULL; ++ } ++} ++ ++void xmplparsedata(void * d, const char * data, int l) ++{ ++ struct xml_parse_strct *xmldata = (struct xml_parse_strct *)d; ++ struct xml_parse_list_elt *xmlelt = NULL; ++ ++ int length = get_data_length(data, l); ++ char *value = (char *)malloc(length*sizeof(char)); ++ memcpy(value, data, length); ++ value[length]='\0'; ++ ++ if(!list_empty(xmldata->xml_elts)) { ++ xmlelt = (struct xml_parse_list_elt *)xmldata->xml_elts->prev; ++ } ++ if (xmlelt && (xmlelt->elt_type == DEVICE || xmlelt->elt_type == SERVICE)) { ++ if(strcmp(xmldata->node_name, "UDN") == 0) { ++ xmlelt->udn = (char *)malloc(length*sizeof(char)); ++ memcpy(xmlelt->udn, value, length); ++ xmlelt->udn[length] = '\0'; ++ } ++ json_object_object_add(xmlelt->eltobj, xmldata->node_name, json_object_new_string(value)); ++ } ++ //free(value); ++} ++ ++void xmplparseendelt(void * d, const char * name, int l) ++{ ++ struct xml_parse_strct *xmldata = (struct xml_parse_strct *)d; ++ struct xml_parse_list_elt *xmlelt; ++ ++ if(memcmp(name, "service", l) == 0 || memcmp(name, "device", l) == 0) { ++ xmlelt = (struct xml_parse_list_elt *)xmldata->xml_elts->prev; ++ if(memcmp(name, "device", l) == 0) { ++ json_object_array_add(xmldata->devicesarray, xmlelt->eltobj); ++ } ++ if(memcmp(name, "service", l) == 0) { ++ json_object_array_add(xmldata->servicesarray, xmlelt->eltobj); ++ } ++ xmlelt->eltobj = NULL; ++ list_del(&xmlelt->list); ++ } ++} ++ ++int upnp_discovery_res(struct ubus_context *ctx, struct ubus_object *obj, ++ struct ubus_request_data *req, const char *method, ++ struct blob_attr *msg) ++{ ++ struct blob_buf bb; ++ struct UPNPDev * dev; ++ int i; ++ ++ memset(&bb,0,sizeof(struct blob_buf)); ++ blob_buf_init(&bb, 0); ++ ++ ++ json_object *devices_array, *services_array, *device_obj, *service_obj; ++ ++ devices_array = json_object_new_array(); ++ services_array = json_object_new_array(); ++ ++ for(dev = devlist, i = 1; dev != NULL; dev = dev->pNext, i++) { ++ if((dev->st && strstr(dev->st, ":rootdevice") != NULL) || (dev->usn && strstr(dev->usn, ":device:") != NULL)){ ++ device_obj = json_object_new_object(); ++ json_object_object_add(device_obj, "descurl", json_object_new_string(dev->descURL)); ++ json_object_object_add(device_obj, "st", json_object_new_string(dev->st)); ++ json_object_object_add(device_obj, "usn", json_object_new_string(dev->usn)); ++ json_object_object_add(device_obj, "is_root_device", json_object_new_string(dev->st && strstr(dev->st, ":rootdevice") ? "1":"0")); ++ json_object_array_add(devices_array, device_obj); ++ } ++ ++ if(dev->usn && strstr(dev->usn, ":service:") != NULL) { ++ service_obj = json_object_new_object(); ++ json_object_object_add(service_obj, "descurl", json_object_new_string(dev->descURL)); ++ json_object_object_add(service_obj, "st", json_object_new_string(dev->st)); ++ json_object_object_add(service_obj, "usn", json_object_new_string(dev->usn)); ++ json_object_array_add(services_array, service_obj); ++ } ++ } ++ blobmsg_add_json_element(&bb, "devices", devices_array); ++ blobmsg_add_json_element(&bb, "services", services_array); ++ json_object_put(device_obj); ++ json_object_put(service_obj); ++ json_object_put(devices_array); ++ json_object_put(services_array); ++ ubus_send_reply(ctx, req, bb.head); ++ blob_buf_free(&bb); ++ return 0; ++} ++ ++int upnp_description_res(struct ubus_context *ctx, struct ubus_object *obj, ++ struct ubus_request_data *req, const char *method, ++ struct blob_attr *msg) ++{ ++ struct blob_buf bb; ++ FILE *file = NULL; ++ char *buffer; ++ int len, r; ++ struct xmlparser parser; ++ struct xml_parse_strct xmldata; ++ struct descriptions_list_elt *desc_elt = NULL; ++ json_object *descriptions_array = NULL, *desc_obj = NULL; ++ memset(&bb,0,sizeof(struct blob_buf)); ++ blob_buf_init(&bb, 0); ++ ++ memset(&parser, 0, sizeof(struct xmlparser)); ++ descriptions_array = json_object_new_array(); ++ LIST_HEAD(tmp_elts); ++ xmldata.devicesarray = json_object_new_array(); ++ xmldata.servicesarray = json_object_new_array(); ++ xmldata.xml_elts = &tmp_elts; ++ parser.data = &xmldata; ++ parser.starteltfunc = xmplparsestartelt; ++ parser.endeltfunc = xmplparseendelt; ++ parser.datafunc = xmplparsedata; ++ int i = 0; ++ list_for_each_entry(desc_elt, &descriptions_list, list) { ++ i++; ++ desc_obj = json_object_new_object(); ++ json_object_object_add(desc_obj, "desc_url", json_object_new_string(desc_elt->url)); ++ json_object_object_add(desc_obj, "is_device_desc", json_object_new_int(desc_elt->is_device_desc)); ++ json_object_array_add(descriptions_array, desc_obj); ++ file = fopen(desc_elt->sytem_file_path,"r"); ++ if (file == NULL){ ++ fprintf(stderr,"Error to open the file filetest\n"); ++ } ++ fseek(file, 0, SEEK_END); ++ len = ftell(file); ++ fseek(file, 0, SEEK_SET); ++ buffer = malloc(len); ++ if(!buffer) { ++ fprintf(stderr, "Memory allocation error.\n"); ++ fclose(file); ++ return 1; ++ } ++ r = (int)fread(buffer, 1, len, file); ++ parser.xmlstart = buffer; ++ parser.xmlsize = len; ++ parsexml(&parser); ++ ++ fclose(file); ++ file = NULL; ++ FREE(buffer); ++ } ++ ++ blobmsg_add_json_element(&bb, "descriptions", descriptions_array); ++ blobmsg_add_json_element(&bb, "devicesinstances", xmldata.devicesarray); ++ blobmsg_add_json_element(&bb, "servicesinstances", xmldata.servicesarray); ++ ubus_send_reply(ctx, req, bb.head); ++ blob_buf_free(&bb); ++ return 0; ++} ++ ++ ++/* ++ * Other functions ++ */ ++ ++ ++char **strsplit(const char* str, const char* delim, size_t* numtokens) ++{ ++ // copy the original string so that we don't overwrite parts of it ++ // (don't do this if you don't need to keep the old line, ++ // as this is less efficient) ++ char *s = strdup(str); ++ // these three variables are part of a very common idiom to ++ // implement a dynamically-growing array ++ size_t tokens_alloc = 1; ++ size_t tokens_used = 0; ++ char **tokens = calloc(tokens_alloc, sizeof(char*)); ++ char *token, *strtok_ctx; ++ for (token = strtok_r(s, delim, &strtok_ctx); ++ token != NULL; ++ token = strtok_r(NULL, delim, &strtok_ctx)) { ++ // check if we need to allocate more space for tokens ++ if (tokens_used == tokens_alloc) { ++ tokens_alloc *= 2; ++ tokens = realloc(tokens, tokens_alloc * sizeof(char*)); ++ } ++ tokens[tokens_used++] = strdup(token); ++ } ++ // cleanup ++ if (tokens_used == 0) { ++ free(tokens); ++ tokens = NULL; ++ } else { ++ tokens = realloc(tokens, tokens_used * sizeof(char*)); ++ } ++ *numtokens = tokens_used; ++ free(s); ++ return tokens; ++} ++ ++void check_create_dir(char *dirpath) { ++ struct stat st = {0}; ++ ++ if (stat(dirpath, &st) == -1) { ++ mkdir(dirpath, 0700); ++ } ++} ++ ++int is_desc_elt_exit_in_list(char *desc_url) ++{ ++ struct descriptions_list_elt *desc_elt = NULL; ++ ++ list_for_each_entry(desc_elt, &descriptions_list, list) { ++ if(strcmp(desc_elt->url, desc_url) == 0) ++ return 1; ++ } ++ return 0; ++} ++ ++void add_desc_to_descriptions_list(char *filepath, char *url, int is_device_desc) { ++ struct descriptions_list_elt *desc_elt; ++ ++ desc_elt = calloc(1, sizeof(struct descriptions_list_elt)); ++ list_add_tail(&desc_elt->list, &descriptions_list); ++ asprintf(&desc_elt->sytem_file_path, "%s", filepath); ++ asprintf(&desc_elt->url, "%s", url); ++ desc_elt->is_device_desc = is_device_desc; ++} ++ ++int get_data_length(const char *data, int l) ++{ ++ int i; ++ ++ for (i=0; i<l; i++) { ++ if(data[i] == '\n' || data[i] == '\t') ++ return i; ++ } ++ return l; ++} +diff -ruN orig/upnp_ubus.h new/upnp_ubus.h +--- a/upnp_ubus.h ++++ b/upnp_ubus.h +@@ -0,0 +1,69 @@ ++/* ++* 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. ++* ++* Copyright (C) 2019 iopsys Software Solutions AB ++* Author: Omar Kallel <omar.kallel@pivasoftware.com> ++* ++*/ ++ ++#ifndef SRC_UPNP_UBUS_H_ ++#define SRC_UPNP_UBUS_H_ ++#include <libubus.h> ++#include <libubox/blobmsg.h> ++#include <libubox/blobmsg_json.h> ++#include <json-c/json.h> ++#include <sys/types.h> ++#include <sys/stat.h> ++#include <unistd.h> ++ ++#define FREE(x) if(x!=NULL) { free(x); x=NULL; } ++ ++enum elemtentTypes { DEVICE, SERVICE, NONE }; ++ ++struct descriptions_list_elt { ++ struct list_head list; ++ char *url; ++ char *sytem_file_path; ++ int is_device_desc; ++}; ++ ++struct xml_parse_list_elt { ++ struct list_head list; ++ json_object *eltobj; ++ enum elemtentTypes elt_type; ++ char *nodename; ++ char *udn; ++}; ++ ++struct xml_parse_strct { ++ json_object *devicesarray; ++ json_object *servicesarray; ++ char *node_name; ++ struct list_head *xml_elts; ++}; ++ ++extern struct UPNPDev * devlist; ++extern struct ubus_object upnpc_object; ++extern struct list_head descriptions_list; ++ ++ ++int upnp_discovery_res(struct ubus_context *ctx, struct ubus_object *obj, ++ struct ubus_request_data *req, const char *method, ++ struct blob_attr *msg); ++ ++int upnp_description_res(struct ubus_context *ctx, struct ubus_object *obj, ++ struct ubus_request_data *req, const char *method, ++ struct blob_attr *msg); ++ ++char **strsplit(const char* str, const char* delim, size_t* numtokens); ++void check_create_dir(char *dirpath); ++void IGDdata1(void *, const char *, int); ++void IGDstartelt1(void * d, const char * name, int l); ++void IGDendelt1(void * d, const char * name, int l); ++int is_desc_elt_exit_in_list(char *desc_url); ++void add_desc_to_descriptions_list(char *filepath, char *url, int is_device_desc); ++int get_data_length(const char *data, int l); ++#endif /* SRC_UPNP_UBUS_H_ */ -- GitLab