doubao_chat/lib/net/turing/turing_airkiss.c

374 lines
10 KiB
C

/*
* airkiss package format
* |-------------------------Header-----------------------|
* | 4 bytes | 4 bytes | 4 bytes | 4 bytes |
* | magic number | version | package length | command id |
* |-------------------------Body-------------------------|
*/
#include "generic/typedef.h"
#include "os/os_api.h"
#include "lwip/sockets.h"
#include "lwip/netdb.h"
#include "json_c/json.h"
static struct {
char *uuid;
char *device_type;
char *device_id_prefix;
} turing_airkiss_para = {0};
#define __this (&turing_airkiss_para)
static const int AIRKISS_DEFAULT_LAN_PORT = 12476;
static const unsigned char AIRKISS_MAGIC_NUMBER[4] = { 0xfd, 0x01, 0xfe, 0xfc};
static const unsigned char AIRKISS_VERSION[4] = { 0x00, 0x20, 0x00, 0x02};
static const unsigned char AIRKISS_SEND_VERSION[4] = { 0x00, 0x10, 0x00, 0x01};
static volatile u8 s_stop_send = false;
static int turing_bind_task_pid;
typedef enum {
/* error message */
TURING_AIRKISS_ERR = -1,
/* correct message, but do not need to handle */
TURING_AIRKISS_CONTINUE = 0,
/* received discovery request */
TURING_AIRKISS_SSDP_REQ = 1,
/* message package ready */
TURING_AIRKISS_PAKE_READY = 2
} turing_airkiss_ret_t;
typedef enum {
TURING_AIRKISS_SSDP_REQ_CMD = 0x1,
TURING_AIRKISS_SSDP_RESP_CMD = 0x1001,
TURING_AIRKISS_SSDP_NOTIFY_CMD = 0x1002
} airkiss_lan_cmdid_t;
static int turing_get_socket_errno(int fd)
{
int sock_errno = 0;
uint32_t optlen = sizeof(sock_errno);
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &sock_errno, &optlen) < 0) {
log_e("getsockopt failed");
}
return sock_errno;
}
static turing_airkiss_ret_t turing_airkiss_lan_recv(const unsigned char *msg, uint16_t len)
{
// msg must contain at least 16 bytes
if (msg == NULL || len < 16) {
log_i("turing_airkiss_lan_recv error msg");
return TURING_AIRKISS_ERR;
}
if (memcmp(msg, AIRKISS_MAGIC_NUMBER, sizeof(AIRKISS_MAGIC_NUMBER)) != 0) {
log_i("not airkiss packet");
return TURING_AIRKISS_ERR;
}
msg += sizeof(AIRKISS_MAGIC_NUMBER);
// skip version code
log_d("version code:0x%x%x%x%x", msg[0], msg[1], msg[2], msg[3]);
msg += 4;
// skip msg length
msg += 4;
uint32_t cmd = msg[0] << 24 | msg[1] << 16 | msg[2] << 8 | msg[3];
log_d("airkiss cmd: 0x%x", cmd);
if (cmd == TURING_AIRKISS_SSDP_REQ_CMD) {
return TURING_AIRKISS_SSDP_REQ;
}
return TURING_AIRKISS_CONTINUE;
}
static int turing_airkiss_pack_device_info(uint32_t cmdid, unsigned char **outbuf, uint16_t *len)
{
struct json_object *msg = NULL;
struct json_object *dev_info = NULL;
unsigned char *buf = NULL;
const char *body = NULL;
char *device_id = NULL;
int id_len = 0;
int body_len = 0;
int ret = -1;
if (outbuf == NULL || *outbuf != NULL || len == NULL) {
log_e("invalid arguments");
return -1;
}
do {
msg = json_object_new_object();
if (msg == NULL) {
log_e("create msg failed");
break;
}
dev_info = json_object_new_object();
if (dev_info == NULL) {
log_e("create dev_info failed");
break;
}
json_object_object_add(msg, "deviceInfo", dev_info);
if (__this->uuid == NULL || __this->uuid[0] == 0 ||
__this->device_id_prefix == NULL || __this->device_id_prefix[0] == 0 ||
__this->device_type == NULL || __this->device_type[0] == 0) {
log_e("invalid device_type or device_id_prefix or uuid");
break;
}
id_len = strlen(__this->device_id_prefix) + strlen(__this->uuid) + 1 + 1;
device_id = (char *)malloc(id_len);
if (device_id == NULL) {
log_e("no free memory");
break;
}
snprintf(device_id, id_len, "%s_%s", __this->device_id_prefix, __this->uuid);
json_object_object_add(dev_info, "deviceType", json_object_new_string(__this->device_type));
json_object_object_add(dev_info, "deviceId", json_object_new_string(device_id));
free(device_id);
device_id = NULL;
body = json_object_to_json_string(msg);
if (body == NULL) {
log_e("get string of msg failed");
break;
}
log_d("body = %s", body);
body_len = strlen(body);
// the length of header is 16 bytes
*len = body_len + 16;
buf = (unsigned char *)malloc(*len);
if (buf == NULL) {
log_e("No free memory");
break;
}
*outbuf = buf;
// magic number
memcpy(buf, AIRKISS_MAGIC_NUMBER, sizeof(AIRKISS_MAGIC_NUMBER));
buf += 4;
// version code
memcpy(buf, AIRKISS_SEND_VERSION, sizeof(AIRKISS_SEND_VERSION));
buf += 4;
// the length of message
buf[0] = buf[1] = 0;
buf[2] = (unsigned char)((*len) >> 8);
buf[3] = (unsigned char)(*len);
buf += 4;
// command id
buf[0] = (unsigned char)(cmdid >> 24);
buf[1] = (unsigned char)(cmdid >> 16);
buf[2] = (unsigned char)(cmdid >> 8);
buf[3] = (unsigned char)cmdid;
buf += 4;
memcpy(buf, body, body_len);
ret = 0;
} while (0);
if (msg) {
json_object_put(msg);
}
return ret;
}
static void turing_bind_device(void *priv)
{
struct sockaddr_in local_addr;
struct sockaddr_in remote_addr;
socklen_t addr_len = sizeof(remote_addr);
const uint16_t BUF_LEN = 200;
uint16_t resp_len = 0;
uint16_t recv_len = 0;
unsigned char *buf = NULL;
unsigned char *resp_buf = NULL;
int ret = 0;
int err = 0;
int fd = -1;
int optval = 1;
volatile int lifecycle = (int)priv;
buf = (unsigned char *)malloc(BUF_LEN);
if (buf == NULL) {
log_e("buf allocate fail");
os_time_dly(100); //此处要加延时是因为避免杀线程比创建线程跑得还要快,导致任务句柄释放了还在使用
return;
}
do {
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); //IPPROTO_UDP
if (fd < 0) {
log_e("failed to create socket!");
break;
}
ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (void *)&optval, sizeof(optval));
if (ret < 0) {
log_e("setsockopt failed");
break;
}
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
local_addr.sin_port = htons(AIRKISS_DEFAULT_LAN_PORT);
ret = bind(fd, (const struct sockaddr *)&local_addr, sizeof(local_addr));
if (ret < 0) {
err = turing_get_socket_errno(fd);
log_e("airkiss bind local port ERROR! errno %d", err);
break;
}
ret = turing_airkiss_pack_device_info(TURING_AIRKISS_SSDP_RESP_CMD, &resp_buf, &resp_len);
if (ret < 0) {
log_e("Pack device info packet error!");
break;
}
while (!s_stop_send && lifecycle) {
struct timeval tv;
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
tv.tv_sec = 1;
tv.tv_usec = 0;
lifecycle--;
ret = select(fd + 1, &rfds, NULL, NULL, &tv);
if (ret > 0) {
recv_len = recvfrom(fd, buf, BUF_LEN, 0, (struct sockaddr *)&remote_addr,
(socklen_t *)&addr_len);
if (recv_len <= 0) {
continue;
}
turing_airkiss_ret_t lan_ret = turing_airkiss_lan_recv(buf, recv_len);
switch (lan_ret) {
case TURING_AIRKISS_SSDP_REQ:
log_d("TURING_AIRKISS_SSDP_REQ");
remote_addr.sin_port = htons(AIRKISS_DEFAULT_LAN_PORT);
ret = sendto(fd, resp_buf, resp_len, 0,
(struct sockaddr *)&remote_addr, addr_len);
if (ret < 0) {
err = turing_get_socket_errno(fd);
if (err != ENOMEM && err != EAGAIN) {
log_e("send notify msg ERROR! errno %d", err);
s_stop_send = true;
}
} else {
log_d("send notify msg OK!");
}
break;
default:
log_v("Pack is not ssdp req!");
break;
}
} else {
/* log_v("recv nothing!"); */
}
}
} while (0);
if (fd > -1) {
close(fd);
fd = -1;
}
if (buf) {
free(buf);
buf = NULL;
}
if (resp_buf) {
free(resp_buf);
resp_buf = NULL;
}
}
int turing_start_bind_device(int lifecycle)
{
if (lifecycle < 60) {
log_e("the lifecycle is too short");
return -1;
}
if (turing_bind_task_pid) {
log_e("a task has been started!");
return -1;
}
s_stop_send = false;
return thread_fork("turing_bind_device_task", 20, 768, 0, &turing_bind_task_pid, turing_bind_device, (void *)lifecycle);
}
void turing_stop_bind_device_task(void)
{
if (!turing_bind_task_pid) {
return;
}
s_stop_send = true;
thread_kill(&turing_bind_task_pid, KILL_WAIT);
turing_bind_task_pid = 0;
}
int turing_set_airkiss_para(const char *device_type, const char *device_id_prefix, const char *uuid)
{
if (!device_id_prefix || !device_type || !uuid) {
return -1;
}
if (__this->device_type) {
free(__this->device_type);
__this->device_type = NULL;
}
if (__this->device_id_prefix) {
free(__this->device_id_prefix);
__this->device_id_prefix = NULL;
}
if (__this->uuid) {
free(__this->uuid);
__this->uuid = NULL;
}
__this->device_type = malloc(strlen(device_type) + 1);
if (__this->device_type == NULL) {
return -1;
}
strcpy(__this->device_type, device_type);
__this->device_id_prefix = malloc(strlen(device_id_prefix) + 1);
if (__this->device_id_prefix == NULL) {
return -1;
}
strcpy(__this->device_id_prefix, device_id_prefix);
__this->uuid = malloc(strlen(uuid) + 1);
if (__this->uuid == NULL) {
return -1;
}
strcpy(__this->uuid, uuid);
return 0;
}