lwip-1.4.1 (partial)
Revision 1:119c4f7144c8, committed 2018-07-24
- Comitter:
- ua1arn
- Date:
- Tue Jul 24 17:36:01 2018 +0000
- Parent:
- 0:c2ca3c5ded62
- Commit message:
- lwip 1.4.1 with necessary servers
Changed in this revision
diff -r c2ca3c5ded62 -r 119c4f7144c8 dhcp-server/dhserver.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dhcp-server/dhserver.c Tue Jul 24 17:36:01 2018 +0000 @@ -0,0 +1,332 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 by Sergey Fetisov <fsenok@gmail.com> + * + * 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() +{ + int i; + for (i = 0; i < config->num_entry; i++) + if (is_vacant(config->entries + i)) + return config->entries + 1; + 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, struct ip_addr *addr, u16_t port) +{ + uint8_t *ptr; + dhcp_entry_t *entry; + struct pbuf *pp; + + int 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, + 0, + *(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, + 0, + *(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 -r c2ca3c5ded62 -r 119c4f7144c8 dhcp-server/dhserver.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dhcp-server/dhserver.h Tue Jul 24 17:36:01 2018 +0000 @@ -0,0 +1,63 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 by Sergey Fetisov <fsenok@gmail.com> + * + * 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 <stdint.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#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 -r c2ca3c5ded62 -r 119c4f7144c8 dns-server/dnserver.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dns-server/dnserver.c Tue Jul 24 17:36:01 2018 +0000 @@ -0,0 +1,200 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 by Sergey Fetisov <fsenok@gmail.com> + * + * 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 = NULL; +dns_query_proc_t query_proc = NULL; + +#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, struct ip_addr *addr, u16_t port) +{ + 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 refuse; + + 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); + pbuf_free(p); + return; + +refuse: + header->flags.qr = 1; + header->flags.rcode = 5; + udp_sendto(upcb, p, addr, port); + pbuf_free(p); + return; + +error: + pbuf_free(p); +} + +err_t dnserv_init(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() +{ + if (pcb == NULL) return; + udp_remove(pcb); + pcb = NULL; +}
diff -r c2ca3c5ded62 -r 119c4f7144c8 dns-server/dnserver.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dns-server/dnserver.h Tue Jul 24 17:36:01 2018 +0000 @@ -0,0 +1,47 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 by Sergey Fetisov <fsenok@gmail.com> + * + * 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 <stdint.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#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(ip_addr_t *bind, uint16_t port, dns_query_proc_t query_proc); +void dnserv_free(void); + +#endif
diff -r c2ca3c5ded62 -r 119c4f7144c8 http-server/cparser.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/http-server/cparser.c Tue Jul 24 17:36:01 2018 +0000 @@ -0,0 +1,519 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 by Sergey Fetisov <fsenok@gmail.com> + * + * 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) + */ + +#include "cparser.h" + +void parser_init(parser_t *p, const char *str, int len) +{ + p->buff = NULL; + p->buff_size = 0; + p->user_data = NULL; + p->read_cb = NULL; + p->total = 0; + p->line = 0; + p->col = 0; + p->priv.curr = str; + p->priv.stop = str + len; +} + +void parser_init_s(parser_t *p, char *stream_buff, size_t size, parser_read_t cb) +{ + p->buff = stream_buff; + p->buff_size = size; + p->user_data = NULL; + p->read_cb = cb; + p->total = 0; + p->line = 0; + p->col = 0; + p->priv.curr = NULL; + p->priv.stop = NULL; +} + +bool parser_provide_space(parser_t *p, int size) +{ + int allow; + allow = p->priv.stop - p->priv.curr; + if (allow >= size) return true; + if (p->buff == NULL || p->buff_size < size) return false; + memmove(p->buff, p->priv.stop - allow, allow); + size = p->read_cb(p, p->buff + allow, p->buff_size - allow); + p->priv.curr = p->buff; + p->priv.stop = p->buff + allow + size; + return true; +} + +bool parser_is_next(parser_t *p, const char *str, int len) +{ + if (!parser_provide_space(p, len)) return false; + return memcmp(p->priv.curr, str, len) == 0; +} + +static bool query_data(parser_t *p) +{ + size_t size = p->read_cb(p, p->buff, p->buff_size); + p->priv.curr = p->buff; + p->priv.stop = p->buff + size; + return size > 0; +} + +#define INC_POS \ + p->total++; \ + p->col++; \ + if (*p->priv.curr == '\n') \ + { \ + p->line++; \ + p->col = 0; \ + } \ + p->priv.curr++; + +#define CHECK_STATE() \ + eof = false; \ + if (p->priv.curr == p->priv.stop) \ + eof = p->buff == NULL ? true : !query_data(p); + +char parser_curr(parser_t *p) +{ + bool eof; + CHECK_STATE(); + return eof ? 0 : *p->priv.curr; +} + +bool parser_eof(parser_t *p) +{ + bool eof; + CHECK_STATE(); + return eof; +} + +int parser_read(parser_t *p, const chatset_t chatset, char *dest, int dest_size) +{ + bool eof; + register int c; + int res = 0; + while (true) + { + CHECK_STATE(); + if (eof) break; + c = (unsigned)*p->priv.curr; + if (!chatset[c]) break; + if (res < dest_size - 1) + dest[res] = c; + res++; + INC_POS; + } + if (res >= dest_size) + dest[dest_size - 1] = 0; else + dest[res] = 0; + return res; +} + +int parser_read_before2(parser_t *p, char *dest, int dest_size, char c1, char c2) +{ + bool eof; + register int cc; + int res = 0; + while (true) + { + CHECK_STATE(); + if (eof) break; + cc = *p->priv.curr; + if (cc == c1 || cc == c2) break; + if (res < dest_size) + dest[res] = cc; + res++; + INC_POS; + } + if (res >= dest_size) + dest[dest_size - 1] = 0; else + dest[res] = 0; + return res; +} + +int parser_read_digits(parser_t *p, char *dest, int dest_size, bool hex) +{ + bool eof; + register int c; + int res = 0; + while (true) + { + bool done; + CHECK_STATE(); + if (eof) break; + c = (unsigned)*p->priv.curr; + done = false; + done |= c >= '0' && c <= '9'; + done |= hex && (c >= 'a' && c <= 'f'); + done |= hex && (c >= 'A' && c <= 'F'); + if (!done) break; + if (res < dest_size - 1) + dest[res] = c; + res++; + INC_POS; + } + if (res >= dest_size) + dest[dest_size - 1] = 0; else + dest[res] = 0; + return res; +} + +int parser_read_value(parser_t *p, char *dest, int dest_size, int fmt) +{ +// state: 0 1 2 3 4 5 6 7 +// 1234567 +// 1234567 e 2 +// 1234567 e - 2 +// 345 . 1234 e + 4 +// 0 . 1234 e + 4 +// 0 x ABCD +// 0 h ABCD +// 0 b 0110 + + bool eof; + register int c; + int res; + int state; + char last; + + res = 0; + state = 0; + + while (true) + { + CHECK_STATE(); + if (eof) break; + c = *p->priv.curr; + + if (state == 0 || state == 2 || state == 5) + { + if (c >= '0' && c <= '9') + { + if (res < dest_size) dest[res] = c; + last = c; + INC_POS; + res++; + continue; + } + if (state == 0 && c == '.' && (fmt & VAL_FMT_FLOAT)) + { + if (res < dest_size) dest[res] = c; + INC_POS; + res++; + state = 2; + continue; + } + if ((state == 0 || state == 2) && (c == 'e' || c == 'E') && (fmt & VAL_FMT_FLOAT)) + { + if (res < dest_size) dest[res] = c; + INC_POS; + res++; + state = 4; + continue; + } + if (state == 0 && last == '0' && res == 1) + { + if (((fmt & VAL_FMT_0H) && (c == 'H' || c == 'h')) || ((c == 'x' || c == 'X') && (fmt & VAL_FMT_0X))) + { + if (res < dest_size) dest[res] = c; + INC_POS; + res++; + state = 6; + continue; + } + if ((fmt & VAL_FMT_0B) && (c == 'b' || c == 'B')) + { + if (res < dest_size) dest[res] = c; + INC_POS; + res++; + state = 7; + continue; + } + } + if ((fmt & VAL_FMT_SIGN) && res == 0 && (c == '+' || c == '-')) + { + if (res < dest_size) dest[res] = c; + INC_POS; + res++; + continue; + } + } + if (state == 4) + { + if (c == '+' || c == '-') + { + if (res < dest_size) dest[res] = c; + INC_POS; + res++; + } + state = 5; + continue; + } + if (state == 6) + { + if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) + { + if (res < dest_size) dest[res] = c; + INC_POS; + res++; + continue; + } + } + if (state == 7) + { + if (c == '0' || c == '1') + { + if (res < dest_size) dest[res] = c; + INC_POS; + res++; + continue; + } + } + break; + } + if (res >= dest_size) + dest[dest_size - 1] = 0; else + dest[res] = 0; + return res; +} + +#define is_ident_char(c) \ + (c == '_' || \ + (c >= '0' && c <= '9') || \ + (c >= 'a' && c <= 'z') || \ + (c >= 'a' && c <= 'z')) + +int parser_read_string(parser_t *p, char *dest, int dest_size, int fmt) +{ +// state: 0 1 2 3 4 5 +// string +// ' string ' +// " string " +// ' str \ ' +// ' str \ rntbf\/" ' +// ' str \ u ' +// ' str \ u ' +// ' str \ u 12AB ' + bool eof; + register int c; + int res; + int state; + char first; + + res = 0; + state = 0; + first = 0; + + while (true) + { + CHECK_STATE(); + if (eof) break; + c = *p->priv.curr; + + if (state == 0 && (((fmt & STR_FMT_QUOTE) && c == '"') || ((fmt & STR_FMT_APOST) && c == '\''))) + { + first = c; + state = 1; + INC_POS; + continue; + } + + if (state == 0 && (fmt & STR_FMT_NAKED)) + { + state = 1; + continue; + } + + if (state == 1 && c == first) + { + INC_POS; + break; + } + + if (state == 1 && first == 0 && is_ident_char(c)) // reading naked + { + if (res < dest_size) dest[res] = c; + INC_POS; + res++; + continue; + } + + if (state == 1 && (fmt & STR_FMT_ESC) && c == '\\') + { + state = 3; + INC_POS; + continue; + } + + if (state == 1 && first != 0) + { + if (res < dest_size) dest[res] = c; + INC_POS; + res++; + continue; + } + + if (state == 3 && (fmt & STR_FMT_ESCU) && c == 'u') + { + state = 4; + INC_POS; + continue; + } + + if (state == 3) + { + char e; + switch (c) + { + case 'r': e = '\r'; break; + case 'n': e = '\n'; break; + case 't': e = '\t'; break; + case 'b': e = '\b'; break; + case 'f': e = '\f'; break; + case '\\': e = '\\'; break; + case '/': e = '/'; break; + case '"': e = '"'; break; + default: + if (res < dest_size) dest[res] = '\\'; + res++; + e = c; + break; + } + state = 1; + if (res < dest_size) dest[res] = e; + INC_POS; + res++; + continue; + } + + if (state == 4) + { + } + + break; + } + if (res >= dest_size) + dest[dest_size - 1] = 0; else + dest[res] = 0; + return res; +} + +int parser_skip(parser_t *p, const chatset_t chatset) +{ + bool eof; + register int c; + int res = 0; + while (true) + { + CHECK_STATE(); + if (eof) break; + c = (unsigned)*p->priv.curr; + if (!chatset[(uint8_t)c]) break; + res++; + INC_POS; + } + return res; +} + +int parser_skip_n(parser_t *p, int n) +{ + bool eof; + int i; + for (i = 0; i < n; i++) + { + CHECK_STATE(); + if (eof) return i; + INC_POS; + } + return n; +} + +bool parser_skip_char(parser_t *p, char c) +{ + bool eof; + CHECK_STATE(); + if (*p->priv.curr != c || eof) return false; + INC_POS; + return true; +} + +bool parser_skip_ws(parser_t *p) +{ + bool eof; + bool res = false; + while (true) + { + CHECK_STATE(); + if (eof) return res; + if (*p->priv.curr > 0x20) return res; + res = true; + INC_POS; + } +} + +bool parser_skip_ws_in_line(parser_t *p) +{ + bool eof; + bool res = false; + while (true) + { + CHECK_STATE(); + if (eof) return res; + if (*p->priv.curr > 0x20 || *p->priv.curr == '\r' || *p->priv.curr == '\n') + return res; + res = true; + INC_POS; + } +} + +bool parser_skip_line(parser_t *p) +{ + bool eof; + while (true) + { + CHECK_STATE(); + if (eof) return false; + if (*p->priv.curr == '\n') + { + INC_POS; + return true; + } + INC_POS; + } +} + +bool parser_skip_before(parser_t *p, char c) +{ + bool eof; + while (true) + { + CHECK_STATE(); + if (eof) return false; + if (*p->priv.curr == c) return true; + INC_POS; + } +} +
diff -r c2ca3c5ded62 -r 119c4f7144c8 http-server/cparser.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/http-server/cparser.h Tue Jul 24 17:36:01 2018 +0000 @@ -0,0 +1,107 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 by Sergey Fetisov <fsenok@gmail.com> + * + * 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) + */ + +#ifndef CPARSER_H +#define CPARSER_H + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include <string.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct parser parser_t; + +typedef size_t (*parser_read_t)(parser_t *parser, char *buffer, size_t size); +typedef char chatset_t[256]; + +typedef struct parser +{ + char *buff; + size_t buff_size; + void *user_data; + parser_read_t read_cb; + size_t total; + size_t line; + size_t col; + struct + { + const char *curr; + const char *stop; + } priv; +} parser_t; + + +#define VAL_FMT_0X 1 // supports 0x12AB +#define VAL_FMT_0H 2 // supports 0h12AB +#define VAL_FMT_0B 4 // supports 0b01101001 +#define VAL_FMT_FLOAT 8 // supports 3.1415 +#define VAL_FMT_EXP 16 // supports 0.31415e+1 +#define VAL_FMT_SIGN 32 // supports +-3.1415 +#define VAL_FMT_ALL 0xFF + +#define STR_FMT_QUOTE 1 // supports "string" +#define STR_FMT_APOST 2 // supports 'string' +#define STR_FMT_NAKED 4 // supports string +#define STR_FMT_ESC 8 // supports \ escaping (bfnrt'"/\) +#define STR_FMT_ESCU 16 // supports unicode escaping \u12AB (like json) +#define STR_FMT_ESCW 32 // supports web escaping %2A +#define STR_FMT_ALL 0xFF + +void parser_init(parser_t *p, const char *str, int len); +void parser_init_s(parser_t *p, char *stream_buff, size_t size, parser_read_t cb); +bool parser_is_next(parser_t *p, const char *str, int len); +char parser_curr(parser_t *p); +bool parser_eof(parser_t *p); + +int parser_read(parser_t *p, const chatset_t chatset, char *dest, int dest_size); +int parser_read_before2(parser_t *p, char *dest, int dest_size, char c1, char c2); +int parser_read_digits(parser_t *p, char *dest, int dest_size, bool hex); +int parser_read_value(parser_t *p, char *dest, int dest_size, int fmt); +int parser_read_string(parser_t *p, char *dest, int dest_size, int fmt); +#define parser_read_before(p, dest, dest_size, c) \ + parser_read_before2(p, dest, dest_size, c, c); +#define parser_read_before_line_end(p, dest, dest_size) \ + parser_read_before2(p, dest, dest_size, '\r', '\n'); + +int parser_skip(parser_t *p, const chatset_t chatset); +int parser_skip_n(parser_t *p, int n); +bool parser_skip_char(parser_t *p, char c); +bool parser_skip_ws(parser_t *p); +bool parser_skip_ws_in_line(parser_t *p); +bool parser_skip_line(parser_t *p); +bool parser_skip_before(parser_t *p, char c); + +#ifdef __cplusplus +} +#endif + +#endif
diff -r c2ca3c5ded62 -r 119c4f7144c8 http-server/htserv.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/http-server/htserv.c Tue Jul 24 17:36:01 2018 +0000 @@ -0,0 +1,405 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 by Sergey Fetisov <fsenok@gmail.com> + * + * 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 http ipv4 server using lwip (pcb) + */ + +#include "htserv.h" + +static struct tcp_pcb *http_pcb = NULL; + +htserv_on_req_t htserv_on_req = NULL; +htserv_on_con_t htserv_on_con = NULL; +htserv_on_err_t htserv_on_err = NULL; + +typedef enum htcon_state_priv +{ + CSP_ACTIVE, // as CON_ACTIVE + CSP_CLOSING, // as CON_CLOSING + CSP_CLOSED, // as CON_CLOSED + CSP_RD_REQ, + CSP_WR_RESP, + CSP_NONE, +} htcon_state_priv_t; + +typedef struct htcon_priv +{ + htcon_t htcon; // user connection struct + char buff[HTTP_CON_BUFF_SIZE]; // request/response buffer + htcon_state_priv_t state; // connection state + struct tcp_pcb *pcb; // connection pcb + http_reqb_t reqb; // request buffer + http_resp_t resp; // responce + // writing managment + char *wbuff; // writing buffer + int wsize; // writing buffer size + int writed; // writed to buffer + int wsended; // sended by tcp + int wacksize; // acknowledged +} htcon_priv_t; + +htcon_priv_t htcons[HTTP_SERVER_MAX_CON]; + +void htserv_io(void); + +void http_prepare_resp(http_resp_t *resp, int code) +{ + resp->code = code; + resp->server = HTTP_SERVER_NAME; + resp->cont_lang = HTTP_DEF_CONT_LANG; + resp->mime = MIME_TEXT_HTML; + resp->cont_len = 0; + resp->conn_type = CT_CLOSE; +} + +void htcon_req_finished(htcon_priv_t *con) +{ + // 1. check request + if (con->reqb.req.method != METHOD_GET) + { + //http_prepare_resp(&con->resp, 405); // Method Not Allowed + //con->state = CSP_WR_RESP; + con->state = CSP_CLOSING; + htserv_io(); + return; + } + // 2. ask user + if (htserv_on_req == NULL) + { + con->state = CSP_CLOSING; + htserv_io(); + return; + } + + http_prepare_resp(&con->resp, 200); + + if (htserv_on_req(&con->reqb.req, &con->resp, &con->htcon.arg)) + { + int len; + con->wbuff = con->buff + con->reqb.size; + con->wsize = HTTP_CON_BUFF_SIZE - con->reqb.size; + len = http_resp_str(&con->resp, con->wbuff, con->wsize); + if (len >= con->wsize) + { + if (htserv_on_err != NULL) + htserv_on_err(HTTP_RESP_NOMEM); + con->state = CSP_CLOSING; + } + else + { + con->state = CSP_WR_RESP; + con->wacksize = 0; + con->writed = len; + con->wsended = 0; + } + } + else + con->state = CSP_CLOSING; + + htserv_io(); +} + +void htcon_received(htcon_priv_t *con, const char *data, int size) +{ + htserv_err_t err; + err = HTTP_NO_ERROR; + + if (con->state == CSP_RD_REQ) + { + http_reqb_push(&con->reqb, data, size); + switch (con->reqb.state) + { + case REQB_UNFINISHED: + return; + case REQB_FINISHED: + htcon_req_finished(con); + return; + case REQB_REQ_TO_BIG: + err = HTTP_BIG_REQUEST; + break; + case REQB_SYNT_ERROR: + err = HTTP_REQ_SYNT_ERR; + break; + } + } + if (err != HTTP_NO_ERROR) + { + if (htserv_on_err != NULL) + htserv_on_err(err); + con->htcon.state = CON_CLOSING; + con->state = CSP_CLOSING; + htserv_io(); + } +} + +err_t htcon_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + htcon_priv_t *con; + + con = (htcon_priv_t *)arg; + + if (err == ERR_OK && p != NULL) + { + tcp_recved(pcb, p->tot_len); + if (con != NULL) + htcon_received(con, p->payload, p->tot_len); + } + + if (err == ERR_OK && p == NULL) + { + if (con != NULL) + if (con->state != CSP_CLOSED) + { + con->state = CSP_CLOSING; + con->htcon.state = CON_CLOSING; + } + } + + if (p != NULL) + pbuf_free(p); + + return ERR_OK; +} + +static int find_free_con(void) +{ + int i; + for (i = 0; i < HTTP_SERVER_MAX_CON; i++) + if (htcons[i].state == CSP_NONE) return i; + return -1; +} + +err_t htcon_sent(void *arg, struct tcp_pcb *tpcb, u16_t len) +{ + htcon_priv_t *con; + con = (htcon_priv_t *)arg; + if (con == NULL) return ERR_OK; + con->wacksize += len; +// if (con->state == CSP_ACTIVE) +// con->htcon.writed += len; + return ERR_OK; +} + +void htcon_err(void *arg, err_t err) +{ + htcon_priv_t *con; + con = (htcon_priv_t *)arg; + if (con == NULL) return; + if (htserv_on_err != NULL) + htserv_on_err(HTTP_TRANSP_ERR); + con->htcon.state = CON_CLOSING; + con->state = CSP_CLOSING; +} + +err_t htserv_accept(void *arg, struct tcp_pcb *newpcb, err_t err) +{ + int index; + htcon_priv_t *con; + + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(err); + + index = find_free_con(); + if (index < 0) return ERR_MEM; + con = &htcons[index]; + + con->pcb = newpcb; + http_reqb_init(&con->reqb, con->buff, HTTP_REQ_MAX_SIZE); + con->wbuff = NULL; + con->wsize = 0; + con->wsended = 0; + con->wacksize = 0; + con->state = CSP_RD_REQ; + + con->htcon.req = &con->reqb.req; + con->htcon.resp = &con->resp; + con->htcon.writed = 0; + con->htcon.arg = NULL; + + + tcp_setprio(newpcb, TCP_PRIO_MIN); + tcp_arg(newpcb, con); + tcp_recv(newpcb, htcon_recv); + tcp_err(newpcb, htcon_err); + tcp_sent(newpcb, htcon_sent); + tcp_poll(newpcb, NULL, 4); // No polling here + + return ERR_OK; +} + +void htserv_io(void) // state machine +{ + for (int i = 0; i < HTTP_SERVER_MAX_CON; i++) + { + htcon_priv_t *con; + con = &htcons[i]; + if (con->state == CSP_NONE || con->state == CSP_CLOSED) + continue; + + // have data to send? + if (con->state != CSP_CLOSING && con->writed > con->wsended) + { + int count; + err_t err; + count = con->writed - con->wsended; + if (count > tcp_sndbuf(con->pcb)) + count = tcp_sndbuf(con->pcb); + err = tcp_write(con->pcb, con->wbuff + con->wsended, count, TCP_WRITE_FLAG_COPY); + if (err == ERR_OK) + { + con->wsended += count; + tcp_output(con->pcb); + } + } + + // data was successfully sended? + if (con->state != CSP_CLOSING && con->wacksize >= con->wsended && con->wsended > 0) + { + con->writed = 0; + con->wsended = 0; + con->wacksize = 0; + // is it http-response? + if (con->state == CSP_WR_RESP) + { + con->htcon.state = CON_ACTIVE; + con->state = CSP_ACTIVE; + if (htserv_on_con != NULL) + htserv_on_con(i); + } + else + // is it document? + if (con->state == CSP_ACTIVE && con->resp.conn_type != CT_KEEP_ALIVE) + if (con->htcon.writed >= con->resp.cont_len) + { + con->htcon.state = CON_CLOSING; + con->state = CSP_CLOSING; + } + } + + // closing connection? + if (con->state == CSP_CLOSING) + // if (tcp_close(con->pcb) == ERR_OK) + { + con->htcon.state = CON_CLOSED; + con->state = CSP_CLOSED; + con->pcb = NULL; + } + } +} + +err_t htserv_init(uint16_t port) +{ + int i; + err_t err; + tcp_init(); + htserv_free(); + if (http_pcb != NULL) + return ERR_USE; + http_pcb = tcp_new(); + if (http_pcb == NULL) return ERR_MEM; + err = tcp_bind(http_pcb, IP_ADDR_ANY, port); + if (err != ERR_OK) + { + htserv_free(); + return err; + } + for (i = 0; i < HTTP_SERVER_MAX_CON; i++) + htcons[i].state = CSP_NONE; + return ERR_OK; +} + +void htserv_free(void) +{ + if (http_pcb == NULL) return; + if (tcp_close(http_pcb) == ERR_OK) + http_pcb = NULL; +} + +void htserv_tmr(uint32_t time_ms) //TODO +{ + http_pcb = tcp_listen(http_pcb); + if (http_pcb != NULL) + { + tcp_accept(http_pcb, htserv_accept); + } + htserv_io(); +} + +const htcon_t *htcon(int index) +{ + if (index < 0 || index >= HTTP_SERVER_MAX_CON) + return NULL; + if (htcons[index].state > CSP_CLOSED) return NULL; + return &htcons[index].htcon; +} + +void htcon_close(int index) +{ + htcon_priv_t *con; + con = (htcon_priv_t *)htcon(index); + if (con == NULL) return; + if (con->state == CSP_CLOSED) return; + con->htcon.state = CON_CLOSING; + con->state = CSP_CLOSING; + htserv_io(); +} + +void htcon_free(int index) +{ + htcon_priv_t *con; + con = (htcon_priv_t *)htcon(index); + if (con == NULL) return; + if (con->state != CSP_CLOSED) return; + htcons[index].state = CSP_NONE; +} + +int htcon_write_avail(int index) +{ + htcon_priv_t *con; + con = (htcon_priv_t *)htcon(index); + if (con == NULL) return 0; + if (con->state != CSP_ACTIVE) return 0; + return con->wsize - con->writed; +} + +int htcon_write(int index, const char *data, int size) +{ + int res; + htcon_priv_t *con; + con = (htcon_priv_t *)htcon(index); + if (con == NULL) return 0; + if (con->state != CSP_ACTIVE) return 0; + res = con->resp.cont_len - con->htcon.writed; + if (size < res) res = size; + if (con->wsize - con->writed < res) + res = con->wsize - con->writed; + if (res <= 0) return 0; + memcpy(con->wbuff + con->writed, data, res); + con->writed += res; + con->htcon.writed += res; + htserv_io(); + return res; +}
diff -r c2ca3c5ded62 -r 119c4f7144c8 http-server/htserv.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/http-server/htserv.h Tue Jul 24 17:36:01 2018 +0000 @@ -0,0 +1,93 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 by Sergey Fetisov <fsenok@gmail.com> + * + * 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 http ipv4 server using lwip (pcb) + */ + +#ifndef HTSERV_H +#define HTSERV_H + +#include <stdlib.h> +#include <stdio.h> +#include "lwip/netif.h" +#include "lwip/pbuf.h" +#include "lwip/tcp_impl.h" +#include "lwip/tcp.h" +#include "http_req.h" + +// server configuration + +#define HTTP_SERVER_NAME "lrndis" // http server name +#define HTTP_SERVER_MAX_CON 3 // max connections number at one time +#define HTTP_CON_BUFF_SIZE 2048 // size of con buffer, used for request/response data storing +#define HTTP_REQ_MAX_SIZE 1024 // max part of con buffer for a request storing +#define HTTP_DEF_CONT_LANG "en" // content language which will be sent to client by default + +// http server types + +typedef enum htserv_err +{ + HTTP_NO_ERROR, + HTTP_REQ_SYNT_ERR, // http request synt error + HTTP_BIG_REQUEST, // http request size more then HTTP_CON_BUFF + HTTP_RESP_NOMEM, // http response size more then HTTP_CON_BUFF + HTTP_TRANSP_ERR // http transport error (client disconnected?) +} htserv_err_t; + +typedef bool (*htserv_on_req_t)(const http_req_t *req, http_resp_t *resp, void **arg); +typedef void (*htserv_on_con_t)(int index); +typedef void (*htserv_on_err_t)(htserv_err_t err); + +typedef enum htcon_state +{ + CON_ACTIVE, + CON_CLOSING, + CON_CLOSED +} htcon_state_t; + +typedef struct htcon +{ + void *arg; + http_req_t *req; + http_resp_t *resp; + htcon_state_t state; + int writed; +} htcon_t; + +extern htserv_on_req_t htserv_on_req; +extern htserv_on_con_t htserv_on_con; +extern htserv_on_err_t htserv_on_err; + +err_t htserv_init(uint16_t port); +void htserv_free(void); +void htserv_tmr(uint32_t time_ms); +const htcon_t *htcon(int index); +void htcon_close(int con); +void htcon_free(int con); +int htcon_write_avail(int con); +int htcon_write(int con, const char *data, int size); + +#endif
diff -r c2ca3c5ded62 -r 119c4f7144c8 http-server/http_req.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/http-server/http_req.c Tue Jul 24 17:36:01 2018 +0000 @@ -0,0 +1,305 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 by Sergey Fetisov <fsenok@gmail.com> + * + * 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: part of tiny http ipv4 server using lwip (pcb) + */ + +#include "http_req.h" + +#ifdef __cplusplus +extern "C" { +#endif + +const char MIME_TEXT_HTML[] = "text/html"; +const char MIME_TEXT_JS[] = "text/javascript"; +const char MIME_TEXT_PLAIN[] = "text/plain"; +const char MIME_TEXT_XML[] = "text/xml"; +const char MIME_TEXT_CSS[] = "text/css"; +const char MIME_IMAGE_GIF[] = "image/gif"; +const char MIME_IMAGE_JPEG[] = "image/jpeg"; +const char MIME_IMAGE_PJPEG[] = "image/pjpeg"; +const char MIME_IMAGE_PNG[] = "image/png"; +const char MIME_IMAGE_SVG[] = "image/svg+xml"; +const char MIME_IMAGE_TIFF[] = "image/tiff"; +const char MIME_IMAGE_ICON[] = "image/vnd.microsoft.icon"; +const char MIME_IMAGE_WBMP[] = "image/vnd.wap.wbmp"; + +static const char *METHODS_STR[] = +{ + "", + "GET", + "POST", + "HEAD", + "PUT", + "CONNECT", + "OPTIONS", + "DELETE", + "TRACE", + "PATCH" +}; + +static const char *CONN_TYPE_STR[] = +{ + "", "close", "keep-alive" +}; + +void http_reqb_init(http_reqb_t *reqb, void *buff, int size) +{ + memset(reqb, 0, sizeof(http_reqb_t)); + reqb->buff = buff; + reqb->bsize = size; +} + +int http_reqb_avail(const http_reqb_t *reqb) +{ + return reqb->bsize - reqb->size; +} + +static chatset_t req_ident = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, // '%', '-', '.', '/' + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // '0'...'9' + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 'A'...'O' + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 'P'...'Z', '_' + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 'a'...'z' + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 'p'...'z' +}; + +static http_mt_t str_to_method(const char *str) +{ + http_mt_t res; + for (res = METHOD_GET; res <= METHOD_PATCH; res++) + if (strcmp(METHODS_STR[res], str) == 0) + return res; + return METHOD_NONE; +} + +static http_ct_t str_to_ct(const char *str) +{ + if (strcmp(str, CONN_TYPE_STR[CT_CLOSE]) == 0) return CT_CLOSE; + if (strcmp(str, CONN_TYPE_STR[CT_KEEP_ALIVE]) == 0) return CT_KEEP_ALIVE; + return CT_NONE; +} + +void http_req_set_param(http_req_t *req, char *name, char *val) +{ + if (strcmp(name, "Host") == 0) + req->host = val; + else + if (strcmp(name, "User-Agent") == 0) + req->user_agent = val; + else + if (strcmp(name, "Content-Type") == 0) + req->mime = val; + else + if (strcmp(name, "Content-Length") == 0) + req->cont_len = strtol(val, NULL, 10); + else + if (strcmp(name, "Accept") == 0) + req->accept = val; + else + if (strcmp(name, "Accept-Language") == 0) + req->accept_lang = val; + else + if (strcmp(name, "Accept-Encoding") == 0) + req->accept_enc = val; + else + if (strcmp(name, "Cookie") == 0) + req->cookie = val; + else + if (strcmp(name, "Connection") == 0) + req->conn_type = str_to_ct(val); + else + if (strcmp(name, "Keep-Alive") == 0) + req->keep_alive = strtol(val, NULL, 10); +} + +void http_reqb_push(http_reqb_t *rb, const void *data, int size) +{ + parser_t p; + int indeces[4]; + + if (rb->size + size >= rb->bsize) + { + rb->state = REQB_REQ_TO_BIG; + return; + } + if (rb->state != REQB_UNFINISHED) return; + + memcpy(&rb->buff[rb->size], data, size); + rb->size += size; + + parser_init(&p, rb->buff, rb->size); + parser_skip_n(&p, rb->parsing.pos); + + while (!parser_eof(&p)) + { + int total; + switch (rb->parsing.state) + { + case 0: + parser_skip_ws(&p); + total = p.total; + parser_skip_before(&p, ' '); + if (parser_eof(&p)) break; + rb->buff[p.total] = 0; + rb->req.method = str_to_method(&rb->buff[total]); + parser_skip_n(&p, 1); + rb->parsing.pos = p.total; + rb->parsing.state = 1; + break; + case 1: + parser_skip_ws(&p); + total = p.total; + parser_skip(&p, req_ident); + if (parser_eof(&p)) break; + switch (parser_curr(&p)) + { + case '?': rb->parsing.state = 2; break; + case ' ': rb->parsing.state = 4; break; + default: rb->parsing.state = 11; break; + } + if (rb->parsing.state >= 10) break; // error + rb->req.uri = &rb->buff[total]; + rb->buff[p.total] = 0; + parser_skip_n(&p, 1); + rb->parsing.pos = p.total; + break; + case 2: + total = p.total; + parser_skip(&p, req_ident); + if (parser_eof(&p)) break; + switch (parser_curr(&p)) + { + case '&': rb->parsing.state = 2; break; + case '=': rb->parsing.state = 3; break; + case ' ': rb->parsing.state = 4; break; + default: rb->parsing.state = 12; break; + } + if (rb->parsing.state >= 10) break; // error + rb->req.num_params++; + if (rb->req.num_params <= HTTP_REQ_MAX_PARAMS) + { + rb->req.params[rb->req.num_params - 1] = &rb->buff[total]; + rb->req.values[rb->req.num_params - 1] = NULL; + } + rb->buff[p.total] = 0; + parser_skip_n(&p, 1); + rb->parsing.pos = p.total; + break; + case 3: + total = p.total; + parser_skip(&p, req_ident); + if (parser_eof(&p)) break; + switch (parser_curr(&p)) + { + case '&': rb->parsing.state = 2; break; + case ' ': rb->parsing.state = 4; break; + default: rb->parsing.state = 13; break; + } + if (rb->parsing.state >= 10) break; // error + if (rb->req.num_params <= HTTP_REQ_MAX_PARAMS) + rb->req.values[rb->req.num_params - 1] = &rb->buff[total]; + rb->buff[p.total] = 0; + parser_skip_n(&p, 1); + rb->parsing.pos = p.total; + break; + case 4: + parser_skip_ws(&p); + total = p.total; + if (!parser_skip_line(&p)) break; + rb->req.ver = &rb->buff[total]; + rb->buff[p.total - 2] = 0; + rb->parsing.pos = p.total; + rb->parsing.state = 5; + break; + case 5: + parser_skip_ws_in_line(&p); + if (parser_skip_char(&p, '\r') || parser_skip_char(&p, '\n')) + { + // DONE + rb->state = REQB_FINISHED; + return; + } + // 0 1 2 3 + // Accept: text/html\r\n... + parser_skip_ws_in_line(&p); + indeces[0] = p.total; + parser_skip(&p, req_ident); + if (parser_eof(&p)) break; + if (parser_curr(&p) != ':') + { + rb->parsing.state = 15; + break; + } + indeces[1] = p.total; + parser_skip_n(&p, 1); + parser_skip_ws_in_line(&p); + indeces[2] = p.total; + if (!parser_skip_line(&p)) break; + indeces[3] = p.total; + rb->buff[indeces[1]] = 0; + rb->buff[indeces[3] - 2] = 0; + http_req_set_param(&rb->req, + &rb->buff[indeces[0]], + &rb->buff[indeces[2]]); + rb->parsing.pos = p.total; + break; + default: + rb->state = REQB_SYNT_ERROR; + return; + } + } +} + +int http_resp_len(const http_resp_t *resp) +{ + char temp[1]; + return http_resp_str(resp, temp, 1); +} + +int http_resp_str(const http_resp_t *resp, char *str, int size) +{ + return snprintf(str, size, + "HTTP/1.1 %d\r\n" + "Server: %s\r\n" +// "Content-Type: text/html\r\n" + "Content-Type: %s\r\n" + "Content-Length: %d\r\n" + "Connection: %s\r\n" + "\r\n", + resp->code, + resp->server, + resp->mime, + resp->cont_len, + CONN_TYPE_STR[resp->conn_type]); +} + +#ifdef __cplusplus +} +#endif
diff -r c2ca3c5ded62 -r 119c4f7144c8 http-server/http_req.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/http-server/http_req.h Tue Jul 24 17:36:01 2018 +0000 @@ -0,0 +1,148 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 by Sergey Fetisov <fsenok@gmail.com> + * + * 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: part of tiny http ipv4 server using lwip (pcb) + */ + +#ifndef HTTP_REQ_H +#define HTTP_REQ_H + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include <string.h> +#include <stdlib.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#include "cparser.h" + +//#define HTTP_REQ_MAX_SIZE 512 +#define HTTP_REQ_MAX_PARAMS 32 + +typedef enum http_method +{ + METHOD_NONE, + METHOD_GET, + METHOD_POST, + METHOD_HEAD, + METHOD_PUT, + METHOD_CONNECT, + METHOD_OPTIONS, + METHOD_DELETE, + METHOD_TRACE, + METHOD_PATCH +} http_mt_t; + +typedef enum http_conn_type +{ + CT_NONE, + CT_CLOSE, + CT_KEEP_ALIVE +} http_ct_t; + +extern const char MIME_TEXT_HTML[]; // text/html +extern const char MIME_TEXT_HTML[]; // text/javascript +extern const char MIME_TEXT_PLAIN[]; // text/plain +extern const char MIME_TEXT_XML[]; // text/xml +extern const char MIME_TEXT_CSS[]; // text/css +extern const char MIME_IMAGE_GIF[]; // image/gif +extern const char MIME_IMAGE_JPEG[]; // image/jpeg +extern const char MIME_IMAGE_PJPEG[]; // image/pjpeg +extern const char MIME_IMAGE_PNG[]; // image/png +extern const char MIME_IMAGE_SVG[]; // image/svg+xml +extern const char MIME_IMAGE_TIFF[]; // image/tiff +extern const char MIME_IMAGE_ICON[]; // image/vnd.microsoft.icon +extern const char MIME_IMAGE_WBMP[]; // image/vnd.wap.wbmp + +typedef struct http_req +{ + http_mt_t method; // GET + char *uri; // /path/resource + int num_params; // 2 + char *params[HTTP_REQ_MAX_PARAMS]; // param1, param2 + char *values[HTTP_REQ_MAX_PARAMS]; // value1, value2 + char *ver; // HTTP/1.1 + char *host; // Host: [wikipedia.org] + char *user_agent; // User-Agent: [Mozilla/5.0] + char *mime; // Content-Type: [multipart/form-data; boundary="Asrf456BGe4h"] + int cont_len; // Content-Length: [123] + char *accept; // Accept: [text/html] + char *accept_lang; // Accept-Language: [en-US;q=0.5,en;q=0.3] + char *accept_enc; // Accept-Encoding: [gzip, deflate] + char *cookie; // Cookie: [Cookie data] + http_ct_t conn_type; // Connection: [keep-alive] + int keep_alive; // Keep-Alive: [300] +} http_req_t; + +typedef struct http_resp +{ + int code; // HTTP/1.1 [200] OK + const char *server; // Server: [lrndis] + const char *cont_lang; // Content-Language: [ru] + const char *mime; // Content-Type: [text/html; charset=utf-8] + int cont_len; // Content-Length: [1234] + http_ct_t conn_type; // Connection: [close] +} http_resp_t; + +// HTTP REQUEST BUFFER + +typedef enum http_reqb_state +{ + REQB_UNFINISHED, + REQB_FINISHED, + REQB_SYNT_ERROR, + REQB_REQ_TO_BIG +} http_reqb_state_t; + +typedef struct http_reqb +{ + http_req_t req; + http_reqb_state_t state; + //char buff[HTTP_REQ_MAX_SIZE]; + char *buff; + int bsize; + int size; + struct + { + int pos; + int state; + } parsing; +} http_reqb_t; + +void http_reqb_init(http_reqb_t *reqb, void *buff, int size); +int http_reqb_avail(const http_reqb_t *reqb); +void http_reqb_push(http_reqb_t *rb, const void *data, int size); +int http_resp_len(const http_resp_t *resp); +int http_resp_str(const http_resp_t *resp, char *str, int size); + +#ifdef __cplusplus +} +#endif + +#endif