链表IPC C语言

时间:2018-11-26 20:39:04

标签: c linux linked-list ipc daemon

我通过守护程序提供了简单的服务,以嗅探任何IP的互联网数据包。我面临的问题是,我不知道如何将IP的链接列表及其数据包计数存储到守护进程中的另一个proc(cli)中(我将它们存储在链接列表中)。我上网浏览信息,发现我应该使用IPC(如共享内存,管道/ FIFO,配对套接字等)。但是我不知道如何使用其中的任何功能将完整链接列表发送到CLI。您能告诉我我应该使用哪种IPC来完成任务吗?以及如何通过任何方式准确地传输链接列表。

要点-make cli可以与我的守护程序进行交互。

链接列表的结构:

typedef struct s_ip
{
        uint64_t address;
        size_t packets;
        struct s_ip *next;
}               t_ip;    

我只能通过共享内存存储像char *这样的单个变量,而不能像链接列表或结构的malloced数组那样存储其他变量

我还应该使用结构数组而不是链表将数据传输到另一个过程吗?

如果可能,给我这样的例子:

DAEMON

/* Daemon code side */
void sendlist_daemon(t_ip *ip_list)
{
    /* code that store linked list */
}

CLI

/* CLI code side */
t_ip *getlist_cli(void)
{
    t_ip *ip_list;

    ip_list = /* here i can get list */
    return (ip_list);
}

注意:守护进程始终在侦听数据包,因此我决定创建链接列表,并仅将元素与传入数据包一起推送。但是有时,当来自cli的用户从所有ip请求有关所有数据包的信息时,我必须将其发送给他。

谢谢。

1 个答案:

答案 0 :(得分:3)

请勿转移Next地址。没关系仅传输所需的信息。您需要建立将使用的数据格式。您将使用哪种耐力?首先是什么位,首先是MSB还是LSB?您将使用什么字符集?二进制流还是可读文本?用特殊字符分隔还是不分隔?压缩?加密?哪个压缩?哪种加密?最后,数据如何格式化?如何处理错误?最后,api的外观如何?是否应该使用FILE指针,文件描述符号,平台相关的输入/输出句柄或函数指针?这些是工程师在设计系统时要回答的问题。

最好是保持尽可能的便携性(size_t不太便携,但我还是保留了)。

#include <stdio.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <stddef.h>
#include <inttypes.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include <assert.h>

typedef struct s_ip
{
        uint64_t address;
        size_t packets;
        struct s_ip *next;
} ip_t;

#define IP_LIST_INIT() {0}

void ip_list_elem_init(ip_t *elem, uint64_t address, size_t packets)
{
    elem->address = address;
    elem->packets = packets;
    elem->next = NULL;
}

int ip_list_add(ip_t **head, uint64_t address, size_t packets)
{
    if (*head == NULL) {
        *head = malloc(sizeof(**head));
        if (*head == NULL) return -__LINE__;
        ip_list_elem_init(*head, address, packets);
    } else {
        ip_t *i;
        for (i = *head; i->next != NULL; i = i->next) {
            continue;
        }
        i->next = malloc(sizeof(*i->next));
        if (i->next == NULL) return -__LINE__;
        ip_list_elem_init(i->next, address, packets);
    }
    return 0;
}

void ip_list_free(ip_t *head)
{
    // use system deallocator.... :)
    return;
}

int ip_list_send(ip_t *head, FILE *f)
{
    char start_of_text = '\x02'; // STX START_OF_TEXT ascii character
    char end_of_text = '\x03'; // ETX END_OF_TEXT ascii character

    if (fprintf(f, "%c\n", start_of_text) < 0) return -__LINE__;

    size_t tmp = 0;
    for (ip_t *i = head; i != NULL; i = i->next) {
        tmp++;
    }
    if (fprintf(f, "%zu\n", tmp) < 0) return -__LINE__;

    for (ip_t *i = head; i != NULL; i = i->next) {
        if (fprintf(f, "%" PRIu64 " %zu\n", i->address, i->packets) < 0) return -__LINE__;
    }

    if (fprintf(f, "%c\n", end_of_text) < 0) return -__LINE__;

    return 0;
}

int ip_list_recv(ip_t **head, FILE *f)
{
    if (fcntl(fileno(f), F_SETFL, O_NONBLOCK) < 0) return -__LINE__;

    enum {
        START_TEXT,
        READING_COUNT,
        READING_ELEMS,
        STOP_TEXT,
        END,
    } state = START_TEXT;

    size_t cnt = 0;
    ip_t *prev = NULL;

    while (state != END) {
        struct pollfd pfd = { .fd = fileno(f), .events = POLLIN };
        int pollret = poll(&pfd, 1, 100);
        if (pollret < 0) return -__LINE__;
        if (pollret == 0) break;
        if (pfd.revents != POLLIN) return -__LINE__;

        switch (state) {
        case START_TEXT: {
            char c;
            if (fscanf(f, "%c\n", &c) != 1) return -__LINE__; // start of transmission
            if (c != '\x02') return -__LINE__;
            state = READING_COUNT;
            break;
        }
        case READING_COUNT: {
            if (fscanf(f, "%zu\n", &cnt) != 1) return -__LINE__;
            state = READING_ELEMS;
            break;
        }
        case READING_ELEMS: {
            ip_t *next = malloc(sizeof(*next));
            if (next == NULL) return -__LINE__;
            if (fscanf(f, "%" SCNu64 " %zu\n", &next->address, &next->packets) != 2) return -__LINE__;
            ip_list_elem_init(next, next->address, next->packets);
            if (prev) {
                prev->next = next;
            } else {
                *head = next;
            }
            prev = next;
            cnt--;
            if (cnt == 0) {
                state = STOP_TEXT;
            }
            break;
        }
        case STOP_TEXT: {
            char c;
            if (fscanf(f, "%c\n", &c) != 1) return -__LINE__;
            if (c != '\x03') return -__LINE__; // end of transmission
            state = END;
            break;
        }
        default:
            assert(0);
            break;
        }
    }
    return 0;
}

void ip_list_print(ip_t *head)
{
    for (ip_t *i = head; i != NULL; i = i->next) {
        fprintf(stdout, "%p %" PRIu64 " %zu\n", (void*)i, i->address, i->packets);
    }
    fprintf(stdout, "\n");
}

int main() 
{
    int ret;


    FILE *f = tmpfile();
    if (!f) return -__LINE__;

    {
        printf("Sending side:\n");
        ip_t *head = IP_LIST_INIT();
        if (ip_list_add(&head, 1, 1)) return -__LINE__;
        if (ip_list_add(&head, 2, 2)) return -__LINE__;
        if (ip_list_add(&head, 3, 3)) return -__LINE__;
        ip_list_print(head);
        if ((ret = ip_list_send(head, f))) return ret;
        ip_list_free(head);
    }

    rewind(f);

    {
        printf("Receiving side:\n");
        ip_t *head = IP_LIST_INIT();
        if ((ret = ip_list_recv(&head, f))) return -ret;
        ip_list_print(head);
        ip_list_free(head);
    }
}

一侧仅使用fprintf中的简单ip_list_send调用来序列化列表。首先,它发送带有换行符的ASCII字符'\ x02',称为START OF TEXT。然后,它将使用换行符以ASCII字符写入的元素计数。然后为每个elem元素添加换行符。最后,'\ x03'被传送。 END OF TEXT和换行符。

ip_list_recv反序列化数据。它使用一个简单的状态机来记住其所处的状态,跟踪计数并分配内存,并使用fscanf读取数据。该代码可能包含大量错误,恶意攻击者可以使用它。这段代码中的poll调用几乎没有用,只有在换行符之后才被调用,这是良好代码的种子。好的代码应该在每个读取的字符之后调用poll,然后fgetc或更佳的read(..., 1)一次读取一个字符,然后将其添加到缓冲区,然后将所有对fscanf的呼叫可能是sscanf(line, ...)。可能还可以为函数实现一个全局/函数作用域参数指定的超时,这很好。当fdopen(fd, ...)需要文件指针时,也可以重写函数以使用文件描述符,并将fclose()fprintf一起使用。