--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+
+#include <string>
+
+#include <wifi-aware.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <errno.h>
+
+#include "vine-constants.h"
+#include "vine-disc-plugin.h"
+#include "vine-log.h"
+#include "vine-utils.h"
+#include "nan-plugin.h"
+
+typedef struct {
+ void *disc_handle; // vine_disc handle
+ wifi_aware_publish_h publish_config;
+ wifi_aware_subscribe_h subscribe_config;
+ wifi_aware_session_h session;
+ //char service_type[NAN_SERVICE_TYPE_LEN + 1];
+ char service_name[VINE_MAX_NAN_SERVICE_NAME_LEN + 1];
+} vine_nan_s;
+
+static bool __is_nan_enabled = false;
+static vine_disc_plugin_callbacks event_callbacks;
+
+static vine_disc_error __convert_nan_error_to_vine_disc_error(int error)
+{
+ return VINE_DISC_ERROR_NONE;
+}
+
+
+vine_disc_error nan_resolve_ip(void *plugin_handle,
+ const char *service_type, const char *service_name,
+ const char *host_name, const char *iface_name, int family)
+{
+ RET_VAL_IF(!plugin_handle, VINE_DISC_ERROR_INVALID_PARAMETER, "plugin_handle is NULL");
+ RET_VAL_IF(!service_type, VINE_DISC_ERROR_INVALID_PARAMETER, "service_type is NULL");
+ RET_VAL_IF(!service_name, VINE_DISC_ERROR_INVALID_PARAMETER, "service_name is NULL");
+ // Ignore host_name and iface_name
+
+ RET_VAL_IF(family == VINE_DISC_ADDR_FAMILY_IPV4, VINE_DISC_ERROR_NOT_SUPPORTED,
+ "Only IPv6 is allowed for NAN");
+
+ VINE_LOGD("Start to resolve IP. plugin_handle[%p]\n", plugin_handle);
+ VINE_LOGD("type[%s] name[%s]", service_type, service_name);
+
+ vine_nan_s *nan_handle = (vine_nan_s *)plugin_handle;
+ return VINE_DISC_ERROR_NONE;
+}
+
+vine_disc_error nan_cancel_resolve_ip(void *plugin_handle)
+{
+ RET_VAL_IF(!plugin_handle, VINE_DISC_ERROR_INVALID_PARAMETER, "plugin_handle is NULL");
+ VINE_LOGD("Cancel resolving IP. plugin_handle[%p]\n", plugin_handle);
+
+ return VINE_DISC_ERROR_NONE;
+}
+
+
+vine_disc_error nan_init(void **plugin_handle, void *disc_handle)
+{
+ vine_nan_s *handle = new vine_nan_s;
+
+ handle->disc_handle = disc_handle;
+ handle->publish_config = nullptr;
+ handle->subscribe_config = nullptr;
+ handle->session = nullptr;
+
+ // NAN will be enabled when publish() or subscribe()
+ // because enable function works asynchronously.
+
+ return VINE_DISC_ERROR_NONE;
+}
+
+void nan_deinit(void *plugin_handle)
+{
+ RET_IF(plugin_handle == NULL, "Plugin handle is null");
+ vine_nan_s *nan_handle = (vine_nan_s *)plugin_handle;
+
+ // TODO
+ // Disable NAN here.
+ // However, we have to check if NAN is used for data path
+
+ delete nan_handle;
+}
+
+static void __published_cb(wifi_aware_session_h session,
+ wifi_aware_error_e error, void *user_data)
+{
+ VINE_LOGD("Service is published");
+ vine_nan_s *nan_handle = (vine_nan_s *)user_data;
+
+ if (event_callbacks.published_cb)
+ event_callbacks.published_cb(nan_handle,
+ nan_handle->service_name, __convert_nan_error_to_vine_disc_error(error),
+ nan_handle->disc_handle);
+}
+
+static void __publish(vine_nan_s *nan_handle)
+{
+ wifi_aware_session_h session;
+ int ret = wifi_aware_session_create(WIFI_AWARE_SESSION_PUBLISH, &session);
+ RET_IF(ret != WIFI_AWARE_ERROR_NONE, "wifi_aware_session_create() fails");
+
+ ret = wifi_aware_session_publish(session, nan_handle->publish_config,
+ __published_cb, nan_handle);
+ if (ret != WIFI_AWARE_ERROR_NONE) {
+ VINE_LOGE("wifi_aware_session_publish() fails");
+ wifi_aware_session_destroy(session);
+ }
+
+ nan_handle->session = session;
+}
+
+static void __subscribed_cb(wifi_aware_session_h session,
+ wifi_aware_error_e error, void *user_data)
+{
+ RET_IF(error == WIFI_AWARE_ERROR_NONE, "No error");
+ vine_nan_s *nan_handle = (vine_nan_s *)user_data;
+
+ VINE_LOGE("Fails to subscribe. error: %s", __convert_nan_error_to_vine_disc_error(error));
+ if (event_callbacks.discovered_cb) {
+ std::map<string, string> empty_map;
+ event_callbacks.discovered_cb(nan_handle, false,
+ nullptr, nullptr, nullptr, -1, empty_map, nullptr, 0, nan_handle->disc_handle);
+ }
+}
+
+static void __subscribe(vine_nan_s *nan_handle)
+{
+ wifi_aware_session_h session;
+ int ret = wifi_aware_session_create(WIFI_AWARE_SESSION_SUBSCRIBE, &session);
+ RET_IF(ret != WIFI_AWARE_ERROR_NONE, "wifi_aware_session_create() fails");
+
+ ret = wifi_aware_session_subscribe(session, nan_handle->subscribe_config,
+ __subscribed_cb, nan_handle);
+ if (ret != WIFI_AWARE_ERROR_NONE) {
+ VINE_LOGE("wifi_aware_session_publish() fails");
+ wifi_aware_session_destroy(session);
+ }
+
+ nan_handle->session = session;
+}
+
+static void __start_session(vine_nan_s *nan_handle)
+{
+ if (nan_handle->publish_config)
+ __publish(nan_handle);
+ else if (nan_handle->subscribe_config)
+ __subscribe(nan_handle);
+ else
+ VINE_LOGE("Invalid operation");
+}
+
+static void __stop_session(vine_nan_s *nan_handle)
+{
+ if (nan_handle->session) {
+ wifi_aware_session_stop(nan_handle->session);
+ wifi_aware_session_destroy(nan_handle->session);
+ }
+}
+
+static void __enabled_cb(wifi_aware_error_e error, void *user_data)
+{
+ RET_IF(!user_data, "nan_handle is NULL");
+ __start_session((vine_nan_s *)user_data);
+}
+
+static bool __check_attr_len(const map<string, string> &attributes)
+{
+ int info_len = VINE_MAX_NAN_SERVICE_NAME_LEN;
+ for (const auto &kv : attributes) {
+ auto key = kv.first;
+ auto val = kv.second;
+ info_len += key.size() + val.size() + 1;
+ if (info_len > MAX_SPECIFIC_INFO_LEN)
+ return false;
+ }
+ return true;
+}
+
+static void __fill_specific_info(unsigned char *info, const char *service_name,
+ const map<string, string> &attributes)
+{
+ int service_name_len = strlen(service_name);
+ memcpy(info, service_name, service_name_len);
+ info[service_name_len] = 0;
+
+ unsigned char *ptr = &info[VINE_MAX_NAN_SERVICE_NAME_LEN];
+ for (const auto &kv : attributes) {
+ auto key = kv.first;
+ auto val = kv.second;
+ int key_len = key.size();
+ int val_len = val.size();
+ *ptr = (unsigned char)(key_len + val_len + 1);
+ ptr++;
+ memcpy(ptr, key.c_str(), key_len);
+ ptr += key_len;
+ *ptr++ = '=';
+ memcpy(ptr, val.c_str(), val_len);
+ ptr += val_len;
+ }
+}
+
+vine_disc_error nan_publish(void *plugin_handle, const char *service_type,
+ const char *service_name, int port, const map<string, string> &attributes,
+ const char *iface_name)
+{
+ RET_VAL_IF(!plugin_handle, VINE_DISC_ERROR_INVALID_PARAMETER, "plugin_handle is NULL");
+ RET_VAL_IF(!service_type, VINE_DISC_ERROR_INVALID_PARAMETER, "service_type is NULL");
+ RET_VAL_IF(!service_name, VINE_DISC_ERROR_INVALID_PARAMETER, "service_name is NULL");
+ RET_VAL_IF(strlen(service_type) > NAN_MAX_SERVICE_NAME_LEN, VINE_DISC_ERROR_INVALID_PARAMETER,
+ "Too long service type");
+ RET_VAL_IF(strlen(service_name) > VINE_MAX_NAN_SERVICE_NAME_LEN, VINE_DISC_ERROR_INVALID_PARAMETER,
+ "Too long service name");
+ RET_VAL_IF(__check_attr_len(attributes) == false, VINE_DISC_ERROR_INVALID_PARAMETER,
+ "Too long attributes");
+ for (const auto &kv : attributes) {
+ auto key = kv.first.c_str();
+ auto val = kv.second.c_str();
+ }
+
+ vine_nan_s *nan_handle = (vine_nan_s *)plugin_handle;
+ VINE_LOGD("Publish a service. plugin_handle[%p]\n", plugin_handle);
+
+ strncpy(nan_handle->service_name, service_name, VINE_MAX_NAN_SERVICE_NAME_LEN);
+
+ wifi_aware_publish_h config = nullptr;
+ int ret = wifi_aware_publish_create(&config);
+ RET_VAL_IF(ret != WIFI_AWARE_ERROR_NONE, __convert_nan_error_to_vine_disc_error(ret),
+ "wifi_aware_publish_create() fails");
+
+ unsigned char info[MAX_SPECIFIC_INFO_LEN + 1] = {0, };
+
+ ret = wifi_aware_publish_set_type(config, WIFI_AWARE_PUBLISH_TYPE_UNSOLICITED);
+ if (ret != WIFI_AWARE_ERROR_NONE) {
+ VINE_LOGE("wifi_aware_publish_set_type() fails");
+ goto ERR;
+ }
+
+ ret = wifi_aware_publish_set_service_name(config, service_type);
+ if (ret != WIFI_AWARE_ERROR_NONE) {
+ VINE_LOGE("wifi_aware_publish_set_service_name() fails");
+ goto ERR;
+ }
+
+ __fill_specific_info(info, service_name, attributes);
+
+ nan_handle->publish_config = config;
+
+ ret = wifi_aware_enable(__enabled_cb, nan_handle);
+ if (ret != WIFI_AWARE_ERROR_NONE) {
+ VINE_LOGE("wifi_aware_enable() fails");
+ goto ERR;
+ }
+
+ return VINE_DISC_ERROR_NONE;
+
+ERR:
+ wifi_aware_publish_destroy(config);
+ return __convert_nan_error_to_vine_disc_error(ret);
+}
+
+vine_disc_error nan_stop_publish(void *plugin_handle)
+{
+ RET_VAL_IF(!plugin_handle, VINE_DISC_ERROR_INVALID_PARAMETER, "plugin_handle is NULL");
+
+ vine_nan_s *nan_handle = (vine_nan_s *)plugin_handle;
+ __stop_session(nan_handle);
+
+ if (nan_handle->publish_config)
+ wifi_aware_publish_destroy(nan_handle->publish_config);
+
+ return VINE_DISC_ERROR_NONE;
+}
+
+vine_disc_error nan_subscribe(void *plugin_handle,
+ const char *service_type, const char *iface_name)
+{
+ RET_VAL_IF(!plugin_handle, VINE_DISC_ERROR_INVALID_PARAMETER, "plugin_handle is NULL");
+ RET_VAL_IF(!service_type, VINE_DISC_ERROR_INVALID_PARAMETER, "service_type is NULL");
+ RET_VAL_IF(strlen(service_type) > NAN_MAX_SERVICE_NAME_LEN, VINE_DISC_ERROR_INVALID_PARAMETER,
+ "Too long service type");
+
+ vine_nan_s *nan_handle = (vine_nan_s *)plugin_handle;
+ VINE_LOGD("Publish a service. plugin_handle[%p]\n", plugin_handle);
+
+ wifi_aware_subscribe_h config = nullptr;
+ int ret = wifi_aware_subscribe_create(&config);
+ RET_VAL_IF(ret != WIFI_AWARE_ERROR_NONE, __convert_nan_error_to_vine_disc_error(ret),
+ "wifi_aware_subscribe_create() fails");
+
+ ret = wifi_aware_subscribe_set_type(config, WIFI_AWARE_SUBSCRIBE_TYPE_ACTIVE);
+ if (ret != WIFI_AWARE_ERROR_NONE) {
+ VINE_LOGE("wifi_aware_subscribe_set_type() fails");
+ goto ERR;
+ }
+
+ ret = wifi_aware_subscribe_set_service_name(config, service_type);
+ if (ret != WIFI_AWARE_ERROR_NONE) {
+ VINE_LOGE("wifi_aware_subscribe_set_service_name() fails");
+ goto ERR;
+ }
+
+ nan_handle->subscribe_config = config;
+
+ ret = wifi_aware_enable(__enabled_cb, nan_handle);
+ if (ret != WIFI_AWARE_ERROR_NONE) {
+ VINE_LOGE("wifi_aware_enable() fails");
+ goto ERR;
+ }
+
+ return VINE_DISC_ERROR_NONE;
+
+ERR:
+ wifi_aware_subscribe_destroy(config);
+ return __convert_nan_error_to_vine_disc_error(ret);
+}
+
+vine_disc_error nan_stop_subscribe(void *plugin_handle)
+{
+ RET_VAL_IF(!plugin_handle, VINE_DISC_ERROR_INVALID_PARAMETER, "plugin_handle is NULL");
+
+ vine_nan_s *nan_handle = (vine_nan_s *)plugin_handle;
+ __stop_session(nan_handle);
+
+ if (nan_handle->subscribe_config)
+ wifi_aware_subscribe_destroy(nan_handle->subscribe_config);
+
+ return VINE_DISC_ERROR_NONE;
+}
+
+
+void nan_register_callbacks(vine_disc_plugin_callbacks callbacks)
+{
+ event_callbacks.published_cb = callbacks.published_cb;
+ event_callbacks.discovered_cb = callbacks.discovered_cb;
+ event_callbacks.ip_resolved_cb = callbacks.ip_resolved_cb;
+ event_callbacks.fd_added_cb = nullptr;
+ event_callbacks.fd_removed_cb = nullptr;
+ event_callbacks.ble_discovered_cb = nullptr;
+}
+
+vine_disc_error nan_process_event(void *plugin_handle, int fd)
+{
+ VINE_LOGE("Not supported function");
+ return VINE_DISC_ERROR_NOT_SUPPORTED;
+}
+
+void vine_disc_plugin_init(vine_disc_plugin_fn *fn)
+{
+ fn->init = nan_init;
+ fn->deinit = nan_deinit;
+ fn->publish = nan_publish;
+ fn->stop_publish = nan_stop_publish;
+ fn->subscribe = nan_subscribe;
+ fn->stop_subscribe = nan_stop_subscribe;
+ fn->resolve_ip = nan_resolve_ip;
+ fn->cancel_resolve_ip = nan_cancel_resolve_ip;
+ fn->register_callbacks = nan_register_callbacks;
+ fn->process_event = nan_process_event;
+}