Network Services
Fork of W5500Interface_K22F by
Revision 15:14382459c8b7, committed 2017-06-15
- Comitter:
- dgriffin65
- Date:
- Thu Jun 15 20:29:03 2017 +0000
- Parent:
- 14:ada7d903623d
- Commit message:
- Converted to a single library
Changed in this revision
--- a/DHCPClient.lib Thu Jun 15 20:25:40 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -https://developer.mbed.org/teams/AMETEK-Powervar/code/DHCPClient/#9697009514d0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DHCPClient/DHCPClient.cpp Thu Jun 15 20:29:03 2017 +0000 @@ -0,0 +1,208 @@ +// DHCPClient.cpp 2013/4/10 +#include "mbed.h" +#include "mbed_debug.h" +#include "UDPSocket.h" +#include "DHCPClient.h" + +#define DBG_DHCP 0 + +#if DBG_DHCP +#define DBG(...) do{debug("[%s:%d]", __PRETTY_FUNCTION__,__LINE__);debug(__VA_ARGS__);} while(0); +#define DBG_HEX(A,B) do{debug("[%s:%d]\r\n", __PRETTY_FUNCTION__,__LINE__);debug_hex(A,B);} while(0); +#else +#define DBG(...) while(0); +#define DBG_HEX(A,B) while(0); +#endif + +int DHCPClient::discover() +{ + m_pos = 0; + const uint8_t header[] = {0x01,0x01,0x06,0x00}; + add_buf((uint8_t*)header, sizeof(header)); + uint32_t x = time(NULL) + rand(); + xid[0] = x>>24; xid[1] = x>>16; xid[2] = x>>8; xid[3] = x; + add_buf(xid, 4); + fill_buf(20, 0x00); + add_buf(chaddr, 6); + fill_buf(10+192, 0x00); + const uint8_t options[] = {0x63,0x82,0x53,0x63, // magic cookie + 53,1,DHCPDISCOVER, // DHCP option 53: DHCP Discover + 55,4,1,3,15,6, + 255}; + add_buf((uint8_t*)options, sizeof(options)); + return m_pos; +} + +int DHCPClient::request() +{ + m_pos = 0; + const uint8_t header[] = {0x01,0x01,0x06,0x00}; + add_buf((uint8_t*)header, sizeof(header)); + add_buf(xid, 4); + fill_buf(12, 0x00); + add_buf(siaddr, 4); + fill_buf(4, 0x00); // giaddr + add_buf(chaddr, 6); + fill_buf(10+192, 0x00); + const uint8_t options[] = {0x63,0x82,0x53,0x63, // magic cookie + 53,1,DHCPREQUEST, // DHCP option 53: DHCP Request + 55,4,1,3,15,6, // DHCP option 55: + }; + add_buf((uint8_t*)options, sizeof(options)); + add_option(50, yiaddr, 4); + add_option(54, siaddr, 4); + add_option(255); + return m_pos; +} + +int DHCPClient::offer(uint8_t buf[], int size) { + memcpy(yiaddr, buf+DHCP_OFFSET_YIADDR, 4); + memcpy(siaddr, buf+DHCP_OFFSET_SIADDR, 4); + uint8_t *p; + int msg_type = -1; + p = buf + DHCP_OFFSET_OPTIONS; + while(*p != 255 && p < (buf+size)) { + uint8_t code = *p++; + if (code == 0) { // Pad Option + continue; + } + int len = *p++; + + DBG("DHCP option: %d\r\n", code); + DBG_HEX(p, len); + + switch(code) { + case 53: + msg_type = *p; + break; + case 1: + memcpy(netmask, p, 4); // Subnet mask address + break; + case 3: + memcpy(gateway, p, 4); // Gateway IP address + break; + case 6: // DNS server + memcpy(dnsaddr, p, 4); + break; + case 51: // IP lease time + break; + case 54: // DHCP server + memcpy(siaddr, p, 4); + break; + } + p += len; + } + return msg_type; +} + +bool DHCPClient::verify(uint8_t buf[], int len) { + if (len < DHCP_OFFSET_OPTIONS) { + return false; + } + if (buf[DHCP_OFFSET_OP] != 0x02) { + return false; + } + if (memcmp(buf+DHCP_OFFSET_XID, xid, 4) != 0) { + return false; + } + return true; +} + +void DHCPClient::callback() +{ + //printf("DHCPClient callback\n"); + SocketAddress host; + int recv_len = m_udp->recvfrom(&host, (char*)m_buf, sizeof(m_buf)); + if (recv_len < 0) { + return; + } + if (!verify(m_buf, recv_len)) { + return; + } + int r = offer(m_buf, recv_len); + if (r == DHCPOFFER) { + int send_size = request(); + m_udp->sendto(m_server, (char*)m_buf, send_size); + } else if (r == DHCPACK) { + exit_flag = true; + } +} + +void DHCPClient::add_buf(uint8_t c) +{ + m_buf[m_pos++] = c; +} + +void DHCPClient::add_buf(uint8_t* buf, int len) +{ + for(int i = 0; i < len; i++) { + add_buf(buf[i]); + } +} + +void DHCPClient::fill_buf(int len, uint8_t data) +{ + while(len-- > 0) { + add_buf(data); + } +} + +void DHCPClient::add_option(uint8_t code, uint8_t* buf, int len) +{ + add_buf(code); + if (len > 0) { + add_buf((uint8_t)len); + add_buf(buf, len); + } +} + +int DHCPClient::setup(NetworkStack *ns, int timeout_ms) +{ + int interval_ms = 5*1000; // 5000msec + if (timeout_ms < interval_ms) { + interval_ms = timeout_ms; + } + UDPSocket udp_sock; + m_udp = &udp_sock; + + udp_sock.open(ns); + udp_sock.set_blocking(false); + udp_sock.bind(68); // local port + m_server.set_ip_address("255.255.255.255"); // DHCP broadcast + m_server.set_port(67); // DHCP broadcast + exit_flag = false; + int err = 0; + int seq = 0; + int send_size; + while(!exit_flag) { + switch(seq) { + case 0: + m_retry = 0; + seq++; + break; + case 1: + send_size = discover(); + udp_sock.sendto(m_server, (char*)m_buf, send_size); + m_interval.reset(); + m_interval.start(); + seq++; + break; + case 2: + callback(); + if (m_interval.read_ms() > interval_ms) { + DBG("m_retry: %d\n", m_retry); + if (++m_retry >= (timeout_ms/interval_ms)) { + err = -1; + exit_flag = true; + } + seq--; + } + break; + } + } + DBG("m_retry: %d, m_interval: %d\n", m_retry, m_interval.read_ms()); + return err; +} + +DHCPClient::DHCPClient() { +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DHCPClient/DHCPClient.h Thu Jun 15 20:29:03 2017 +0000 @@ -0,0 +1,54 @@ +// DHCPClient.h 2013/4/10 +#ifndef DHCPCLIENT_H +#define DHCPCLIENT_H +#include "wiznet.h" +#include "UDPSocket.h" + +#define DHCP_OFFSET_OP 0 +#define DHCP_OFFSET_XID 4 +#define DHCP_OFFSET_YIADDR 16 +#define DHCP_OFFSET_SIADDR 20 +#define DHCP_OFFSET_OPTIONS 240 +#define DHCP_MAX_PACKET_SIZE 600 + +// DHCP Message Type +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNAK 6 +#define DHCPRELEASE 7 +#define DHCPINFORM 8 + +class DHCPClient { +public: + DHCPClient(); + int setup(NetworkStack *ns, int timeout_ms = 15*1000); + uint8_t chaddr[6]; // MAC + uint8_t yiaddr[4]; // IP + uint8_t dnsaddr[4]; // DNS + uint8_t gateway[4]; + uint8_t netmask[4]; + uint8_t siaddr[4]; +private: + int discover(); + int request(); + int offer(uint8_t buf[], int size); + void add_buf(uint8_t* buf, int len); + void fill_buf(int len, uint8_t data = 0x00); + void add_buf(uint8_t c); + void add_option(uint8_t code, uint8_t* buf = NULL, int len = 0); + bool verify(uint8_t buf[], int len); + void callback(); + UDPSocket* m_udp; + SocketAddress m_server; + uint8_t xid[4]; + bool exit_flag; + Timer m_interval; + int m_retry; + uint8_t m_buf[DHCP_MAX_PACKET_SIZE]; + int m_pos; + //WIZnet_Chip* eth; +}; +#endif //DHCPCLIENT_H
--- a/DNSClient.lib Thu Jun 15 20:25:40 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -https://developer.mbed.org/teams/AMETEK-Powervar/code/DNSClient/#06314bd0bca3
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DNSClient/DNSClient.cpp Thu Jun 15 20:29:03 2017 +0000 @@ -0,0 +1,245 @@ +// DNSClient.cpp 2013/8/27 +#include "mbed.h" +#include "mbed_debug.h" +#include "DNSClient.h" +#include "UDPSocket.h" +#include "dnsname.h" +#include "wiznet.h" + +#if DBG_DNS +#define DBG2(...) do{debug("[DNS]%p %d %s ", this,__LINE__,__PRETTY_FUNCTION__); debug(__VA_ARGS__); } while(0); +#else +#define DBG2(...) while(0); +#endif + +static SocketAddress theDnsServer; + +DNSClient::DNSClient() : m_state(MYNETDNS_START), m_udp(NULL) { + m_ns = NULL; + + //default to google + theDnsServer.set_ip_address("8.8.8.8"); // DNS + theDnsServer.set_port(53); // DNS +} + +DNSClient::DNSClient(NetworkStack *ns, const char* hostname) : m_state(MYNETDNS_START), m_udp(NULL) { + m_hostname = hostname; + m_ns = ns; + + //default to google + theDnsServer.set_ip_address("8.8.8.8"); // DNS + theDnsServer.set_port(53); // DNS +} + +DNSClient::DNSClient(NetworkStack *ns, SocketAddress* pHost) : m_state(MYNETDNS_START), m_udp(NULL) { + m_ns = ns; + + //default to google + theDnsServer.set_ip_address("8.8.8.8"); // DNS + theDnsServer.set_port(53); // DNS +} + +DNSClient::~DNSClient() { + if (m_udp) { + delete m_udp; + } +} + +int DNSClient::setup(NetworkStack *ns) { + m_ns = ns; + return 0; +} + +bool DNSClient::set_server(const char* serverip, int port) { + theDnsServer.set_ip_address(serverip); + theDnsServer.set_port(port); + return true; +} + +void DNSClient::callback() +{ + uint8_t buf[512]; + SocketAddress host; + int len = m_udp->recvfrom(&host, (char*)buf, sizeof(buf)); + if (len < 0) { + return; + } + if (memcmp(buf+0, m_id, 2) != 0) { //verify + return; + } + + int rcode = response(buf, len); + if (rcode == 0) { + m_state = MYNETDNS_OK; + } else { + m_state = MYNETDNS_NOTFOUND; + } +} + +int DNSClient::response(uint8_t buf[], int size) { + int rcode = buf[3] & 0x0f; + if (rcode != 0) { + return rcode; + } + int qdcount = buf[4]<<8|buf[5]; + int ancount = buf[6]<<8|buf[7]; + int pos = 12; + while(qdcount-- > 0) { + dnsname qname(buf); + pos = qname.decode(pos); // qname + pos += 4; // qtype qclass + } + while(ancount-- > 0) { + dnsname name(buf); + pos = name.decode(pos); // name + int type = buf[pos]<<8|buf[pos+1]; + pos += 8; // type class TTL + int rdlength = buf[pos]<<8|buf[pos+1]; pos += 2; + int rdata_pos = pos; + pos += rdlength; + if (type == 1) { // A record + m_ip = (buf[rdata_pos]<<24) | (buf[rdata_pos+1]<<16) | (buf[rdata_pos+2]<<8) | buf[rdata_pos+3]; + sprintf(m_ipaddr, "%d.%d.%d.%d", buf[rdata_pos],buf[rdata_pos+1],buf[rdata_pos+2],buf[rdata_pos+3]); + } +#if DBG_DNS + printf("%s", name.str.c_str()); + if (type == 1) { + printf(" A %d.%d.%d.%d\n", + buf[rdata_pos],buf[rdata_pos+1],buf[rdata_pos+2],buf[rdata_pos+3]); + } else if (type == 5) { + dnsname rdname(buf); + rdname.decode(rdata_pos); + printf(" CNAME %s\n", rdname.str.c_str()); + } else { + printf(" TYPE:%d", type); + printfBytes(" RDATA:", &buf[rdata_pos], rdlength); + } +#endif + } + return rcode; +} + +int DNSClient::query(uint8_t buf[], int size, const char* hostname) { + const uint8_t header[] = { + 0x00,0x00,0x01,0x00, // id=0x0000 QR=0 rd=1 opcode=0 rcode=0 + 0x00,0x01,0x00,0x00, // qdcount=1 ancount=0 + 0x00,0x00,0x00,0x00};// nscount=0 arcount=0 + const uint8_t tail[] = {0x00,0x01,0x00,0x01}; // qtype=A qclass=IN + memcpy(buf, header, sizeof(header)); + int t = rand(); + m_id[0] = t>>8; + m_id[1] = t; + memcpy(buf, m_id, 2); + dnsname qname(buf); + int pos = qname.encode(sizeof(header), (char*)hostname); + memcpy(buf+pos, tail, sizeof(tail)); + pos += sizeof(tail); + return pos; +} + +void DNSClient::resolve(const char* hostname) { + if (m_udp == NULL) { + m_udp = new UDPSocket; + m_udp->open(m_ns); + } + m_udp->set_blocking(false); + m_udp->bind(rand()&0x7fff); + uint8_t buf[256]; + int size = query(buf, sizeof(buf), hostname); +#if DBG_DNS + printf("hostname:[%s]\n", hostname); + printHex(buf, size); +#endif + m_udp->sendto(theDnsServer, (char*)buf, size); + m_interval.reset(); + m_interval.start(); +} + +void DNSClient::poll() { +#if DBG_DNS + printf("%p m_state: %d, m_udp: %p\n", this, m_state, m_udp); + wait_ms(400); +#endif + switch(m_state) { + case MYNETDNS_START: + m_retry = 0; + resolve(m_hostname); + m_state = MYNETDNS_PROCESSING; + break; + case MYNETDNS_PROCESSING: + break; + case MYNETDNS_NOTFOUND: + break; + case MYNETDNS_ERROR: + break; + case MYNETDNS_OK: + DBG2("m_retry=%d, m_interval=%d\n", m_retry, m_interval.read_ms()); + break; + } + if (m_interval.read_ms() > 1000) { + m_interval.stop(); + DBG2("timeout m_retry=%d\n", m_retry); + if (++m_retry >= 2) { + m_state = MYNETDNS_ERROR; + } else { + resolve(m_hostname); + m_state = MYNETDNS_PROCESSING; + } + } +} + +//tests for a valid IP address, returns 0 if invalid IP address format, returns ip address if valid +static uint32_t +isValidIP(const char* ip) { + int i1, i2, i3, i4; + int len = strlen(ip); + + if (len < 7) return 0; + + //count the number of dots, there must be 3 + int dotcount = 0; + for (int i = 0; i < len; i++) { + if (ip[i] == '.' ) dotcount++; + } + + //there must be exactly 3 dots + if (dotcount != 3) return 0; + + sscanf((char*)ip, "%d.%d.%d.%d", &i1, &i2, &i3, &i4); + + if (strlen(ip) <= 16 && (i1 >= 0 && i1 <= 255) && (i2 >= 0 && i2 <= 255) && + (i3 >= 0 && i3 <= 255) && (i4 >= 0 && i4 <= 255)) { + return ((i1<<24) | (i2<<16) | (i3<<8) | i4); + } + return 0; +} + +bool DNSClient::lookup(const char* hostname) { + m_hostname = hostname; + + uint32_t ip = isValidIP(hostname); + + //check if hostname is an IP address + if (ip > 0) { + //if it is already an IP address just return immediately + m_ip = ip; + strcpy(m_ipaddr, hostname); + m_state = MYNETDNS_OK; + return true; + } + + m_state = MYNETDNS_START; + while(1) { + poll(); + callback(); + if (m_state != MYNETDNS_PROCESSING) { + break; + } + } + + if (m_udp) { + delete m_udp; + m_udp = NULL; + } + return m_state == MYNETDNS_OK; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DNSClient/DNSClient.h Thu Jun 15 20:29:03 2017 +0000 @@ -0,0 +1,48 @@ +// DNSClient.h 2013/4/5 +#pragma once + +#include "UDPSocket.h" + +//#define DBG_DNS 1 + +class DNSClient { +public: + DNSClient(); + DNSClient(NetworkStack *ns, const char* hostname = NULL); + DNSClient(NetworkStack *ns, SocketAddress* pHost); + + int setup(NetworkStack *ns); + + virtual ~DNSClient(); + bool lookup(const char* hostname); + bool set_server(const char* serverip, int port=53); + uint32_t get_ip() {return m_ip;} + const char* get_ip_address() {return m_ipaddr;} + +protected: + void poll(); + void callback(); + int response(uint8_t buf[], int size); + int query(uint8_t buf[], int size, const char* hostname); + void resolve(const char* hostname); + uint8_t m_id[2]; + Timer m_interval; + int m_retry; + const char* m_hostname; + +private: + enum MyNetDnsState + { + MYNETDNS_START, + MYNETDNS_PROCESSING, //Req has not completed + MYNETDNS_NOTFOUND, + MYNETDNS_ERROR, + MYNETDNS_OK + }; + MyNetDnsState m_state; + UDPSocket *m_udp; + NetworkStack *m_ns; + + uint32_t m_ip; + char m_ipaddr[24]; +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DNSClient/dnsname.h Thu Jun 15 20:29:03 2017 +0000 @@ -0,0 +1,51 @@ +// dnsname.h 2013/8/27 +#pragma once +//#include <string> +#include "pico_string.h" +class dnsname { +public: + uint8_t *buf; + pico_string str; + dnsname(uint8_t *s) { + buf = s; + } + int decode(int pos) { + while(1) { + int len = buf[pos++]; + if (len == 0x00) { + break; + } + if ((len&0xc0) == 0xc0) { //compress + int offset = (len&0x3f)<<8|buf[pos]; + decode(offset); + return pos+1; + } + if (!str.empty()) { + str.append("."); + } + str.append((const char*)(buf+pos), len); + pos += len; + } + return pos; + } + + int encode(int pos, char* s) { + while(*s) { + char *f = strchr(s, '.'); + if (f == NULL) { + int len = strlen(s); + buf[pos++] = len; + memcpy(buf+pos, s, len); + pos += len; + break; + } + int len = f - s; + buf[pos++] = len; + memcpy(buf+pos, s, len); + s = f+1; + pos += len; + } + buf[pos++] = 0x00; + return pos; + } +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DNSClient/pico_string.h Thu Jun 15 20:29:03 2017 +0000 @@ -0,0 +1,46 @@ +// pico_string.h 2013/8/27 +#pragma once +class pico_string { +public: + pico_string(){ + _len = 0; + _buf = (char*)malloc(1); + if (_buf) { + _buf[0] = '\0'; + } + } + ~pico_string() { + if (_buf) { + free(_buf); + } + } + bool empty() { + return _len == 0; + } + void append(const char* s, int len) { + if (_buf == NULL) { + return; + } + char* p = (char*)malloc(_len+len+1); + if (p == NULL) { + return; + } + memcpy(p, _buf, _len); + memcpy(p+_len, s, len); + p[_len+len] = '\0'; + free(_buf); + _buf = p; + } + void append(const char* s) { + append(s, strlen(s)); + } + char* c_str() { + if (_buf) { + return _buf; + } + return ""; + } +private: + char* _buf; + int _len; +};
--- a/HTTPD.lib Thu Jun 15 20:25:40 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -https://developer.mbed.org/teams/AMETEK-Powervar/code/HTTPD/#b724fdb741e7
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HTTPD/CBuffer.h Thu Jun 15 20:29:03 2017 +0000 @@ -0,0 +1,75 @@ +/* Copyright (C) 2012 mbed.org, MIT License + * + * 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. + */ + +#ifndef CIRCBUFFER_H_ +#define CIRCBUFFER_H_ + +template <class T> +class CircBuffer { +public: + CircBuffer(int length) { + write = 0; + read = 0; + size = length + 1; + buf = (T *)malloc(size * sizeof(T)); + }; + + bool isFull() { + return (((write + 1) % size) == read); + }; + + bool isEmpty() { + return (read == write); + }; + + void queue(T k) { + if (isFull()) { + read++; + read %= size; + } + buf[write++] = k; + write %= size; + } + + void flush() { + read = 0; + write = 0; + } + + + uint32_t available() { + return (write >= read) ? write - read : size - read + write; + }; + + bool dequeue(T * c) { + bool empty = isEmpty(); + if (!empty) { + *c = buf[read++]; + read %= size; + } + return(!empty); + }; + +private: + volatile uint32_t write; + volatile uint32_t read; + uint32_t size; + T * buf; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HTTPD/HTTPD.cpp Thu Jun 15 20:29:03 2017 +0000 @@ -0,0 +1,137 @@ +/* Copyright (C) 2013 Hiroshi Suga, MIT License + * + * 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 "HTTPD.h" + +HTTPD * HTTPD::_inst; + +HTTPD::HTTPD () { + _inst = this; + memset(_state, 0, sizeof(_state)); + _handler_count = 0; +} + +int HTTPD::start (NetworkStack *ns, int port) { + int i; + + m_ns = ns; + + for (i = 0; i < HTTPD_MAX_CLIENTS; i ++) { + _state[i].buf = new CircBuffer<char>(HTTPD_BUF_SIZE); + _state[i].thread = new Thread(osPriorityNormal, HTTPD_STACK_SIZE); + _state[i].client = new TCPSocket(); + _state[i].thread->start(callback(child, (void*)i)); + } + +#ifdef HTTPD_ENABLE_CLOSER + _state[HTTPD_MAX_CLIENTS].thread = new Thread(closer, (void*)HTTPD_MAX_CLIENTS, osPriorityNormal, 128); + _state[HTTPD_MAX_CLIENTS].client = new TCPSocket(m_ns); +#endif + + _server.open(m_ns); + _server.bind(port); + _server.set_blocking(true); + _server.listen(); + _daemon = new Thread(osPriorityNormal, HTTPD_STACK_SIZE); + _daemon->start(HTTPD::daemon); + return 0; +} + +void HTTPD::daemon () { + HTTPD *httpd = HTTPD::getInstance(); + int i, t = 0; + + INFO("Wait for new connection...\r\n"); + for (;;) { + if (t >= 0) { + if (httpd->_server.accept(httpd->_state[t].client) == 0) { + INFO("accept %d\r\n", t); + httpd->_state[t].thread->signal_set(1); + } + } else { +#ifdef HTTPD_ENABLE_CLOSER + if (httpd->_server.accept(httpd->_state[HTTPD_MAX_CLIENTS].client) == 0) { + INFO("accept x\r\n"); + httpd->_state[HTTPD_MAX_CLIENTS].thread->signal_set(1); + } +#endif + } + + t = -1; + for (i = 0; i < HTTPD_MAX_CLIENTS; i ++) { + if (httpd->_state[i].thread->get_state() == Thread::WaitingAnd) { + if (t < 0) t = i; // next empty thread + } + } + } +} + +void HTTPD::child (void const *arg) { + HTTPD *httpd = HTTPD::getInstance(); + int id = (int)arg; + int i, n; + char buf[HTTPD_BUF_SIZE]; + + for (;;) { + Thread::signal_wait(1); + httpd->_state[id].mode = MODE_REQUEST; + httpd->_state[id].buf->flush(); + httpd->_state[id].keepalive = 0; + INFO("Connection from client\r\n"); +// INFO("Connection from %s\r\n", httpd->_state[id].client->get_ip_address()); + + httpd->_state[id].client->set_blocking(false); + httpd->_state[id].client->set_timeout(HTTPD_TIMEOUT); + + for (;;) { + //if (! httpd->_state[id].client->is_connected()) break; + + n = httpd->_state[id].client->recv(buf, sizeof(buf)); + + if (n < 0 ) { + printf("HTTPD::child breaking n = %d\r\n", n); + break; + } + buf[n] = 0; + //DBG("Recv %d ", n); + DBG("Recv %d '%s'", n, buf); + + for (i = 0; i < n; i ++) { + httpd->recvData(id, buf[i]); + } + } + + httpd->_state[id].client->close(); + INFO("Closed client connection\r\n"); + //INFO("Close %s\r\n", httpd->_state[id].client->get_ip_address()); + } +} + +void HTTPD::closer (void const *arg) { + HTTPD *httpd = HTTPD::getInstance(); + int id = (int)arg; + + for (;;) { + Thread::signal_wait(1); + + httpd->_state[id].client->close(); + INFO("Closed client connection\r\n"); + //INFO("Close %s\r\n", httpd->_state[id].client->get_ip_address()); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HTTPD/HTTPD.h Thu Jun 15 20:29:03 2017 +0000 @@ -0,0 +1,181 @@ +/* Copyright (C) 2013 Hiroshi Suga, MIT License + * + * 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. + */ + +#ifndef HTTPD_H +#define HTTPD_H + +#include "mbed.h" +#include "rtos.h" +#include "NetworkStack.h" +#include "EthInterface.h" +#include "CBuffer.h" + +#define DEBUG + +#define HTTPD_PORT 80 +#define HTTPD_MAX_CLIENTS 5 +#define HTTPD_KEEPALIVE 10 +#define HTTPD_TIMEOUT 15000 +#define HTTPD_MAX_HANDLES 10 + +#define HTTPD_CMD_SIZE 100 +#define HTTPD_BUF_SIZE 256 +#define HTTPD_STACK_SIZE (1024 * 6) +//#define HTTPD_ENABLE_CLOSER + +//Debug is disabled by default +#if defined(DEBUG) and (!defined(TARGET_LPC11U24)) +#define DBG(x, ...) std::printf("[DBG]" x "\r\n", ##__VA_ARGS__); +#define WARN(x, ...) std::printf("[WARN]" x "\r\n", ##__VA_ARGS__); +#define ERR(x, ...) std::printf("[ERR]" x "\r\n", ##__VA_ARGS__); +#define INFO(x, ...) std::printf("[INFO]" x "\r\n", ##__VA_ARGS__); +#else +#define DBG(x, ...) +#define WARN(x, ...) +#define ERR(x, ...) +#define INFO(x, ...) +#endif + + +class HTTPD { +public: + + enum Request { + REQ_HTTPGET, + REQ_HTTPPOST, + REQ_PUT, + }; + + enum Mode { + MODE_REQUEST, + MODE_REQSTR, + MODE_HEADER, + MODE_BODY, + MODE_ENTER, + MODE_ERROR, + MODE_WEBSOCKET, + MODE_WEBSOCKET_MASK, + MODE_WEBSOCKET_BODY, + MODE_WEBSOCKET_ENTER, + }; + + struct STATE { + Thread *thread; + TCPSocket *client; + volatile Request req; + volatile Mode mode; + CircBuffer <char>*buf; + char uri[HTTPD_CMD_SIZE]; + char *filename; + char *querystring; + int enter; + int length; + int n; + int keepalive; + int websocket; + char *websocket_key; + int websocket_opcode; + int websocket_flg; + char websocket_mask[4]; + int websocket_payload; + int (*sendws)(int id, const char *buf, int len, const char *mask); + }; + + HTTPD (); + + int start (NetworkStack *ns, int port = HTTPD_PORT); + + // --- HTTPD_req.cpp --- + void httpdError (int id, int err); + + // --- HTTPD_ws.cpp --- + static int sendWebsocket (int id, const char *buf, int len, const char *mask = NULL); + + // --- HTTPD_util.cpp --- + char *getUri (int id); + char *getFilename (int id); + char *getQueryString (int id); + + TCPSocket *getClientSocket(int id) { + if (id >= HTTPD_MAX_CLIENTS) return NULL; + return _state[id].client; + } + int send (int id, const char *body, int len, const char *header = NULL); + int sendstr (int id, const char *buf); + int hprintf(int id, const char* format, ...); + int receive (int id, char *buf, int len); + int attach (const char *uri, const char *dir); + int attach (const char *uri, void (*funcCgi)(int id)); + int base64encode (const char *input, int length, char *output, int len); + int urlencode (const char *str, char *buf, int len); + int urldecode (const char *str, char *buf, int len); + + static HTTPD * getInstance() { + return _inst; + }; + +private : + static HTTPD *_inst; + Thread *_daemon; + TCPServer _server; + + NetworkStack *m_ns; + +#ifdef HTTPD_ENABLE_CLOSER + struct STATE _state[HTTPD_MAX_CLIENTS + 1]; +#else + struct STATE _state[HTTPD_MAX_CLIENTS]; +#endif + + struct HANDLER { + char *uri; + char *dir; + void (*funcCgi)(int id); + } _handler[HTTPD_MAX_HANDLES]; + + int _handler_count; + + static void daemon (); + static void child (void const *arg); + static void closer (void const *arg); + + // --- HTTPD_req.cpp --- + int httpdFile (int id, char *dir); + void recvData (int id, char c); + int parseRequest (int id); + int parseHeader (int id); + void reqContentLength (int id, const char *buf); + void reqConnection (int id, const char *buf); + void reqUpgrade (int id, const char *buf); + void reqWebSocketVersion (int id, const char *buf); + void reqWebSocketKey (int id, const char *buf); + + // --- HTTPD_ws.cpp --- + void recvWS (int id, char c); + int parseWebsocket (int id); + int acceptWebsocket (int id); + + // --- HTTPD_util.cpp --- + int getHandler (const char *uri); + char *mimetype (char *file); + int strnicmp (const char *p1, const char *p2, int n); + int from_hex (int ch); + int to_hex (int code); +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HTTPD/HTTPD_req.cpp Thu Jun 15 20:29:03 2017 +0000 @@ -0,0 +1,332 @@ +/* Copyright (C) 2013 Hiroshi Suga, MIT License + * + * 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 "HTTPD.h" + + +int HTTPD::httpdFile (int id, char *dir) { + FILE *fp; + int i, len; + char buf[HTTPD_BUF_SIZE]; + char file[HTTPD_CMD_SIZE]; + + INFO("httpdFile %d %s", id, dir); + + strcpy(file, dir); + strcat(file, _state[id].filename); + if (file[strlen(file) - 1] == '/') { + strcat(file, "index.html"); + } + DBG("file: %s\r\n", file); + + fp = fopen(file, "r"); + if (fp) { + strcpy(buf, "HTTP/1.1 200 OK\r\n"); + _state[id].client->send(buf, strlen(buf)); + { + // file size + i = ftell(fp); + fseek(fp, 0, SEEK_END); + len = ftell(fp); + fseek(fp, i, SEEK_SET); + } + + strcpy(buf, "Server: GSwifi httpd\r\n"); + _state[id].client->send(buf, strlen(buf)); + if (_state[id].keepalive) { + strcpy(buf, "Connection: Keep-Alive\r\n"); + } else { + strcpy(buf, "Connection: close\r\n"); + } + _state[id].client->send(buf, strlen(buf)); + sprintf(buf, "Content-Type: %s\r\n", mimetype(file)); + _state[id].client->send(buf, strlen(buf)); + sprintf(buf, "Content-Length: %d\r\n\r\n", len); + _state[id].client->send(buf, strlen(buf)); + + for (;;) { + i = fread(buf, sizeof(char), sizeof(buf), fp); + if (i <= 0) break; + _state[id].client->send(buf, i); +#if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) + if (feof(fp)) break; +#endif + } + fclose(fp); + return 0; + } + + httpdError(id, 404); + return -1; +} + +void HTTPD::httpdError (int id, int err) { + char buf[HTTPD_CMD_SIZE], msg[30]; + + switch (err) { + case 400: + strcpy(msg, "Bad Request"); + break; + case 403: + strcpy(msg, "Forbidden"); + break; + case 404: + strcpy(msg, "Not Found"); + break; + case 500: + default: + strcpy(msg, "Internal Server Error"); + break; + } + DBG("httpd error: %d %d %s\r\n", id, err, msg); + + sprintf(buf, "HTTP/1.1 %d %s\r\n", err, msg); + _state[id].client->send(buf, strlen(buf)); + strcpy(buf, "Content-Type: text/html\r\n\r\n"); + _state[id].client->send(buf, strlen(buf)); + + sprintf(buf, "<html><head><title>%d %s</title></head>\r\n", err, msg); + _state[id].client->send(buf, strlen(buf)); + sprintf(buf, "<body><h1>%s</h1></body></html>\r\n", msg); + _state[id].client->send(buf, strlen(buf)); + wait_ms(100); + _state[id].client->close(); + //WARN("%d.%d.%d.%d ", _httpd[cid].host.getIp()[0], _httpd[cid].host.getIp()[1], _httpd[cid].host.getIp()[2], _httpd[cid].host.getIp()[3]); + //WARN("%s %s %d %d -\r\n", _httpd[cid].type == GSPROT_HTTPGET ? "GET" : "POST", _httpd[cid].uri, _httpd[cid].length, err); +} + +void HTTPD::recvData (int id, char c) { + + switch (_state[id].mode) { + case MODE_REQUEST: + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) { + _state[id].buf->flush(); + _state[id].buf->queue(c); + _state[id].mode = MODE_REQSTR; + } else { + _state[id].buf->flush(); + } + break; + case MODE_REQSTR: + switch (c) { + case 0: + break; + case 0x0a: // LF + case 0x0d: // CR + if (parseRequest(id)) { + _state[id].mode = MODE_REQSTR; + } else { + _state[id].mode = MODE_HEADER; + } + _state[id].enter = 0; + break; + default: + _state[id].buf->queue(c); + break; + } + break; + case MODE_HEADER: + switch (c) { + case 0: + break; + case 0x0a: // LF + case 0x0d: // CR + if (_state[id].buf->available() == 0) { + if ((_state[id].enter == 0x0d && c == 0x0a) || (_state[id].enter == 0x0a && c == 0x0a)) { + _state[id].buf->flush(); + if (_state[id].websocket) { + INFO("MODE_WEBSOCKET"); + acceptWebsocket(id); + _state[id].mode = MODE_WEBSOCKET; + } else + if (_state[id].length) { + INFO("MODE_BODY"); + _state[id].mode = MODE_BODY; + } else { + INFO("MODE_ENTER"); + _state[id].mode = MODE_ENTER; + } + } + _state[id].enter = c; + _state[id].buf->flush(); + break; + } + + parseHeader(id); + _state[id].enter = 0; + break; + default: + _state[id].buf->queue(c); + _state[id].enter = 0; + break; + } + break; + case MODE_BODY: + _state[id].buf->queue(c); + if (_state[id].buf->available() >= _state[id].length) { + _state[id].mode = MODE_ENTER; + } + break; + case MODE_WEBSOCKET: + case MODE_WEBSOCKET_MASK: + case MODE_WEBSOCKET_BODY: + recvWS(id, c); + break; + } + + if (_state[id].mode == MODE_ENTER) { + int i = getHandler(_state[id].uri); + printf("handler = %d, uri = %s\r\n", i, _state[id].uri); + if (i >= 0) { + if (_handler[i].dir) { + // file + httpdFile(id, _handler[i].dir); + } else if (_handler[i].funcCgi) { + // cgi + _handler[i].funcCgi(id); + _state[id].keepalive = 0; + } else { + httpdError(id, 403); + } + } else { + httpdError(id, 404); + } + + if (_state[id].keepalive) { + DBG("keepalive %d", _state[id].keepalive); + _state[id].keepalive--; + } else { + _state[id].client->close(); + } + _state[id].mode = MODE_REQUEST; + } else + if (_state[id].mode == MODE_WEBSOCKET_ENTER) { + parseWebsocket(id); + _state[id].mode = MODE_WEBSOCKET; + } +} + +int HTTPD::parseRequest (int id) { + int i, j, len; + char buf[HTTPD_CMD_SIZE]; + + for (len = 0; len < sizeof(buf); len++) { + if (_state[id].buf->dequeue(&buf[len]) == false) break; + } + buf[len] = 0; + + if (strnicmp(buf, "GET ", 4) == 0) { + _state[id].req = REQ_HTTPGET; + j = 4; + } else + if (strnicmp(buf, "POST ", 5) == 0) { + _state[id].req = REQ_HTTPPOST; + j = 5; + } else { + return -1; + } + + for (i = 0; i < len - j; i ++) { + _state[id].uri[i] = buf[i + j]; + if (buf[i + j] == ' ' || i >= sizeof(buf) - 1) { + _state[id].uri[i] = 0; + INFO("URI %d '%s'", _state[id].req, _state[id].uri); + _state[id].mode = MODE_HEADER; + _state[id].buf->flush(); + _state[id].length = 0; + _state[id].n = 0; + _state[id].websocket = 0; + _state[id].filename = NULL; + _state[id].querystring = NULL; + break; + } + } + + i = getHandler(_state[id].uri); + if (i >= 0) { + _state[id].filename = &_state[id].uri[strlen(_handler[i].uri)]; + for (i = 0; i < strlen(_state[id].filename); i ++) { + if (_state[id].filename[i] == '?') { + _state[id].filename[i] = 0; + _state[id].querystring = _state[id].filename + i + 1; + break; + } + } + INFO("FILE '%s' QUERY '%s'", _state[id].filename, _state[id].querystring); + } + return 0; +} + +#define HEADER_TABLE_NUM 5 +int HTTPD::parseHeader (int id) { + int i; + char buf[HTTPD_CMD_SIZE]; + static const struct HEADER_TABLE { + const char header[24]; + void (HTTPD::*func)(int id, const char*); + } header_table[HEADER_TABLE_NUM] = { + {"Content-Length:", &HTTPD::reqContentLength}, + {"Connection:", &HTTPD::reqConnection}, + {"Upgrade: websocket", &HTTPD::reqUpgrade}, + {"Sec-WebSocket-Version:", &HTTPD::reqWebSocketVersion}, + {"Sec-WebSocket-Key:", &HTTPD::reqWebSocketKey}, + }; + for (i = 0; i < sizeof(buf); i++) { + if (_state[id].buf->dequeue(&buf[i]) == false) break; + } + buf[i] = 0; + + for (i = 0; i < HEADER_TABLE_NUM; i ++) { + if (strnicmp(buf, header_table[i].header, strlen(header_table[i].header)) == 0) { + DBG("parse header %d '%s'\r\n", i, buf); + if (header_table[i].func != NULL) { + (this->*(header_table[i].func))(id, buf); + } + return 0; + } + } + + return -1; +} + +void HTTPD::reqContentLength (int id, const char *buf) { + _state[id].length = atoi(&buf[16]); +} + +void HTTPD::reqConnection (int id, const char *buf) { + if (strnicmp(&buf[12], "Keep-Alive", 10) == 0 && _state[id].keepalive == 0) { + _state[id].keepalive = HTTPD_KEEPALIVE; + } else { + _state[id].keepalive = 0; + } +} + +void HTTPD::reqUpgrade (int id, const char *buf) { + if (! _state[id].websocket) _state[id].websocket = 1; +} + +void HTTPD::reqWebSocketVersion (int id, const char *buf) { + _state[id].websocket = atoi(&buf[23]); +} + +void HTTPD::reqWebSocketKey (int id, const char *buf) { + if (_state[id].websocket_key == NULL) { + _state[id].websocket_key = (char*)malloc(30); + } + strncpy(_state[id].websocket_key, &buf[19], 30); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HTTPD/HTTPD_util.cpp Thu Jun 15 20:29:03 2017 +0000 @@ -0,0 +1,256 @@ +/* Copyright (C) 2013 Hiroshi Suga, MIT License + * + * 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 "HTTPD.h" +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +#define MIMETABLE_NUM 9 +static const struct { + char ext[5]; + char type[24]; +} mimetable[MIMETABLE_NUM] = { + {"txt", "text/plain"}, // default + {"html", "text/html"}, + {"htm", "text/html"}, + {"css", "text/css"}, + {"js", "application/javascript"}, + {"jpg", "image/jpeg"}, + {"png", "image/png"}, + {"gif", "image/gif"}, + {"ico", "image/x-icon"}, +}; + +char *HTTPD::getUri (int id) { + return _state[id].uri; +} + +char *HTTPD::getFilename (int id) { + return _state[id].filename; +} + +char *HTTPD::getQueryString (int id) { + return _state[id].querystring; +} + +int HTTPD::receive (int id, char *buf, int len) { + int i; + + for (i = 0; i < len; i ++) { + if (_state[id].buf->dequeue(&buf[i]) == false) break; + } + return i; +} + +int HTTPD::send (int id, const char *body, int len, const char *header) { + char buf[HTTPD_CMD_SIZE]; + + strcpy(buf, "HTTP/1.1 200 OK\r\n"); + _state[id].client->send(buf, strlen(buf)); + strcpy(buf, "Server: GSwifi httpd\r\n"); + _state[id].client->send(buf, strlen(buf)); + if (_state[id].keepalive) { + strcpy(buf, "Connection: Keep-Alive\r\n"); + } else { + strcpy(buf, "Connection: close\r\n"); + } + _state[id].client->send(buf, strlen(buf)); + if (header) { + _state[id].client->send((char*)header, strlen(header)); + } + sprintf(buf, "Content-Length: %d\r\n\r\n", len); + _state[id].client->send(buf, strlen(buf)); + + return _state[id].client->send((char*)body, len); +} + +int HTTPD::sendstr (int id, const char *buf) { + return _state[id].client->send((char*)buf, strlen(buf)); +} + + +int HTTPD::hprintf(int id, const char* format, ...) { + //FIX ME: This could result in memory overruns if the buffer size is exceeded + static Mutex _mtx; + static char _buf[1024]; + + std::va_list arg; + + _mtx.lock(); + + va_start(arg, format); + vsprintf(_buf, format, arg); + va_end(arg); + + int r = _state[id].client->send(_buf, strlen(_buf)); + + _mtx.unlock(); + return r; +} + +int HTTPD::getHandler (const char *uri) { + int i; + + for (i = 0; i < _handler_count; i ++) { + if (strncmp(uri, _handler[i].uri, strlen(_handler[i].uri)) == NULL) { + // found + return i; + } + } + return -1; +} + +int HTTPD::attach (const char *uri, const char *dir) { + if (_handler_count < HTTPD_MAX_HANDLES) { + _handler[_handler_count].uri = (char*)malloc(strlen(uri) + 1); + strcpy(_handler[_handler_count].uri, uri); + _handler[_handler_count].dir = (char*)malloc(strlen(dir) + 1); + strcpy(_handler[_handler_count].dir, dir); + _handler[_handler_count].funcCgi = NULL; + _handler_count ++; + return 0; + } else { + return -1; + } +} + +int HTTPD::attach (const char *uri, void (*funcCgi)(int)) { + if (_handler_count < HTTPD_MAX_HANDLES) { + _handler[_handler_count].uri = (char*)malloc(strlen(uri) + 1); + strcpy(_handler[_handler_count].uri, uri); + _handler[_handler_count].dir = NULL; + _handler[_handler_count].funcCgi = funcCgi; + _handler_count ++; + return 0; + } else { + return -1; + } +} + + +char *HTTPD::mimetype (char *file) { + int i, j; + + for (i = 0; i < MIMETABLE_NUM; i ++) { + j = strlen(mimetable[i].ext); + if (file[strlen(file) - j - 1] == '.' && + strnicmp(&file[strlen(file) - j], mimetable[i].ext, j) == NULL) { + return (char*)mimetable[i].type; + } + } + return (char*)mimetable[0].type; +} + +int HTTPD::strnicmp (const char *p1, const char *p2, int n) { + int i, r = -1; + char c1, c2; + + for (i = 0; i < n; i ++) { + c1 = (p1[i] >= 'a' && p1[i] <= 'z') ? p1[i] - ('a' - 'A'): p1[i]; + c2 = (p2[i] >= 'a' && p2[i] <= 'z') ? p2[i] - ('a' - 'A'): p2[i]; + r = c1 - c2; + if (r) break; + } + return r; +} + + +/* base64encode code from + * Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com) + */ +int HTTPD::base64encode (const char *input, int length, char *output, int len) { + static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + unsigned int c, c1, c2, c3; + + if (len < ((((length-1)/3)+1)<<2)) return -1; + for(unsigned int i = 0, j = 0; i<length; i+=3,j+=4) { + c1 = ((((unsigned char)*((unsigned char *)&input[i])))); + c2 = (length>i+1)?((((unsigned char)*((unsigned char *)&input[i+1])))):0; + c3 = (length>i+2)?((((unsigned char)*((unsigned char *)&input[i+2])))):0; + + c = ((c1 & 0xFC) >> 2); + output[j+0] = base64[c]; + c = ((c1 & 0x03) << 4) | ((c2 & 0xF0) >> 4); + output[j+1] = base64[c]; + c = ((c2 & 0x0F) << 2) | ((c3 & 0xC0) >> 6); + output[j+2] = (length>i+1)?base64[c]:'='; + c = (c3 & 0x3F); + output[j+3] = (length>i+2)?base64[c]:'='; + } + output[(((length-1)/3)+1)<<2] = '\0'; + return 0; +} + + +/* urlencode code from + * Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com) + */ +int HTTPD::urlencode (const char *str, char *buf, int len) { +// char *pstr = str, *buf = (char*)malloc(strlen(str) * 3 + 1), *pbuf = buf; + const char *pstr = str; + char *pbuf = buf; + + if (len < (strlen(str) * 3 + 1)) return -1; + while (*pstr) { + if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~') + *pbuf++ = *pstr; + else if (*pstr == ' ') + *pbuf++ = '+'; + else + *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15); + pstr++; + } + *pbuf = '\0'; + return 0; +} + +/* urldecode code from + * Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com) + */ +int HTTPD::urldecode (const char *str, char *buf, int len) { +// char *pstr = str, *buf = (char*)malloc(strlen(str) + 1), *pbuf = buf; + const char *pstr = str; + char *pbuf = buf; + + if (len < (strlen(str) / 3 - 1)) return -1; + while (*pstr) { + if (*pstr == '%') { + if (pstr[1] && pstr[2]) { + *pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]); + pstr += 2; + } + } else if (*pstr == '+') { + *pbuf++ = ' '; + } else { + *pbuf++ = *pstr; + } + pstr++; + } + *pbuf = '\0'; + return 0; +} + +int HTTPD::from_hex (int ch) { + return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10; +} + +int HTTPD::to_hex (int code) { + static char hex[] = "0123456789abcdef"; + return hex[code & 15]; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HTTPD/HTTPD_ws.cpp Thu Jun 15 20:29:03 2017 +0000 @@ -0,0 +1,198 @@ +/* Copyright (C) 2013 Hiroshi Suga, MIT License + * + * 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 "HTTPD.h" +#include "sha1.h" + +void HTTPD::recvWS (int id, char c) { + + switch (_state[id].mode) { + case MODE_WEBSOCKET: + if (_state[id].n == 0) { + // flag + _state[id].websocket_opcode = c & 0x0f; + _state[id].websocket_flg = c << 8; + _state[id].n ++; + } else + if (_state[id].n == 1) { + // length 7bit + _state[id].websocket_flg |= c; + _state[id].length = c & 0x7f; + _state[id].n ++; + if (_state[id].length < 126) { + _state[id].n = 0; + if (_state[id].length) { + if (_state[id].websocket_flg & 0x0080) { + _state[id].mode = MODE_WEBSOCKET_MASK; + } else { + _state[id].mode = MODE_WEBSOCKET_BODY; + } + } else { + _state[id].mode = MODE_WEBSOCKET_ENTER; + } + DBG("ws length %d\r\n", _state[id].length); + } + } else { + // length 16bit,64bit + if (_state[id].n == 2) { + _state[id].length = c; + _state[id].n ++; + } else + if (_state[id].n < 9 && (_state[id].websocket_flg & 0x7f) == 127) { + // 64bit + _state[id].length = (_state[id].length << 8) | c; + _state[id].n ++; + } else { + // end + _state[id].length = (_state[id].length << 8) | c; + _state[id].n = 0; + if (_state[id].websocket_flg & 0x0080) { + _state[id].mode = MODE_WEBSOCKET_MASK; + } else { + _state[id].mode = MODE_WEBSOCKET_BODY; + } + DBG("ws length2 %d\r\n", _state[id].length); + } + } + break; + case MODE_WEBSOCKET_MASK: + // masking key + _state[id].websocket_mask[_state[id].n] = c; + _state[id].n ++; + if (_state[id].n >= 4) { + _state[id].n = 0; + _state[id].mode = MODE_WEBSOCKET_BODY; + DBG("ws mask\r\n"); + } + break; + case MODE_WEBSOCKET_BODY: + // payload + if (_state[id].websocket_flg & 0x0080) { + // un-mask + _state[id].buf->queue(c ^ _state[id].websocket_mask[_state[id].n & 0x03]); + } else { + _state[id].buf->queue(c); + } + _state[id].n ++; + if (_state[id].n >= _state[id].length) { + _state[id].mode = MODE_WEBSOCKET_ENTER; + } + break; + } +} + +int HTTPD::parseWebsocket (int id) { + int i; + + DBG("ws opcode %d\r\n", _state[id].websocket_opcode); + switch (_state[id].websocket_opcode) { + case 0x00: // continuation + break; + case 0x01: // text + case 0x02: // binary + i = getHandler(_state[id].uri); + if (i >= 0) { + if (_handler[i].funcCgi) { + // cgi + _handler[i].funcCgi(id); + } + } + break; + case 0x08: // close + _state[id].client->close(); + break; + case 0x09: // ping + { + char pong[_state[id].n + 2]; + pong[0] = 0x8a; + pong[1] = 0x04; + for (i = 0; i < _state[id].length; i ++) { + if (_state[id].buf->dequeue(&pong[i + 2]) == false) break; + } + _state[id].client->send(pong, _state[id].length + 2); + } + break; + case 0x0a: // pong + break; + default: + break; + } + _state[id].n = 0; + _state[id].length = 0; + return 0; +} + +int HTTPD::acceptWebsocket (int id) { + char buf[HTTPD_CMD_SIZE], buf2[HTTPD_CMD_SIZE]; + + DBG("websocket accept: %d\r\n", id); + + strcpy(buf, "HTTP/1.1 101 Switching Protocols\r\n"); + _state[id].client->send(buf, strlen(buf)); + strcpy(buf, "Upgrade: websocket\r\n"); + _state[id].client->send(buf, strlen(buf)); + strcpy(buf, "Connection: Upgrade\r\n"); + _state[id].client->send(buf, strlen(buf)); + + strcpy(buf, "Sec-WebSocket-Accept: "); + _state[id].client->send(buf, strlen(buf)); + strcpy(buf, _state[id].websocket_key); + strcat(buf, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); + sha1(buf, strlen(buf), buf2); + base64encode(buf2, 20, buf, sizeof(buf)); + _state[id].client->send(buf, strlen(buf)); + strcpy(buf, "\r\n\r\n"); + _state[id].client->send(buf, strlen(buf)); + //_state[id].client->set_blocking(true, HTTPD_TIMEOUT * 100); + return 0; +} + +int HTTPD::sendWebsocket (int id, const char *buf, int len, const char *mask) { + HTTPD *httpd = HTTPD::getInstance(); + int i = 0, r; + char tmp[10]; + + tmp[i++] = 0x81; // single, text frame + if (len < 126) { + tmp[i++] = (mask == NULL ? 0 : 0x80) | len; + } else { + tmp[i++] = (mask == NULL ? 0 : 0x80) | 126; + tmp[i++] = (len >> 8) & 0xff; + tmp[i++] = len & 0xff; + } + if (mask) { + memcpy(&tmp[i], mask, 4); + i += 4; + } + r = httpd->_state[id].client->send(tmp, i); + + if (r >= 0) { + if (mask) { + char tmp2[len]; + for (i = 0; i < len; i ++) { + tmp2[i] = buf[i] ^ mask[i & 0x03]; + } + r = httpd->_state[id].client->send(tmp2, len); + } else { + r = httpd->_state[id].client->send((char*)buf, len); + } + } + return r; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HTTPD/sha1.cpp Thu Jun 15 20:29:03 2017 +0000 @@ -0,0 +1,364 @@ +/* + * source from http://www.ipa.go.jp/security/rfc/RFC3174JA.html + */ +#include "sha1.h" + +/* + * Define the SHA1 circular left shift macro + */ +#define SHA1CircularShift(bits,word) \ + (((word) << (bits)) | ((word) >> (32-(bits)))) + +/* Local Function Prototyptes */ +void SHA1PadMessage(SHA1Context *); +void SHA1ProcessMessageBlock(SHA1Context *); + +/* + * SHA1Reset + * + * Description: + * This function will initialize the SHA1Context in preparation + * for computing a new SHA1 message digest. + * + * Parameters: + * context: [in/out] + * The context to reset. + * + * Returns: + * sha Error Code. + * + */ +int SHA1Reset(SHA1Context *context) +{ + if (!context) + { + return shaNull; + } + + context->Length_Low = 0; + context->Length_High = 0; + context->Message_Block_Index = 0; + + context->Intermediate_Hash[0] = 0x67452301; + context->Intermediate_Hash[1] = 0xEFCDAB89; + context->Intermediate_Hash[2] = 0x98BADCFE; + context->Intermediate_Hash[3] = 0x10325476; + context->Intermediate_Hash[4] = 0xC3D2E1F0; + + context->Computed = 0; + context->Corrupted = 0; + + return shaSuccess; +} + +/* + * SHA1Result + * + * Description: + * This function will return the 160-bit message digest into the + * Message_Digest array provided by the caller. + * NOTE: The first octet of hash is stored in the 0th element, + * the last octet of hash in the 19th element. + * + * Parameters: + * context: [in/out] + * The context to use to calculate the SHA-1 hash. + * Message_Digest: [out] + * Where the digest is returned. + * + * Returns: + * sha Error Code. + * + */ +int SHA1Result( SHA1Context *context, + uint8_t Message_Digest[SHA1HashSize]) +{ + int i; + + if (!context || !Message_Digest) + { + return shaNull; + } + + if (context->Corrupted) + { + return context->Corrupted; + } + + if (!context->Computed) + { + SHA1PadMessage(context); + for(i=0; i<64; ++i) + { + /* message may be sensitive, clear it out */ + context->Message_Block[i] = 0; + } + context->Length_Low = 0; /* and clear length */ + context->Length_High = 0; + context->Computed = 1; + } + + for(i = 0; i < SHA1HashSize; ++i) + { + Message_Digest[i] = context->Intermediate_Hash[i>>2] + >> 8 * ( 3 - ( i & 0x03 ) ); + } + + return shaSuccess; +} + +/* + * SHA1Input + * + * Description: + * This function accepts an array of octets as the next portion + * of the message. + * + * Parameters: + * context: [in/out] + * The SHA context to update + * message_array: [in] + * An array of characters representing the next portion of + * the message. + * length: [in] + * The length of the message in message_array + * + * Returns: + * sha Error Code. + * + */ +int SHA1Input( SHA1Context *context, + const uint8_t *message_array, + unsigned length) +{ + if (!length) + { + return shaSuccess; + } + + if (!context || !message_array) + { + return shaNull; + } + + if (context->Computed) + { + context->Corrupted = shaStateError; + return shaStateError; + } + + if (context->Corrupted) + { + return context->Corrupted; + } + while(length-- && !context->Corrupted) + { + context->Message_Block[context->Message_Block_Index++] = + (*message_array & 0xFF); + + context->Length_Low += 8; + if (context->Length_Low == 0) + { + context->Length_High++; + if (context->Length_High == 0) + { + /* Message is too long */ + context->Corrupted = 1; + } + } + + if (context->Message_Block_Index == 64) + { + SHA1ProcessMessageBlock(context); + } + + message_array++; + } + + return shaSuccess; +} + +/* + * SHA1ProcessMessageBlock + * + * Description: + * This function will process the next 512 bits of the message + * stored in the Message_Block array. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + * + * Comments: + * Many of the variable names in this code, especially the + * single character names, were used because those were the + * names used in the publication. + * + * + */ +void SHA1ProcessMessageBlock(SHA1Context *context) +{ + const uint32_t K[] = { /* Constants defined in SHA-1 */ + 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 + }; + int t; /* Loop counter */ + uint32_t temp; /* Temporary word value */ + uint32_t W[80]; /* Word sequence */ + uint32_t A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for(t = 0; t < 16; t++) + { + W[t] = context->Message_Block[t * 4] << 24; + W[t] |= context->Message_Block[t * 4 + 1] << 16; + W[t] |= context->Message_Block[t * 4 + 2] << 8; + W[t] |= context->Message_Block[t * 4 + 3]; + } + + for(t = 16; t < 80; t++) + { + W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = context->Intermediate_Hash[0]; + B = context->Intermediate_Hash[1]; + C = context->Intermediate_Hash[2]; + D = context->Intermediate_Hash[3]; + E = context->Intermediate_Hash[4]; + + for(t = 0; t < 20; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 20; t < 40; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 40; t < 60; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 60; t < 80; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + context->Intermediate_Hash[0] += A; + context->Intermediate_Hash[1] += B; + context->Intermediate_Hash[2] += C; + context->Intermediate_Hash[3] += D; + context->Intermediate_Hash[4] += E; + + context->Message_Block_Index = 0; +} + + +/* + * SHA1PadMessage + * + * Description: + * According to the standard, the message must be padded to an even + * 512 bits. The first padding bit must be a '1'. The last 64 + * bits represent the length of the original message. All bits in + * between should be 0. This function will pad the message + * according to those rules by filling the Message_Block array + * accordingly. It will also call the ProcessMessageBlock function + * provided appropriately. When it returns, it can be assumed that + * the message digest has been computed. + * + * Parameters: + * context: [in/out] + * The context to pad + * ProcessMessageBlock: [in] + * The appropriate SHA*ProcessMessageBlock function + * Returns: + * Nothing. + * + */ + +void SHA1PadMessage(SHA1Context *context) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index > 55) + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 64) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + + SHA1ProcessMessageBlock(context); + + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + else + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + + /* + * Store the message length as the last 8 octets + */ + context->Message_Block[56] = context->Length_High >> 24; + context->Message_Block[57] = context->Length_High >> 16; + context->Message_Block[58] = context->Length_High >> 8; + context->Message_Block[59] = context->Length_High; + context->Message_Block[60] = context->Length_Low >> 24; + context->Message_Block[61] = context->Length_Low >> 16; + context->Message_Block[62] = context->Length_Low >> 8; + context->Message_Block[63] = context->Length_Low; + + SHA1ProcessMessageBlock(context); +} + +void sha1 (const char *input, int len, char *output) { + SHA1Context sha; + + SHA1Reset(&sha); + SHA1Input(&sha, (unsigned char*)input, len); + SHA1Result(&sha, (uint8_t*)output); + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HTTPD/sha1.h Thu Jun 15 20:29:03 2017 +0000 @@ -0,0 +1,78 @@ +/* + * source from http://www.ipa.go.jp/security/rfc/RFC3174JA.html + */ +/* + * sha1.h + * + * Description: + * This is the header file for code which implements the Secure + * Hashing Algorithm 1 as defined in FIPS PUB 180-1 published + * April 17, 1995. + * + * Many of the variable names in this code, especially the + * single character names, were used because those were the names + * used in the publication. + * + * Please read the file sha1.c for more information. + * + */ + +#ifndef _SHA1_H_ +#define _SHA1_H_ + +#include "mbed.h" +/* + * If you do not have the ISO standard stdint.h header file, then you + * must typdef the following: + * name meaning + * uint32_t unsigned 32 bit integer + * uint8_t unsigned 8 bit integer (i.e., unsigned char) + * int_least16_t integer of >= 16 bits + * + */ + +#ifndef _SHA_enum_ +#define _SHA_enum_ +enum +{ + shaSuccess = 0, + shaNull, /* Null pointer parameter */ + shaInputTooLong, /* input data too long */ + shaStateError /* called Input after Result */ +}; +#endif +#define SHA1HashSize 20 + +/* + * This structure will hold context information for the SHA-1 + * hashing operation + */ +typedef struct SHA1Context +{ + uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */ + + uint32_t Length_Low; /* Message length in bits */ + uint32_t Length_High; /* Message length in bits */ + + /* Index into message block array */ + int_least16_t Message_Block_Index; + uint8_t Message_Block[64]; /* 512-bit message blocks */ + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the message digest corrupted? */ +} SHA1Context; + +/* + * Function Prototypes + */ + +int SHA1Reset( SHA1Context *); +int SHA1Input( SHA1Context *, + const uint8_t *, + unsigned int); +int SHA1Result( SHA1Context *, + uint8_t Message_Digest[SHA1HashSize]); + + +void sha1 (const char *input, int len, char *output); +#endif
--- a/SNTPClient.lib Thu Jun 15 20:25:40 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -https://developer.mbed.org/teams/AMETEK-Powervar/code/SNTPClient/#a5a91530712d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SNTPClient/SNTPClient.cpp Thu Jun 15 20:29:03 2017 +0000 @@ -0,0 +1,447 @@ +#include "SNTPClient.h" +#include "DNSClient.h" + +#define MAX_TRY_WRITE 20 +#define MAX_TRY_READ 10 + +//Debug is disabled by default +#ifdef _SNTP_DEBUG_ +#define DBG(x, ...) std::printf("[SNTPClient : DBG]"x"\r\n", ##__VA_ARGS__); +#define WARN(x, ...) std::printf("[SNTPClient : WARN]"x"\r\n", ##__VA_ARGS__); +#define ERR(x, ...) std::printf("[SNTPClient : ERR]"x"\r\n", ##__VA_ARGS__); +#else +#define DBG(x, ...) +#define WARN(x, ...) +#define ERR(x, ...) +#endif + +#define INFO(x, ...) printf("[SNTPClient : INFO]"x"\r\n", ##__VA_ARGS__); + +SNTPClient::SNTPClient(NetworkStack *ns, const char* url, uint8_t time_zone) { + DNSClient dns(ns); + + if (dns.lookup(url)) { + const char* dns_addr = dns.get_ip_address(); + memcpy(host, dns_addr, strlen(dns_addr)); + host[strlen(dns_addr)] = '\0'; + } else { + memcpy(host, url, strlen(url)); + host[strlen(url)] = '\0'; + } + + port = ntp_port; + m_ns = ns; + m_udp = NULL; + tz = time_zone; +} + +bool SNTPClient::connect() { + if (m_udp == NULL) { + m_udp = new UDPSocket; + m_udp->open(m_ns); + } + + m_udp->set_blocking(false); + m_udp->set_timeout(3000); + m_udp->bind(rand()&0x7fff); + + sntp_server.set_ip_address(host); + sntp_server.set_port(port); + + nsapi_addr_t na = sntp_server.get_addr(); + NTPformat.dstaddr[0] = na.bytes[0]; + NTPformat.dstaddr[1] = na.bytes[1]; + NTPformat.dstaddr[2] = na.bytes[2]; + NTPformat.dstaddr[3] = na.bytes[3]; + DBG("NTP Server: %s\r\n", sntp_server.get_ip_address()); + + uint8_t Flag; + NTPformat.leap = 0; /* leap indicator */ + NTPformat.version = 4; /* version number */ + NTPformat.mode = 3; /* mode */ + NTPformat.stratum = 0; /* stratum */ + NTPformat.poll = 0; /* poll interval */ + NTPformat.precision = 0; /* precision */ + NTPformat.rootdelay = 0; /* root delay */ + NTPformat.rootdisp = 0; /* root dispersion */ + NTPformat.refid = 0; /* reference ID */ + NTPformat.reftime = 0; /* reference time */ + NTPformat.org = 0; /* origin timestamp */ + NTPformat.rec = 0; /* receive timestamp */ + NTPformat.xmt = 1; /* transmit timestamp */ + + Flag = (NTPformat.leap<<6)+(NTPformat.version<<3)+NTPformat.mode; //one byte Flag + memcpy(ntpmessage,(void const*)(&Flag),1); + + return true; +} + +bool SNTPClient::getTime(datetime *time) { + if (!m_udp) return false; + + uint16_t startindex = 40; //last 8-byte of data_buf[size is 48 byte] is xmt, so the startindex should be 40 + + int n = m_udp->sendto(sntp_server, (char *)ntpmessage, sizeof(ntpmessage)); + + char in_buffer[MAX_SNTP_BUF_SIZE]; + n = m_udp->recvfrom(&sntp_server, in_buffer, sizeof(in_buffer)); + + if(n <= 0) { + return false; + } + + get_seconds_from_ntp_server((uint8_t *)in_buffer,startindex); + + time->yy = Nowdatetime.yy; + time->mo = Nowdatetime.mo; + time->dd = Nowdatetime.dd; + time->hh = Nowdatetime.hh; + time->mm = Nowdatetime.mm; + time->ss = Nowdatetime.ss; + + return true; +} + +bool SNTPClient::close() { + if (m_udp) { + delete m_udp; + m_udp = NULL; + } + return true; +} + +char* SNTPClient::getHost() { + return host; +} + +/* +00)UTC-12:00 Baker Island, Howland Island (both uninhabited) +01) UTC-11:00 American Samoa, Samoa +02) UTC-10:00 (Summer)French Polynesia (most), United States (Aleutian Islands, Hawaii) +03) UTC-09:30 Marquesas Islands +04) UTC-09:00 Gambier Islands;(Summer)United States (most of Alaska) +05) UTC-08:00 (Summer)Canada (most of British Columbia), Mexico (Baja California) +06) UTC-08:00 United States (California, most of Nevada, most of Oregon, Washington (state)) +07) UTC-07:00 Mexico (Sonora), United States (Arizona); (Summer)Canada (Alberta) +08) UTC-07:00 Mexico (Chihuahua), United States (Colorado) +09) UTC-06:00 Costa Rica, El Salvador, Ecuador (Galapagos Islands), Guatemala, Honduras +10) UTC-06:00 Mexico (most), Nicaragua;(Summer)Canada (Manitoba, Saskatchewan), United States (Illinois, most of Texas) +11) UTC-05:00 Colombia, Cuba, Ecuador (continental), Haiti, Jamaica, Panama, Peru +12) UTC-05:00 (Summer)Canada (most of Ontario, most of Quebec) +13) UTC-05:00 United States (most of Florida, Georgia, Massachusetts, most of Michigan, New York, North Carolina, Ohio, Washington D.C.) +14) UTC-04:30 Venezuela +15) UTC-04:00 Bolivia, Brazil (Amazonas), Chile (continental), Dominican Republic, Canada (Nova Scotia), Paraguay, +16) UTC-04:00 Puerto Rico, Trinidad and Tobago +17) UTC-03:30 Canada (Newfoundland) +18) UTC-03:00 Argentina; (Summer) Brazil (Brasilia, Rio de Janeiro, Sao Paulo), most of Greenland, Uruguay +19) UTC-02:00 Brazil (Fernando de Noronha), South Georgia and the South Sandwich Islands +20) UTC-01:00 Portugal (Azores), Cape Verde +21) UTC±00:00 Cote d'Ivoire, Faroe Islands, Ghana, Iceland, Senegal; (Summer) Ireland, Portugal (continental and Madeira) +22) UTC±00:00 Spain (Canary Islands), Morocco, United Kingdom +23) UTC+01:00 Angola, Cameroon, Nigeria, Tunisia; (Summer)Albania, Algeria, Austria, Belgium, Bosnia and Herzegovina, +24) UTC+01:00 Spain (continental), Croatia, Czech Republic, Denmark, Germany, Hungary, Italy, Kinshasa, Kosovo, +25) UTC+01:00 Macedonia, France (metropolitan), the Netherlands, Norway, Poland, Serbia, Slovakia, Slovenia, Sweden, Switzerland +26) UTC+02:00 Libya, Egypt, Malawi, Mozambique, South Africa, Zambia, Zimbabwe, (Summer)Bulgaria, Cyprus, Estonia, +27) UTC+02:00 Finland, Greece, Israel, Jordan, Latvia, Lebanon, Lithuania, Moldova, Palestine, Romania, Syria, Turkey, Ukraine +28) UTC+03:00 Belarus, Djibouti, Eritrea, Ethiopia, Iraq, Kenya, Madagascar, Russia (Kaliningrad Oblast), Saudi Arabia, +29) UTC+03:00 South Sudan, Sudan, Somalia, South Sudan, Tanzania, Uganda, Yemen +30) UTC+03:30 (Summer)Iran +31) UTC+04:00 Armenia, Azerbaijan, Georgia, Mauritius, Oman, Russia (European), Seychelles, United Arab Emirates +32) UTC+04:30 Afghanistan +33) UTC+05:00 Kazakhstan (West), Maldives, Pakistan, Uzbekistan +34) UTC+05:30 India, Sri Lanka +35) UTC+05:45 Nepal +36) UTC+06:00 Kazakhstan (most), Bangladesh, Russia (Ural: Sverdlovsk Oblast, Chelyabinsk Oblast) +37) UTC+06:30 Cocos Islands, Myanmar +38) UTC+07:00 Jakarta, Russia (Novosibirsk Oblast), Thailand, Vietnam +39) UTC+08:00 China, Hong Kong, Russia (Krasnoyarsk Krai), Malaysia, Philippines, Singapore, Taiwan, most of Mongolia, Western Australia +40) UTC+09:00 Korea, East Timor, Russia (Irkutsk Oblast), Japan +41) UTC+09:30 Australia (Northern Territory);(Summer)Australia (South Australia)) +42) UTC+10:00 Russia (Zabaykalsky Krai); (Summer)Australia (New South Wales, Queensland, Tasmania, Victoria) +43) UTC+10:30 Lord Howe Island +44) UTC+11:00 New Caledonia, Russia (Primorsky Krai), Solomon Islands +45) UTC+11:30 Norfolk Island +46) UTC+12:00 Fiji, Russia (Kamchatka Krai);(Summer)New Zealand +47) UTC+12:45 (Summer)New Zealand +48) UTC+13:00 Tonga +49) UTC+14:00 Kiribati (Line Islands) +*/ +void SNTPClient::get_seconds_from_ntp_server(uint8_t *buf, uint16_t idx) +{ + tstamp seconds = 0; + uint8_t i=0; + for (i = 0; i < 4; i++) + { + seconds = (seconds << 8) | buf[idx + i]; + } + switch (tz) // Time Zone + { + case 0: + seconds -= 12*3600; + break; + case 1: + seconds -= 11*3600; + break; + case 2: + seconds -= 10*3600; + break; + case 3: + seconds -= (9*3600+30*60); + break; + case 4: + seconds -= 9*3600; + break; + case 5: + case 6: + seconds -= 8*3600; + break; + case 7: + case 8: + seconds -= 7*3600; + break; + case 9: + case 10: + seconds -= 6*3600; + break; + case 11: + case 12: + case 13: + seconds -= 5*3600; + break; + case 14: + seconds -= (4*3600+30*60); + break; + case 15: + case 16: + seconds -= 4*3600; + break; + case 17: + seconds -= (3*3600+30*60); + break; + case 18: + seconds -= 3*3600; + break; + case 19: + seconds -= 2*3600; + break; + case 20: + seconds -= 1*3600; + break; + case 21: //? + case 22: + break; + case 23: + case 24: + case 25: + seconds += 1*3600; + break; + case 26: + case 27: + seconds += 2*3600; + break; + case 28: + case 29: + seconds += 3*3600; + break; + case 30: + seconds += (3*3600+30*60); + break; + case 31: + seconds += 4*3600; + break; + case 32: + seconds += (4*3600+30*60); + break; + case 33: + seconds += 5*3600; + break; + case 34: + seconds += (5*3600+30*60); + break; + case 35: + seconds += (5*3600+45*60); + break; + case 36: + seconds += 6*3600; + break; + case 37: + seconds += (6*3600+30*60); + break; + case 38: + seconds += 7*3600; + break; + case 39: + seconds += 8*3600; + break; + case 40: + seconds += 9*3600; + break; + case 41: + seconds += (9*3600+30*60); + break; + case 42: + seconds += 10*3600; + break; + case 43: + seconds += (10*3600+30*60); + break; + case 44: + seconds += 11*3600; + break; + case 45: + seconds += (11*3600+30*60); + break; + case 46: + seconds += 12*3600; + break; + case 47: + seconds += (12*3600+45*60); + break; + case 48: + seconds += 13*3600; + break; + case 49: + seconds += 14*3600; + break; + + } + + //calculation for date + calcdatetime(seconds); +} + +void SNTPClient::calcdatetime(tstamp seconds) +{ + uint8_t yf=0; + tstamp n=0,d=0,total_d=0,rz=0; + uint16_t y=0,r=0,yr=0; + signed long long yd=0; + + n = seconds; + total_d = seconds/(SECS_PERDAY); + d=0; + uint32_t p_year_total_sec=SECS_PERDAY*365; + uint32_t r_year_total_sec=SECS_PERDAY*366; + while(n>=p_year_total_sec) + { + if((EPOCH+r)%400==0 || ((EPOCH+r)%100!=0 && (EPOCH+r)%4==0)) + { + n = n -(r_year_total_sec); + d = d + 366; + } + else + { + n = n - (p_year_total_sec); + d = d + 365; + } + r+=1; + y+=1; + + } + + y += EPOCH; + + Nowdatetime.yy = y; + + yd=0; + yd = total_d - d; + + yf=1; + while(yd>=28) + { + + if(yf==1 || yf==3 || yf==5 || yf==7 || yf==8 || yf==10 || yf==12) + { + yd -= 31; + if(yd<0)break; + rz += 31; + } + + if (yf==2) + { + if (y%400==0 || (y%100!=0 && y%4==0)) + { + yd -= 29; + if(yd<0)break; + rz += 29; + } + else + { + yd -= 28; + if(yd<0)break; + rz += 28; + } + } + if(yf==4 || yf==6 || yf==9 || yf==11 ) + { + yd -= 30; + if(yd<0)break; + rz += 30; + } + yf += 1; + + } + Nowdatetime.mo=yf; + yr = total_d-d-rz; + + yr += 1; + + Nowdatetime.dd=yr; + + //calculation for time + seconds = seconds%SECS_PERDAY; + Nowdatetime.hh = seconds/3600; + Nowdatetime.mm = (seconds%3600)/60; + Nowdatetime.ss = (seconds%3600)%60; + +} + +tstamp SNTPClient::changedatetime_to_seconds(void) +{ + tstamp seconds=0; + uint32_t total_day=0; + uint16_t i=0,run_year_cnt=0,l=0; + + l = Nowdatetime.yy;//low + + + for(i=EPOCH;i<l;i++) + { + if((i%400==0) || ((i%100!=0) && (i%4==0))) + { + run_year_cnt += 1; + } + } + + total_day=(l-EPOCH-run_year_cnt)*365+run_year_cnt*366; + + for(i=1;i<=Nowdatetime.mo;i++) + { + if(i==5 || i==7 || i==10 || i==12) + { + total_day += 30; + } + if (i==3) + { + if (l%400==0 && l%100!=0 && l%4==0) + { + total_day += 29; + } + else + { + total_day += 28; + } + } + if(i==2 || i==4 || i==6 || i==8 || i==9 || i==11) + { + total_day += 31; + } + } + + seconds = (total_day+Nowdatetime.dd-1)*24*3600; + seconds += Nowdatetime.ss;//seconds + seconds += Nowdatetime.mm*60;//minute + seconds += Nowdatetime.hh*3600;//hour + + return seconds; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SNTPClient/SNTPClient.h Thu Jun 15 20:29:03 2017 +0000 @@ -0,0 +1,175 @@ +/** +* @author Raphael Kwon +* +* @section LICENSE +* +* Copyright (c) 2014 WIZnet Co., Ltd. +* +* 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. +* +* @section DESCRIPTION +* Simple Network Time Protocol Client +* +*/ + +#ifndef SNTPCLIENT_H +#define SNTPCLIENT_H + +#include "mbed.h" +#include "UDPSocket.h" + +/* + * @brief Define it for Debug & Monitor DNS processing. + * @note + */ +#define _SNTP_DEBUG_ + +#define MAX_SNTP_BUF_SIZE sizeof(ntpformat) ///< maximum size of DNS buffer. */ +#define ntp_port 123 //ntp server port number +#define SECS_PERDAY 86400UL // seconds in a day = 60*60*24 +#define UTC_ADJ_HRS 9 // SEOUL : GMT+9 +#define EPOCH 1900 // NTP start year + +/* for ntpclient */ +typedef signed char s_char; +typedef unsigned long long tstamp; +typedef unsigned int tdist; + +typedef struct _ntpformat +{ + uint8_t dstaddr[4]; /* destination (local) address */ + char version; /* version number */ + char leap; /* leap indicator */ + char mode; /* mode */ + char stratum; /* stratum */ + char poll; /* poll interval */ + s_char precision; /* precision */ + tdist rootdelay; /* root delay */ + tdist rootdisp; /* root dispersion */ + char refid; /* reference ID */ + tstamp reftime; /* reference time */ + tstamp org; /* origin timestamp */ + tstamp rec; /* receive timestamp */ + tstamp xmt; /* transmit timestamp */ +} ntpformat; + +typedef struct _datetime +{ + uint16_t yy; + uint8_t mo; + uint8_t dd; + uint8_t hh; + uint8_t mm; + uint8_t ss; +} datetime; + +/** SNTPClient client Class. + * + * Example (ethernet network): + * @code + * #include "mbed.h" + * #include "EthernetInterface.h" + * #include "SNTPClient.h" + * + * int main() { + * EthernetInterface eth; + * eth.init(); //Use DHCP + * eth.connect(); + * printf("IP Address is %s\n\r", eth.getIPAddress()); + * + * SNTPClient ws("ws://sockets.mbed.org:443/ws/demo/rw"); + * ws.connect(); + * + * while (1) { + * int res = ws.send("SNTPClient Hello World!"); + * + * if (ws.read(recv)) { + * printf("rcv: %s\r\n", recv); + * } + * + * wait(0.1); + * } + * } + * @endcode + */ + + +class SNTPClient +{ + public: + /** + * Constructor + * + * @param url The SNTPClient host + */ + SNTPClient(NetworkStack *ns, const char* url, uint8_t time_zone); + + virtual ~SNTPClient() {close();} + + /** + * Connect to the SNTPClient url + * + *@return true if the connection is established, false otherwise + */ + bool connect(); + + /** + * Read a SNTPClient message + * + * @param message pointer to the string to be read (null if drop frame) + * + * @return true if a SNTPClient frame has been read + */ + bool getTime(datetime *time); + + /** + * Close the SNTPClient connection + * + * @return true if the connection has been closed, false otherwise + */ + bool close(); + + /* + * Accessor: get host from the SNTPClient url + * + * @return host + */ + char* getHost(); + + private: + + uint16_t port; + char host[32]; + + UDPSocket *m_udp; + NetworkStack *m_ns; + + SocketAddress sntp_server; + + ntpformat NTPformat; + datetime Nowdatetime; + uint8_t ntpmessage[48]; + uint8_t tz; // Time Zone + + void get_seconds_from_ntp_server(uint8_t *buf, uint16_t idx); + tstamp changedatetime_to_seconds(void); + void calcdatetime(tstamp seconds); +}; + +#endif
--- a/WebSocketClient.lib Thu Jun 15 20:25:40 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -https://developer.mbed.org/teams/AMETEK-Powervar/code/WebSocketClient/#860d3b8e21f9
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WebSocketClient/Websocket.cpp Thu Jun 15 20:29:03 2017 +0000 @@ -0,0 +1,378 @@ +#include "Websocket.h" +#include "DNSClient.h" + +#define MAX_TRY_WRITE 20 +#define MAX_TRY_READ 10 + +//Debug is disabled by default +#if 1 +#define DBG(x, ...) std::printf("[WebSocket : DBG]"x"\r\n", ##__VA_ARGS__); +#define WARN(x, ...) std::printf("[WebSocket : WARN]"x"\r\n", ##__VA_ARGS__); +#define ERR(x, ...) std::printf("[WebSocket : ERR]"x"\r\n", ##__VA_ARGS__); +#else +#define DBG(x, ...) +#define WARN(x, ...) +#define ERR(x, ...) +#endif + +#define INFO(x, ...) printf("[WebSocket : INFO]"x"\r\n", ##__VA_ARGS__); + +Websocket::Websocket(NetworkStack *ns, char * url) { + m_ns = ns; + fillFields(url); + socket.open(ns); + socket.set_blocking(false); + socket.set_timeout(400); +} + +void Websocket::fillFields(char * url) { + int ret = parseURL(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path)); + if(ret) + { + ERR("URL parsing failed; please use: \"ws://ip-or-domain[:port]/path\""); + return; + } + + if(port == 0) //TODO do handle WSS->443 + { + port = 80; + } + + if(strcmp(scheme, "ws")) + { + ERR("Wrong scheme, please use \"ws\" instead"); + } +} + +int Websocket::parseURL(const char* url, char* scheme, size_t maxSchemeLen, char* host, size_t maxHostLen, uint16_t* port, char* path, size_t maxPathLen) //Parse URL +{ + char* schemePtr = (char*) url; + char* hostPtr = (char*) strstr(url, "://"); + if(hostPtr == NULL) + { + WARN("Could not find host"); + return -1; //URL is invalid + } + + if( maxSchemeLen < hostPtr - schemePtr + 1 ) //including NULL-terminating char + { + WARN("Scheme str is too small (%d >= %d)", maxSchemeLen, hostPtr - schemePtr + 1); + return -1; + } + memcpy(scheme, schemePtr, hostPtr - schemePtr); + scheme[hostPtr - schemePtr] = '\0'; + + hostPtr+=3; + + size_t hostLen = 0; + + char* portPtr = strchr(hostPtr, ':'); + if( portPtr != NULL ) + { + hostLen = portPtr - hostPtr; + portPtr++; + if( sscanf(portPtr, "%hu", port) != 1) + { + WARN("Could not find port"); + return -1; + } + } + else + { + *port=0; + } + char* pathPtr = strchr(hostPtr, '/'); + if( hostLen == 0 ) + { + hostLen = pathPtr - hostPtr; + } + + if( maxHostLen < hostLen + 1 ) //including NULL-terminating char + { + WARN("Host str is too small (%d >= %d)", maxHostLen, hostLen + 1); + return -1; + } + memcpy(host, hostPtr, hostLen); + host[hostLen] = '\0'; + + size_t pathLen; + char* fragmentPtr = strchr(hostPtr, '#'); + if(fragmentPtr != NULL) + { + pathLen = fragmentPtr - pathPtr; + } + else + { + pathLen = strlen(pathPtr); + } + + if( maxPathLen < pathLen + 1 ) //including NULL-terminating char + { + WARN("Path str is too small (%d >= %d)", maxPathLen, pathLen + 1); + return -1; + } + memcpy(path, pathPtr, pathLen); + path[pathLen] = '\0'; + + return 0; +} + + +bool Websocket::connect() { + DNSClient dns(m_ns); + char cmd[200]; + + if (!dns.lookup(host)) { + ERR("Websocket: Unable to resolve hostname (%s)", host); + wait(0.2); + connected = false; + return false; + } + + while (socket.connect(dns.get_ip_address(), port) < 0) { + ERR("Websocket: Unable to connect to (%s) on port (%d)", host, port); + wait(0.2); + connected = false; + return false; + } + + connected = true; + + // sent http header to upgrade to the ws protocol + sprintf(cmd, "GET %s HTTP/1.1\r\n", path); + write(cmd, strlen(cmd)); + + sprintf(cmd, "Host: %s:%d\r\n", host, port); + write(cmd, strlen(cmd)); + + sprintf(cmd, "Upgrade: WebSocket\r\n"); + write(cmd, strlen(cmd)); + + sprintf(cmd, "Connection: Upgrade\r\n"); + write(cmd, strlen(cmd)); + + sprintf(cmd, "Sec-WebSocket-Key: L159VM0TWUzyDxwJEIEzjw==\r\n"); + write(cmd, strlen(cmd)); + + sprintf(cmd, "Sec-WebSocket-Version: 13\r\n\r\n"); + int ret = write(cmd, strlen(cmd)); + if (ret != strlen(cmd)) { + close(); + ERR("Could not send request"); + return false; + } + + ret = read(cmd, 200, 100); + if (ret < 0) { + close(); + ERR("Could not receive answer\r\n"); + return false; + } + + cmd[ret] = '\0'; + DBG("recv: %s\r\n", cmd); + + if ( strstr(cmd, "DdLWT/1JcX+nQFHebYP+rqEx5xI=") == NULL ) { + ERR("Wrong answer from server, got \"%s\" instead\r\n", cmd); + do { + ret = read(cmd, 200, 100); + if (ret < 0) { + ERR("Could not receive answer\r\n"); + return false; + } + cmd[ret] = '\0'; + printf("%s",cmd); + } while (ret > 0); + close(); + return false; + } + + INFO("\r\nhost: %s\r\npath: %s\r\nport: %d\r\n\r\n", host, path, port); + return true; +} + +int Websocket::sendLength(uint32_t len, char * msg) { + + if (len < 126) { + msg[0] = len | (1<<7); + return 1; + } else if (len < 65535) { + msg[0] = 126 | (1<<7); + msg[1] = (len >> 8) & 0xff; + msg[2] = len & 0xff; + return 3; + } else { + msg[0] = 127 | (1<<7); + for (int i = 0; i < 8; i++) { + msg[i+1] = (len >> i*8) & 0xff; + } + return 9; + } +} + +int Websocket::readChar(char * pC, bool block) { + return read(pC, 1, 1); +} + +int Websocket::sendOpcode(uint8_t opcode, char * msg) { + msg[0] = 0x80 | (opcode & 0x0f); + return 1; +} + +int Websocket::sendMask(char * msg) { + for (int i = 0; i < 4; i++) { + msg[i] = 0; + } + return 4; +} + +int Websocket::send(char * str) { + char msg[strlen(str) + 15]; + int idx = 0; + idx = sendOpcode(0x01, msg); + idx += sendLength(strlen(str), msg + idx); + idx += sendMask(msg + idx); + memcpy(msg+idx, str, strlen(str)); + int res = write(msg, idx + strlen(str)); + return res; +} + + +bool Websocket::read(char * message) { + int i = 0; + uint32_t len_msg; + char opcode = 0; + char c; + char mask[4] = {0, 0, 0, 0}; + bool is_masked = false; + Timer tmr; + + // read the opcode + tmr.start(); + while (true) { + if (tmr.read() > 3) { + DBG("timeout ws\r\n"); + return false; + } + + if(!connected) + { + WARN("Connection was closed by server"); + return false; + } + + socket.set_blocking(false); + socket.set_timeout(1); + if (socket.recv(&opcode, 1) != 1) { + socket.set_blocking(false); + socket.set_timeout(2000); + return false; + } + + socket.set_blocking(false); + socket.set_timeout(2000); + + if (opcode == 0x81) + break; + } + DBG("opcode: 0x%X\r\n", opcode); + + readChar(&c); + len_msg = c & 0x7f; + is_masked = c & 0x80; + if (len_msg == 126) { + readChar(&c); + len_msg = c << 8; + readChar(&c); + len_msg += c; + } else if (len_msg == 127) { + len_msg = 0; + for (int i = 0; i < 8; i++) { + readChar(&c); + len_msg += (c << (7-i)*8); + } + } + + if (len_msg == 0) { + return false; + } + DBG("length: %d\r\n", len_msg); + + if (is_masked) { + for (i = 0; i < 4; i++) + readChar(&c); + mask[i] = c; + } + + int nb = read(message, len_msg, len_msg); + if (nb != len_msg) + return false; + + for (i = 0; i < len_msg; i++) { + message[i] = message[i] ^ mask[i % 4]; + } + + message[len_msg] = '\0'; + + return true; +} + +bool Websocket::close() { + if (!is_connected()) + return false; + + int ret = socket.close(); + if (ret < 0) { + ERR("Could not disconnect"); + return false; + } + return true; +} + +bool Websocket::is_connected() { + return connected; +} + +char* Websocket::getPath() { + return path; +} + +int Websocket::write(char * str, int len) { + int res = 0, idx = 0; + + for (int j = 0; j < MAX_TRY_WRITE; j++) { + + if(!connected) + { + WARN("Connection was closed by server"); + break; + } + + if ((res = socket.send(str + idx, len - idx)) == -1) + continue; + + idx += res; + + if (idx == len) + return len; + } + + return (idx == 0) ? -1 : idx; +} + +int Websocket::read(char * str, int len, int min_len) { + int res = 0, idx = 0; + + for (int j = 0; j < MAX_TRY_WRITE; j++) { + + if ((res = socket.recv(str + idx, len - idx)) == -1) + continue; + + idx += res; + + if (idx == len || (min_len != -1 && idx > min_len)) + return idx; + } + + return (idx == 0) ? -1 : idx; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WebSocketClient/Websocket.h Thu Jun 15 20:29:03 2017 +0000 @@ -0,0 +1,144 @@ +/** +* @author Samuel Mokrani +* +* @section LICENSE +* +* Copyright (c) 2011 mbed +* +* 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. +* +* @section DESCRIPTION +* Simple websocket client +* +*/ + +#ifndef WEBSOCKET_H +#define WEBSOCKET_H + +#include "mbed.h" + +/** Websocket client Class. + * + * Example (ethernet network): + * @code + * #include "mbed.h" + * #include "EthernetInterface.h" + * #include "Websocket.h" + * + * int main() { + * EthernetInterface eth; + * eth.init(); //Use DHCP + * eth.connect(); + * printf("IP Address is %s\n\r", eth.getIPAddress()); + * + * Websocket ws("ws://sockets.mbed.org:443/ws/demo/rw"); + * ws.connect(); + * + * while (1) { + * int res = ws.send("WebSocket Hello World!"); + * + * if (ws.read(recv)) { + * printf("rcv: %s\r\n", recv); + * } + * + * wait(0.1); + * } + * } + * @endcode + */ + +class Websocket +{ + public: + /** + * Constructor + * + * @param url The Websocket url in the form "ws://ip_domain[:port]/path" (by default: port = 80) + */ + Websocket(NetworkStack *ns, char * url); + + /** + * Connect to the websocket url + * + *@return true if the connection is established, false otherwise + */ + bool connect(); + + /** + * Send a string according to the websocket format (see rfc 6455) + * + * @param str string to be sent + * + * @returns the number of bytes sent + */ + int send(char * str); + + /** + * Read a websocket message + * + * @param message pointer to the string to be read (null if drop frame) + * + * @return true if a websocket frame has been read + */ + bool read(char * message); + + /** + * To see if there is a websocket connection active + * + * @return true if there is a connection active + */ + bool is_connected(); + + /** + * Close the websocket connection + * + * @return true if the connection has been closed, false otherwise + */ + bool close(); + + /* + * Accessor: get path from the websocket url + * + * @return path + */ + char* getPath(); + + private: + + void fillFields(char * url); + int parseURL(const char* url, char* scheme, size_t maxSchemeLen, char* host, size_t maxHostLen, uint16_t* port, char* path, size_t maxPathLen); //Parse URL + int sendOpcode(uint8_t opcode, char * msg); + int sendLength(uint32_t len, char * msg); + int sendMask(char * msg); + int readChar(char * pC, bool block = true); + + char scheme[8]; + uint16_t port; + char host[32]; + char path[64]; + bool connected; + + TCPSocket socket; + NetworkStack *m_ns; + + int read(char * buf, int len, int min_len = -1); + int write(char * buf, int len); +}; + +#endif