From 9f668f1a8f45db617a165d6616c19e5618350c08 Mon Sep 17 00:00:00 2001 From: Bogdan Marinescu Date: Tue, 5 Apr 2016 17:30:31 +0300 Subject: [PATCH 01/40] Preparing new layout - added net/NetworkSocketAPI Origin: https://developer.mbed.org/teams/NetworkSocketAPI/code/NetworkSocketAPI/ --- DnsQuery/DnsQuery.cpp | 214 ++++++++++++++++++++++++++++++++++++++++++ DnsQuery/DnsQuery.h | 41 ++++++++ EthernetInterface.h | 39 ++++++++ NetworkInterface.cpp | 29 ++++++ NetworkInterface.h | 92 ++++++++++++++++++ Socket.cpp | 130 +++++++++++++++++++++++++ Socket.h | 85 +++++++++++++++++ SocketInterface.h | 83 ++++++++++++++++ TCPSocket.h | 39 ++++++++ UDPSocket.h | 39 ++++++++ WiFiInterface.h | 52 ++++++++++ 11 files changed, 843 insertions(+) create mode 100644 DnsQuery/DnsQuery.cpp create mode 100644 DnsQuery/DnsQuery.h create mode 100644 EthernetInterface.h create mode 100644 NetworkInterface.cpp create mode 100644 NetworkInterface.h create mode 100644 Socket.cpp create mode 100644 Socket.h create mode 100644 SocketInterface.h create mode 100644 TCPSocket.h create mode 100644 UDPSocket.h create mode 100644 WiFiInterface.h diff --git a/DnsQuery/DnsQuery.cpp b/DnsQuery/DnsQuery.cpp new file mode 100644 index 0000000000..8cf6ca367e --- /dev/null +++ b/DnsQuery/DnsQuery.cpp @@ -0,0 +1,214 @@ +/* Copyright (c) 2013 Henry Leinen (henry[dot]leinen [at] online [dot] de) + * + * 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 "DnsQuery.h" +#include +#include + + +#define DNS_COUNT (sizeof DNS_IPS / sizeof DNS_IPS[0]) +const char *DNS_IPS[] = { + "8.8.8.8", + "209.244.0.3", + "84.200.69.80", + "8.26.56.26", + "208.67.222.222" +}; + +static bool isIP(const char *host) +{ + int i; + + for (i = 0; host[i]; i++) { + if (!(host[i] >= '0' && host[i] <= '9') && host[i] != '.') { + return false; + } + } + + // Ending with '.' garuntees host + if (i > 0 && host[i-1] == '.') { + return false; + } + + return true; +} + + +static bool parseRR(uint8_t *resp, int &c, char *adr) +{ + int n = 0; + while((n=resp[c++]) != 0) { + if ((n & 0xc0) != 0) { + // This is a link + c++; + break; + } else { + // skip this segment, not interested in string domain names + c+= n; + } + } + + int TYPE = (((int)resp[c])<<8) + resp[c+1]; + int CLASS = (((int)resp[c+2])<<8) + resp[c+3]; + int RDLENGTH = (((int)resp[c+8])<<8) + resp[c+9]; + + c+= 10; + if ((CLASS == 1) && (TYPE == 1)) { + sprintf(adr,"%d.%d.%d.%d", resp[c], resp[c+1], resp[c+2], resp[c+3]); + c+= RDLENGTH; + return true; + } + c+= RDLENGTH; + + return false; +} + + +static bool resolve(unsigned char *resp, char *ipaddress) +{ + + int ID = (((int)resp[0]) <<8) + resp[1]; + int QR = resp[2] >>7; + int Opcode = (resp[2]>>3) & 0x0F; + int RCODE = (resp[3] & 0x0F); + int ANCOUNT = (((int)resp[6])<<8)+ resp[7]; + + if ((ID != 1) || (QR != 1) || (Opcode != 0) || (RCODE != 0)) { + return false; + } + + int c = 12; + int d; + // Skip domain question + while( (d=resp[c++]) != 0) { + c+=d; + } + c+= 4; // skip QTYPE and QCLASS + + // Here comes the resource record + for (int ans = 0 ; ans < ANCOUNT; ans++) { + if (parseRR(resp, c, ipaddress)) { + return true; + } + } + + return false; +} + + +int32_t dnsQuery(NetworkInterface *iface, const char *host, char *ip) +{ + if (isIP(host)) { + strcpy(ip, host); + return 0; + } + + UDPSocket sock(iface); + int32_t err; + + for (unsigned i = 0; i < DNS_COUNT; i++) { + err = sock.open(DNS_IPS[0], 53); + if (err < 0) { + return err; + } + + err = dnsQuery(&sock, host, ip); + sock.close(); + return err; + } + + sock.close(); + return NS_ERROR_DNS_FAILURE; +} + +int32_t dnsQuery(UDPSocket *socket, const char *hostname, char *ipaddress) +{ + int len = 0; + if (hostname == NULL) { + return false; + } + len = strlen(hostname); + if ((len > 128) || (len == 0)) { + return false; + } + + int packetlen = /* size of HEADER structure */ 12 + /* size of QUESTION Structure */5 + len + 1; + uint8_t *packet = new uint8_t[packetlen]; /* this is the UDP packet to send to the DNS */ + if (packet == NULL) { + return false; + } + + // Fill the header + memset(packet, 0, packetlen); + packet[1] = 1; // ID = 1 + packet[5] = 1; // QDCOUNT = 1 (contains one question) + packet[2] = 1; // recursion requested + + int c = 13; // point to NAME element in question section or request + int cnt = 12; // points to the counter of + packet[cnt] = 0; + for (int i = 0 ; i < len ; i++) { + if (hostname[i] != '.') { + // Copy the character and increment the character counter + packet[cnt]++; + packet[c++] = hostname[i]; + } else { + // Finished with this part, so go to the next + cnt = c++; + packet[cnt] = 0; + } + } + + // Terminate this domain name with a zero entry + packet[c++] = 0; + + // Set QTYPE + packet[c++] = 0; + packet[c++] = 1; + // Set QCLASS + packet[c++] = 0; + packet[c++] = 1; + + + if (socket->send(packet, packetlen) < 0) { + delete packet; + return false; + } + delete packet; + + packet = new uint8_t [1024]; + + // Receive the answer from DNS + int response_length = 0; + response_length = socket->recv(packet, 1024); + + if (response_length > 0 ) { + if (!resolve(packet, ipaddress)) { + delete packet; + return NS_ERROR_DNS_FAILURE; + } + + // cleanup and return + delete packet; + return 0; + } + + delete packet; + return NS_ERROR_DNS_FAILURE; +} + + diff --git a/DnsQuery/DnsQuery.h b/DnsQuery/DnsQuery.h new file mode 100644 index 0000000000..6e1e6265b7 --- /dev/null +++ b/DnsQuery/DnsQuery.h @@ -0,0 +1,41 @@ +/* + * + * 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 __DNSQUERY_H__ +#define __DNSQUERY_H__ + +#include "NetworkInterface.h" +#include "UDPSocket.h" + + +/** Function dnsQuery implements the functionality to query a domain name + * server for an IP-Address of a given hostname. + * @param iface : Network interface to use for DNS resolution. + * @param sock : Previously opened socket to use for DNS resolution. + * @param hostname : The hostname of interest as a string. + * Format must be without http:// or www. IE google.com, mbed.org, etc. + * If a standard IP Address is passed, it will be copied into ip unmodified. + * @param ipaddress : A reference to a IPADDRESS_t object which will receive + * the resolved IP Address of the host in question. + * @returns 0 on succes, NS_DNS_FAILURE if host is not found, + * or a negative value for other errors. + */ +int32_t dnsQuery(NetworkInterface *iface, const char *host, char *ip); +int32_t dnsQuery(UDPSocket *sock, const char *host, char *ip); + + +#endif // __DNSQUERY_H__ diff --git a/EthernetInterface.h b/EthernetInterface.h new file mode 100644 index 0000000000..9caf067294 --- /dev/null +++ b/EthernetInterface.h @@ -0,0 +1,39 @@ +/* EthernetInterface Base Class + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ETHERNET_INTERFACE_H +#define ETHERNET_INTERFACE_H + +#include "NetworkInterface.h" + +/** EthernetInterface class + * Common interface that is shared between ethernet hardware + */ +class EthernetInterface : public NetworkInterface +{ +public: + /** Start the interface + * @return 0 on success + */ + virtual int32_t connect() = 0; + + /** Stop the interface + * @return 0 on success + */ + virtual int32_t disconnect() = 0; +}; + +#endif diff --git a/NetworkInterface.cpp b/NetworkInterface.cpp new file mode 100644 index 0000000000..b5e53140de --- /dev/null +++ b/NetworkInterface.cpp @@ -0,0 +1,29 @@ +/* NetworkInterface Base Class + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NetworkInterface.h" +#include "DnsQuery.h" + + +bool NetworkInterface::isConnected() +{ + return getIPAddress() != 0; +} + +int32_t NetworkInterface::getHostByName(const char *name, char *ip) +{ + return dnsQuery(this, name, ip); +} diff --git a/NetworkInterface.h b/NetworkInterface.h new file mode 100644 index 0000000000..d63750e576 --- /dev/null +++ b/NetworkInterface.h @@ -0,0 +1,92 @@ +/* NetworkInterface Base Class + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NETWORK_INTERFACE_H +#define NETWORK_INTERFACE_H + +#include "SocketInterface.h" +#include "stdint.h" + +/** Maximum storage needed for IP address and MAC addresses + */ +#define NS_IP_SIZE 16 +#define NS_MAC_SIZE 18 + +/** + * @enum ns_error_t + * @brief enum of standardized error codes + */ +enum ns_error_t { + NS_ERROR_WOULD_BLOCK = -3000, /*!< no data is not available but call is non-blocking */ + NS_ERROR_TIMEOUT = -3001, /*!< operation took longer than allowed */ + NS_ERROR_NO_CONNECTION = -3002, /*!< not connected to a network */ + NS_ERROR_NO_SOCKET = -3003, /*!< socket not available for use */ + NS_ERROR_NO_ADDRESS = -3004, /*!< IP address is not known */ + NS_ERROR_NO_MEMORY = -3005, /*!< memory resource not available */ + NS_ERROR_DNS_FAILURE = -3006, /*!< DNS failed to complete successfully */ + NS_ERROR_DHCP_FAILURE = -3007, /*!< DHCP failed to complete successfully */ + NS_ERROR_AUTH_FAILURE = -3008, /*!< connection to access point faield */ + NS_ERROR_DEVICE_ERROR = -3009 /*!< failure interfacing with the network procesor */ +}; + +/** NetworkInterface class + * Common interface that is shared between all hardware that + * can connect to a network over IP. + */ +class NetworkInterface +{ +public: + virtual ~NetworkInterface() {}; + + /** Get the IP address + * @return IP address of the interface or 0 if not yet connected + */ + virtual const char *getIPAddress() = 0; + + /** Get the current MAC address + * @return String MAC address of the interface + */ + virtual const char *getMACAddress() = 0; + + /** Get the current status of the interface + * @return true if connected + */ + virtual bool isConnected(); + + /** Looks up the specified host's IP address + * @param name URL of host + * @param ip Buffer to hold IP address, must be at least SOCK_IP_SIZE + * @return 0 on success + */ + virtual int32_t getHostByName(const char *name, char *ip); + +protected: + friend class Socket; + + /** Internally create a socket + * @param proto The type of socket to open, NS_TCP or NS_UDP + * @return The allocated socket + */ + virtual SocketInterface *createSocket(ns_protocol_t proto) = 0; + + /** Internally destroy a socket + * @param socket An allocated SocketInterface + * @returns 0 on success + */ + virtual void destroySocket(SocketInterface *socket) = 0; +}; + +#endif diff --git a/Socket.cpp b/Socket.cpp new file mode 100644 index 0000000000..92618b7fbb --- /dev/null +++ b/Socket.cpp @@ -0,0 +1,130 @@ +/* Socket + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Socket.h" +#include + +Socket::Socket(NetworkInterface *iface, ns_protocol_t proto) + : _iface(iface) + , _proto(proto) + , _socket(0) +{ + memset(_ip_address, 0, NS_IP_SIZE); + _port = 0; +} + +Socket::~Socket() +{ + if (_socket) { + close(); + } +} + + +int32_t Socket::open(const char *address, uint16_t port) +{ + int32_t err; + + err = close(); + if (err) { + return err; + } + + err = _iface->getHostByName(address, _ip_address); + _port = port; + if (err) { + return err; + } + + _socket = _iface->createSocket(_proto); + if (!_socket) { + return NS_ERROR_NO_SOCKET; + } + + err = _socket->open(_ip_address, _port); + + if (err) { + _iface->destroySocket(_socket); + _socket = 0; + } + + return err; +} + +int32_t Socket::close() +{ + if (!_socket) { + return 0; + } + + SocketInterface *socket = _socket; + _socket = 0; + + int32_t err = socket->close(); + if (!err) { + _iface->destroySocket(socket); + } + + return err; +} + +int32_t Socket::send(const void *data, uint32_t size) +{ + if (!_socket) { + return NS_ERROR_NO_CONNECTION; + } + return _socket->send(data, size); +} + +int32_t Socket::recv(void *data, uint32_t size, bool blocking) +{ + while (true) { + if (!_socket) { + return NS_ERROR_NO_CONNECTION; + } + + int32_t recv = _socket->recv(data, size); + + if (recv != NS_ERROR_WOULD_BLOCK || !blocking) { + return recv; + } + } +} + + +const char *Socket::getIPAddress() const +{ + if (_ip_address[0]) { + return _ip_address; + } else { + return 0; + } +} + +uint16_t Socket::getPort() const +{ + return _port; +} + +bool Socket::isConnected() +{ + if (!_socket) { + return false; + } + + return _socket->isConnected(); +} + diff --git a/Socket.h b/Socket.h new file mode 100644 index 0000000000..f02fc29246 --- /dev/null +++ b/Socket.h @@ -0,0 +1,85 @@ +/* Socket + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOCKET_H +#define SOCKET_H + +#include "NetworkInterface.h" + +/** Abstract socket class + * API for handling general sockets. Supports IP address operations + * and sending/recieving data. + */ +class Socket +{ +public: + ~Socket(); + + /** Open a connection to the underlying address + * @param address URL or IP address to connect to + * @param port Port to connect to + * @return 0 on success + */ + int32_t open(const char *address, uint16_t port); + + /** Close an open connection + * @return 0 on success + */ + int32_t close(); + + /** Send data over the socket + * @param data Buffer of data to send + * @param size Size of data to send + * @return Number of bytes sent or a negative value on failure + */ + int32_t send(const void *data, uint32_t size); + + /** Recieve data over the socket + * @param data Buffer to store recieved data + * @param size Size of provided buffer + * @param blocking If true wait for data, otherwise return NS_ERROR_WOULD_BLOCK + * @return Number of bytes recieved or a negative value on failure + */ + int32_t recv(void *data, uint32_t size, bool blocking = true); + + /** Gets the IP address + * @return IP address to connect to + */ + const char *getIPAddress() const; + + /** Gets the port + * @return Port to connect to + */ + uint16_t getPort() const; + + /** Returns status of socket + * @return true if connected + */ + bool isConnected(); + +protected: + Socket(NetworkInterface *iface, ns_protocol_t proto); + +private: + NetworkInterface *_iface; + ns_protocol_t _proto; + SocketInterface *_socket; + + char _ip_address[NS_IP_SIZE]; + uint16_t _port; +}; + +#endif diff --git a/SocketInterface.h b/SocketInterface.h new file mode 100644 index 0000000000..3ca975bc20 --- /dev/null +++ b/SocketInterface.h @@ -0,0 +1,83 @@ +/* SocketInterface Base Class + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOCKET_INTERFACE_H +#define SOCKET_INTERFACE_H + +#include "stdint.h" + + +/** + * @enum ns_protocol_t + * @brief enum of socket protocols + */ +enum ns_protocol_t { + NS_TCP, /*!< Socket is of TCP type */ + NS_UDP, /*!< Socket is of UDP type */ +}; + + +/** SocketInterface class + * Common interface for implementation specific sockets created through + * network interfaces. This class is used internally by the + * TCPSocket and UDPSocket classes + */ +class SocketInterface +{ +public: + + virtual ~SocketInterface() {} + + /** Open a connection to the underlying address + * @param ip IP address to connect to + * @param port Port to connect to + * @return 0 on success + */ + virtual int32_t open(const char *ip, uint16_t port) = 0; + + /** Close an open connection + * @return 0 on success + */ + virtual int32_t close() = 0; + + /** Send data + * @param data Buffer of data to send + * @param size Size of data to send + * @return Number of bytes received or a negative value on success + */ + virtual int32_t send(const void *data, uint32_t size) = 0; + + /** Receive data + * @note + * This call should return immediately with a value of + * NS_ERROR_WOULD_BOCK if no data is available. + * + * @param data A buffer to store the data in + * @param size Size of buffer + * @return Number of bytes received or a negative value on failure + */ + virtual int32_t recv(void *data, uint32_t size) = 0; + + /** Status of the socket + * @return True if connected + */ + virtual bool isConnected() { + // By default return true if socket was created successfully + return true; + } +}; + +#endif diff --git a/TCPSocket.h b/TCPSocket.h new file mode 100644 index 0000000000..31674e2171 --- /dev/null +++ b/TCPSocket.h @@ -0,0 +1,39 @@ +/* TCPSocket + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TCP_SOCKET_H +#define TCP_SOCKET_H + +#include "Socket.h" + +/** TCPSocket class + * API for handling TCP sockets. The implementation is determined + * by the interface passed during construction. + */ +class TCPSocket : public Socket +{ +public: + /** Create a socket using the specified network interface + * No network operations are performed until the socket is actually used + * @param iface The network interface to use + * @param url Optional URL to connect to, copied internally + * @param port Optional port to connect to + */ + TCPSocket(NetworkInterface *iface) + : Socket(iface, NS_TCP) {} +}; + +#endif diff --git a/UDPSocket.h b/UDPSocket.h new file mode 100644 index 0000000000..faa4787d3e --- /dev/null +++ b/UDPSocket.h @@ -0,0 +1,39 @@ +/* UDPSocket + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UDP_SOCKET_H +#define UDP_SOCKET_H + +#include "Socket.h" + +/** UDPSocket class + * API for handling UDP sockets. The implementation is determined + * by the interface passed during construction. + */ +class UDPSocket : public Socket +{ +public: + /** Create a socket using the specified network interface + * No network operations are performed until the socket is actually used + * @param iface The network interface to use + * @param ip Optional URL to connect to, copied internally + * @param port Optional port to connect to + */ + UDPSocket(NetworkInterface *iface) + : Socket(iface, NS_UDP) {} +}; + +#endif diff --git a/WiFiInterface.h b/WiFiInterface.h new file mode 100644 index 0000000000..e91ef5a212 --- /dev/null +++ b/WiFiInterface.h @@ -0,0 +1,52 @@ +/* WiFiInterface Base Class + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WIFI_INTERFACE_H +#define WIFI_INTERFACE_H + +#include "NetworkInterface.h" + +/** Enum for WiFi encryption types + */ +enum ns_security_t { + NS_SECURITY_NONE = 0, /*!< open access point */ + NS_SECURITY_WEP, /*!< phrase conforms to WEP */ + NS_SECURITY_WPA, /*!< phrase conforms to WPA */ + NS_SECURITY_WPA2, /*!< phrase conforms to WPA2 */ +}; + + +/** WiFiInterface class + * Common interface that is shared between WiFi devices + */ +class WiFiInterface : public NetworkInterface +{ +public: + /** Start the interface + * @param ssid Name of the network to connect to + * @param pass Security passphrase to connect to the network + * @param security Type of encryption to connect with + * @return 0 on success + */ + virtual int32_t connect(const char *ssid, const char *pass, ns_security_t security = NS_SECURITY_NONE) = 0; + + /** Stop the interface + * @return 0 on success + */ + virtual int32_t disconnect() = 0; +}; + +#endif From 4c7992cb242ce80bd2f35f098884df5cab2fd557 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Wed, 6 Apr 2016 08:50:56 -0500 Subject: [PATCH 02/40] Matched changes NetworkSocketAPI Responded to feedback from mbed-client implementation to introduce a full feature set that should support most of the use cases for the API. --- DnsQuery/DnsQuery.cpp | 66 +++++++------ EthernetInterface.h | 10 +- MeshInterface.h | 39 ++++++++ NetworkInterface.cpp | 14 +-- NetworkInterface.h | 223 ++++++++++++++++++++++++++++++++++-------- Socket.cpp | 124 +++++++---------------- Socket.h | 82 +++++++--------- SocketAddress.cpp | 67 +++++++++++++ SocketAddress.h | 83 ++++++++++++++++ SocketInterface.h | 83 ---------------- TCPServer.cpp | 87 ++++++++++++++++ TCPServer.h | 67 +++++++++++++ TCPSocket.cpp | 117 ++++++++++++++++++++++ TCPSocket.h | 85 +++++++++++++--- UDPSocket.cpp | 100 +++++++++++++++++++ UDPSocket.h | 74 +++++++++++--- WiFiInterface.h | 27 +++-- 17 files changed, 1000 insertions(+), 348 deletions(-) create mode 100644 MeshInterface.h create mode 100644 SocketAddress.cpp create mode 100644 SocketAddress.h delete mode 100644 SocketInterface.h create mode 100644 TCPServer.cpp create mode 100644 TCPServer.h create mode 100644 TCPSocket.cpp create mode 100644 UDPSocket.cpp diff --git a/DnsQuery/DnsQuery.cpp b/DnsQuery/DnsQuery.cpp index 8cf6ca367e..e940e34694 100644 --- a/DnsQuery/DnsQuery.cpp +++ b/DnsQuery/DnsQuery.cpp @@ -109,33 +109,7 @@ static bool resolve(unsigned char *resp, char *ipaddress) return false; } - -int32_t dnsQuery(NetworkInterface *iface, const char *host, char *ip) -{ - if (isIP(host)) { - strcpy(ip, host); - return 0; - } - - UDPSocket sock(iface); - int32_t err; - - for (unsigned i = 0; i < DNS_COUNT; i++) { - err = sock.open(DNS_IPS[0], 53); - if (err < 0) { - return err; - } - - err = dnsQuery(&sock, host, ip); - sock.close(); - return err; - } - - sock.close(); - return NS_ERROR_DNS_FAILURE; -} - -int32_t dnsQuery(UDPSocket *socket, const char *hostname, char *ipaddress) +static int32_t query(UDPSocket *socket, const SocketAddress &addr, const char *hostname, char *ipaddress) { int len = 0; if (hostname == NULL) { @@ -184,7 +158,7 @@ int32_t dnsQuery(UDPSocket *socket, const char *hostname, char *ipaddress) packet[c++] = 1; - if (socket->send(packet, packetlen) < 0) { + if (socket->sendto(addr, packet, packetlen) < 0) { delete packet; return false; } @@ -194,21 +168,49 @@ int32_t dnsQuery(UDPSocket *socket, const char *hostname, char *ipaddress) // Receive the answer from DNS int response_length = 0; - response_length = socket->recv(packet, 1024); + response_length = socket->recvfrom(NULL, packet, 1024); if (response_length > 0 ) { if (!resolve(packet, ipaddress)) { delete packet; - return NS_ERROR_DNS_FAILURE; + return NSAPI_ERROR_DNS_FAILURE; } - + // cleanup and return delete packet; return 0; } delete packet; - return NS_ERROR_DNS_FAILURE; + return NSAPI_ERROR_DNS_FAILURE; } +int32_t dnsQuery(NetworkInterface *iface, const char *host, char *ip) +{ + if (isIP(host)) { + strcpy(ip, host); + return 0; + } + UDPSocket sock(iface); + + for (unsigned i = 0; i < DNS_COUNT; i++) { + return query(&sock, SocketAddress(DNS_IPS[0], 53), host, ip); + } + + return NSAPI_ERROR_DNS_FAILURE; +} + +int32_t dnsQuery(UDPSocket *socket, const char *host, char *ip) +{ + if (isIP(host)) { + strcpy(ip, host); + return 0; + } + + for (unsigned i = 0; i < DNS_COUNT; i++) { + return query(socket, SocketAddress(DNS_IPS[0], 53), host, ip); + } + + return NSAPI_ERROR_DNS_FAILURE; +} diff --git a/EthernetInterface.h b/EthernetInterface.h index 9caf067294..a48846f650 100644 --- a/EthernetInterface.h +++ b/EthernetInterface.h @@ -1,4 +1,4 @@ -/* EthernetInterface Base Class +/* Socket * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,14 +26,14 @@ class EthernetInterface : public NetworkInterface { public: /** Start the interface - * @return 0 on success + * @return 0 on success, negative on failure */ - virtual int32_t connect() = 0; + virtual int connect() = 0; /** Stop the interface - * @return 0 on success + * @return 0 on success, negative on failure */ - virtual int32_t disconnect() = 0; + virtual int disconnect() = 0; }; #endif diff --git a/MeshInterface.h b/MeshInterface.h new file mode 100644 index 0000000000..884708a623 --- /dev/null +++ b/MeshInterface.h @@ -0,0 +1,39 @@ +/* Socket + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MESH_INTERFACE_H +#define MESH_INTERFACE_H + +#include "NetworkInterface.h" + +/** MeshInterface class + * Common interface that is shared between ethernet hardware + */ +class MeshInterface : public NetworkInterface +{ +public: + /** Start the interface + * @return 0 on success, negative on failure + */ + virtual int connect() = 0; + + /** Stop the interface + * @return 0 on success, negative on failure + */ + virtual int disconnect() = 0; +}; + +#endif diff --git a/NetworkInterface.cpp b/NetworkInterface.cpp index b5e53140de..455d3ddf3b 100644 --- a/NetworkInterface.cpp +++ b/NetworkInterface.cpp @@ -1,4 +1,4 @@ -/* NetworkInterface Base Class +/* Socket * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,16 +14,10 @@ * limitations under the License. */ -#include "NetworkInterface.h" #include "DnsQuery.h" +#include "mbed.h" - -bool NetworkInterface::isConnected() +int NetworkInterface::gethostbyname(const char *name, char *dest) { - return getIPAddress() != 0; -} - -int32_t NetworkInterface::getHostByName(const char *name, char *ip) -{ - return dnsQuery(this, name, ip); + return dnsQuery(this, name, dest); } diff --git a/NetworkInterface.h b/NetworkInterface.h index d63750e576..28ad41e28e 100644 --- a/NetworkInterface.h +++ b/NetworkInterface.h @@ -1,4 +1,4 @@ -/* NetworkInterface Base Class +/* Socket * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,29 +17,37 @@ #ifndef NETWORK_INTERFACE_H #define NETWORK_INTERFACE_H -#include "SocketInterface.h" -#include "stdint.h" +#include "mbed.h" +#include "SocketAddress.h" -/** Maximum storage needed for IP address and MAC addresses - */ -#define NS_IP_SIZE 16 -#define NS_MAC_SIZE 18 - -/** +/** Enum of standardized error codes * @enum ns_error_t - * @brief enum of standardized error codes */ -enum ns_error_t { - NS_ERROR_WOULD_BLOCK = -3000, /*!< no data is not available but call is non-blocking */ - NS_ERROR_TIMEOUT = -3001, /*!< operation took longer than allowed */ - NS_ERROR_NO_CONNECTION = -3002, /*!< not connected to a network */ - NS_ERROR_NO_SOCKET = -3003, /*!< socket not available for use */ - NS_ERROR_NO_ADDRESS = -3004, /*!< IP address is not known */ - NS_ERROR_NO_MEMORY = -3005, /*!< memory resource not available */ - NS_ERROR_DNS_FAILURE = -3006, /*!< DNS failed to complete successfully */ - NS_ERROR_DHCP_FAILURE = -3007, /*!< DHCP failed to complete successfully */ - NS_ERROR_AUTH_FAILURE = -3008, /*!< connection to access point faield */ - NS_ERROR_DEVICE_ERROR = -3009 /*!< failure interfacing with the network procesor */ +enum nsapi_error_t { + NSAPI_ERROR_WOULD_BLOCK = -3001, /*!< no data is not available but call is non-blocking */ + NSAPI_ERROR_UNSUPPORTED = -3002, /*!< unsupported configuration */ + NSAPI_ERROR_NO_CONNECTION = -3003, /*!< not connected to a network */ + NSAPI_ERROR_NO_SOCKET = -3004, /*!< socket not available for use */ + NSAPI_ERROR_NO_ADDRESS = -3005, /*!< IP address is not known */ + NSAPI_ERROR_NO_MEMORY = -3006, /*!< memory resource not available */ + NSAPI_ERROR_DNS_FAILURE = -3007, /*!< DNS failed to complete successfully */ + NSAPI_ERROR_DHCP_FAILURE = -3008, /*!< DHCP failed to complete successfully */ + NSAPI_ERROR_AUTH_FAILURE = -3009, /*!< connection to access point faield */ + NSAPI_ERROR_DEVICE_ERROR = -3010, /*!< failure interfacing with the network procesor */ +}; + +/** Enum of available options + * @enum ns_opt_t + */ +enum ns_opt_t { +}; + +/** Enum of socket protocols + * @enum protocol_t + */ +enum nsapi_protocol_t { + NSAPI_TCP, /*!< Socket is of TCP type */ + NSAPI_UDP, /*!< Socket is of UDP type */ }; /** NetworkInterface class @@ -51,42 +59,175 @@ class NetworkInterface public: virtual ~NetworkInterface() {}; - /** Get the IP address - * @return IP address of the interface or 0 if not yet connected + /** Get the internally stored IP address + * @return IP address of the interface or null if not yet connected */ - virtual const char *getIPAddress() = 0; + virtual const char *get_ip_address() = 0; - /** Get the current MAC address - * @return String MAC address of the interface + /** Get the internally stored MAC address + * @return MAC address of the interface */ - virtual const char *getMACAddress() = 0; + virtual const char *get_mac_address() = 0; /** Get the current status of the interface - * @return true if connected + * @return true if connected */ - virtual bool isConnected(); + virtual bool is_connected() { + return get_ip_address() != NULL; + } /** Looks up the specified host's IP address - * @param name URL of host - * @param ip Buffer to hold IP address, must be at least SOCK_IP_SIZE - * @return 0 on success + * @param name Hostname to lookup + * @param dest Destination for IP address, must have space for SocketAddress::IP_SIZE + * @return 0 on success, negative on failure */ - virtual int32_t getHostByName(const char *name, char *ip); + virtual int gethostbyname(const char *name, char *dest); protected: friend class Socket; + friend class UDPSocket; + friend class TCPSocket; + friend class TCPServer; - /** Internally create a socket - * @param proto The type of socket to open, NS_TCP or NS_UDP - * @return The allocated socket + /** Create a socket + * @param proto The type of socket to open, TCP or UDP + * @return The alocated socket or null on failure */ - virtual SocketInterface *createSocket(ns_protocol_t proto) = 0; + virtual void *socket_create(nsapi_protocol_t proto) = 0; - /** Internally destroy a socket - * @param socket An allocated SocketInterface - * @returns 0 on success + /** Destroy a socket + * @param socket Previously allocated socket */ - virtual void destroySocket(SocketInterface *socket) = 0; + virtual void socket_destroy(void *handle) = 0; + + /** Set socket options + * @param handle Socket handle + * @param optname Option ID + * @param optval Option value + * @param optlen Length of the option value + * @return 0 on success, negative on failure + */ + virtual int socket_set_option(void *handle, int optname, const void *optval, unsigned int optlen) = 0; + + /** Get socket options + * @param handle Socket handle + * @param optname Option ID + * @param optval Buffer pointer where to write the option value + * @param optlen Length of the option value + * @return 0 on success, negative on failure + */ + virtual int socket_get_option(void *handle, int optname, void *optval, unsigned int *optlen) = 0; + + /** Bind a server socket to a specific port + * @param handle Socket handle + * @param port The port to listen for incoming connections on + * @return 0 on success, negative on failure. + */ + virtual int socket_bind(void *handle, int port) = 0; + + /** Start listening for incoming connections + * @param handle Socket handle + * @param backlog Number of pending connections that can be queued up at any + * one time [Default: 1] + * @return 0 on success, negative on failure + */ + virtual int socket_listen(void *handle, int backlog) = 0; + + /** Connects this TCP socket to the server + * @param handle Socket handle + * @param address SocketAddress to connect to + * @return 0 on success, negative on failure + */ + virtual int socket_connect(void *handle, const SocketAddress &address) = 0; + + /** Check if the socket is connected + * @param handle Socket handle + * @return true if connected, false otherwise + */ + virtual bool socket_is_connected(void *handle) = 0; + + /** Accept a new connection. + * @param handle Socket handle + * @param socket A TCPSocket instance that will handle the incoming connection. + * @return 0 on success, negative on failure. + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_accept(void *handle, void **connection) = 0; + + /** Send data to the remote host + * @param handle Socket handle + * @param data The buffer to send to the host + * @param size The length of the buffer to send + * @return Number of written bytes on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_send(void *handle, const void *data, unsigned size) = 0; + + /** Receive data from the remote host + * @param handle Socket handle + * @param data The buffer in which to store the data received from the host + * @param size The maximum length of the buffer + * @return Number of received bytes on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_recv(void *handle, void *data, unsigned size) = 0; + + /** Send a packet to a remote endpoint + * @param handle Socket handle + * @param address The remote SocketAddress + * @param data The packet to be sent + * @param size The length of the packet to be sent + * @return the number of written bytes on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_sendto(void *handle, const SocketAddress &address, const void *data, unsigned size) = 0; + + /** Receive a packet from a remote endpoint + * @param handle Socket handle + * @param address Destination for the remote SocketAddress or null + * @param buffer The buffer for storing the incoming packet data + * If a packet is too long to fit in the supplied buffer, + * excess bytes are discarded + * @param size The length of the buffer + * @return the number of received bytes on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_recvfrom(void *handle, SocketAddress *address, void *buffer, unsigned size) = 0; + + /** Close the socket + * @param handle Socket handle + * @param shutdown free the left-over data in message queues + */ + virtual int socket_close(void *handle, bool shutdown) = 0; + + /** Register a callback on when a new connection is ready + * @param handle Socket handle + * @param callback Function to call when accept will succeed, may be called in + * interrupt context. + * @param id Argument to pass to callback + */ + virtual void socket_attach_accept(void *handle, void (*callback)(void *), void *id) = 0; + + /** Register a callback on when send is ready + * @param handle Socket handle + * @param callback Function to call when accept will succeed, may be called in + * interrupt context. + * @param id Argument to pass to callback + */ + virtual void socket_attach_send(void *handle, void (*callback)(void *), void *id) = 0; + + /** Register a callback on when recv is ready + * @param handle Socket handle + * @param callback Function to call when accept will succeed, may be called in + * interrupt context. + * @param id Argument to pass to callback + */ + virtual void socket_attach_recv(void *handle, void (*callback)(void *), void *id) = 0; }; #endif diff --git a/Socket.cpp b/Socket.cpp index 92618b7fbb..7f90895ede 100644 --- a/Socket.cpp +++ b/Socket.cpp @@ -15,116 +15,68 @@ */ #include "Socket.h" -#include -Socket::Socket(NetworkInterface *iface, ns_protocol_t proto) +Socket::Socket(NetworkInterface *iface, nsapi_protocol_t proto) : _iface(iface) - , _proto(proto) - , _socket(0) + , _blocking(true) + , _timeout(0) { - memset(_ip_address, 0, NS_IP_SIZE); - _port = 0; + _socket = _iface->socket_create(proto); } Socket::~Socket() { if (_socket) { - close(); + close(false); } } - -int32_t Socket::open(const char *address, uint16_t port) +void Socket::set_blocking(bool blocking) { - int32_t err; - - err = close(); - if (err) { - return err; - } - - err = _iface->getHostByName(address, _ip_address); - _port = port; - if (err) { - return err; - } - - _socket = _iface->createSocket(_proto); - if (!_socket) { - return NS_ERROR_NO_SOCKET; - } - - err = _socket->open(_ip_address, _port); - - if (err) { - _iface->destroySocket(_socket); - _socket = 0; - } - - return err; + _blocking = blocking; } -int32_t Socket::close() +void Socket::set_timeout(unsigned timeout) +{ + _timeout = timeout; +} + +int Socket::set_option(int optname, const void *optval, unsigned int optlen) +{ + if (!_socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + return _iface->socket_set_option(_socket, optname, optval, optlen); +} + +int Socket::get_option(int optname, void *optval, unsigned int *optlen) +{ + if (!_socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + return _iface->socket_get_option(_socket, optname, optval, optlen); +} + +int Socket::close(bool shutdown) { if (!_socket) { return 0; } - - SocketInterface *socket = _socket; - _socket = 0; - int32_t err = socket->close(); + int err = _iface->socket_close(_socket, shutdown); if (!err) { - _iface->destroySocket(socket); + void *socket = _socket; + _socket = 0; + _iface->socket_destroy(socket); } return err; } -int32_t Socket::send(const void *data, uint32_t size) +void Socket::thunk(void *p) { - if (!_socket) { - return NS_ERROR_NO_CONNECTION; - } - return _socket->send(data, size); + FunctionPointer *fptr = (FunctionPointer *)p; + (*fptr)(); } - -int32_t Socket::recv(void *data, uint32_t size, bool blocking) -{ - while (true) { - if (!_socket) { - return NS_ERROR_NO_CONNECTION; - } - - int32_t recv = _socket->recv(data, size); - - if (recv != NS_ERROR_WOULD_BLOCK || !blocking) { - return recv; - } - } -} - - -const char *Socket::getIPAddress() const -{ - if (_ip_address[0]) { - return _ip_address; - } else { - return 0; - } -} - -uint16_t Socket::getPort() const -{ - return _port; -} - -bool Socket::isConnected() -{ - if (!_socket) { - return false; - } - - return _socket->isConnected(); -} - diff --git a/Socket.h b/Socket.h index f02fc29246..cb359e7040 100644 --- a/Socket.h +++ b/Socket.h @@ -17,69 +17,57 @@ #ifndef SOCKET_H #define SOCKET_H +#include "SocketAddress.h" #include "NetworkInterface.h" /** Abstract socket class - * API for handling general sockets. Supports IP address operations - * and sending/recieving data. */ -class Socket -{ +class Socket { public: - ~Socket(); - - /** Open a connection to the underlying address - * @param address URL or IP address to connect to - * @param port Port to connect to - * @return 0 on success + /** Socket lifetime */ - int32_t open(const char *address, uint16_t port); - - /** Close an open connection - * @return 0 on success + virtual ~Socket(); + + /** Set blocking or non-blocking mode of the socket + * @param blocking true for blocking mode, false for non-blocking mode. */ - int32_t close(); - - /** Send data over the socket - * @param data Buffer of data to send - * @param size Size of data to send - * @return Number of bytes sent or a negative value on failure + void set_blocking(bool blocking); + + /** Set timeout on a socket operation if blocking behaviour is enabled + * @param timeout timeout in ms */ - int32_t send(const void *data, uint32_t size); + void set_timeout(unsigned int timeout); - /** Recieve data over the socket - * @param data Buffer to store recieved data - * @param size Size of provided buffer - * @param blocking If true wait for data, otherwise return NS_ERROR_WOULD_BLOCK - * @return Number of bytes recieved or a negative value on failure + /* Set socket options + * @param optname Option ID + * @param optval Option value + * @param optlen Length of the option value + * @return 0 on success, negative on failure */ - int32_t recv(void *data, uint32_t size, bool blocking = true); - - /** Gets the IP address - * @return IP address to connect to + int set_option(int optname, const void *optval, unsigned optlen); + + /* Get socket options + * @param optname Option ID + * @param optval Buffer pointer where to write the option value + * @param optlen Length of the option value + * @return 0 on success, negative on failure */ - const char *getIPAddress() const; - - /** Gets the port - * @return Port to connect to + int get_option(int optname, void *optval, unsigned *optlen); + + /** Close the socket + * @param shutdown free the left-over data in message queues */ - uint16_t getPort() const; - - /** Returns status of socket - * @return true if connected - */ - bool isConnected(); + int close(bool shutdown=true); protected: - Socket(NetworkInterface *iface, ns_protocol_t proto); + Socket(NetworkInterface *iface, nsapi_protocol_t proto); + + static void thunk(void *); -private: NetworkInterface *_iface; - ns_protocol_t _proto; - SocketInterface *_socket; - - char _ip_address[NS_IP_SIZE]; - uint16_t _port; + void *_socket; + bool _blocking; + unsigned _timeout; }; #endif diff --git a/SocketAddress.cpp b/SocketAddress.cpp new file mode 100644 index 0000000000..f57fd8253f --- /dev/null +++ b/SocketAddress.cpp @@ -0,0 +1,67 @@ +/* Socket + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SocketAddress.h" +#include "NetworkInterface.h" +#include +#include "mbed.h" + +SocketAddress::SocketAddress(NetworkInterface *iface, const char *host, uint16_t port) +{ + int err = iface->gethostbyname(host, _ip_address); + set_port(port); + + if (err) { + _ip_address[0] = '\0'; + _port = 0; + } +} + +SocketAddress::SocketAddress(const char *addr, uint16_t port) +{ + set_ip_address(addr); + set_port(port); +} + +SocketAddress::SocketAddress(const SocketAddress &addr) +{ + set_ip_address(addr.get_ip_address()); + set_port(addr.get_port()); +} + +void SocketAddress::set_ip_address(const char *addr) +{ + strncpy(_ip_address, addr, sizeof _ip_address); + _ip_address[sizeof _ip_address - 1] = '\0'; +} + +void SocketAddress::set_port(uint16_t port) +{ + _port = port; +} + +const char *SocketAddress::get_ip_address() const +{ + if (!_ip_address[0]) { + return 0; + } + return _ip_address; +} + +uint16_t SocketAddress::get_port() const +{ + return _port; +} diff --git a/SocketAddress.h b/SocketAddress.h new file mode 100644 index 0000000000..44e55bc9d1 --- /dev/null +++ b/SocketAddress.h @@ -0,0 +1,83 @@ +/* Socket + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOCKET_ADDRESS_H +#define SOCKET_ADDRESS_H + +#include + +/** Maximum size of IP address +*/ +#define NSAPI_IP_SIZE 16 + +/** Maximum size of MAC address +*/ +#define NSAPI_MAC_SIZE 18 + +// Predeclared classes +class NetworkInterface; + +/** + * A general socket address composed of the IP address and port + */ +class SocketAddress { +public: + /** SocketAddress construction using DNS resolution + * @param iface NetworkInterface to use for DNS resolution + * @param addr Null-terminated hostname that will be resolved + * @param port 16-bit port + * @note on failure, IP address and port will be set to null + */ + SocketAddress(NetworkInterface *iface, const char *addr, uint16_t port = 0); + + /** SocketAddress construction + * @param addr Null-terminated IP address + * @param port 16-bit port + * @note on failure, IP address and port will be set to null + */ + SocketAddress(const char *addr = 0, uint16_t port = 0); + + /** SocketAddress construction + * @param addr SocketAddress to copy + */ + SocketAddress(const SocketAddress &addr); + + /** Set the IP address + * @param addr Null-terminated string representing the IP address + */ + void set_ip_address(const char *addr); + + /** Set the port + * @param port 16-bit port + */ + void set_port(uint16_t port); + + /** Get the IP address + * @return The string representation of the IP Address + */ + const char *get_ip_address() const; + + /** Get the port + * @return The 16-bit port + */ + uint16_t get_port(void) const; + +private: + char _ip_address[NSAPI_IP_SIZE]; + uint16_t _port; +}; + +#endif diff --git a/SocketInterface.h b/SocketInterface.h deleted file mode 100644 index 3ca975bc20..0000000000 --- a/SocketInterface.h +++ /dev/null @@ -1,83 +0,0 @@ -/* SocketInterface Base Class - * Copyright (c) 2015 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SOCKET_INTERFACE_H -#define SOCKET_INTERFACE_H - -#include "stdint.h" - - -/** - * @enum ns_protocol_t - * @brief enum of socket protocols - */ -enum ns_protocol_t { - NS_TCP, /*!< Socket is of TCP type */ - NS_UDP, /*!< Socket is of UDP type */ -}; - - -/** SocketInterface class - * Common interface for implementation specific sockets created through - * network interfaces. This class is used internally by the - * TCPSocket and UDPSocket classes - */ -class SocketInterface -{ -public: - - virtual ~SocketInterface() {} - - /** Open a connection to the underlying address - * @param ip IP address to connect to - * @param port Port to connect to - * @return 0 on success - */ - virtual int32_t open(const char *ip, uint16_t port) = 0; - - /** Close an open connection - * @return 0 on success - */ - virtual int32_t close() = 0; - - /** Send data - * @param data Buffer of data to send - * @param size Size of data to send - * @return Number of bytes received or a negative value on success - */ - virtual int32_t send(const void *data, uint32_t size) = 0; - - /** Receive data - * @note - * This call should return immediately with a value of - * NS_ERROR_WOULD_BOCK if no data is available. - * - * @param data A buffer to store the data in - * @param size Size of buffer - * @return Number of bytes received or a negative value on failure - */ - virtual int32_t recv(void *data, uint32_t size) = 0; - - /** Status of the socket - * @return True if connected - */ - virtual bool isConnected() { - // By default return true if socket was created successfully - return true; - } -}; - -#endif diff --git a/TCPServer.cpp b/TCPServer.cpp new file mode 100644 index 0000000000..aa3623caa0 --- /dev/null +++ b/TCPServer.cpp @@ -0,0 +1,87 @@ +/* Socket + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TCPServer.h" +#include "Timer.h" + +TCPServer::TCPServer(NetworkInterface *iface) + : Socket(iface, NSAPI_TCP) +{ +} + +int TCPServer::bind(uint16_t port) +{ + if (!_socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + return _iface->socket_bind(_socket, port); +} + +int TCPServer::listen(int backlog) +{ + if (!_socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + return _iface->socket_listen(_socket, backlog); +} + +int TCPServer::accept(TCPSocket *connection) +{ + mbed::Timer timer; + timer.start(); + + void *socket = connection->_socket; + connection->_socket = 0; + _iface->socket_destroy(socket); + + while (true) { + if (!_socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + int err = _iface->socket_accept(_socket, &socket); + + if (err > 0) { + connection->_socket = socket; + } + + if (err != NSAPI_ERROR_WOULD_BLOCK || !_blocking || + (_timeout && timer.read_ms() > _timeout)) { + return err; + } + } +} + + +void TCPServer::attach_accept(FunctionPointer callback) +{ + _accept_cb = callback; + + if (_socket && _accept_cb) { + return _iface->socket_attach_accept(_socket, Socket::thunk, &_accept_cb); + } else if (_socket) { + return _iface->socket_attach_accept(_socket, 0, 0); + } +} + +TCPServer::~TCPServer() +{ + if (_socket && _accept_cb) { + _iface->socket_attach_accept(_socket, 0, 0); + } +} diff --git a/TCPServer.h b/TCPServer.h new file mode 100644 index 0000000000..0072ec3f1c --- /dev/null +++ b/TCPServer.h @@ -0,0 +1,67 @@ +/* Socket + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TCPSERVER_H +#define TCPSERVER_H + +#include "Socket.h" +#include "TCPSocket.h" +#include "NetworkInterface.h" + +/** TCP Server. + */ +class TCPServer : public Socket { +public: + /** TCP Server lifetime + */ + TCPServer(NetworkInterface *iface); + virtual ~TCPServer(); + + /** Bind a socket to a specific port + * @param port The port to listen for incoming connections on + * @return 0 on success, negative on failure + */ + int bind(uint16_t port); + + /** Start listening for incoming connections + * @param backlog Number of pending connections that can be queued up at any + * one time [Default: 1] + * @return 0 on success, negative on failure + */ + int listen(int backlog=1); + + /** Accept a new connection. + * @param socket A TCPSocket instance that will handle the incoming connection. + * @return 0 on success, negative on failure. + */ + int accept(TCPSocket *connection); + + /** Register a callback on when a new connection is ready + * @param callback Function to call when accept will succeed, may be called in + * interrupt context. + */ + void attach_accept(FunctionPointer callback); + + template + void attach_accept(T *tptr, M mptr) { + attach_accept(FunctionPointer(tptr, mptr)); + } + +private: + FunctionPointer _accept_cb; +}; + +#endif diff --git a/TCPSocket.cpp b/TCPSocket.cpp new file mode 100644 index 0000000000..13bd8b6c0a --- /dev/null +++ b/TCPSocket.cpp @@ -0,0 +1,117 @@ +/* Socket + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TCPSocket.h" +#include "Timer.h" + +TCPSocket::TCPSocket(NetworkInterface *iface) + : Socket(iface, NSAPI_TCP) +{ +} + +int TCPSocket::connect(const SocketAddress &addr) +{ + if (!_socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + return _iface->socket_connect(_socket, addr); +} + +int TCPSocket::connect(const char *host, uint16_t port) +{ + SocketAddress addr(_iface, host, port); + if (!addr.get_ip_address()) { + return NSAPI_ERROR_DNS_FAILURE; + } + + return connect(addr); +} + +bool TCPSocket::is_connected() +{ + return _socket && _iface->socket_is_connected(_socket); +} + +int TCPSocket::send(const void *data, unsigned size) +{ + mbed::Timer timer; + timer.start(); + + while (true) { + if (!_socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + int sent = _iface->socket_send(_socket, data, size); + if (sent != NSAPI_ERROR_WOULD_BLOCK || !_blocking || + (_timeout && timer.read_ms() > _timeout)) { + return sent; + } + } +} + +int TCPSocket::recv(void *data, unsigned size) +{ + mbed::Timer timer; + timer.start(); + + while (true) { + if (!_socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + int recv = _iface->socket_recv(_socket, data, size); + if (recv != NSAPI_ERROR_WOULD_BLOCK || !_blocking || + (_timeout && timer.read_ms() > _timeout)) { + return recv; + } + } +} + + +void TCPSocket::attach_send(FunctionPointer callback) +{ + _send_cb = callback; + + if (_socket && _send_cb) { + return _iface->socket_attach_send(_socket, Socket::thunk, &_send_cb); + } else if (_socket) { + return _iface->socket_attach_send(_socket, 0, 0); + } +} + +void TCPSocket::attach_recv(FunctionPointer callback) +{ + _recv_cb = callback; + + if (_socket && _recv_cb) { + return _iface->socket_attach_recv(_socket, Socket::thunk, &_recv_cb); + } else if (_socket) { + return _iface->socket_attach_recv(_socket, 0, 0); + } +} + +TCPSocket::~TCPSocket() +{ + if (_socket && _send_cb) { + _iface->socket_attach_send(_socket, 0, 0); + } + + if (_socket && _recv_cb) { + _iface->socket_attach_recv(_socket, 0, 0); + } +} diff --git a/TCPSocket.h b/TCPSocket.h index 31674e2171..b4f7917424 100644 --- a/TCPSocket.h +++ b/TCPSocket.h @@ -1,4 +1,4 @@ -/* TCPSocket +/* Socket * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,26 +14,81 @@ * limitations under the License. */ -#ifndef TCP_SOCKET_H -#define TCP_SOCKET_H +#ifndef TCPSOCKET_H +#define TCPSOCKET_H #include "Socket.h" +#include "NetworkInterface.h" -/** TCPSocket class - * API for handling TCP sockets. The implementation is determined - * by the interface passed during construction. +/** TCP socket connection */ -class TCPSocket : public Socket -{ +class TCPSocket : public Socket { public: - /** Create a socket using the specified network interface - * No network operations are performed until the socket is actually used - * @param iface The network interface to use - * @param url Optional URL to connect to, copied internally - * @param port Optional port to connect to + /** TCP socket lifetime */ - TCPSocket(NetworkInterface *iface) - : Socket(iface, NS_TCP) {} + TCPSocket(NetworkInterface *iface); + virtual ~TCPSocket(); + + /** Connects this TCP socket to the server + * @param host The host to connect to. It can either be an IP Address + * or a hostname that will be resolved with DNS + * @param port The host's port to connect to + * @return 0 on success, negative on failure + */ + int connect(const char *host, uint16_t port); + + /** Connects this TCP socket to the server + * @param address SocketAddress to connect to + * @return 0 on success, negative on failure + */ + int connect(const SocketAddress &address); + + /** Check if the socket is connected + * @return true if connected, false otherwise + */ + bool is_connected(); + + /** Send data to the remote host + * @param data The buffer to send to the host + * @param size The length of the buffer to send + * @return Number of written bytes on success, negative on failure + */ + int send(const void *data, unsigned size); + + /** Receive data from the remote host + * @param data The buffer in which to store the data received from the host + * @param size The maximum length of the buffer + * @return Number of received bytes on success, negative on failure + */ + int recv(void *data, unsigned size); + + /** Register a callback on when send is ready + * @param callback Function to call when send will succeed, may be called in + * interrupt context. + */ + void attach_send(FunctionPointer callback); + + template + void attach_send(T *tptr, M mptr) { + attach_send(FunctionPointer(tptr, mptr)); + } + + /** Register a callback on when recv is ready + * @param callback Function to call when recv will succeed, may be called in + * interrupt context. + */ + void attach_recv(FunctionPointer callback); + + template + void attach_recv(T *tptr, M mptr) { + attach_recv(FunctionPointer(tptr, mptr)); + } + +private: + friend class TCPServer; + + FunctionPointer _send_cb; + FunctionPointer _recv_cb; }; #endif diff --git a/UDPSocket.cpp b/UDPSocket.cpp new file mode 100644 index 0000000000..90ed74ad39 --- /dev/null +++ b/UDPSocket.cpp @@ -0,0 +1,100 @@ +/* Socket + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "UDPSocket.h" +#include "Timer.h" + +UDPSocket::UDPSocket(NetworkInterface *iface) + : Socket(iface, NSAPI_UDP) +{ +} + +int UDPSocket::bind(uint16_t port) +{ + if (!_socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + return _iface->socket_bind(_socket, port); +} + +int UDPSocket::sendto(const SocketAddress &address, const void *data, unsigned size) +{ + mbed::Timer timer; + timer.start(); + + while (true) { + if (!_socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + int sent = _iface->socket_sendto(_socket, address, data, size); + if (sent != NSAPI_ERROR_WOULD_BLOCK || !_blocking || + (_timeout && timer.read_ms() > _timeout)) { + return sent; + } + } +} + +int UDPSocket::recvfrom(SocketAddress *address, void *buffer, unsigned size) +{ + mbed::Timer timer; + timer.start(); + + while (true) { + if (!_socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + int recv = _iface->socket_recvfrom(_socket, address, buffer, size); + if (recv != NSAPI_ERROR_WOULD_BLOCK || !_blocking || + (_timeout && timer.read_ms() > _timeout)) { + return recv; + } + } +} + + +void UDPSocket::attach_send(FunctionPointer callback) +{ + _send_cb = callback; + if (_socket && _send_cb) { + return _iface->socket_attach_send(_socket, Socket::thunk, &_send_cb); + } else if (_socket) { + return _iface->socket_attach_send(_socket, 0, 0); + } +} + +void UDPSocket::attach_recv(FunctionPointer callback) +{ + _recv_cb = callback; + if (_socket && _recv_cb) { + return _iface->socket_attach_recv(_socket, Socket::thunk, &_recv_cb); + } else if (_socket) { + return _iface->socket_attach_recv(_socket, 0, 0); + } +} + +UDPSocket::~UDPSocket() +{ + if (_socket && _send_cb) { + _iface->socket_attach_send(_socket, 0, 0); + } + + if (_socket && _recv_cb) { + _iface->socket_attach_recv(_socket, 0, 0); + } +} diff --git a/UDPSocket.h b/UDPSocket.h index faa4787d3e..1614f8f920 100644 --- a/UDPSocket.h +++ b/UDPSocket.h @@ -1,4 +1,4 @@ -/* UDPSocket +/* Socket * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,26 +14,70 @@ * limitations under the License. */ -#ifndef UDP_SOCKET_H -#define UDP_SOCKET_H +#ifndef UDPSOCKET_H +#define UDPSOCKET_H #include "Socket.h" +#include "NetworkInterface.h" -/** UDPSocket class - * API for handling UDP sockets. The implementation is determined - * by the interface passed during construction. +/** UDP Socket */ -class UDPSocket : public Socket -{ +class UDPSocket : public Socket { public: - /** Create a socket using the specified network interface - * No network operations are performed until the socket is actually used - * @param iface The network interface to use - * @param ip Optional URL to connect to, copied internally - * @param port Optional port to connect to + /** UDPSocket lifetime */ - UDPSocket(NetworkInterface *iface) - : Socket(iface, NS_UDP) {} + UDPSocket(NetworkInterface *iface); + virtual ~UDPSocket(); + + /** Bind a UDP Server Socket to a specific port + * @param port The port to listen for incoming connections on + * @return 0 on success, negative on failure. + */ + int bind(uint16_t port); + + /** Send a packet to a remote endpoint + * @param address The remote SocketAddress + * @param data The packet to be sent + * @param size The length of the packet to be sent + * @return The number of written bytes on success, negative on failure + */ + int sendto(const SocketAddress &address, const void *data, unsigned size); + + /** Receive a packet from a remote endpoint + * @param address Destination for the remote SocketAddress or null + * @param buffer The buffer for storing the incoming packet data + * If a packet is too long to fit in the supplied buffer, + * excess bytes are discarded + * @param size The length of the buffer + * @return The number of received bytes on success, negative on failure + */ + int recvfrom(SocketAddress *address, void *buffer, unsigned size); + + /** Register a callback on when send is ready + * @param callback Function to call when send will succeed, may be called in + * interrupt context. + */ + void attach_send(FunctionPointer callback); + + template + void attach_send(T *tptr, M mptr) { + attach_send(FunctionPointer(tptr, mptr)); + } + + /** Register a callback on when recv is ready + * @param callback Function to call when recv will succeed, may be called in + * interrupt context. + */ + void attach_recv(FunctionPointer callback); + + template + void attach_recv(T *tptr, M mptr) { + attach_recv(FunctionPointer(tptr, mptr)); + } + +private: + FunctionPointer _send_cb; + FunctionPointer _recv_cb; }; #endif diff --git a/WiFiInterface.h b/WiFiInterface.h index e91ef5a212..21fa397a66 100644 --- a/WiFiInterface.h +++ b/WiFiInterface.h @@ -1,4 +1,4 @@ -/* WiFiInterface Base Class +/* Socket * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,14 +21,13 @@ /** Enum for WiFi encryption types */ -enum ns_security_t { - NS_SECURITY_NONE = 0, /*!< open access point */ - NS_SECURITY_WEP, /*!< phrase conforms to WEP */ - NS_SECURITY_WPA, /*!< phrase conforms to WPA */ - NS_SECURITY_WPA2, /*!< phrase conforms to WPA2 */ +enum nsapi_security_t { + NSAPI_SECURITY_NONE = 0, /*!< open access point */ + NSAPI_SECURITY_WEP, /*!< phrase conforms to WEP */ + NSAPI_SECURITY_WPA, /*!< phrase conforms to WPA */ + NSAPI_SECURITY_WPA2, /*!< phrase conforms to WPA2 */ }; - /** WiFiInterface class * Common interface that is shared between WiFi devices */ @@ -36,17 +35,17 @@ class WiFiInterface : public NetworkInterface { public: /** Start the interface - * @param ssid Name of the network to connect to - * @param pass Security passphrase to connect to the network - * @param security Type of encryption to connect with - * @return 0 on success + * @param ssid Name of the network to connect to + * @param pass Security passphrase to connect to the network + * @param security Type of encryption for connection + * @return 0 on success, negative on failure */ - virtual int32_t connect(const char *ssid, const char *pass, ns_security_t security = NS_SECURITY_NONE) = 0; + virtual int connect(const char *ssid, const char *pass, nsapi_security_t security = NSAPI_SECURITY_NONE) = 0; /** Stop the interface - * @return 0 on success + * @return 0 on success, negative on failure */ - virtual int32_t disconnect() = 0; + virtual int disconnect() = 0; }; #endif From d38ccb70a6e1ccaddc4a6d1aac9726d387edf10b Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sun, 13 Mar 2016 07:08:27 -0500 Subject: [PATCH 03/40] Add open call as alternative to passing NetworkInterface at construction Pros - Allows memory to be statically allocated - Avoids issues with Thread creation before entering main - Matches existing APIs such as FunctionPointer and Ticker Cons - Does not enforce passing a NetworkInterface --- Socket.cpp | 44 +++++++++++++++++++++++++------------------- Socket.h | 8 +++++++- TCPServer.cpp | 20 +++++++++++++++----- TCPServer.h | 6 ++++++ TCPSocket.cpp | 13 +++++++++++-- TCPSocket.h | 6 ++++++ UDPSocket.cpp | 13 +++++++++++-- UDPSocket.h | 6 ++++++ 8 files changed, 87 insertions(+), 29 deletions(-) diff --git a/Socket.cpp b/Socket.cpp index 7f90895ede..e52ba47793 100644 --- a/Socket.cpp +++ b/Socket.cpp @@ -16,12 +16,12 @@ #include "Socket.h" -Socket::Socket(NetworkInterface *iface, nsapi_protocol_t proto) - : _iface(iface) +Socket::Socket() + : _iface(0) + , _socket(0) , _blocking(true) , _timeout(0) { - _socket = _iface->socket_create(proto); } Socket::~Socket() @@ -31,6 +31,28 @@ Socket::~Socket() } } +int Socket::open(NetworkInterface *iface, nsapi_protocol_t proto) +{ + _iface = iface; + _socket = _iface->socket_create(proto); +} + +int Socket::close(bool shutdown) +{ + if (!_socket) { + return 0; + } + + int err = _iface->socket_close(_socket, shutdown); + if (!err) { + void *socket = _socket; + _socket = 0; + _iface->socket_destroy(socket); + } + + return err; +} + void Socket::set_blocking(bool blocking) { _blocking = blocking; @@ -59,22 +81,6 @@ int Socket::get_option(int optname, void *optval, unsigned int *optlen) return _iface->socket_get_option(_socket, optname, optval, optlen); } -int Socket::close(bool shutdown) -{ - if (!_socket) { - return 0; - } - - int err = _iface->socket_close(_socket, shutdown); - if (!err) { - void *socket = _socket; - _socket = 0; - _iface->socket_destroy(socket); - } - - return err; -} - void Socket::thunk(void *p) { FunctionPointer *fptr = (FunctionPointer *)p; diff --git a/Socket.h b/Socket.h index cb359e7040..ebae4440e0 100644 --- a/Socket.h +++ b/Socket.h @@ -27,6 +27,11 @@ public: /** Socket lifetime */ virtual ~Socket(); + + /** Open the socket + * @param iface Interface to open socket on + */ + virtual int open(NetworkInterface *iface) = 0; /** Set blocking or non-blocking mode of the socket * @param blocking true for blocking mode, false for non-blocking mode. @@ -60,7 +65,8 @@ public: int close(bool shutdown=true); protected: - Socket(NetworkInterface *iface, nsapi_protocol_t proto); + Socket(); + int open(NetworkInterface *iface, nsapi_protocol_t proto); static void thunk(void *); diff --git a/TCPServer.cpp b/TCPServer.cpp index aa3623caa0..c3e9dd6c18 100644 --- a/TCPServer.cpp +++ b/TCPServer.cpp @@ -17,11 +17,20 @@ #include "TCPServer.h" #include "Timer.h" -TCPServer::TCPServer(NetworkInterface *iface) - : Socket(iface, NSAPI_TCP) +TCPServer::TCPServer() { } +TCPServer::TCPServer(NetworkInterface *iface) +{ + open(iface); +} + +int TCPServer::open(NetworkInterface *iface) +{ + return Socket::open(iface, NSAPI_TCP); +} + int TCPServer::bind(uint16_t port) { if (!_socket) { @@ -45,15 +54,16 @@ int TCPServer::accept(TCPSocket *connection) mbed::Timer timer; timer.start(); - void *socket = connection->_socket; - connection->_socket = 0; - _iface->socket_destroy(socket); + if (connection->_socket) { + connection->close(); + } while (true) { if (!_socket) { return NSAPI_ERROR_NO_SOCKET; } + void *socket; int err = _iface->socket_accept(_socket, &socket); if (err > 0) { diff --git a/TCPServer.h b/TCPServer.h index 0072ec3f1c..8d52c06bd1 100644 --- a/TCPServer.h +++ b/TCPServer.h @@ -27,8 +27,14 @@ class TCPServer : public Socket { public: /** TCP Server lifetime */ + TCPServer(); TCPServer(NetworkInterface *iface); virtual ~TCPServer(); + + /** Open the socket + * @param iface Interface to open socket on + */ + virtual int open(NetworkInterface *iface); /** Bind a socket to a specific port * @param port The port to listen for incoming connections on diff --git a/TCPSocket.cpp b/TCPSocket.cpp index 13bd8b6c0a..84a86c1ff7 100644 --- a/TCPSocket.cpp +++ b/TCPSocket.cpp @@ -17,11 +17,20 @@ #include "TCPSocket.h" #include "Timer.h" -TCPSocket::TCPSocket(NetworkInterface *iface) - : Socket(iface, NSAPI_TCP) +TCPSocket::TCPSocket() { } +TCPSocket::TCPSocket(NetworkInterface *iface) +{ + open(iface); +} + +int TCPSocket::open(NetworkInterface *iface) +{ + return Socket::open(iface, NSAPI_TCP); +} + int TCPSocket::connect(const SocketAddress &addr) { if (!_socket) { diff --git a/TCPSocket.h b/TCPSocket.h index b4f7917424..31085919eb 100644 --- a/TCPSocket.h +++ b/TCPSocket.h @@ -26,8 +26,14 @@ class TCPSocket : public Socket { public: /** TCP socket lifetime */ + TCPSocket(); TCPSocket(NetworkInterface *iface); virtual ~TCPSocket(); + + /** Open the socket + * @param iface Interface to open socket on + */ + virtual int open(NetworkInterface *iface); /** Connects this TCP socket to the server * @param host The host to connect to. It can either be an IP Address diff --git a/UDPSocket.cpp b/UDPSocket.cpp index 90ed74ad39..3025cd93c4 100644 --- a/UDPSocket.cpp +++ b/UDPSocket.cpp @@ -17,11 +17,20 @@ #include "UDPSocket.h" #include "Timer.h" -UDPSocket::UDPSocket(NetworkInterface *iface) - : Socket(iface, NSAPI_UDP) +UDPSocket::UDPSocket() { } +UDPSocket::UDPSocket(NetworkInterface *iface) +{ + open(iface); +} + +int UDPSocket::open(NetworkInterface *iface) +{ + return Socket::open(iface, NSAPI_UDP); +} + int UDPSocket::bind(uint16_t port) { if (!_socket) { diff --git a/UDPSocket.h b/UDPSocket.h index 1614f8f920..8564713c53 100644 --- a/UDPSocket.h +++ b/UDPSocket.h @@ -26,8 +26,14 @@ class UDPSocket : public Socket { public: /** UDPSocket lifetime */ + UDPSocket(); UDPSocket(NetworkInterface *iface); virtual ~UDPSocket(); + + /** Open the socket + * @param iface Interface to open socket on + */ + virtual int open(NetworkInterface *iface); /** Bind a UDP Server Socket to a specific port * @param port The port to listen for incoming connections on From a7e6c105b3ea2866b134194856a823d0cd207b1e Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sun, 13 Mar 2016 07:13:14 -0500 Subject: [PATCH 04/40] Remove shutdown parameter from close call Pros - Simplifies interface - Easier base implementation Cons - May need shutdown functionality, in this case shutdown can be added as another function in the future --- NetworkInterface.h | 3 +-- Socket.cpp | 6 +++--- Socket.h | 3 +-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/NetworkInterface.h b/NetworkInterface.h index 28ad41e28e..26cd98cbac 100644 --- a/NetworkInterface.h +++ b/NetworkInterface.h @@ -201,9 +201,8 @@ protected: /** Close the socket * @param handle Socket handle - * @param shutdown free the left-over data in message queues */ - virtual int socket_close(void *handle, bool shutdown) = 0; + virtual int socket_close(void *handle) = 0; /** Register a callback on when a new connection is ready * @param handle Socket handle diff --git a/Socket.cpp b/Socket.cpp index e52ba47793..53d66a635b 100644 --- a/Socket.cpp +++ b/Socket.cpp @@ -27,7 +27,7 @@ Socket::Socket() Socket::~Socket() { if (_socket) { - close(false); + close(); } } @@ -37,13 +37,13 @@ int Socket::open(NetworkInterface *iface, nsapi_protocol_t proto) _socket = _iface->socket_create(proto); } -int Socket::close(bool shutdown) +int Socket::close() { if (!_socket) { return 0; } - int err = _iface->socket_close(_socket, shutdown); + int err = _iface->socket_close(_socket); if (!err) { void *socket = _socket; _socket = 0; diff --git a/Socket.h b/Socket.h index ebae4440e0..376e73a41c 100644 --- a/Socket.h +++ b/Socket.h @@ -60,9 +60,8 @@ public: int get_option(int optname, void *optval, unsigned *optlen); /** Close the socket - * @param shutdown free the left-over data in message queues */ - int close(bool shutdown=true); + int close(); protected: Socket(); From dfc1ca4cefa6aae153a458e1b3a6ab0ecaf8bc97 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sun, 13 Mar 2016 11:36:13 -0500 Subject: [PATCH 05/40] Move to single state-change interrupt Pros - Easier to implement - More similar to SIGIO in BDS sockets Cons - Less information, but this information had a high risk of being faulty/spurious --- NetworkInterface.h | 26 +++++--------------------- Socket.cpp | 14 +++++++++++--- Socket.h | 14 ++++++++++++++ TCPServer.cpp | 19 ------------------- TCPServer.h | 14 -------------- TCPSocket.cpp | 34 ---------------------------------- TCPSocket.h | 26 -------------------------- UDPSocket.cpp | 32 -------------------------------- UDPSocket.h | 27 --------------------------- 9 files changed, 30 insertions(+), 176 deletions(-) diff --git a/NetworkInterface.h b/NetworkInterface.h index 26cd98cbac..a3cbbb889b 100644 --- a/NetworkInterface.h +++ b/NetworkInterface.h @@ -204,29 +204,13 @@ protected: */ virtual int socket_close(void *handle) = 0; - /** Register a callback on when a new connection is ready + /** Register a callback on state change of the socket * @param handle Socket handle - * @param callback Function to call when accept will succeed, may be called in - * interrupt context. - * @param id Argument to pass to callback + * @param callback Function to call on state change + * @param data Argument to pass to callback + * @note Callback may be called in an interrupt context. */ - virtual void socket_attach_accept(void *handle, void (*callback)(void *), void *id) = 0; - - /** Register a callback on when send is ready - * @param handle Socket handle - * @param callback Function to call when accept will succeed, may be called in - * interrupt context. - * @param id Argument to pass to callback - */ - virtual void socket_attach_send(void *handle, void (*callback)(void *), void *id) = 0; - - /** Register a callback on when recv is ready - * @param handle Socket handle - * @param callback Function to call when accept will succeed, may be called in - * interrupt context. - * @param id Argument to pass to callback - */ - virtual void socket_attach_recv(void *handle, void (*callback)(void *), void *id) = 0; + virtual void socket_attach(void *handle, void (*callback)(void *), void *data) = 0; }; #endif diff --git a/Socket.cpp b/Socket.cpp index 53d66a635b..a26d9984f9 100644 --- a/Socket.cpp +++ b/Socket.cpp @@ -35,6 +35,7 @@ int Socket::open(NetworkInterface *iface, nsapi_protocol_t proto) { _iface = iface; _socket = _iface->socket_create(proto); + _iface->socket_attach(_socket, &Socket::thunk, this); } int Socket::close() @@ -81,8 +82,15 @@ int Socket::get_option(int optname, void *optval, unsigned int *optlen) return _iface->socket_get_option(_socket, optname, optval, optlen); } -void Socket::thunk(void *p) +void Socket::thunk(void *data) { - FunctionPointer *fptr = (FunctionPointer *)p; - (*fptr)(); + Socket *self = (Socket *)data; + if (self->_callback) { + self->_callback(); + } +} + +void Socket::attach(FunctionPointer callback) +{ + _callback = callback; } diff --git a/Socket.h b/Socket.h index 376e73a41c..e8b96dd8c5 100644 --- a/Socket.h +++ b/Socket.h @@ -63,6 +63,19 @@ public: */ int close(); + /** Register a callback on state change of the socket + * @param callback Function to call on state change + * @note Callback may be called in an interrupt context. + * The callback should not perform long operations + * such as recv or send calls. + */ + void attach(FunctionPointer callback); + + template + void attach(T *tptr, M mptr) { + attach(FunctionPointer(tptr, mptr)); + } + protected: Socket(); int open(NetworkInterface *iface, nsapi_protocol_t proto); @@ -73,6 +86,7 @@ protected: void *_socket; bool _blocking; unsigned _timeout; + FunctionPointer _callback; }; #endif diff --git a/TCPServer.cpp b/TCPServer.cpp index c3e9dd6c18..1430adfa4c 100644 --- a/TCPServer.cpp +++ b/TCPServer.cpp @@ -76,22 +76,3 @@ int TCPServer::accept(TCPSocket *connection) } } } - - -void TCPServer::attach_accept(FunctionPointer callback) -{ - _accept_cb = callback; - - if (_socket && _accept_cb) { - return _iface->socket_attach_accept(_socket, Socket::thunk, &_accept_cb); - } else if (_socket) { - return _iface->socket_attach_accept(_socket, 0, 0); - } -} - -TCPServer::~TCPServer() -{ - if (_socket && _accept_cb) { - _iface->socket_attach_accept(_socket, 0, 0); - } -} diff --git a/TCPServer.h b/TCPServer.h index 8d52c06bd1..84fbe82c97 100644 --- a/TCPServer.h +++ b/TCPServer.h @@ -54,20 +54,6 @@ public: * @return 0 on success, negative on failure. */ int accept(TCPSocket *connection); - - /** Register a callback on when a new connection is ready - * @param callback Function to call when accept will succeed, may be called in - * interrupt context. - */ - void attach_accept(FunctionPointer callback); - - template - void attach_accept(T *tptr, M mptr) { - attach_accept(FunctionPointer(tptr, mptr)); - } - -private: - FunctionPointer _accept_cb; }; #endif diff --git a/TCPSocket.cpp b/TCPSocket.cpp index 84a86c1ff7..6c19ddc2e1 100644 --- a/TCPSocket.cpp +++ b/TCPSocket.cpp @@ -90,37 +90,3 @@ int TCPSocket::recv(void *data, unsigned size) } } } - - -void TCPSocket::attach_send(FunctionPointer callback) -{ - _send_cb = callback; - - if (_socket && _send_cb) { - return _iface->socket_attach_send(_socket, Socket::thunk, &_send_cb); - } else if (_socket) { - return _iface->socket_attach_send(_socket, 0, 0); - } -} - -void TCPSocket::attach_recv(FunctionPointer callback) -{ - _recv_cb = callback; - - if (_socket && _recv_cb) { - return _iface->socket_attach_recv(_socket, Socket::thunk, &_recv_cb); - } else if (_socket) { - return _iface->socket_attach_recv(_socket, 0, 0); - } -} - -TCPSocket::~TCPSocket() -{ - if (_socket && _send_cb) { - _iface->socket_attach_send(_socket, 0, 0); - } - - if (_socket && _recv_cb) { - _iface->socket_attach_recv(_socket, 0, 0); - } -} diff --git a/TCPSocket.h b/TCPSocket.h index 31085919eb..c5a7e97c20 100644 --- a/TCPSocket.h +++ b/TCPSocket.h @@ -28,7 +28,6 @@ public: */ TCPSocket(); TCPSocket(NetworkInterface *iface); - virtual ~TCPSocket(); /** Open the socket * @param iface Interface to open socket on @@ -68,33 +67,8 @@ public: */ int recv(void *data, unsigned size); - /** Register a callback on when send is ready - * @param callback Function to call when send will succeed, may be called in - * interrupt context. - */ - void attach_send(FunctionPointer callback); - - template - void attach_send(T *tptr, M mptr) { - attach_send(FunctionPointer(tptr, mptr)); - } - - /** Register a callback on when recv is ready - * @param callback Function to call when recv will succeed, may be called in - * interrupt context. - */ - void attach_recv(FunctionPointer callback); - - template - void attach_recv(T *tptr, M mptr) { - attach_recv(FunctionPointer(tptr, mptr)); - } - private: friend class TCPServer; - - FunctionPointer _send_cb; - FunctionPointer _recv_cb; }; #endif diff --git a/UDPSocket.cpp b/UDPSocket.cpp index 3025cd93c4..5970d61def 100644 --- a/UDPSocket.cpp +++ b/UDPSocket.cpp @@ -75,35 +75,3 @@ int UDPSocket::recvfrom(SocketAddress *address, void *buffer, unsigned size) } } } - - -void UDPSocket::attach_send(FunctionPointer callback) -{ - _send_cb = callback; - if (_socket && _send_cb) { - return _iface->socket_attach_send(_socket, Socket::thunk, &_send_cb); - } else if (_socket) { - return _iface->socket_attach_send(_socket, 0, 0); - } -} - -void UDPSocket::attach_recv(FunctionPointer callback) -{ - _recv_cb = callback; - if (_socket && _recv_cb) { - return _iface->socket_attach_recv(_socket, Socket::thunk, &_recv_cb); - } else if (_socket) { - return _iface->socket_attach_recv(_socket, 0, 0); - } -} - -UDPSocket::~UDPSocket() -{ - if (_socket && _send_cb) { - _iface->socket_attach_send(_socket, 0, 0); - } - - if (_socket && _recv_cb) { - _iface->socket_attach_recv(_socket, 0, 0); - } -} diff --git a/UDPSocket.h b/UDPSocket.h index 8564713c53..e40d231287 100644 --- a/UDPSocket.h +++ b/UDPSocket.h @@ -28,7 +28,6 @@ public: */ UDPSocket(); UDPSocket(NetworkInterface *iface); - virtual ~UDPSocket(); /** Open the socket * @param iface Interface to open socket on @@ -58,32 +57,6 @@ public: * @return The number of received bytes on success, negative on failure */ int recvfrom(SocketAddress *address, void *buffer, unsigned size); - - /** Register a callback on when send is ready - * @param callback Function to call when send will succeed, may be called in - * interrupt context. - */ - void attach_send(FunctionPointer callback); - - template - void attach_send(T *tptr, M mptr) { - attach_send(FunctionPointer(tptr, mptr)); - } - - /** Register a callback on when recv is ready - * @param callback Function to call when recv will succeed, may be called in - * interrupt context. - */ - void attach_recv(FunctionPointer callback); - - template - void attach_recv(T *tptr, M mptr) { - attach_recv(FunctionPointer(tptr, mptr)); - } - -private: - FunctionPointer _send_cb; - FunctionPointer _recv_cb; }; #endif From 1b368cf525f407a1dbbd99791bb123aa6903278d Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sun, 13 Mar 2016 14:37:55 -0500 Subject: [PATCH 06/40] Renamed NetworkInterface create/destroy methods to match Socket methods - socket_create -> socket_open - socket_destroy -> socket_close --- NetworkInterface.h | 31 +++++++++++++++---------------- Socket.cpp | 22 +++++++++++++--------- TCPServer.cpp | 5 ++--- 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/NetworkInterface.h b/NetworkInterface.h index a3cbbb889b..45123053a2 100644 --- a/NetworkInterface.h +++ b/NetworkInterface.h @@ -89,16 +89,20 @@ protected: friend class TCPSocket; friend class TCPServer; - /** Create a socket - * @param proto The type of socket to open, TCP or UDP - * @return The alocated socket or null on failure + /** Open a socket + * @param handle Handle in which to store new socket + * @param proto Type of socket to open, NSAPI_TCP or NSAPI_UDP + * @return 0 on success, negative on failure */ - virtual void *socket_create(nsapi_protocol_t proto) = 0; + virtual int socket_open(void **handle, nsapi_protocol_t proto) = 0; - /** Destroy a socket - * @param socket Previously allocated socket + /** Close the socket + * @param handle Socket handle + * @return 0 on success, negative on failure + * @note On failure, any memory associated with the socket must still + * be cleaned up */ - virtual void socket_destroy(void *handle) = 0; + virtual int socket_close(void *handle) = 0; /** Set socket options * @param handle Socket handle @@ -147,13 +151,13 @@ protected: virtual bool socket_is_connected(void *handle) = 0; /** Accept a new connection. - * @param handle Socket handle - * @param socket A TCPSocket instance that will handle the incoming connection. - * @return 0 on success, negative on failure. + * @param handle Handle in which to store new socket + * @param server Socket handle to server to accept from + * @return 0 on success, negative on failure * @note This call is not-blocking, if this call would block, must * immediately return NSAPI_ERROR_WOULD_WAIT */ - virtual int socket_accept(void *handle, void **connection) = 0; + virtual int socket_accept(void **handle, void *server) = 0; /** Send data to the remote host * @param handle Socket handle @@ -199,11 +203,6 @@ protected: */ virtual int socket_recvfrom(void *handle, SocketAddress *address, void *buffer, unsigned size) = 0; - /** Close the socket - * @param handle Socket handle - */ - virtual int socket_close(void *handle) = 0; - /** Register a callback on state change of the socket * @param handle Socket handle * @param callback Function to call on state change diff --git a/Socket.cpp b/Socket.cpp index a26d9984f9..1b3858aa91 100644 --- a/Socket.cpp +++ b/Socket.cpp @@ -34,8 +34,17 @@ Socket::~Socket() int Socket::open(NetworkInterface *iface, nsapi_protocol_t proto) { _iface = iface; - _socket = _iface->socket_create(proto); + + void *socket; + int err = _iface->socket_open(&socket, proto); + if (err) { + return err; + } + + _socket = socket; _iface->socket_attach(_socket, &Socket::thunk, this); + + return 0; } int Socket::close() @@ -44,14 +53,9 @@ int Socket::close() return 0; } - int err = _iface->socket_close(_socket); - if (!err) { - void *socket = _socket; - _socket = 0; - _iface->socket_destroy(socket); - } - - return err; + void *socket = _socket; + _socket = 0; + return _iface->socket_close(socket); } void Socket::set_blocking(bool blocking) diff --git a/TCPServer.cpp b/TCPServer.cpp index 1430adfa4c..ccfd65056b 100644 --- a/TCPServer.cpp +++ b/TCPServer.cpp @@ -64,9 +64,8 @@ int TCPServer::accept(TCPSocket *connection) } void *socket; - int err = _iface->socket_accept(_socket, &socket); - - if (err > 0) { + int err = _iface->socket_accept(&socket, _socket); + if (!err) { connection->_socket = socket; } From 3f08f3957c364bbe30041b4ecb4721ba1417f304 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sun, 13 Mar 2016 16:49:18 -0500 Subject: [PATCH 07/40] Added support for storing bytes directly in SocketAddress Bytes are stored by default, however enough space is allocated in a SocketAddress to generate the string representation if necessary. Currently there is no support for shortened addresses --- NetworkInterface.h | 10 +++ SocketAddress.cpp | 172 +++++++++++++++++++++++++++++++++++++++++---- SocketAddress.h | 96 +++++++++++++++++++------ 3 files changed, 245 insertions(+), 33 deletions(-) diff --git a/NetworkInterface.h b/NetworkInterface.h index 45123053a2..76fac217e5 100644 --- a/NetworkInterface.h +++ b/NetworkInterface.h @@ -20,6 +20,7 @@ #include "mbed.h" #include "SocketAddress.h" + /** Enum of standardized error codes * @enum ns_error_t */ @@ -50,6 +51,15 @@ enum nsapi_protocol_t { NSAPI_UDP, /*!< Socket is of UDP type */ }; +/** Maximum size of MAC address representation + */ +#define NSAPI_MAC_SIZE 18 + +/** Maximum number of bytes for MAC address + */ +#define NSAPI_MAC_BYTES 6 + + /** NetworkInterface class * Common interface that is shared between all hardware that * can connect to a network over IP. diff --git a/SocketAddress.cpp b/SocketAddress.cpp index f57fd8253f..af65723ff4 100644 --- a/SocketAddress.cpp +++ b/SocketAddress.cpp @@ -19,14 +19,95 @@ #include #include "mbed.h" +static bool address_is_ipv4(const char *addr) +{ + int i = 0; + + // Check each digit for [0-9.] + for (; addr[i]; i++) { + if (!(addr[i] >= '0' && addr[i] <= '9') && addr[i] != '.') { + return false; + } + } + + // Ending with '.' garuntees host + if (i > 0 && addr[i-1] == '.') { + return false; + } + + return true; +} + +static bool address_is_ipv6(const char *addr) +{ + // Check each digit for [0-9a-fA-F:] + for (int i = 0; addr[i]; i++) { + if (!(addr[i] >= '0' && addr[i] <= '9') && + !(addr[i] >= 'a' && addr[i] <= 'f') && + !(addr[i] >= 'A' && addr[i] <= 'F') && + addr[i] != ':') { + return false; + } + } + + return true; +} + +static void address_to_ipv4(uint8_t *bytes, const char *addr) +{ + sscanf(addr, "%hhd.%hhd.%hhd.%hhd", &bytes[0], &bytes[1], &bytes[2], &bytes[3]); +} + +static void address_to_ipv6(uint8_t *bytes, const char *addr) +{ + // TODO support short form (::1, 2001::ffee:100a) + // Use a more intellegent algorithm + uint16_t shorts[NSAPI_IPv6_BYTES/2]; + sscanf(addr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx", + &shorts[0], &shorts[1], &shorts[2], &shorts[3], + &shorts[4], &shorts[5], &shorts[6], &shorts[7]); + + for (int i = 0; i < NSAPI_IPv6_BYTES/2; i++) { + bytes[2*i+0] = (uint8_t)(shorts[i] >> 8); + bytes[2*i+1] = (uint8_t)(shorts[i] >> 0); + } +} + +static void ipv4_to_address(char *addr, const uint8_t *bytes) +{ + sprintf(addr, "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3]); +} + +static void ipv6_to_address(char *addr, const uint8_t *bytes) +{ + for (int i = 0; i < NSAPI_IPv6_BYTES; i+=2) { + sprintf(&addr[5*i], "%02x%02x", bytes[i], bytes[i+1]); + addr[5*i+4] = ':'; + } + addr[NSAPI_IPv6_BYTES-1] = '\0'; +} + SocketAddress::SocketAddress(NetworkInterface *iface, const char *host, uint16_t port) { - int err = iface->gethostbyname(host, _ip_address); - set_port(port); - - if (err) { - _ip_address[0] = '\0'; - _port = 0; + // Check for valid IP addresses + if (host && address_is_ipv4(host)) { + _ip_version = NSAPI_IPv4; + address_to_ipv4(_ip_bytes, host); + } else if (host && address_is_ipv6(host)) { + _ip_version = NSAPI_IPv6; + address_to_ipv4(_ip_bytes, host); + } else { + // DNS lookup + char addr[NSAPI_IP_SIZE]; + int err = iface->gethostbyname(host, addr); + if (!err) { + set_ip_address(addr); + set_port(port); + } else { + _ip_version = NSAPI_IPv4; + memset(_ip_bytes, 0, NSAPI_IPv4_BYTES); + set_port(0); + } } } @@ -36,16 +117,48 @@ SocketAddress::SocketAddress(const char *addr, uint16_t port) set_port(port); } +SocketAddress::SocketAddress(const void *bytes, nsapi_version_t version, uint16_t port) +{ + set_ip_bytes(bytes, version); + set_port(port); +} + SocketAddress::SocketAddress(const SocketAddress &addr) { - set_ip_address(addr.get_ip_address()); + set_ip_bytes(addr.get_ip_bytes(), addr.get_ip_version()); set_port(addr.get_port()); } void SocketAddress::set_ip_address(const char *addr) { - strncpy(_ip_address, addr, sizeof _ip_address); - _ip_address[sizeof _ip_address - 1] = '\0'; + _ip_address[0] = '\0'; + + if (addr && address_is_ipv4(addr)) { + _ip_version = NSAPI_IPv4; + address_to_ipv4(_ip_bytes, addr); + } else if (addr && address_is_ipv6(addr)) { + _ip_version = NSAPI_IPv6; + address_to_ipv4(_ip_bytes, addr); + } else { + _ip_version = NSAPI_IPv4; + memset(_ip_bytes, 0, NSAPI_IPv4_BYTES); + } +} + +void SocketAddress::set_ip_bytes(const void *bytes, nsapi_version_t version) +{ + _ip_address[0] = '\0'; + + if (_ip_version == NSAPI_IPv4) { + _ip_version = NSAPI_IPv4; + memcpy(_ip_bytes, bytes, NSAPI_IPv4_BYTES); + } else if (_ip_version == NSAPI_IPv6) { + _ip_version = NSAPI_IPv6; + memcpy(_ip_bytes, bytes, NSAPI_IPv6_BYTES); + } else { + _ip_version = NSAPI_IPv4; + memset(_ip_bytes, 0, NSAPI_IPv4_BYTES); + } } void SocketAddress::set_port(uint16_t port) @@ -55,13 +168,48 @@ void SocketAddress::set_port(uint16_t port) const char *SocketAddress::get_ip_address() const { - if (!_ip_address[0]) { - return 0; + char *ip_address = (char *)_ip_address; + + if (!ip_address[0]) { + if (_ip_version == NSAPI_IPv4) { + ipv4_to_address(ip_address, _ip_bytes); + } else if (_ip_version == NSAPI_IPv4) { + ipv6_to_address(ip_address, _ip_bytes); + } } - return _ip_address; + + return ip_address; +} + +const void *SocketAddress::get_ip_bytes() const +{ + return _ip_bytes; +} + +nsapi_version_t SocketAddress::get_ip_version() const +{ + return _ip_version; } uint16_t SocketAddress::get_port() const { return _port; } + +SocketAddress::operator bool() const +{ + int count = 0; + if (_ip_version == NSAPI_IPv4) { + count = NSAPI_IPv4_BYTES; + } else if (_ip_version == NSAPI_IPv6) { + count = NSAPI_IPv6_BYTES; + } + + for (int i = 0; i < count; i++) { + if (_ip_bytes[i]) { + return true; + } + } + + return false; +} diff --git a/SocketAddress.h b/SocketAddress.h index 44e55bc9d1..1f93177b5e 100644 --- a/SocketAddress.h +++ b/SocketAddress.h @@ -19,64 +19,118 @@ #include -/** Maximum size of IP address -*/ -#define NSAPI_IP_SIZE 16 -/** Maximum size of MAC address -*/ -#define NSAPI_MAC_SIZE 18 +/** Maximum size of IP address representation + */ +#define NSAPI_IP_SIZE NSAPI_IPv6_SIZE + +/** Maximum number of bytes for IP address + */ +#define NSAPI_IP_BYTES NSAPI_IPv6_BYTES + +/** Enum of address families + * @enum nsapi_family_t + */ +enum nsapi_version_t { + NSAPI_IPv4, /*!< Address is IPv4 */ + NSAPI_IPv6, /*!< Address is IPv6 */ +}; + +/** Size of IPv4 representation + */ +#define NSAPI_IPv4_SIZE 16 + +/** Number of bytes in IPv4 address + */ +#define NSAPI_IPv4_BYTES 4 + +/** Size of IPv6 representation + */ +#define NSAPI_IPv6_SIZE 40 + +/** Number of bytes in IPv6 address + */ +#define NSAPI_IPv6_BYTES 16 // Predeclared classes class NetworkInterface; -/** - * A general socket address composed of the IP address and port + +/** A general address class composed of the IP address and optional port */ class SocketAddress { public: /** SocketAddress construction using DNS resolution - * @param iface NetworkInterface to use for DNS resolution - * @param addr Null-terminated hostname that will be resolved - * @param port 16-bit port - * @note on failure, IP address and port will be set to null + * @param iface NetworkInterface to use for DNS resolution + * @param addr Null-terminated hostname that will be resolved + * @param port Optional 16-bit port + * @note on failure, IP address and port will be set to zero */ SocketAddress(NetworkInterface *iface, const char *addr, uint16_t port = 0); /** SocketAddress construction - * @param addr Null-terminated IP address - * @param port 16-bit port - * @note on failure, IP address and port will be set to null + * @param addr Null-terminated IP address + * @param port Optional 16-bit port */ SocketAddress(const char *addr = 0, uint16_t port = 0); /** SocketAddress construction - * @param addr SocketAddress to copy + * @param bytes Bytes to assign to address in big-endian order + * @param version IP address version, NSAPI_IPv4 or NSAPI_IPv6 + * @param port Optional 16-bit port + */ + SocketAddress(const void *bytes, nsapi_version_t version, uint16_t port = 0); + + /** SocketAddress construction + * @param addr SocketAddress to copy */ SocketAddress(const SocketAddress &addr); /** Set the IP address - * @param addr Null-terminated string representing the IP address + * @param addr Null-terminated string representing the IP address */ void set_ip_address(const char *addr); + /** Set the IP address bytes directly + * @param bytes Bytes to assign to address in big-endian order + * @param version IP address version, NSAPI_IPv4 or NSAPI_IPv6 + */ + void set_ip_bytes(const void *bytes, nsapi_version_t version); + /** Set the port - * @param port 16-bit port + * @param port 16-bit port */ void set_port(uint16_t port); /** Get the IP address - * @return The string representation of the IP Address + * @return The string representation of the IP Address */ const char *get_ip_address() const; + + /** Get the IP address bytes directly + * @return IP address bytes + */ + const void *get_ip_bytes() const; + + /** Get the type of the IP address + * @return IP address version, NSAPI_IPv4 or NSAPI_IPv6 + */ + nsapi_version_t get_ip_version() const; /** Get the port - * @return The 16-bit port + * @return The 16-bit port */ - uint16_t get_port(void) const; + uint16_t get_port() const; + + /** Determine if address is all zeros + * @return True if address is not zero address + */ + operator bool() const; private: char _ip_address[NSAPI_IP_SIZE]; + uint8_t _ip_bytes[NSAPI_IP_BYTES]; + nsapi_version_t _ip_version; uint16_t _port; }; From bd8cbf0fcb9bafb52d389ccaff2c527d685d056d Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sun, 13 Mar 2016 16:55:34 -0500 Subject: [PATCH 08/40] Move to SocketAddress in gethostbyname --- NetworkInterface.cpp | 11 +++++++++-- NetworkInterface.h | 4 ++-- SocketAddress.cpp | 4 +--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/NetworkInterface.cpp b/NetworkInterface.cpp index 455d3ddf3b..39ccdd69ff 100644 --- a/NetworkInterface.cpp +++ b/NetworkInterface.cpp @@ -17,7 +17,14 @@ #include "DnsQuery.h" #include "mbed.h" -int NetworkInterface::gethostbyname(const char *name, char *dest) +int NetworkInterface::gethostbyname(SocketAddress *address, const char *name) { - return dnsQuery(this, name, dest); + char buffer[NSAPI_IP_SIZE]; + int err = dnsQuery(this, name, buffer); + if (err) { + return err; + } + + address->set_ip_address(buffer); + return 0; } diff --git a/NetworkInterface.h b/NetworkInterface.h index 76fac217e5..da002f6d7e 100644 --- a/NetworkInterface.h +++ b/NetworkInterface.h @@ -87,11 +87,11 @@ public: } /** Looks up the specified host's IP address + * @param address Destination for the host SocketAddress * @param name Hostname to lookup - * @param dest Destination for IP address, must have space for SocketAddress::IP_SIZE * @return 0 on success, negative on failure */ - virtual int gethostbyname(const char *name, char *dest); + virtual int gethostbyname(SocketAddress *address, const char *name); protected: friend class Socket; diff --git a/SocketAddress.cpp b/SocketAddress.cpp index af65723ff4..e8251a7c1b 100644 --- a/SocketAddress.cpp +++ b/SocketAddress.cpp @@ -98,10 +98,8 @@ SocketAddress::SocketAddress(NetworkInterface *iface, const char *host, uint16_t address_to_ipv4(_ip_bytes, host); } else { // DNS lookup - char addr[NSAPI_IP_SIZE]; - int err = iface->gethostbyname(host, addr); + int err = iface->gethostbyname(this, host); if (!err) { - set_ip_address(addr); set_port(port); } else { _ip_version = NSAPI_IPv4; From 56e11d67098b9775c62245bb06b8a33a19500822 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sun, 13 Mar 2016 17:25:05 -0500 Subject: [PATCH 09/40] Added better support for SocketAddress/string addresses/ports --- NetworkInterface.h | 10 +++++----- TCPServer.cpp | 16 ++++++++++++++-- TCPServer.h | 19 ++++++++++++++++--- TCPSocket.cpp | 2 +- UDPSocket.cpp | 24 +++++++++++++++++++++++- UDPSocket.h | 23 +++++++++++++++++++++++ 6 files changed, 82 insertions(+), 12 deletions(-) diff --git a/NetworkInterface.h b/NetworkInterface.h index da002f6d7e..4f1c2cd139 100644 --- a/NetworkInterface.h +++ b/NetworkInterface.h @@ -22,7 +22,7 @@ /** Enum of standardized error codes - * @enum ns_error_t + * @enum nsapi_error_t */ enum nsapi_error_t { NSAPI_ERROR_WOULD_BLOCK = -3001, /*!< no data is not available but call is non-blocking */ @@ -38,9 +38,9 @@ enum nsapi_error_t { }; /** Enum of available options - * @enum ns_opt_t + * @enum nsapi_opt_t */ -enum ns_opt_t { +enum nsapi_opt_t { }; /** Enum of socket protocols @@ -134,10 +134,10 @@ protected: /** Bind a server socket to a specific port * @param handle Socket handle - * @param port The port to listen for incoming connections on + * @param address Local address to listen for incoming connections on * @return 0 on success, negative on failure. */ - virtual int socket_bind(void *handle, int port) = 0; + virtual int socket_bind(void *handle, const SocketAddress &address) = 0; /** Start listening for incoming connections * @param handle Socket handle diff --git a/TCPServer.cpp b/TCPServer.cpp index ccfd65056b..b2b7e57a6a 100644 --- a/TCPServer.cpp +++ b/TCPServer.cpp @@ -32,12 +32,24 @@ int TCPServer::open(NetworkInterface *iface) } int TCPServer::bind(uint16_t port) +{ + SocketAddress addr(0, port); + return bind(addr); +} + +int TCPServer::bind(const char *address, uint16_t port) +{ + SocketAddress addr(address, port); + return bind(addr); +} + +int TCPServer::bind(const SocketAddress &address) { if (!_socket) { - return NSAPI_ERROR_NO_SOCKET; + return NSAPI_ERROR_NO_SOCKET; } - return _iface->socket_bind(_socket, port); + return _iface->socket_bind(_socket, address); } int TCPServer::listen(int backlog) diff --git a/TCPServer.h b/TCPServer.h index 84fbe82c97..17b2a48c7b 100644 --- a/TCPServer.h +++ b/TCPServer.h @@ -36,11 +36,24 @@ public: */ virtual int open(NetworkInterface *iface); - /** Bind a socket to a specific port - * @param port The port to listen for incoming connections on - * @return 0 on success, negative on failure + /** Bind a TCP Server to a specific port + * @param port The port to listen for incoming connections on + * @return 0 on success, negative on failure. */ int bind(uint16_t port); + + /** Bind a TCP Server to a local address + * @param address The null-terminated address to listen for incoming connections on + * @param port The port to listen for incoming connections on + * @return 0 on success, negative on failure. + */ + int bind(const char *address, uint16_t port); + + /** Bind a TCP Server to a local address + * @param address The SocketAddress to listen for incoming connections on + * @return 0 on success, negative on failure. + */ + int bind(const SocketAddress &address); /** Start listening for incoming connections * @param backlog Number of pending connections that can be queued up at any diff --git a/TCPSocket.cpp b/TCPSocket.cpp index 6c19ddc2e1..a5225ce56c 100644 --- a/TCPSocket.cpp +++ b/TCPSocket.cpp @@ -43,7 +43,7 @@ int TCPSocket::connect(const SocketAddress &addr) int TCPSocket::connect(const char *host, uint16_t port) { SocketAddress addr(_iface, host, port); - if (!addr.get_ip_address()) { + if (!addr) { return NSAPI_ERROR_DNS_FAILURE; } diff --git a/UDPSocket.cpp b/UDPSocket.cpp index 5970d61def..6e210946e9 100644 --- a/UDPSocket.cpp +++ b/UDPSocket.cpp @@ -32,12 +32,34 @@ int UDPSocket::open(NetworkInterface *iface) } int UDPSocket::bind(uint16_t port) +{ + SocketAddress addr(0, port); + return bind(addr); +} + +int UDPSocket::bind(const char *address, uint16_t port) +{ + SocketAddress addr(address, port); + return bind(addr); +} + +int UDPSocket::bind(const SocketAddress &address) { if (!_socket) { return NSAPI_ERROR_NO_SOCKET; } - return _iface->socket_bind(_socket, port); + return _iface->socket_bind(_socket, address); +} + +int UDPSocket::sendto(const char *host, uint16_t port, const void *data, unsigned size) +{ + SocketAddress addr(_iface, host, port); + if (!addr) { + return NSAPI_ERROR_DNS_FAILURE; + } + + return sendto(addr, data, size); } int UDPSocket::sendto(const SocketAddress &address, const void *data, unsigned size) diff --git a/UDPSocket.h b/UDPSocket.h index e40d231287..f17ad6c8a3 100644 --- a/UDPSocket.h +++ b/UDPSocket.h @@ -40,6 +40,29 @@ public: */ int bind(uint16_t port); + /** Bind a UDP Server Socket to a local address + * @param address The null-terminated address to listen for incoming connections on + * @param port The port to listen for incoming connections on + * @return 0 on success, negative on failure. + */ + int bind(const char *address, uint16_t port); + + /** Bind a UDP Server Socket to a local address + * @param address The SocketAddress to listen for incoming connections on + * @return 0 on success, negative on failure. + */ + int bind(const SocketAddress &address); + + /** Send a packet to a remote endpoint + * @param host The host to connect to. It can either be an IP Address + * or a hostname that will be resolved with DNS + * @param port The remote port + * @param data The packet to be sent + * @param size The length of the packet to be sent + * @return The number of written bytes on success, negative on failure + */ + int sendto(const char *host, uint16_t port, const void *data, unsigned size); + /** Send a packet to a remote endpoint * @param address The remote SocketAddress * @param data The packet to be sent From aea45c88ced4ba61edceaab6c35d875910e72ca5 Mon Sep 17 00:00:00 2001 From: Russ Butler Date: Mon, 18 Apr 2016 21:26:39 -0500 Subject: [PATCH 10/40] Fix ipv6 addr in SocketAddress Correctly set and return the ipv6 address. --- SocketAddress.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/SocketAddress.cpp b/SocketAddress.cpp index e8251a7c1b..a30daaf585 100644 --- a/SocketAddress.cpp +++ b/SocketAddress.cpp @@ -80,11 +80,20 @@ static void ipv4_to_address(char *addr, const uint8_t *bytes) static void ipv6_to_address(char *addr, const uint8_t *bytes) { + int pos = 0; for (int i = 0; i < NSAPI_IPv6_BYTES; i+=2) { - sprintf(&addr[5*i], "%02x%02x", bytes[i], bytes[i+1]); - addr[5*i+4] = ':'; + int ret = sprintf(&addr[pos], "%02x%02x", bytes[i], bytes[i+1]); + if (ret < 0) { + memset(addr, 0, NSAPI_IPv6_SIZE + 1); + return; + } + pos += ret; + + addr[pos++] = ':'; } - addr[NSAPI_IPv6_BYTES-1] = '\0'; + pos -= 1; // Overwrite last ':' + addr[pos++] = '\0'; + MBED_ASSERT(NSAPI_IPv6_SIZE == pos); } SocketAddress::SocketAddress(NetworkInterface *iface, const char *host, uint16_t port) @@ -136,7 +145,7 @@ void SocketAddress::set_ip_address(const char *addr) address_to_ipv4(_ip_bytes, addr); } else if (addr && address_is_ipv6(addr)) { _ip_version = NSAPI_IPv6; - address_to_ipv4(_ip_bytes, addr); + address_to_ipv6(_ip_bytes, addr); } else { _ip_version = NSAPI_IPv4; memset(_ip_bytes, 0, NSAPI_IPv4_BYTES); @@ -171,7 +180,7 @@ const char *SocketAddress::get_ip_address() const if (!ip_address[0]) { if (_ip_version == NSAPI_IPv4) { ipv4_to_address(ip_address, _ip_bytes); - } else if (_ip_version == NSAPI_IPv4) { + } else if (_ip_version == NSAPI_IPv6) { ipv6_to_address(ip_address, _ip_bytes); } } From 63725d653c788bda054e0b4cf582606a2182cd8e Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sun, 13 Mar 2016 17:57:17 -0500 Subject: [PATCH 11/40] Move bind to Socket Bind can operate on any IP socket and is not specific to a protocol --- Socket.cpp | 21 +++++++++++++++++++++ Socket.h | 19 +++++++++++++++++++ TCPServer.cpp | 21 --------------------- TCPServer.h | 19 ------------------- UDPSocket.cpp | 21 --------------------- UDPSocket.h | 19 ------------------- 6 files changed, 40 insertions(+), 80 deletions(-) diff --git a/Socket.cpp b/Socket.cpp index 1b3858aa91..0d6e652993 100644 --- a/Socket.cpp +++ b/Socket.cpp @@ -58,6 +58,27 @@ int Socket::close() return _iface->socket_close(socket); } +int Socket::bind(uint16_t port) +{ + SocketAddress addr(0, port); + return bind(addr); +} + +int Socket::bind(const char *address, uint16_t port) +{ + SocketAddress addr(address, port); + return bind(addr); +} + +int Socket::bind(const SocketAddress &address) +{ + if (!_socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + return _iface->socket_bind(_socket, address); +} + void Socket::set_blocking(bool blocking) { _blocking = blocking; diff --git a/Socket.h b/Socket.h index e8b96dd8c5..68353f1b12 100644 --- a/Socket.h +++ b/Socket.h @@ -33,6 +33,25 @@ public: */ virtual int open(NetworkInterface *iface) = 0; + /** Bind a socket to a specific port + * @param port The port to listen for incoming connections on + * @return 0 on success, negative on failure. + */ + int bind(uint16_t port); + + /** Bind a socket to a specific port + * @param address The null-terminated address to listen for incoming connections on + * @param port The port to listen for incoming connections on + * @return 0 on success, negative on failure. + */ + int bind(const char *address, uint16_t port); + + /** Bind a socket to a specific port + * @param address The SocketAddress to listen for incoming connections on + * @return 0 on success, negative on failure. + */ + int bind(const SocketAddress &address); + /** Set blocking or non-blocking mode of the socket * @param blocking true for blocking mode, false for non-blocking mode. */ diff --git a/TCPServer.cpp b/TCPServer.cpp index b2b7e57a6a..6c513f4be0 100644 --- a/TCPServer.cpp +++ b/TCPServer.cpp @@ -31,27 +31,6 @@ int TCPServer::open(NetworkInterface *iface) return Socket::open(iface, NSAPI_TCP); } -int TCPServer::bind(uint16_t port) -{ - SocketAddress addr(0, port); - return bind(addr); -} - -int TCPServer::bind(const char *address, uint16_t port) -{ - SocketAddress addr(address, port); - return bind(addr); -} - -int TCPServer::bind(const SocketAddress &address) -{ - if (!_socket) { - return NSAPI_ERROR_NO_SOCKET; - } - - return _iface->socket_bind(_socket, address); -} - int TCPServer::listen(int backlog) { if (!_socket) { diff --git a/TCPServer.h b/TCPServer.h index 17b2a48c7b..59fbbac5fb 100644 --- a/TCPServer.h +++ b/TCPServer.h @@ -36,25 +36,6 @@ public: */ virtual int open(NetworkInterface *iface); - /** Bind a TCP Server to a specific port - * @param port The port to listen for incoming connections on - * @return 0 on success, negative on failure. - */ - int bind(uint16_t port); - - /** Bind a TCP Server to a local address - * @param address The null-terminated address to listen for incoming connections on - * @param port The port to listen for incoming connections on - * @return 0 on success, negative on failure. - */ - int bind(const char *address, uint16_t port); - - /** Bind a TCP Server to a local address - * @param address The SocketAddress to listen for incoming connections on - * @return 0 on success, negative on failure. - */ - int bind(const SocketAddress &address); - /** Start listening for incoming connections * @param backlog Number of pending connections that can be queued up at any * one time [Default: 1] diff --git a/UDPSocket.cpp b/UDPSocket.cpp index 6e210946e9..9177de60c8 100644 --- a/UDPSocket.cpp +++ b/UDPSocket.cpp @@ -31,27 +31,6 @@ int UDPSocket::open(NetworkInterface *iface) return Socket::open(iface, NSAPI_UDP); } -int UDPSocket::bind(uint16_t port) -{ - SocketAddress addr(0, port); - return bind(addr); -} - -int UDPSocket::bind(const char *address, uint16_t port) -{ - SocketAddress addr(address, port); - return bind(addr); -} - -int UDPSocket::bind(const SocketAddress &address) -{ - if (!_socket) { - return NSAPI_ERROR_NO_SOCKET; - } - - return _iface->socket_bind(_socket, address); -} - int UDPSocket::sendto(const char *host, uint16_t port, const void *data, unsigned size) { SocketAddress addr(_iface, host, port); diff --git a/UDPSocket.h b/UDPSocket.h index f17ad6c8a3..4fecb7cf18 100644 --- a/UDPSocket.h +++ b/UDPSocket.h @@ -33,25 +33,6 @@ public: * @param iface Interface to open socket on */ virtual int open(NetworkInterface *iface); - - /** Bind a UDP Server Socket to a specific port - * @param port The port to listen for incoming connections on - * @return 0 on success, negative on failure. - */ - int bind(uint16_t port); - - /** Bind a UDP Server Socket to a local address - * @param address The null-terminated address to listen for incoming connections on - * @param port The port to listen for incoming connections on - * @return 0 on success, negative on failure. - */ - int bind(const char *address, uint16_t port); - - /** Bind a UDP Server Socket to a local address - * @param address The SocketAddress to listen for incoming connections on - * @return 0 on success, negative on failure. - */ - int bind(const SocketAddress &address); /** Send a packet to a remote endpoint * @param host The host to connect to. It can either be an IP Address From 3fa1bb6469f90fb846392d980d9e3c1889cde633 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sun, 13 Mar 2016 18:47:41 -0500 Subject: [PATCH 12/40] Revised stack specific configurations Adds the following functions for direct configuration of interface - (set|get)stackopt - (set|get)sockopt --- NetworkInterface.cpp | 20 ++++++++++++++ NetworkInterface.h | 62 +++++++++++++++++++++++++++----------------- Socket.cpp | 9 ++++--- Socket.h | 26 ++++++++++--------- 4 files changed, 77 insertions(+), 40 deletions(-) diff --git a/NetworkInterface.cpp b/NetworkInterface.cpp index 39ccdd69ff..a39fdc7c43 100644 --- a/NetworkInterface.cpp +++ b/NetworkInterface.cpp @@ -28,3 +28,23 @@ int NetworkInterface::gethostbyname(SocketAddress *address, const char *name) address->set_ip_address(buffer); return 0; } + +int NetworkInterface::setstackopt(int level, int optname, const void *optval, unsigned optlen) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +int NetworkInterface::getstackopt(int level, int optname, void *optval, unsigned *optlen) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +int NetworkInterface::setsockopt(void *handle, int level, int optname, const void *optval, unsigned optlen) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +int NetworkInterface::getsockopt(void *handle, int level, int optname, void *optval, unsigned *optlen) +{ + return NSAPI_ERROR_UNSUPPORTED; +} diff --git a/NetworkInterface.h b/NetworkInterface.h index 4f1c2cd139..162fbe0861 100644 --- a/NetworkInterface.h +++ b/NetworkInterface.h @@ -36,12 +36,6 @@ enum nsapi_error_t { NSAPI_ERROR_AUTH_FAILURE = -3009, /*!< connection to access point faield */ NSAPI_ERROR_DEVICE_ERROR = -3010, /*!< failure interfacing with the network procesor */ }; - -/** Enum of available options - * @enum nsapi_opt_t - */ -enum nsapi_opt_t { -}; /** Enum of socket protocols * @enum protocol_t @@ -93,6 +87,24 @@ public: */ virtual int gethostbyname(SocketAddress *address, const char *name); + /* Set stack options + * @param level Option level + * @param optname Option identifier + * @param optval Option value + * @param optlen Length of the option value + * @return 0 on success, negative on failure + */ + virtual int setstackopt(int level, int optname, const void *optval, unsigned optlen); + + /* Get stack options + * @param level Option level + * @param optname Option identifier + * @param optval Buffer where to write option value + * @param optlen Length of the option value + * @return 0 on success, negative on failure + */ + virtual int getstackopt(int level, int optname, void *optval, unsigned *optlen); + protected: friend class Socket; friend class UDPSocket; @@ -114,24 +126,6 @@ protected: */ virtual int socket_close(void *handle) = 0; - /** Set socket options - * @param handle Socket handle - * @param optname Option ID - * @param optval Option value - * @param optlen Length of the option value - * @return 0 on success, negative on failure - */ - virtual int socket_set_option(void *handle, int optname, const void *optval, unsigned int optlen) = 0; - - /** Get socket options - * @param handle Socket handle - * @param optname Option ID - * @param optval Buffer pointer where to write the option value - * @param optlen Length of the option value - * @return 0 on success, negative on failure - */ - virtual int socket_get_option(void *handle, int optname, void *optval, unsigned int *optlen) = 0; - /** Bind a server socket to a specific port * @param handle Socket handle * @param address Local address to listen for incoming connections on @@ -220,6 +214,26 @@ protected: * @note Callback may be called in an interrupt context. */ virtual void socket_attach(void *handle, void (*callback)(void *), void *data) = 0; + + /* Set socket options + * @param handle Socket handle + * @param level Option level + * @param optname Option identifier + * @param optval Option value + * @param optlen Length of the option value + * @return 0 on success, negative on failure + */ + virtual int setsockopt(void *handle, int level, int optname, const void *optval, unsigned optlen); + + /* Get socket options + * @param handle Socket handle + * @param level Option level + * @param optname Option identifier + * @param optval Buffer where to write option value + * @param optlen Length of the option value + * @return 0 on success, negative on failure + */ + virtual int getsockopt(void *handle, int level, int optname, void *optval, unsigned *optlen); }; #endif diff --git a/Socket.cpp b/Socket.cpp index 0d6e652993..e291f7e1a5 100644 --- a/Socket.cpp +++ b/Socket.cpp @@ -89,22 +89,23 @@ void Socket::set_timeout(unsigned timeout) _timeout = timeout; } -int Socket::set_option(int optname, const void *optval, unsigned int optlen) +int Socket::setsockopt(int level, int optname, const void *optval, unsigned optlen) { if (!_socket) { return NSAPI_ERROR_NO_SOCKET; } - return _iface->socket_set_option(_socket, optname, optval, optlen); + return _iface->setsockopt(_socket, level, optname, optval, optlen); } -int Socket::get_option(int optname, void *optval, unsigned int *optlen) +int Socket::getsockopt(int level, int optname, void *optval, unsigned *optlen) { if (!_socket) { return NSAPI_ERROR_NO_SOCKET; } - return _iface->socket_get_option(_socket, optname, optval, optlen); + return _iface->getsockopt(_socket, level, optname, optval, optlen); + } void Socket::thunk(void *data) diff --git a/Socket.h b/Socket.h index 68353f1b12..59e8f9558f 100644 --- a/Socket.h +++ b/Socket.h @@ -33,6 +33,10 @@ public: */ virtual int open(NetworkInterface *iface) = 0; + /** Close the socket + */ + int close(); + /** Bind a socket to a specific port * @param port The port to listen for incoming connections on * @return 0 on success, negative on failure. @@ -63,24 +67,22 @@ public: void set_timeout(unsigned int timeout); /* Set socket options - * @param optname Option ID + * @param level Option level + * @param optname Option identifier * @param optval Option value * @param optlen Length of the option value * @return 0 on success, negative on failure - */ - int set_option(int optname, const void *optval, unsigned optlen); - + */ + int setsockopt(int level, int optname, const void *optval, unsigned optlen); + /* Get socket options - * @param optname Option ID - * @param optval Buffer pointer where to write the option value + * @param level Option level + * @param optname Option identifier + * @param optval Buffer where to write option value * @param optlen Length of the option value * @return 0 on success, negative on failure - */ - int get_option(int optname, void *optval, unsigned *optlen); - - /** Close the socket - */ - int close(); + */ + int getsockopt(int level, int optname, void *optval, unsigned *optlen); /** Register a callback on state change of the socket * @param callback Function to call on state change From d84f4be6eda9094a28b937c7db4b27859100c86b Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sun, 13 Mar 2016 19:30:30 -0500 Subject: [PATCH 13/40] Added support for shortened form ipv6 addresses supported: 1.2.3.4 1:2:3:4:5:6:7:8 1:2::7:8 :: currently not supported: 1:2:3:4:5:6:1.2.3.4 --- SocketAddress.cpp | 96 +++++++++++++++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 33 deletions(-) diff --git a/SocketAddress.cpp b/SocketAddress.cpp index a30daaf585..f8446ea349 100644 --- a/SocketAddress.cpp +++ b/SocketAddress.cpp @@ -19,7 +19,8 @@ #include #include "mbed.h" -static bool address_is_ipv4(const char *addr) + +static bool ipv4_is_valid(const char *addr) { int i = 0; @@ -38,7 +39,7 @@ static bool address_is_ipv4(const char *addr) return true; } -static bool address_is_ipv6(const char *addr) +static bool ipv6_is_valid(const char *addr) { // Check each digit for [0-9a-fA-F:] for (int i = 0; addr[i]; i++) { @@ -53,20 +54,57 @@ static bool address_is_ipv6(const char *addr) return true; } -static void address_to_ipv4(uint8_t *bytes, const char *addr) +static void ipv4_from_address(uint8_t *bytes, const char *addr) { - sscanf(addr, "%hhd.%hhd.%hhd.%hhd", &bytes[0], &bytes[1], &bytes[2], &bytes[3]); + sscanf(addr, "%hhu.%hhu.%hhu.%hhu", &bytes[0], &bytes[1], &bytes[2], &bytes[3]); } -static void address_to_ipv6(uint8_t *bytes, const char *addr) -{ - // TODO support short form (::1, 2001::ffee:100a) - // Use a more intellegent algorithm - uint16_t shorts[NSAPI_IPv6_BYTES/2]; - sscanf(addr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx", - &shorts[0], &shorts[1], &shorts[2], &shorts[3], - &shorts[4], &shorts[5], &shorts[6], &shorts[7]); +static int ipv6_scan_chunk(uint16_t *shorts, const char *chunk) { + int count = 0; + int i = 0; + for (; count < NSAPI_IPv6_BYTES/2; count++) { + int scanned = sscanf(&chunk[i], "%hx", &shorts[count]); + if (scanned < 1) { + return count; + } + + for (; chunk[i] != ':'; i++) { + if (!chunk[i]) { + return count+1; + } + } + + i++; + } + + return count; +} + +static void ipv6_from_address(uint8_t *bytes, const char *addr) +{ + // Start with zeroed address + uint16_t shorts[NSAPI_IPv6_BYTES/2]; + memset(shorts, 0, sizeof shorts); + + int suffix = 0; + + // Find double colons and scan suffix + for (int i = 0; addr[i]; i++) { + if (addr[i] == ':' && addr[i+1] == ':') { + suffix = ipv6_scan_chunk(shorts, &addr[i+2]); + break; + } + } + + // Move suffix to end + memmove(&shorts[NSAPI_IPv6_BYTES/2-suffix], &shorts[0], + suffix*sizeof(uint16_t)); + + // Scan prefix + ipv6_scan_chunk(shorts, &addr[0]); + + // Flip bytes for (int i = 0; i < NSAPI_IPv6_BYTES/2; i++) { bytes[2*i+0] = (uint8_t)(shorts[i] >> 8); bytes[2*i+1] = (uint8_t)(shorts[i] >> 0); @@ -80,31 +118,23 @@ static void ipv4_to_address(char *addr, const uint8_t *bytes) static void ipv6_to_address(char *addr, const uint8_t *bytes) { - int pos = 0; - for (int i = 0; i < NSAPI_IPv6_BYTES; i+=2) { - int ret = sprintf(&addr[pos], "%02x%02x", bytes[i], bytes[i+1]); - if (ret < 0) { - memset(addr, 0, NSAPI_IPv6_SIZE + 1); - return; - } - pos += ret; - - addr[pos++] = ':'; + for (int i = 0; i < NSAPI_IPv6_BYTES/2; i++) { + sprintf(&addr[5*i], "%02x%02x", bytes[2*i], bytes[2*i+1]); + addr[5*i+4] = ':'; } - pos -= 1; // Overwrite last ':' - addr[pos++] = '\0'; - MBED_ASSERT(NSAPI_IPv6_SIZE == pos); + addr[NSAPI_IPv6_SIZE-1] = '\0'; } + SocketAddress::SocketAddress(NetworkInterface *iface, const char *host, uint16_t port) { // Check for valid IP addresses - if (host && address_is_ipv4(host)) { + if (host && ipv4_is_valid(host)) { _ip_version = NSAPI_IPv4; - address_to_ipv4(_ip_bytes, host); - } else if (host && address_is_ipv6(host)) { + ipv4_from_address(_ip_bytes, host); + } else if (host && ipv6_is_valid(host)) { _ip_version = NSAPI_IPv6; - address_to_ipv4(_ip_bytes, host); + ipv4_from_address(_ip_bytes, host); } else { // DNS lookup int err = iface->gethostbyname(this, host); @@ -140,12 +170,12 @@ void SocketAddress::set_ip_address(const char *addr) { _ip_address[0] = '\0'; - if (addr && address_is_ipv4(addr)) { + if (addr && ipv4_is_valid(addr)) { _ip_version = NSAPI_IPv4; - address_to_ipv4(_ip_bytes, addr); - } else if (addr && address_is_ipv6(addr)) { + ipv4_from_address(_ip_bytes, addr); + } else if (addr && ipv6_is_valid(addr)) { _ip_version = NSAPI_IPv6; - address_to_ipv6(_ip_bytes, addr); + ipv6_from_address(_ip_bytes, addr); } else { _ip_version = NSAPI_IPv4; memset(_ip_bytes, 0, NSAPI_IPv4_BYTES); From b9d9842846ec3710618f36110763c970de7f2518 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 19 Apr 2016 15:47:36 -0500 Subject: [PATCH 14/40] Revisited documentation for NetworkInterface specific methods --- NetworkInterface.h | 262 ++++++++++++++++++++++++++++++--------------- 1 file changed, 173 insertions(+), 89 deletions(-) diff --git a/NetworkInterface.h b/NetworkInterface.h index 162fbe0861..63e1800732 100644 --- a/NetworkInterface.h +++ b/NetworkInterface.h @@ -21,7 +21,11 @@ #include "SocketAddress.h" -/** Enum of standardized error codes +/** Enum of standardized error codes + * + * Valid error codes have negative values and may + * be returned by any network operation. + * * @enum nsapi_error_t */ enum nsapi_error_t { @@ -38,6 +42,10 @@ enum nsapi_error_t { }; /** Enum of socket protocols + * + * The socket protocol specifies a particular protocol to + * be used with a newly created socket. + * * @enum protocol_t */ enum nsapi_protocol_t { @@ -55,8 +63,11 @@ enum nsapi_protocol_t { /** NetworkInterface class - * Common interface that is shared between all hardware that - * can connect to a network over IP. + * + * Common interface that is shared between hardware that + * can connect to a network over IP. By implementing the + * NetworkInterface, a network stack can be used as a target + * for instantiating network sockets. */ class NetworkInterface { @@ -64,44 +75,54 @@ public: virtual ~NetworkInterface() {}; /** Get the internally stored IP address + * * @return IP address of the interface or null if not yet connected */ virtual const char *get_ip_address() = 0; /** Get the internally stored MAC address + * * @return MAC address of the interface */ virtual const char *get_mac_address() = 0; - /** Get the current status of the interface - * @return true if connected - */ - virtual bool is_connected() { - return get_ip_address() != NULL; - } - - /** Looks up the specified host's IP address + /** Translates a host name to an IP address + * + * The host name may be either a domain name or an IP address. + * If no stack-specific DNS resolution is provided, the host name + * will be resolve using a UDP socket on the stack. + * * @param address Destination for the host SocketAddress - * @param name Hostname to lookup - * @return 0 on success, negative on failure + * @param host Host name to lookup + * @return 0 on success, negative error code on failure */ - virtual int gethostbyname(SocketAddress *address, const char *name); + virtual int gethostbyname(SocketAddress *address, const char *host); - /* Set stack options - * @param level Option level - * @param optname Option identifier + /* Set stack-specific stack options + * + * The setstackopt allow an application to pass stack-specific hints + * to the underlying stack. For unsupported options, + * NSAPI_ERROR_UNSUPPORTED is returned and the stack is unmodified. + * + * @param level Stack-specific protocol level + * @param optname Stack-specific option identifier * @param optval Option value * @param optlen Length of the option value - * @return 0 on success, negative on failure + * @return 0 on success, negative error code on failure */ virtual int setstackopt(int level, int optname, const void *optval, unsigned optlen); - /* Get stack options - * @param level Option level - * @param optname Option identifier - * @param optval Buffer where to write option value + /* Get stack-specific stack options + * + * The getstackopt allow an application to retrieve stack-specific hints + * from the underlying stack. For unsupported options, + * NSAPI_ERROR_UNSUPPORTED is returned and optval is unmodified. + * + * @param level Stack-specific protocol level + * @param optname Stack-specific option identifier + * @param optval Destination for option value * @param optlen Length of the option value - * @return 0 on success, negative on failure + * @return 0 on success, negative error code on failure */ virtual int getstackopt(int level, int optname, void *optval, unsigned *optlen); @@ -111,127 +132,190 @@ protected: friend class TCPSocket; friend class TCPServer; - /** Open a socket - * @param handle Handle in which to store new socket - * @param proto Type of socket to open, NSAPI_TCP or NSAPI_UDP - * @return 0 on success, negative on failure + /** Opens a socket + * + * Creates a socket for communication and stores it in the specified + * handle. The handle must be passed to following calls on the socket. + * + * A stack may have a finite number of sockets, in this case + * NSAPI_ERROR_NO_SOCKET is returned if no socket is available. + * + * @param handle Destination for the handle to a newly created socket + * @param proto Protocol of socket to open, NSAPI_TCP or NSAPI_UDP + * @return 0 on success, negative error code on failure */ virtual int socket_open(void **handle, nsapi_protocol_t proto) = 0; /** Close the socket + * + * Closes any open connection and deallocates any memory associated with + * the socket. + * * @param handle Socket handle - * @return 0 on success, negative on failure - * @note On failure, any memory associated with the socket must still - * be cleaned up + * @return 0 on success, negative error code on failure */ virtual int socket_close(void *handle) = 0; - /** Bind a server socket to a specific port + /** Bind a specific address to a socket + * + * Binding a socket specifies the address and port on which to recieve + * data. If the IP address is zeroed, only the port is bound. + * * @param handle Socket handle - * @param address Local address to listen for incoming connections on - * @return 0 on success, negative on failure. + * @param address Local address to bind + * @return 0 on success, negative error code on failure. */ virtual int socket_bind(void *handle, const SocketAddress &address) = 0; - /** Start listening for incoming connections + /** Listen for connections on a TCP socket + * + * Marks the socket as a passive socket that can be used to accept + * incoming connections. + * * @param handle Socket handle - * @param backlog Number of pending connections that can be queued up at any - * one time [Default: 1] - * @return 0 on success, negative on failure + * @param backlog Number of pending connections that can be queued + * simultaneously, defaults to 1 + * @return 0 on success, negative error code on failure */ virtual int socket_listen(void *handle, int backlog) = 0; - /** Connects this TCP socket to the server + /** Connects TCP socket to a remote host + * + * Initiates a connection to a remote server specified by the + * indicated address. + * * @param handle Socket handle - * @param address SocketAddress to connect to - * @return 0 on success, negative on failure + * @param address The SocketAddress of the remote host + * @return 0 on success, negative error code on failure */ virtual int socket_connect(void *handle, const SocketAddress &address) = 0; - - /** Check if the socket is connected - * @param handle Socket handle - * @return true if connected, false otherwise - */ - virtual bool socket_is_connected(void *handle) = 0; - /** Accept a new connection. - * @param handle Handle in which to store new socket + /** Accepts a connection on a TCP socket + * + * The server socket must be bound and set to listen for connections. + * On a new connection, creates a socket stores it in the specified + * handle. The handle must be passed to following calls on the socket. + * + * A stack may have a finite number of sockets, in this case + * NSAPI_ERROR_NO_SOCKET is returned if no socket is available. + * + * This call is non-blocking. If accept would block, + * NSAPI_ERROR_WOULD_BLOCK is returned immediately. + * + * @param handle Destination for a handle to the newly created sockey * @param server Socket handle to server to accept from - * @return 0 on success, negative on failure - * @note This call is not-blocking, if this call would block, must - * immediately return NSAPI_ERROR_WOULD_WAIT + * @return 0 on success, negative error code on failure */ virtual int socket_accept(void **handle, void *server) = 0; - /** Send data to the remote host + /** Send data over a TCP socket + * + * The socket must be connected to a remote host. Returns the number of + * bytes sent from the buffer. + * + * This call is non-blocking. If send would block, + * NSAPI_ERROR_WOULD_BLOCK is returned immediately. + * * @param handle Socket handle - * @param data The buffer to send to the host - * @param size The length of the buffer to send - * @return Number of written bytes on success, negative on failure - * @note This call is not-blocking, if this call would block, must - * immediately return NSAPI_ERROR_WOULD_WAIT + * @param data Buffer of data to send to the host + * @param size Size of the buffer in bytes + * @return Number of sent bytes on success, negative error + * code on failure */ virtual int socket_send(void *handle, const void *data, unsigned size) = 0; - /** Receive data from the remote host + /** Receive data over a TCP socket + * + * The socket must be connected to a remote host. Returns the number of + * bytes received into the buffer. + * + * This call is non-blocking. If recv would block, + * NSAPI_ERROR_WOULD_BLOCK is returned immediately. + * * @param handle Socket handle - * @param data The buffer in which to store the data received from the host - * @param size The maximum length of the buffer - * @return Number of received bytes on success, negative on failure - * @note This call is not-blocking, if this call would block, must - * immediately return NSAPI_ERROR_WOULD_WAIT + * @param data Destination buffer for data received from the host + * @param size Size of the buffer in bytes + * @return Number of received bytes on success, negative error + * code on failure */ virtual int socket_recv(void *handle, void *data, unsigned size) = 0; - /** Send a packet to a remote endpoint + /** Send a packet over a UDP socket + * + * Sends data to the specified address. Returns the number of bytes + * sent from the buffer. + * + * This call is non-blocking. If sendto would block, + * NSAPI_ERROR_WOULD_BLOCK is returned immediately. + * * @param handle Socket handle - * @param address The remote SocketAddress - * @param data The packet to be sent - * @param size The length of the packet to be sent - * @return the number of written bytes on success, negative on failure - * @note This call is not-blocking, if this call would block, must - * immediately return NSAPI_ERROR_WOULD_WAIT + * @param address The SocketAddress of the remote host + * @param data Buffer of data to send to the host + * @param size Size of the buffer in bytes + * @return Number of sent bytes on success, negative error + * code on failure */ virtual int socket_sendto(void *handle, const SocketAddress &address, const void *data, unsigned size) = 0; - /** Receive a packet from a remote endpoint + /** Receive a packet over a UDP socket + * + * Receives data and stores the source address in address if address + * is not NULL. Returns the number of bytes received into the buffer. + * + * This call is non-blocking. If recvfrom would block, + * NSAPI_ERROR_WOULD_BLOCK is returned immediately. + * * @param handle Socket handle - * @param address Destination for the remote SocketAddress or null - * @param buffer The buffer for storing the incoming packet data - * If a packet is too long to fit in the supplied buffer, - * excess bytes are discarded - * @param size The length of the buffer - * @return the number of received bytes on success, negative on failure - * @note This call is not-blocking, if this call would block, must - * immediately return NSAPI_ERROR_WOULD_WAIT + * @param address Destination for the source address or NULL + * @param data Destination buffer for data received from the host + * @param size Size of the buffer in bytes + * @return Number of received bytes on success, negative error + * code on failure */ virtual int socket_recvfrom(void *handle, SocketAddress *address, void *buffer, unsigned size) = 0; /** Register a callback on state change of the socket + * + * The specified callback will be called on state changes such as when + * the socket can recv/send/accept successfully and on when an error + * occurs. The callback may also be called spuriously without reason. + * + * The callback may be called in an interrupt context and should not + * perform expensive operations such as recv/send calls. + * * @param handle Socket handle * @param callback Function to call on state change * @param data Argument to pass to callback - * @note Callback may be called in an interrupt context. */ virtual void socket_attach(void *handle, void (*callback)(void *), void *data) = 0; - /* Set socket options + /* Set stack-specific socked options + * + * The setsockopt allow an application to pass stack-specific hints + * to the underlying stack. For unsupported options, + * NSAPI_ERROR_UNSUPPORTED is returned and the socket is unmodified. + * * @param handle Socket handle - * @param level Option level - * @param optname Option identifier + * @param level Stack-specific protocol level + * @param optname Stack-specific option identifier * @param optval Option value * @param optlen Length of the option value - * @return 0 on success, negative on failure + * @return 0 on success, negative error code on failure */ virtual int setsockopt(void *handle, int level, int optname, const void *optval, unsigned optlen); - /* Get socket options + /* Get stack-specific socket options + * + * The getstackopt allow an application to retrieve stack-specific hints + * from the underlying stack. For unsupported options, + * NSAPI_ERROR_UNSUPPORTED is returned and optval is unmodified. + * * @param handle Socket handle - * @param level Option level - * @param optname Option identifier - * @param optval Buffer where to write option value + * @param level Stack-specific protocol level + * @param optname Stack-specific option identifier + * @param optval Destination for option value * @param optlen Length of the option value - * @return 0 on success, negative on failure + * @return 0 on success, negative error code on failure */ virtual int getsockopt(void *handle, int level, int optname, void *optval, unsigned *optlen); }; From 0ae11b49e83d57e77fada6949d2fdb9e92f10405 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 19 Apr 2016 15:47:54 -0500 Subject: [PATCH 15/40] Removed is_connected function - Not supported by TCP/UDP protocols - Uncommon and less useful with proper error handling --- TCPSocket.cpp | 5 ----- TCPSocket.h | 5 ----- 2 files changed, 10 deletions(-) diff --git a/TCPSocket.cpp b/TCPSocket.cpp index a5225ce56c..a837f29fe2 100644 --- a/TCPSocket.cpp +++ b/TCPSocket.cpp @@ -50,11 +50,6 @@ int TCPSocket::connect(const char *host, uint16_t port) return connect(addr); } -bool TCPSocket::is_connected() -{ - return _socket && _iface->socket_is_connected(_socket); -} - int TCPSocket::send(const void *data, unsigned size) { mbed::Timer timer; diff --git a/TCPSocket.h b/TCPSocket.h index c5a7e97c20..c6714feea9 100644 --- a/TCPSocket.h +++ b/TCPSocket.h @@ -48,11 +48,6 @@ public: */ int connect(const SocketAddress &address); - /** Check if the socket is connected - * @return true if connected, false otherwise - */ - bool is_connected(); - /** Send data to the remote host * @param data The buffer to send to the host * @param size The length of the buffer to send From aa2357a836b145b433196522d6d22c0c8ff28851 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 19 Apr 2016 16:52:06 -0500 Subject: [PATCH 16/40] Revisited documentation for Socket API --- NetworkInterface.h | 19 ++++---- Socket.h | 117 ++++++++++++++++++++++++++++++++++----------- TCPServer.h | 54 +++++++++++++++------ TCPSocket.h | 79 ++++++++++++++++++++++-------- UDPSocket.h | 87 +++++++++++++++++++++++---------- 5 files changed, 261 insertions(+), 95 deletions(-) diff --git a/NetworkInterface.h b/NetworkInterface.h index 63e1800732..f15a2c16cb 100644 --- a/NetworkInterface.h +++ b/NetworkInterface.h @@ -1,4 +1,4 @@ -/* Socket +/* NetworkInterface * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -134,8 +134,8 @@ protected: /** Opens a socket * - * Creates a socket for communication and stores it in the specified - * handle. The handle must be passed to following calls on the socket. + * Creates a network socket and stores it in the specified handle. + * The handle must be passed to following calls on the socket. * * A stack may have a finite number of sockets, in this case * NSAPI_ERROR_NO_SOCKET is returned if no socket is available. @@ -148,8 +148,8 @@ protected: /** Close the socket * - * Closes any open connection and deallocates any memory associated with - * the socket. + * Closes any open connection and deallocates any memory associated + * with the socket. * * @param handle Socket handle * @return 0 on success, negative error code on failure @@ -174,7 +174,7 @@ protected: * * @param handle Socket handle * @param backlog Number of pending connections that can be queued - * simultaneously, defaults to 1 + * simultaneously * @return 0 on success, negative error code on failure */ virtual int socket_listen(void *handle, int backlog) = 0; @@ -193,8 +193,9 @@ protected: /** Accepts a connection on a TCP socket * * The server socket must be bound and set to listen for connections. - * On a new connection, creates a socket stores it in the specified - * handle. The handle must be passed to following calls on the socket. + * On a new connection, creates a network socket and stores it in the + * specified handle. The handle must be passed to following calls on + * the socket. * * A stack may have a finite number of sockets, in this case * NSAPI_ERROR_NO_SOCKET is returned if no socket is available. @@ -289,7 +290,7 @@ protected: */ virtual void socket_attach(void *handle, void (*callback)(void *), void *data) = 0; - /* Set stack-specific socked options + /* Set stack-specific socket options * * The setsockopt allow an application to pass stack-specific hints * to the underlying stack. For unsupported options, diff --git a/Socket.h b/Socket.h index 59e8f9558f..612360e927 100644 --- a/Socket.h +++ b/Socket.h @@ -24,74 +24,135 @@ */ class Socket { public: - /** Socket lifetime + /** Destroy a socket + * + * Closes socket if the socket is still open */ virtual ~Socket(); - /** Open the socket - * @param iface Interface to open socket on + /** Opens a socket + * + * Creates a network socket on the specified network stack. + * Not needed if stack is passed to the socket's constructor. + * + * @param iface Network stack as target for socket + * @return 0 on success, negative error code on failure */ virtual int open(NetworkInterface *iface) = 0; /** Close the socket + * + * Closes any open connection and deallocates any memory associated + * with the socket. Called from destructor if socket is not closed. + * + * @return 0 on success, negative error code on failure */ int close(); - /** Bind a socket to a specific port - * @param port The port to listen for incoming connections on - * @return 0 on success, negative on failure. + /** Bind a specific address to a socket + * + * Binding a socket specifies the address and port on which to recieve + * data. + * + * @param port Local port to bind + * @return 0 on success, negative error code on failure. */ int bind(uint16_t port); - /** Bind a socket to a specific port - * @param address The null-terminated address to listen for incoming connections on - * @param port The port to listen for incoming connections on - * @return 0 on success, negative on failure. + /** Bind a specific address to a socket + * + * Binding a socket specifies the address and port on which to recieve + * data. If the IP address is zeroed, only the port is bound. + * + * @param address Null-terminated local address to bind + * @param port Local port to bind + * @return 0 on success, negative error code on failure. */ int bind(const char *address, uint16_t port); - /** Bind a socket to a specific port - * @param address The SocketAddress to listen for incoming connections on - * @return 0 on success, negative on failure. + /** Bind a specific address to a socket + * + * Binding a socket specifies the address and port on which to recieve + * data. If the IP address is zeroed, only the port is bound. + * + * @param address Local address to bind + * @return 0 on success, negative error code on failure. */ int bind(const SocketAddress &address); /** Set blocking or non-blocking mode of the socket - * @param blocking true for blocking mode, false for non-blocking mode. + * + * Initially all sockets are in blocking mode. In non-blocking mode + * blocking operations such as send/recv/accept return + * NSAPI_ERROR_WOULD_BLOCK if they can not continue. + * + * @param blocking True for blocking mode, false for non-blocking mode. */ void set_blocking(bool blocking); - /** Set timeout on a socket operation if blocking behaviour is enabled - * @param timeout timeout in ms + /** Set timeout on blocking socket operations + * + * Initially all sockets have unbounded timeouts. NSAPI_ERROR_WOULD_BLOCK + * is returned if a blocking operation takes longer than the specified + * timeout. A timeout of 0 removes a timeout from the socket. + * + * @param timeout Timeout in milliseconds */ void set_timeout(unsigned int timeout); - /* Set socket options - * @param level Option level - * @param optname Option identifier + /* Set stack-specific socket options + * + * The setsockopt allow an application to pass stack-specific hints + * to the underlying stack. For unsupported options, + * NSAPI_ERROR_UNSUPPORTED is returned and the socket is unmodified. + * + * @param level Stack-specific protocol level + * @param optname Stack-specific option identifier * @param optval Option value * @param optlen Length of the option value - * @return 0 on success, negative on failure + * @return 0 on success, negative error code on failure */ int setsockopt(int level, int optname, const void *optval, unsigned optlen); - /* Get socket options - * @param level Option level - * @param optname Option identifier - * @param optval Buffer where to write option value + /* Get stack-specific socket options + * + * The getstackopt allow an application to retrieve stack-specific hints + * from the underlying stack. For unsupported options, + * NSAPI_ERROR_UNSUPPORTED is returned and optval is unmodified. + * + * @param level Stack-specific protocol level + * @param optname Stack-specific option identifier + * @param optval Destination for option value * @param optlen Length of the option value - * @return 0 on success, negative on failure + * @return 0 on success, negative error code on failure */ int getsockopt(int level, int optname, void *optval, unsigned *optlen); /** Register a callback on state change of the socket + * + * The specified callback will be called on state changes such as when + * the socket can recv/send/accept successfully and on when an error + * occurs. The callback may also be called spuriously without reason. + * + * The callback may be called in an interrupt context and should not + * perform expensive operations such as recv/send calls. + * * @param callback Function to call on state change - * @note Callback may be called in an interrupt context. - * The callback should not perform long operations - * such as recv or send calls. */ void attach(FunctionPointer callback); + /** Register a callback on state change of the socket + * + * The specified callback will be called on state changes such as when + * the socket can recv/send/accept successfully and on when an error + * occurs. The callback may also be called spuriously without reason. + * + * The callback may be called in an interrupt context and should not + * perform expensive operations such as recv/send calls. + * + * @param tptr Pointer to object to call method on + * @param mptr Method to call on state change + */ template void attach(T *tptr, M mptr) { attach(FunctionPointer(tptr, mptr)); diff --git a/TCPServer.h b/TCPServer.h index 59fbbac5fb..ec2e488316 100644 --- a/TCPServer.h +++ b/TCPServer.h @@ -21,31 +21,57 @@ #include "TCPSocket.h" #include "NetworkInterface.h" -/** TCP Server. +/** TCP socket server */ class TCPServer : public Socket { public: - /** TCP Server lifetime + /** Create an uninitialized socket + * + * Must call open to initialize the socket on a network stack. */ TCPServer(); - TCPServer(NetworkInterface *iface); - virtual ~TCPServer(); - /** Open the socket - * @param iface Interface to open socket on + /** Create a socket on a network stack + * + * Creates and opens a socket on the specified network stack. + * + * @param iface Network stack as target for socket + */ + TCPServer(NetworkInterface *iface); + + /** Opens a socket + * + * Creates a network socket on the specified network stack. + * Not needed if stack is passed to the socket's constructor. + * + * @param iface Network stack as target for socket + * @return 0 on success, negative error code on failure */ virtual int open(NetworkInterface *iface); - /** Start listening for incoming connections - * @param backlog Number of pending connections that can be queued up at any - * one time [Default: 1] - * @return 0 on success, negative on failure + /** Listen for connections on a TCP socket + * + * Marks the socket as a passive socket that can be used to accept + * incoming connections. + * + * @param backlog Number of pending connections that can be queued + * simultaneously, defaults to 1 + * @return 0 on success, negative error code on failure */ - int listen(int backlog=1); + int listen(int backlog = 1); - /** Accept a new connection. - * @param socket A TCPSocket instance that will handle the incoming connection. - * @return 0 on success, negative on failure. + /** Accepts a connection on a TCP socket + * + * The server socket must be bound and set to listen for connections. + * On a new connection, creates a network socket using the specified + * socket instance. + * + * By default, accept blocks until data is sent. If socket is set to + * non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned + * immediately. + * + * @param socket TCPSocket instance that will handle the incoming connection. + * @return 0 on success, negative error code on failure */ int accept(TCPSocket *connection); }; diff --git a/TCPSocket.h b/TCPSocket.h index c6714feea9..f2501e22ee 100644 --- a/TCPSocket.h +++ b/TCPSocket.h @@ -24,41 +24,80 @@ */ class TCPSocket : public Socket { public: - /** TCP socket lifetime + /** Create an uninitialized socket + * + * Must call open to initialize the socket on a network stack. */ TCPSocket(); + + /** Create a socket on a network stack + * + * Creates and opens a socket on the specified network stack. + * + * @param iface Network stack as target for socket + */ TCPSocket(NetworkInterface *iface); - /** Open the socket - * @param iface Interface to open socket on + /** Opens a socket + * + * Creates a network socket on the specified network stack. + * Not needed if stack is passed to the socket's constructor. + * + * @param iface Network stack as target for socket + * @return 0 on success, negative error code on failure */ virtual int open(NetworkInterface *iface); - - /** Connects this TCP socket to the server - * @param host The host to connect to. It can either be an IP Address - * or a hostname that will be resolved with DNS - * @param port The host's port to connect to - * @return 0 on success, negative on failure + + /** Connects TCP socket to a remote host + * + * Initiates a connection to a remote server specified by either + * a domain name or an IP address and a port. + * + * @param host Host name of the remote host + * @param port Port of the remote host + * @return 0 on success, negative error code on failure */ int connect(const char *host, uint16_t port); - /** Connects this TCP socket to the server - * @param address SocketAddress to connect to - * @return 0 on success, negative on failure + /** Connects TCP socket to a remote host + * + * Initiates a connection to a remote server specified by the + * indicated address. + * + * @param address The SocketAddress of the remote host + * @return 0 on success, negative error code on failure */ int connect(const SocketAddress &address); - /** Send data to the remote host - * @param data The buffer to send to the host - * @param size The length of the buffer to send - * @return Number of written bytes on success, negative on failure + /** Send data over a TCP socket + * + * The socket must be connected to a remote host. Returns the number of + * bytes sent from the buffer. + * + * By default, send blocks until data is sent. If socket is set to + * non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned + * immediately. + * + * @param data Buffer of data to send to the host + * @param size Size of the buffer in bytes + * @return Number of sent bytes on success, negative error + * code on failure */ int send(const void *data, unsigned size); - /** Receive data from the remote host - * @param data The buffer in which to store the data received from the host - * @param size The maximum length of the buffer - * @return Number of received bytes on success, negative on failure + /** Receive data over a TCP socket + * + * The socket must be connected to a remote host. Returns the number of + * bytes received into the buffer. + * + * By default, recv blocks until data is sent. If socket is set to + * non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned + * immediately. + * + * @param data Destination buffer for data received from the host + * @param size Size of the buffer in bytes + * @return Number of received bytes on success, negative error + * code on failure */ int recv(void *data, unsigned size); diff --git a/UDPSocket.h b/UDPSocket.h index 4fecb7cf18..23c91c92f5 100644 --- a/UDPSocket.h +++ b/UDPSocket.h @@ -20,47 +20,86 @@ #include "Socket.h" #include "NetworkInterface.h" -/** UDP Socket +/** UDP socket */ class UDPSocket : public Socket { public: - /** UDPSocket lifetime + /** Create an uninitialized socket + * + * Must call open to initialize the socket on a network stack. */ UDPSocket(); + + /** Create a socket on a network stack + * + * Creates and opens a socket on the specified network stack. + * + * @param iface Network stack as target for socket + */ UDPSocket(NetworkInterface *iface); - /** Open the socket - * @param iface Interface to open socket on + /** Opens a socket + * + * Creates a network socket on the specified network stack. + * Not needed if stack is passed to the socket's constructor. + * + * @param iface Network stack as target for socket + * @return 0 on success, negative error code on failure */ virtual int open(NetworkInterface *iface); - /** Send a packet to a remote endpoint - * @param host The host to connect to. It can either be an IP Address - * or a hostname that will be resolved with DNS - * @param port The remote port - * @param data The packet to be sent - * @param size The length of the packet to be sent - * @return The number of written bytes on success, negative on failure + /** Send a packet over a UDP socket + * + * Sends data to the specified address specified by either a domain name + * or an IP address and port. Returns the number of bytes sent from the + * buffer. + * + * By default, sendto blocks until data is sent. If socket is set to + * non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned + * immediately. + * + * @param host Host name of the remote host + * @param port Port of the remote host + * @param data Buffer of data to send to the host + * @param size Size of the buffer in bytes + * @return Number of sent bytes on success, negative error + * code on failure */ int sendto(const char *host, uint16_t port, const void *data, unsigned size); - /** Send a packet to a remote endpoint - * @param address The remote SocketAddress - * @param data The packet to be sent - * @param size The length of the packet to be sent - * @return The number of written bytes on success, negative on failure + /** Send a packet over a UDP socket + * + * Sends data to the specified address. Returns the number of bytes + * sent from the buffer. + * + * By default, sendto blocks until data is sent. If socket is set to + * non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned + * immediately. + * + * @param address The SocketAddress of the remote host + * @param data Buffer of data to send to the host + * @param size Size of the buffer in bytes + * @return Number of sent bytes on success, negative error + * code on failure */ int sendto(const SocketAddress &address, const void *data, unsigned size); - /** Receive a packet from a remote endpoint - * @param address Destination for the remote SocketAddress or null - * @param buffer The buffer for storing the incoming packet data - * If a packet is too long to fit in the supplied buffer, - * excess bytes are discarded - * @param size The length of the buffer - * @return The number of received bytes on success, negative on failure + /** Receive a packet over a UDP socket + * + * Receives data and stores the source address in address if address + * is not NULL. Returns the number of bytes received into the buffer. + * + * By default, recvfrom blocks until data is sent. If socket is set to + * non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned + * immediately. + * + * @param address Destination for the source address or NULL + * @param data Destination buffer for data received from the host + * @param size Size of the buffer in bytes + * @return Number of received bytes on success, negative error + * code on failure */ - int recvfrom(SocketAddress *address, void *buffer, unsigned size); + int recvfrom(SocketAddress *address, void *data, unsigned size); }; #endif From d36a0b6b88b553d03abfd99836b6529f0e63cad6 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 19 Apr 2016 17:51:51 -0500 Subject: [PATCH 17/40] Revised documentation for Interface classes --- CellularInterface.h | 45 +++++++++++++++++++++++++++++ EthernetInterface.h | 11 +++++--- MeshInterface.h | 7 +++-- NetworkInterface.h | 21 ++++++++------ SocketAddress.h | 69 +++++++++++++++++++++++++++++---------------- TCPServer.h | 2 +- TCPSocket.h | 4 +-- UDPSocket.h | 4 +-- WiFiInterface.h | 19 ++++++++++--- 9 files changed, 134 insertions(+), 48 deletions(-) create mode 100644 CellularInterface.h diff --git a/CellularInterface.h b/CellularInterface.h new file mode 100644 index 0000000000..a65ac62012 --- /dev/null +++ b/CellularInterface.h @@ -0,0 +1,45 @@ +/* CellularInterface + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CELLULAR_INTERFACE_H +#define CELLULAR_INTERFACE_H + +#include "NetworkInterface.h" + +/** CellularInterface class + * + * Common interface that is shared between ethernet hardware + */ +class CellularInterface : public NetworkInterface +{ +public: + /** Start the interface + * + * @param apn Optional name of the network to connect to + * @param username Optional username for your APN + * @param password Optional password for your APN + * @return 0 on success, negative error code on failure + */ + virtual int connect(const char *apn = 0, const char *username = 0, const char *password = 0) = 0; + + /** Stop the interface + * + * @return 0 on success, negative error code on failure + */ + virtual int disconnect() = 0; +}; + +#endif diff --git a/EthernetInterface.h b/EthernetInterface.h index a48846f650..8fce3082d0 100644 --- a/EthernetInterface.h +++ b/EthernetInterface.h @@ -1,4 +1,4 @@ -/* Socket +/* EthernetInterface * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,18 +20,21 @@ #include "NetworkInterface.h" /** EthernetInterface class - * Common interface that is shared between ethernet hardware + * + * Common interface that is shared between ethernet hardware. */ class EthernetInterface : public NetworkInterface { public: /** Start the interface - * @return 0 on success, negative on failure + * + * @return 0 on success, negative error code on failure */ virtual int connect() = 0; /** Stop the interface - * @return 0 on success, negative on failure + * + * @return 0 on success, negative error code on failure */ virtual int disconnect() = 0; }; diff --git a/MeshInterface.h b/MeshInterface.h index 884708a623..0b1f598a00 100644 --- a/MeshInterface.h +++ b/MeshInterface.h @@ -1,4 +1,4 @@ -/* Socket +/* MeshInterface * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,17 +20,20 @@ #include "NetworkInterface.h" /** MeshInterface class - * Common interface that is shared between ethernet hardware + * + * Common interface that is shared between mesh hardware */ class MeshInterface : public NetworkInterface { public: /** Start the interface + * * @return 0 on success, negative on failure */ virtual int connect() = 0; /** Stop the interface + * * @return 0 on success, negative on failure */ virtual int disconnect() = 0; diff --git a/NetworkInterface.h b/NetworkInterface.h index f15a2c16cb..1f35571d6e 100644 --- a/NetworkInterface.h +++ b/NetworkInterface.h @@ -46,7 +46,7 @@ enum nsapi_error_t { * The socket protocol specifies a particular protocol to * be used with a newly created socket. * - * @enum protocol_t + * @enum nsapi_protocol_t */ enum nsapi_protocol_t { NSAPI_TCP, /*!< Socket is of TCP type */ @@ -74,26 +74,29 @@ class NetworkInterface public: virtual ~NetworkInterface() {}; - /** Get the internally stored IP address + /** Get the local IP address * - * @return IP address of the interface or null if not yet connected + * @return Null-terminated representation of the local IP address + * or null if not yet connected */ virtual const char *get_ip_address() = 0; - /** Get the internally stored MAC address + /** Get the local MAC address * - * @return MAC address of the interface + * @return Null-terminated representation of the local MAC address */ virtual const char *get_mac_address() = 0; - /** Translates a host name to an IP address + /** Translates a hostname to an IP address * - * The host name may be either a domain name or an IP address. - * If no stack-specific DNS resolution is provided, the host name + * The hostname may be either a domain name or an IP address. If the + * hostname is an IP address, no network transactions will be performed. + * + * If no stack-specific DNS resolution is provided, the hostname * will be resolve using a UDP socket on the stack. * * @param address Destination for the host SocketAddress - * @param host Host name to lookup + * @param host Hostname to resolve * @return 0 on success, negative error code on failure */ virtual int gethostbyname(SocketAddress *address, const char *host); diff --git a/SocketAddress.h b/SocketAddress.h index 1f93177b5e..4e68053265 100644 --- a/SocketAddress.h +++ b/SocketAddress.h @@ -1,4 +1,4 @@ -/* Socket +/* SocketAddress * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,8 +28,11 @@ */ #define NSAPI_IP_BYTES NSAPI_IPv6_BYTES -/** Enum of address families - * @enum nsapi_family_t +/** Enum of IP address versions + * + * The IP version specifies the type of an IP address. + * + * @enum nsapi_version_t */ enum nsapi_version_t { NSAPI_IPv4, /*!< Address is IPv4 */ @@ -56,74 +59,92 @@ enum nsapi_version_t { class NetworkInterface; -/** A general address class composed of the IP address and optional port +/** SocketAddress class + * + * Representation of an IP address and port pair. */ class SocketAddress { public: - /** SocketAddress construction using DNS resolution - * @param iface NetworkInterface to use for DNS resolution - * @param addr Null-terminated hostname that will be resolved + /** Create a SocketAddress from a hostname and port + * + * The hostname may be either a domain name or an IP address. If the + * hostname is an IP address, no network transactions will be performed. + * + * On failure, the IP address and port will be set to zero + * + * @param iface Network stack to use for DNS resolution + * @param host Hostname to resolve * @param port Optional 16-bit port - * @note on failure, IP address and port will be set to zero */ - SocketAddress(NetworkInterface *iface, const char *addr, uint16_t port = 0); + SocketAddress(NetworkInterface *iface, const char *host, uint16_t port = 0); - /** SocketAddress construction - * @param addr Null-terminated IP address + /** Create a SocketAddress from an IP address and port + * + * @param host Null-terminated representation of the IP address * @param port Optional 16-bit port */ SocketAddress(const char *addr = 0, uint16_t port = 0); - /** SocketAddress construction - * @param bytes Bytes to assign to address in big-endian order + /** Create a SocketAddress from a raw IP address and port + * + * @param bytes Raw IP address in big-endian order * @param version IP address version, NSAPI_IPv4 or NSAPI_IPv6 * @param port Optional 16-bit port */ SocketAddress(const void *bytes, nsapi_version_t version, uint16_t port = 0); - /** SocketAddress construction - * @param addr SocketAddress to copy + /** Create a SocketAddress from another SocketAddress + * + * @param address SocketAddress to copy */ SocketAddress(const SocketAddress &addr); /** Set the IP address - * @param addr Null-terminated string representing the IP address + * + * @param addr Null-terminated represention of the IP address */ void set_ip_address(const char *addr); - /** Set the IP address bytes directly - * @param bytes Bytes to assign to address in big-endian order + /** Set the raw IP address + * + * @param bytes Raw IP address in big-endian order * @param version IP address version, NSAPI_IPv4 or NSAPI_IPv6 */ void set_ip_bytes(const void *bytes, nsapi_version_t version); /** Set the port + * * @param port 16-bit port */ void set_port(uint16_t port); /** Get the IP address - * @return The string representation of the IP Address + * + * @return Null-terminated representation of the IP Address */ const char *get_ip_address() const; - /** Get the IP address bytes directly - * @return IP address bytes + /** Get the raw IP address + * + * @return Raw IP address in big-endian order */ const void *get_ip_bytes() const; - /** Get the type of the IP address + /** Get the IP address version + * * @return IP address version, NSAPI_IPv4 or NSAPI_IPv6 */ nsapi_version_t get_ip_version() const; /** Get the port + * * @return The 16-bit port */ uint16_t get_port() const; - /** Determine if address is all zeros - * @return True if address is not zero address + /** Test if address is zero + * + * @return True if address is not zero */ operator bool() const; diff --git a/TCPServer.h b/TCPServer.h index ec2e488316..619056369c 100644 --- a/TCPServer.h +++ b/TCPServer.h @@ -1,4 +1,4 @@ -/* Socket +/* TCPServer * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/TCPSocket.h b/TCPSocket.h index f2501e22ee..834b064015 100644 --- a/TCPSocket.h +++ b/TCPSocket.h @@ -1,4 +1,4 @@ -/* Socket +/* TCPSocket * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -53,7 +53,7 @@ public: * Initiates a connection to a remote server specified by either * a domain name or an IP address and a port. * - * @param host Host name of the remote host + * @param host Hostname of the remote host * @param port Port of the remote host * @return 0 on success, negative error code on failure */ diff --git a/UDPSocket.h b/UDPSocket.h index 23c91c92f5..74f8c4a340 100644 --- a/UDPSocket.h +++ b/UDPSocket.h @@ -1,4 +1,4 @@ -/* Socket +/* UDPSocket * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -58,7 +58,7 @@ public: * non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned * immediately. * - * @param host Host name of the remote host + * @param host Hostname of the remote host * @param port Port of the remote host * @param data Buffer of data to send to the host * @param size Size of the buffer in bytes diff --git a/WiFiInterface.h b/WiFiInterface.h index 21fa397a66..1a9e267e0b 100644 --- a/WiFiInterface.h +++ b/WiFiInterface.h @@ -1,4 +1,4 @@ -/* Socket +/* WiFiInterface * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,7 +19,12 @@ #include "NetworkInterface.h" -/** Enum for WiFi encryption types +/** Enum of WiFi encryption types + * + * The security type specifies a particular security to use when + * connected to a WiFi network + * + * @enum nsapi_protocol_t */ enum nsapi_security_t { NSAPI_SECURITY_NONE = 0, /*!< open access point */ @@ -29,21 +34,27 @@ enum nsapi_security_t { }; /** WiFiInterface class + * * Common interface that is shared between WiFi devices */ class WiFiInterface : public NetworkInterface { public: /** Start the interface + * + * Attempts to connect to a WiFi network. If passphrase is invalid, + * NSAPI_ERROR_AUTH_ERROR is returned. + * * @param ssid Name of the network to connect to * @param pass Security passphrase to connect to the network * @param security Type of encryption for connection - * @return 0 on success, negative on failure + * @return 0 on success, negative error code on failure */ virtual int connect(const char *ssid, const char *pass, nsapi_security_t security = NSAPI_SECURITY_NONE) = 0; /** Stop the interface - * @return 0 on success, negative on failure + * + * @return 0 on success, negative error code on failure */ virtual int disconnect() = 0; }; From fc71badb69b1624b5b3e5c059ff5237b6498d34c Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 19 Apr 2016 18:07:43 -0500 Subject: [PATCH 18/40] Rename Interface -> Stack NetworkInterface -> NetworkStack EthernetInterface -> EthernetStack WiFiInterface -> WiFiStack CellularInterface -> CellularStack MeshInterface -> MeshStack --- CellularInterface.h => CellularStack.h | 8 ++++---- DnsQuery/DnsQuery.cpp | 2 +- DnsQuery/DnsQuery.h | 4 ++-- EthernetInterface.h => EthernetStack.h | 8 ++++---- MeshInterface.h => MeshStack.h | 8 ++++---- NetworkInterface.cpp => NetworkStack.cpp | 10 +++++----- NetworkInterface.h => NetworkStack.h | 10 +++++----- Socket.cpp | 2 +- Socket.h | 8 ++++---- SocketAddress.cpp | 4 ++-- SocketAddress.h | 4 ++-- TCPServer.cpp | 4 ++-- TCPServer.h | 6 +++--- TCPSocket.cpp | 4 ++-- TCPSocket.h | 6 +++--- UDPSocket.cpp | 4 ++-- UDPSocket.h | 6 +++--- WiFiInterface.h => WiFiStack.h | 8 ++++---- 18 files changed, 53 insertions(+), 53 deletions(-) rename CellularInterface.h => CellularStack.h (90%) rename EthernetInterface.h => EthernetStack.h (88%) rename MeshInterface.h => MeshStack.h (89%) rename NetworkInterface.cpp => NetworkStack.cpp (67%) rename NetworkInterface.h => NetworkStack.h (98%) rename WiFiInterface.h => WiFiStack.h (94%) diff --git a/CellularInterface.h b/CellularStack.h similarity index 90% rename from CellularInterface.h rename to CellularStack.h index a65ac62012..da96cafedb 100644 --- a/CellularInterface.h +++ b/CellularStack.h @@ -1,4 +1,4 @@ -/* CellularInterface +/* CellularStack * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,13 +17,13 @@ #ifndef CELLULAR_INTERFACE_H #define CELLULAR_INTERFACE_H -#include "NetworkInterface.h" +#include "NetworkStack.h" -/** CellularInterface class +/** CellularStack class * * Common interface that is shared between ethernet hardware */ -class CellularInterface : public NetworkInterface +class CellularStack : public NetworkStack { public: /** Start the interface diff --git a/DnsQuery/DnsQuery.cpp b/DnsQuery/DnsQuery.cpp index e940e34694..2c209f529e 100644 --- a/DnsQuery/DnsQuery.cpp +++ b/DnsQuery/DnsQuery.cpp @@ -185,7 +185,7 @@ static int32_t query(UDPSocket *socket, const SocketAddress &addr, const char *h return NSAPI_ERROR_DNS_FAILURE; } -int32_t dnsQuery(NetworkInterface *iface, const char *host, char *ip) +int32_t dnsQuery(NetworkStack *iface, const char *host, char *ip) { if (isIP(host)) { strcpy(ip, host); diff --git a/DnsQuery/DnsQuery.h b/DnsQuery/DnsQuery.h index 6e1e6265b7..3c5dfa7d70 100644 --- a/DnsQuery/DnsQuery.h +++ b/DnsQuery/DnsQuery.h @@ -18,7 +18,7 @@ #ifndef __DNSQUERY_H__ #define __DNSQUERY_H__ -#include "NetworkInterface.h" +#include "NetworkStack.h" #include "UDPSocket.h" @@ -34,7 +34,7 @@ * @returns 0 on succes, NS_DNS_FAILURE if host is not found, * or a negative value for other errors. */ -int32_t dnsQuery(NetworkInterface *iface, const char *host, char *ip); +int32_t dnsQuery(NetworkStack *iface, const char *host, char *ip); int32_t dnsQuery(UDPSocket *sock, const char *host, char *ip); diff --git a/EthernetInterface.h b/EthernetStack.h similarity index 88% rename from EthernetInterface.h rename to EthernetStack.h index 8fce3082d0..061648af14 100644 --- a/EthernetInterface.h +++ b/EthernetStack.h @@ -1,4 +1,4 @@ -/* EthernetInterface +/* EthernetStack * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,13 +17,13 @@ #ifndef ETHERNET_INTERFACE_H #define ETHERNET_INTERFACE_H -#include "NetworkInterface.h" +#include "NetworkStack.h" -/** EthernetInterface class +/** EthernetStack class * * Common interface that is shared between ethernet hardware. */ -class EthernetInterface : public NetworkInterface +class EthernetStack : public NetworkStack { public: /** Start the interface diff --git a/MeshInterface.h b/MeshStack.h similarity index 89% rename from MeshInterface.h rename to MeshStack.h index 0b1f598a00..cdb4360700 100644 --- a/MeshInterface.h +++ b/MeshStack.h @@ -1,4 +1,4 @@ -/* MeshInterface +/* MeshStack * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,13 +17,13 @@ #ifndef MESH_INTERFACE_H #define MESH_INTERFACE_H -#include "NetworkInterface.h" +#include "NetworkStack.h" -/** MeshInterface class +/** MeshStack class * * Common interface that is shared between mesh hardware */ -class MeshInterface : public NetworkInterface +class MeshStack : public NetworkStack { public: /** Start the interface diff --git a/NetworkInterface.cpp b/NetworkStack.cpp similarity index 67% rename from NetworkInterface.cpp rename to NetworkStack.cpp index a39fdc7c43..1b70dd6551 100644 --- a/NetworkInterface.cpp +++ b/NetworkStack.cpp @@ -17,7 +17,7 @@ #include "DnsQuery.h" #include "mbed.h" -int NetworkInterface::gethostbyname(SocketAddress *address, const char *name) +int NetworkStack::gethostbyname(SocketAddress *address, const char *name) { char buffer[NSAPI_IP_SIZE]; int err = dnsQuery(this, name, buffer); @@ -29,22 +29,22 @@ int NetworkInterface::gethostbyname(SocketAddress *address, const char *name) return 0; } -int NetworkInterface::setstackopt(int level, int optname, const void *optval, unsigned optlen) +int NetworkStack::setstackopt(int level, int optname, const void *optval, unsigned optlen) { return NSAPI_ERROR_UNSUPPORTED; } -int NetworkInterface::getstackopt(int level, int optname, void *optval, unsigned *optlen) +int NetworkStack::getstackopt(int level, int optname, void *optval, unsigned *optlen) { return NSAPI_ERROR_UNSUPPORTED; } -int NetworkInterface::setsockopt(void *handle, int level, int optname, const void *optval, unsigned optlen) +int NetworkStack::setsockopt(void *handle, int level, int optname, const void *optval, unsigned optlen) { return NSAPI_ERROR_UNSUPPORTED; } -int NetworkInterface::getsockopt(void *handle, int level, int optname, void *optval, unsigned *optlen) +int NetworkStack::getsockopt(void *handle, int level, int optname, void *optval, unsigned *optlen) { return NSAPI_ERROR_UNSUPPORTED; } diff --git a/NetworkInterface.h b/NetworkStack.h similarity index 98% rename from NetworkInterface.h rename to NetworkStack.h index 1f35571d6e..37306e6c19 100644 --- a/NetworkInterface.h +++ b/NetworkStack.h @@ -1,4 +1,4 @@ -/* NetworkInterface +/* NetworkStack * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -62,17 +62,17 @@ enum nsapi_protocol_t { #define NSAPI_MAC_BYTES 6 -/** NetworkInterface class +/** NetworkStack class * * Common interface that is shared between hardware that * can connect to a network over IP. By implementing the - * NetworkInterface, a network stack can be used as a target + * NetworkStack, a network stack can be used as a target * for instantiating network sockets. */ -class NetworkInterface +class NetworkStack { public: - virtual ~NetworkInterface() {}; + virtual ~NetworkStack() {}; /** Get the local IP address * diff --git a/Socket.cpp b/Socket.cpp index e291f7e1a5..aa8a47023c 100644 --- a/Socket.cpp +++ b/Socket.cpp @@ -31,7 +31,7 @@ Socket::~Socket() } } -int Socket::open(NetworkInterface *iface, nsapi_protocol_t proto) +int Socket::open(NetworkStack *iface, nsapi_protocol_t proto) { _iface = iface; diff --git a/Socket.h b/Socket.h index 612360e927..ab093e39d3 100644 --- a/Socket.h +++ b/Socket.h @@ -18,7 +18,7 @@ #define SOCKET_H #include "SocketAddress.h" -#include "NetworkInterface.h" +#include "NetworkStack.h" /** Abstract socket class */ @@ -38,7 +38,7 @@ public: * @param iface Network stack as target for socket * @return 0 on success, negative error code on failure */ - virtual int open(NetworkInterface *iface) = 0; + virtual int open(NetworkStack *iface) = 0; /** Close the socket * @@ -160,11 +160,11 @@ public: protected: Socket(); - int open(NetworkInterface *iface, nsapi_protocol_t proto); + int open(NetworkStack *iface, nsapi_protocol_t proto); static void thunk(void *); - NetworkInterface *_iface; + NetworkStack *_iface; void *_socket; bool _blocking; unsigned _timeout; diff --git a/SocketAddress.cpp b/SocketAddress.cpp index f8446ea349..f238c883d8 100644 --- a/SocketAddress.cpp +++ b/SocketAddress.cpp @@ -15,7 +15,7 @@ */ #include "SocketAddress.h" -#include "NetworkInterface.h" +#include "NetworkStack.h" #include #include "mbed.h" @@ -126,7 +126,7 @@ static void ipv6_to_address(char *addr, const uint8_t *bytes) } -SocketAddress::SocketAddress(NetworkInterface *iface, const char *host, uint16_t port) +SocketAddress::SocketAddress(NetworkStack *iface, const char *host, uint16_t port) { // Check for valid IP addresses if (host && ipv4_is_valid(host)) { diff --git a/SocketAddress.h b/SocketAddress.h index 4e68053265..5d71e2678e 100644 --- a/SocketAddress.h +++ b/SocketAddress.h @@ -56,7 +56,7 @@ enum nsapi_version_t { #define NSAPI_IPv6_BYTES 16 // Predeclared classes -class NetworkInterface; +class NetworkStack; /** SocketAddress class @@ -76,7 +76,7 @@ public: * @param host Hostname to resolve * @param port Optional 16-bit port */ - SocketAddress(NetworkInterface *iface, const char *host, uint16_t port = 0); + SocketAddress(NetworkStack *iface, const char *host, uint16_t port = 0); /** Create a SocketAddress from an IP address and port * diff --git a/TCPServer.cpp b/TCPServer.cpp index 6c513f4be0..83a85d66b4 100644 --- a/TCPServer.cpp +++ b/TCPServer.cpp @@ -21,12 +21,12 @@ TCPServer::TCPServer() { } -TCPServer::TCPServer(NetworkInterface *iface) +TCPServer::TCPServer(NetworkStack *iface) { open(iface); } -int TCPServer::open(NetworkInterface *iface) +int TCPServer::open(NetworkStack *iface) { return Socket::open(iface, NSAPI_TCP); } diff --git a/TCPServer.h b/TCPServer.h index 619056369c..0bb7d15a01 100644 --- a/TCPServer.h +++ b/TCPServer.h @@ -19,7 +19,7 @@ #include "Socket.h" #include "TCPSocket.h" -#include "NetworkInterface.h" +#include "NetworkStack.h" /** TCP socket server */ @@ -37,7 +37,7 @@ public: * * @param iface Network stack as target for socket */ - TCPServer(NetworkInterface *iface); + TCPServer(NetworkStack *iface); /** Opens a socket * @@ -47,7 +47,7 @@ public: * @param iface Network stack as target for socket * @return 0 on success, negative error code on failure */ - virtual int open(NetworkInterface *iface); + virtual int open(NetworkStack *iface); /** Listen for connections on a TCP socket * diff --git a/TCPSocket.cpp b/TCPSocket.cpp index a837f29fe2..b59715594c 100644 --- a/TCPSocket.cpp +++ b/TCPSocket.cpp @@ -21,12 +21,12 @@ TCPSocket::TCPSocket() { } -TCPSocket::TCPSocket(NetworkInterface *iface) +TCPSocket::TCPSocket(NetworkStack *iface) { open(iface); } -int TCPSocket::open(NetworkInterface *iface) +int TCPSocket::open(NetworkStack *iface) { return Socket::open(iface, NSAPI_TCP); } diff --git a/TCPSocket.h b/TCPSocket.h index 834b064015..2a37f776fb 100644 --- a/TCPSocket.h +++ b/TCPSocket.h @@ -18,7 +18,7 @@ #define TCPSOCKET_H #include "Socket.h" -#include "NetworkInterface.h" +#include "NetworkStack.h" /** TCP socket connection */ @@ -36,7 +36,7 @@ public: * * @param iface Network stack as target for socket */ - TCPSocket(NetworkInterface *iface); + TCPSocket(NetworkStack *iface); /** Opens a socket * @@ -46,7 +46,7 @@ public: * @param iface Network stack as target for socket * @return 0 on success, negative error code on failure */ - virtual int open(NetworkInterface *iface); + virtual int open(NetworkStack *iface); /** Connects TCP socket to a remote host * diff --git a/UDPSocket.cpp b/UDPSocket.cpp index 9177de60c8..85569c7a58 100644 --- a/UDPSocket.cpp +++ b/UDPSocket.cpp @@ -21,12 +21,12 @@ UDPSocket::UDPSocket() { } -UDPSocket::UDPSocket(NetworkInterface *iface) +UDPSocket::UDPSocket(NetworkStack *iface) { open(iface); } -int UDPSocket::open(NetworkInterface *iface) +int UDPSocket::open(NetworkStack *iface) { return Socket::open(iface, NSAPI_UDP); } diff --git a/UDPSocket.h b/UDPSocket.h index 74f8c4a340..b2a8454d9f 100644 --- a/UDPSocket.h +++ b/UDPSocket.h @@ -18,7 +18,7 @@ #define UDPSOCKET_H #include "Socket.h" -#include "NetworkInterface.h" +#include "NetworkStack.h" /** UDP socket */ @@ -36,7 +36,7 @@ public: * * @param iface Network stack as target for socket */ - UDPSocket(NetworkInterface *iface); + UDPSocket(NetworkStack *iface); /** Opens a socket * @@ -46,7 +46,7 @@ public: * @param iface Network stack as target for socket * @return 0 on success, negative error code on failure */ - virtual int open(NetworkInterface *iface); + virtual int open(NetworkStack *iface); /** Send a packet over a UDP socket * diff --git a/WiFiInterface.h b/WiFiStack.h similarity index 94% rename from WiFiInterface.h rename to WiFiStack.h index 1a9e267e0b..f5a2ef632b 100644 --- a/WiFiInterface.h +++ b/WiFiStack.h @@ -1,4 +1,4 @@ -/* WiFiInterface +/* WiFiStack * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,7 +17,7 @@ #ifndef WIFI_INTERFACE_H #define WIFI_INTERFACE_H -#include "NetworkInterface.h" +#include "NetworkStack.h" /** Enum of WiFi encryption types * @@ -33,11 +33,11 @@ enum nsapi_security_t { NSAPI_SECURITY_WPA2, /*!< phrase conforms to WPA2 */ }; -/** WiFiInterface class +/** WiFiStack class * * Common interface that is shared between WiFi devices */ -class WiFiInterface : public NetworkInterface +class WiFiStack : public NetworkStack { public: /** Start the interface From cd4a521e22d6916f902f11a8a57e35a8fd5aba15 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 19 Apr 2016 20:43:18 -0500 Subject: [PATCH 19/40] Fix race condition in socket close --- Socket.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Socket.cpp b/Socket.cpp index aa8a47023c..f2eeb14f1e 100644 --- a/Socket.cpp +++ b/Socket.cpp @@ -53,7 +53,9 @@ int Socket::close() return 0; } - void *socket = _socket; + _iface->socket_attach(_socket, 0, 0); + + void *volatile socket = _socket; _socket = 0; return _iface->socket_close(socket); } From abd25f9bb3197f1c934120c500b51b07e9890a86 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Wed, 20 Apr 2016 03:22:31 -0500 Subject: [PATCH 20/40] Added workaround for bug in newlib sscanf https://bugs.launchpad.net/gcc-arm-embedded/+bug/1399224 --- SocketAddress.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/SocketAddress.cpp b/SocketAddress.cpp index f238c883d8..933f851731 100644 --- a/SocketAddress.cpp +++ b/SocketAddress.cpp @@ -56,7 +56,23 @@ static bool ipv6_is_valid(const char *addr) static void ipv4_from_address(uint8_t *bytes, const char *addr) { - sscanf(addr, "%hhu.%hhu.%hhu.%hhu", &bytes[0], &bytes[1], &bytes[2], &bytes[3]); + int count = 0; + int i = 0; + + for (; count < NSAPI_IPv4_BYTES; count++) { + int scanned = sscanf(&addr[i], "%hhu", &bytes[count]); + if (scanned < 1) { + return; + } + + for (; addr[i] != '.'; i++) { + if (!addr[i]) { + return; + } + } + + i++; + } } static int ipv6_scan_chunk(uint16_t *shorts, const char *chunk) { From a4f1071996c1f42ff726812e90297eb6d8824c9a Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Wed, 20 Apr 2016 03:23:58 -0500 Subject: [PATCH 21/40] Add standardized stack options --- NetworkStack.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/NetworkStack.h b/NetworkStack.h index 37306e6c19..6ce6995bbc 100644 --- a/NetworkStack.h +++ b/NetworkStack.h @@ -53,6 +53,30 @@ enum nsapi_protocol_t { NSAPI_UDP, /*!< Socket is of UDP type */ }; +/* Enum of standardized stack option levels + * + * @enum nsapi_level_t + */ +enum nsapi_level_t { + NSAPI_STACK, /*!< Stack option level */ + NSAPI_SOCKET, /*!< Socket option level */ +}; + +/* Enum of standardized stack options + * + * These options may not be supported on all stacks, in which + * case NSAPI_ERROR_UNSUPPORTED may be returned from setsockopt. + * + * @enum nsapi_option_t + */ +enum nsapi_option_t { + NSAPI_REUSEADDR, /*!< Allow bind to reuse local addresses */ + NSAPI_KEEPALIVE, /*!< Enables sending of keepalive messages */ + NSAPI_LINGER, /*!< Keeps close from returning until queues empty */ + NSAPI_SNDBUF, /*!< Sets send buffer size */ + NSAPI_RCVBUF, /*!< Sets recv buffer size */ +}; + /** Maximum size of MAC address representation */ #define NSAPI_MAC_SIZE 18 From bd5e913ca14643b2aae2437ee3cfcf0e17cd5f8e Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Wed, 20 Apr 2016 14:39:13 -0500 Subject: [PATCH 22/40] Separate Stack/Interface concept into two distinct classes --- CellularStack.h => CellularInterface.h | 12 +++++++++--- EthernetStack.h => EthernetInterface.h | 12 +++++++++--- MeshStack.h => MeshInterface.h | 12 +++++++++--- NetworkStack.h | 14 -------------- Socket.cpp | 4 ++-- SocketAddress.h | 8 ++++++++ WiFiStack.h => WiFiInterface.h | 12 +++++++++--- 7 files changed, 46 insertions(+), 28 deletions(-) rename CellularStack.h => CellularInterface.h (84%) rename EthernetStack.h => EthernetInterface.h (80%) rename MeshStack.h => MeshInterface.h (81%) rename WiFiStack.h => WiFiInterface.h (88%) diff --git a/CellularStack.h b/CellularInterface.h similarity index 84% rename from CellularStack.h rename to CellularInterface.h index da96cafedb..0f9c28b840 100644 --- a/CellularStack.h +++ b/CellularInterface.h @@ -1,4 +1,4 @@ -/* CellularStack +/* CellularInterface * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,11 +19,11 @@ #include "NetworkStack.h" -/** CellularStack class +/** CellularInterface class * * Common interface that is shared between ethernet hardware */ -class CellularStack : public NetworkStack +class CellularInterface { public: /** Start the interface @@ -40,6 +40,12 @@ public: * @return 0 on success, negative error code on failure */ virtual int disconnect() = 0; + + /** Get the local MAC address + * + * @return Null-terminated representation of the local MAC address + */ + virtual const char *get_mac_address() = 0; }; #endif diff --git a/EthernetStack.h b/EthernetInterface.h similarity index 80% rename from EthernetStack.h rename to EthernetInterface.h index 061648af14..c0214c688b 100644 --- a/EthernetStack.h +++ b/EthernetInterface.h @@ -1,4 +1,4 @@ -/* EthernetStack +/* EthernetInterface * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,11 +19,11 @@ #include "NetworkStack.h" -/** EthernetStack class +/** EthernetInterface class * * Common interface that is shared between ethernet hardware. */ -class EthernetStack : public NetworkStack +class EthernetInterface { public: /** Start the interface @@ -37,6 +37,12 @@ public: * @return 0 on success, negative error code on failure */ virtual int disconnect() = 0; + + /** Get the local MAC address + * + * @return Null-terminated representation of the local MAC address + */ + virtual const char *get_mac_address() = 0; }; #endif diff --git a/MeshStack.h b/MeshInterface.h similarity index 81% rename from MeshStack.h rename to MeshInterface.h index cdb4360700..811b3f7352 100644 --- a/MeshStack.h +++ b/MeshInterface.h @@ -1,4 +1,4 @@ -/* MeshStack +/* MeshInterface * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,11 +19,11 @@ #include "NetworkStack.h" -/** MeshStack class +/** MeshInterface class * * Common interface that is shared between mesh hardware */ -class MeshStack : public NetworkStack +class MeshInterface { public: /** Start the interface @@ -37,6 +37,12 @@ public: * @return 0 on success, negative on failure */ virtual int disconnect() = 0; + + /** Get the local MAC address + * + * @return Null-terminated representation of the local MAC address + */ + virtual const char *get_mac_address() = 0; }; #endif diff --git a/NetworkStack.h b/NetworkStack.h index 6ce6995bbc..f82bd24e1d 100644 --- a/NetworkStack.h +++ b/NetworkStack.h @@ -77,14 +77,6 @@ enum nsapi_option_t { NSAPI_RCVBUF, /*!< Sets recv buffer size */ }; -/** Maximum size of MAC address representation - */ -#define NSAPI_MAC_SIZE 18 - -/** Maximum number of bytes for MAC address - */ -#define NSAPI_MAC_BYTES 6 - /** NetworkStack class * @@ -105,12 +97,6 @@ public: */ virtual const char *get_ip_address() = 0; - /** Get the local MAC address - * - * @return Null-terminated representation of the local MAC address - */ - virtual const char *get_mac_address() = 0; - /** Translates a hostname to an IP address * * The hostname may be either a domain name or an IP address. If the diff --git a/Socket.cpp b/Socket.cpp index f2eeb14f1e..943187d364 100644 --- a/Socket.cpp +++ b/Socket.cpp @@ -52,9 +52,9 @@ int Socket::close() if (!_socket) { return 0; } - + _iface->socket_attach(_socket, 0, 0); - + void *volatile socket = _socket; _socket = 0; return _iface->socket_close(socket); diff --git a/SocketAddress.h b/SocketAddress.h index 5d71e2678e..64f2991e0b 100644 --- a/SocketAddress.h +++ b/SocketAddress.h @@ -28,6 +28,14 @@ */ #define NSAPI_IP_BYTES NSAPI_IPv6_BYTES +/** Maximum size of MAC address representation + */ +#define NSAPI_MAC_SIZE 18 + +/** Maximum number of bytes for MAC address + */ +#define NSAPI_MAC_BYTES 6 + /** Enum of IP address versions * * The IP version specifies the type of an IP address. diff --git a/WiFiStack.h b/WiFiInterface.h similarity index 88% rename from WiFiStack.h rename to WiFiInterface.h index f5a2ef632b..09f5340d32 100644 --- a/WiFiStack.h +++ b/WiFiInterface.h @@ -1,4 +1,4 @@ -/* WiFiStack +/* WiFiInterface * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -33,11 +33,11 @@ enum nsapi_security_t { NSAPI_SECURITY_WPA2, /*!< phrase conforms to WPA2 */ }; -/** WiFiStack class +/** WiFiInterface class * * Common interface that is shared between WiFi devices */ -class WiFiStack : public NetworkStack +class WiFiInterface { public: /** Start the interface @@ -57,6 +57,12 @@ public: * @return 0 on success, negative error code on failure */ virtual int disconnect() = 0; + + /** Get the local MAC address + * + * @return Null-terminated representation of the local MAC address + */ + virtual const char *get_mac_address() = 0; }; #endif From f2715b7e771a4242e27d7146634d015efd0c85bc Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Wed, 20 Apr 2016 19:10:13 -0500 Subject: [PATCH 23/40] Consolidate set_timeout/set_blocking behaviour - Avoids ambiguity when both are used - Matches Python behaviour --- Socket.cpp | 7 +++---- Socket.h | 15 ++++++++++----- TCPServer.cpp | 5 +++-- TCPSocket.cpp | 10 ++++++---- UDPSocket.cpp | 10 ++++++---- 5 files changed, 28 insertions(+), 19 deletions(-) diff --git a/Socket.cpp b/Socket.cpp index 943187d364..d21f984e7e 100644 --- a/Socket.cpp +++ b/Socket.cpp @@ -19,8 +19,7 @@ Socket::Socket() : _iface(0) , _socket(0) - , _blocking(true) - , _timeout(0) + , _timeout(-1) { } @@ -83,10 +82,10 @@ int Socket::bind(const SocketAddress &address) void Socket::set_blocking(bool blocking) { - _blocking = blocking; + set_timeout(blocking ? -1 : 0); } -void Socket::set_timeout(unsigned timeout) +void Socket::set_timeout(int timeout) { _timeout = timeout; } diff --git a/Socket.h b/Socket.h index ab093e39d3..195da38204 100644 --- a/Socket.h +++ b/Socket.h @@ -86,7 +86,10 @@ public: * blocking operations such as send/recv/accept return * NSAPI_ERROR_WOULD_BLOCK if they can not continue. * - * @param blocking True for blocking mode, false for non-blocking mode. + * set_blocking(false) is equivalent to set_timeout(-1) + * set_blocking(true) is equivalent to set_timeout(0) + * + * @param blocking true for blocking mode, false for non-blocking mode. */ void set_blocking(bool blocking); @@ -94,11 +97,14 @@ public: * * Initially all sockets have unbounded timeouts. NSAPI_ERROR_WOULD_BLOCK * is returned if a blocking operation takes longer than the specified - * timeout. A timeout of 0 removes a timeout from the socket. + * timeout. A timeout of -1 removes the timeout from the socket. + * + * set_timeout(-1) is equivalent to set_blocking(false) + * set_timeout(0) is equivalent to set_blocking(true) * * @param timeout Timeout in milliseconds */ - void set_timeout(unsigned int timeout); + void set_timeout(int timeout); /* Set stack-specific socket options * @@ -166,8 +172,7 @@ protected: NetworkStack *_iface; void *_socket; - bool _blocking; - unsigned _timeout; + int _timeout; FunctionPointer _callback; }; diff --git a/TCPServer.cpp b/TCPServer.cpp index 83a85d66b4..7a65e954da 100644 --- a/TCPServer.cpp +++ b/TCPServer.cpp @@ -60,8 +60,9 @@ int TCPServer::accept(TCPSocket *connection) connection->_socket = socket; } - if (err != NSAPI_ERROR_WOULD_BLOCK || !_blocking || - (_timeout && timer.read_ms() > _timeout)) { + if (err != NSAPI_ERROR_WOULD_BLOCK + || _timeout < 0 + || timer.read_ms() > _timeout) { return err; } } diff --git a/TCPSocket.cpp b/TCPSocket.cpp index b59715594c..926473dacc 100644 --- a/TCPSocket.cpp +++ b/TCPSocket.cpp @@ -61,8 +61,9 @@ int TCPSocket::send(const void *data, unsigned size) } int sent = _iface->socket_send(_socket, data, size); - if (sent != NSAPI_ERROR_WOULD_BLOCK || !_blocking || - (_timeout && timer.read_ms() > _timeout)) { + if (sent != NSAPI_ERROR_WOULD_BLOCK + || _timeout < 0 + || timer.read_ms() > _timeout) { return sent; } } @@ -79,8 +80,9 @@ int TCPSocket::recv(void *data, unsigned size) } int recv = _iface->socket_recv(_socket, data, size); - if (recv != NSAPI_ERROR_WOULD_BLOCK || !_blocking || - (_timeout && timer.read_ms() > _timeout)) { + if (recv != NSAPI_ERROR_WOULD_BLOCK + || _timeout < 0 + || timer.read_ms() > _timeout) { return recv; } } diff --git a/UDPSocket.cpp b/UDPSocket.cpp index 85569c7a58..a8148fb38f 100644 --- a/UDPSocket.cpp +++ b/UDPSocket.cpp @@ -52,8 +52,9 @@ int UDPSocket::sendto(const SocketAddress &address, const void *data, unsigned s } int sent = _iface->socket_sendto(_socket, address, data, size); - if (sent != NSAPI_ERROR_WOULD_BLOCK || !_blocking || - (_timeout && timer.read_ms() > _timeout)) { + if (sent != NSAPI_ERROR_WOULD_BLOCK + || _timeout < 0 + || timer.read_ms() > _timeout) { return sent; } } @@ -70,8 +71,9 @@ int UDPSocket::recvfrom(SocketAddress *address, void *buffer, unsigned size) } int recv = _iface->socket_recvfrom(_socket, address, buffer, size); - if (recv != NSAPI_ERROR_WOULD_BLOCK || !_blocking || - (_timeout && timer.read_ms() > _timeout)) { + if (recv != NSAPI_ERROR_WOULD_BLOCK + || _timeout < 0 + || timer.read_ms() > _timeout) { return recv; } } From 943dd711a9065adfd1645ecb396afea43d13cdce Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Wed, 20 Apr 2016 19:30:16 -0500 Subject: [PATCH 24/40] Added WFI to save power in temporary polling implementation --- Socket.cpp | 4 ++++ Socket.h | 1 + TCPServer.cpp | 9 +++++++-- TCPSocket.cpp | 18 ++++++++++++++---- UDPSocket.cpp | 18 ++++++++++++++---- 5 files changed, 40 insertions(+), 10 deletions(-) diff --git a/Socket.cpp b/Socket.cpp index d21f984e7e..3b853e19ec 100644 --- a/Socket.cpp +++ b/Socket.cpp @@ -109,6 +109,10 @@ int Socket::getsockopt(int level, int optname, void *optval, unsigned *optlen) } +void Socket::wakeup() +{ +} + void Socket::thunk(void *data) { Socket *self = (Socket *)data; diff --git a/Socket.h b/Socket.h index 195da38204..64e7325214 100644 --- a/Socket.h +++ b/Socket.h @@ -169,6 +169,7 @@ protected: int open(NetworkStack *iface, nsapi_protocol_t proto); static void thunk(void *); + static void wakeup(); NetworkStack *_iface; void *_socket; diff --git a/TCPServer.cpp b/TCPServer.cpp index 7a65e954da..e31a6328be 100644 --- a/TCPServer.cpp +++ b/TCPServer.cpp @@ -44,6 +44,10 @@ int TCPServer::accept(TCPSocket *connection) { mbed::Timer timer; timer.start(); + mbed::Timeout timeout; + if (_timeout >= 0) { + timeout.attach_us(&Socket::wakeup, _timeout * 1000); + } if (connection->_socket) { connection->close(); @@ -61,9 +65,10 @@ int TCPServer::accept(TCPSocket *connection) } if (err != NSAPI_ERROR_WOULD_BLOCK - || _timeout < 0 - || timer.read_ms() > _timeout) { + || (_timeout >= 0 && timer.read_ms() >= _timeout)) { return err; } + + __WFI(); } } diff --git a/TCPSocket.cpp b/TCPSocket.cpp index 926473dacc..4ec5ce6ab1 100644 --- a/TCPSocket.cpp +++ b/TCPSocket.cpp @@ -54,6 +54,10 @@ int TCPSocket::send(const void *data, unsigned size) { mbed::Timer timer; timer.start(); + mbed::Timeout timeout; + if (_timeout >= 0) { + timeout.attach_us(&Socket::wakeup, _timeout * 1000); + } while (true) { if (!_socket) { @@ -62,10 +66,11 @@ int TCPSocket::send(const void *data, unsigned size) int sent = _iface->socket_send(_socket, data, size); if (sent != NSAPI_ERROR_WOULD_BLOCK - || _timeout < 0 - || timer.read_ms() > _timeout) { + || (_timeout >= 0 && timer.read_ms() >= _timeout)) { return sent; } + + __WFI(); } } @@ -73,6 +78,10 @@ int TCPSocket::recv(void *data, unsigned size) { mbed::Timer timer; timer.start(); + mbed::Timeout timeout; + if (_timeout >= 0) { + timeout.attach_us(&Socket::wakeup, _timeout * 1000); + } while (true) { if (!_socket) { @@ -81,9 +90,10 @@ int TCPSocket::recv(void *data, unsigned size) int recv = _iface->socket_recv(_socket, data, size); if (recv != NSAPI_ERROR_WOULD_BLOCK - || _timeout < 0 - || timer.read_ms() > _timeout) { + || (_timeout >= 0 && timer.read_ms() >= _timeout)) { return recv; } + + __WFI(); } } diff --git a/UDPSocket.cpp b/UDPSocket.cpp index a8148fb38f..9494323fbd 100644 --- a/UDPSocket.cpp +++ b/UDPSocket.cpp @@ -45,6 +45,10 @@ int UDPSocket::sendto(const SocketAddress &address, const void *data, unsigned s { mbed::Timer timer; timer.start(); + mbed::Timeout timeout; + if (_timeout >= 0) { + timeout.attach_us(&Socket::wakeup, _timeout * 1000); + } while (true) { if (!_socket) { @@ -53,10 +57,11 @@ int UDPSocket::sendto(const SocketAddress &address, const void *data, unsigned s int sent = _iface->socket_sendto(_socket, address, data, size); if (sent != NSAPI_ERROR_WOULD_BLOCK - || _timeout < 0 - || timer.read_ms() > _timeout) { + || (_timeout >= 0 && timer.read_ms() >= _timeout)) { return sent; } + + __WFI(); } } @@ -64,6 +69,10 @@ int UDPSocket::recvfrom(SocketAddress *address, void *buffer, unsigned size) { mbed::Timer timer; timer.start(); + mbed::Timeout timeout; + if (_timeout >= 0) { + timeout.attach_us(&Socket::wakeup, _timeout * 1000); + } while (true) { if (!_socket) { @@ -72,9 +81,10 @@ int UDPSocket::recvfrom(SocketAddress *address, void *buffer, unsigned size) int recv = _iface->socket_recvfrom(_socket, address, buffer, size); if (recv != NSAPI_ERROR_WOULD_BLOCK - || _timeout < 0 - || timer.read_ms() > _timeout) { + || (_timeout >= 0 && timer.read_ms() >= _timeout)) { return recv; } + + __WFI(); } } From 710fab5b8b462aeb86b3de875e27a0f315224948 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 10 May 2016 12:45:12 -0500 Subject: [PATCH 25/40] Add NSAPI_ERROR_PARAMETER per @c1728p9 --- NetworkStack.h | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/NetworkStack.h b/NetworkStack.h index f82bd24e1d..30dea56b26 100644 --- a/NetworkStack.h +++ b/NetworkStack.h @@ -30,15 +30,16 @@ */ enum nsapi_error_t { NSAPI_ERROR_WOULD_BLOCK = -3001, /*!< no data is not available but call is non-blocking */ - NSAPI_ERROR_UNSUPPORTED = -3002, /*!< unsupported configuration */ - NSAPI_ERROR_NO_CONNECTION = -3003, /*!< not connected to a network */ - NSAPI_ERROR_NO_SOCKET = -3004, /*!< socket not available for use */ - NSAPI_ERROR_NO_ADDRESS = -3005, /*!< IP address is not known */ - NSAPI_ERROR_NO_MEMORY = -3006, /*!< memory resource not available */ - NSAPI_ERROR_DNS_FAILURE = -3007, /*!< DNS failed to complete successfully */ - NSAPI_ERROR_DHCP_FAILURE = -3008, /*!< DHCP failed to complete successfully */ - NSAPI_ERROR_AUTH_FAILURE = -3009, /*!< connection to access point faield */ - NSAPI_ERROR_DEVICE_ERROR = -3010, /*!< failure interfacing with the network procesor */ + NSAPI_ERROR_UNSUPPORTED = -3002, /*!< unsupported functionality */ + NSAPI_ERROR_PARAMETER = -3003, /*!< invalid configuration */ + NSAPI_ERROR_NO_CONNECTION = -3004, /*!< not connected to a network */ + NSAPI_ERROR_NO_SOCKET = -3005, /*!< socket not available for use */ + NSAPI_ERROR_NO_ADDRESS = -3006, /*!< IP address is not known */ + NSAPI_ERROR_NO_MEMORY = -3007, /*!< memory resource not available */ + NSAPI_ERROR_DNS_FAILURE = -3008, /*!< DNS failed to complete successfully */ + NSAPI_ERROR_DHCP_FAILURE = -3009, /*!< DHCP failed to complete successfully */ + NSAPI_ERROR_AUTH_FAILURE = -3010, /*!< connection to access point faield */ + NSAPI_ERROR_DEVICE_ERROR = -3011, /*!< failure interfacing with the network procesor */ }; /** Enum of socket protocols From 3d310c2463bcf760899d6cd52f009a5bb4bfc003 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 10 May 2016 12:46:26 -0500 Subject: [PATCH 26/40] Small bug fixes mirrored from: https://developer.mbed.org/teams/NetworkSocketAPI/code/NetworkSocketAPI/ - Fix bug with SocketAddress init per @c1728p9 - Fix issue with not passing interface through accept call - Fix port issue in SocketAddress constructor --- SocketAddress.cpp | 11 +++++++++-- TCPServer.cpp | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/SocketAddress.cpp b/SocketAddress.cpp index 933f851731..1496aef21d 100644 --- a/SocketAddress.cpp +++ b/SocketAddress.cpp @@ -144,13 +144,17 @@ static void ipv6_to_address(char *addr, const uint8_t *bytes) SocketAddress::SocketAddress(NetworkStack *iface, const char *host, uint16_t port) { + memset(&_ip_address, 0, sizeof _ip_address); + // Check for valid IP addresses if (host && ipv4_is_valid(host)) { _ip_version = NSAPI_IPv4; ipv4_from_address(_ip_bytes, host); + set_port(port); } else if (host && ipv6_is_valid(host)) { _ip_version = NSAPI_IPv6; ipv4_from_address(_ip_bytes, host); + set_port(port); } else { // DNS lookup int err = iface->gethostbyname(this, host); @@ -166,18 +170,21 @@ SocketAddress::SocketAddress(NetworkStack *iface, const char *host, uint16_t por SocketAddress::SocketAddress(const char *addr, uint16_t port) { + memset(&_ip_address, 0, sizeof _ip_address); set_ip_address(addr); set_port(port); } SocketAddress::SocketAddress(const void *bytes, nsapi_version_t version, uint16_t port) { + memset(&_ip_address, 0, sizeof _ip_address); set_ip_bytes(bytes, version); set_port(port); } SocketAddress::SocketAddress(const SocketAddress &addr) { + memset(&_ip_address, 0, sizeof _ip_address); set_ip_bytes(addr.get_ip_bytes(), addr.get_ip_version()); set_port(addr.get_port()); } @@ -202,10 +209,10 @@ void SocketAddress::set_ip_bytes(const void *bytes, nsapi_version_t version) { _ip_address[0] = '\0'; - if (_ip_version == NSAPI_IPv4) { + if (version == NSAPI_IPv4) { _ip_version = NSAPI_IPv4; memcpy(_ip_bytes, bytes, NSAPI_IPv4_BYTES); - } else if (_ip_version == NSAPI_IPv6) { + } else if (version == NSAPI_IPv6) { _ip_version = NSAPI_IPv6; memcpy(_ip_bytes, bytes, NSAPI_IPv6_BYTES); } else { diff --git a/TCPServer.cpp b/TCPServer.cpp index e31a6328be..84c47a09ae 100644 --- a/TCPServer.cpp +++ b/TCPServer.cpp @@ -61,6 +61,7 @@ int TCPServer::accept(TCPSocket *connection) void *socket; int err = _iface->socket_accept(&socket, _socket); if (!err) { + connection->_iface = _iface; connection->_socket = socket; } From 8f33c0fc4f1470336ea3a5de7a73ae9d6d1f844c Mon Sep 17 00:00:00 2001 From: Russ Butler Date: Tue, 3 May 2016 15:29:54 -0500 Subject: [PATCH 27/40] Add synchronization to the network socket API Add mutexes to protect the network socket API. Also use semaphores to wait for read/write events. Also fix a typo in the comments for timeout. --- Socket.cpp | 100 +++++++++++++++++++++++++++++------------ Socket.h | 13 +++--- TCPServer.cpp | 72 ++++++++++++++++++++---------- TCPServer.h | 4 ++ TCPSocket.cpp | 121 +++++++++++++++++++++++++++++++++++++------------- TCPSocket.h | 8 +++- UDPSocket.cpp | 108 ++++++++++++++++++++++++++++++++------------ UDPSocket.h | 7 +++ 8 files changed, 315 insertions(+), 118 deletions(-) diff --git a/Socket.cpp b/Socket.cpp index 3b853e19ec..fd32a30cb8 100644 --- a/Socket.cpp +++ b/Socket.cpp @@ -19,109 +19,153 @@ Socket::Socket() : _iface(0) , _socket(0) - , _timeout(-1) + , _timeout(osWaitForever) { } Socket::~Socket() { - if (_socket) { - close(); - } + // Underlying close is thread safe + close(); } int Socket::open(NetworkStack *iface, nsapi_protocol_t proto) { + _lock.lock(); + + if (_iface != NULL) { + _lock.unlock(); + return NSAPI_ERROR_PARAMETER; + } _iface = iface; void *socket; int err = _iface->socket_open(&socket, proto); if (err) { + _lock.unlock(); return err; } _socket = socket; _iface->socket_attach(_socket, &Socket::thunk, this); + _lock.unlock(); + return 0; } int Socket::close() { - if (!_socket) { - return 0; + _lock.lock(); + + int ret = 0; + if (_socket) { + _iface->socket_attach(_socket, 0, 0); + + void * socket = _socket; + _socket = 0; + ret = _iface->socket_close(socket); } - - _iface->socket_attach(_socket, 0, 0); - - void *volatile socket = _socket; - _socket = 0; - return _iface->socket_close(socket); + + // Wakeup anything in a blocking operation + // on this socket + socket_event(); + + _lock.unlock(); + return ret; } int Socket::bind(uint16_t port) { + // Underlying bind is thread safe SocketAddress addr(0, port); return bind(addr); } int Socket::bind(const char *address, uint16_t port) { + // Underlying bind is thread safe SocketAddress addr(address, port); return bind(addr); } int Socket::bind(const SocketAddress &address) { - if (!_socket) { - return NSAPI_ERROR_NO_SOCKET; + _lock.lock(); + + int ret = NSAPI_ERROR_NO_SOCKET; + if (_socket) { + ret = _iface->socket_bind(_socket, address); } - return _iface->socket_bind(_socket, address); + _lock.unlock(); + return ret; } void Socket::set_blocking(bool blocking) { + // Socket::set_timeout is thread safe set_timeout(blocking ? -1 : 0); } void Socket::set_timeout(int timeout) { - _timeout = timeout; + _lock.lock(); + + if (timeout >= 0) { + _timeout = (uint32_t)timeout; + } else { + _timeout = osWaitForever; + } + + _lock.unlock(); } int Socket::setsockopt(int level, int optname, const void *optval, unsigned optlen) { - if (!_socket) { - return NSAPI_ERROR_NO_SOCKET; + _lock.lock(); + + int ret = NSAPI_ERROR_NO_SOCKET; + if (_socket) { + ret = _iface->setsockopt(_socket, level, optname, optval, optlen); } - return _iface->setsockopt(_socket, level, optname, optval, optlen); + _lock.unlock(); + return ret; } int Socket::getsockopt(int level, int optname, void *optval, unsigned *optlen) { - if (!_socket) { - return NSAPI_ERROR_NO_SOCKET; + _lock.lock(); + + int ret = NSAPI_ERROR_NO_SOCKET; + if (_socket) { + ret = _iface->getsockopt(_socket, level, optname, optval, optlen); } - return _iface->getsockopt(_socket, level, optname, optval, optlen); + _lock.unlock(); + return ret; } -void Socket::wakeup() +void Socket::attach(FunctionPointer callback) { + _lock.lock(); + + _callback = callback; + + _lock.unlock(); } void Socket::thunk(void *data) { Socket *self = (Socket *)data; - if (self->_callback) { - self->_callback(); - } + self->socket_event(); } -void Socket::attach(FunctionPointer callback) +void Socket::socket_event(void) { - _callback = callback; + if (_callback) { + _callback(); + } } diff --git a/Socket.h b/Socket.h index 64e7325214..bee2d11a74 100644 --- a/Socket.h +++ b/Socket.h @@ -19,6 +19,7 @@ #include "SocketAddress.h" #include "NetworkStack.h" +#include "Mutex.h" /** Abstract socket class */ @@ -97,10 +98,11 @@ public: * * Initially all sockets have unbounded timeouts. NSAPI_ERROR_WOULD_BLOCK * is returned if a blocking operation takes longer than the specified - * timeout. A timeout of -1 removes the timeout from the socket. + * timeout. A timeout of 0 removes the timeout from the socket. A negative + * value give the socket an unbounded timeout. * - * set_timeout(-1) is equivalent to set_blocking(false) - * set_timeout(0) is equivalent to set_blocking(true) + * set_timeout(0) is equivalent to set_blocking(false) + * set_timeout(-1) is equivalent to set_blocking(true) * * @param timeout Timeout in milliseconds */ @@ -169,12 +171,13 @@ protected: int open(NetworkStack *iface, nsapi_protocol_t proto); static void thunk(void *); - static void wakeup(); + virtual void socket_event(void); NetworkStack *_iface; void *_socket; - int _timeout; + uint32_t _timeout; FunctionPointer _callback; + rtos::Mutex _lock; }; #endif diff --git a/TCPServer.cpp b/TCPServer.cpp index 84c47a09ae..4d4c9f5390 100644 --- a/TCPServer.cpp +++ b/TCPServer.cpp @@ -17,11 +17,11 @@ #include "TCPServer.h" #include "Timer.h" -TCPServer::TCPServer() +TCPServer::TCPServer(): _accept_sem(0) { } -TCPServer::TCPServer(NetworkStack *iface) +TCPServer::TCPServer(NetworkStack *iface): _accept_sem(0) { open(iface); } @@ -33,43 +33,69 @@ int TCPServer::open(NetworkStack *iface) int TCPServer::listen(int backlog) { - if (!_socket) { - return NSAPI_ERROR_NO_SOCKET; + _lock.lock(); + + int ret = NSAPI_ERROR_NO_SOCKET; + if (_socket) { + ret = _iface->socket_listen(_socket, backlog); } - return _iface->socket_listen(_socket, backlog); + _lock.unlock(); + return ret; } int TCPServer::accept(TCPSocket *connection) { - mbed::Timer timer; - timer.start(); - mbed::Timeout timeout; - if (_timeout >= 0) { - timeout.attach_us(&Socket::wakeup, _timeout * 1000); - } - - if (connection->_socket) { - connection->close(); - } + _lock.lock(); + int ret = NSAPI_ERROR_NO_SOCKET; while (true) { if (!_socket) { - return NSAPI_ERROR_NO_SOCKET; + ret = NSAPI_ERROR_NO_SOCKET; + break; } void *socket; - int err = _iface->socket_accept(&socket, _socket); - if (!err) { + ret = _iface->socket_accept(&socket, _socket); + if (0 == ret) { + connection->_lock.lock(); + + if (connection->_socket) { + connection->close(); + } + connection->_iface = _iface; connection->_socket = socket; + _iface->socket_attach(socket, &Socket::thunk, connection); + + connection->_lock.unlock(); + break; } - if (err != NSAPI_ERROR_WOULD_BLOCK - || (_timeout >= 0 && timer.read_ms() >= _timeout)) { - return err; - } + if (NSAPI_ERROR_WOULD_BLOCK == ret) { + int32_t count; - __WFI(); + _lock.unlock(); + count = _accept_sem.wait(_timeout); + _lock.lock(); + + if (count < 1) { + ret = NSAPI_ERROR_WOULD_BLOCK; + break; + } + } } + + _lock.unlock(); + return ret; +} + +void TCPServer::socket_event() +{ + int32_t status = _accept_sem.wait(0); + if (status <= 1) { + _accept_sem.release(); + } + + Socket::socket_event(); } diff --git a/TCPServer.h b/TCPServer.h index 0bb7d15a01..9155f78e67 100644 --- a/TCPServer.h +++ b/TCPServer.h @@ -20,6 +20,7 @@ #include "Socket.h" #include "TCPSocket.h" #include "NetworkStack.h" +#include "Semaphore.h" /** TCP socket server */ @@ -74,6 +75,9 @@ public: * @return 0 on success, negative error code on failure */ int accept(TCPSocket *connection); +protected: + virtual void socket_event(void); + rtos::Semaphore _accept_sem; }; #endif diff --git a/TCPSocket.cpp b/TCPSocket.cpp index 4ec5ce6ab1..14c0b61736 100644 --- a/TCPSocket.cpp +++ b/TCPSocket.cpp @@ -17,83 +17,140 @@ #include "TCPSocket.h" #include "Timer.h" -TCPSocket::TCPSocket() +TCPSocket::TCPSocket(): _read_sem(0), _write_sem(0) { } -TCPSocket::TCPSocket(NetworkStack *iface) +TCPSocket::TCPSocket(NetworkStack *iface): _read_sem(0), _write_sem(0) { + // TCPSocket::open is thread safe open(iface); } int TCPSocket::open(NetworkStack *iface) { + // Socket::open is thread safe return Socket::open(iface, NSAPI_TCP); } int TCPSocket::connect(const SocketAddress &addr) { - if (!_socket) { - return NSAPI_ERROR_NO_SOCKET; + _lock.lock(); + + int ret = NSAPI_ERROR_NO_SOCKET; + if (_socket) { + ret = _iface->socket_connect(_socket, addr); } - return _iface->socket_connect(_socket, addr); + _lock.unlock(); + return ret; } int TCPSocket::connect(const char *host, uint16_t port) { + _lock.lock(); + SocketAddress addr(_iface, host, port); - if (!addr) { - return NSAPI_ERROR_DNS_FAILURE; + int ret = NSAPI_ERROR_DNS_FAILURE; + if (addr) { + ret = connect(addr); } - return connect(addr); + _lock.unlock(); + return ret; } int TCPSocket::send(const void *data, unsigned size) { - mbed::Timer timer; - timer.start(); - mbed::Timeout timeout; - if (_timeout >= 0) { - timeout.attach_us(&Socket::wakeup, _timeout * 1000); + if (osOK != _write_lock.lock(_timeout)) { + return NSAPI_ERROR_WOULD_BLOCK; } + _lock.lock(); + int ret; while (true) { if (!_socket) { - return NSAPI_ERROR_NO_SOCKET; + ret = NSAPI_ERROR_NO_SOCKET; + break; } int sent = _iface->socket_send(_socket, data, size); - if (sent != NSAPI_ERROR_WOULD_BLOCK - || (_timeout >= 0 && timer.read_ms() >= _timeout)) { - return sent; - } + if ((0 == _timeout) || (NSAPI_ERROR_WOULD_BLOCK != sent)) { + ret = sent; + break; + } else { + int32_t count; - __WFI(); + // Release lock before blocking so other threads + // accessing this object aren't blocked + _lock.unlock(); + count = _write_sem.wait(_timeout); + _lock.lock(); + + if (count < 1) { + // Semaphore wait timed out so break out and return + ret = NSAPI_ERROR_WOULD_BLOCK; + break; + } + } } + + _lock.unlock(); + _write_lock.unlock(); + return ret; } int TCPSocket::recv(void *data, unsigned size) { - mbed::Timer timer; - timer.start(); - mbed::Timeout timeout; - if (_timeout >= 0) { - timeout.attach_us(&Socket::wakeup, _timeout * 1000); + if (osOK != _read_lock.lock(_timeout)) { + return NSAPI_ERROR_WOULD_BLOCK; } + _lock.lock(); + int ret; while (true) { if (!_socket) { - return NSAPI_ERROR_NO_SOCKET; - } - - int recv = _iface->socket_recv(_socket, data, size); - if (recv != NSAPI_ERROR_WOULD_BLOCK - || (_timeout >= 0 && timer.read_ms() >= _timeout)) { - return recv; + ret = NSAPI_ERROR_NO_SOCKET; + break; } - __WFI(); + int recv = _iface->socket_recv(_socket, data, size); + if ((0 == _timeout) || (NSAPI_ERROR_WOULD_BLOCK != recv)) { + ret = recv; + break; + } else { + int32_t count; + + // Release lock before blocking so other threads + // accessing this object aren't blocked + _lock.unlock(); + count = _read_sem.wait(_timeout); + _lock.lock(); + + if (count < 1) { + // Semaphore wait timed out so break out and return + ret = NSAPI_ERROR_WOULD_BLOCK; + break; + } + } } + + _lock.unlock(); + _read_lock.unlock(); + return ret; +} + +void TCPSocket::socket_event() +{ + int32_t count; + count = _write_sem.wait(0); + if (count <= 1) { + _write_sem.release(); + } + count = _read_sem.wait(0); + if (count <= 1) { + _read_sem.release(); + } + + Socket::socket_event(); } diff --git a/TCPSocket.h b/TCPSocket.h index 2a37f776fb..5341174344 100644 --- a/TCPSocket.h +++ b/TCPSocket.h @@ -19,6 +19,7 @@ #include "Socket.h" #include "NetworkStack.h" +#include "Semaphore.h" /** TCP socket connection */ @@ -101,7 +102,12 @@ public: */ int recv(void *data, unsigned size); -private: +protected: + virtual void socket_event(void); + rtos::Mutex _read_lock; + rtos::Semaphore _read_sem; + rtos::Mutex _write_lock; + rtos::Semaphore _write_sem; friend class TCPServer; }; diff --git a/UDPSocket.cpp b/UDPSocket.cpp index 9494323fbd..dbff20fbf0 100644 --- a/UDPSocket.cpp +++ b/UDPSocket.cpp @@ -17,11 +17,11 @@ #include "UDPSocket.h" #include "Timer.h" -UDPSocket::UDPSocket() +UDPSocket::UDPSocket(): _read_sem(0), _write_sem(0) { } -UDPSocket::UDPSocket(NetworkStack *iface) +UDPSocket::UDPSocket(NetworkStack *iface): _read_sem(0), _write_sem(0) { open(iface); } @@ -38,53 +38,103 @@ int UDPSocket::sendto(const char *host, uint16_t port, const void *data, unsigne return NSAPI_ERROR_DNS_FAILURE; } - return sendto(addr, data, size); + // sendto is thread safe + int ret = sendto(addr, data, size); + + return ret; } int UDPSocket::sendto(const SocketAddress &address, const void *data, unsigned size) { - mbed::Timer timer; - timer.start(); - mbed::Timeout timeout; - if (_timeout >= 0) { - timeout.attach_us(&Socket::wakeup, _timeout * 1000); + if (osOK != _write_lock.lock(_timeout)) { + return NSAPI_ERROR_WOULD_BLOCK; } + _lock.lock(); + int ret; while (true) { if (!_socket) { - return NSAPI_ERROR_NO_SOCKET; - } - - int sent = _iface->socket_sendto(_socket, address, data, size); - if (sent != NSAPI_ERROR_WOULD_BLOCK - || (_timeout >= 0 && timer.read_ms() >= _timeout)) { - return sent; + ret = NSAPI_ERROR_NO_SOCKET; + break; } - __WFI(); + int sent = _iface->socket_sendto(_socket, address, data, size); + if ((0 == _timeout) || (NSAPI_ERROR_WOULD_BLOCK != sent)) { + ret = sent; + break; + } else { + int32_t count; + + // Release lock before blocking so other threads + // accessing this object aren't blocked + _lock.unlock(); + count = _write_sem.wait(_timeout); + _lock.lock(); + + if (count < 1) { + // Semaphore wait timed out so break out and return + ret = NSAPI_ERROR_WOULD_BLOCK; + break; + } + } } + + _lock.unlock(); + _write_lock.unlock(); + return ret; } int UDPSocket::recvfrom(SocketAddress *address, void *buffer, unsigned size) { - mbed::Timer timer; - timer.start(); - mbed::Timeout timeout; - if (_timeout >= 0) { - timeout.attach_us(&Socket::wakeup, _timeout * 1000); + if (osOK != _read_lock.lock(_timeout)) { + return NSAPI_ERROR_WOULD_BLOCK; } + _lock.lock(); + int ret; while (true) { if (!_socket) { - return NSAPI_ERROR_NO_SOCKET; - } - - int recv = _iface->socket_recvfrom(_socket, address, buffer, size); - if (recv != NSAPI_ERROR_WOULD_BLOCK - || (_timeout >= 0 && timer.read_ms() >= _timeout)) { - return recv; + ret = NSAPI_ERROR_NO_SOCKET; + break; } - __WFI(); + int recv = _iface->socket_recvfrom(_socket, address, buffer, size); + if ((0 == _timeout) || (NSAPI_ERROR_WOULD_BLOCK != recv)) { + ret = recv; + break; + } else { + int32_t count; + + // Release lock before blocking so other threads + // accessing this object aren't blocked + _lock.unlock(); + count = _read_sem.wait(_timeout); + _lock.lock(); + + if (count < 1) { + // Semaphore wait timed out so break out and return + ret = NSAPI_ERROR_WOULD_BLOCK; + break; + } + } } + + _lock.unlock(); + _read_lock.unlock(); + return ret; +} + +void UDPSocket::socket_event() +{ + int32_t count; + count = _write_sem.wait(0); + if (count <= 1) { + _write_sem.release(); + } + count = _read_sem.wait(0); + if (count <= 1) { + _read_sem.release(); + } + + Socket::socket_event(); } diff --git a/UDPSocket.h b/UDPSocket.h index b2a8454d9f..d5152d6348 100644 --- a/UDPSocket.h +++ b/UDPSocket.h @@ -19,6 +19,7 @@ #include "Socket.h" #include "NetworkStack.h" +#include "Semaphore.h" /** UDP socket */ @@ -100,6 +101,12 @@ public: * code on failure */ int recvfrom(SocketAddress *address, void *data, unsigned size); +protected: + virtual void socket_event(void); + rtos::Mutex _read_lock; + rtos::Semaphore _read_sem; + rtos::Mutex _write_lock; + rtos::Semaphore _write_sem; }; #endif From 1c0a7c2426e3386180725c3e9463e58f567f0f35 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Mon, 16 May 2016 11:48:09 -0500 Subject: [PATCH 28/40] Adopt Callback class in NetworkSocketAPI --- Socket.cpp | 2 +- Socket.h | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Socket.cpp b/Socket.cpp index fd32a30cb8..b525a2d6ae 100644 --- a/Socket.cpp +++ b/Socket.cpp @@ -148,7 +148,7 @@ int Socket::getsockopt(int level, int optname, void *optval, unsigned *optlen) } -void Socket::attach(FunctionPointer callback) +void Socket::attach(Callback callback) { _lock.lock(); diff --git a/Socket.h b/Socket.h index bee2d11a74..cb9d01df12 100644 --- a/Socket.h +++ b/Socket.h @@ -145,9 +145,9 @@ public: * The callback may be called in an interrupt context and should not * perform expensive operations such as recv/send calls. * - * @param callback Function to call on state change + * @param func Function to call on state change */ - void attach(FunctionPointer callback); + void attach(Callback func); /** Register a callback on state change of the socket * @@ -158,12 +158,12 @@ public: * The callback may be called in an interrupt context and should not * perform expensive operations such as recv/send calls. * - * @param tptr Pointer to object to call method on - * @param mptr Method to call on state change + * @param obj Pointer to object to call method on + * @param method Method to call on state change */ template - void attach(T *tptr, M mptr) { - attach(FunctionPointer(tptr, mptr)); + void attach(T *obj, M method) { + attach(Callback(obj, method)); } protected: @@ -176,7 +176,7 @@ protected: NetworkStack *_iface; void *_socket; uint32_t _timeout; - FunctionPointer _callback; + Callback _callback; rtos::Mutex _lock; }; From c3baf2792e3193fb29f45146a35bea444a48503d Mon Sep 17 00:00:00 2001 From: Russ Butler Date: Tue, 17 May 2016 08:24:10 -0500 Subject: [PATCH 29/40] Fix SocketAddress constructor to support ipv6 Fix typo causing ipv6 addresses in the constructor to fail. --- SocketAddress.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SocketAddress.cpp b/SocketAddress.cpp index 1496aef21d..ec6a15fb82 100644 --- a/SocketAddress.cpp +++ b/SocketAddress.cpp @@ -153,7 +153,7 @@ SocketAddress::SocketAddress(NetworkStack *iface, const char *host, uint16_t por set_port(port); } else if (host && ipv6_is_valid(host)) { _ip_version = NSAPI_IPv6; - ipv4_from_address(_ip_bytes, host); + ipv6_from_address(_ip_bytes, host); set_port(port); } else { // DNS lookup From a6edc268f34b254b7578278a336109e25b00f016 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Mon, 6 Jun 2016 16:35:12 -0500 Subject: [PATCH 30/40] Coalesce redundant events to reduce cpu usage in lazy implementations *cough* esp8266 *cough* this is especially important for event-loop based systems where excessive events results in problematic memory consumption. --- Socket.cpp | 24 +++--------------------- Socket.h | 7 +++---- TCPServer.cpp | 26 +++++++++++++++++++------- TCPServer.h | 9 ++++++++- TCPSocket.cpp | 29 ++++++++++++++++++++--------- TCPSocket.h | 9 ++++++++- UDPSocket.cpp | 29 ++++++++++++++++++++--------- UDPSocket.h | 9 ++++++++- 8 files changed, 89 insertions(+), 53 deletions(-) diff --git a/Socket.cpp b/Socket.cpp index b525a2d6ae..49b92f4d13 100644 --- a/Socket.cpp +++ b/Socket.cpp @@ -23,12 +23,6 @@ Socket::Socket() { } -Socket::~Socket() -{ - // Underlying close is thread safe - close(); -} - int Socket::open(NetworkStack *iface, nsapi_protocol_t proto) { _lock.lock(); @@ -47,7 +41,8 @@ int Socket::open(NetworkStack *iface, nsapi_protocol_t proto) } _socket = socket; - _iface->socket_attach(_socket, &Socket::thunk, this); + _event.attach(this, &Socket::event); + _iface->socket_attach(_socket, Callback::thunk, &_event); _lock.unlock(); @@ -69,7 +64,7 @@ int Socket::close() // Wakeup anything in a blocking operation // on this socket - socket_event(); + event(); _lock.unlock(); return ret; @@ -156,16 +151,3 @@ void Socket::attach(Callback callback) _lock.unlock(); } - -void Socket::thunk(void *data) -{ - Socket *self = (Socket *)data; - self->socket_event(); -} - -void Socket::socket_event(void) -{ - if (_callback) { - _callback(); - } -} diff --git a/Socket.h b/Socket.h index cb9d01df12..0050cd2dd2 100644 --- a/Socket.h +++ b/Socket.h @@ -29,7 +29,7 @@ public: * * Closes socket if the socket is still open */ - virtual ~Socket(); + virtual ~Socket() {} /** Opens a socket * @@ -169,13 +169,12 @@ public: protected: Socket(); int open(NetworkStack *iface, nsapi_protocol_t proto); - - static void thunk(void *); - virtual void socket_event(void); + virtual void event() = 0; NetworkStack *_iface; void *_socket; uint32_t _timeout; + Callback _event; Callback _callback; rtos::Mutex _lock; }; diff --git a/TCPServer.cpp b/TCPServer.cpp index 4d4c9f5390..3edae6093e 100644 --- a/TCPServer.cpp +++ b/TCPServer.cpp @@ -17,15 +17,22 @@ #include "TCPServer.h" #include "Timer.h" -TCPServer::TCPServer(): _accept_sem(0) +TCPServer::TCPServer() + : _pending(0), _accept_sem(0) { } -TCPServer::TCPServer(NetworkStack *iface): _accept_sem(0) +TCPServer::TCPServer(NetworkStack *iface) + : _pending(0), _accept_sem(0) { open(iface); } +TCPServer::~TCPServer() +{ + close(); +} + int TCPServer::open(NetworkStack *iface) { return Socket::open(iface, NSAPI_TCP); @@ -55,6 +62,7 @@ int TCPServer::accept(TCPSocket *connection) break; } + _pending = 0; void *socket; ret = _iface->socket_accept(&socket, _socket); if (0 == ret) { @@ -66,7 +74,8 @@ int TCPServer::accept(TCPSocket *connection) connection->_iface = _iface; connection->_socket = socket; - _iface->socket_attach(socket, &Socket::thunk, connection); + connection->_event = Callback(connection, &TCPSocket::event); + _iface->socket_attach(socket, &Callback::thunk, &connection->_event); connection->_lock.unlock(); break; @@ -90,12 +99,15 @@ int TCPServer::accept(TCPSocket *connection) return ret; } -void TCPServer::socket_event() +void TCPServer::event() { - int32_t status = _accept_sem.wait(0); - if (status <= 1) { + int32_t acount = _accept_sem.wait(0); + if (acount <= 1) { _accept_sem.release(); } - Socket::socket_event(); + _pending += 1; + if (_callback && _pending == 1) { + _callback(); + } } diff --git a/TCPServer.h b/TCPServer.h index 9155f78e67..6c32286216 100644 --- a/TCPServer.h +++ b/TCPServer.h @@ -32,6 +32,12 @@ public: */ TCPServer(); + /** Destroy a socket + * + * Closes socket if the socket is still open + */ + virtual ~TCPServer(); + /** Create a socket on a network stack * * Creates and opens a socket on the specified network stack. @@ -76,7 +82,8 @@ public: */ int accept(TCPSocket *connection); protected: - virtual void socket_event(void); + virtual void event(); + volatile unsigned _pending; rtos::Semaphore _accept_sem; }; diff --git a/TCPSocket.cpp b/TCPSocket.cpp index 14c0b61736..286683e75a 100644 --- a/TCPSocket.cpp +++ b/TCPSocket.cpp @@ -17,16 +17,23 @@ #include "TCPSocket.h" #include "Timer.h" -TCPSocket::TCPSocket(): _read_sem(0), _write_sem(0) +TCPSocket::TCPSocket() + : _pending(0), _read_sem(0), _write_sem(0) { } -TCPSocket::TCPSocket(NetworkStack *iface): _read_sem(0), _write_sem(0) +TCPSocket::TCPSocket(NetworkStack *iface) + : _pending(0), _read_sem(0), _write_sem(0) { // TCPSocket::open is thread safe open(iface); } +TCPSocket::~TCPSocket() +{ + close(); +} + int TCPSocket::open(NetworkStack *iface) { // Socket::open is thread safe @@ -74,6 +81,7 @@ int TCPSocket::send(const void *data, unsigned size) break; } + _pending = 0; int sent = _iface->socket_send(_socket, data, size); if ((0 == _timeout) || (NSAPI_ERROR_WOULD_BLOCK != sent)) { ret = sent; @@ -114,6 +122,7 @@ int TCPSocket::recv(void *data, unsigned size) break; } + _pending = 0; int recv = _iface->socket_recv(_socket, data, size); if ((0 == _timeout) || (NSAPI_ERROR_WOULD_BLOCK != recv)) { ret = recv; @@ -140,17 +149,19 @@ int TCPSocket::recv(void *data, unsigned size) return ret; } -void TCPSocket::socket_event() +void TCPSocket::event() { - int32_t count; - count = _write_sem.wait(0); - if (count <= 1) { + int32_t wcount = _write_sem.wait(0); + if (wcount <= 1) { _write_sem.release(); } - count = _read_sem.wait(0); - if (count <= 1) { + int32_t rcount = _read_sem.wait(0); + if (rcount <= 1) { _read_sem.release(); } - Socket::socket_event(); + _pending += 1; + if (_callback && _pending == 1) { + _callback(); + } } diff --git a/TCPSocket.h b/TCPSocket.h index 5341174344..4f19b60a0b 100644 --- a/TCPSocket.h +++ b/TCPSocket.h @@ -31,6 +31,12 @@ public: */ TCPSocket(); + /** Destroy a socket + * + * Closes socket if the socket is still open + */ + virtual ~TCPSocket(); + /** Create a socket on a network stack * * Creates and opens a socket on the specified network stack. @@ -103,7 +109,8 @@ public: int recv(void *data, unsigned size); protected: - virtual void socket_event(void); + virtual void event(); + volatile unsigned _pending; rtos::Mutex _read_lock; rtos::Semaphore _read_sem; rtos::Mutex _write_lock; diff --git a/UDPSocket.cpp b/UDPSocket.cpp index dbff20fbf0..361e3c83ae 100644 --- a/UDPSocket.cpp +++ b/UDPSocket.cpp @@ -17,15 +17,22 @@ #include "UDPSocket.h" #include "Timer.h" -UDPSocket::UDPSocket(): _read_sem(0), _write_sem(0) +UDPSocket::UDPSocket() + : _pending(0), _read_sem(0), _write_sem(0) { } -UDPSocket::UDPSocket(NetworkStack *iface): _read_sem(0), _write_sem(0) +UDPSocket::UDPSocket(NetworkStack *iface) + : _pending(0), _read_sem(0), _write_sem(0) { open(iface); } +UDPSocket::~UDPSocket() +{ + close(); +} + int UDPSocket::open(NetworkStack *iface) { return Socket::open(iface, NSAPI_UDP); @@ -58,6 +65,7 @@ int UDPSocket::sendto(const SocketAddress &address, const void *data, unsigned s break; } + _pending = 0; int sent = _iface->socket_sendto(_socket, address, data, size); if ((0 == _timeout) || (NSAPI_ERROR_WOULD_BLOCK != sent)) { ret = sent; @@ -98,6 +106,7 @@ int UDPSocket::recvfrom(SocketAddress *address, void *buffer, unsigned size) break; } + _pending = 0; int recv = _iface->socket_recvfrom(_socket, address, buffer, size); if ((0 == _timeout) || (NSAPI_ERROR_WOULD_BLOCK != recv)) { ret = recv; @@ -124,17 +133,19 @@ int UDPSocket::recvfrom(SocketAddress *address, void *buffer, unsigned size) return ret; } -void UDPSocket::socket_event() +void UDPSocket::event() { - int32_t count; - count = _write_sem.wait(0); - if (count <= 1) { + int32_t wcount = _write_sem.wait(0); + if (wcount <= 1) { _write_sem.release(); } - count = _read_sem.wait(0); - if (count <= 1) { + int32_t rcount = _read_sem.wait(0); + if (rcount <= 1) { _read_sem.release(); } - Socket::socket_event(); + _pending += 1; + if (_callback && _pending == 1) { + _callback(); + } } diff --git a/UDPSocket.h b/UDPSocket.h index d5152d6348..7e3439678e 100644 --- a/UDPSocket.h +++ b/UDPSocket.h @@ -31,6 +31,12 @@ public: */ UDPSocket(); + /** Destroy a socket + * + * Closes socket if the socket is still open + */ + virtual ~UDPSocket(); + /** Create a socket on a network stack * * Creates and opens a socket on the specified network stack. @@ -102,7 +108,8 @@ public: */ int recvfrom(SocketAddress *address, void *data, unsigned size); protected: - virtual void socket_event(void); + virtual void event(); + volatile unsigned _pending; rtos::Mutex _read_lock; rtos::Semaphore _read_sem; rtos::Mutex _write_lock; From 3ee8e30ae422a1a961ff7ccf2e08fac0d6b14991 Mon Sep 17 00:00:00 2001 From: geky Date: Wed, 8 Jun 2016 20:01:51 +0100 Subject: [PATCH 31/40] Added NSAPI_KEEPINTVL/NSAPI_KEEPIDLE options to the socket API --- NetworkStack.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NetworkStack.h b/NetworkStack.h index 30dea56b26..8667c9d9f0 100644 --- a/NetworkStack.h +++ b/NetworkStack.h @@ -73,6 +73,8 @@ enum nsapi_level_t { enum nsapi_option_t { NSAPI_REUSEADDR, /*!< Allow bind to reuse local addresses */ NSAPI_KEEPALIVE, /*!< Enables sending of keepalive messages */ + NSAPI_KEEPIDLE, /*!< Sets timeout value to initiate keepalive */ + NSAPI_KEEPINTVL, /*!< Sets timeout value for keepalive */ NSAPI_LINGER, /*!< Keeps close from returning until queues empty */ NSAPI_SNDBUF, /*!< Sets send buffer size */ NSAPI_RCVBUF, /*!< Sets recv buffer size */ From 90cd978785d0ac7d38115b09aecee797ff4c8a2c Mon Sep 17 00:00:00 2001 From: Russ Butler Date: Thu, 26 May 2016 23:54:05 -0500 Subject: [PATCH 32/40] Separate interface from stack in NSAPI This patch consists of: -Add NetworkInterface to wrap objects bound to a stack and update socket code to handle this in addition to NetworkStacks -Update MeshInterface to inherit from NetworkInterface -Update NanostackInterface so it only inherits from NetworkStack -Add MeshInterfaceNanostack and update LoWPANNDInterface and ThreadInterface to inherit from this --- EthernetInterface.h => EthInterface.h | 12 +++--- MeshInterface.h | 4 +- NetworkInterface.h | 41 +++++++++++++++++++++ NetworkStack.h | 4 +- SocketAddress.cpp | 53 ++++++++++++++++----------- SocketAddress.h | 15 ++++++++ TCPServer.cpp | 11 ++++++ TCPServer.h | 21 +++++++++++ TCPSocket.cpp | 13 +++++++ TCPSocket.h | 21 +++++++++++ UDPSocket.cpp | 11 ++++++ UDPSocket.h | 21 +++++++++++ WiFiInterface.h | 4 +- 13 files changed, 198 insertions(+), 33 deletions(-) rename EthernetInterface.h => EthInterface.h (87%) create mode 100644 NetworkInterface.h diff --git a/EthernetInterface.h b/EthInterface.h similarity index 87% rename from EthernetInterface.h rename to EthInterface.h index c0214c688b..e16a71b128 100644 --- a/EthernetInterface.h +++ b/EthInterface.h @@ -1,4 +1,4 @@ -/* EthernetInterface +/* EthInterface * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,16 +14,16 @@ * limitations under the License. */ -#ifndef ETHERNET_INTERFACE_H -#define ETHERNET_INTERFACE_H +#ifndef ETH_INTERFACE_H +#define ETH_INTERFACE_H -#include "NetworkStack.h" +#include "NetworkInterface.h" -/** EthernetInterface class +/** EthInterface class * * Common interface that is shared between ethernet hardware. */ -class EthernetInterface +class EthInterface : public NetworkInterface { public: /** Start the interface diff --git a/MeshInterface.h b/MeshInterface.h index 811b3f7352..54d3654b45 100644 --- a/MeshInterface.h +++ b/MeshInterface.h @@ -17,13 +17,13 @@ #ifndef MESH_INTERFACE_H #define MESH_INTERFACE_H -#include "NetworkStack.h" +#include "NetworkInterface.h" /** MeshInterface class * * Common interface that is shared between mesh hardware */ -class MeshInterface +class MeshInterface : public NetworkInterface { public: /** Start the interface diff --git a/NetworkInterface.h b/NetworkInterface.h new file mode 100644 index 0000000000..af97ee7ffd --- /dev/null +++ b/NetworkInterface.h @@ -0,0 +1,41 @@ +/* NetworkStack + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NETWORK_INTERFACE_H +#define NETWORK_INTERFACE_H + +#include "mbed.h" +#include "SocketAddress.h" + +class NetworkInterface { +public: + virtual ~NetworkInterface() {}; + NetworkInterface() {} + + /** Get the internally stored IP address + /return IP address of the interface or null if not yet connected + */ + virtual const char *get_ip_address() = 0; +protected: + friend class Socket; + friend class UDPSocket; + friend class TCPSocket; + friend class TCPServer; + friend class SocketAddress; + virtual NetworkStack * get_stack(void) = 0; +}; + +#endif diff --git a/NetworkStack.h b/NetworkStack.h index 8667c9d9f0..2c5b0f9c5a 100644 --- a/NetworkStack.h +++ b/NetworkStack.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef NETWORK_INTERFACE_H -#define NETWORK_INTERFACE_H +#ifndef NETWORK_STACK_H +#define NETWORK_STACK_H #include "mbed.h" #include "SocketAddress.h" diff --git a/SocketAddress.cpp b/SocketAddress.cpp index ec6a15fb82..31d560f2db 100644 --- a/SocketAddress.cpp +++ b/SocketAddress.cpp @@ -15,6 +15,7 @@ */ #include "SocketAddress.h" +#include "NetworkInterface.h" #include "NetworkStack.h" #include #include "mbed.h" @@ -144,28 +145,12 @@ static void ipv6_to_address(char *addr, const uint8_t *bytes) SocketAddress::SocketAddress(NetworkStack *iface, const char *host, uint16_t port) { - memset(&_ip_address, 0, sizeof _ip_address); + _SocketAddress(iface, host, port); +} - // Check for valid IP addresses - if (host && ipv4_is_valid(host)) { - _ip_version = NSAPI_IPv4; - ipv4_from_address(_ip_bytes, host); - set_port(port); - } else if (host && ipv6_is_valid(host)) { - _ip_version = NSAPI_IPv6; - ipv6_from_address(_ip_bytes, host); - set_port(port); - } else { - // DNS lookup - int err = iface->gethostbyname(this, host); - if (!err) { - set_port(port); - } else { - _ip_version = NSAPI_IPv4; - memset(_ip_bytes, 0, NSAPI_IPv4_BYTES); - set_port(0); - } - } +SocketAddress::SocketAddress(NetworkInterface *iface, const char *host, uint16_t port) +{ + _SocketAddress(iface->get_stack(), host, port); } SocketAddress::SocketAddress(const char *addr, uint16_t port) @@ -273,3 +258,29 @@ SocketAddress::operator bool() const return false; } + +void SocketAddress::_SocketAddress(NetworkStack *iface, const char *host, uint16_t port) +{ + memset(&_ip_address, 0, sizeof _ip_address); + + // Check for valid IP addresses + if (host && ipv4_is_valid(host)) { + _ip_version = NSAPI_IPv4; + ipv4_from_address(_ip_bytes, host); + set_port(port); + } else if (host && ipv6_is_valid(host)) { + _ip_version = NSAPI_IPv6; + ipv6_from_address(_ip_bytes, host); + set_port(port); + } else { + // DNS lookup + int err = iface->gethostbyname(this, host); + if (!err) { + set_port(port); + } else { + _ip_version = NSAPI_IPv4; + memset(_ip_bytes, 0, NSAPI_IPv4_BYTES); + set_port(0); + } + } +} diff --git a/SocketAddress.h b/SocketAddress.h index 64f2991e0b..f87653e987 100644 --- a/SocketAddress.h +++ b/SocketAddress.h @@ -65,6 +65,7 @@ enum nsapi_version_t { // Predeclared classes class NetworkStack; +class NetworkInterface; /** SocketAddress class @@ -86,6 +87,19 @@ public: */ SocketAddress(NetworkStack *iface, const char *host, uint16_t port = 0); + /** Create a SocketAddress from a hostname and port + * + * The hostname may be either a domain name or an IP address. If the + * hostname is an IP address, no network transactions will be performed. + * + * On failure, the IP address and port will be set to zero + * + * @param iface Network interface to use for DNS resolution + * @param host Hostname to resolve + * @param port Optional 16-bit port + */ + SocketAddress(NetworkInterface *iface, const char *host, uint16_t port = 0); + /** Create a SocketAddress from an IP address and port * * @param host Null-terminated representation of the IP address @@ -157,6 +171,7 @@ public: operator bool() const; private: + void _SocketAddress(NetworkStack *iface, const char *host, uint16_t port); char _ip_address[NSAPI_IP_SIZE]; uint8_t _ip_bytes[NSAPI_IP_BYTES]; nsapi_version_t _ip_version; diff --git a/TCPServer.cpp b/TCPServer.cpp index 3edae6093e..cf75096e3b 100644 --- a/TCPServer.cpp +++ b/TCPServer.cpp @@ -28,6 +28,12 @@ TCPServer::TCPServer(NetworkStack *iface) open(iface); } +TCPServer::TCPServer(NetworkInterface *iface) + : _pending(0), _accept_sem(0) +{ + open(iface->get_stack()); +} + TCPServer::~TCPServer() { close(); @@ -38,6 +44,11 @@ int TCPServer::open(NetworkStack *iface) return Socket::open(iface, NSAPI_TCP); } +int TCPServer::open(NetworkInterface *iface) +{ + return TCPServer::open(iface->get_stack()); +} + int TCPServer::listen(int backlog) { _lock.lock(); diff --git a/TCPServer.h b/TCPServer.h index 6c32286216..f5a7e2c87a 100644 --- a/TCPServer.h +++ b/TCPServer.h @@ -19,6 +19,7 @@ #include "Socket.h" #include "TCPSocket.h" +#include "NetworkInterface.h" #include "NetworkStack.h" #include "Semaphore.h" @@ -46,6 +47,15 @@ public: */ TCPServer(NetworkStack *iface); + /** Create a socket on a network interface + * + * Creates and opens a socket on the network stack of the given + * network interface. + * + * @param iface Network interface as target for socket + */ + TCPServer(NetworkInterface *iface); + /** Opens a socket * * Creates a network socket on the specified network stack. @@ -56,6 +66,17 @@ public: */ virtual int open(NetworkStack *iface); + /** Opens a socket + * + * Creates a network socket on the network stack of the given + * network interface. Not needed if stack is passed to the + * socket's constructor. + * + * @param iface Network interface as target for socket + * @return 0 on success, negative error code on failure + */ + virtual int open(NetworkInterface *iface); + /** Listen for connections on a TCP socket * * Marks the socket as a passive socket that can be used to accept diff --git a/TCPSocket.cpp b/TCPSocket.cpp index 286683e75a..59566d1921 100644 --- a/TCPSocket.cpp +++ b/TCPSocket.cpp @@ -29,6 +29,13 @@ TCPSocket::TCPSocket(NetworkStack *iface) open(iface); } +TCPSocket::TCPSocket(NetworkInterface *iface) + : _pending(0), _read_sem(0), _write_sem(0) +{ + // TCPSocket::open is thread safe + open(iface->get_stack()); +} + TCPSocket::~TCPSocket() { close(); @@ -40,6 +47,12 @@ int TCPSocket::open(NetworkStack *iface) return Socket::open(iface, NSAPI_TCP); } +int TCPSocket::open(NetworkInterface *iface) +{ + // Socket::open is thread safe + return TCPSocket::open(iface->get_stack()); +} + int TCPSocket::connect(const SocketAddress &addr) { _lock.lock(); diff --git a/TCPSocket.h b/TCPSocket.h index 4f19b60a0b..d4e6a1f845 100644 --- a/TCPSocket.h +++ b/TCPSocket.h @@ -18,6 +18,7 @@ #define TCPSOCKET_H #include "Socket.h" +#include "NetworkInterface.h" #include "NetworkStack.h" #include "Semaphore.h" @@ -45,6 +46,15 @@ public: */ TCPSocket(NetworkStack *iface); + /** Create a socket on a network interface + * + * Creates and opens a socket on the network stack of the given + * network interface. + * + * @param iface Network interface as target for socket + */ + TCPSocket(NetworkInterface *iface); + /** Opens a socket * * Creates a network socket on the specified network stack. @@ -55,6 +65,17 @@ public: */ virtual int open(NetworkStack *iface); + /** Opens a socket + * + * Creates a network socket on the network stack of the given + * network interface. Not needed if stack is passed to the + * socket's constructor. + * + * @param iface Network interface as target for socket + * @return 0 on success, negative error code on failure + */ + virtual int open(NetworkInterface *iface); + /** Connects TCP socket to a remote host * * Initiates a connection to a remote server specified by either diff --git a/UDPSocket.cpp b/UDPSocket.cpp index 361e3c83ae..d6e13b4226 100644 --- a/UDPSocket.cpp +++ b/UDPSocket.cpp @@ -28,6 +28,12 @@ UDPSocket::UDPSocket(NetworkStack *iface) open(iface); } +UDPSocket::UDPSocket(NetworkInterface *iface) + : _pending(0), _read_sem(0), _write_sem(0) +{ + open(iface->get_stack()); +} + UDPSocket::~UDPSocket() { close(); @@ -38,6 +44,11 @@ int UDPSocket::open(NetworkStack *iface) return Socket::open(iface, NSAPI_UDP); } +int UDPSocket::open(NetworkInterface *iface) +{ + return UDPSocket::open(iface->get_stack()); +} + int UDPSocket::sendto(const char *host, uint16_t port, const void *data, unsigned size) { SocketAddress addr(_iface, host, port); diff --git a/UDPSocket.h b/UDPSocket.h index 7e3439678e..3575f18cf5 100644 --- a/UDPSocket.h +++ b/UDPSocket.h @@ -18,6 +18,7 @@ #define UDPSOCKET_H #include "Socket.h" +#include "NetworkInterface.h" #include "NetworkStack.h" #include "Semaphore.h" @@ -45,6 +46,15 @@ public: */ UDPSocket(NetworkStack *iface); + /** Create a socket on a network interface + * + * Creates and opens a socket on the network stack of the given + * network interface. + * + * @param iface Network interface as target for socket + */ + UDPSocket(NetworkInterface *iface); + /** Opens a socket * * Creates a network socket on the specified network stack. @@ -55,6 +65,17 @@ public: */ virtual int open(NetworkStack *iface); + /** Opens a socket + * + * Creates a network socket on the network stack of the given + * network interface. Not needed if stack is passed to the + * socket's constructor. + * + * @param iface Network interface as target for socket + * @return 0 on success, negative error code on failure + */ + virtual int open(NetworkInterface *iface); + /** Send a packet over a UDP socket * * Sends data to the specified address specified by either a domain name diff --git a/WiFiInterface.h b/WiFiInterface.h index 09f5340d32..f99b411b01 100644 --- a/WiFiInterface.h +++ b/WiFiInterface.h @@ -17,7 +17,7 @@ #ifndef WIFI_INTERFACE_H #define WIFI_INTERFACE_H -#include "NetworkStack.h" +#include "NetworkInterface.h" /** Enum of WiFi encryption types * @@ -37,7 +37,7 @@ enum nsapi_security_t { * * Common interface that is shared between WiFi devices */ -class WiFiInterface +class WiFiInterface: public NetworkInterface { public: /** Start the interface From 04e4d9f450dfcc2b87280c7bb1436177056c6a93 Mon Sep 17 00:00:00 2001 From: Russ Butler Date: Tue, 19 Jul 2016 11:07:39 -0500 Subject: [PATCH 33/40] Fix async NSAPI race condition Remove read and write mutexes since multiple calls to send or multiple calls to recv on different threads is undefined behavior. This is because the size of data sent or received by these calls is undefined and could lead to the data being interleaved. The code now asserts that there are not multiple threads calling send at the same or calling recv at the same time. Note that calling send and recv from different threads at the same time is still safe and well defined behavior. By removing the read and write mutexes and associated timeout it guarantees that a stack call will always be made and thus the value NSAPI_ERROR_TIMEOUT cannot get falsely returned. --- TCPSocket.cpp | 30 +++++++++++++++++++----------- TCPSocket.h | 4 ++-- UDPSocket.cpp | 30 +++++++++++++++++++----------- UDPSocket.h | 4 ++-- 4 files changed, 42 insertions(+), 26 deletions(-) diff --git a/TCPSocket.cpp b/TCPSocket.cpp index 59566d1921..a9441de60b 100644 --- a/TCPSocket.cpp +++ b/TCPSocket.cpp @@ -16,21 +16,25 @@ #include "TCPSocket.h" #include "Timer.h" +#include "mbed_assert.h" TCPSocket::TCPSocket() - : _pending(0), _read_sem(0), _write_sem(0) + : _pending(0), _read_sem(0), _write_sem(0), + _read_in_progress(false), _write_in_progress(false) { } TCPSocket::TCPSocket(NetworkStack *iface) - : _pending(0), _read_sem(0), _write_sem(0) + : _pending(0), _read_sem(0), _write_sem(0), + _read_in_progress(false), _write_in_progress(false) { // TCPSocket::open is thread safe open(iface); } TCPSocket::TCPSocket(NetworkInterface *iface) - : _pending(0), _read_sem(0), _write_sem(0) + : _pending(0), _read_sem(0), _write_sem(0), + _read_in_progress(false), _write_in_progress(false) { // TCPSocket::open is thread safe open(iface->get_stack()); @@ -82,10 +86,12 @@ int TCPSocket::connect(const char *host, uint16_t port) int TCPSocket::send(const void *data, unsigned size) { - if (osOK != _write_lock.lock(_timeout)) { - return NSAPI_ERROR_WOULD_BLOCK; - } _lock.lock(); + // If this assert is hit then there are two threads + // performing a send at the same time which is undefined + // behavior + MBED_ASSERT(!_write_in_progress); + _write_in_progress = true; int ret; while (true) { @@ -116,17 +122,19 @@ int TCPSocket::send(const void *data, unsigned size) } } + _write_in_progress = false; _lock.unlock(); - _write_lock.unlock(); return ret; } int TCPSocket::recv(void *data, unsigned size) { - if (osOK != _read_lock.lock(_timeout)) { - return NSAPI_ERROR_WOULD_BLOCK; - } _lock.lock(); + // If this assert is hit then there are two threads + // performing a recv at the same time which is undefined + // behavior + MBED_ASSERT(!_read_in_progress); + _read_in_progress = true; int ret; while (true) { @@ -157,8 +165,8 @@ int TCPSocket::recv(void *data, unsigned size) } } + _read_in_progress = false; _lock.unlock(); - _read_lock.unlock(); return ret; } diff --git a/TCPSocket.h b/TCPSocket.h index d4e6a1f845..041aaa7629 100644 --- a/TCPSocket.h +++ b/TCPSocket.h @@ -132,10 +132,10 @@ public: protected: virtual void event(); volatile unsigned _pending; - rtos::Mutex _read_lock; rtos::Semaphore _read_sem; - rtos::Mutex _write_lock; rtos::Semaphore _write_sem; + bool _read_in_progress; + bool _write_in_progress; friend class TCPServer; }; diff --git a/UDPSocket.cpp b/UDPSocket.cpp index d6e13b4226..29b2dcdb4a 100644 --- a/UDPSocket.cpp +++ b/UDPSocket.cpp @@ -16,20 +16,24 @@ #include "UDPSocket.h" #include "Timer.h" +#include "mbed_assert.h" UDPSocket::UDPSocket() - : _pending(0), _read_sem(0), _write_sem(0) + : _pending(0), _read_sem(0), _write_sem(0), + _read_in_progress(false), _write_in_progress(false) { } UDPSocket::UDPSocket(NetworkStack *iface) - : _pending(0), _read_sem(0), _write_sem(0) + : _pending(0), _read_sem(0), _write_sem(0), + _read_in_progress(false), _write_in_progress(false) { open(iface); } UDPSocket::UDPSocket(NetworkInterface *iface) - : _pending(0), _read_sem(0), _write_sem(0) + : _pending(0), _read_sem(0), _write_sem(0), + _read_in_progress(false), _write_in_progress(false) { open(iface->get_stack()); } @@ -64,10 +68,12 @@ int UDPSocket::sendto(const char *host, uint16_t port, const void *data, unsigne int UDPSocket::sendto(const SocketAddress &address, const void *data, unsigned size) { - if (osOK != _write_lock.lock(_timeout)) { - return NSAPI_ERROR_WOULD_BLOCK; - } _lock.lock(); + // If this assert is hit then there are two threads + // performing a send at the same time which is undefined + // behavior + MBED_ASSERT(!_write_in_progress); + _write_in_progress = true; int ret; while (true) { @@ -98,17 +104,19 @@ int UDPSocket::sendto(const SocketAddress &address, const void *data, unsigned s } } + _write_in_progress = false; _lock.unlock(); - _write_lock.unlock(); return ret; } int UDPSocket::recvfrom(SocketAddress *address, void *buffer, unsigned size) { - if (osOK != _read_lock.lock(_timeout)) { - return NSAPI_ERROR_WOULD_BLOCK; - } _lock.lock(); + // If this assert is hit then there are two threads + // performing a recv at the same time which is undefined + // behavior + MBED_ASSERT(!_read_in_progress); + _read_in_progress = true; int ret; while (true) { @@ -139,8 +147,8 @@ int UDPSocket::recvfrom(SocketAddress *address, void *buffer, unsigned size) } } + _read_in_progress = false; _lock.unlock(); - _read_lock.unlock(); return ret; } diff --git a/UDPSocket.h b/UDPSocket.h index 3575f18cf5..a46a3c0941 100644 --- a/UDPSocket.h +++ b/UDPSocket.h @@ -131,10 +131,10 @@ public: protected: virtual void event(); volatile unsigned _pending; - rtos::Mutex _read_lock; rtos::Semaphore _read_sem; - rtos::Mutex _write_lock; rtos::Semaphore _write_sem; + bool _read_in_progress; + bool _write_in_progress; }; #endif From 97380a75c6d99de522396161873b67342bb1e96c Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 19 Jul 2016 17:12:22 -0500 Subject: [PATCH 34/40] Restructured nsapi headers - Provide nsapi.h as central entry point - Provide nsapi_types.h as c-compatible destination for nsapi types - Standardize include paths --- CellularInterface.h | 6 +- EthInterface.h | 4 +- MeshInterface.h | 4 +- NetworkInterface.h | 5 +- NetworkStack.h | 65 +-------------------- Socket.h | 21 ++++--- SocketAddress.h | 47 +-------------- TCPServer.h | 12 ++-- TCPSocket.h | 10 ++-- UDPSocket.h | 10 ++-- WiFiInterface.h | 4 +- nsapi.h | 47 +++++++++++++++ nsapi_types.h | 136 ++++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 237 insertions(+), 134 deletions(-) create mode 100644 nsapi.h create mode 100644 nsapi_types.h diff --git a/CellularInterface.h b/CellularInterface.h index 0f9c28b840..a2b303e228 100644 --- a/CellularInterface.h +++ b/CellularInterface.h @@ -17,13 +17,14 @@ #ifndef CELLULAR_INTERFACE_H #define CELLULAR_INTERFACE_H -#include "NetworkStack.h" +#include "NetworkSocketAPI/NetworkInterface.h" + /** CellularInterface class * * Common interface that is shared between ethernet hardware */ -class CellularInterface +class CellularInterface : public NetworkInterface { public: /** Start the interface @@ -47,5 +48,6 @@ public: */ virtual const char *get_mac_address() = 0; }; + #endif diff --git a/EthInterface.h b/EthInterface.h index e16a71b128..1a0066c685 100644 --- a/EthInterface.h +++ b/EthInterface.h @@ -17,7 +17,8 @@ #ifndef ETH_INTERFACE_H #define ETH_INTERFACE_H -#include "NetworkInterface.h" +#include "NetworkSocketAPI/NetworkInterface.h" + /** EthInterface class * @@ -45,4 +46,5 @@ public: virtual const char *get_mac_address() = 0; }; + #endif diff --git a/MeshInterface.h b/MeshInterface.h index 54d3654b45..6aac499dd2 100644 --- a/MeshInterface.h +++ b/MeshInterface.h @@ -17,7 +17,8 @@ #ifndef MESH_INTERFACE_H #define MESH_INTERFACE_H -#include "NetworkInterface.h" +#include "NetworkSocketAPI/NetworkInterface.h" + /** MeshInterface class * @@ -45,4 +46,5 @@ public: virtual const char *get_mac_address() = 0; }; + #endif diff --git a/NetworkInterface.h b/NetworkInterface.h index af97ee7ffd..6a8b3ea6b4 100644 --- a/NetworkInterface.h +++ b/NetworkInterface.h @@ -17,8 +17,8 @@ #ifndef NETWORK_INTERFACE_H #define NETWORK_INTERFACE_H -#include "mbed.h" -#include "SocketAddress.h" +#include "NetworkSocketAPI/NetworkStack.h" + class NetworkInterface { public: @@ -38,4 +38,5 @@ protected: virtual NetworkStack * get_stack(void) = 0; }; + #endif diff --git a/NetworkStack.h b/NetworkStack.h index 2c5b0f9c5a..dc612a624e 100644 --- a/NetworkStack.h +++ b/NetworkStack.h @@ -17,68 +17,8 @@ #ifndef NETWORK_STACK_H #define NETWORK_STACK_H -#include "mbed.h" -#include "SocketAddress.h" - - -/** Enum of standardized error codes - * - * Valid error codes have negative values and may - * be returned by any network operation. - * - * @enum nsapi_error_t - */ -enum nsapi_error_t { - NSAPI_ERROR_WOULD_BLOCK = -3001, /*!< no data is not available but call is non-blocking */ - NSAPI_ERROR_UNSUPPORTED = -3002, /*!< unsupported functionality */ - NSAPI_ERROR_PARAMETER = -3003, /*!< invalid configuration */ - NSAPI_ERROR_NO_CONNECTION = -3004, /*!< not connected to a network */ - NSAPI_ERROR_NO_SOCKET = -3005, /*!< socket not available for use */ - NSAPI_ERROR_NO_ADDRESS = -3006, /*!< IP address is not known */ - NSAPI_ERROR_NO_MEMORY = -3007, /*!< memory resource not available */ - NSAPI_ERROR_DNS_FAILURE = -3008, /*!< DNS failed to complete successfully */ - NSAPI_ERROR_DHCP_FAILURE = -3009, /*!< DHCP failed to complete successfully */ - NSAPI_ERROR_AUTH_FAILURE = -3010, /*!< connection to access point faield */ - NSAPI_ERROR_DEVICE_ERROR = -3011, /*!< failure interfacing with the network procesor */ -}; - -/** Enum of socket protocols - * - * The socket protocol specifies a particular protocol to - * be used with a newly created socket. - * - * @enum nsapi_protocol_t - */ -enum nsapi_protocol_t { - NSAPI_TCP, /*!< Socket is of TCP type */ - NSAPI_UDP, /*!< Socket is of UDP type */ -}; - -/* Enum of standardized stack option levels - * - * @enum nsapi_level_t - */ -enum nsapi_level_t { - NSAPI_STACK, /*!< Stack option level */ - NSAPI_SOCKET, /*!< Socket option level */ -}; - -/* Enum of standardized stack options - * - * These options may not be supported on all stacks, in which - * case NSAPI_ERROR_UNSUPPORTED may be returned from setsockopt. - * - * @enum nsapi_option_t - */ -enum nsapi_option_t { - NSAPI_REUSEADDR, /*!< Allow bind to reuse local addresses */ - NSAPI_KEEPALIVE, /*!< Enables sending of keepalive messages */ - NSAPI_KEEPIDLE, /*!< Sets timeout value to initiate keepalive */ - NSAPI_KEEPINTVL, /*!< Sets timeout value for keepalive */ - NSAPI_LINGER, /*!< Keeps close from returning until queues empty */ - NSAPI_SNDBUF, /*!< Sets send buffer size */ - NSAPI_RCVBUF, /*!< Sets recv buffer size */ -}; +#include "nsapi_types.h" +#include "NetworkSocketAPI/SocketAddress.h" /** NetworkStack class @@ -337,4 +277,5 @@ protected: virtual int getsockopt(void *handle, int level, int optname, void *optval, unsigned *optlen); }; + #endif diff --git a/Socket.h b/Socket.h index 0050cd2dd2..358a4bb874 100644 --- a/Socket.h +++ b/Socket.h @@ -17,9 +17,15 @@ #ifndef SOCKET_H #define SOCKET_H -#include "SocketAddress.h" -#include "NetworkStack.h" -#include "Mutex.h" +#include "NetworkSocketAPI/SocketAddress.h" +#include "NetworkSocketAPI/NetworkStack.h" +#include "rtos/Mutex.h" +#include "Callback.h" + +#ifndef NSAPI_NO_INCLUDE_MBED +#include "mbed.h" // needed for backwards compatability +#endif + /** Abstract socket class */ @@ -147,7 +153,7 @@ public: * * @param func Function to call on state change */ - void attach(Callback func); + void attach(mbed::Callback func); /** Register a callback on state change of the socket * @@ -163,7 +169,7 @@ public: */ template void attach(T *obj, M method) { - attach(Callback(obj, method)); + attach(mbed::Callback(obj, method)); } protected: @@ -174,9 +180,10 @@ protected: NetworkStack *_iface; void *_socket; uint32_t _timeout; - Callback _event; - Callback _callback; + mbed::Callback _event; + mbed::Callback _callback; rtos::Mutex _lock; }; + #endif diff --git a/SocketAddress.h b/SocketAddress.h index f87653e987..049b3a79b8 100644 --- a/SocketAddress.h +++ b/SocketAddress.h @@ -17,51 +17,7 @@ #ifndef SOCKET_ADDRESS_H #define SOCKET_ADDRESS_H -#include - - -/** Maximum size of IP address representation - */ -#define NSAPI_IP_SIZE NSAPI_IPv6_SIZE - -/** Maximum number of bytes for IP address - */ -#define NSAPI_IP_BYTES NSAPI_IPv6_BYTES - -/** Maximum size of MAC address representation - */ -#define NSAPI_MAC_SIZE 18 - -/** Maximum number of bytes for MAC address - */ -#define NSAPI_MAC_BYTES 6 - -/** Enum of IP address versions - * - * The IP version specifies the type of an IP address. - * - * @enum nsapi_version_t - */ -enum nsapi_version_t { - NSAPI_IPv4, /*!< Address is IPv4 */ - NSAPI_IPv6, /*!< Address is IPv6 */ -}; - -/** Size of IPv4 representation - */ -#define NSAPI_IPv4_SIZE 16 - -/** Number of bytes in IPv4 address - */ -#define NSAPI_IPv4_BYTES 4 - -/** Size of IPv6 representation - */ -#define NSAPI_IPv6_SIZE 40 - -/** Number of bytes in IPv6 address - */ -#define NSAPI_IPv6_BYTES 16 +#include "nsapi_types.h" // Predeclared classes class NetworkStack; @@ -178,4 +134,5 @@ private: uint16_t _port; }; + #endif diff --git a/TCPServer.h b/TCPServer.h index f5a7e2c87a..0cbdc6e3cd 100644 --- a/TCPServer.h +++ b/TCPServer.h @@ -17,11 +17,12 @@ #ifndef TCPSERVER_H #define TCPSERVER_H -#include "Socket.h" -#include "TCPSocket.h" -#include "NetworkInterface.h" -#include "NetworkStack.h" -#include "Semaphore.h" +#include "NetworkSocketAPI/Socket.h" +#include "NetworkSocketAPI/TCPSocket.h" +#include "NetworkSocketAPI/NetworkStack.h" +#include "NetworkSocketAPI/NetworkInterface.h" +#include "rtos/Semaphore.h" + /** TCP socket server */ @@ -108,4 +109,5 @@ protected: rtos::Semaphore _accept_sem; }; + #endif diff --git a/TCPSocket.h b/TCPSocket.h index 041aaa7629..b537736b06 100644 --- a/TCPSocket.h +++ b/TCPSocket.h @@ -17,10 +17,11 @@ #ifndef TCPSOCKET_H #define TCPSOCKET_H -#include "Socket.h" -#include "NetworkInterface.h" -#include "NetworkStack.h" -#include "Semaphore.h" +#include "NetworkSocketAPI/Socket.h" +#include "NetworkSocketAPI/NetworkStack.h" +#include "NetworkSocketAPI/NetworkInterface.h" +#include "rtos/Semaphore.h" + /** TCP socket connection */ @@ -139,4 +140,5 @@ protected: friend class TCPServer; }; + #endif diff --git a/UDPSocket.h b/UDPSocket.h index a46a3c0941..235820c5d4 100644 --- a/UDPSocket.h +++ b/UDPSocket.h @@ -17,10 +17,11 @@ #ifndef UDPSOCKET_H #define UDPSOCKET_H -#include "Socket.h" -#include "NetworkInterface.h" -#include "NetworkStack.h" -#include "Semaphore.h" +#include "NetworkSocketAPI/Socket.h" +#include "NetworkSocketAPI/NetworkStack.h" +#include "NetworkSocketAPI/NetworkInterface.h" +#include "rtos/Semaphore.h" + /** UDP socket */ @@ -137,4 +138,5 @@ protected: bool _write_in_progress; }; + #endif diff --git a/WiFiInterface.h b/WiFiInterface.h index f99b411b01..30120e85e8 100644 --- a/WiFiInterface.h +++ b/WiFiInterface.h @@ -17,7 +17,8 @@ #ifndef WIFI_INTERFACE_H #define WIFI_INTERFACE_H -#include "NetworkInterface.h" +#include "NetworkSocketAPI/NetworkInterface.h" + /** Enum of WiFi encryption types * @@ -65,4 +66,5 @@ public: virtual const char *get_mac_address() = 0; }; + #endif diff --git a/nsapi.h b/nsapi.h new file mode 100644 index 0000000000..56dc446037 --- /dev/null +++ b/nsapi.h @@ -0,0 +1,47 @@ +/* nsapi.h - The network socket API + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NSAPI_H +#define NSAPI_H + + +// entry point for nsapi types +#include "nsapi_types.h" + +// disable bug-compatible mbed inclusion +#define NSAPI_NO_INCLUDE_MBED + +#ifdef __cplusplus + +// entry point for C++ api +#include "NetworkSocketAPI/SocketAddress.h" +#include "NetworkSocketAPI/NetworkStack.h" + +#include "NetworkSocketAPI/NetworkInterface.h" +#include "NetworkSocketAPI/EthInterface.h" +#include "NetworkSocketAPI/WiFiInterface.h" +#include "NetworkSocketAPI/CellularInterface.h" +#include "NetworkSocketAPI/MeshInterface.h" + +#include "NetworkSocketAPI/Socket.h" +#include "NetworkSocketAPI/UDPSocket.h" +#include "NetworkSocketAPI/TCPSocket.h" +#include "NetworkSocketAPI/TCPServer.h" + +#endif + + +#endif diff --git a/nsapi_types.h b/nsapi_types.h new file mode 100644 index 0000000000..db647ccfe3 --- /dev/null +++ b/nsapi_types.h @@ -0,0 +1,136 @@ +/* nsapi.h - The network socket API + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NSAPI_TYPES_H +#define NSAPI_TYPES_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/** Enum of standardized error codes + * + * Valid error codes have negative values and may + * be returned by any network operation. + * + * @enum nsapi_error_t + */ +typedef enum nsapi_error { + NSAPI_ERROR_WOULD_BLOCK = -3001, /*!< no data is not available but call is non-blocking */ + NSAPI_ERROR_UNSUPPORTED = -3002, /*!< unsupported functionality */ + NSAPI_ERROR_PARAMETER = -3003, /*!< invalid configuration */ + NSAPI_ERROR_NO_CONNECTION = -3004, /*!< not connected to a network */ + NSAPI_ERROR_NO_SOCKET = -3005, /*!< socket not available for use */ + NSAPI_ERROR_NO_ADDRESS = -3006, /*!< IP address is not known */ + NSAPI_ERROR_NO_MEMORY = -3007, /*!< memory resource not available */ + NSAPI_ERROR_DNS_FAILURE = -3008, /*!< DNS failed to complete successfully */ + NSAPI_ERROR_DHCP_FAILURE = -3009, /*!< DHCP failed to complete successfully */ + NSAPI_ERROR_AUTH_FAILURE = -3010, /*!< connection to access point faield */ + NSAPI_ERROR_DEVICE_ERROR = -3011, /*!< failure interfacing with the network procesor */ +} nsapi_error_t; + + +/** Maximum size of IP address representation + */ +#define NSAPI_IP_SIZE NSAPI_IPv6_SIZE + +/** Maximum number of bytes for IP address + */ +#define NSAPI_IP_BYTES NSAPI_IPv6_BYTES + +/** Maximum size of MAC address representation + */ +#define NSAPI_MAC_SIZE 18 + +/** Maximum number of bytes for MAC address + */ +#define NSAPI_MAC_BYTES 6 + +/** Size of IPv4 representation + */ +#define NSAPI_IPv4_SIZE 16 + +/** Number of bytes in IPv4 address + */ +#define NSAPI_IPv4_BYTES 4 + +/** Size of IPv6 representation + */ +#define NSAPI_IPv6_SIZE 40 + +/** Number of bytes in IPv6 address + */ +#define NSAPI_IPv6_BYTES 16 + +/** Enum of IP address versions + * + * The IP version specifies the type of an IP address. + * + * @enum nsapi_version_t + */ +typedef enum nsapi_version { + NSAPI_IPv4, /*!< Address is IPv4 */ + NSAPI_IPv6, /*!< Address is IPv6 */ +} nsapi_version_t; + + +/** Enum of socket protocols + * + * The socket protocol specifies a particular protocol to + * be used with a newly created socket. + * + * @enum nsapi_protocol_t + */ +typedef enum nsapi_protocol { + NSAPI_TCP, /*!< Socket is of TCP type */ + NSAPI_UDP, /*!< Socket is of UDP type */ +} nsapi_protocol_t; + +/* Enum of standardized stack option levels + * + * @enum nsapi_level_t + */ +typedef enum nsapi_level { + NSAPI_STACK, /*!< Stack option level */ + NSAPI_SOCKET, /*!< Socket option level */ +} nsapi_level_t; + +/* Enum of standardized stack options + * + * These options may not be supported on all stacks, in which + * case NSAPI_ERROR_UNSUPPORTED may be returned from setsockopt. + * + * @enum nsapi_option_t + */ +typedef enum nsapi_option { + NSAPI_REUSEADDR, /*!< Allow bind to reuse local addresses */ + NSAPI_KEEPALIVE, /*!< Enables sending of keepalive messages */ + NSAPI_KEEPIDLE, /*!< Sets timeout value to initiate keepalive */ + NSAPI_KEEPINTVL, /*!< Sets timeout value for keepalive */ + NSAPI_LINGER, /*!< Keeps close from returning until queues empty */ + NSAPI_SNDBUF, /*!< Sets send buffer size */ + NSAPI_RCVBUF, /*!< Sets recv buffer size */ +} nsapi_option_t; + + +#ifdef __cplusplus +} +#endif + +#endif From d985d70bf878caa323aaa5de19f88eb89d4a7696 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 19 Jul 2016 19:00:33 -0500 Subject: [PATCH 35/40] Added primitive nsapi_addr_t type Provides primitive type for network address that is more constrained than "pointer to array of bytes". - POD type avoids issues with constructors - Removes complexity from SocketAddress class - Easily passed through C interfaces - Easily casted to SocketAddress and vice-versa --- SocketAddress.cpp | 92 +++++++++++++++++++++++++---------------------- SocketAddress.h | 32 +++++++++++++---- nsapi_types.h | 14 ++++++++ 3 files changed, 89 insertions(+), 49 deletions(-) diff --git a/SocketAddress.cpp b/SocketAddress.cpp index 31d560f2db..a6ff9a83ec 100644 --- a/SocketAddress.cpp +++ b/SocketAddress.cpp @@ -153,24 +153,31 @@ SocketAddress::SocketAddress(NetworkInterface *iface, const char *host, uint16_t _SocketAddress(iface->get_stack(), host, port); } +SocketAddress::SocketAddress(nsapi_addr_t addr, uint16_t port) +{ + _ip_address[0] = '\0'; + set_addr(addr); + set_port(port); +} + SocketAddress::SocketAddress(const char *addr, uint16_t port) { - memset(&_ip_address, 0, sizeof _ip_address); + _ip_address[0] = '\0'; set_ip_address(addr); set_port(port); } SocketAddress::SocketAddress(const void *bytes, nsapi_version_t version, uint16_t port) { - memset(&_ip_address, 0, sizeof _ip_address); + _ip_address[0] = '\0'; set_ip_bytes(bytes, version); set_port(port); } SocketAddress::SocketAddress(const SocketAddress &addr) { - memset(&_ip_address, 0, sizeof _ip_address); - set_ip_bytes(addr.get_ip_bytes(), addr.get_ip_version()); + _ip_address[0] = '\0'; + set_addr(addr.get_addr()); set_port(addr.get_port()); } @@ -179,31 +186,28 @@ void SocketAddress::set_ip_address(const char *addr) _ip_address[0] = '\0'; if (addr && ipv4_is_valid(addr)) { - _ip_version = NSAPI_IPv4; - ipv4_from_address(_ip_bytes, addr); + _addr.version = NSAPI_IPv4; + ipv4_from_address(_addr.bytes, addr); } else if (addr && ipv6_is_valid(addr)) { - _ip_version = NSAPI_IPv6; - ipv6_from_address(_ip_bytes, addr); + _addr.version = NSAPI_IPv6; + ipv6_from_address(_addr.bytes, addr); } else { - _ip_version = NSAPI_IPv4; - memset(_ip_bytes, 0, NSAPI_IPv4_BYTES); + _addr = (nsapi_addr_t){}; } } void SocketAddress::set_ip_bytes(const void *bytes, nsapi_version_t version) { - _ip_address[0] = '\0'; + nsapi_addr_t addr; + addr.version = version; + memcpy(addr.bytes, bytes, NSAPI_IP_BYTES); + set_addr(addr); +} - if (version == NSAPI_IPv4) { - _ip_version = NSAPI_IPv4; - memcpy(_ip_bytes, bytes, NSAPI_IPv4_BYTES); - } else if (version == NSAPI_IPv6) { - _ip_version = NSAPI_IPv6; - memcpy(_ip_bytes, bytes, NSAPI_IPv6_BYTES); - } else { - _ip_version = NSAPI_IPv4; - memset(_ip_bytes, 0, NSAPI_IPv4_BYTES); - } +void SocketAddress::set_addr(nsapi_addr_t addr) +{ + _ip_address[0] = '\0'; + _addr = addr; } void SocketAddress::set_port(uint16_t port) @@ -216,10 +220,10 @@ const char *SocketAddress::get_ip_address() const char *ip_address = (char *)_ip_address; if (!ip_address[0]) { - if (_ip_version == NSAPI_IPv4) { - ipv4_to_address(ip_address, _ip_bytes); - } else if (_ip_version == NSAPI_IPv6) { - ipv6_to_address(ip_address, _ip_bytes); + if (_addr.version == NSAPI_IPv4) { + ipv4_to_address(ip_address, _addr.bytes); + } else if (_addr.version == NSAPI_IPv6) { + ipv6_to_address(ip_address, _addr.bytes); } } @@ -228,12 +232,17 @@ const char *SocketAddress::get_ip_address() const const void *SocketAddress::get_ip_bytes() const { - return _ip_bytes; + return _addr.bytes; } nsapi_version_t SocketAddress::get_ip_version() const { - return _ip_version; + return _addr.version; +} + +nsapi_addr_t SocketAddress::get_addr() const +{ + return _addr; } uint16_t SocketAddress::get_port() const @@ -244,14 +253,14 @@ uint16_t SocketAddress::get_port() const SocketAddress::operator bool() const { int count = 0; - if (_ip_version == NSAPI_IPv4) { + if (_addr.version == NSAPI_IPv4) { count = NSAPI_IPv4_BYTES; - } else if (_ip_version == NSAPI_IPv6) { + } else if (_addr.version == NSAPI_IPv6) { count = NSAPI_IPv6_BYTES; } for (int i = 0; i < count; i++) { - if (_ip_bytes[i]) { + if (_addr.bytes[i]) { return true; } } @@ -261,26 +270,23 @@ SocketAddress::operator bool() const void SocketAddress::_SocketAddress(NetworkStack *iface, const char *host, uint16_t port) { - memset(&_ip_address, 0, sizeof _ip_address); + _ip_address[0] = '\0'; // Check for valid IP addresses if (host && ipv4_is_valid(host)) { - _ip_version = NSAPI_IPv4; - ipv4_from_address(_ip_bytes, host); - set_port(port); + _addr.version = NSAPI_IPv4; + ipv4_from_address(_addr.bytes, host); + _port = port; } else if (host && ipv6_is_valid(host)) { - _ip_version = NSAPI_IPv6; - ipv6_from_address(_ip_bytes, host); - set_port(port); + _addr.version = NSAPI_IPv6; + ipv6_from_address(_addr.bytes, host); + _port = port; } else { // DNS lookup int err = iface->gethostbyname(this, host); - if (!err) { - set_port(port); - } else { - _ip_version = NSAPI_IPv4; - memset(_ip_bytes, 0, NSAPI_IPv4_BYTES); - set_port(0); + if (err) { + _addr = (nsapi_addr_t){}; + _port = 0; } } } diff --git a/SocketAddress.h b/SocketAddress.h index 049b3a79b8..81d85569cd 100644 --- a/SocketAddress.h +++ b/SocketAddress.h @@ -18,6 +18,7 @@ #define SOCKET_ADDRESS_H #include "nsapi_types.h" +#include "toolchain.h" // Predeclared classes class NetworkStack; @@ -56,14 +57,21 @@ public: */ SocketAddress(NetworkInterface *iface, const char *host, uint16_t port = 0); + /** Create a SocketAddress from a raw IP address and port + * + * @param addr Raw IP address + * @param port Optional 16-bit port + */ + SocketAddress(nsapi_addr_t addr = (nsapi_addr_t){}, uint16_t port = 0); + /** Create a SocketAddress from an IP address and port * * @param host Null-terminated representation of the IP address * @param port Optional 16-bit port */ - SocketAddress(const char *addr = 0, uint16_t port = 0); + SocketAddress(const char *addr, uint16_t port = 0); - /** Create a SocketAddress from a raw IP address and port + /** Create a SocketAddress from raw IP bytes, IP version, and port * * @param bytes Raw IP address in big-endian order * @param version IP address version, NSAPI_IPv4 or NSAPI_IPv6 @@ -83,13 +91,19 @@ public: */ void set_ip_address(const char *addr); - /** Set the raw IP address + /** Set the raw IP bytes and IP version * * @param bytes Raw IP address in big-endian order * @param version IP address version, NSAPI_IPv4 or NSAPI_IPv6 */ void set_ip_bytes(const void *bytes, nsapi_version_t version); + /** Set the raw IP address + * + * @param addr Raw IP address + */ + void set_addr(nsapi_addr_t addr); + /** Set the port * * @param port 16-bit port @@ -102,7 +116,7 @@ public: */ const char *get_ip_address() const; - /** Get the raw IP address + /* Get the raw IP bytes * * @return Raw IP address in big-endian order */ @@ -113,6 +127,12 @@ public: * @return IP address version, NSAPI_IPv4 or NSAPI_IPv6 */ nsapi_version_t get_ip_version() const; + + /** Get the raw IP address + * + * @return Raw IP address + */ + nsapi_addr_t get_addr() const; /** Get the port * @@ -128,9 +148,9 @@ public: private: void _SocketAddress(NetworkStack *iface, const char *host, uint16_t port); + char _ip_address[NSAPI_IP_SIZE]; - uint8_t _ip_bytes[NSAPI_IP_BYTES]; - nsapi_version_t _ip_version; + nsapi_addr_t _addr; uint16_t _port; }; diff --git a/nsapi_types.h b/nsapi_types.h index db647ccfe3..999e430c76 100644 --- a/nsapi_types.h +++ b/nsapi_types.h @@ -89,6 +89,20 @@ typedef enum nsapi_version { NSAPI_IPv6, /*!< Address is IPv6 */ } nsapi_version_t; +/** IP address structure for passing IP addresses by value + */ +typedef struct nsapi_addr { + /** IP version + * NSAPI_IPv4 or NSAPI_IPv6 + */ + nsapi_version_t version; + + /** IP address + * The raw bytes of the IP address stored in big-endian format + */ + uint8_t bytes[NSAPI_IP_BYTES]; +} nsapi_addr_t; + /** Enum of socket protocols * From 7ab9f4c267f806b1fc70e8181539590ec892fd49 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 19 Jul 2016 20:59:12 -0500 Subject: [PATCH 36/40] Added opaque nsapi_socket_t type typedef void *nsapi_socket_t Helps avoid confusion with multiple void*s floating around, especially when passing a nsapi_socket_t by pointer as a destination. The nsapi_socket_t is just an opaque handle, implementations still need a cast to obtain implementation specific socket pointer. --- NetworkStack.h | 26 +++++++++++++------------- Socket.h | 2 +- nsapi_types.h | 5 +++++ 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/NetworkStack.h b/NetworkStack.h index dc612a624e..015ff53089 100644 --- a/NetworkStack.h +++ b/NetworkStack.h @@ -100,7 +100,7 @@ protected: * @param proto Protocol of socket to open, NSAPI_TCP or NSAPI_UDP * @return 0 on success, negative error code on failure */ - virtual int socket_open(void **handle, nsapi_protocol_t proto) = 0; + virtual int socket_open(nsapi_socket_t *handle, nsapi_protocol_t proto) = 0; /** Close the socket * @@ -110,7 +110,7 @@ protected: * @param handle Socket handle * @return 0 on success, negative error code on failure */ - virtual int socket_close(void *handle) = 0; + virtual int socket_close(nsapi_socket_t handle) = 0; /** Bind a specific address to a socket * @@ -121,7 +121,7 @@ protected: * @param address Local address to bind * @return 0 on success, negative error code on failure. */ - virtual int socket_bind(void *handle, const SocketAddress &address) = 0; + virtual int socket_bind(nsapi_socket_t handle, const SocketAddress &address) = 0; /** Listen for connections on a TCP socket * @@ -133,7 +133,7 @@ protected: * simultaneously * @return 0 on success, negative error code on failure */ - virtual int socket_listen(void *handle, int backlog) = 0; + virtual int socket_listen(nsapi_socket_t handle, int backlog) = 0; /** Connects TCP socket to a remote host * @@ -144,7 +144,7 @@ protected: * @param address The SocketAddress of the remote host * @return 0 on success, negative error code on failure */ - virtual int socket_connect(void *handle, const SocketAddress &address) = 0; + virtual int socket_connect(nsapi_socket_t handle, const SocketAddress &address) = 0; /** Accepts a connection on a TCP socket * @@ -163,7 +163,7 @@ protected: * @param server Socket handle to server to accept from * @return 0 on success, negative error code on failure */ - virtual int socket_accept(void **handle, void *server) = 0; + virtual int socket_accept(nsapi_socket_t *handle, nsapi_socket_t server) = 0; /** Send data over a TCP socket * @@ -179,7 +179,7 @@ protected: * @return Number of sent bytes on success, negative error * code on failure */ - virtual int socket_send(void *handle, const void *data, unsigned size) = 0; + virtual int socket_send(nsapi_socket_t handle, const void *data, unsigned size) = 0; /** Receive data over a TCP socket * @@ -195,7 +195,7 @@ protected: * @return Number of received bytes on success, negative error * code on failure */ - virtual int socket_recv(void *handle, void *data, unsigned size) = 0; + virtual int socket_recv(nsapi_socket_t handle, void *data, unsigned size) = 0; /** Send a packet over a UDP socket * @@ -212,7 +212,7 @@ protected: * @return Number of sent bytes on success, negative error * code on failure */ - virtual int socket_sendto(void *handle, const SocketAddress &address, const void *data, unsigned size) = 0; + virtual int socket_sendto(nsapi_socket_t handle, const SocketAddress &address, const void *data, unsigned size) = 0; /** Receive a packet over a UDP socket * @@ -229,7 +229,7 @@ protected: * @return Number of received bytes on success, negative error * code on failure */ - virtual int socket_recvfrom(void *handle, SocketAddress *address, void *buffer, unsigned size) = 0; + virtual int socket_recvfrom(nsapi_socket_t handle, SocketAddress *address, void *buffer, unsigned size) = 0; /** Register a callback on state change of the socket * @@ -244,7 +244,7 @@ protected: * @param callback Function to call on state change * @param data Argument to pass to callback */ - virtual void socket_attach(void *handle, void (*callback)(void *), void *data) = 0; + virtual void socket_attach(nsapi_socket_t handle, void (*callback)(void *), void *data) = 0; /* Set stack-specific socket options * @@ -259,7 +259,7 @@ protected: * @param optlen Length of the option value * @return 0 on success, negative error code on failure */ - virtual int setsockopt(void *handle, int level, int optname, const void *optval, unsigned optlen); + virtual int setsockopt(nsapi_socket_t handle, int level, int optname, const void *optval, unsigned optlen); /* Get stack-specific socket options * @@ -274,7 +274,7 @@ protected: * @param optlen Length of the option value * @return 0 on success, negative error code on failure */ - virtual int getsockopt(void *handle, int level, int optname, void *optval, unsigned *optlen); + virtual int getsockopt(nsapi_socket_t handle, int level, int optname, void *optval, unsigned *optlen); }; diff --git a/Socket.h b/Socket.h index 358a4bb874..22d2c83377 100644 --- a/Socket.h +++ b/Socket.h @@ -178,7 +178,7 @@ protected: virtual void event() = 0; NetworkStack *_iface; - void *_socket; + nsapi_socket_t _socket; uint32_t _timeout; mbed::Callback _event; mbed::Callback _callback; diff --git a/nsapi_types.h b/nsapi_types.h index 999e430c76..d159af266a 100644 --- a/nsapi_types.h +++ b/nsapi_types.h @@ -104,6 +104,11 @@ typedef struct nsapi_addr { } nsapi_addr_t; +/** Opaque handle for network sockets + */ +typedef void *nsapi_socket_t; + + /** Enum of socket protocols * * The socket protocol specifies a particular protocol to From 5b63cbbd3e0454376273c4653666ee389c9b6369 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 19 Jul 2016 21:25:28 -0500 Subject: [PATCH 37/40] Added primitive nsapi_stack_t and nsapi_stack_api_t structures Provides a primitive structure for explicitly instantiating network stack structures/vtables. - Avoids reoccuring issue with non-gced vtables - Provides thick api layer for additional nsapi features - Provides more explicit seperation between implementation and user api Now implementors have two options: 1. Extend NetworkStack and implement all of the abstract member functions 2. Fill out a nsapi_stack_api_t and provide a NetworkStack with nsapi_create_stack option 1 provides an easy route for porting single drivers such as the esp8266 or the c027 and can easily benefit from other C++ classes. option 2 provides an easy route for porting full network-stacks such as lwip or nanostack, which provide their own host of networking utilities and only need a minimal socket interface. --- NetworkStack.cpp | 201 ++++++++++++++++++++++++++++++++ NetworkStack.h | 8 ++ nsapi_types.h | 290 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 499 insertions(+) diff --git a/NetworkStack.cpp b/NetworkStack.cpp index 1b70dd6551..b6cc903ef7 100644 --- a/NetworkStack.cpp +++ b/NetworkStack.cpp @@ -14,9 +14,14 @@ * limitations under the License. */ +#include "NetworkStack.h" #include "DnsQuery.h" #include "mbed.h" +#include "stddef.h" +#include + +// Default NetworkStack operations int NetworkStack::gethostbyname(SocketAddress *address, const char *name) { char buffer[NSAPI_IP_SIZE]; @@ -48,3 +53,199 @@ int NetworkStack::getsockopt(void *handle, int level, int optname, void *optval, { return NSAPI_ERROR_UNSUPPORTED; } + + +// NetworkStackWrapper class for encapsulating the raw nsapi_stack structure +class NetworkStackWrapper : public NetworkStack +{ +private: + inline nsapi_stack_t *_stack() + { + return reinterpret_cast( + reinterpret_cast(this) + - offsetof(nsapi_stack_t, _stack_buffer)); + } + + inline const nsapi_stack_api_t *_stack_api() + { + return _stack()->stack_api; + } + +public: + virtual const char *get_ip_address() + { + if (!_stack_api()->get_ip_address) { + return 0; + } + + static uint8_t buffer[sizeof(SocketAddress)]; + SocketAddress *address = new (buffer) SocketAddress(_stack_api()->get_ip_address(_stack())); + return address->get_ip_address(); + } + + virtual int gethostbyname(SocketAddress *address, const char *name) + { + if (!_stack_api()->gethostbyname) { + return NetworkStack::gethostbyname(address, name); + } + + nsapi_addr_t addr = {NSAPI_IPv4, 0}; + int err = _stack_api()->gethostbyname(_stack(), &addr, name); + address->set_addr(addr); + return err; + } + + virtual int setstackopt(int level, int optname, const void *optval, unsigned optlen) + { + if (!_stack_api()->setstackopt) { + return NSAPI_ERROR_UNSUPPORTED; + } + + return _stack_api()->setstackopt(_stack(), level, optname, optval, optlen); + } + + virtual int getstackopt(int level, int optname, void *optval, unsigned *optlen) + { + if (!_stack_api()->getstackopt) { + return NSAPI_ERROR_UNSUPPORTED; + } + + return _stack_api()->getstackopt(_stack(), level, optname, optval, optlen); + } + +protected: + virtual int socket_open(nsapi_socket_t *socket, nsapi_protocol_t proto) + { + if (!_stack_api()->socket_open) { + return NSAPI_ERROR_UNSUPPORTED; + } + + return _stack_api()->socket_open(_stack(), socket, proto); + } + + virtual int socket_close(nsapi_socket_t socket) + { + if (!_stack_api()->socket_close) { + return NSAPI_ERROR_UNSUPPORTED; + } + + return _stack_api()->socket_close(_stack(), socket); + } + + virtual int socket_bind(nsapi_socket_t socket, const SocketAddress &address) + { + if (!_stack_api()->socket_bind) { + return NSAPI_ERROR_UNSUPPORTED; + } + + return _stack_api()->socket_bind(_stack(), socket, address.get_addr(), address.get_port()); + } + + virtual int socket_listen(nsapi_socket_t socket, int backlog) + { + if (!_stack_api()->socket_listen) { + return NSAPI_ERROR_UNSUPPORTED; + } + + return _stack_api()->socket_listen(_stack(), socket, backlog); + } + + virtual int socket_connect(nsapi_socket_t socket, const SocketAddress &address) + { + if (!_stack_api()->socket_connect) { + return NSAPI_ERROR_UNSUPPORTED; + } + + return _stack_api()->socket_connect(_stack(), socket, address.get_addr(), address.get_port()); + } + + virtual int socket_accept(nsapi_socket_t *socket, nsapi_socket_t server) + { + if (!_stack_api()->socket_accept) { + return NSAPI_ERROR_UNSUPPORTED; + } + + return _stack_api()->socket_accept(_stack(), socket, server); + } + + virtual int socket_send(nsapi_socket_t socket, const void *data, unsigned size) + { + if (!_stack_api()->socket_send) { + return NSAPI_ERROR_UNSUPPORTED; + } + + return _stack_api()->socket_send(_stack(), socket, data, size); + } + + virtual int socket_recv(nsapi_socket_t socket, void *data, unsigned size) + { + if (!_stack_api()->socket_recv) { + return NSAPI_ERROR_UNSUPPORTED; + } + + return _stack_api()->socket_recv(_stack(), socket, data, size); + } + + virtual int socket_sendto(nsapi_socket_t socket, const SocketAddress &address, const void *data, unsigned size) + { + if (!_stack_api()->socket_sendto) { + return NSAPI_ERROR_UNSUPPORTED; + } + + return _stack_api()->socket_sendto(_stack(), socket, address.get_addr(), address.get_port(), data, size); + } + + virtual int socket_recvfrom(nsapi_socket_t socket, SocketAddress *address, void *data, unsigned size) + { + if (!_stack_api()->socket_recvfrom) { + return NSAPI_ERROR_UNSUPPORTED; + } + + nsapi_addr_t addr = {NSAPI_IPv4, 0}; + uint16_t port = 0; + + int err = _stack_api()->socket_recvfrom(_stack(), socket, &addr, &port, data, size); + + if (address) { + address->set_addr(addr); + address->set_port(port); + } + + return err; + } + + virtual void socket_attach(nsapi_socket_t socket, void (*callback)(void *), void *data) + { + if (!_stack_api()->socket_attach) { + return; + } + + return _stack_api()->socket_attach(_stack(), socket, callback, data); + } + + virtual int setsockopt(nsapi_socket_t socket, int level, int optname, const void *optval, unsigned optlen) + { + if (!_stack_api()->setsockopt) { + return NSAPI_ERROR_UNSUPPORTED; + } + + return _stack_api()->setsockopt(_stack(), socket, level, optname, optval, optlen); + } + + virtual int getsockopt(nsapi_socket_t socket, int level, int optname, void *optval, unsigned *optlen) + { + if (!_stack_api()->getsockopt) { + return NSAPI_ERROR_UNSUPPORTED; + } + + return _stack_api()->getsockopt(_stack(), socket, level, optname, optval, optlen); + } +}; + + +// Conversion function for network stacks +NetworkStack *nsapi_create_stack(nsapi_stack_t *stack) +{ + MBED_ASSERT(sizeof stack->_stack_buffer >= sizeof(NetworkStackWrapper)); + return new (stack->_stack_buffer) NetworkStackWrapper; +} diff --git a/NetworkStack.h b/NetworkStack.h index 015ff53089..2920f8abc2 100644 --- a/NetworkStack.h +++ b/NetworkStack.h @@ -278,4 +278,12 @@ protected: }; +/** Convert a raw nsapi_stack_t object into a C++ NetworkStack object + * + * @param stack Reference to a raw nsapi_stack_t object + * @return Reference to the underlying network stack + */ +NetworkStack *nsapi_create_stack(nsapi_stack_t *stack); + + #endif diff --git a/nsapi_types.h b/nsapi_types.h index d159af266a..176e42f35c 100644 --- a/nsapi_types.h +++ b/nsapi_types.h @@ -148,6 +148,296 @@ typedef enum nsapi_option { } nsapi_option_t; +/** nsapi_stack structure + * + * Stack structure representing a specific instance of a stack. + */ +typedef struct nsapi_stack { + /** Network stack operation table + * + * Provides access to the underlying api of the stack. This is not + * flattened into the nsapi_stack to allow allocation in read-only + * memory. + */ + const struct nsapi_stack_api *stack_api; + + /** Opaque handle for network stacks + */ + void *stack; + + // Internal nsapi buffer + unsigned _stack_buffer[16]; +} nsapi_stack_t; + +/** nsapi_stack_api structure + * + * Common api structure for network stack operations. A network stack + * can provide a nsapi_stack_api structure filled out with the + * appropriate implementation. + * + * Unsupported operations can be left as null pointers. + */ +typedef struct nsapi_stack_api +{ + /** Get the local IP address + * + * @param stack Stack handle + * @return Local IP Address or null address if not connected + */ + nsapi_addr_t (*get_ip_address)(nsapi_stack_t *stack); + + /** Translates a hostname to an IP address + * + * The hostname may be either a domain name or an IP address. If the + * hostname is an IP address, no network transactions will be performed. + * + * If no stack-specific DNS resolution is provided, the hostname + * will be resolve using a UDP socket on the stack. + * + * @param stack Stack handle + * @param addr Destination for the host IP address + * @param host Hostname to resolve + * @return 0 on success, negative error code on failure + */ + int (*gethostbyname)(nsapi_stack_t *stack, nsapi_addr_t *addr, const char *host); + + /* Set stack-specific stack options + * + * The setstackopt allow an application to pass stack-specific hints + * to the underlying stack. For unsupported options, + * NSAPI_ERROR_UNSUPPORTED is returned and the stack is unmodified. + * + * @param stack Stack handle + * @param level Stack-specific protocol level + * @param optname Stack-specific option identifier + * @param optval Option value + * @param optlen Length of the option value + * @return 0 on success, negative error code on failure + */ + int (*setstackopt)(nsapi_stack_t *stack, int level, int optname, const void *optval, unsigned optlen); + + /* Get stack-specific stack options + * + * The getstackopt allow an application to retrieve stack-specific hints + * from the underlying stack. For unsupported options, + * NSAPI_ERROR_UNSUPPORTED is returned and optval is unmodified. + * + * @param stack Stack handle + * @param level Stack-specific protocol level + * @param optname Stack-specific option identifier + * @param optval Destination for option value + * @param optlen Length of the option value + * @return 0 on success, negative error code on failure + */ + int (*getstackopt)(nsapi_stack_t *stack, int level, int optname, void *optval, unsigned *optlen); + + /** Opens a socket + * + * Creates a network socket and stores it in the specified handle. + * The handle must be passed to following calls on the socket. + * + * A stack may have a finite number of sockets, in this case + * NSAPI_ERROR_NO_SOCKET is returned if no socket is available. + * + * @param stack Stack context + * @param socket Destination for the handle to a newly created socket + * @param proto Protocol of socket to open, NSAPI_TCP or NSAPI_UDP + * @return 0 on success, negative error code on failure + */ + int (*socket_open)(nsapi_stack_t *stack, nsapi_socket_t *socket, nsapi_protocol_t proto); + + /** Close the socket + * + * Closes any open connection and deallocates any memory associated + * with the socket. + * + * @param stack Stack handle + * @param socket Socket handle + * @return 0 on success, negative error code on failure + */ + int (*socket_close)(nsapi_stack_t *stack, nsapi_socket_t socket); + + /** Bind a specific address to a socket + * + * Binding a socket specifies the address and port on which to recieve + * data. If the IP address is zeroed, only the port is bound. + * + * @param stack Stack handle + * @param socket Socket handle + * @param addr Local address to bind, may be null + * @param port Local port to bind + * @return 0 on success, negative error code on failure. + */ + int (*socket_bind)(nsapi_stack_t *stack, nsapi_socket_t socket, nsapi_addr_t addr, uint16_t port); + + /** Listen for connections on a TCP socket + * + * Marks the socket as a passive socket that can be used to accept + * incoming connections. + * + * @param stack Stack handle + * @param socket Socket handle + * @param backlog Number of pending connections that can be queued + * simultaneously + * @return 0 on success, negative error code on failure + */ + int (*socket_listen)(nsapi_stack_t *stack, nsapi_socket_t socket, int backlog); + + /** Connects TCP socket to a remote host + * + * Initiates a connection to a remote server specified by the + * indicated address. + * + * @param stack Stack handle + * @param socket Socket handle + * @param addr The address of the remote host + * @param port The port of the remote host + * @return 0 on success, negative error code on failure + */ + int (*socket_connect)(nsapi_stack_t *stack, nsapi_socket_t socket, nsapi_addr_t addr, uint16_t port); + + /** Accepts a connection on a TCP socket + * + * The server socket must be bound and set to listen for connections. + * On a new connection, creates a network socket and stores it in the + * specified handle. The handle must be passed to following calls on + * the socket. + * + * A stack may have a finite number of sockets, in this case + * NSAPI_ERROR_NO_SOCKET is returned if no socket is available. + * + * This call is non-blocking. If accept would block, + * NSAPI_ERROR_WOULD_BLOCK is returned immediately. + * + * @param stack Stack handle + * @param socket Destination for a handle to the newly created sockey + * @param server Socket handle to server to accept from + * @return 0 on success, negative error code on failure + */ + int (*socket_accept)(nsapi_stack_t *stack, nsapi_socket_t *socket, nsapi_socket_t server); + + /** Send data over a TCP socket + * + * The socket must be connected to a remote host. Returns the number of + * bytes sent from the buffer. + * + * This call is non-blocking. If send would block, + * NSAPI_ERROR_WOULD_BLOCK is returned immediately. + * + * @param stack Stack handle + * @param socket Socket handle + * @param data Buffer of data to send to the host + * @param size Size of the buffer in bytes + * @return Number of sent bytes on success, negative error + * code on failure + */ + int (*socket_send)(nsapi_stack_t *stack, nsapi_socket_t socket, const void *data, unsigned size); + + /** Receive data over a TCP socket + * + * The socket must be connected to a remote host. Returns the number of + * bytes received into the buffer. + * + * This call is non-blocking. If recv would block, + * NSAPI_ERROR_WOULD_BLOCK is returned immediately. + * + * @param stack Stack handle + * @param socket Socket handle + * @param data Destination buffer for data received from the host + * @param size Size of the buffer in bytes + * @return Number of received bytes on success, negative error + * code on failure + */ + int (*socket_recv)(nsapi_stack_t *stack, nsapi_socket_t socket, void *data, unsigned size); + + /** Send a packet over a UDP socket + * + * Sends data to the specified address. Returns the number of bytes + * sent from the buffer. + * + * This call is non-blocking. If sendto would block, + * NSAPI_ERROR_WOULD_BLOCK is returned immediately. + * + * @param stack Stack handle + * @param socket Socket handle + * @param addr The address of the remote host + * @param port The port of the remote host + * @param data Buffer of data to send to the host + * @param size Size of the buffer in bytes + * @return Number of sent bytes on success, negative error + * code on failure + */ + int (*socket_sendto)(nsapi_stack_t *stack, nsapi_socket_t socket, nsapi_addr_t addr, uint16_t port, const void *data, unsigned size); + + /** Receive a packet over a UDP socket + * + * Receives data and stores the source address in address if address + * is not NULL. Returns the number of bytes received into the buffer. + * + * This call is non-blocking. If recvfrom would block, + * NSAPI_ERROR_WOULD_BLOCK is returned immediately. + * + * @param stack Stack handle + * @param socket Socket handle + * @param addr Destination for the address of the remote host + * @param port Destination for the port of the remote host + * @param data Destination buffer for data received from the host + * @param size Size of the buffer in bytes + * @return Number of received bytes on success, negative error + * code on failure + */ + int (*socket_recvfrom)(nsapi_stack_t *stack, nsapi_socket_t socket, nsapi_addr_t *addr, uint16_t *port, void *buffer, unsigned size); + + /** Register a callback on state change of the socket + * + * The specified callback will be called on state changes such as when + * the socket can recv/send/accept successfully and on when an error + * occurs. The callback may also be called spuriously without reason. + * + * The callback may be called in an interrupt context and should not + * perform expensive operations such as recv/send calls. + * + * @param stack Stack handle + * @param socket Socket handle + * @param callback Function to call on state change + * @param data Argument to pass to callback + */ + void (*socket_attach)(nsapi_stack_t *stack, nsapi_socket_t socket, void (*callback)(void *), void *data); + + /* Set stack-specific socket options + * + * The setsockopt allow an application to pass stack-specific hints + * to the underlying stack. For unsupported options, + * NSAPI_ERROR_UNSUPPORTED is returned and the socket is unmodified. + * + * @param stack Stack handle + * @param socket Socket handle + * @param level Stack-specific protocol level + * @param optname Stack-specific option identifier + * @param optval Option value + * @param optlen Length of the option value + * @return 0 on success, negative error code on failure + */ + int (*setsockopt)(nsapi_stack_t *stack, nsapi_socket_t socket, int level, int optname, const void *optval, unsigned optlen); + + /* Get stack-specific socket options + * + * The getstackopt allow an application to retrieve stack-specific hints + * from the underlying stack. For unsupported options, + * NSAPI_ERROR_UNSUPPORTED is returned and optval is unmodified. + * + * @param stack Stack handle + * @param socket Socket handle + * @param level Stack-specific protocol level + * @param optname Stack-specific option identifier + * @param optval Destination for option value + * @param optlen Length of the option value + * @return 0 on success, negative error code on failure + */ + int (*getsockopt)(nsapi_stack_t *stack, nsapi_socket_t socket, int level, int optname, void *optval, unsigned *optlen); +} nsapi_stack_api_t; + + #ifdef __cplusplus } #endif From 01f38cc940a9b237b20f202b3e6d4f5ae7fadc74 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 19 Jul 2016 21:50:24 -0500 Subject: [PATCH 38/40] Added better support for NetworkInterface/NetworkStack Mostly reduced code duplication for NetworkInterface/NetworkStack arguments. Moved to templated Socket constructors/open member functions. This allows multiple inheritance to be used for classes that provide both a NetworkInterface and NetworkStack, such as the esp8266. --- Socket.cpp | 46 +++++++++++++++++++----------------- Socket.h | 18 +++++++++----- SocketAddress.cpp | 5 ---- SocketAddress.h | 21 ++++++----------- TCPServer.cpp | 38 ++++++++++++------------------ TCPServer.h | 53 +++++++++++++++++++---------------------- TCPSocket.cpp | 60 +++++++++++++++++------------------------------ TCPSocket.h | 49 ++++++++++++++++++-------------------- UDPSocket.cpp | 38 ++++++++++-------------------- UDPSocket.h | 47 +++++++++++++++++-------------------- 10 files changed, 163 insertions(+), 212 deletions(-) diff --git a/Socket.cpp b/Socket.cpp index 49b92f4d13..70d393bc59 100644 --- a/Socket.cpp +++ b/Socket.cpp @@ -17,24 +17,24 @@ #include "Socket.h" Socket::Socket() - : _iface(0) + : _stack(0) , _socket(0) , _timeout(osWaitForever) { } -int Socket::open(NetworkStack *iface, nsapi_protocol_t proto) +int Socket::open(NetworkStack *stack, nsapi_protocol_t proto) { _lock.lock(); - if (_iface != NULL) { + if (_stack != NULL || stack == NULL) { _lock.unlock(); return NSAPI_ERROR_PARAMETER; } - _iface = iface; + _stack = stack; - void *socket; - int err = _iface->socket_open(&socket, proto); + nsapi_socket_t socket; + int err = _stack->socket_open(&socket, proto); if (err) { _lock.unlock(); return err; @@ -42,10 +42,9 @@ int Socket::open(NetworkStack *iface, nsapi_protocol_t proto) _socket = socket; _event.attach(this, &Socket::event); - _iface->socket_attach(_socket, Callback::thunk, &_event); + _stack->socket_attach(_socket, Callback::thunk, &_event); _lock.unlock(); - return 0; } @@ -55,11 +54,10 @@ int Socket::close() int ret = 0; if (_socket) { - _iface->socket_attach(_socket, 0, 0); - - void * socket = _socket; + _stack->socket_attach(_socket, 0, 0); + nsapi_socket_t socket = _socket; _socket = 0; - ret = _iface->socket_close(socket); + ret = _stack->socket_close(socket); } // Wakeup anything in a blocking operation @@ -87,10 +85,12 @@ int Socket::bind(const char *address, uint16_t port) int Socket::bind(const SocketAddress &address) { _lock.lock(); + int ret; - int ret = NSAPI_ERROR_NO_SOCKET; - if (_socket) { - ret = _iface->socket_bind(_socket, address); + if (!_socket) { + ret = NSAPI_ERROR_NO_SOCKET; + } else { + ret = _stack->socket_bind(_socket, address); } _lock.unlock(); @@ -119,10 +119,12 @@ void Socket::set_timeout(int timeout) int Socket::setsockopt(int level, int optname, const void *optval, unsigned optlen) { _lock.lock(); + int ret; - int ret = NSAPI_ERROR_NO_SOCKET; - if (_socket) { - ret = _iface->setsockopt(_socket, level, optname, optval, optlen); + if (!_socket) { + ret = NSAPI_ERROR_NO_SOCKET; + } else { + ret = _stack->setsockopt(_socket, level, optname, optval, optlen); } _lock.unlock(); @@ -132,10 +134,12 @@ int Socket::setsockopt(int level, int optname, const void *optval, unsigned optl int Socket::getsockopt(int level, int optname, void *optval, unsigned *optlen) { _lock.lock(); + int ret; - int ret = NSAPI_ERROR_NO_SOCKET; - if (_socket) { - ret = _iface->getsockopt(_socket, level, optname, optval, optlen); + if (!_socket) { + ret = NSAPI_ERROR_NO_SOCKET; + } else { + ret = _stack->getsockopt(_socket, level, optname, optval, optlen); } _lock.unlock(); diff --git a/Socket.h b/Socket.h index 22d2c83377..a3209c82ca 100644 --- a/Socket.h +++ b/Socket.h @@ -39,13 +39,19 @@ public: /** Opens a socket * - * Creates a network socket on the specified network stack. - * Not needed if stack is passed to the socket's constructor. + * Creates a network socket on the network stack of the given + * network interface. Not needed if stack is passed to the + * socket's constructor. * - * @param iface Network stack as target for socket + * @param stack Network stack as target for socket * @return 0 on success, negative error code on failure */ - virtual int open(NetworkStack *iface) = 0; + virtual int open(NetworkStack *stack) = 0; + + template + int open(IF *iface) { + return open(iface->get_stack()); + } /** Close the socket * @@ -174,10 +180,10 @@ public: protected: Socket(); - int open(NetworkStack *iface, nsapi_protocol_t proto); + int open(NetworkStack *stack, nsapi_protocol_t proto); virtual void event() = 0; - NetworkStack *_iface; + NetworkStack *_stack; nsapi_socket_t _socket; uint32_t _timeout; mbed::Callback _event; diff --git a/SocketAddress.cpp b/SocketAddress.cpp index a6ff9a83ec..8c16518a8f 100644 --- a/SocketAddress.cpp +++ b/SocketAddress.cpp @@ -148,11 +148,6 @@ SocketAddress::SocketAddress(NetworkStack *iface, const char *host, uint16_t por _SocketAddress(iface, host, port); } -SocketAddress::SocketAddress(NetworkInterface *iface, const char *host, uint16_t port) -{ - _SocketAddress(iface->get_stack(), host, port); -} - SocketAddress::SocketAddress(nsapi_addr_t addr, uint16_t port) { _ip_address[0] = '\0'; diff --git a/SocketAddress.h b/SocketAddress.h index 81d85569cd..64bc80a998 100644 --- a/SocketAddress.h +++ b/SocketAddress.h @@ -31,19 +31,6 @@ class NetworkInterface; */ class SocketAddress { public: - /** Create a SocketAddress from a hostname and port - * - * The hostname may be either a domain name or an IP address. If the - * hostname is an IP address, no network transactions will be performed. - * - * On failure, the IP address and port will be set to zero - * - * @param iface Network stack to use for DNS resolution - * @param host Hostname to resolve - * @param port Optional 16-bit port - */ - SocketAddress(NetworkStack *iface, const char *host, uint16_t port = 0); - /** Create a SocketAddress from a hostname and port * * The hostname may be either a domain name or an IP address. If the @@ -55,7 +42,13 @@ public: * @param host Hostname to resolve * @param port Optional 16-bit port */ - SocketAddress(NetworkInterface *iface, const char *host, uint16_t port = 0); + SocketAddress(NetworkStack *iface, const char *host, uint16_t port = 0); + + template + SocketAddress(IF *iface, const char *host, uint16_t port = 0) + { + _SocketAddress(iface->get_stack(), host, port); + } /** Create a SocketAddress from a raw IP address and port * diff --git a/TCPServer.cpp b/TCPServer.cpp index cf75096e3b..6598ff9e4a 100644 --- a/TCPServer.cpp +++ b/TCPServer.cpp @@ -22,16 +22,10 @@ TCPServer::TCPServer() { } -TCPServer::TCPServer(NetworkStack *iface) +TCPServer::TCPServer(NetworkStack *stack) : _pending(0), _accept_sem(0) { - open(iface); -} - -TCPServer::TCPServer(NetworkInterface *iface) - : _pending(0), _accept_sem(0) -{ - open(iface->get_stack()); + open(stack); } TCPServer::~TCPServer() @@ -39,23 +33,20 @@ TCPServer::~TCPServer() close(); } -int TCPServer::open(NetworkStack *iface) +int TCPServer::open(NetworkStack *stack) { - return Socket::open(iface, NSAPI_TCP); -} - -int TCPServer::open(NetworkInterface *iface) -{ - return TCPServer::open(iface->get_stack()); + return Socket::open(stack, NSAPI_TCP); } int TCPServer::listen(int backlog) { _lock.lock(); + int ret; - int ret = NSAPI_ERROR_NO_SOCKET; - if (_socket) { - ret = _iface->socket_listen(_socket, backlog); + if (!_socket) { + ret = NSAPI_ERROR_NO_SOCKET; + } else { + ret = _stack->socket_listen(_socket, backlog); } _lock.unlock(); @@ -65,17 +56,18 @@ int TCPServer::listen(int backlog) int TCPServer::accept(TCPSocket *connection) { _lock.lock(); + int ret; - int ret = NSAPI_ERROR_NO_SOCKET; while (true) { if (!_socket) { ret = NSAPI_ERROR_NO_SOCKET; break; - } + } _pending = 0; void *socket; - ret = _iface->socket_accept(&socket, _socket); + ret = _stack->socket_accept(&socket, _socket); + if (0 == ret) { connection->_lock.lock(); @@ -83,10 +75,10 @@ int TCPServer::accept(TCPSocket *connection) connection->close(); } - connection->_iface = _iface; + connection->_stack = _stack; connection->_socket = socket; connection->_event = Callback(connection, &TCPSocket::event); - _iface->socket_attach(socket, &Callback::thunk, &connection->_event); + _stack->socket_attach(socket, &Callback::thunk, &connection->_event); connection->_lock.unlock(); break; diff --git a/TCPServer.h b/TCPServer.h index 0cbdc6e3cd..4bca0ca5f3 100644 --- a/TCPServer.h +++ b/TCPServer.h @@ -34,49 +34,43 @@ public: */ TCPServer(); + /** Create a socket on a network interface + * + * Creates and opens a socket on the network stack of the given + * network interface. + * + * @param stack Network stack as target for socket + */ + TCPServer(NetworkStack *stack); + + template + TCPServer(IF *iface) + : _pending(0), _accept_sem(0) + { + open(iface->get_stack()); + } + /** Destroy a socket * * Closes socket if the socket is still open */ virtual ~TCPServer(); - /** Create a socket on a network stack - * - * Creates and opens a socket on the specified network stack. - * - * @param iface Network stack as target for socket - */ - TCPServer(NetworkStack *iface); - - /** Create a socket on a network interface - * - * Creates and opens a socket on the network stack of the given - * network interface. - * - * @param iface Network interface as target for socket - */ - TCPServer(NetworkInterface *iface); - - /** Opens a socket - * - * Creates a network socket on the specified network stack. - * Not needed if stack is passed to the socket's constructor. - * - * @param iface Network stack as target for socket - * @return 0 on success, negative error code on failure - */ - virtual int open(NetworkStack *iface); - /** Opens a socket * * Creates a network socket on the network stack of the given * network interface. Not needed if stack is passed to the * socket's constructor. * - * @param iface Network interface as target for socket + * @param stack Network stack as target for socket * @return 0 on success, negative error code on failure */ - virtual int open(NetworkInterface *iface); + virtual int open(NetworkStack *stack); + + template + int open(IF *iface) { + return open(iface->get_stack()); + } /** Listen for connections on a TCP socket * @@ -105,6 +99,7 @@ public: int accept(TCPSocket *connection); protected: virtual void event(); + volatile unsigned _pending; rtos::Semaphore _accept_sem; }; diff --git a/TCPSocket.cpp b/TCPSocket.cpp index a9441de60b..1e4232dc33 100644 --- a/TCPSocket.cpp +++ b/TCPSocket.cpp @@ -24,20 +24,10 @@ TCPSocket::TCPSocket() { } -TCPSocket::TCPSocket(NetworkStack *iface) - : _pending(0), _read_sem(0), _write_sem(0), - _read_in_progress(false), _write_in_progress(false) +TCPSocket::TCPSocket(NetworkStack *stack) + : _pending(0) { - // TCPSocket::open is thread safe - open(iface); -} - -TCPSocket::TCPSocket(NetworkInterface *iface) - : _pending(0), _read_sem(0), _write_sem(0), - _read_in_progress(false), _write_in_progress(false) -{ - // TCPSocket::open is thread safe - open(iface->get_stack()); + open(stack); } TCPSocket::~TCPSocket() @@ -45,25 +35,20 @@ TCPSocket::~TCPSocket() close(); } -int TCPSocket::open(NetworkStack *iface) +int TCPSocket::open(NetworkStack *stack) { - // Socket::open is thread safe - return Socket::open(iface, NSAPI_TCP); + return Socket::open(stack, NSAPI_TCP); } -int TCPSocket::open(NetworkInterface *iface) -{ - // Socket::open is thread safe - return TCPSocket::open(iface->get_stack()); -} - -int TCPSocket::connect(const SocketAddress &addr) +int TCPSocket::connect(const SocketAddress &address) { _lock.lock(); + int ret; - int ret = NSAPI_ERROR_NO_SOCKET; - if (_socket) { - ret = _iface->socket_connect(_socket, addr); + if (!_socket) { + ret = NSAPI_ERROR_NO_SOCKET; + } else { + ret = _stack->socket_connect(_socket, address); } _lock.unlock(); @@ -72,28 +57,26 @@ int TCPSocket::connect(const SocketAddress &addr) int TCPSocket::connect(const char *host, uint16_t port) { - _lock.lock(); - - SocketAddress addr(_iface, host, port); - int ret = NSAPI_ERROR_DNS_FAILURE; - if (addr) { - ret = connect(addr); + SocketAddress address(_stack, host, port); + if (!address) { + return NSAPI_ERROR_DNS_FAILURE; } - _lock.unlock(); - return ret; + // connect is thread safe + return connect(address); } int TCPSocket::send(const void *data, unsigned size) { _lock.lock(); + int ret; + // If this assert is hit then there are two threads // performing a send at the same time which is undefined // behavior MBED_ASSERT(!_write_in_progress); _write_in_progress = true; - int ret; while (true) { if (!_socket) { ret = NSAPI_ERROR_NO_SOCKET; @@ -101,7 +84,7 @@ int TCPSocket::send(const void *data, unsigned size) } _pending = 0; - int sent = _iface->socket_send(_socket, data, size); + int sent = _stack->socket_send(_socket, data, size); if ((0 == _timeout) || (NSAPI_ERROR_WOULD_BLOCK != sent)) { ret = sent; break; @@ -130,13 +113,14 @@ int TCPSocket::send(const void *data, unsigned size) int TCPSocket::recv(void *data, unsigned size) { _lock.lock(); + int ret; + // If this assert is hit then there are two threads // performing a recv at the same time which is undefined // behavior MBED_ASSERT(!_read_in_progress); _read_in_progress = true; - int ret; while (true) { if (!_socket) { ret = NSAPI_ERROR_NO_SOCKET; @@ -144,7 +128,7 @@ int TCPSocket::recv(void *data, unsigned size) } _pending = 0; - int recv = _iface->socket_recv(_socket, data, size); + int recv = _stack->socket_recv(_socket, data, size); if ((0 == _timeout) || (NSAPI_ERROR_WOULD_BLOCK != recv)) { ret = recv; break; diff --git a/TCPSocket.h b/TCPSocket.h index b537736b06..b0e23f87a1 100644 --- a/TCPSocket.h +++ b/TCPSocket.h @@ -33,38 +33,28 @@ public: */ TCPSocket(); - /** Destroy a socket - * - * Closes socket if the socket is still open - */ - virtual ~TCPSocket(); - - /** Create a socket on a network stack - * - * Creates and opens a socket on the specified network stack. - * - * @param iface Network stack as target for socket - */ - TCPSocket(NetworkStack *iface); - /** Create a socket on a network interface * * Creates and opens a socket on the network stack of the given * network interface. * - * @param iface Network interface as target for socket + * @param stack Network stack as target for socket */ - TCPSocket(NetworkInterface *iface); + TCPSocket(NetworkStack *stack); - /** Opens a socket + template + TCPSocket(IF *iface) + : _pending(0), _read_sem(0), _write_sem(0), + _read_in_progress(false), _write_in_progress(false) + { + open(iface->get_stack()); + } + + /** Destroy a socket * - * Creates a network socket on the specified network stack. - * Not needed if stack is passed to the socket's constructor. - * - * @param iface Network stack as target for socket - * @return 0 on success, negative error code on failure + * Closes socket if the socket is still open */ - virtual int open(NetworkStack *iface); + virtual ~TCPSocket(); /** Opens a socket * @@ -72,10 +62,15 @@ public: * network interface. Not needed if stack is passed to the * socket's constructor. * - * @param iface Network interface as target for socket + * @param stack Network stack as target for socket * @return 0 on success, negative error code on failure */ - virtual int open(NetworkInterface *iface); + virtual int open(NetworkStack *stack); + + template + int open(IF *iface) { + return open(iface->get_stack()); + } /** Connects TCP socket to a remote host * @@ -131,13 +126,15 @@ public: int recv(void *data, unsigned size); protected: + friend class TCPServer; + virtual void event(); + volatile unsigned _pending; rtos::Semaphore _read_sem; rtos::Semaphore _write_sem; bool _read_in_progress; bool _write_in_progress; - friend class TCPServer; }; diff --git a/UDPSocket.cpp b/UDPSocket.cpp index 29b2dcdb4a..95eb97e25c 100644 --- a/UDPSocket.cpp +++ b/UDPSocket.cpp @@ -24,18 +24,11 @@ UDPSocket::UDPSocket() { } -UDPSocket::UDPSocket(NetworkStack *iface) +UDPSocket::UDPSocket(NetworkStack *stack) : _pending(0), _read_sem(0), _write_sem(0), _read_in_progress(false), _write_in_progress(false) { - open(iface); -} - -UDPSocket::UDPSocket(NetworkInterface *iface) - : _pending(0), _read_sem(0), _write_sem(0), - _read_in_progress(false), _write_in_progress(false) -{ - open(iface->get_stack()); + open(stack); } UDPSocket::~UDPSocket() @@ -43,39 +36,33 @@ UDPSocket::~UDPSocket() close(); } -int UDPSocket::open(NetworkStack *iface) +int UDPSocket::open(NetworkStack *stack) { - return Socket::open(iface, NSAPI_UDP); -} - -int UDPSocket::open(NetworkInterface *iface) -{ - return UDPSocket::open(iface->get_stack()); + return Socket::open(stack, NSAPI_UDP); } int UDPSocket::sendto(const char *host, uint16_t port, const void *data, unsigned size) { - SocketAddress addr(_iface, host, port); - if (!addr) { + SocketAddress address(_stack, host, port); + if (!address) { return NSAPI_ERROR_DNS_FAILURE; } // sendto is thread safe - int ret = sendto(addr, data, size); - - return ret; + return sendto(address, data, size); } int UDPSocket::sendto(const SocketAddress &address, const void *data, unsigned size) { _lock.lock(); + int ret; + // If this assert is hit then there are two threads // performing a send at the same time which is undefined // behavior MBED_ASSERT(!_write_in_progress); _write_in_progress = true; - int ret; while (true) { if (!_socket) { ret = NSAPI_ERROR_NO_SOCKET; @@ -83,7 +70,7 @@ int UDPSocket::sendto(const SocketAddress &address, const void *data, unsigned s } _pending = 0; - int sent = _iface->socket_sendto(_socket, address, data, size); + int sent = _stack->socket_sendto(_socket, address, data, size); if ((0 == _timeout) || (NSAPI_ERROR_WOULD_BLOCK != sent)) { ret = sent; break; @@ -112,13 +99,14 @@ int UDPSocket::sendto(const SocketAddress &address, const void *data, unsigned s int UDPSocket::recvfrom(SocketAddress *address, void *buffer, unsigned size) { _lock.lock(); + int ret; + // If this assert is hit then there are two threads // performing a recv at the same time which is undefined // behavior MBED_ASSERT(!_read_in_progress); _read_in_progress = true; - int ret; while (true) { if (!_socket) { ret = NSAPI_ERROR_NO_SOCKET; @@ -126,7 +114,7 @@ int UDPSocket::recvfrom(SocketAddress *address, void *buffer, unsigned size) } _pending = 0; - int recv = _iface->socket_recvfrom(_socket, address, buffer, size); + int recv = _stack->socket_recvfrom(_socket, address, buffer, size); if ((0 == _timeout) || (NSAPI_ERROR_WOULD_BLOCK != recv)) { ret = recv; break; diff --git a/UDPSocket.h b/UDPSocket.h index 235820c5d4..02cc8e90a5 100644 --- a/UDPSocket.h +++ b/UDPSocket.h @@ -33,38 +33,28 @@ public: */ UDPSocket(); - /** Destroy a socket - * - * Closes socket if the socket is still open - */ - virtual ~UDPSocket(); - - /** Create a socket on a network stack - * - * Creates and opens a socket on the specified network stack. - * - * @param iface Network stack as target for socket - */ - UDPSocket(NetworkStack *iface); - /** Create a socket on a network interface * * Creates and opens a socket on the network stack of the given * network interface. * - * @param iface Network interface as target for socket + * @param stack Network stack as target for socket */ - UDPSocket(NetworkInterface *iface); + UDPSocket(NetworkStack *stack); - /** Opens a socket + template + UDPSocket(IF *iface) + : _pending(0), _read_sem(0), _write_sem(0), + _read_in_progress(false), _write_in_progress(false) + { + open(iface->get_stack()); + } + + /** Destroy a socket * - * Creates a network socket on the specified network stack. - * Not needed if stack is passed to the socket's constructor. - * - * @param iface Network stack as target for socket - * @return 0 on success, negative error code on failure + * Closes socket if the socket is still open */ - virtual int open(NetworkStack *iface); + virtual ~UDPSocket(); /** Opens a socket * @@ -72,10 +62,15 @@ public: * network interface. Not needed if stack is passed to the * socket's constructor. * - * @param iface Network interface as target for socket + * @param stack Network stack as target for socket * @return 0 on success, negative error code on failure */ - virtual int open(NetworkInterface *iface); + virtual int open(NetworkStack *stack); + + template + int open(IF *iface) { + return open(iface->get_stack()); + } /** Send a packet over a UDP socket * @@ -129,8 +124,10 @@ public: * code on failure */ int recvfrom(SocketAddress *address, void *data, unsigned size); + protected: virtual void event(); + volatile unsigned _pending; rtos::Semaphore _read_sem; rtos::Semaphore _write_sem; From 856120994ef31d14c5a947fcf1f58f83b29cd704 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 19 Jul 2016 21:54:51 -0500 Subject: [PATCH 39/40] Standardized documentation of the NetworkInterface class --- NetworkInterface.h | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/NetworkInterface.h b/NetworkInterface.h index 6a8b3ea6b4..9c7706b2b5 100644 --- a/NetworkInterface.h +++ b/NetworkInterface.h @@ -20,22 +20,33 @@ #include "NetworkSocketAPI/NetworkStack.h" +/** NetworkInterface class + * + * Common interface that is shared between network devices + */ class NetworkInterface { public: virtual ~NetworkInterface() {}; - NetworkInterface() {} - /** Get the internally stored IP address - /return IP address of the interface or null if not yet connected - */ + /** Get the local IP address + * + * @return Null-terminated representation of the local IP address + * or null if not yet connected + */ virtual const char *get_ip_address() = 0; + protected: friend class Socket; friend class UDPSocket; friend class TCPSocket; friend class TCPServer; friend class SocketAddress; - virtual NetworkStack * get_stack(void) = 0; + + /** Provide access to the NetworkStack object + * + * @return The underlying NetworkStack object + */ + virtual NetworkStack *get_stack() = 0; }; From e09565474188b905cada21166118a82e66359217 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Wed, 20 Jul 2016 15:20:03 -0500 Subject: [PATCH 40/40] Fixed ambiguity/protected access issue in Socket::open NetworkStack/NetworkInterface overloads - Multiple inheritance results in ambiguity NetworkStack with templated get_stack overload - get_stack hidden behind protected access prevents template match - explicit call to NetworkInterface::get_stack does not follow vtable Solution is to use two overloaded function for NetworkStack/NetworkInterface, and an overload for a templated type that supports a static_cast to NetworkStack. This resolves the ambiguity without being blocked by protected access. Moved duplicated overloads out to overload of common nsapi_create_stack function. --- NetworkInterface.h | 5 ++++- NetworkStack.cpp | 6 ++++++ NetworkStack.h | 13 ++++++++++++- Socket.cpp | 4 ++-- Socket.h | 10 +++++----- SocketAddress.cpp | 5 ----- SocketAddress.h | 10 ++++------ TCPServer.cpp | 10 ++-------- TCPServer.h | 18 ++---------------- TCPSocket.cpp | 10 ++-------- TCPSocket.h | 25 ++++--------------------- UDPSocket.cpp | 11 ++--------- UDPSocket.h | 25 ++++--------------------- 13 files changed, 49 insertions(+), 103 deletions(-) diff --git a/NetworkInterface.h b/NetworkInterface.h index 9c7706b2b5..4defc0c5ec 100644 --- a/NetworkInterface.h +++ b/NetworkInterface.h @@ -17,7 +17,8 @@ #ifndef NETWORK_INTERFACE_H #define NETWORK_INTERFACE_H -#include "NetworkSocketAPI/NetworkStack.h" +// Predeclared class +class NetworkStack; /** NetworkInterface class @@ -41,6 +42,8 @@ protected: friend class TCPSocket; friend class TCPServer; friend class SocketAddress; + template + friend NetworkStack *nsapi_create_stack(IF *iface); /** Provide access to the NetworkStack object * diff --git a/NetworkStack.cpp b/NetworkStack.cpp index b6cc903ef7..701324a8dd 100644 --- a/NetworkStack.cpp +++ b/NetworkStack.cpp @@ -249,3 +249,9 @@ NetworkStack *nsapi_create_stack(nsapi_stack_t *stack) MBED_ASSERT(sizeof stack->_stack_buffer >= sizeof(NetworkStackWrapper)); return new (stack->_stack_buffer) NetworkStackWrapper; } + +NetworkStack *nsapi_create_stack(NetworkStack *stack) +{ + return stack; +} + diff --git a/NetworkStack.h b/NetworkStack.h index 2920f8abc2..d1b6f0e495 100644 --- a/NetworkStack.h +++ b/NetworkStack.h @@ -19,6 +19,7 @@ #include "nsapi_types.h" #include "NetworkSocketAPI/SocketAddress.h" +#include "NetworkSocketAPI/NetworkInterface.h" /** NetworkStack class @@ -280,10 +281,20 @@ protected: /** Convert a raw nsapi_stack_t object into a C++ NetworkStack object * - * @param stack Reference to a raw nsapi_stack_t object + * @param stack Reference to an object that can be converted to a stack + * - A raw nsapi_stack_t object + * - A reference to a network stack + * - A reference to a network interface * @return Reference to the underlying network stack */ NetworkStack *nsapi_create_stack(nsapi_stack_t *stack); +NetworkStack *nsapi_create_stack(NetworkStack *stack); + +template +NetworkStack *nsapi_create_stack(IF *iface) +{ + return nsapi_create_stack(static_cast(iface)->get_stack()); +} #endif diff --git a/Socket.cpp b/Socket.cpp index 70d393bc59..c9be8921b1 100644 --- a/Socket.cpp +++ b/Socket.cpp @@ -23,7 +23,7 @@ Socket::Socket() { } -int Socket::open(NetworkStack *stack, nsapi_protocol_t proto) +int Socket::open(NetworkStack *stack) { _lock.lock(); @@ -34,7 +34,7 @@ int Socket::open(NetworkStack *stack, nsapi_protocol_t proto) _stack = stack; nsapi_socket_t socket; - int err = _stack->socket_open(&socket, proto); + int err = _stack->socket_open(&socket, get_proto()); if (err) { _lock.unlock(); return err; diff --git a/Socket.h b/Socket.h index a3209c82ca..16441b8735 100644 --- a/Socket.h +++ b/Socket.h @@ -46,11 +46,11 @@ public: * @param stack Network stack as target for socket * @return 0 on success, negative error code on failure */ - virtual int open(NetworkStack *stack) = 0; + int open(NetworkStack *stack); - template - int open(IF *iface) { - return open(iface->get_stack()); + template + int open(S *stack) { + return open(nsapi_create_stack(stack)); } /** Close the socket @@ -180,7 +180,7 @@ public: protected: Socket(); - int open(NetworkStack *stack, nsapi_protocol_t proto); + virtual nsapi_protocol_t get_proto() = 0; virtual void event() = 0; NetworkStack *_stack; diff --git a/SocketAddress.cpp b/SocketAddress.cpp index 8c16518a8f..deaa715008 100644 --- a/SocketAddress.cpp +++ b/SocketAddress.cpp @@ -143,11 +143,6 @@ static void ipv6_to_address(char *addr, const uint8_t *bytes) } -SocketAddress::SocketAddress(NetworkStack *iface, const char *host, uint16_t port) -{ - _SocketAddress(iface, host, port); -} - SocketAddress::SocketAddress(nsapi_addr_t addr, uint16_t port) { _ip_address[0] = '\0'; diff --git a/SocketAddress.h b/SocketAddress.h index 64bc80a998..bb5f66e03f 100644 --- a/SocketAddress.h +++ b/SocketAddress.h @@ -38,16 +38,14 @@ public: * * On failure, the IP address and port will be set to zero * - * @param iface Network interface to use for DNS resolution + * @param stack Network stack to use for DNS resolution * @param host Hostname to resolve * @param port Optional 16-bit port */ - SocketAddress(NetworkStack *iface, const char *host, uint16_t port = 0); - - template - SocketAddress(IF *iface, const char *host, uint16_t port = 0) + template + SocketAddress(S *stack, const char *host, uint16_t port = 0) { - _SocketAddress(iface->get_stack(), host, port); + _SocketAddress(nsapi_create_stack(stack), host, port); } /** Create a SocketAddress from a raw IP address and port diff --git a/TCPServer.cpp b/TCPServer.cpp index 6598ff9e4a..888b91b229 100644 --- a/TCPServer.cpp +++ b/TCPServer.cpp @@ -22,20 +22,14 @@ TCPServer::TCPServer() { } -TCPServer::TCPServer(NetworkStack *stack) - : _pending(0), _accept_sem(0) -{ - open(stack); -} - TCPServer::~TCPServer() { close(); } -int TCPServer::open(NetworkStack *stack) +nsapi_protocol_t TCPServer::get_proto() { - return Socket::open(stack, NSAPI_TCP); + return NSAPI_TCP; } int TCPServer::listen(int backlog) diff --git a/TCPServer.h b/TCPServer.h index 4bca0ca5f3..36a888cab4 100644 --- a/TCPServer.h +++ b/TCPServer.h @@ -56,22 +56,6 @@ public: */ virtual ~TCPServer(); - /** Opens a socket - * - * Creates a network socket on the network stack of the given - * network interface. Not needed if stack is passed to the - * socket's constructor. - * - * @param stack Network stack as target for socket - * @return 0 on success, negative error code on failure - */ - virtual int open(NetworkStack *stack); - - template - int open(IF *iface) { - return open(iface->get_stack()); - } - /** Listen for connections on a TCP socket * * Marks the socket as a passive socket that can be used to accept @@ -97,7 +81,9 @@ public: * @return 0 on success, negative error code on failure */ int accept(TCPSocket *connection); + protected: + virtual nsapi_protocol_t get_proto(); virtual void event(); volatile unsigned _pending; diff --git a/TCPSocket.cpp b/TCPSocket.cpp index 1e4232dc33..7a1d3893d7 100644 --- a/TCPSocket.cpp +++ b/TCPSocket.cpp @@ -24,20 +24,14 @@ TCPSocket::TCPSocket() { } -TCPSocket::TCPSocket(NetworkStack *stack) - : _pending(0) -{ - open(stack); -} - TCPSocket::~TCPSocket() { close(); } -int TCPSocket::open(NetworkStack *stack) +nsapi_protocol_t TCPSocket::get_proto() { - return Socket::open(stack, NSAPI_TCP); + return NSAPI_TCP; } int TCPSocket::connect(const SocketAddress &address) diff --git a/TCPSocket.h b/TCPSocket.h index b0e23f87a1..5b18684a5d 100644 --- a/TCPSocket.h +++ b/TCPSocket.h @@ -40,14 +40,12 @@ public: * * @param stack Network stack as target for socket */ - TCPSocket(NetworkStack *stack); - - template - TCPSocket(IF *iface) + template + TCPSocket(S *stack) : _pending(0), _read_sem(0), _write_sem(0), _read_in_progress(false), _write_in_progress(false) { - open(iface->get_stack()); + open(stack); } /** Destroy a socket @@ -56,22 +54,6 @@ public: */ virtual ~TCPSocket(); - /** Opens a socket - * - * Creates a network socket on the network stack of the given - * network interface. Not needed if stack is passed to the - * socket's constructor. - * - * @param stack Network stack as target for socket - * @return 0 on success, negative error code on failure - */ - virtual int open(NetworkStack *stack); - - template - int open(IF *iface) { - return open(iface->get_stack()); - } - /** Connects TCP socket to a remote host * * Initiates a connection to a remote server specified by either @@ -128,6 +110,7 @@ public: protected: friend class TCPServer; + virtual nsapi_protocol_t get_proto(); virtual void event(); volatile unsigned _pending; diff --git a/UDPSocket.cpp b/UDPSocket.cpp index 95eb97e25c..dfee1fff6f 100644 --- a/UDPSocket.cpp +++ b/UDPSocket.cpp @@ -24,21 +24,14 @@ UDPSocket::UDPSocket() { } -UDPSocket::UDPSocket(NetworkStack *stack) - : _pending(0), _read_sem(0), _write_sem(0), - _read_in_progress(false), _write_in_progress(false) -{ - open(stack); -} - UDPSocket::~UDPSocket() { close(); } -int UDPSocket::open(NetworkStack *stack) +nsapi_protocol_t UDPSocket::get_proto() { - return Socket::open(stack, NSAPI_UDP); + return NSAPI_UDP; } int UDPSocket::sendto(const char *host, uint16_t port, const void *data, unsigned size) diff --git a/UDPSocket.h b/UDPSocket.h index 02cc8e90a5..36f9f3669c 100644 --- a/UDPSocket.h +++ b/UDPSocket.h @@ -40,14 +40,12 @@ public: * * @param stack Network stack as target for socket */ - UDPSocket(NetworkStack *stack); - - template - UDPSocket(IF *iface) + template + UDPSocket(S *stack) : _pending(0), _read_sem(0), _write_sem(0), _read_in_progress(false), _write_in_progress(false) { - open(iface->get_stack()); + open(stack); } /** Destroy a socket @@ -56,22 +54,6 @@ public: */ virtual ~UDPSocket(); - /** Opens a socket - * - * Creates a network socket on the network stack of the given - * network interface. Not needed if stack is passed to the - * socket's constructor. - * - * @param stack Network stack as target for socket - * @return 0 on success, negative error code on failure - */ - virtual int open(NetworkStack *stack); - - template - int open(IF *iface) { - return open(iface->get_stack()); - } - /** Send a packet over a UDP socket * * Sends data to the specified address specified by either a domain name @@ -126,6 +108,7 @@ public: int recvfrom(SocketAddress *address, void *data, unsigned size); protected: + virtual nsapi_protocol_t get_proto(); virtual void event(); volatile unsigned _pending;