| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- /*
- * Copyright (C) 2001-2005 by egnite Software GmbH. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the copyright holders nor the names of
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
- * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * For additional information see http://www.ethernut.de/
- *
- */
- /*
- * $Log$
- * Revision 1.6 2009/02/13 14:52:05 haraldkipp
- * Include memdebug.h for heap management debugging support.
- *
- * Revision 1.5 2009/02/06 15:37:40 haraldkipp
- * Added stack space multiplier and addend. Adjusted stack space.
- *
- * Revision 1.4 2009/01/17 11:26:52 haraldkipp
- * Getting rid of two remaining BSD types in favor of stdint.
- * Replaced 'u_int' by 'unsinged int' and 'uptr_t' by 'uintptr_t'.
- *
- * Revision 1.3 2009/01/16 17:03:50 haraldkipp
- * Configurable discovery protocol version and port plus
- * configurable service thread stack size. The new version 1.1
- * allows host names up to 98 characters. Added some code
- * to make sure, that nothing is overwritten with version 1.0
- * protocol and too long host names. Protocol version 1.0
- * is still the default.
- *
- * Revision 1.2 2008/08/11 07:00:35 haraldkipp
- * BSD types replaced by stdint types (feature request #1282721).
- *
- * Revision 1.1 2006/09/07 09:06:17 haraldkipp
- * Discovery service added.
- *
- */
- #include <sys/confnet.h>
- #include <sys/confos.h>
- #include <sys/thread.h>
- #include <sys/timer.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <net/if_var.h>
- #include <stdlib.h>
- #include <string.h>
- #include <memdebug.h>
- #include <pro/discover.h>
- /*!
- * \addtogroup xgDiscover
- */
- /*@{*/
- #ifndef NUT_THREAD_DISTSTACK
- #if defined(__AVR__)
- #if defined(__GNUC__)
- /* avr-gcc size optimized code uses 124 bytes. */
- #define NUT_THREAD_DISTSTACK 224
- #else
- /* icc-avr v7.19 uses 244 bytes. */
- #define NUT_THREAD_DISTSTACK 384
- #endif
- #elif defined(__AVR32__)
- #define NUT_THREAD_DISTSTACK 512
- #else
- /* arm-elf-gcc used 232 bytes with size optimized, 476 bytes with debug code. */
- #define NUT_THREAD_DISTSTACK 320
- #endif
- #endif
- #ifndef DISCOVERY_PORT
- #define DISCOVERY_PORT 9806
- #endif
- typedef struct {
- uint32_t disopt_ipmask;
- uint16_t disopt_port;
- unsigned int disopt_flags;
- } DISCOVERY_OPTIONS;
- static DISCOVERY_OPTIONS disopt;
- static uint32_t xid;
- static int NutDiscoveryHandler(uint32_t ip, uint16_t port, DISCOVERY_TELE * dist, int len);
- static NutDiscoveryCallback discovery_callback = NutDiscoveryHandler;
- /*!
- * \brief Create an announcement datagram.
- *
- * \param dist Pointer to the datagram buffer that will be filled.
- *
- * \return Number of bytes filled into the datagram buffer.
- */
- int NutDiscoveryAnnTele(DISCOVERY_TELE * dist)
- {
- memset(dist, 0, sizeof(DISCOVERY_TELE));
- dist->dist_xid = xid;
- dist->dist_type = DIST_ANNOUNCE;
- dist->dist_ver = DISCOVERY_VERSION;
- memcpy(dist->dist_mac, confnet.cdn_mac, sizeof(dist->dist_mac));
- dist->dist_ip_addr = confnet.cdn_ip_addr;
- dist->dist_ip_mask = confnet.cdn_ip_mask;
- dist->dist_gateway = confnet.cdn_gateway;
- dist->dist_cip_addr = confnet.cdn_cip_addr;
- #if DISCOVERY_VERSION <= 0x10
- memcpy(dist->dist_hostname, confos.hostname, sizeof(dist->dist_hostname));
- return sizeof(DISCOVERY_TELE) - sizeof(dist->dist_custom);
- #else
- dist->dist_appendix[0] = (uint8_t)strlen(confos.hostname);
- memcpy(&dist->dist_appendix[1], confos.hostname, dist->dist_appendix[0]);
- return sizeof(DISCOVERY_TELE) - sizeof(dist->dist_appendix) + dist->dist_appendix[0] + 1;
- #endif
- }
- /*!
- * \brief Apply new configuration.
- *
- * \param dist Pointer to the discovery datagram. It is assumed, that
- * the validity of the datagram contents had been checked
- * by the caller.
- */
- int NutDiscoveryAppConf(DISCOVERY_TELE * dist)
- {
- memset(confos.hostname, 0, sizeof(confos.hostname));
- #if DISCOVERY_VERSION <= 0x10
- strncpy(confos.hostname, (char *)dist->dist_hostname, sizeof(confos.hostname) - 1);
- #else
- memcpy(confos.hostname, &dist->dist_appendix[1], dist->dist_appendix[0]);
- #endif
- NutSaveConfig();
- memcpy(confnet.cdn_mac, dist->dist_mac, sizeof(confnet.cdn_mac));
- confnet.cdn_ip_addr = dist->dist_ip_addr;
- confnet.cdn_ip_mask = dist->dist_ip_mask;
- confnet.cdn_gateway = dist->dist_gateway;
- confnet.cdn_cip_addr = dist->dist_cip_addr;
- return NutNetSaveConfig();
- }
- /*
- * \brief Default discovery datagram handler.
- *
- * \param ip Sender's IP address.
- * \param port Sender's UDP port.
- * \param dtel Pointer to the UDP telegram buffer.
- * \param len UDP telegram size.
- */
- static int NutDiscoveryHandler(uint32_t ip, uint16_t port, DISCOVERY_TELE * dist, int len)
- {
- int rc = -1;
- #if DISCOVERY_VERSION <= 0x10
- size_t minlen = sizeof(DISCOVERY_TELE) - sizeof(dist->dist_custom);
- #else
- size_t minlen = sizeof(DISCOVERY_TELE) - sizeof(dist->dist_appendix) + 1;
- #endif
- /* Check minimum datagram length. */
- if (len >= minlen) {
- /*
- * Request telegram.
- */
- if (dist->dist_type == DIST_REQUEST) {
- /* Respond to requests. */
- rc = NutDiscoveryAnnTele(dist);
- }
- /*
- * Apply telegram.
- */
- else if (dist->dist_type == DIST_APPLY
- /* Check exchange ID. */
- && dist->dist_xid == xid
- /* Required protocol version. */
- && dist->dist_ver == DISCOVERY_VERSION) {
- xid += NutGetTickCount();
- /* Store configuration. */
- rc = NutDiscoveryAppConf(dist);
- }
- }
- return rc;
- }
- THREAD(DiscoveryResponder, arg)
- {
- UDPSOCKET *sock;
- DISCOVERY_TELE *dist;
- uint32_t raddr;
- uint16_t rport;
- int len;
- /* Insist on allocating a datagram buffer. */
- while ((dist = malloc(sizeof(DISCOVERY_TELE))) == NULL) {
- NutSleep(1000);
- }
- /* Insist on creating a socket. */
- while ((sock = NutUdpCreateSocket(disopt.disopt_port)) == NULL) {
- NutSleep(1000);
- }
- /* Nut/Net doesn't provide UDP datagram buffering by default. */
- {
- uint16_t max_ms = sizeof(DISCOVERY_TELE) * 3;
- NutUdpSetSockOpt(sock, SO_RCVBUF, &max_ms, sizeof(max_ms));
- }
- /* Create a pseudo random telegram identifier. */
- memcpy(&xid, &confnet.cdn_mac[2], sizeof(xid));
- xid += NutGetTickCount();
- /* Optionally send initial announce datagram. */
- if (disopt.disopt_flags & DISF_INITAL_ANN) {
- /* Variable sleep avoids broadcast storms in large installations. */
- NutSleep(500 + (xid & 0x7FF));
- if ((len = NutDiscoveryAnnTele(dist)) > 0) {
- NutUdpSendTo(sock, INADDR_BROADCAST, disopt.disopt_port, dist, len);
- }
- }
- /* Handle discovery telegrams in an endless loop. */
- for (;;) {
- len = NutUdpReceiveFrom(sock, &raddr, &rport, dist, sizeof(DISCOVERY_TELE), 0);
- /* Updates may be filtered by an IP mask. */
- if ((raddr & disopt.disopt_ipmask) == raddr && len > 0 && discovery_callback) {
- if ((len = discovery_callback(raddr, rport, dist, len)) > 0) {
- /* Broadcast response if destination is unreachable. */
- if ((raddr & confnet.cdn_ip_mask) != (confnet.cdn_ip_addr & confnet.cdn_ip_mask)) {
- raddr = INADDR_BROADCAST;
- }
- NutUdpSendTo(sock, raddr, disopt.disopt_port, dist, len);
- }
- }
- /* Avoid system blocking by datagram storms. */
- NutSleep(100);
- }
- }
- /*!
- * \brief Register a custom discovery callback handler.
- *
- * When a callback had been registered, all datagrams matching the IP mask
- * are passed to this function, which should inspect the datagram contents
- * and take any required action like calling NutDiscoveryAnnTele() etc.
- * If the callback returns a positive value, a response will be sent
- * back, using that return value as its length.
- *
- * \param func Pointer to the callback function or NULL to disable
- * responses.
- *
- * \return Previously used callback function pointer.
- */
- NutDiscoveryCallback NutRegisterDiscoveryCallback(NutDiscoveryCallback func)
- {
- NutDiscoveryCallback rc = discovery_callback;
- discovery_callback = func;
- return rc;
- }
- /*!
- * \brief Register discovery telegram responder.
- *
- * The first call will activate the responder thread. Any subsequent
- * calls will return a failure.
- *
- * \note Enabling a discovery responder is a potential security hole.
- *
- * \param ipmask Update datagrams from remote hosts, which do not fit to
- * this mask are ignored. Set to INADDR_BROADCAST to allow
- * any. If zero, no updates are allowed.
- * \param port The responder will listen to this UDP port. If zero,
- * the default port \ref DISCOVERY_PORT is used.
- * \param flags Option value, may contain any combination of DISF_ flags.
- *
- * \return 0 if a handler thread had been started, -1 otherwise.
- */
- int NutRegisterDiscovery(uint32_t ipmask, uint16_t port, unsigned int flags)
- {
- static HANDLE tid = NULL;
- if (tid == NULL) {
- disopt.disopt_ipmask = ipmask;
- disopt.disopt_port = port ? port : DISCOVERY_PORT;
- disopt.disopt_flags = flags;
- tid = NutThreadCreate("udisc", DiscoveryResponder, NULL,
- (NUT_THREAD_DISTSTACK * NUT_THREAD_STACK_MULT) + NUT_THREAD_STACK_ADD);
- if (tid) {
- return 0;
- }
- }
- return -1;
- }
- /*@}*/
|