From 8fa517016e1a12f8b7deaa15b82105ed441097b0 Mon Sep 17 00:00:00 2001 From: sakumisu <1203593632@qq.com> Date: Thu, 27 Jun 2024 22:36:48 +0800 Subject: [PATCH] feat: add dhcp-server and dns-server module --- third_party/dhcp-server/dhserver.c | 338 +++++++++++++++++++++++++++++ third_party/dhcp-server/dhserver.h | 61 ++++++ third_party/dns-server/dnserver.c | 198 +++++++++++++++++ third_party/dns-server/dnserver.h | 47 ++++ 4 files changed, 644 insertions(+) create mode 100644 third_party/dhcp-server/dhserver.c create mode 100644 third_party/dhcp-server/dhserver.h create mode 100644 third_party/dns-server/dnserver.c create mode 100644 third_party/dns-server/dnserver.h diff --git a/third_party/dhcp-server/dhserver.c b/third_party/dhcp-server/dhserver.c new file mode 100644 index 0000000..09e9d74 --- /dev/null +++ b/third_party/dhcp-server/dhserver.c @@ -0,0 +1,338 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 by Sergey Fetisov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "dhserver.h" + +/* DHCP message type */ +#define DHCP_DISCOVER 1 +#define DHCP_OFFER 2 +#define DHCP_REQUEST 3 +#define DHCP_DECLINE 4 +#define DHCP_ACK 5 +#define DHCP_NAK 6 +#define DHCP_RELEASE 7 +#define DHCP_INFORM 8 + +/* DHCP options */ +enum DHCP_OPTIONS { + DHCP_PAD = 0, + DHCP_SUBNETMASK = 1, + DHCP_ROUTER = 3, + DHCP_DNSSERVER = 6, + DHCP_HOSTNAME = 12, + DHCP_DNSDOMAIN = 15, + DHCP_MTU = 26, + DHCP_BROADCAST = 28, + DHCP_PERFORMROUTERDISC = 31, + DHCP_STATICROUTE = 33, + DHCP_NISDOMAIN = 40, + DHCP_NISSERVER = 41, + DHCP_NTPSERVER = 42, + DHCP_VENDOR = 43, + DHCP_IPADDRESS = 50, + DHCP_LEASETIME = 51, + DHCP_OPTIONSOVERLOADED = 52, + DHCP_MESSAGETYPE = 53, + DHCP_SERVERID = 54, + DHCP_PARAMETERREQUESTLIST = 55, + DHCP_MESSAGE = 56, + DHCP_MAXMESSAGESIZE = 57, + DHCP_RENEWALTIME = 58, + DHCP_REBINDTIME = 59, + DHCP_CLASSID = 60, + DHCP_CLIENTID = 61, + DHCP_USERCLASS = 77, /* RFC 3004 */ + DHCP_FQDN = 81, + DHCP_DNSSEARCH = 119, /* RFC 3397 */ + DHCP_CSR = 121, /* RFC 3442 */ + DHCP_MSCSR = 249, /* MS code for RFC 3442 */ + DHCP_END = 255 +}; + +typedef struct { + uint8_t dp_op; /* packet opcode type */ + uint8_t dp_htype; /* hardware addr type */ + uint8_t dp_hlen; /* hardware addr length */ + uint8_t dp_hops; /* gateway hops */ + uint32_t dp_xid; /* transaction ID */ + uint16_t dp_secs; /* seconds since boot began */ + uint16_t dp_flags; + uint8_t dp_ciaddr[4]; /* client IP address */ + uint8_t dp_yiaddr[4]; /* 'your' IP address */ + uint8_t dp_siaddr[4]; /* server IP address */ + uint8_t dp_giaddr[4]; /* gateway IP address */ + uint8_t dp_chaddr[16]; /* client hardware address */ + uint8_t dp_legacy[192]; + uint8_t dp_magic[4]; + uint8_t dp_options[275]; /* options area */ +} DHCP_TYPE; + +DHCP_TYPE dhcp_data; +static struct udp_pcb *pcb = NULL; +static dhcp_config_t *config = NULL; + +char magic_cookie[] = { 0x63, 0x82, 0x53, 0x63 }; + +static dhcp_entry_t *entry_by_ip(uint32_t ip) +{ + int i; + for (i = 0; i < config->num_entry; i++) + if (*(uint32_t *)config->entries[i].addr == ip) + return &config->entries[i]; + return NULL; +} + +static dhcp_entry_t *entry_by_mac(uint8_t *mac) +{ + int i; + for (i = 0; i < config->num_entry; i++) + if (memcmp(config->entries[i].mac, mac, 6) == 0) + return &config->entries[i]; + return NULL; +} + +static inline bool is_vacant(dhcp_entry_t *entry) +{ + return memcmp("\0\0\0\0\0", entry->mac, 6) == 0; +} + +static dhcp_entry_t *vacant_address(void) +{ + int i; + for (i = 0; i < config->num_entry; i++) + if (is_vacant(config->entries + i)) + return config->entries + i; + return NULL; +} + +static inline void free_entry(dhcp_entry_t *entry) +{ + memset(entry->mac, 0, 6); +} + +uint8_t *find_dhcp_option(uint8_t *attrs, int size, uint8_t attr) +{ + int i = 0; + while ((i + 1) < size) { + int next = i + attrs[i + 1] + 2; + if (next > size) + return NULL; + if (attrs[i] == attr) + return attrs + i; + i = next; + } + return NULL; +} + +int fill_options(void *dest, + uint8_t msg_type, + const char *domain, + uint32_t dns, + int lease_time, + uint32_t serverid, + uint32_t router, + uint32_t subnet) +{ + uint8_t *ptr = (uint8_t *)dest; + /* ACK message type */ + *ptr++ = 53; + *ptr++ = 1; + *ptr++ = msg_type; + + /* dhcp server identifier */ + *ptr++ = DHCP_SERVERID; + *ptr++ = 4; + *(uint32_t *)ptr = serverid; + ptr += 4; + + /* lease time */ + *ptr++ = DHCP_LEASETIME; + *ptr++ = 4; + *ptr++ = (lease_time >> 24) & 0xFF; + *ptr++ = (lease_time >> 16) & 0xFF; + *ptr++ = (lease_time >> 8) & 0xFF; + *ptr++ = (lease_time >> 0) & 0xFF; + + /* subnet mask */ + *ptr++ = DHCP_SUBNETMASK; + *ptr++ = 4; + *(uint32_t *)ptr = subnet; + ptr += 4; + + /* router */ + if (router != 0) { + *ptr++ = DHCP_ROUTER; + *ptr++ = 4; + *(uint32_t *)ptr = router; + ptr += 4; + } + + /* domain name */ + if (domain != NULL) { + int len = strlen(domain); + *ptr++ = DHCP_DNSDOMAIN; + *ptr++ = len; + memcpy(ptr, domain, len); + ptr += len; + } + + /* domain name server (DNS) */ + if (dns != 0) { + *ptr++ = DHCP_DNSSERVER; + *ptr++ = 4; + *(uint32_t *)ptr = dns; + ptr += 4; + } + + /* end */ + *ptr++ = DHCP_END; + return ptr - (uint8_t *)dest; +} + +static void udp_recv_proc(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) +{ + (void)arg; + (void)addr; + uint8_t *ptr; + dhcp_entry_t *entry; + struct pbuf *pp; + + uint32_t n = p->len; + if (n > sizeof(dhcp_data)) + n = sizeof(dhcp_data); + memcpy(&dhcp_data, p->payload, n); + switch (dhcp_data.dp_options[2]) { + case DHCP_DISCOVER: + entry = entry_by_mac(dhcp_data.dp_chaddr); + if (entry == NULL) + entry = vacant_address(); + if (entry == NULL) + break; + + dhcp_data.dp_op = 2; /* reply */ + dhcp_data.dp_secs = 0; + dhcp_data.dp_flags = 0; + *(uint32_t *)dhcp_data.dp_yiaddr = *(uint32_t *)entry->addr; + memcpy(dhcp_data.dp_magic, magic_cookie, 4); + + memset(dhcp_data.dp_options, 0, sizeof(dhcp_data.dp_options)); + + fill_options(dhcp_data.dp_options, + DHCP_OFFER, + config->domain, + *(uint32_t *)config->dns, + entry->lease, + *(uint32_t *)config->addr, + *(uint32_t *)config->addr, + *(uint32_t *)entry->subnet); + + pp = pbuf_alloc(PBUF_TRANSPORT, sizeof(dhcp_data), PBUF_POOL); + if (pp == NULL) + break; + memcpy(pp->payload, &dhcp_data, sizeof(dhcp_data)); + udp_sendto(upcb, pp, IP_ADDR_BROADCAST, port); + pbuf_free(pp); + break; + + case DHCP_REQUEST: + /* 1. find requested ipaddr in option list */ + ptr = find_dhcp_option(dhcp_data.dp_options, sizeof(dhcp_data.dp_options), DHCP_IPADDRESS); + if (ptr == NULL) + break; + if (ptr[1] != 4) + break; + ptr += 2; + + /* 2. does hw-address registered? */ + entry = entry_by_mac(dhcp_data.dp_chaddr); + if (entry != NULL) + free_entry(entry); + + /* 3. find requested ipaddr */ + entry = entry_by_ip(*(uint32_t *)ptr); + if (entry == NULL) + break; + if (!is_vacant(entry)) + break; + + /* 4. fill struct fields */ + memcpy(dhcp_data.dp_yiaddr, ptr, 4); + dhcp_data.dp_op = 2; /* reply */ + dhcp_data.dp_secs = 0; + dhcp_data.dp_flags = 0; + memcpy(dhcp_data.dp_magic, magic_cookie, 4); + + /* 5. fill options */ + memset(dhcp_data.dp_options, 0, sizeof(dhcp_data.dp_options)); + + fill_options(dhcp_data.dp_options, + DHCP_ACK, + config->domain, + *(uint32_t *)config->dns, + entry->lease, + *(uint32_t *)config->addr, + *(uint32_t *)config->addr, + *(uint32_t *)entry->subnet); + + /* 6. send ACK */ + pp = pbuf_alloc(PBUF_TRANSPORT, sizeof(dhcp_data), PBUF_POOL); + if (pp == NULL) + break; + memcpy(entry->mac, dhcp_data.dp_chaddr, 6); + memcpy(pp->payload, &dhcp_data, sizeof(dhcp_data)); + udp_sendto(upcb, pp, IP_ADDR_BROADCAST, port); + pbuf_free(pp); + break; + + default: + break; + } + pbuf_free(p); +} + +err_t dhserv_init(dhcp_config_t *c) +{ + err_t err; + udp_init(); + dhserv_free(); + pcb = udp_new(); + if (pcb == NULL) + return ERR_MEM; + err = udp_bind(pcb, IP_ADDR_ANY, c->port); + if (err != ERR_OK) { + dhserv_free(); + return err; + } + udp_recv(pcb, udp_recv_proc, NULL); + config = c; + return ERR_OK; +} + +void dhserv_free(void) +{ + if (pcb == NULL) + return; + udp_remove(pcb); + pcb = NULL; +} diff --git a/third_party/dhcp-server/dhserver.h b/third_party/dhcp-server/dhserver.h new file mode 100644 index 0000000..039f066 --- /dev/null +++ b/third_party/dhcp-server/dhserver.h @@ -0,0 +1,61 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 by Sergey Fetisov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * version: 1.0 demo (7.02.2015) + * brief: tiny dhcp ipv4 server using lwip (pcb) + * ref: https://lists.gnu.org/archive/html/lwip-users/2012-12/msg00016.html + */ + +#ifndef DHSERVER_H +#define DHSERVER_H + +#include +#include +#include +#include +#include "lwip/err.h" +#include "lwip/udp.h" +#include "netif/etharp.h" + +typedef struct dhcp_entry { + uint8_t mac[6]; + uint8_t addr[4]; + uint8_t subnet[4]; + uint32_t lease; +} dhcp_entry_t; + +typedef struct dhcp_config { + uint8_t addr[4]; + uint16_t port; + uint8_t dns[4]; + const char *domain; + int num_entry; + dhcp_entry_t *entries; +} dhcp_config_t; + +err_t dhserv_init(dhcp_config_t *config); +void dhserv_free(void); + +#endif /* DHSERVER_H */ diff --git a/third_party/dns-server/dnserver.c b/third_party/dns-server/dnserver.c new file mode 100644 index 0000000..c162064 --- /dev/null +++ b/third_party/dns-server/dnserver.c @@ -0,0 +1,198 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 by Sergey Fetisov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * version: 1.0 demo (7.02.2015) + * brief: tiny dns ipv4 server using lwip (pcb) + */ + +#include "dnserver.h" + +#define DNS_MAX_HOST_NAME_LEN 128 + +static struct udp_pcb *pcb; +dns_query_proc_t query_proc; + +#pragma pack(push, 1) +typedef struct { +#if BYTE_ORDER == LITTLE_ENDIAN + uint8_t rd : 1, /* Recursion Desired */ + tc : 1, /* Truncation Flag */ + aa : 1, /* Authoritative Answer Flag */ + opcode : 4, /* Operation code */ + qr : 1; /* Query/Response Flag */ + uint8_t rcode : 4, /* Response Code */ + z : 3, /* Zero */ + ra : 1; /* Recursion Available */ +#else + uint8_t qr : 1, /* Query/Response Flag */ + opcode : 4, /* Operation code */ + aa : 1, /* Authoritative Answer Flag */ + tc : 1, /* Truncation Flag */ + rd : 1; /* Recursion Desired */ + uint8_t ra : 1, /* Recursion Available */ + z : 3, /* Zero */ + rcode : 4; /* Response Code */ +#endif +} dns_header_flags_t; + +typedef struct { + uint16_t id; + dns_header_flags_t flags; + uint16_t n_record[4]; +} dns_header_t; + +typedef struct dns_answer { + uint16_t name; + uint16_t type; + uint16_t Class; + uint32_t ttl; + uint16_t len; + uint32_t addr; +} dns_answer_t; +#pragma pack(pop) + +typedef struct dns_query { + char name[DNS_MAX_HOST_NAME_LEN]; + uint16_t type; + uint16_t Class; +} dns_query_t; + +static int parse_next_query(void *data, int size, dns_query_t *query) +{ + int len; + int lables; + uint8_t *ptr; + + len = 0; + lables = 0; + ptr = (uint8_t *)data; + + while (true) { + uint8_t lable_len; + if (size <= 0) + return -1; + lable_len = *ptr++; + size--; + if (lable_len == 0) + break; + if (lables > 0) { + if (len == DNS_MAX_HOST_NAME_LEN) + return -2; + query->name[len++] = '.'; + } + if (lable_len > size) + return -1; + if (len + lable_len >= DNS_MAX_HOST_NAME_LEN) + return -2; + memcpy(&query->name[len], ptr, lable_len); + len += lable_len; + ptr += lable_len; + size -= lable_len; + lables++; + } + + if (size < 4) + return -1; + query->name[len] = 0; + query->type = *(uint16_t *)ptr; + ptr += 2; + query->Class = *(uint16_t *)ptr; + ptr += 2; + return ptr - (uint8_t *)data; +} + +static void udp_recv_proc(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) +{ + (void)arg; + int len; + dns_header_t *header; + static dns_query_t query; + struct pbuf *out; + ip_addr_t host_addr; + dns_answer_t *answer; + + if (p->len <= sizeof(dns_header_t)) + goto error; + header = (dns_header_t *)p->payload; + if (header->flags.qr != 0) + goto error; + if (ntohs(header->n_record[0]) != 1) + goto error; + + len = parse_next_query(header + 1, p->len - sizeof(dns_header_t), &query); + if (len < 0) + goto error; + if (!query_proc(query.name, &host_addr)) + goto error; + + len += sizeof(dns_header_t); + out = pbuf_alloc(PBUF_TRANSPORT, len + 16, PBUF_POOL); + if (out == NULL) + goto error; + + memcpy(out->payload, p->payload, len); + header = (dns_header_t *)out->payload; + header->flags.qr = 1; + header->n_record[1] = htons(1); + answer = (struct dns_answer *)((uint8_t *)out->payload + len); + answer->name = htons(0xC00C); + answer->type = htons(1); + answer->Class = htons(1); + answer->ttl = htonl(32); + answer->len = htons(4); + answer->addr = host_addr.addr; + + udp_sendto(upcb, out, addr, port); + pbuf_free(out); + +error: + pbuf_free(p); +} + +err_t dnserv_init(const ip_addr_t *bind, uint16_t port, dns_query_proc_t qp) +{ + err_t err; + udp_init(); + dnserv_free(); + pcb = udp_new(); + if (pcb == NULL) + return ERR_MEM; + err = udp_bind(pcb, bind, port); + if (err != ERR_OK) { + dnserv_free(); + return err; + } + udp_recv(pcb, udp_recv_proc, NULL); + query_proc = qp; + return ERR_OK; +} + +void dnserv_free(void) +{ + if (pcb == NULL) + return; + udp_remove(pcb); + pcb = NULL; +} diff --git a/third_party/dns-server/dnserver.h b/third_party/dns-server/dnserver.h new file mode 100644 index 0000000..bcaef41 --- /dev/null +++ b/third_party/dns-server/dnserver.h @@ -0,0 +1,47 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 by Sergey Fetisov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * version: 1.0 demo (7.02.2015) + * brief: tiny dns ipv4 server using lwip (pcb) + */ + +#ifndef DNSERVER +#define DNSERVER + +#include +#include +#include +#include +#include "lwip/def.h" +#include "lwip/err.h" +#include "lwip/udp.h" +#include "netif/etharp.h" + +typedef bool (*dns_query_proc_t)(const char *name, ip_addr_t *addr); + +err_t dnserv_init(const ip_addr_t *bind, uint16_t port, dns_query_proc_t query_proc); +void dnserv_free(void); + +#endif