diff --git a/features/net/network-socket/CellularInterface.h b/features/net/network-socket/CellularInterface.h new file mode 100644 index 0000000000..45364fd562 --- /dev/null +++ b/features/net/network-socket/CellularInterface.h @@ -0,0 +1,53 @@ +/* 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 "network-socket/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; + + /** 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/features/net/network-socket/DnsQuery/DnsQuery.cpp b/features/net/network-socket/DnsQuery/DnsQuery.cpp new file mode 100644 index 0000000000..2c209f529e --- /dev/null +++ b/features/net/network-socket/DnsQuery/DnsQuery.cpp @@ -0,0 +1,216 @@ +/* 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; +} + +static int32_t query(UDPSocket *socket, const SocketAddress &addr, 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->sendto(addr, 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->recvfrom(NULL, packet, 1024); + + if (response_length > 0 ) { + if (!resolve(packet, ipaddress)) { + delete packet; + return NSAPI_ERROR_DNS_FAILURE; + } + + // cleanup and return + delete packet; + return 0; + } + + delete packet; + return NSAPI_ERROR_DNS_FAILURE; +} + +int32_t dnsQuery(NetworkStack *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/features/net/network-socket/DnsQuery/DnsQuery.h b/features/net/network-socket/DnsQuery/DnsQuery.h new file mode 100644 index 0000000000..3c5dfa7d70 --- /dev/null +++ b/features/net/network-socket/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 "NetworkStack.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(NetworkStack *iface, const char *host, char *ip); +int32_t dnsQuery(UDPSocket *sock, const char *host, char *ip); + + +#endif // __DNSQUERY_H__ diff --git a/features/net/network-socket/EthInterface.h b/features/net/network-socket/EthInterface.h new file mode 100644 index 0000000000..7a094e7c4c --- /dev/null +++ b/features/net/network-socket/EthInterface.h @@ -0,0 +1,50 @@ +/* EthInterface + * 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 ETH_INTERFACE_H +#define ETH_INTERFACE_H + +#include "network-socket/NetworkInterface.h" + + +/** EthInterface class + * + * Common interface that is shared between ethernet hardware. + */ +class EthInterface : public NetworkInterface +{ +public: + /** Start the interface + * + * @return 0 on success, negative error code on failure + */ + virtual int connect() = 0; + + /** Stop the interface + * + * @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/features/net/network-socket/MeshInterface.h b/features/net/network-socket/MeshInterface.h new file mode 100644 index 0000000000..01e737871d --- /dev/null +++ b/features/net/network-socket/MeshInterface.h @@ -0,0 +1,50 @@ +/* MeshInterface + * 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 "network-socket/NetworkInterface.h" + + +/** MeshInterface class + * + * 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; + + /** 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/features/net/network-socket/NetworkInterface.h b/features/net/network-socket/NetworkInterface.h new file mode 100644 index 0000000000..4defc0c5ec --- /dev/null +++ b/features/net/network-socket/NetworkInterface.h @@ -0,0 +1,56 @@ +/* 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 + +// Predeclared class +class NetworkStack; + + +/** NetworkInterface class + * + * Common interface that is shared between network devices + */ +class NetworkInterface { +public: + virtual ~NetworkInterface() {}; + + /** 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; + template + friend NetworkStack *nsapi_create_stack(IF *iface); + + /** Provide access to the NetworkStack object + * + * @return The underlying NetworkStack object + */ + virtual NetworkStack *get_stack() = 0; +}; + + +#endif diff --git a/features/net/network-socket/NetworkStack.cpp b/features/net/network-socket/NetworkStack.cpp new file mode 100644 index 0000000000..701324a8dd --- /dev/null +++ b/features/net/network-socket/NetworkStack.cpp @@ -0,0 +1,257 @@ +/* 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 "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]; + int err = dnsQuery(this, name, buffer); + if (err) { + return err; + } + + address->set_ip_address(buffer); + return 0; +} + +int NetworkStack::setstackopt(int level, int optname, const void *optval, unsigned optlen) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +int NetworkStack::getstackopt(int level, int optname, void *optval, unsigned *optlen) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +int NetworkStack::setsockopt(void *handle, int level, int optname, const void *optval, unsigned optlen) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +int NetworkStack::getsockopt(void *handle, int level, int optname, void *optval, unsigned *optlen) +{ + 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; +} + +NetworkStack *nsapi_create_stack(NetworkStack *stack) +{ + return stack; +} + diff --git a/features/net/network-socket/NetworkStack.h b/features/net/network-socket/NetworkStack.h new file mode 100644 index 0000000000..7faab703b6 --- /dev/null +++ b/features/net/network-socket/NetworkStack.h @@ -0,0 +1,300 @@ +/* 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_STACK_H +#define NETWORK_STACK_H + +#include "nsapi_types.h" +#include "network-socket/SocketAddress.h" +#include "network-socket/NetworkInterface.h" + + +/** NetworkStack class + * + * Common interface that is shared between hardware that + * can connect to a network over IP. By implementing the + * NetworkStack, a network stack can be used as a target + * for instantiating network sockets. + */ +class NetworkStack +{ +public: + virtual ~NetworkStack() {}; + + /** 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; + + /** 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 address Destination for the host SocketAddress + * @param host Hostname to resolve + * @return 0 on success, negative error code on failure + */ + virtual int gethostbyname(SocketAddress *address, 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 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 + */ + virtual int setstackopt(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 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 + */ + virtual int getstackopt(int level, int optname, void *optval, unsigned *optlen); + +protected: + friend class Socket; + friend class UDPSocket; + friend class TCPSocket; + friend class TCPServer; + + /** 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 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(nsapi_socket_t *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 error code on failure + */ + virtual int socket_close(nsapi_socket_t handle) = 0; + + /** 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 bind + * @return 0 on success, negative error code on failure. + */ + virtual int socket_bind(nsapi_socket_t handle, const SocketAddress &address) = 0; + + /** 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 + * simultaneously + * @return 0 on success, negative error code on failure + */ + virtual int socket_listen(nsapi_socket_t handle, int backlog) = 0; + + /** 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 The SocketAddress of the remote host + * @return 0 on success, negative error code on failure + */ + virtual int socket_connect(nsapi_socket_t handle, const SocketAddress &address) = 0; + + /** 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 handle 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 + */ + virtual int socket_accept(nsapi_socket_t *handle, nsapi_socket_t server) = 0; + + /** 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 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(nsapi_socket_t handle, const void *data, unsigned size) = 0; + + /** 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 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(nsapi_socket_t handle, void *data, unsigned size) = 0; + + /** 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 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(nsapi_socket_t handle, const SocketAddress &address, const void *data, unsigned size) = 0; + + /** 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 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(nsapi_socket_t 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 + */ + virtual void socket_attach(nsapi_socket_t handle, void (*callback)(void *), void *data) = 0; + + /* 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 handle 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 + */ + virtual int setsockopt(nsapi_socket_t handle, 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 handle 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 + */ + virtual int getsockopt(nsapi_socket_t handle, int level, int optname, void *optval, unsigned *optlen); +}; + + +/** Convert a raw nsapi_stack_t object into a C++ NetworkStack 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/features/net/network-socket/Socket.cpp b/features/net/network-socket/Socket.cpp new file mode 100644 index 0000000000..c9be8921b1 --- /dev/null +++ b/features/net/network-socket/Socket.cpp @@ -0,0 +1,157 @@ +/* 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" + +Socket::Socket() + : _stack(0) + , _socket(0) + , _timeout(osWaitForever) +{ +} + +int Socket::open(NetworkStack *stack) +{ + _lock.lock(); + + if (_stack != NULL || stack == NULL) { + _lock.unlock(); + return NSAPI_ERROR_PARAMETER; + } + _stack = stack; + + nsapi_socket_t socket; + int err = _stack->socket_open(&socket, get_proto()); + if (err) { + _lock.unlock(); + return err; + } + + _socket = socket; + _event.attach(this, &Socket::event); + _stack->socket_attach(_socket, Callback::thunk, &_event); + + _lock.unlock(); + return 0; +} + +int Socket::close() +{ + _lock.lock(); + + int ret = 0; + if (_socket) { + _stack->socket_attach(_socket, 0, 0); + nsapi_socket_t socket = _socket; + _socket = 0; + ret = _stack->socket_close(socket); + } + + // Wakeup anything in a blocking operation + // on this 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) +{ + _lock.lock(); + int ret; + + if (!_socket) { + ret = NSAPI_ERROR_NO_SOCKET; + } else { + ret = _stack->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) +{ + _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) +{ + _lock.lock(); + int ret; + + if (!_socket) { + ret = NSAPI_ERROR_NO_SOCKET; + } else { + ret = _stack->setsockopt(_socket, level, optname, optval, optlen); + } + + _lock.unlock(); + return ret; +} + +int Socket::getsockopt(int level, int optname, void *optval, unsigned *optlen) +{ + _lock.lock(); + int ret; + + if (!_socket) { + ret = NSAPI_ERROR_NO_SOCKET; + } else { + ret = _stack->getsockopt(_socket, level, optname, optval, optlen); + } + + _lock.unlock(); + return ret; + +} + +void Socket::attach(Callback callback) +{ + _lock.lock(); + + _callback = callback; + + _lock.unlock(); +} diff --git a/features/net/network-socket/Socket.h b/features/net/network-socket/Socket.h new file mode 100644 index 0000000000..35dfe0865b --- /dev/null +++ b/features/net/network-socket/Socket.h @@ -0,0 +1,195 @@ +/* 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 "network-socket/SocketAddress.h" +#include "network-socket/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 + */ +class Socket { +public: + /** Destroy a socket + * + * Closes socket if the socket is still open + */ + virtual ~Socket() {} + + /** 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 + */ + int open(NetworkStack *stack); + + template + int open(S *stack) { + return open(nsapi_create_stack(stack)); + } + + /** 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 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 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 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 + * + * 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. + * + * 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); + + /** 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 the timeout from the socket. A negative + * value give the socket an unbounded timeout. + * + * set_timeout(0) is equivalent to set_blocking(false) + * set_timeout(-1) is equivalent to set_blocking(true) + * + * @param timeout Timeout in milliseconds + */ + void set_timeout(int timeout); + + /* 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 error code on failure + */ + int setsockopt(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 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(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 func Function to call on state change + */ + void attach(mbed::Callback func); + + /** 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 obj Pointer to object to call method on + * @param method Method to call on state change + */ + template + void attach(T *obj, M method) { + attach(mbed::Callback(obj, method)); + } + +protected: + Socket(); + virtual nsapi_protocol_t get_proto() = 0; + virtual void event() = 0; + + NetworkStack *_stack; + nsapi_socket_t _socket; + uint32_t _timeout; + mbed::Callback _event; + mbed::Callback _callback; + rtos::Mutex _lock; +}; + + +#endif diff --git a/features/net/network-socket/SocketAddress.cpp b/features/net/network-socket/SocketAddress.cpp new file mode 100644 index 0000000000..deaa715008 --- /dev/null +++ b/features/net/network-socket/SocketAddress.cpp @@ -0,0 +1,282 @@ +/* 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 "NetworkStack.h" +#include +#include "mbed.h" + + +static bool ipv4_is_valid(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 ipv6_is_valid(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 ipv4_from_address(uint8_t *bytes, const char *addr) +{ + 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) { + 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); + } +} + +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/2; i++) { + sprintf(&addr[5*i], "%02x%02x", bytes[2*i], bytes[2*i+1]); + addr[5*i+4] = ':'; + } + addr[NSAPI_IPv6_SIZE-1] = '\0'; +} + + +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) +{ + _ip_address[0] = '\0'; + set_ip_address(addr); + set_port(port); +} + +SocketAddress::SocketAddress(const void *bytes, nsapi_version_t version, uint16_t port) +{ + _ip_address[0] = '\0'; + set_ip_bytes(bytes, version); + set_port(port); +} + +SocketAddress::SocketAddress(const SocketAddress &addr) +{ + _ip_address[0] = '\0'; + set_addr(addr.get_addr()); + set_port(addr.get_port()); +} + +void SocketAddress::set_ip_address(const char *addr) +{ + _ip_address[0] = '\0'; + + if (addr && ipv4_is_valid(addr)) { + _addr.version = NSAPI_IPv4; + ipv4_from_address(_addr.bytes, addr); + } else if (addr && ipv6_is_valid(addr)) { + _addr.version = NSAPI_IPv6; + ipv6_from_address(_addr.bytes, addr); + } else { + _addr = (nsapi_addr_t){}; + } +} + +void SocketAddress::set_ip_bytes(const void *bytes, nsapi_version_t version) +{ + nsapi_addr_t addr; + addr.version = version; + memcpy(addr.bytes, bytes, NSAPI_IP_BYTES); + set_addr(addr); +} + +void SocketAddress::set_addr(nsapi_addr_t addr) +{ + _ip_address[0] = '\0'; + _addr = addr; +} + +void SocketAddress::set_port(uint16_t port) +{ + _port = port; +} + +const char *SocketAddress::get_ip_address() const +{ + char *ip_address = (char *)_ip_address; + + if (!ip_address[0]) { + 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); + } + } + + return ip_address; +} + +const void *SocketAddress::get_ip_bytes() const +{ + return _addr.bytes; +} + +nsapi_version_t SocketAddress::get_ip_version() const +{ + return _addr.version; +} + +nsapi_addr_t SocketAddress::get_addr() const +{ + return _addr; +} + +uint16_t SocketAddress::get_port() const +{ + return _port; +} + +SocketAddress::operator bool() const +{ + int count = 0; + if (_addr.version == NSAPI_IPv4) { + count = NSAPI_IPv4_BYTES; + } else if (_addr.version == NSAPI_IPv6) { + count = NSAPI_IPv6_BYTES; + } + + for (int i = 0; i < count; i++) { + if (_addr.bytes[i]) { + return true; + } + } + + return false; +} + +void SocketAddress::_SocketAddress(NetworkStack *iface, const char *host, uint16_t port) +{ + _ip_address[0] = '\0'; + + // Check for valid IP addresses + if (host && ipv4_is_valid(host)) { + _addr.version = NSAPI_IPv4; + ipv4_from_address(_addr.bytes, host); + _port = port; + } else if (host && ipv6_is_valid(host)) { + _addr.version = NSAPI_IPv6; + ipv6_from_address(_addr.bytes, host); + _port = port; + } else { + // DNS lookup + int err = iface->gethostbyname(this, host); + if (err) { + _addr = (nsapi_addr_t){}; + _port = 0; + } + } +} diff --git a/features/net/network-socket/SocketAddress.h b/features/net/network-socket/SocketAddress.h new file mode 100644 index 0000000000..bb5f66e03f --- /dev/null +++ b/features/net/network-socket/SocketAddress.h @@ -0,0 +1,149 @@ +/* SocketAddress + * 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 "nsapi_types.h" +#include "toolchain.h" + +// Predeclared classes +class NetworkStack; +class NetworkInterface; + + +/** SocketAddress class + * + * Representation of an IP address and port pair. + */ +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 stack Network stack to use for DNS resolution + * @param host Hostname to resolve + * @param port Optional 16-bit port + */ + template + SocketAddress(S *stack, const char *host, uint16_t port = 0) + { + _SocketAddress(nsapi_create_stack(stack), host, port); + } + + /** 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, uint16_t port = 0); + + /** 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 + * @param port Optional 16-bit port + */ + SocketAddress(const void *bytes, nsapi_version_t version, uint16_t port = 0); + + /** Create a SocketAddress from another SocketAddress + * + * @param address SocketAddress to copy + */ + SocketAddress(const SocketAddress &addr); + + /** Set the IP address + * + * @param addr Null-terminated represention of the IP address + */ + void set_ip_address(const char *addr); + + /** 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 + */ + void set_port(uint16_t port); + + /** Get the IP address + * + * @return Null-terminated representation of the IP Address + */ + const char *get_ip_address() const; + + /* Get the raw IP bytes + * + * @return Raw IP address in big-endian order + */ + const void *get_ip_bytes() const; + + /** Get the IP address version + * + * @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 + * + * @return The 16-bit port + */ + uint16_t get_port() const; + + /** Test if address is zero + * + * @return True if address is not zero + */ + operator bool() const; + +private: + void _SocketAddress(NetworkStack *iface, const char *host, uint16_t port); + + char _ip_address[NSAPI_IP_SIZE]; + nsapi_addr_t _addr; + uint16_t _port; +}; + + +#endif diff --git a/features/net/network-socket/TCPServer.cpp b/features/net/network-socket/TCPServer.cpp new file mode 100644 index 0000000000..888b91b229 --- /dev/null +++ b/features/net/network-socket/TCPServer.cpp @@ -0,0 +1,110 @@ +/* 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() + : _pending(0), _accept_sem(0) +{ +} + +TCPServer::~TCPServer() +{ + close(); +} + +nsapi_protocol_t TCPServer::get_proto() +{ + return NSAPI_TCP; +} + +int TCPServer::listen(int backlog) +{ + _lock.lock(); + int ret; + + if (!_socket) { + ret = NSAPI_ERROR_NO_SOCKET; + } else { + ret = _stack->socket_listen(_socket, backlog); + } + + _lock.unlock(); + return ret; +} + +int TCPServer::accept(TCPSocket *connection) +{ + _lock.lock(); + int ret; + + while (true) { + if (!_socket) { + ret = NSAPI_ERROR_NO_SOCKET; + break; + } + + _pending = 0; + void *socket; + ret = _stack->socket_accept(&socket, _socket); + + if (0 == ret) { + connection->_lock.lock(); + + if (connection->_socket) { + connection->close(); + } + + connection->_stack = _stack; + connection->_socket = socket; + connection->_event = Callback(connection, &TCPSocket::event); + _stack->socket_attach(socket, &Callback::thunk, &connection->_event); + + connection->_lock.unlock(); + break; + } + + if (NSAPI_ERROR_WOULD_BLOCK == ret) { + int32_t count; + + _lock.unlock(); + count = _accept_sem.wait(_timeout); + _lock.lock(); + + if (count < 1) { + ret = NSAPI_ERROR_WOULD_BLOCK; + break; + } + } + } + + _lock.unlock(); + return ret; +} + +void TCPServer::event() +{ + int32_t acount = _accept_sem.wait(0); + if (acount <= 1) { + _accept_sem.release(); + } + + _pending += 1; + if (_callback && _pending == 1) { + _callback(); + } +} diff --git a/features/net/network-socket/TCPServer.h b/features/net/network-socket/TCPServer.h new file mode 100644 index 0000000000..f57c5f5ad9 --- /dev/null +++ b/features/net/network-socket/TCPServer.h @@ -0,0 +1,94 @@ +/* TCPServer + * 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 "network-socket/Socket.h" +#include "network-socket/TCPSocket.h" +#include "network-socket/NetworkStack.h" +#include "network-socket/NetworkInterface.h" +#include "rtos/Semaphore.h" + + +/** TCP socket server + */ +class TCPServer : public Socket { +public: + /** Create an uninitialized socket + * + * Must call open to initialize the socket on a network stack. + */ + 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(); + + /** 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); + + /** 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); + +protected: + virtual nsapi_protocol_t get_proto(); + virtual void event(); + + volatile unsigned _pending; + rtos::Semaphore _accept_sem; +}; + + +#endif diff --git a/features/net/network-socket/TCPSocket.cpp b/features/net/network-socket/TCPSocket.cpp new file mode 100644 index 0000000000..7a1d3893d7 --- /dev/null +++ b/features/net/network-socket/TCPSocket.cpp @@ -0,0 +1,166 @@ +/* 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" +#include "mbed_assert.h" + +TCPSocket::TCPSocket() + : _pending(0), _read_sem(0), _write_sem(0), + _read_in_progress(false), _write_in_progress(false) +{ +} + +TCPSocket::~TCPSocket() +{ + close(); +} + +nsapi_protocol_t TCPSocket::get_proto() +{ + return NSAPI_TCP; +} + +int TCPSocket::connect(const SocketAddress &address) +{ + _lock.lock(); + int ret; + + if (!_socket) { + ret = NSAPI_ERROR_NO_SOCKET; + } else { + ret = _stack->socket_connect(_socket, address); + } + + _lock.unlock(); + return ret; +} + +int TCPSocket::connect(const char *host, uint16_t port) +{ + SocketAddress address(_stack, host, port); + if (!address) { + return NSAPI_ERROR_DNS_FAILURE; + } + + // 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; + + while (true) { + if (!_socket) { + ret = NSAPI_ERROR_NO_SOCKET; + break; + } + + _pending = 0; + int sent = _stack->socket_send(_socket, 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; + } + } + } + + _write_in_progress = false; + _lock.unlock(); + return ret; +} + +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; + + while (true) { + if (!_socket) { + ret = NSAPI_ERROR_NO_SOCKET; + break; + } + + _pending = 0; + int recv = _stack->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; + } + } + } + + _read_in_progress = false; + _lock.unlock(); + return ret; +} + +void TCPSocket::event() +{ + int32_t wcount = _write_sem.wait(0); + if (wcount <= 1) { + _write_sem.release(); + } + int32_t rcount = _read_sem.wait(0); + if (rcount <= 1) { + _read_sem.release(); + } + + _pending += 1; + if (_callback && _pending == 1) { + _callback(); + } +} diff --git a/features/net/network-socket/TCPSocket.h b/features/net/network-socket/TCPSocket.h new file mode 100644 index 0000000000..41d9ec0a40 --- /dev/null +++ b/features/net/network-socket/TCPSocket.h @@ -0,0 +1,124 @@ +/* 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 TCPSOCKET_H +#define TCPSOCKET_H + +#include "network-socket/Socket.h" +#include "network-socket/NetworkStack.h" +#include "network-socket/NetworkInterface.h" +#include "rtos/Semaphore.h" + + +/** TCP socket connection + */ +class TCPSocket : public Socket { +public: + /** Create an uninitialized socket + * + * Must call open to initialize the socket on a network stack. + */ + TCPSocket(); + + /** 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 + */ + template + TCPSocket(S *stack) + : _pending(0), _read_sem(0), _write_sem(0), + _read_in_progress(false), _write_in_progress(false) + { + open(stack); + } + + /** Destroy a socket + * + * Closes socket if the socket is still open + */ + virtual ~TCPSocket(); + + /** 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 Hostname 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 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 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 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); + +protected: + friend class TCPServer; + + virtual nsapi_protocol_t get_proto(); + virtual void event(); + + volatile unsigned _pending; + rtos::Semaphore _read_sem; + rtos::Semaphore _write_sem; + bool _read_in_progress; + bool _write_in_progress; +}; + + +#endif diff --git a/features/net/network-socket/UDPSocket.cpp b/features/net/network-socket/UDPSocket.cpp new file mode 100644 index 0000000000..dfee1fff6f --- /dev/null +++ b/features/net/network-socket/UDPSocket.cpp @@ -0,0 +1,151 @@ +/* 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" +#include "mbed_assert.h" + +UDPSocket::UDPSocket() + : _pending(0), _read_sem(0), _write_sem(0), + _read_in_progress(false), _write_in_progress(false) +{ +} + +UDPSocket::~UDPSocket() +{ + close(); +} + +nsapi_protocol_t UDPSocket::get_proto() +{ + return NSAPI_UDP; +} + +int UDPSocket::sendto(const char *host, uint16_t port, const void *data, unsigned size) +{ + SocketAddress address(_stack, host, port); + if (!address) { + return NSAPI_ERROR_DNS_FAILURE; + } + + // sendto is thread safe + 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; + + while (true) { + if (!_socket) { + ret = NSAPI_ERROR_NO_SOCKET; + break; + } + + _pending = 0; + int sent = _stack->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; + } + } + } + + _write_in_progress = false; + _lock.unlock(); + return ret; +} + +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; + + while (true) { + if (!_socket) { + ret = NSAPI_ERROR_NO_SOCKET; + break; + } + + _pending = 0; + int recv = _stack->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; + } + } + } + + _read_in_progress = false; + _lock.unlock(); + return ret; +} + +void UDPSocket::event() +{ + int32_t wcount = _write_sem.wait(0); + if (wcount <= 1) { + _write_sem.release(); + } + int32_t rcount = _read_sem.wait(0); + if (rcount <= 1) { + _read_sem.release(); + } + + _pending += 1; + if (_callback && _pending == 1) { + _callback(); + } +} diff --git a/features/net/network-socket/UDPSocket.h b/features/net/network-socket/UDPSocket.h new file mode 100644 index 0000000000..a44ec2fed7 --- /dev/null +++ b/features/net/network-socket/UDPSocket.h @@ -0,0 +1,122 @@ +/* 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 UDPSOCKET_H +#define UDPSOCKET_H + +#include "network-socket/Socket.h" +#include "network-socket/NetworkStack.h" +#include "network-socket/NetworkInterface.h" +#include "rtos/Semaphore.h" + + +/** UDP socket + */ +class UDPSocket : public Socket { +public: + /** Create an uninitialized socket + * + * Must call open to initialize the socket on a network stack. + */ + UDPSocket(); + + /** 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 + */ + template + UDPSocket(S *stack) + : _pending(0), _read_sem(0), _write_sem(0), + _read_in_progress(false), _write_in_progress(false) + { + open(stack); + } + + /** Destroy a socket + * + * Closes socket if the socket is still open + */ + virtual ~UDPSocket(); + + /** 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 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 + * @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 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 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 *data, unsigned size); + +protected: + virtual nsapi_protocol_t get_proto(); + virtual void event(); + + volatile unsigned _pending; + rtos::Semaphore _read_sem; + rtos::Semaphore _write_sem; + bool _read_in_progress; + bool _write_in_progress; +}; + + +#endif diff --git a/features/net/network-socket/WiFiInterface.h b/features/net/network-socket/WiFiInterface.h new file mode 100644 index 0000000000..dada562aec --- /dev/null +++ b/features/net/network-socket/WiFiInterface.h @@ -0,0 +1,70 @@ +/* WiFiInterface + * 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 "network-socket/NetworkInterface.h" + + +/** 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 */ + 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 + */ +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 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 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/features/net/network-socket/nsapi.h b/features/net/network-socket/nsapi.h new file mode 100644 index 0000000000..35fd3a1b9f --- /dev/null +++ b/features/net/network-socket/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 "network-socket/SocketAddress.h" +#include "network-socket/NetworkStack.h" + +#include "network-socket/NetworkInterface.h" +#include "network-socket/EthInterface.h" +#include "network-socket/WiFiInterface.h" +#include "network-socket/CellularInterface.h" +#include "network-socket/MeshInterface.h" + +#include "network-socket/Socket.h" +#include "network-socket/UDPSocket.h" +#include "network-socket/TCPSocket.h" +#include "network-socket/TCPServer.h" + +#endif + + +#endif diff --git a/features/net/network-socket/nsapi_types.h b/features/net/network-socket/nsapi_types.h new file mode 100644 index 0000000000..176e42f35c --- /dev/null +++ b/features/net/network-socket/nsapi_types.h @@ -0,0 +1,445 @@ +/* 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; + +/** 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; + + +/** Opaque handle for network sockets + */ +typedef void *nsapi_socket_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; + + +/** 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 + +#endif