<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "ua_server_internal.h"
#include "ua_services.h"
#include "ua_mdns_internal.h"

#if defined(UA_ENABLE_DISCOVERY) && defined(UA_ENABLE_DISCOVERY_MULTICAST)

#ifdef _MSC_VER
# ifndef UNDER_CE
#  include <io.h> //access
#  define access _access
# endif
#else
# include <unistd.h> //access
#endif

#include <fcntl.h>
#include <errno.h>
#ifdef _WIN32
# define CLOSESOCKET(S) closesocket((SOCKET)S)
#else
# define CLOSESOCKET(S) close(S)
#endif

void Service_FindServersOnNetwork(UA_Server *server, UA_Session *session,
                                  const UA_FindServersOnNetworkRequest *request,
                                  UA_FindServersOnNetworkResponse *response) {
    UA_UInt32 recordCount;
    if(request->startingRecordId < server->serverOnNetworkRecordIdCounter)
        recordCount = server->serverOnNetworkRecordIdCounter - request->startingRecordId;
    else
        recordCount = 0;

    if(request->maxRecordsToReturn && recordCount > request->maxRecordsToReturn)
        recordCount = recordCount > request->maxRecordsToReturn ? request->maxRecordsToReturn : recordCount;

    UA_ServerOnNetwork** filtered = NULL;
    if(recordCount > 0) {
        filtered = (UA_ServerOnNetwork**)UA_malloc(sizeof(UA_ServerOnNetwork*) * recordCount);
        if(!filtered) {
            response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
            return;
        }
        memset(filtered, 0, sizeof(UA_ServerOnNetwork*) * recordCount);

        if(request->serverCapabilityFilterSize) {
            UA_UInt32 filteredCount = 0;
            // iterate over all records and add to filtered list
            serverOnNetwork_list_entry* current;
            LIST_FOREACH(current, &server->serverOnNetwork, pointers) {
                if (current->serverOnNetwork.recordId < request->startingRecordId)
                    continue;
                UA_Boolean foundAll = UA_TRUE;
                for(size_t i = 0; i < request->serverCapabilityFilterSize && foundAll; i++) {
                    UA_Boolean foundSingle = UA_FALSE;
                    for(size_t j = 0; j < current->serverOnNetwork.serverCapabilitiesSize && !foundSingle; j++)
                        foundSingle |= UA_String_equal(&request->serverCapabilityFilter[i],
                                                       &current->serverOnNetwork.serverCapabilities[j]);
                    foundAll &= foundSingle;
                }
                if (foundAll) {
                    filtered[filteredCount++] = &current->serverOnNetwork;
                    if (filteredCount >= recordCount)
                        break;
                }
            }
            recordCount = filteredCount;
        } else {
            UA_UInt32 filteredCount = 0;
            serverOnNetwork_list_entry* current;
            LIST_FOREACH(current, &server->serverOnNetwork, pointers) {
                if (current->serverOnNetwork.recordId < request->startingRecordId)
                    continue;
                filtered[filteredCount++] = &current->serverOnNetwork;
                if (filteredCount >= recordCount)
                    break;
            }
            recordCount = filteredCount;
        }
    }

    response->serversSize = recordCount;
    if(recordCount > 0) {
        response->servers = (UA_ServerOnNetwork *)UA_malloc(sizeof(UA_ServerOnNetwork)*recordCount);
        if(!response->servers) {
            response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
            free(filtered);
            return;
        }

        for(size_t i = 0; i < recordCount; i++)
            UA_ServerOnNetwork_copy(filtered[i],&response->servers[recordCount-i-1]);
    }
    free(filtered);

    UA_DateTime_copy(&server->serverOnNetworkRecordIdLastReset, &response->lastCounterResetTime);
    response->responseHeader.serviceResult = UA_STATUSCODE_GOOD;
}

void
UA_Discovery_update_MdnsForDiscoveryUrl(UA_Server *server, const char *serverName,
                                        UA_MdnsDiscoveryConfiguration *mdnsConfig,
                                        const UA_String discoveryUrl, UA_Boolean isOnline,
                                        UA_Boolean updateTxt) {
    UA_UInt16 port = 0;
    char hostname[256]; hostname[0] = '\0';
    const char *path = NULL;

    size_t uriSize = sizeof(char) * discoveryUrl.length + 1;

    // todo: malloc may fail: return a statuscode
    char* uri = (char*)malloc(uriSize);
    strncpy(uri, (char*) discoveryUrl.data, discoveryUrl.length);
    uri[discoveryUrl.length] = '\0';

    UA_StatusCode retval = UA_EndpointUrl_split(uri, hostname, &port, &path);
    if (retval != UA_STATUSCODE_GOOD) {
        hostname[0] = '\0';
        if (retval == UA_STATUSCODE_BADOUTOFRANGE)
            UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_NETWORK,
                           "Server url size invalid");
        else if (retval == UA_STATUSCODE_BADATTRIBUTEIDINVALID)
            UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_NETWORK,
                           "Server url does not begin with opc.tcp://");
    }
    free(uri);

    if(!isOnline) {
        UA_StatusCode removeRetval =
                UA_Discovery_removeRecord(server, serverName, hostname,
                                          (unsigned short) port, updateTxt);
        if(removeRetval != UA_STATUSCODE_GOOD) {
            UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
                           "Could not remove mDNS record for hostname %s.", serverName);
        }
    } else {
        UA_String *capabilities = NULL;
        size_t capabilitiesSize = 0;
        if(mdnsConfig) {
            capabilities = mdnsConfig->serverCapabilities;
            capabilitiesSize = mdnsConfig->serverCapabilitiesSize;
        }
        UA_StatusCode addRetval =
                UA_Discovery_addRecord(server, serverName, hostname,
                                       (unsigned short) port, path,
                                       UA_DISCOVERY_TCP, updateTxt, capabilities, &capabilitiesSize);
        if(addRetval != UA_STATUSCODE_GOOD) {
            UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
                           "Could not add mDNS record for hostname %s.", serverName);
        }
    }
}

void
UA_Server_setServerOnNetworkCallback(UA_Server *server, UA_Server_serverOnNetworkCallback cb,
                                     void* data) {
    server->serverOnNetworkCallback = cb;
    server->serverOnNetworkCallbackData = data;
}

static void
socket_mdns_set_nonblocking(int sockfd) {
#ifdef _WIN32
    u_long iMode = 1;
    ioctlsocket(sockfd, FIONBIO, &iMode);
#else
    int opts = fcntl(sockfd, F_GETFL);
    fcntl(sockfd, F_SETFL, opts|O_NONBLOCK);
#endif
}

/* Create multicast 192.168.3.11:5353 socket */
static int discovery_createMulticastSocket(void) {
    int s, flag = 1, ittl = 255;
    struct sockaddr_in in;
    struct ip_mreq mc;
    char ttl = (char)255; // publish to complete net, not only subnet. See:
                          // https://docs.oracle.com/cd/E23824_01/html/821-1602/sockets-137.html

    memset(&in, 0, sizeof(in));
    in.sin_family = AF_INET;
    in.sin_port = htons(5353);
    in.sin_addr.s_addr = 0;

    if ((s = (int)socket(AF_INET, SOCK_DGRAM, 0)) < 0)
        return 0;

#ifdef SO_REUSEPORT
    setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (char *)&flag, sizeof(flag));
#endif
    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
    if (bind(s, (struct sockaddr *)&in, sizeof(in))) {
        CLOSESOCKET(s);
        return 0;
    }

    mc.imr_multiaddr.s_addr = inet_addr("192.168.3.11");
    mc.imr_interface.s_addr = htonl(INADDR_ANY);
    setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mc, sizeof(mc));
    setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ttl, sizeof(ttl));<commit_msg>move filtering of server records into a separate function<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "ua_server_internal.h"
#include "ua_services.h"
#include "ua_mdns_internal.h"

#if defined(UA_ENABLE_DISCOVERY) && defined(UA_ENABLE_DISCOVERY_MULTICAST)

#ifdef _MSC_VER
# ifndef UNDER_CE
#  include <io.h> //access
#  define access _access
# endif
#else
# include <unistd.h> //access
#endif

#include <fcntl.h>
#include <errno.h>
#ifdef _WIN32
# define CLOSESOCKET(S) closesocket((SOCKET)S)
#else
# define CLOSESOCKET(S) close(S)
#endif

/* All filter criteria must be fulfilled */
static UA_Boolean
filterServerRecord(size_t serverCapabilityFilterSize, UA_String *serverCapabilityFilter,
                   serverOnNetwork_list_entry* current) {
    for(size_t i = 0; i < request->serverCapabilityFilterSize; i++) {
        for(size_t j = 0; j < current->serverOnNetwork.serverCapabilitiesSize; j++)
            if(!UA_String_equal(&request->serverCapabilityFilter[i],
                                &current->serverOnNetwork.serverCapabilities[j]))
                return false;
    }
    return true;
}

void Service_FindServersOnNetwork(UA_Server *server, UA_Session *session,
                                  const UA_FindServersOnNetworkRequest *request,
                                  UA_FindServersOnNetworkResponse *response) {
    /* Set LastCounterResetTime */
    UA_DateTime_copy(&server->serverOnNetworkRecordIdLastReset, &response->lastCounterResetTime);

    /* Compute the max number of records to return */
    UA_UInt32 recordCount = 0;
    if(request->startingRecordId < server->serverOnNetworkRecordIdCounter)
        recordCount = server->serverOnNetworkRecordIdCounter - request->startingRecordId;
    if(request->maxRecordsToReturn && recordCount > request->maxRecordsToReturn)
        recordCount = recordCount > request->maxRecordsToReturn ? request->maxRecordsToReturn : recordCount;
    if(recordCount == 0) {
        response->serversSize = 0;
        return;
    }

    /* Iterate over all records and add to filtered list */
    UA_UInt32 filteredCount = 0;
    UA_ServerOnNetwork** filtered =
        (UA_ServerOnNetwork**)UA_alloca(sizeof(UA_ServerOnNetwork*) * recordCount);
    serverOnNetwork_list_entry* current;
    LIST_FOREACH(current, &server->serverOnNetwork, pointers) {
        if(filteredCount >= recordCount)
            break;
        if(current->serverOnNetwork.recordId < request->startingRecordId)
            continue;
        if(!filterServerRecord(request->serverCapabilityFilterSize,
                               request->serverCapabilityFilter, current))
            continue;
        filtered[filteredCount++] = &current->serverOnNetwork;
    }

    /* Allocate the array for the response */
    response->servers = (UA_ServerOnNetwork*)UA_malloc(sizeof(UA_ServerOnNetwork)*filteredCount);
    if(!response->servers) {
        response->serversSize = -1;
        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
    }
    response->serversSize = filteredCount;

    /* Copy the server names */
    for(size_t i = 0; i < filteredCount; i++)
        UA_ServerOnNetwork_copy(filtered[i], &response->servers[filteredCount-i-1]);
}

void
UA_Discovery_update_MdnsForDiscoveryUrl(UA_Server *server, const char *serverName,
                                        UA_MdnsDiscoveryConfiguration *mdnsConfig,
                                        const UA_String discoveryUrl, UA_Boolean isOnline,
                                        UA_Boolean updateTxt) {
    UA_UInt16 port = 0;
    char hostname[256]; hostname[0] = '\0';
    const char *path = NULL;

    size_t uriSize = sizeof(char) * discoveryUrl.length + 1;

    // todo: malloc may fail: return a statuscode
    char* uri = (char*)malloc(uriSize);
    strncpy(uri, (char*) discoveryUrl.data, discoveryUrl.length);
    uri[discoveryUrl.length] = '\0';

    UA_StatusCode retval = UA_EndpointUrl_split(uri, hostname, &port, &path);
    if (retval != UA_STATUSCODE_GOOD) {
        hostname[0] = '\0';
        if (retval == UA_STATUSCODE_BADOUTOFRANGE)
            UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_NETWORK,
                           "Server url size invalid");
        else if (retval == UA_STATUSCODE_BADATTRIBUTEIDINVALID)
            UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_NETWORK,
                           "Server url does not begin with opc.tcp://");
    }
    free(uri);

    if(!isOnline) {
        UA_StatusCode removeRetval =
                UA_Discovery_removeRecord(server, serverName, hostname,
                                          (unsigned short) port, updateTxt);
        if(removeRetval != UA_STATUSCODE_GOOD) {
            UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
                           "Could not remove mDNS record for hostname %s.", serverName);
        }
    } else {
        UA_String *capabilities = NULL;
        size_t capabilitiesSize = 0;
        if(mdnsConfig) {
            capabilities = mdnsConfig->serverCapabilities;
            capabilitiesSize = mdnsConfig->serverCapabilitiesSize;
        }
        UA_StatusCode addRetval =
                UA_Discovery_addRecord(server, serverName, hostname,
                                       (unsigned short) port, path,
                                       UA_DISCOVERY_TCP, updateTxt,
                                       capabilities, &capabilitiesSize);
        if(addRetval != UA_STATUSCODE_GOOD) {
            UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
                           "Could not add mDNS record for hostname %s.", serverName);
        }
    }
}

void
UA_Server_setServerOnNetworkCallback(UA_Server *server, UA_Server_serverOnNetworkCallback cb,
                                     void* data) {
    server->serverOnNetworkCallback = cb;
    server->serverOnNetworkCallbackData = data;
}

static void
socket_mdns_set_nonblocking(int sockfd) {
#ifdef _WIN32
    u_long iMode = 1;
    ioctlsocket(sockfd, FIONBIO, &iMode);
#else
    int opts = fcntl(sockfd, F_GETFL);
    fcntl(sockfd, F_SETFL, opts|O_NONBLOCK);
#endif
}

/* Create multicast 192.168.3.11:5353 socket */
static int
discovery_createMulticastSocket(void) {
    int s, flag = 1, ittl = 255;
    struct sockaddr_in in;
    struct ip_mreq mc;
    char ttl = (char)255; // publish to complete net, not only subnet. See:
                          // https://docs.oracle.com/cd/E23824_01/html/821-1602/sockets-137.html

    memset(&in, 0, sizeof(in));
    in.sin_family = AF_INET;
    in.sin_port = htons(5353);
    in.sin_addr.s_addr = 0;

    if ((s = (int)socket(AF_INET, SOCK_DGRAM, 0)) < 0)
        return 0;

#ifdef SO_REUSEPORT
    setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (char *)&flag, sizeof(flag));
#endif
    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
    if (bind(s, (struct sockaddr *)&in, sizeof(in))) {
        CLOSESOCKET(s);
        return 0;
    }

    mc.imr_multiaddr.s_addr = inet_addr("192.168.3.11");
    mc.imr_interface.s_addr = htonl(INADDR_ANY);
    setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mc, sizeof(mc));
    setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ttl, sizeof(ttl));
{'fraction_non_alphanumeric': 0.10034847702632937, 'fraction_numerical': 0.011938564790913785, 'mean_word_length': 2.5124660018132365, 'pattern_counts': {'":': 0, '<': 24, '<?xml version=': 0, '>': 93, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 4, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 23, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4730257', 'n_tokens_mistral': 4880, 'n_tokens_neox': 4629, 'n_words': 956}