onvif开发踩坑【一】soap_send___wsdd__Probe无法发现设备

在测试环境下,当开发机配置多个固定IP时,使用特定组播IP进行设备探测失败。改为DHCP自动获取IP后,成功实现目标设备的发现。

soap_send___wsdd__Probe无法发现设备:
测试时开发机配置了多个固定ip,使用组播IP:239.255.255.250无法发现设备。开发机改为dhcp自动获取ip,成功发现目标。

/****************************************************************************** * Copyright (c) 2018-2018 TP-Link Systems Inc. * * Filename: soap_wsdd.c * Version: 1.0 * Description: soap 处理函数头文件 * Author: liyijie<liyijie@tp-link.com.cn> * Date: 2018-10-30 ******************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <arpa/inet.h> #include "soap_global.h" #include "soap_parse.h" #include "soap_pack.h" #include "soap_wsdd.h" /****************************************************************************** * 函数名称: soap_check_scope() * 函数描述: 检查输入的scope是否在设备保存的scope列表内 * 输 入: scopes -- 输入的scope字串 * 输 出: N/A * 返 回 值: TRUE/FALSE ******************************************************************************/ LOCAL S32 soap_check_scope(char *scopes) { int i = 0; S32 flag = -1; S32 len = 0; char *s = NULL; char *e = NULL; char *t = NULL; SOAP_NODE *node = NULL; if (scopes == NULL) { return 0; } if (strnlen(scopes, 1) == 0) { return 0; } for (t = scopes; t && strnlen(t, 1); t++) { e = (char *)strchr(t, ' '); if (e != NULL) { len = e - t; } else { len = strlen(scopes) - (t - scopes); } if (len <= 0) { break; } for (i = 0; i < FIX_SCOPE_NUM; i++) { s = G_TP_FIXED_SCOPE[i]; if ( 0 == strncmp(s, t, len) && ('/' == *(s + len) || '\0' == *(s + len)) ) { flag = 0; break; } } if (0 == flag) { break; } for (node = g_scope_list; node; node = node->next) { s = (char*)node->data; if (0 == strncmp(s, t, len) && ('/' == *(s + len) || '\0' == *(s + len))) { flag = 0; break; } } if ( 0 == flag ) { break; } if (NULL != e) { t = strchr(e, ' '); } else { t = NULL; } if (!t) { break; } } return flag; } LOCAL S32 soap_out_scopes_item(ONVIF_BUF *xml_buf) { SOAP_NODE *node = NULL; char scope_string[FIX_SCOPE_NUM * LEN_INFO + FIX_SCOPE_NUM] = {0}; if (xml_buf == NULL) { ONVIF_WARN("xml_buf == NULL."); return ERROR; } /* wsdd:Scopes begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, "wsdd:Scopes", NULL)); snprintf(scope_string, sizeof(scope_string), "%s %s %s %s %s", G_TP_FIXED_SCOPE[0], G_TP_FIXED_SCOPE[1], G_TP_FIXED_SCOPE[2], G_TP_FIXED_SCOPE[3], G_TP_FIXED_SCOPE[4]); SOAP_IF_FAIL_RET(onvif_buf_append_string(xml_buf, scope_string, strlen(scope_string))); for (node = g_scope_list; node; node = node->next) { if (node->data != NULL) { SOAP_IF_FAIL_RET(onvif_buf_append_string(xml_buf, " ", 1)); SOAP_IF_FAIL_RET(onvif_buf_append_string(xml_buf, (char *)node->data, strlen((char *)node->data))); } } /* wsdd:Scopes end */ SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, "wsdd:Scopes")); return OK; } /****************************************************************************** * 函数名称: soap_out_wsdd_hello_type() * 函数描述: 组装wsdd:Hello element内容 * 输 入: xml_buf -- 存放输出xml字串的内存地址 * data -- 为空 * 输 出: N/A * 返 回 值: ERROR/OK ******************************************************************************/ LOCAL S32 soap_out_wsdd_hello_type(ONVIF_BUF *xml_buf, void *data) { S32 len = 0; char tmp_str[LEN_INFO] = {0}; char ip_str[LEN_IP] = {0}; char mac_str[LEN_MAC] = {0}; if (xml_buf == NULL) { ONVIF_WARN("xml_buf == NULL."); return ERROR; } SOAP_IF_FAIL_RET(onvif_get_ip_str(ip_str, LEN_IP)); SOAP_IF_FAIL_RET(onvif_get_mac_str(mac_str, LEN_MAC, FALSE)); /* wsdd:Hello begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, "wsdd:Hello", NULL)); /* wsa:EndpointReference */ len = snprintf(tmp_str, sizeof(tmp_str), "uuid:%s%s", UUID_PREFIX_FMAT, mac_str); if (len < 0 || len >= sizeof(tmp_str)) { return ERROR; } SOAP_IF_FAIL_RET(soap_out_wsa_endpoint_reference(xml_buf, "wsa:EndpointReference", tmp_str)); /* wsdd:Types */ SOAP_IF_FAIL_RET(soap_element(xml_buf, "wsdd:Types", G_TYPES_VAL_STR, NULL)); /* wsdd:Scopes */ SOAP_IF_FAIL_RET(soap_out_scopes_item(xml_buf)); /* wsdd:XAddrs */ len = snprintf(tmp_str, sizeof(tmp_str), "http://%s:%d/onvif/device_service", ip_str, ONVIF_PORT_FOR_MAIN); if (len < 0 || len >= sizeof(tmp_str)) { return ERROR; } SOAP_IF_FAIL_RET(soap_element(xml_buf, "wsdd:XAddrs", tmp_str, NULL)); /* wsdd:MetadataVersion */ SOAP_IF_FAIL_RET(soap_element(xml_buf, "wsdd:MetadataVersion", "1", NULL)); SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, "wsdd:Hello")); return OK; } /****************************************************************************** * 函数名称: soap_out_wsdd_probe_match_type() * 函数描述: 组装wsdd:ProbeMatch element内容 * 输 入: xml_buf -- 存放输出xml字串的内存地址 * 输 出: N/A * 返 回 值: ERROR/OK ******************************************************************************/ LOCAL S32 soap_out_wsdd_probe_match_type(ONVIF_BUF *xml_buf) { S32 len = 0; char tmp_str[LEN_INFO] = {0}; char ip_str[LEN_IP] = {0}; char mac_str[LEN_MAC] = {0}; if (xml_buf == NULL) { ONVIF_WARN("xml_buf == NULL."); return ERROR; } SOAP_IF_FAIL_RET(onvif_get_ip_str(ip_str, LEN_IP)); SOAP_IF_FAIL_RET(onvif_get_mac_str(mac_str, LEN_MAC, FALSE)); /* wsdd:ProbeMatch begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, "wsdd:ProbeMatch", NULL)); /* wsa:EndpointReference */ len = snprintf(tmp_str, sizeof(tmp_str), "uuid:%s%s", UUID_PREFIX_FMAT, mac_str); if (len < 0 || len >= sizeof(tmp_str)) { return ERROR; } SOAP_IF_FAIL_RET(soap_out_wsa_endpoint_reference(xml_buf, "wsa:EndpointReference", tmp_str)); /* wsdd:Types */ SOAP_IF_FAIL_RET(soap_element(xml_buf, "wsdd:Types", G_TYPES_VAL_STR, NULL)); /* wsdd:Scopes */ SOAP_IF_FAIL_RET(soap_out_scopes_item(xml_buf)); /* wsdd:XAddrs */ len = snprintf(tmp_str, sizeof(tmp_str), "http://%s:%d/onvif/device_service", ip_str, ONVIF_PORT_FOR_MAIN); if (len < 0 || len >= sizeof(tmp_str)) { return ERROR; } SOAP_IF_FAIL_RET(soap_element(xml_buf, "wsdd:XAddrs", tmp_str, NULL)); /* wsdd:MetadataVersion */ SOAP_IF_FAIL_RET(soap_element(xml_buf, "wsdd:MetadataVersion", "1", NULL)); /* wsdd:ProbeMatch end */ SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, "wsdd:ProbeMatch")); return OK; } /****************************************************************************** * 函数名称: soap_out_wsdd_probe_matches() * 函数描述: 组装wsdd:ProbeMatches element内容 * 输 入: xml_buf -- 存放输出xml字串的内存地址 * data -- 为空 * 输 出: N/A * 返 回 值: ERROR/OK ******************************************************************************/ LOCAL S32 soap_out_wsdd_probe_matches(ONVIF_BUF *xml_buf, void *data) { if (xml_buf == NULL) { ONVIF_WARN("xml_buf == NULL."); return ERROR; } /* wsdd:ProbeMatches begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, "wsdd:ProbeMatches", NULL)); /* wsdd:ProbeMatch */ SOAP_IF_FAIL_RET(soap_out_wsdd_probe_match_type(xml_buf)); /* wsdd:ProbeMatches end */ SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, "wsdd:ProbeMatches")); return OK; } /********************************************************************************* * Soap wsdd req parse *********************************************************************************/ /****************************************************************************** * 函数名称: soap_wsdd_probe_req_parse() * 函数描述: 解析发现报文的具体内容 * 输 入: soap -- soap结构体 * req_buf -- 请求内容 * buf_len -- 请求内容长度 * 输 出: N/A * 返 回 值: ERROR/OK ******************************************************************************/ LOCAL S32 soap_wsdd_probe_req_parse(SOAP_CONTEXT *soap, char **req_buf, S32 buf_len) { S32 ch = 0; char charbuf[LEN_INFO] = {0}; char *str_start = NULL; char **p = req_buf; if (soap == NULL) { ONVIF_TRACE("soap == NULL."); return ERROR; } if (req_buf == NULL || *req_buf == NULL || buf_len <= 0) { ONVIF_TRACE("no request context to parse."); return OK; } str_start = *req_buf; while (((*p) - str_start) < buf_len) { if ((ch = soap_get_tag(str_start, buf_len, p, charbuf, sizeof(charbuf))) == EOF) { goto error_parse; } if (charbuf[0] == '/') { continue; } if (TRUE == soap_match_tag(charbuf, "Types")) { if (OK != soap_parse_element_value(str_start, buf_len, p, ch, soap->types, sizeof(soap->types))) { goto error_parse; } ONVIF_TRACE("Types: %s", soap->types); } if (TRUE == soap_match_tag(charbuf, "Scopes")) { if (OK != soap_parse_element_value(str_start, buf_len, p, ch, soap->scopes_item, sizeof(soap->scopes_item))) { goto error_parse; } ONVIF_TRACE("Scopes: %s", soap->scopes_item); } if (TRUE == soap_match_tag(charbuf, "MatchBy")) { if (OK != soap_parse_element_value(str_start, buf_len, p, ch, soap->scopes_matchby, sizeof(soap->scopes_matchby))) { goto error_parse; } ONVIF_TRACE("MatchBy: %s", soap->scopes_matchby); } } return OK; error_parse: return ERROR; } /****************************************************************************** * 函数名称: soap_wsdd_hello() * 函数描述: 发送onvif Hello报文 * 输 入: soap -- soap结构体 * 输 出: N/A * 返 回 值: ERROR/OK ******************************************************************************/ S32 soap_wsdd_hello(SOAP_CONTEXT *soap) { if (soap == NULL) { ONVIF_TRACE("soap == NULL."); return ERROR; } SOAP_IF_FAIL_RET(soap_generate_xml((p_out_fun)(soap_out_wsdd_hello_type), soap, NULL)); return onvif_send_udp_packet(soap->sock, inet_addr(ONVIF_MCAST_IP), htons(ONVIF_PORT_FOR_DISCV), soap->xml_buf.start, (soap->xml_buf.last - soap->xml_buf.start)); } /****************************************************************************** * 函数名称: soap_wsdd_probe_handle() * 函数描述: 发现请求的处理函数 * 输 入: soap -- soap结构体 * 输 出: N/A * 返 回 值: ERROR/OK ******************************************************************************/ LOCAL S32 soap_wsdd_probe_handle(SOAP_CONTEXT *soap) { S32 request_len = 0; BOOL match = FALSE; char *xml_buf = NULL; char **xml_str = NULL; char ifname[LEN_IFNAME] = {0}; S32 scopes_matchby_flag = 0; if (soap == NULL) { ONVIF_WARN("soap == NULL."); return ERROR; } if (onvif_get_discv_mode() == DIS_MODE_NO) { ONVIF_DEBUG("discover mode is non-discoverable."); return OK; } if (soap->request_begin != NULL && soap->request_end != NULL) { request_len = soap->request_end - soap->request_begin; if (request_len < 0) { ONVIF_WARN("request_len < 0."); return ERROR; } xml_buf = soap->request_begin; xml_str = (char **)&xml_buf; if (strstr(xml_buf, "<d:Scopes MatchBy=\"http://schemas.xmlsoap.org/ws/2005/04/discovery/rfc3986\"></d:Scopes>") != NULL) { scopes_matchby_flag = 1; } } else { ONVIF_ERROR("soap request content is NULL."); return ERROR; } if (OK != soap_wsdd_probe_req_parse(soap, xml_str, request_len)) { return ERROR; } if (soap->types[0] != '\0') { if(0 == strcmp("tds:Device", soap->types) || 0 == strcmp("tdn:NetworkVideoTransmitter", soap->types) || 0 == strcmp("dn:NetworkVideoTransmitter", soap->types)) { match = TRUE; } else if(0 == strcmp("dn:NetworkVideoRecorder", soap->types) || 0 == strcmp("tdn:NetworkVideoRecorder", soap->types)) { return OK; } } else { match = TRUE; } if (match) { if (onvif_get_netdevice_name(soap->ifindex, ifname, LEN_IFNAME) == 0 && strncmp(ifname, IFNAME_AP, LEN_IFNAME) != 0) { /* 通知IPCD进行probe IP的跟随修改 */ NSD_SEND(NSD_PROBE_IP, (U8*)(&soap->in_ip), sizeof(soap->in_ip)); } } if (soap->scopes_item[0] != '\0') { if (soap->scopes_matchby[0] != '\0') { if (soap->has_header) { snprintf(soap->header.wsa_action, sizeof(soap->header.wsa_action), "http://schemas.xmlsoap.org/ws/2005/04/discovery/fault"); snprintf(soap->header.wsa_to, sizeof(soap->header.wsa_to), "http://schemas.xmlsoap.org/ws/2005/04/addressing/role/anonymous"); } soap_fault(soap, "SOAP-ENV:Sender", "wsdd:MatchingRuleNotSupported", NULL, "the matching rule specified is not supported"); soap->error = SOAP_FAULT; return ERROR; } if (0 != soap_check_scope(soap->scopes_item)) { return ERROR; } } if (scopes_matchby_flag == 1) { if (soap->has_header) { snprintf(soap->header.wsa_action, sizeof(soap->header.wsa_action), "http://schemas.xmlsoap.org/ws/2005/04/discovery/fault"); snprintf(soap->header.wsa_to, sizeof(soap->header.wsa_to), "http://schemas.xmlsoap.org/ws/2005/04/addressing/role/anonymous"); } soap_fault(soap, "SOAP-ENV:Sender", "wsdd:MatchingRuleNotSupported", NULL, "the matching rule specified is not supported"); soap->error = SOAP_FAULT; return ERROR; } //SOAP_IF_FAIL_RET(soap_generate_xml((p_out_fun)(soap_out_wsdd_probe_matches), soap, NULL)); //return onvif_send_udp_packet(soap->sock, soap->in_ip, htons(ONVIF_PORT_FOR_DISCV), // soap->xml_buf.start, (soap->xml_buf.last - soap->xml_buf.start)); if (soap->header.wsa_relates_to[0] == '\0') { snprintf(soap->header.wsa_relates_to, sizeof(soap->header.wsa_relates_to), "%s", soap->header.wsa_msg_id); } if (soap->header.wsa_action[0] != '\0') { onvif_str_append_chr(soap->header.wsa_action, sizeof(soap->header.wsa_action), "Matches"); } return soap_generate_xml((p_out_fun)(soap_out_wsdd_probe_matches), soap, NULL); } /********************************************************************************* * Soap wsdd Handles init *********************************************************************************/ void soap_wsdd_handle_init() { soap_tag_handle_add("Probe", soap_wsdd_probe_handle); } ——这个能不能详解下,尤其是要讲清楚每个函数的功能及输入输出,整个模块的结构/功能以及对外接口
08-31
/****************************************************************************** \* Copyright (c) 2018-2020 TP-Link Systems Inc. * \* 文件名称: onvif_common.h \* 版 本: 1.0 \* 摘 要: ONVIF 通用函数及定义头文件 \* 作 者: liyijie<liyijie@tp-link.com.cn> \* 创建时间: 2018-10-30 ******************************************************************************/ \#ifndef __ONVIF_COMMON_H__ \#define __ONVIF_COMMON_H__ \#include <arpa/inet.h> \#include "nsd_common.h" \#include "../ipcd/api.h" \#define ONVIF_DEBUG(fmt, ...) NSD_DEBUG(NSD_ONVIF, "[ONVIF]" fmt, ##__VA_ARGS__) \#define ONVIF_WARN(fmt, ...) NSD_WARN(NSD_ONVIF, "[ONVIF]" fmt , ##__VA_ARGS__) \#define ONVIF_ERROR(fmt, ...) NSD_ERROR(NSD_ONVIF, "[ONVIF]" fmt , ##__VA_ARGS__) \#define ONVIF_INFO(fmt, ...) NSD_LOG(LOG_INFO, LOG_THIRDPARTY, "[ONVIF]" fmt, ##__VA_ARGS__) \#ifdef __ONVIF_TRACE__ \#define ONVIF_TRACE(fmt, ...) ONVIF_DEBUG(fmt, ##__VA_ARGS__) \#else \#define ONVIF_TRACE(fmt, ...) \#endif \#define ONVIF_FREE SAFE_FREE \#define ONVIF_MALLOC SAFE_MALLOC \#define ONVIF_STRDUP(str) strdup((str)) \#define TOLOWER(c) (char)((c >= 'A' && c <= 'Z') ? (c | 0x20) : c) \#define TOUPPER(c) (char)((c >= 'a' && c <= 'z') ? (c & ~0x20) : c) \#define ONVIF_TTL 4 \#define ONVIF_MCAST_IP "239.255.255.250" /* Params for Web Service */ \#define ONVIF_PORT_FOR_HTTP 80 \#define ONVIF_PORT_FOR_MAIN 2020 \#define ONVIF_PORT_FOR_DISCV 3702 \#define ONVIF_PORT_FOR_EVENT_BASE 1024 /* Prefix of UUID */ \#define UUID_PREFIX_FMAT "3fa1fe68-b915-4053-a3e1-" \#define UUID_PREFIX_UFMAT "3fa1fe68b9154053a3e1" /* msgq control message*/ \#define MSGQ_ON "on" \#define MSGQ_OFF "off" \#ifndef IFNAME_AP \#define IFNAME_AP "wlan1" \#endif /* Length */ \#define LEN_IFNAME 8 /* length of interface name */ \#define LEN_MTU 8 /* length of MTU str */ \#define LEN_IP 16 /* length of IP */ \#define LEN_MAC 18 /* length of mac */ \#define LEN_XADDR 128 /* length of XAddr */ \#define LEN_STREAM_URI 256 /* length of STREAM URI */ \#define LEN_SOME_INFO 256 /* length of SOME_INFO */ \#define LEN_ANY 4096 /* length of __any */ \#define LEN_INFO 128 /* length of infomation */ \#define LEN_TOKEN 128 /* length of TOKEN */ \#define LEN_NAME 128 /* length of user name */ \#define LEN_TIME 22 /* lendth of time string */ \#define LEN_RECOVERY_STR 9 \#define LEN_SHA_PWD 15 \#define LEN_RES_OPTION_NAME 8 \#define LEN_UUID 37 /* length of UUID */ \#define LEN_TAG 64 \#define LEN_VALUE 32 \#define LEN_INT 16 \#define LEN_BOOL 8 \#define STREAM_TYPE_LEN 8 \#define ONVIF_MAX_FREE_NUM 80 \#define ONVIF_DISCV_BUF_LEN (4 * 1024) /* receive buffer length */ /* 当前NVMP平台的事件通知机制可以取消订阅者数量的限制, \* 但为了防止恶意订阅消耗资源,按目前可支持接入的最大NVR数量限制订阅节点数量。*/ /* tev能力集中支持的最大节点数为10,创建节点是的节点数量限制应该同能力集致, \* RTSP最大流数位8,节点限制位10足够 */ \#define EVENT_NODE_LIMIT 10 \#define ONVIF_SOCKET_RECV_BUF_SIZE (32 * 1024) \#define ONVIF_SOCKET_SEND_BUF_SIZE (32 * 1024) \#define MAX_TP_NAME_SPACE_COUNT 48 \#define MAX_NAMESPACE_ABBR_LEN 16 \#define MAX_NAMESPACE_LEN 128 struct onvif_namespace { char namespace_abbr[MAX_NAMESPACE_ABBR_LEN]; char namespace[MAX_NAMESPACE_LEN]; }; typedef struct _ONVIF_BUF { char *start; char *last; char *end; }ONVIF_BUF; /* 判断有nvr接入需要进行的处理 */ void onvif_nvr_access_event(); BOOL onvif_get_dhcp_enable(); S32 onvif_get_ip_str(char *ip_str, U32 out_len); S32 onvif_get_mac_str(char *mac_str, U32 out_len, BOOL need_separator); U32 onvif_get_mtu(); U32 onvif_get_netmask(); S32 onvif_create_buf(ONVIF_BUF *buf, S32 size); void onvif_free_buf(ONVIF_BUF *buf); S32 onvif_buf_append_string(ONVIF_BUF *buf, const char *str, S32 len); S32 onvif_str_append_chr(char *str, S32 buf_size, const char *fmt, ...); S32 onvif_send_udp_packet(S32 sock, U32 dst_ip, U16 port, char *send_buf, S32 send_len); S32 onvif_send_tcp_block(S32 sock, char *send_buf, S32 send_len); size_t onvif_base64_encode_alloc (const char *in, size_t inlen, char **out); BOOL onvif_base64_decode_alloc (const char *in, size_t inlen, char **out, size_t *outlen); S32 onvif_get_netdevice_name(S32 ifindex, char *ifname, S32 len); S32 onvif_get_discv_mode(); S32 onvif_set_discv_mode(S32 mode); S32 onvif_set_sock_rcvbuf(S32 sock, S32 buf_size); S32 onvif_set_sock_sndbuf(S32 sock, S32 buf_size); void onvif_init_supported_event_topic(); void free_ptrs(void *ptrs[], S32 num); S32 register_ptrs_to_free(void *ptr, void *ptrs[], S32 num); void *malloc_and_register(U32 size, void *ptrs[], S32 num); \#ifndef MALLOC_AND_REGISTER \#define MALLOC_AND_REGISTER(struct, cnt, ptrs, ptr_cnt_max) \ (struct *)malloc_and_register(sizeof(struct) * (cnt), ptrs, ptr_cnt_max) \#endif \#endif /* __ONVIF_COMMON_H__ */ /****************************************************************************** \* Copyright (c) 2018-2018 TP-Link Systems Inc. * \* Filename: onvif_discv.c \* Version: 1.0 \* Description: ONVIF 发现模块 \* Author: liyijie<liyijie@tp-link.com.cn> \* Date: 2018-10-30 ******************************************************************************/ \#include <stdio.h> \#include <stdlib.h> \#include <unistd.h> \#include <string.h> \#include <errno.h> \#include <sys/types.h> \#include <sys/socket.h> \#include <netinet/tcp.h> \#include <arpa/inet.h> \#include "slp_model.h" \#include "onvif_common.h" \#include "onvif_discv.h" \#include "soap_parse.h" \#include "soap_wsdd.h" LOCAL char g_onvif_discv_packet[ONVIF_DISCV_BUF_LEN + 1]; SOAP_CONTEXT *g_soap_for_discv = NULL; S32 g_index_for_discv = -1; LOCAL S32 onvif_recv_udp(SOAP_CONTEXT *soap, S32 sock, char *buf, ​ S32 buf_len, struct sockaddr_in *client) { S32 ret = 0; struct msghdr mh; struct iovec vector; char cmbuf[100] = {0}; struct cmsghdr *cmsg = NULL; struct in_pktinfo *pi = NULL; if (soap == NULL || sock < 0 || buf == NULL || client == NULL) { ​ return -1; } vector.iov_base = buf; vector.iov_len = buf_len; mh.msg_name = client; mh.msg_namelen = sizeof(struct sockaddr_in); mh.msg_iov = &vector; mh.msg_iovlen = 1; mh.msg_control = cmbuf; mh.msg_controllen = sizeof(cmbuf); mh.msg_flags = 0; ret = recvmsg(sock, &mh, 0); for (cmsg = CMSG_FIRSTHDR(&mh); cmsg != NULL; cmsg = CMSG_NXTHDR(&mh, cmsg)) { ​ if (cmsg->cmsg_level != IPPROTO_IP || cmsg->cmsg_type != IP_PKTINFO) ​ { ​ continue; ​ } ​ pi = (struct in_pktinfo *)CMSG_DATA(cmsg); ​ if (pi) ​ { ​ soap->ifindex = pi->ipi_ifindex; ​ } ​ else ​ { ​ soap->ifindex = -1; ​ } ​ break; } return ret; } /****************************************************************************** \* 函数名称: onvif_discover_handle() \* 函数描述: onvif discover报文处理函数 \* 输 入: sock -- socket \* 输 出: N/A \* 返 回 值: ERROR/OK ******************************************************************************/ LOCAL S32 onvif_discover_handle(S32 sock) { S32 length = 0; struct sockaddr_in client; char *xml_buf = NULL; char **xml_str = NULL; char ip_str[LEN_IP] = {0}; if (sock < 0) { ​ ONVIF_ERROR("Invalid listen socket."); ​ return ERROR; } soap_init(g_soap_for_discv); g_soap_for_discv->use_udp = TRUE; memset((void *)&client, 0, sizeof(client)); length = onvif_recv_udp(g_soap_for_discv, sock, g_onvif_discv_packet, ​ ONVIF_DISCV_BUF_LEN, &client); if (length <= 0) { ​ ONVIF_ERROR("recv onvif discover msg failed."); ​ return ERROR; } ONVIF_TRACE("Recv onvif discover msg."); if (OK != onvif_get_ip_str(ip_str, LEN_IP)) { ​ ONVIF_WARN("onvif_get_ip_str failed."); ​ return ERROR; } /* 忽略本机发送的ONVIF包。 */ if (inet_addr(ip_str) == client.sin_addr.s_addr) { ​ ONVIF_TRACE("Recv onvif packet from self."); ​ return OK; } g_soap_for_discv->in_ip = client.sin_addr.s_addr; g_soap_for_discv->sin_port = client.sin_port; g_onvif_discv_packet[length] = '\0'; xml_buf = g_onvif_discv_packet; xml_str = (char **)&xml_buf; if (OK != soap_xml_parse(g_soap_for_discv, xml_str, length)) { ​ ONVIF_TRACE("Soap_xml_parse error."); ​ return ERROR; } if (g_soap_for_discv->tag[0] == '\0') { ​ ONVIF_TRACE("No request tag."); ​ return ERROR; } return soap_serve_request(g_soap_for_discv); } /****************************************************************************** \* 函数名称: onvif_discv_start() \* 函数描述: onvif discover处理模块启动函数 \* 输 入: N/A \* 输 出: N/A \* 返 回 值: ERROR/OK ******************************************************************************/ S32 onvif_discv_start() { int on = 1; ONVIF_INFO("Onvif discovery start"); g_soap_for_discv = new_soap(); if (g_soap_for_discv == NULL) { ​ ONVIF_ERROR("Malloc g_soap_for_discv failed"); ​ return ERROR; } g_soap_for_discv->sock = sock_create_udp(INADDR_ANY, ONVIF_PORT_FOR_DISCV); if (g_soap_for_discv->sock < 0) { ​ ONVIF_ERROR("Sock_create_udp failed"); ​ free_soap(g_soap_for_discv); ​ g_soap_for_discv = NULL; ​ return ERROR; } if (sock_non_block(g_soap_for_discv->sock) < 0) { ​ ONVIF_WARN("Sock_non_block fail with errno:%d", errno); } g_index_for_discv = inet_add_socket(g_soap_for_discv->sock, ​ (void*)onvif_discover_handle, NULL, NULL); if (g_index_for_discv < 0) { ​ ONVIF_ERROR("Inet_socket_add failed"); ​ close(g_soap_for_discv->sock); ​ g_soap_for_discv->sock = -1; ​ free_soap(g_soap_for_discv); ​ g_soap_for_discv = NULL; ​ return ERROR; } if (0 > setsockopt(g_soap_for_discv->sock, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on))) { ​ ONVIF_WARN("setsockopt IP_PKTINFO failed."); } if (0 > onvif_set_sock_rcvbuf(g_soap_for_discv->sock, ONVIF_SOCKET_RECV_BUF_SIZE)) { ​ ONVIF_WARN("setsockopt SO_RCVBUF failed."); } if (0 > onvif_set_sock_sndbuf(g_soap_for_discv->sock, ONVIF_SOCKET_SEND_BUF_SIZE)) { ​ ONVIF_WARN("setsockopt SO_SNDBUF failed."); } if (sock_mcast_ttl(g_soap_for_discv->sock, ONVIF_TTL) < 0) { ​ ONVIF_WARN("setsockopt IP_MULTICAST_TTL fail with errno:%d", errno); } if (sock_mcast_group(g_soap_for_discv->sock, htonl(INADDR_ANY), inet_addr(ONVIF_MCAST_IP)) < 0) { ​ ONVIF_WARN("setsockopt IP_ADD_MEMBERSHIP fail with errno:%d", errno); } if (sock_mcast_if(g_soap_for_discv->sock, htonl(INADDR_ANY)) < 0) { ​ ONVIF_WARN("setsockopt IP_MULTICAST_IF fail with errno:%d", errno); } return OK; } /****************************************************************************** \* 函数名称: onvif_discv_stop() \* 函数描述: onvif discover处理模块停止函数,释放数据 \* 输 入: N/A \* 输 出: N/A \* 返 回 值: ERROR/OK ******************************************************************************/ void onvif_discv_stop() { if (g_index_for_discv > 0) { ​ inet_del_socket(g_index_for_discv); ​ g_index_for_discv = -1; } if (g_soap_for_discv->sock > 0) { ​ close(g_soap_for_discv->sock); } free_soap(g_soap_for_discv); g_soap_for_discv = NULL; return; } /****************************************************************************** \* 函数名称: onvif_discv_remove_socket() \* 函数描述: onvif discover处理模块固件升级时只移除socket,资源设备重启时回收 \* 输 入: N/A \* 输 出: N/A \* 返 回 值: ERROR/OK ******************************************************************************/ void onvif_discv_remove_socket() { if (g_index_for_discv > 0) { ​ inet_del_socket(g_index_for_discv); ​ g_index_for_discv = -1; } if (g_soap_for_discv->sock > 0) { ​ close(g_soap_for_discv->sock); ​ g_soap_for_discv->sock = -1; } return; } /****************************************************************************** \* 函数名称: onvif_hello() \* 函数描述: 发送onvif Hello报文 \* 输 入: N/A \* 输 出: N/A \* 返 回 值: ERROR/OK ******************************************************************************/ S32 onvif_hello() { return soap_wsdd_hello(g_soap_for_discv); } /****************************************************************************** \* 函数名称: onvif_discv_handle_init() \* 函数描述: 注册onvif discover模块需要的soap处理函数 \* 输 入: N/A \* 输 出: N/A \* 返 回 值: N/A ******************************************************************************/ void onvif_discv_handle_init() { soap_wsdd_handle_init(); } /****************************************************************************** \* Copyright (c) 2018-2018 TP-Link Systems Inc. * \* Filename: soap_wsdd.c \* Version: 1.0 \* Description: soap 处理函数头文件 \* Author: liyijie<liyijie@tp-link.com.cn> \* Date: 2018-10-30 ******************************************************************************/ \#include <stdio.h> \#include <stdlib.h> \#include <unistd.h> \#include <string.h> \#include <arpa/inet.h> \#include "soap_global.h" \#include "soap_parse.h" \#include "soap_pack.h" \#include "soap_wsdd.h" /****************************************************************************** \* 函数名称: soap_check_scope() \* 函数描述: 检查输入的scope是否在设备保存的scope列表内 \* 输 入: scopes -- 输入的scope字串 \* 输 出: N/A \* 返 回 值: TRUE/FALSE ******************************************************************************/ LOCAL S32 soap_check_scope(char *scopes) { int i = 0; S32 flag = -1; S32 len = 0; char *s = NULL; char *e = NULL; char *t = NULL; SOAP_NODE *node = NULL; if (scopes == NULL) { ​ return 0; } if (strnlen(scopes, 1) == 0) { ​ return 0; } for (t = scopes; t && strnlen(t, 1); t++) { ​ e = (char *)strchr(t, ' '); ​ if (e != NULL) ​ { ​ len = e - t; ​ } ​ else ​ { ​ len = strlen(scopes) - (t - scopes); ​ } ​ if (len <= 0) ​ { ​ break; ​ } ​ for (i = 0; i < FIX_SCOPE_NUM; i++) ​ { ​ s = G_TP_FIXED_SCOPE[i]; ​ if ( 0 == strncmp(s, t, len) ​ && ('/' == *(s + len) || '\0' == *(s + len)) ) ​ { ​ flag = 0; ​ break; ​ } ​ } ​ if (0 == flag) ​ { ​ break; ​ } ​ for (node = g_scope_list; node; node = node->next) ​ { ​ s = (char*)node->data; ​ if (0 == strncmp(s, t, len) && ('/' == *(s + len) || '\0' == *(s + len))) ​ { ​ flag = 0; ​ break; ​ } ​ } ​ if ( 0 == flag ) ​ { ​ break; ​ } ​ if (NULL != e) ​ { ​ t = strchr(e, ' '); ​ } ​ else ​ { ​ t = NULL; ​ } ​ if (!t) ​ { ​ break; ​ } } return flag; } LOCAL S32 soap_out_scopes_item(ONVIF_BUF *xml_buf) { SOAP_NODE *node = NULL; char scope_string[FIX_SCOPE_NUM * LEN_INFO + FIX_SCOPE_NUM] = {0}; if (xml_buf == NULL) { ​ ONVIF_WARN("xml_buf == NULL."); ​ return ERROR; } /* wsdd:Scopes begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, "wsdd:Scopes", NULL)); snprintf(scope_string, sizeof(scope_string), "%s %s %s %s %s", ​ G_TP_FIXED_SCOPE[0], G_TP_FIXED_SCOPE[1], G_TP_FIXED_SCOPE[2], G_TP_FIXED_SCOPE[3], G_TP_FIXED_SCOPE[4]); SOAP_IF_FAIL_RET(onvif_buf_append_string(xml_buf, scope_string, strlen(scope_string))); for (node = g_scope_list; node; node = node->next) { ​ if (node->data != NULL) ​ { ​ SOAP_IF_FAIL_RET(onvif_buf_append_string(xml_buf, " ", 1)); ​ SOAP_IF_FAIL_RET(onvif_buf_append_string(xml_buf, (char *)node->data, strlen((char *)node->data))); ​ } } /* wsdd:Scopes end */ SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, "wsdd:Scopes")); return OK; } /****************************************************************************** \* 函数名称: soap_out_wsdd_hello_type() \* 函数描述: 组装wsdd:Hello element内容 \* 输 入: xml_buf -- 存放输出xml字串的内存地址 \* data -- 为空 \* 输 出: N/A \* 返 回 值: ERROR/OK ******************************************************************************/ LOCAL S32 soap_out_wsdd_hello_type(ONVIF_BUF *xml_buf, void *data) { S32 len = 0; char tmp_str[LEN_INFO] = {0}; char ip_str[LEN_IP] = {0}; char mac_str[LEN_MAC] = {0}; if (xml_buf == NULL) { ​ ONVIF_WARN("xml_buf == NULL."); ​ return ERROR; } SOAP_IF_FAIL_RET(onvif_get_ip_str(ip_str, LEN_IP)); SOAP_IF_FAIL_RET(onvif_get_mac_str(mac_str, LEN_MAC, FALSE)); /* wsdd:Hello begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, "wsdd:Hello", NULL)); /* wsa:EndpointReference */ len = snprintf(tmp_str, sizeof(tmp_str), "uuid:%s%s", UUID_PREFIX_FMAT, mac_str); if (len < 0 || len >= sizeof(tmp_str)) { ​ return ERROR; } SOAP_IF_FAIL_RET(soap_out_wsa_endpoint_reference(xml_buf, "wsa:EndpointReference", tmp_str)); /* wsdd:Types */ SOAP_IF_FAIL_RET(soap_element(xml_buf, "wsdd:Types", G_TYPES_VAL_STR, NULL)); /* wsdd:Scopes */ SOAP_IF_FAIL_RET(soap_out_scopes_item(xml_buf)); /* wsdd:XAddrs */ len = snprintf(tmp_str, sizeof(tmp_str), "http://%s:%d/onvif/device_service", ​ ip_str, ONVIF_PORT_FOR_MAIN); if (len < 0 || len >= sizeof(tmp_str)) { ​ return ERROR; } SOAP_IF_FAIL_RET(soap_element(xml_buf, "wsdd:XAddrs", tmp_str, NULL)); /* wsdd:MetadataVersion */ SOAP_IF_FAIL_RET(soap_element(xml_buf, "wsdd:MetadataVersion", "1", NULL)); SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, "wsdd:Hello")); return OK; } /****************************************************************************** \* 函数名称: soap_out_wsdd_probe_match_type() \* 函数描述: 组装wsdd:ProbeMatch element内容 \* 输 入: xml_buf -- 存放输出xml字串的内存地址 \* 输 出: N/A \* 返 回 值: ERROR/OK ******************************************************************************/ LOCAL S32 soap_out_wsdd_probe_match_type(ONVIF_BUF *xml_buf) { S32 len = 0; char tmp_str[LEN_INFO] = {0}; char ip_str[LEN_IP] = {0}; char mac_str[LEN_MAC] = {0}; if (xml_buf == NULL) { ​ ONVIF_WARN("xml_buf == NULL."); ​ return ERROR; } SOAP_IF_FAIL_RET(onvif_get_ip_str(ip_str, LEN_IP)); SOAP_IF_FAIL_RET(onvif_get_mac_str(mac_str, LEN_MAC, FALSE)); /* wsdd:ProbeMatch begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, "wsdd:ProbeMatch", NULL)); /* wsa:EndpointReference */ len = snprintf(tmp_str, sizeof(tmp_str), "uuid:%s%s", UUID_PREFIX_FMAT, mac_str); if (len < 0 || len >= sizeof(tmp_str)) { ​ return ERROR; } SOAP_IF_FAIL_RET(soap_out_wsa_endpoint_reference(xml_buf, "wsa:EndpointReference", tmp_str)); /* wsdd:Types */ SOAP_IF_FAIL_RET(soap_element(xml_buf, "wsdd:Types", G_TYPES_VAL_STR, NULL)); /* wsdd:Scopes */ SOAP_IF_FAIL_RET(soap_out_scopes_item(xml_buf)); /* wsdd:XAddrs */ len = snprintf(tmp_str, sizeof(tmp_str), "http://%s:%d/onvif/device_service", ​ ip_str, ONVIF_PORT_FOR_MAIN); if (len < 0 || len >= sizeof(tmp_str)) { ​ return ERROR; } SOAP_IF_FAIL_RET(soap_element(xml_buf, "wsdd:XAddrs", tmp_str, NULL)); /* wsdd:MetadataVersion */ SOAP_IF_FAIL_RET(soap_element(xml_buf, "wsdd:MetadataVersion", "1", NULL)); /* wsdd:ProbeMatch end */ SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, "wsdd:ProbeMatch")); return OK; } /****************************************************************************** \* 函数名称: soap_out_wsdd_probe_matches() \* 函数描述: 组装wsdd:ProbeMatches element内容 \* 输 入: xml_buf -- 存放输出xml字串的内存地址 \* data -- 为空 \* 输 出: N/A \* 返 回 值: ERROR/OK ******************************************************************************/ LOCAL S32 soap_out_wsdd_probe_matches(ONVIF_BUF *xml_buf, void *data) { if (xml_buf == NULL) { ​ ONVIF_WARN("xml_buf == NULL."); ​ return ERROR; } /* wsdd:ProbeMatches begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, "wsdd:ProbeMatches", NULL)); /* wsdd:ProbeMatch */ SOAP_IF_FAIL_RET(soap_out_wsdd_probe_match_type(xml_buf)); /* wsdd:ProbeMatches end */ SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, "wsdd:ProbeMatches")); return OK; } /********************************************************************************* \* Soap wsdd req parse *********************************************************************************/ /****************************************************************************** \* 函数名称: soap_wsdd_probe_req_parse() \* 函数描述: 解析发现报文的具体内容 \* 输 入: soap -- soap结构体 \* req_buf -- 请求内容 \* buf_len -- 请求内容长度 \* 输 出: N/A \* 返 回 值: ERROR/OK ******************************************************************************/ LOCAL S32 soap_wsdd_probe_req_parse(SOAP_CONTEXT *soap, char **req_buf, S32 buf_len) { S32 ch = 0; char charbuf[LEN_INFO] = {0}; char *str_start = NULL; char **p = req_buf; if (soap == NULL) { ​ ONVIF_TRACE("soap == NULL."); ​ return ERROR; } if (req_buf == NULL || *req_buf == NULL || buf_len <= 0) { ​ ONVIF_TRACE("no request context to parse."); ​ return OK; } str_start = *req_buf; while (((*p) - str_start) < buf_len) { ​ if ((ch = soap_get_tag(str_start, buf_len, p, charbuf, sizeof(charbuf))) == EOF) ​ { ​ goto error_parse; ​ } ​ if (charbuf[0] == '/') ​ { ​ continue; ​ } ​ if (TRUE == soap_match_tag(charbuf, "Types")) ​ { ​ if (OK != soap_parse_element_value(str_start, buf_len, p, ch, ​ soap->types, sizeof(soap->types))) ​ { ​ goto error_parse; ​ } ​ ONVIF_TRACE("Types: %s", soap->types); ​ } ​ if (TRUE == soap_match_tag(charbuf, "Scopes")) ​ { ​ if (OK != soap_parse_element_value(str_start, buf_len, p, ch, ​ soap->scopes_item, sizeof(soap->scopes_item))) ​ { ​ goto error_parse; ​ } ​ ONVIF_TRACE("Scopes: %s", soap->scopes_item); ​ } ​ if (TRUE == soap_match_tag(charbuf, "MatchBy")) ​ { ​ if (OK != soap_parse_element_value(str_start, buf_len, p, ch, ​ soap->scopes_matchby, sizeof(soap->scopes_matchby))) ​ { ​ goto error_parse; ​ } ​ ONVIF_TRACE("MatchBy: %s", soap->scopes_matchby); ​ } } return OK; error_parse: return ERROR; } /****************************************************************************** \* 函数名称: soap_wsdd_hello() \* 函数描述: 发送onvif Hello报文 \* 输 入: soap -- soap结构体 \* 输 出: N/A \* 返 回 值: ERROR/OK ******************************************************************************/ S32 soap_wsdd_hello(SOAP_CONTEXT *soap) { if (soap == NULL) { ​ ONVIF_TRACE("soap == NULL."); ​ return ERROR; } SOAP_IF_FAIL_RET(soap_generate_xml((p_out_fun)(soap_out_wsdd_hello_type), soap, NULL)); return onvif_send_udp_packet(soap->sock, inet_addr(ONVIF_MCAST_IP), htons(ONVIF_PORT_FOR_DISCV), ​ soap->xml_buf.start, (soap->xml_buf.last - soap->xml_buf.start)); } /****************************************************************************** \* 函数名称: soap_wsdd_probe_handle() \* 函数描述: 发现请求的处理函数 \* 输 入: soap -- soap结构体 \* 输 出: N/A \* 返 回 值: ERROR/OK ******************************************************************************/ LOCAL S32 soap_wsdd_probe_handle(SOAP_CONTEXT *soap) { S32 request_len = 0; BOOL match = FALSE; char *xml_buf = NULL; char **xml_str = NULL; char ifname[LEN_IFNAME] = {0}; S32 scopes_matchby_flag = 0; if (soap == NULL) { ​ ONVIF_WARN("soap == NULL."); ​ return ERROR; } if (onvif_get_discv_mode() == DIS_MODE_NO) { ​ ONVIF_DEBUG("discover mode is non-discoverable."); ​ return OK; } if (soap->request_begin != NULL && soap->request_end != NULL) { ​ request_len = soap->request_end - soap->request_begin; ​ if (request_len < 0) ​ { ​ ONVIF_WARN("request_len < 0."); ​ return ERROR; ​ } ​ xml_buf = soap->request_begin; ​ xml_str = (char **)&xml_buf; ​ if (strstr(xml_buf, "<d:Scopes MatchBy=\"http://schemas.xmlsoap.org/ws/2005/04/discovery/rfc3986\"></d:Scopes>") != NULL) ​ { ​ scopes_matchby_flag = 1; ​ } } else { ​ ONVIF_ERROR("soap request content is NULL."); ​ return ERROR; } if (OK != soap_wsdd_probe_req_parse(soap, xml_str, request_len)) { ​ return ERROR; } if (soap->types[0] != '\0') { ​ if(0 == strcmp("tds:Device", soap->types) || ​ 0 == strcmp("tdn:NetworkVideoTransmitter", soap->types) || ​ 0 == strcmp("dn:NetworkVideoTransmitter", soap->types)) ​ { ​ match = TRUE; ​ } ​ else if(0 == strcmp("dn:NetworkVideoRecorder", soap->types) || ​ 0 == strcmp("tdn:NetworkVideoRecorder", soap->types)) ​ { ​ return OK; ​ } } else { ​ match = TRUE; } if (match) { ​ if (onvif_get_netdevice_name(soap->ifindex, ifname, LEN_IFNAME) == 0 && ​ strncmp(ifname, IFNAME_AP, LEN_IFNAME) != 0) ​ { ​ /* 通知IPCD进行probe IP的跟随修改 */ ​ NSD_SEND(NSD_PROBE_IP, (U8*)(&soap->in_ip), sizeof(soap->in_ip)); ​ } } if (soap->scopes_item[0] != '\0') { ​ if (soap->scopes_matchby[0] != '\0') ​ { ​ if (soap->has_header) ​ { ​ snprintf(soap->header.wsa_action, sizeof(soap->header.wsa_action), ​ "http://schemas.xmlsoap.org/ws/2005/04/discovery/fault"); ​ snprintf(soap->header.wsa_to, sizeof(soap->header.wsa_to), ​ "http://schemas.xmlsoap.org/ws/2005/04/addressing/role/anonymous"); ​ } ​ soap_fault(soap, "SOAP-ENV:Sender", "wsdd:MatchingRuleNotSupported", ​ NULL, "the matching rule specified is not supported"); ​ soap->error = SOAP_FAULT; ​ return ERROR; ​ } ​ if (0 != soap_check_scope(soap->scopes_item)) ​ { ​ return ERROR; ​ } } if (scopes_matchby_flag == 1) { ​ if (soap->has_header) ​ { ​ snprintf(soap->header.wsa_action, sizeof(soap->header.wsa_action), ​ "http://schemas.xmlsoap.org/ws/2005/04/discovery/fault"); ​ snprintf(soap->header.wsa_to, sizeof(soap->header.wsa_to), ​ "http://schemas.xmlsoap.org/ws/2005/04/addressing/role/anonymous"); ​ } ​ soap_fault(soap, "SOAP-ENV:Sender", "wsdd:MatchingRuleNotSupported", ​ NULL, "the matching rule specified is not supported"); ​ soap->error = SOAP_FAULT; ​ return ERROR; } //SOAP_IF_FAIL_RET(soap_generate_xml((p_out_fun)(soap_out_wsdd_probe_matches), soap, NULL)); //return onvif_send_udp_packet(soap->sock, soap->in_ip, htons(ONVIF_PORT_FOR_DISCV), // soap->xml_buf.start, (soap->xml_buf.last - soap->xml_buf.start)); if (soap->header.wsa_relates_to[0] == '\0') { ​ snprintf(soap->header.wsa_relates_to, sizeof(soap->header.wsa_relates_to), ​ "%s", soap->header.wsa_msg_id); } if (soap->header.wsa_action[0] != '\0') { ​ onvif_str_append_chr(soap->header.wsa_action, sizeof(soap->header.wsa_action), "Matches"); } return soap_generate_xml((p_out_fun)(soap_out_wsdd_probe_matches), soap, NULL); } /********************************************************************************* \* Soap wsdd Handles init *********************************************************************************/ void soap_wsdd_handle_init() { soap_tag_handle_add("Probe", soap_wsdd_probe_handle); }——这部分是onvif模块剩下的部分代码,当前onvif模块仅支持http不支持https,需要提供onvif over tls支持,能够兼容切换非tls和tls client连接,端口选择443,请你分析下目前已有条件还有接下来需要做的所有修改,详细阐述并给出代码
最新发布
10-08
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值