static int s_get_metric_report_json()

in source/device_defender.c [285:559]


static int s_get_metric_report_json(
    struct aws_byte_buf *json_out,
    struct aws_iotdevice_defender_task *task,
    uint64_t report_id,
    const struct aws_iotdevice_metric_network_transfer *net_xfer,
    const struct aws_array_list *net_conns,
    size_t custom_metrics_len,
    const struct defender_custom_metric_data *custom_metrics_data) {
    int return_value = AWS_OP_ERR;
    const char *json_report = NULL;
    struct cJSON *root = cJSON_CreateObject();
    if (root == NULL) {
        goto cleanup;
    }
    struct cJSON *header = cJSON_CreateObject();
    if (header == NULL) {
        goto cleanup;
    }
    cJSON_AddItemToObject(root, "header", header);
    if (NULL == cJSON_AddNumberToObject(header, "report_id", (double)report_id)) {
        goto cleanup;
    }

    if (NULL == cJSON_AddStringToObject(header, "version", "1.0")) {
        goto cleanup;
    }

    struct cJSON *metrics = cJSON_CreateObject();
    if (metrics == NULL) {
        goto cleanup;
    }
    cJSON_AddItemToObject(root, "metrics", metrics);

    struct cJSON *listening_tcp_ports = cJSON_CreateObject();
    if (listening_tcp_ports == NULL) {
        goto cleanup;
    }
    cJSON_AddItemToObject(metrics, "listening_tcp_ports", listening_tcp_ports);

    struct cJSON *tcp_listen_ports = cJSON_CreateArray();
    if (tcp_listen_ports == NULL) {
        goto cleanup;
    }
    cJSON_AddItemToObject(listening_tcp_ports, "ports", tcp_listen_ports);

    struct cJSON *tcp_connections = cJSON_CreateObject();
    if (tcp_connections == NULL) {
        goto cleanup;
    }
    cJSON_AddItemToObject(metrics, "tcp_connections", tcp_connections);

    struct cJSON *established_tcp_conns = cJSON_CreateObject();
    if (established_tcp_conns == NULL) {
        goto cleanup;
    }
    cJSON_AddItemToObject(tcp_connections, "established_connections", established_tcp_conns);

    struct cJSON *est_connections = cJSON_CreateArray();
    if (est_connections == NULL) {
        goto cleanup;
    }
    cJSON_AddItemToObject(established_tcp_conns, "connections", est_connections);

    struct cJSON *listening_udp_ports = cJSON_CreateObject();
    if (listening_udp_ports == NULL) {
        goto cleanup;
    }
    cJSON_AddItemToObject(metrics, "listening_udp_ports", listening_udp_ports);

    struct cJSON *udp_ports = cJSON_CreateArray();
    if (udp_ports == NULL) {
        goto cleanup;
    }
    cJSON_AddItemToObject(listening_udp_ports, "ports", udp_ports);

    int total_listening_tcp_ports = 0;
    int total_established_tcp_conns = 0;
    int total_udp_listeners = 0;
    const size_t net_conn_sz = aws_array_list_length(net_conns);
    for (size_t tcp_index = 0; tcp_index < net_conn_sz; ++tcp_index) {
        struct aws_iotdevice_metric_net_connection *net_conn = NULL;
        aws_array_list_get_at_ptr(net_conns, (void **)&net_conn, tcp_index);
        if (net_conn->state == AWS_IDNCS_ESTABLISHED && net_conn->protocol == AWS_IDNP_TCP) {
            total_established_tcp_conns++;
            struct cJSON *conn = cJSON_CreateObject();
            if (conn == NULL) {
                goto cleanup;
            }
            cJSON_AddItemToArray(est_connections, conn);
            if (NULL == cJSON_AddStringToObject(conn, "local_interface", aws_string_c_str(net_conn->local_interface))) {
                goto cleanup;
            }
            if (NULL == cJSON_AddNumberToObject(conn, "local_port", net_conn->local_port)) {
                goto cleanup;
            }
            char remote_addr[22];
            snprintf(remote_addr, 22, "%s:%u", aws_string_c_str(net_conn->remote_address), net_conn->remote_port);
            if (NULL == cJSON_AddStringToObject(conn, "remote_addr", remote_addr)) {
                goto cleanup;
            }
        } else if (net_conn->state == AWS_IDNCS_LISTEN && net_conn->protocol == AWS_IDNP_TCP) {
            total_listening_tcp_ports++;
            struct cJSON *conn = cJSON_CreateObject();
            if (conn == NULL) {
                goto cleanup;
            }
            cJSON_AddItemToArray(tcp_listen_ports, conn);
            if (NULL == cJSON_AddStringToObject(conn, "interface", aws_string_c_str(net_conn->local_interface))) {
                goto cleanup;
            }
            if (NULL == cJSON_AddNumberToObject(conn, "port", net_conn->local_port)) {
                goto cleanup;
            }
        } else if (net_conn->state == AWS_IDNCS_LISTEN && net_conn->protocol == AWS_IDNP_UDP) {
            ++total_udp_listeners;
            struct cJSON *conn = cJSON_CreateObject();
            if (conn == NULL) {
                goto cleanup;
            }
            cJSON_AddItemToArray(udp_ports, conn);
            if (NULL == cJSON_AddStringToObject(conn, "interface", aws_string_c_str(net_conn->local_interface))) {
                goto cleanup;
            }
            if (NULL == cJSON_AddNumberToObject(conn, "port", net_conn->local_port)) {
                goto cleanup;
            }
        }
    }

    if (NULL == cJSON_AddNumberToObject(established_tcp_conns, "total", total_established_tcp_conns)) {
        goto cleanup;
    }
    if (NULL == cJSON_AddNumberToObject(listening_tcp_ports, "total", total_listening_tcp_ports)) {
        goto cleanup;
    }
    if (NULL == cJSON_AddNumberToObject(listening_udp_ports, "total", (double)total_udp_listeners)) {
        goto cleanup;
    }

    struct cJSON *network_stats = cJSON_CreateObject();
    if (network_stats == NULL) {
        goto cleanup;
    }
    cJSON_AddItemToObject(metrics, "network_stats", network_stats);

    if (NULL == cJSON_AddNumberToObject(network_stats, "bytes_in", net_xfer != NULL ? (double)net_xfer->bytes_in : 0)) {
        goto cleanup;
    }
    if (NULL ==
        cJSON_AddNumberToObject(network_stats, "bytes_out", net_xfer != NULL ? (double)net_xfer->bytes_out : 0)) {
        goto cleanup;
    }
    if (NULL ==
        cJSON_AddNumberToObject(network_stats, "packets_in", net_xfer != NULL ? (double)net_xfer->packets_in : 0)) {
        goto cleanup;
    }
    if (NULL ==
        cJSON_AddNumberToObject(network_stats, "packets_out", net_xfer != NULL ? (double)net_xfer->packets_out : 0)) {
        goto cleanup;
    }

    if (custom_metrics_len != 0) {
        struct cJSON *custom_metrics = cJSON_CreateObject();
        if (NULL == custom_metrics) {
            goto cleanup;
        }
        cJSON_AddItemToObject(root, "custom_metrics", custom_metrics);

        size_t list_size = 0;
        struct cJSON *array_item = NULL;
        struct cJSON *item = NULL;
        struct cJSON *json_list = NULL;
        struct cJSON *spurious_array_container = NULL;
        for (size_t metric_index = 0; metric_index < custom_metrics_len; ++metric_index) {
            if (custom_metrics_data[metric_index].callback_result != AWS_OP_SUCCESS) {
                /* if the collection of a metric failed, do not output it to the report */
                continue;
            }
            spurious_array_container = cJSON_CreateArray();
            if (NULL == spurious_array_container) {
                goto cleanup;
            }
            cJSON_AddItemToObject(
                custom_metrics,
                aws_string_c_str(custom_metrics_data[metric_index].metric->metric_name),
                spurious_array_container);

            item = cJSON_CreateObject();
            if (NULL == item) {
                goto cleanup;
            }
            cJSON_AddItemToArray(spurious_array_container, item);

            switch (custom_metrics_data[metric_index].metric->type) {
                case DD_METRIC_NUMBER:
                    cJSON_AddNumberToObject(item, "number", (double)custom_metrics_data[metric_index].data.number);
                    break;
                case DD_METRIC_NUMBER_LIST:
                    list_size = aws_array_list_length(&custom_metrics_data[metric_index].data.list);
                    json_list = cJSON_CreateArray();
                    if (NULL == json_list) {
                        goto cleanup;
                    }
                    cJSON_AddItemToObject(item, "number_list", json_list);
                    for (size_t num_index = 0; num_index < list_size; ++num_index) {
                        int64_t number = 0;
                        aws_array_list_get_at(&custom_metrics_data[metric_index].data.list, &number, num_index);
                        array_item = cJSON_CreateNumber((double)number);
                        cJSON_AddItemToArray(json_list, array_item);
                    }
                    break;
                case DD_METRIC_STRING_LIST:
                    list_size = aws_array_list_length(&custom_metrics_data[metric_index].data.list);
                    json_list = cJSON_CreateArray();
                    if (NULL == json_list) {
                        goto cleanup;
                    }
                    cJSON_AddItemToObject(item, "string_list", json_list);
                    for (size_t string_index = 0; string_index < list_size; ++string_index) {
                        struct aws_string *string_value = NULL;
                        aws_array_list_get_at(
                            &custom_metrics_data[metric_index].data.list, &string_value, string_index);
                        array_item = cJSON_CreateString(aws_string_c_str(string_value));
                        cJSON_AddItemToArray(json_list, array_item);
                    }
                    break;
                case DD_METRIC_IP_LIST:
                    list_size = aws_array_list_length(&custom_metrics_data[metric_index].data.list);
                    json_list = cJSON_CreateArray();
                    if (NULL == json_list) {
                        goto cleanup;
                    }
                    cJSON_AddItemToObject(item, "ip_list", json_list);
                    for (size_t ip_index = 0; ip_index < list_size; ++ip_index) {
                        struct aws_string *ip_value = NULL;
                        aws_array_list_get_at(&custom_metrics_data[metric_index].data.list, &ip_value, ip_index);
                        array_item = cJSON_CreateString(aws_string_c_str(ip_value));
                        cJSON_AddItemToArray(json_list, array_item);
                    }
                    break;
                case DD_METRIC_UNKNOWN:
                default:
                    AWS_LOGF_WARN(
                        AWS_LS_IOTDEVICE_DEFENDER_TASK,
                        "id=%p: Unknown custom metrics type found during report generation: %d, name %s",
                        (void *)task,
                        custom_metrics_data[metric_index].metric->type,
                        aws_string_c_str(custom_metrics_data[metric_index].metric->metric_name));
                    continue;
                    break;
            }
        }
    }

    json_report = cJSON_PrintUnformatted(root);
    struct aws_byte_cursor json_report_buf = {.len = strlen(json_report) + 1, .ptr = (uint8_t *)json_report};
    if (AWS_OP_SUCCESS != aws_byte_buf_init_copy_from_cursor(json_out, task->allocator, json_report_buf)) {
        s_invoke_failure_callback(&task->config, false, AWS_ERROR_IOTDEVICE_DEFENDER_REPORT_SERIALIZATION_FAILURE);
        return_value = AWS_OP_ERR;
    } else {
        return_value = AWS_OP_SUCCESS;
    }

cleanup:
    if (json_report) {
        cJSON_free((void *)json_report);
    }
    if (root) {
        cJSON_Delete(root);
    }
    if (return_value != AWS_OP_SUCCESS) {
        aws_raise_error(AWS_ERROR_IOTDEVICE_DEFENDER_REPORT_SERIALIZATION_FAILURE);
    }
    return return_value;
}