>

컴파일 : gcc -std=gnu11 -o "portknock" "portknock.c" -lpcap

이 작업을 학습 경험으로했으며 실제로 유용한 것으로 나타났습니다. 포트 노킹에 역 TTY를 생성하여 포트 노킹 방화벽 데몬으로 쉽게 만들 수 있습니다.

#include <stdio.h>
#include <stdlib.h>
#include <pcap.h>
#include <netinet/if_ether.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <time.h>
#include <string.h>
#include <linux/kd.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
struct knock {
        struct sockaddr* addr;
        int prog;
};
struct knock** np = NULL;
size_t nps = 0;
uint16_t kxs[] = { 5900, 80, 88, 82, 86 };
size_t kxsn = 5;
int main() {
    if (getuid() != 0) {
        printf("Port Knocking requires root!\n");
    }
    char errbuf[PCAP_ERRBUF_SIZE];
    char *dev = pcap_lookupdev(errbuf);
    if (dev == NULL) {
        printf("pcap_lookupdev %s\n", errbuf);
        exit(1);
    }
    pcap_t* descr = pcap_open_live(dev, 8192, 0, 100, errbuf);
    if (descr == NULL) {
        printf("pcap_open_live %s\n", errbuf);
        exit(1);
    }
    struct pcap_pkthdr hdr;
    struct pcap_pkthdr* hdrp = &hdr;
    const u_char* packet;
    int ret;
    while (1) {
        ret = pcap_next_ex(descr, &hdrp, &packet);
        if (ret < 0) {
            printf("pcap receive error, %s\n", pcap_geterr(descr));
            exit(1);
        } else if (ret == 0) {
            //printf("timeout!\n");
            continue;
        }
        const struct ether_header *eptr = (struct ether_header *) packet;
        uint16_t et = ntohs(eptr->ether_type);
        u_int size_ip;
        struct sockaddr* from;
        struct sockaddr_in ip4;
        struct sockaddr_in6 ip6;
        if (et == ETHERTYPE_IP) {
            const struct iphdr *ip = (struct iphdr*) (packet + ETHER_HDR_LEN);
            size_ip = ip->ihl * 4;
            if (size_ip < 20) {
                printf("bad ip header size: %i\n", size_ip);
                continue;
            }
            if (ip->protocol != 6) continue;
            from = (struct sockaddr*) &ip4;
            ip4.sin_family = AF_INET;
            ip4.sin_addr.s_addr = ip->saddr;
        } else if (et == ETHERTYPE_IPV6) {
            const struct ip6_hdr *ip = (struct ip6_hdr*) (packet + ETHER_HDR_LEN);
            size_ip = 40;
            uint8_t et = ip->ip6_ctlun.ip6_un1.ip6_un1_nxt;
            if (et == 0 || et == 60 || et == 43 || et == 44 || et == 51 || et == 50 || et == 60 || et == 135) {
                struct ip6_ext *ext = (struct ip6_ext*) (packet + ETHER_HDR_LEN + size_ip);
                size_ip += 8 + (ext->ip6e_len * 8);
                et = ext->ip6e_nxt;
                while (et == 0 || et == 60 || et == 43 || et == 44 || et == 51 || et == 50 || et == 60 || et == 135) {
                    size_ip += 8 + (ext->ip6e_len * 8);
                    ext = (struct ip6_ext*) (packet + ETHER_HDR_LEN + size_ip);
                }
                if (et == 59) continue;
            } else if (et == 59) continue;
            if (et != 6) continue;
            from = (struct sockaddr*) &ip6;
            ip6.sin6_family = AF_INET6;
            ip6.sin6_addr = ip->ip6_src;
            //printf("ipv6 size: %u\n", size_ip);
        } else continue;
        const struct tcphdr *tcp = (struct tcphdr*) (packet + ETHER_HDR_LEN + size_ip);
        u_int size_tcp = tcp->th_off * 4;
        if (size_tcp < 20) {
            printf("bad tcp header size: %i\n", size_tcp);
            continue;
        }
        //const u_char* data = (u_char *) (packet + ETHER_HDR_LEN + size_ip + size_tcp);
        if ((tcp->th_flags & TH_SYN) == TH_SYN) {
            //char* add;
            //char ip6a[64];
            //if (from->sa_family == AF_INET) {
            //  add = inet_ntoa(ip4.sin_addr);
            //} else if (from->sa_family == AF_INET6) {
            //  add = inet_ntop(AF_INET6, &ip6.sin6_addr, ip6a, 64);
            //}
            uint16_t dport = ntohs(tcp->th_dport);
            //uint16_t sport = ntohs(tcp->th_sport);
            //printf("%s sent a syn to port dest = %u, source = %u\n", add, dport, sport);
            struct knock* cn = NULL;
            for (int i = 0; i < nps; i++) {
                if (np[i]->addr->sa_family == AF_INET && from->sa_family == AF_INET) {
                    if (((struct sockaddr_in*) (np[i]->addr))->sin_addr.s_addr == ip4.sin_addr.s_addr) {
                        cn = np[i];
                    }
                }
            }
            int cp = cn == NULL ? 0 : cn->prog;
            if (dport == kxs[cp]) {
                if (++cp == kxsn) {
                    printf("knock complete!\n");
                    //do knock stuff, ie accept a connection from an IP, by interfacing with libiptc
                } else {
                    printf("knock progress = %i!\n", cp);
                    if (cn == NULL) {
                        cn = malloc(sizeof(struct knock));
                        if (cn == NULL) {
                            printf("out of memory!\n");
                            exit(1);
                        }
                        cn->prog = 1;
                        size_t s = from->sa_family == AF_INET ? sizeof(ip4) : sizeof(ip6);
                        cn->addr = malloc(s);
                        if (cn->addr == NULL) {
                            printf("out of memory!\n");
                            exit(1);
                        }
                        memcpy(cn->addr, from, s);
                        if (np == NULL) {
                            np = malloc(sizeof(struct knock*));
                            nps = 0;
                        } else {
                            np = realloc(np, sizeof(struct knock*) * (nps + 1));
                        }
                        if (np == NULL) {
                            printf("out of memory!\n");
                            exit(1);
                        }
                        np[nps++] = cn;
                    } else {
                        cn->prog++;
                    }
                }
            }
        }
    }
    exit(0);
    return EXIT_SUCCESS;
}

  • 답변 # 1

    마법의 숫자

    나는 그것이 kxsn 의 우연의 일치가 아니라고 생각한다  5이고 kxs  5 개의 요소가 있습니다. 관용구는

       size_t kxsn = sizeof(kxs)/sizeof(kxs[0]);
    
    

    kxs 의 값이 무엇인지 설명하는 것이 좋습니다 (명명 된 상수를 통해).  의미합니다. 동일하다

       et == 0 || et == 60 || et == 43 || et == 44 || et == 51 || et == 50 || et == 60 || et == 135
    
    

    (그 사람들은 누구입니까?).

    이름 지정

    정직하게 의미가 없습니다. 와이즈 비즈 np cn  독자에게 정보를 전달하지 마십시오.

    메모리는 회수되지 않습니다

    할당 된 메모리는 해제되지 않습니다. 데몬은 메모리가 부족한 것으로 알려져 있으며 그 전에는 전체 시스템을 크롤링합니다.

    cp 는 어떻습니까 ?

    AF_INET6  패킷은 단지 AF_INET6 를 방해  정렬. 그들의 주소는 절대 np 에서 찾을 수 없습니다 그래서 np  항상 새로 할당되어 cn 에 추가됩니다 . 올바르게 이해하면 연결 당 5 번

    기능

    @looserdroog가 언급했듯이 논리는 함수로 분리되어야합니다. 반복되는 코드의 문제가 아니라 코드 통합 및 책임 분리에 관한 문제입니다. 게다가 함수는 좋은 설명을 제공 할 수있는 것입니다.

    예를 들어, 메인 루프의 본문은 다음과 같습니다.

    np
    
    

  • 답변 # 2

    하나를 선택하십시오 :

       while (1) {
            get_packet(descr, &hdrp, &packet);
            struct sockaddr * from = parse_address(packet, &size_ip);
            if (from == NULL) continue;
            struct tcphdr * tcp = get_tcp_header(packet, size_ip);
            if (tcp == NULL || !is_syn(tcp)) continue;
            uint16_t dport = tcp->th_dport;
            struct knock * knock = find_knock(known_knocks, from);
            if (knock == NULL) {
                knock = allocate_knock(from);
                add_knock(knock, known_knocks);
            }
            if (dport == kxs[knock->progress]) {
                knock->progress++;
                if (knock_completed(knock)) {
                    do_stuff(knock);
                }
            }
        }
    
    

    그 외에도 소켓 프로그래밍에 대해 잘 모르므로 거기서 무엇을하고 있는지 비판 할 수 없습니다. 그러나 논리를 여러 개의 작은 기능으로 분리하는 것은 거의 확실히 개선 된 것입니다. 한 화면에서 전체 기능을 볼 수있는 것이 매우 유리합니다. 단일 단위로보다 쉽게 ​​이해할 수 있습니다. 부수적 인 이점으로, 필요한 들여 쓰기 수준을 자연스럽게 줄입니다.

    와이즈 비즈  기능은

    명령 줄 인수 처리

    호출 초기화 기능

    "workhorse"기능 호출

    이제 여기에 "작업장"에 여전히 대부분의 코드가 포함되어있을 수 있으며, 그럴 필요가있을 수도 있습니다. 그러나 C의 주요 추상화 수단은함수 (및 매크로)이므로이를 사용하십시오 (그러나 매크로에 열중하지는 마십시오).

    한 번 나에게 인용 조언,

    와이즈 비즈 이 조언은 물론 재귀 적입니다. 따라서 그것은 교육 학적 목적에 대한 끔찍한 정의이지만, 일단 "만들었다"면,이 조언은 IMO를 훌륭하게 요약합니다. 기능적 추상화는 언어 자체와 아무 관련이 없기 때문에 프로그래밍 언어에서 가장 어려운 측면 중 하나이지만 개념 도구는 상황에 영향을 미칩니다. 작은 기능을 작성하면 해결하려는 문제를 더 잘 개념화 할 수 있습니다. 그리고보다 명확한 개념화는 강력한 방식으로 결합 된 간단한원자구성을 통해 자연스럽게 표현됩니다.

    이것은 유닉스/C 철학의 일부로서 전체 프로그램조차도 "단일 기능"처럼 동작해야하므로 쉘 프로그래밍을 통해 다른 프로그램과 구성 될 수 있습니다. 내부적으로도 exit(0); return EXIT_SUCCESS; 와 같은 이름의 연산 (함수)이 있어야합니다 main

    Try and remove chunks into separate functions that

      have a single entrance and exit

      perform a single function well

    pass parameters, don't use global data Nick Keighley

    . 영감을 얻으려면 기능적 추상화를 기괴한 극단으로 가져 오는 "Literate Programming"을 살펴보십시오. (아이디어는 종종 더 극단적 인 증상을 파악하기가 더 쉽습니다.)

    open_connection

  • 이전 c++ - 비트 조작 도구 세트
  • 다음 performance - 두 정수의 공약수를 반환