From 9d574f6e79c46e628d1c483e77d9e5928bc73b8a Mon Sep 17 00:00:00 2001 From: Nicolas Cornu Date: Thu, 31 Jan 2019 10:42:30 +0100 Subject: [PATCH] minissdpc: add a libuv client --- miniupnpc-libuv/README | 11 ++ miniupnpc-libuv/codelength.h | 54 ++++++ miniupnpc-libuv/example.c | 55 ++++++ miniupnpc-libuv/minissdpc-libuv.c | 270 +++++++++++++++++++++++++++ miniupnpc-libuv/minissdpc-libuv.h | 56 ++++++ miniupnpc-libuv/miniupnpc_declspec.h | 21 +++ miniupnpc-libuv/upnpdev.h | 36 ++++ 7 files changed, 503 insertions(+) create mode 100644 miniupnpc-libuv/README create mode 100644 miniupnpc-libuv/codelength.h create mode 100644 miniupnpc-libuv/example.c create mode 100644 miniupnpc-libuv/minissdpc-libuv.c create mode 100644 miniupnpc-libuv/minissdpc-libuv.h create mode 100644 miniupnpc-libuv/miniupnpc_declspec.h create mode 100644 miniupnpc-libuv/upnpdev.h diff --git a/miniupnpc-libuv/README b/miniupnpc-libuv/README new file mode 100644 index 0000000..e97741a --- /dev/null +++ b/miniupnpc-libuv/README @@ -0,0 +1,11 @@ +Here is a basic libuv implementation of minissdp client. + +You can find an example find attached to test it. + +BUILD +gcc -luv example.c minissdpc-libuv.c -o minissdpc + +EXECUTION +./minissdpc /var/run/your.sock ssdp:all + +This should print all find upnp services. diff --git a/miniupnpc-libuv/codelength.h b/miniupnpc-libuv/codelength.h new file mode 100644 index 0000000..ea0b005 --- /dev/null +++ b/miniupnpc-libuv/codelength.h @@ -0,0 +1,54 @@ +/* $Id: codelength.h,v 1.3 2011/07/30 13:10:05 nanard Exp $ */ +/* Project : miniupnp + * Author : Thomas BERNARD + * copyright (c) 2005-2015 Thomas Bernard + * This software is subjet to the conditions detailed in the + * provided LICENCE file. */ +#ifndef CODELENGTH_H_INCLUDED +#define CODELENGTH_H_INCLUDED + +/* Encode length by using 7bit per Byte : + * Most significant bit of each byte specifies that the + * following byte is part of the code */ + +/* n : unsigned + * p : unsigned char * + */ +#define DECODELENGTH(n, p) n = 0; \ + do { n = (n << 7) | (*p & 0x7f); } \ + while((*(p++)&0x80) && (n<(1<<25))); + +/* n : unsigned + * READ : function/macro to read one byte (unsigned char) + */ +#define DECODELENGTH_READ(n, READ) \ + n = 0; \ + do { \ + unsigned char c; \ + READ(c); \ + n = (n << 7) | (c & 0x07f); \ + if(!(c&0x80)) break; \ + } while(n<(1<<25)); + +/* n : unsigned + * p : unsigned char * + * p_limit : unsigned char * + */ +#define DECODELENGTH_CHECKLIMIT(n, p, p_limit) \ + n = 0; \ + do { \ + if((p) >= (p_limit)) break; \ + n = (n << 7) | (*(p) & 0x7f); \ + } while((*((p)++)&0x80) && (n<(1<<25))); + + +/* n : unsigned + * p : unsigned char * + */ +#define CODELENGTH(n, p) if(n>=268435456) *(p++) = (n >> 28) | 0x80; \ + if(n>=2097152) *(p++) = (n >> 21) | 0x80; \ + if(n>=16384) *(p++) = (n >> 14) | 0x80; \ + if(n>=128) *(p++) = (n >> 7) | 0x80; \ + *(p++) = n & 0x7f; + +#endif /* CODELENGTH_H_INCLUDED */ diff --git a/miniupnpc-libuv/example.c b/miniupnpc-libuv/example.c new file mode 100644 index 0000000..d40ea27 --- /dev/null +++ b/miniupnpc-libuv/example.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include "minissdpc-libuv.h" +#include + +void requestFinish2(void* session, void* userdata, struct UPNPDev* upnpdev) +{ + struct UPNPDev* it = upnpdev; + while(it != NULL) { + printf("url = %s\n", it->descURL); + printf("st = %s\n", it->st); + printf("usn = %s\n", it->usn); + printf("\n"); + it = it->pNext; + } + disconnectFromMiniSSDPD((uv_stream_t*)session); +} + +void requestFinish(void* session, int success, void* userdata) +{ + if (success == 0) + { + printf("Error while requesting results.\n"); + return; + } + receiveDevicesFromMiniSSDPD(session, &requestFinish2, NULL); +} + +void connect_cb(void* session, void* userdata) +{ + if (session == 0) { + printf("Error while connecting\n"); + return; + } + + char* search = userdata; + + int ret; + if ((ret = requestDevicesFromMiniSSDPD(session, search, &requestFinish, NULL)) != MINISSDPC_SUCCESS) { + printf("Error while requesting devices\n"); + if (ret == MINISSDPC_INVALID_INPUT) + printf("Invalid input!!\n"); + else if (ret == MINISSDPC_MEMORY_ERROR) + printf("Can't malloc?\n"); + } +} + +int main(int argc, char *argv[]) +{ + char* pipeName = argv[1]; + char* search = argv[2]; + connectToMiniSSDPD(pipeName, &connect_cb, search); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); +} diff --git a/miniupnpc-libuv/minissdpc-libuv.c b/miniupnpc-libuv/minissdpc-libuv.c new file mode 100644 index 0000000..99d597e --- /dev/null +++ b/miniupnpc-libuv/minissdpc-libuv.c @@ -0,0 +1,270 @@ +/* $Id: minissdpc.c,v 1.32 2016/10/07 09:04:36 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project : miniupnp + * Web : http://miniupnp.free.fr/ + * Author : Thomas BERNARD + * copyright (c) 2005-2019 Thomas Bernard + * This software is subjet to the conditions detailed in the + * provided LICENCE file. */ +#include +#include +#include + +#include "minissdpc-libuv.h" + +#include "codelength.h" +#include + +struct userdata_s +{ + void* cb; + void* userdata; +}; + +static void connect_cb(uv_connect_t* req, int status) +{ + uv_stream_t *stream = req->handle; + struct userdata_s *us = stream->data; + void(*user_connect_cb)(void*, void*) = us->cb; + + if(status < 0) + user_connect_cb(0, us->userdata); + else + user_connect_cb(req->handle, us->userdata); + + free(req); + free(us); + + if(status < 0) + free(stream); +} + +int +connectToMiniSSDPD(const char * socketpath, void(*user_connect_cb)(void* connect, void* userdata), void *userdata) +{ + if(user_connect_cb == 0) + return MINISSDPC_INVALID_INPUT; + + if(!socketpath) + socketpath = "/var/run/minissdpd.sock"; + + uv_pipe_t *p = malloc(sizeof(uv_pipe_t)); + + if(uv_pipe_init(uv_default_loop(), p, 1) < 0) + return MINISSDPC_SOCKET_ERROR; + + uv_connect_t *conn = malloc(sizeof(uv_connect_t)); + struct userdata_s *us = malloc(sizeof(struct userdata_s)); + us->cb = user_connect_cb; + us->userdata = userdata; + p->data = us; + uv_pipe_connect(conn, p, socketpath, &connect_cb); + return MINISSDPC_SUCCESS; +} + +static void +close_cb(uv_handle_t *handle) +{ + free(handle); +} + +MINIUPNP_LIBSPEC void +disconnectFromMiniSSDPD(void *session) +{ + uv_close((uv_handle_t *)session, close_cb); +} + +static void write_cb(uv_write_t* req, int status) +{ + uv_stream_t* stream = req->handle; + struct userdata_s *us = stream->data; + void(*user_write_cb)(void*, int, void*) = us->cb; + user_write_cb(req->handle, status == 0, us->userdata); + // free(req->bufs->base); + free(req); + free(us); +} + +MINIUPNP_LIBSPEC int +requestDevicesFromMiniSSDPD(void *session, const char * devtype, void(*requestFinish)(void *connect, int success, void* userdata), void* userdata) +{ + char *buffer = malloc(256); + + if(buffer == NULL) + { + return MINISSDPC_MEMORY_ERROR; + } + + char *p = buffer; + unsigned int stsize = strlen(devtype); + + if(stsize == 8 && 0 == memcmp(devtype, "ssdp:all", 8)) + { + buffer[0] = 3; /* request type 3 : everything */ + } + else + { + buffer[0] = 1; /* request type 1 : request devices/services by type */ + } + + p++; + unsigned int l = stsize; + CODELENGTH(l, p); + + if(p + stsize > buffer + 256) + { + /* devtype is too long ! */ + #ifdef DEBUG + fprintf(stderr, "devtype is too long ! stsize=%u sizeof(buffer)=%u\n", + stsize, (unsigned)sizeof(buffer)); + #endif /* DEBUG */ + free(buffer); + return MINISSDPC_INVALID_INPUT; + } + + memcpy(p, devtype, stsize); + p += stsize; + uv_write_t *req = malloc(sizeof(uv_write_t)); + + if(req == NULL) + { + free(buffer); + return MINISSDPC_MEMORY_ERROR; + } + + struct userdata_s *us = malloc(sizeof(struct userdata_s)); + + if(us == NULL) + { + free(req); + free(buffer); + return MINISSDPC_MEMORY_ERROR; + } + + us->cb = requestFinish; + us->userdata = userdata; + uv_stream_t* stream = session; + stream->data = us; + uv_buf_t data[] = + { + { .base = buffer, .len = p - buffer } + }; + uv_write(req, stream, data, 1, write_cb); + return MINISSDPC_SUCCESS; +} + +static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) +{ + buf->base = malloc(size); + buf->len = size; +} + +static void read_cb(uv_stream_t *stream, ssize_t size, const uv_buf_t* buffer) +{ + struct userdata_s *us = stream->data; + void(*user_write_cb)(void *connect, void *userdata, struct UPNPDev*) = us->cb; + struct UPNPDev * devlist = NULL; + char * p = buffer->base; + unsigned int i, ndev; + + if(size == 0) + { + return; + } + + if(size < 0) + { + user_write_cb(stream, us->userdata, devlist); + uv_read_stop(stream); + free(us); + stream->data = NULL; + return; + } + + ndev = *p; + p++; + + for(i = 0; i < ndev; i++) + { + unsigned int urlsize; + DECODELENGTH(urlsize, p); + char *url = NULL; + if(size != 0) + { + url = strndup(p, urlsize); + + if(url == NULL) + break; + } + p += urlsize; + + unsigned int stsize; + DECODELENGTH(stsize, p); + char *st = NULL; + if(size != 0) + { + st = strndup(p, stsize); + + if(st == NULL) + { + free(url); + break; + } + } + p += stsize; + + unsigned int usnsize; + DECODELENGTH(usnsize, p); + char *usn = NULL; + if(size != 0) + { + usn = strndup(p, usnsize); + + if(usn == NULL) + { + free(url); + free(st); + break; + } + } + p += usnsize; + + struct UPNPDev *tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize); + if(tmp == NULL) + { + free(url); + free(st); + free(usn); + break; + } + + tmp->pNext = devlist; + tmp->descURL = tmp->buffer; + tmp->st = tmp->buffer + 1 + urlsize; + tmp->usn = tmp->buffer + 1 + urlsize + 1 + stsize; + memcpy(tmp->descURL, url, urlsize+1); + memcpy(tmp->st, st, stsize+1); + memcpy(tmp->usn, usn, usnsize+1); + free(url); + free(st); + free(usn); + tmp->scope_id = 0; /* default value. scope_id is not available with MiniSSDPd */ + devlist = tmp; + } + + user_write_cb(stream, us->userdata, devlist); + uv_read_stop(stream); + stream->data = NULL; + free(us); +} + +void +receiveDevicesFromMiniSSDPD(void *session, void(*requestFinish)(void *session, void *userdata, struct UPNPDev*), void* userdata) +{ + struct userdata_s *us = malloc(sizeof(struct userdata_s)); + us->cb = requestFinish; + us->userdata = userdata; + uv_stream_t *stream = session; + stream->data = us; + uv_read_start(stream, alloc_cb, read_cb); +} diff --git a/miniupnpc-libuv/minissdpc-libuv.h b/miniupnpc-libuv/minissdpc-libuv.h new file mode 100644 index 0000000..1b9429c --- /dev/null +++ b/miniupnpc-libuv/minissdpc-libuv.h @@ -0,0 +1,56 @@ +/* $Id: minissdpc.h,v 1.6 2015/09/18 12:45:16 nanard Exp $ */ +/* Project: miniupnp + * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ + * Author: Thomas Bernard + * Copyright (c) 2005-2015 Thomas Bernard + * This software is subjects to the conditions detailed + * in the LICENCE file provided within this distribution */ +#ifndef MINISSDPC_H_INCLUDED +#define MINISSDPC_H_INCLUDED + +#include "miniupnpc_declspec.h" +#include "upnpdev.h" + +/* error codes : */ +#define MINISSDPC_SUCCESS (0) +#define MINISSDPC_UNKNOWN_ERROR (-1) +#define MINISSDPC_SOCKET_ERROR (-101) +#define MINISSDPC_MEMORY_ERROR (-102) +#define MINISSDPC_INVALID_INPUT (-103) +#define MINISSDPC_INVALID_SERVER_REPLY (-104) + +#ifdef __cplusplus +extern "C" { +#endif + +MINIUPNP_LIBSPEC int +connectToMiniSSDPD(const char *socketpath, void(*connect_cb)(void *session, void *userdata), void *userdata); + +MINIUPNP_LIBSPEC void +disconnectFromMiniSSDPD(void *session); + +MINIUPNP_LIBSPEC int +requestDevicesFromMiniSSDPD(void *session, const char *devtype, void(*requestFinish)(void *session, int success, void *userdata), void *userdata); + +MINIUPNP_LIBSPEC void +receiveDevicesFromMiniSSDPD(void *session, void(*requestFinish)(void *session, void *userdata, struct UPNPDev *upnpdev), void *userdata); + +/**** +MINIUPNP_LIBSPEC struct UPNPDev * +getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath, int * error); + +MINIUPNP_LIBSPEC struct UPNPDev * +ssdpDiscoverDevices(const char * const deviceTypes[], + int delay, const char * multicastif, + int localport, + int ipv6, unsigned char ttl, + int * error, + int searchalltypes); +****/ + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/miniupnpc-libuv/miniupnpc_declspec.h b/miniupnpc-libuv/miniupnpc_declspec.h new file mode 100644 index 0000000..40adb92 --- /dev/null +++ b/miniupnpc-libuv/miniupnpc_declspec.h @@ -0,0 +1,21 @@ +#ifndef MINIUPNPC_DECLSPEC_H_INCLUDED +#define MINIUPNPC_DECLSPEC_H_INCLUDED + +#if defined(_WIN32) && !defined(MINIUPNP_STATICLIB) + /* for windows dll */ + #ifdef MINIUPNP_EXPORTS + #define MINIUPNP_LIBSPEC __declspec(dllexport) + #else + #define MINIUPNP_LIBSPEC __declspec(dllimport) + #endif +#else + #if defined(__GNUC__) && __GNUC__ >= 4 + /* fix dynlib for OS X 10.9.2 and Apple LLVM version 5.0 */ + #define MINIUPNP_LIBSPEC __attribute__ ((visibility ("default"))) + #else + #define MINIUPNP_LIBSPEC + #endif +#endif + +#endif /* MINIUPNPC_DECLSPEC_H_INCLUDED */ + diff --git a/miniupnpc-libuv/upnpdev.h b/miniupnpc-libuv/upnpdev.h new file mode 100644 index 0000000..f4ae174 --- /dev/null +++ b/miniupnpc-libuv/upnpdev.h @@ -0,0 +1,36 @@ +/* $Id: upnpdev.h,v 1.1 2015/08/28 12:14:19 nanard Exp $ */ +/* Project : miniupnp + * Web : http://miniupnp.free.fr/ + * Author : Thomas BERNARD + * copyright (c) 2005-2018 Thomas Bernard + * This software is subjet to the conditions detailed in the + * provided LICENSE file. */ +#ifndef UPNPDEV_H_INCLUDED +#define UPNPDEV_H_INCLUDED + +#include "miniupnpc_declspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct UPNPDev { + struct UPNPDev * pNext; + char * descURL; + char * st; + char * usn; + unsigned int scope_id; + char buffer[3]; +}; + +/* freeUPNPDevlist() + * free list returned by upnpDiscover() */ +MINIUPNP_LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist); + + +#ifdef __cplusplus +} +#endif + + +#endif /* UPNPDEV_H_INCLUDED */