链表 IPC C 语言

Posted

技术标签:

【中文标题】链表 IPC C 语言【英文标题】:Linked List IPC C Language 【发布时间】:2018-11-26 20:39:04 【问题描述】:

我正在使用守护进程提供简单的服务来嗅探来自任何 ip 的互联网数据包。我面临的问题是,我不知道如何将 IP 的链接列表及其数据包计数从守护进程进程(我将它们存储在链接列表中)存储到另一个进程(cli)。我上网查找信息,发现我应该使用 IPC,如共享内存、管道/fifo、配对套接字等。但我不知道如何使用其中任何一个来将完整的链接列表发送到 CLI。你能告诉我,我应该使用哪种 IPC 案例来完成我的任务?以及如何通过任何方式传输 LINKED LIST。

重点 - 是制作可以与我的守护进程交互的 cli。

链表结构:

typedef struct s_ip

        uint64_t address;
        size_t packets;
        struct s_ip *next;
               t_ip;    

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

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

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

守护进程

/* 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 的所有数据包的信息时,我必须将它们发送给他。

谢谢。

【问题讨论】:

您必须使用循环一次发送整个列表一项;在此之前,您应该发送要发送的项目数,或者让 cli 继续阅读,直到出现某种标记。 使用不涉及指针的数据结构,或消息队列或数据报 Unix 域套接字,一次发送一项。 欢迎来到Stack Overflow。请注意,要求我们填写,本质上是一个空模板不属于minimal reproducible example 指导方针。我建议你先解决这个问题(例如,发送单个项目的代码在哪里?) 这其实很简单。只需使用fprintf(stream, "%" PRIu64 " %zu\n", it->address, it->packets)" 发送和接收方fcntl(fileno(stream), O_NONBLOCK); while (poll(fileno(STREAM), POLLIN, ....)) new = malloc(...); fscanf(f, "%" PRIu64 " %zu\n", &new->address, &new->packets); last->next = new; last=new; 注意ASLR。这是相关的。另请仔细阅读shm_overview(7) 【参考方案1】:

不要转移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);
    

一方面只是使用ip_list_send 中的简单fprintf 调用来序列化列表。首先它发送 ASCII 字符 '\x02',它被称为 START OF TEXT 并带有换行符。然后它将写入的元素计数用换行符写成 ASCII 字符。然后为每个元素添加一个换行符。最后 '\x03' 被转移,即。 END OF TEXT 带有换行符。

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

【讨论】:

非常好的答案,但不确定您是否需要为“安全代码”一次读取单个字符。

以上是关于链表 IPC C 语言的主要内容,如果未能解决你的问题,请参考以下文章

C 语言链表问题

c语言 链表问题~~~

C语言链表问题

双向链表排序c语言程序设计

双向链表排序c语言程序设计

C语言链表的使用