Mbed library for ENC28J60 Ethernet modules. Full support for TCP/IP and UDP Server, Client and HTTP server (webserver). DHCP and DNS is included.

Dependents:   mBuino_ENC28_MQTT Nucleo_Web_ENC28J60 Nucleo_Web_ENC28J60_ADC Serial_over_Ethernet ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers UdpSocket.cpp Source File

UdpSocket.cpp

00001 /*
00002  UIPUdp.cpp - Arduino implementation of a UIP wrapper class.
00003  Copyright (c) 2013 Norbert Truchsess <norbert.truchsess@t-online.de>
00004  All rights reserved.
00005 
00006  This program is free software: you can redistribute it and/or modify
00007  it under the terms of the GNU General Public License as published by
00008  the Free Software Foundation, either version 3 of the License, or
00009  (at your option) any later version.
00010 
00011  This program is distributed in the hope that it will be useful,
00012  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  GNU General Public License for more details.
00015 
00016  You should have received a copy of the GNU General Public License
00017  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00018  */
00019 #include "UipEthernet.h"
00020 #include "UdpSocket.h"
00021 #include "DnsClient.h"
00022 
00023 extern "C"
00024 {
00025 #include "utility/uip-conf.h"
00026 #include "utility/uip.h"
00027 #include "utility/uip_arp.h"
00028 }
00029 #if UIP_UDP
00030 #define UIP_ARPHDRSIZE  42
00031 #define UDPBUF          ((struct uip_udpip_hdr*) &uip_buf[UIP_LLH_LEN])
00032 
00033 /**
00034  * @brief
00035  * @note
00036  * @param
00037  * @retval
00038  */
00039 UdpSocket::UdpSocket() :
00040     _uip_udp_conn(NULL),
00041     _timeout_ms(1000)
00042 {
00043     memset(&appdata, 0, sizeof(appdata));
00044 }
00045 
00046 /**
00047  * @brief
00048  * @note
00049  * @param
00050  * @retval
00051  */
00052 UdpSocket::UdpSocket(int timeout_ms) :
00053     _uip_udp_conn(NULL),
00054     _timeout_ms(timeout_ms)
00055 {
00056     memset(&appdata, 0, sizeof(appdata));
00057 }
00058 
00059 /**
00060  * @brief
00061  * @note
00062  * @param
00063  * @retval
00064  */
00065 UdpSocket::UdpSocket(UipEthernet* net, int timeout_ms /*= 1000*/ ) :
00066     _uip_udp_conn(NULL),
00067     _timeout_ms(timeout_ms)
00068 {
00069     memset(&appdata, 0, sizeof(appdata));
00070     if (UipEthernet::ethernet != net)
00071         UipEthernet::ethernet = net;
00072 }
00073 
00074 // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use
00075 uint8_t UdpSocket::begin(uint16_t port)
00076 {
00077     if (!_uip_udp_conn) {
00078         _uip_udp_conn = uip_udp_new(NULL, 0);
00079     }
00080 
00081     if (_uip_udp_conn) {
00082         uip_udp_bind(_uip_udp_conn, htons(port));
00083         _uip_udp_conn->appstate = &appdata;
00084         return 1;
00085     }
00086 
00087     return 0;
00088 }
00089 
00090 // Finish with the UDP socket
00091 void UdpSocket::stop()
00092 {
00093     if (_uip_udp_conn) {
00094         uip_udp_remove(_uip_udp_conn);
00095         _uip_udp_conn->appstate = NULL;
00096         _uip_udp_conn = NULL;
00097         UipEthernet::ethernet->enc28j60Eth.freeBlock(appdata.packet_in);
00098         UipEthernet::ethernet->enc28j60Eth.freeBlock(appdata.packet_next);
00099         UipEthernet::ethernet->enc28j60Eth.freeBlock(appdata.packet_out);
00100         memset(&appdata, 0, sizeof(appdata));
00101     }
00102 }
00103 
00104 /**
00105  * @brief
00106  * @note
00107  * @param
00108  * @retval
00109  */
00110 void UdpSocket::close()
00111 {
00112     stop();
00113 }
00114 
00115 // Sending UDP packets
00116 // Start building up a packet to send to the remote host specific in ip and port
00117 
00118 // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port
00119 int UdpSocket::beginPacket(IpAddress ip, uint16_t port)
00120 {
00121     UipEthernet::ethernet->tick();
00122     if (ip && port) {
00123         uip_ipaddr_t    ripaddr;
00124         uip_ip_addr(&ripaddr, ip);
00125 #ifdef UIPETHERNET_DEBUG_UDP
00126         printf("udp beginPacket, ");
00127 #endif
00128         if (_uip_udp_conn) {
00129             _uip_udp_conn->rport = htons(port);
00130             uip_ipaddr_copy(_uip_udp_conn->ripaddr, &ripaddr);
00131         }
00132         else {
00133             _uip_udp_conn = uip_udp_new(&ripaddr, htons(port));
00134             if (_uip_udp_conn)
00135             {
00136 #ifdef UIPETHERNET_DEBUG_UDP
00137                 printf("new connection, ");
00138 #endif
00139                 _uip_udp_conn->appstate = &appdata;
00140             }
00141             else
00142             {
00143 #ifdef UIPETHERNET_DEBUG_UDP
00144                 printf("failed to allocate new connection\r\n");
00145 #endif
00146                 return 0;
00147             }
00148         }
00149 
00150 #ifdef UIPETHERNET_DEBUG_UDP
00151         char buf[16];
00152         printf("rip: %s, port: %d\r\n", ip.toString(buf), port);
00153 #endif
00154     }
00155 
00156     if (_uip_udp_conn) {
00157         if (appdata.packet_out == NOBLOCK) {
00158             appdata.packet_out = UipEthernet::ethernet->enc28j60Eth.allocBlock(UIP_UDP_MAXPACKETSIZE);
00159             appdata.out_pos = UIP_UDP_PHYH_LEN;
00160             if (appdata.packet_out != NOBLOCK) {
00161                 return 1;
00162             }
00163 #ifdef UIPETHERNET_DEBUG_UDP
00164             else {
00165                 printf("failed to allocate memory for packet\r\n");
00166             }
00167 #endif
00168         }
00169 
00170 #ifdef UIPETHERNET_DEBUG_UDP
00171         else {
00172             printf("previous packet on that connection not sent yet\r\n");
00173         }
00174 #endif
00175     }
00176 
00177     return 0;
00178 }
00179 
00180 // Start building up a packet to send to the remote host specific in host and port
00181 
00182 // Returns 1 if successful, 0 if there was a problem resolving the hostname or port
00183 int UdpSocket::beginPacket(const char* host, uint16_t port)
00184 {
00185     // Look up the host first
00186     int         ret = 0;
00187     DnsClient   dns;
00188     IpAddress   remote_addr;
00189 
00190     dns.begin(UipEthernet::ethernet->dnsServerIP());
00191     ret = dns.getHostByName(host, remote_addr);
00192     if (ret == 1) {
00193         return beginPacket(remote_addr, port);
00194     }
00195     else {
00196         return ret;
00197     }
00198 }
00199 
00200 // Finish off this packet and send it
00201 
00202 // Returns 1 if the packet was sent successfully, 0 if there was an error
00203 int UdpSocket::endPacket()
00204 {
00205     if (_uip_udp_conn && appdata.packet_out != NOBLOCK) {
00206         appdata.send = true;
00207         UipEthernet::ethernet->enc28j60Eth.resizeBlock(appdata.packet_out, 0, appdata.out_pos);
00208         uip_udp_periodic_conn(_uip_udp_conn);
00209         if (uip_len > 0) {
00210             _send(&appdata);
00211             return 1;
00212         }
00213     }
00214 
00215     return 0;
00216 }
00217 
00218 // Write a single byte into the packet
00219 size_t UdpSocket::write(uint8_t c)
00220 {
00221     return write(&c, 1);
00222 }
00223 
00224 // Write size bytes from buffer into the packet
00225 size_t UdpSocket::write(const uint8_t* buffer, size_t size)
00226 {
00227     if (appdata.packet_out != NOBLOCK) {
00228         size_t  ret = UipEthernet::ethernet->enc28j60Eth.writePacket
00229             (
00230                 appdata.packet_out,
00231                 appdata.out_pos,
00232                 (uint8_t*)buffer,
00233                 size
00234             );
00235         appdata.out_pos += ret;
00236         return ret;
00237     }
00238 
00239     return 0;
00240 }
00241 
00242 // Start processing the next available incoming packet
00243 
00244 // Returns the size of the packet in bytes, or 0 if no packets are available
00245 int UdpSocket::parsePacket()
00246 {
00247     UipEthernet::ethernet->tick();
00248 #ifdef UIPETHERNET_DEBUG_UDP
00249     if (appdata.packet_in != NOBLOCK) {
00250         printf("udp parsePacket freeing previous packet: %d\r\n", appdata.packet_in);
00251     }
00252 #endif
00253     UipEthernet::ethernet->enc28j60Eth.freeBlock(appdata.packet_in);
00254 
00255     appdata.packet_in = appdata.packet_next;
00256     appdata.packet_next = NOBLOCK;
00257 
00258 #ifdef UIPETHERNET_DEBUG_UDP
00259     if (appdata.packet_in != NOBLOCK) {
00260         printf("udp parsePacket received packet: %d", appdata.packet_in);
00261     }
00262 #endif
00263 
00264     int size = UipEthernet::ethernet->enc28j60Eth.blockSize(appdata.packet_in);
00265 #ifdef UIPETHERNET_DEBUG_UDP
00266     if (appdata.packet_in != NOBLOCK) {
00267         printf(", size: %d\r\n", size);
00268     }
00269 #endif
00270     return size;
00271 }
00272 
00273 // Number of bytes remaining in the current packet
00274 size_t UdpSocket::available()
00275 {
00276     UipEthernet::ethernet->tick();
00277     return UipEthernet::ethernet->enc28j60Eth.blockSize(appdata.packet_in);
00278 }
00279 
00280 // Read a single byte from the current packet. Returns -1 if no byte is available.
00281 int UdpSocket::read()
00282 {
00283     static unsigned char    c;
00284     if (read(&c, 1) > 0) {
00285         return c;
00286     }
00287 
00288     return -1;
00289 }
00290 
00291 // Read up to len bytes from the current packet and place them into buffer
00292 
00293 // Returns the number of bytes read, or 0 if none are available
00294 size_t UdpSocket::read(unsigned char* buffer, size_t len)
00295 {
00296     UipEthernet::ethernet->tick();
00297     if (appdata.packet_in != NOBLOCK) {
00298         memaddress  read = UipEthernet::ethernet->enc28j60Eth.readPacket(appdata.packet_in, 0, buffer, len);
00299         if (read == UipEthernet::ethernet->enc28j60Eth.blockSize(appdata.packet_in)) {
00300             UipEthernet::ethernet->enc28j60Eth.freeBlock(appdata.packet_in);
00301             appdata.packet_in = NOBLOCK;
00302         }
00303         else
00304             UipEthernet::ethernet->enc28j60Eth.resizeBlock(appdata.packet_in, read);
00305         return read;
00306     }
00307 
00308     return 0;
00309 }
00310 
00311 // Return the next byte from the current packet without moving on to the next byte
00312 int UdpSocket::peek()
00313 {
00314     UipEthernet::ethernet->tick();
00315     if (appdata.packet_in != NOBLOCK) {
00316         unsigned char   c;
00317         if (UipEthernet::ethernet->enc28j60Eth.readPacket(appdata.packet_in, 0, &c, 1) == 1)
00318             return c;
00319     }
00320 
00321     return -1;
00322 }
00323 
00324 // Finish reading the current packet
00325 void UdpSocket::flush()
00326 {
00327     UipEthernet::ethernet->tick();
00328     UipEthernet::ethernet->enc28j60Eth.freeBlock(appdata.packet_in);
00329     appdata.packet_in = NOBLOCK;
00330 }
00331 
00332 // Return the IP address of the host who sent the current incoming packet
00333 IpAddress UdpSocket::remoteIP()
00334 {
00335     return _uip_udp_conn ? ip_addr_uip(_uip_udp_conn->ripaddr) : IpAddress();
00336 }
00337 
00338 // Return the port of the host who sent the current incoming packet
00339 uint16_t UdpSocket::remotePort()
00340 {
00341     return _uip_udp_conn ? ntohs(_uip_udp_conn->rport) : 0;
00342 }
00343 
00344 // UIP callback function
00345 void uipudp_appcall()
00346 {
00347     if (uip_udp_userdata_t * data = (uip_udp_userdata_t *) (uip_udp_conn->appstate)) {
00348         if (uip_newdata()) {
00349             if (data->packet_next == NOBLOCK) {
00350                 uip_udp_conn->rport = UDPBUF->srcport;
00351                 uip_ipaddr_copy(uip_udp_conn->ripaddr, UDPBUF->srcipaddr);
00352                 data->packet_next = UipEthernet::ethernet->enc28j60Eth.allocBlock(ntohs(UDPBUF->udplen) - UIP_UDPH_LEN);
00353 
00354                 //if we are unable to allocate memory the packet is dropped. udp doesn't guarantee packet delivery
00355                 if (data->packet_next != NOBLOCK) {
00356                     //discard Linklevel and IP and udp-header and any trailing bytes:
00357                     UipEthernet::ethernet->enc28j60Eth.copyPacket
00358                         (
00359                             data->packet_next,
00360                             0,
00361                             UipEthernet::inPacket,
00362                             UIP_UDP_PHYH_LEN,
00363                             UipEthernet::ethernet->enc28j60Eth.blockSize(data->packet_next)
00364                         );
00365 #ifdef UIPETHERNET_DEBUG_UDP
00366                     printf
00367                     (
00368                         "udp, uip_newdata received packet: %d, size: %d\r\n",
00369                         data->packet_next,
00370                         UipEthernet::ethernet->enc28j60Eth.blockSize(data->packet_next)
00371                     );
00372 #endif
00373                 }
00374             }
00375         }
00376 
00377         if (uip_poll() && data->send)
00378         {
00379             //set uip_slen (uip private) by calling uip_udp_send
00380 #ifdef UIPETHERNET_DEBUG_UDP
00381             printf
00382             (
00383                 "udp, uip_poll preparing packet to send: %d, size: %d\r\n",
00384                 data->packet_out,
00385                 UipEthernet::ethernet->enc28j60Eth.blockSize(data->packet_out)
00386             );
00387 #endif
00388             UipEthernet::uipPacket = data->packet_out;
00389             UipEthernet::uipHeaderLen = UIP_UDP_PHYH_LEN;
00390             uip_udp_send(data->out_pos - (UIP_UDP_PHYH_LEN));
00391         }
00392     }
00393 }
00394 
00395 /**
00396  * @brief
00397  * @note
00398  * @param
00399  * @retval
00400  */
00401 void UdpSocket::_send(uip_udp_userdata_t* data)
00402 {
00403     uip_arp_out();  //add arp
00404     if (uip_len == UIP_ARPHDRSIZE) {
00405         UipEthernet::uipPacket = NOBLOCK;
00406         UipEthernet::packetState &= ~UIPETHERNET_SENDPACKET;
00407 #ifdef UIPETHERNET_DEBUG_UDP
00408         printf("udp, uip_poll results in ARP-packet\r\n");
00409 #endif
00410     }
00411     else {
00412         //arp found ethaddr for ip (otherwise packet is replaced by arp-request)
00413         data->send = false;
00414         data->packet_out = NOBLOCK;
00415         UipEthernet::packetState |= UIPETHERNET_SENDPACKET;
00416 #ifdef UIPETHERNET_DEBUG_UDP
00417         printf("udp, uip_packet to send: %d\r\n", UipEthernet::uipPacket);
00418 #endif
00419     }
00420 
00421     UipEthernet::ethernet->network_send();
00422 }
00423 #endif
00424 
00425 /**
00426  * @brief
00427  * @note
00428  * @param
00429  * @retval
00430  */
00431 nsapi_size_or_error_t UdpSocket::sendto(const char* host, uint16_t port, const void* data, size_t size)
00432 {
00433     DnsClient   dns;
00434     IpAddress   address;
00435     uint32_t    address_bytes;
00436 
00437     dns.begin(UipEthernet::ethernet->dnsServerIP());
00438     if (dns.getHostByName(host, address) == 1) {
00439         address_bytes = address;
00440         _remote_addr = SocketAddress(&address_bytes, NSAPI_IPv4 , port);
00441     }
00442     else {
00443         _remote_addr = SocketAddress(host, port);
00444     }
00445 
00446     if (beginPacket(host, port) == 0) {
00447         stop();
00448         return NSAPI_ERROR_NO_ADDRESS ;
00449     }
00450 
00451     if (write((uint8_t*)data, size) == 0) {
00452         stop();
00453         return NSAPI_ERROR_WOULD_BLOCK ;
00454     };
00455 
00456     if (endPacket() <= 0) {
00457         stop();
00458         return NSAPI_ERROR_WOULD_BLOCK ;
00459     }
00460 
00461     return NSAPI_ERROR_OK ;
00462 }
00463 
00464 /**
00465  * @brief
00466  * @note
00467  * @param
00468  * @retval
00469  */
00470 nsapi_size_or_error_t UdpSocket::sendto(const SocketAddress& address, const void* data, size_t size)
00471 {
00472     IpAddress   ip_addr(address.get_addr().bytes);
00473     uint16_t    port = address.get_port();
00474 
00475     _remote_addr = address;
00476 
00477     if (beginPacket(ip_addr, port) == 0) {
00478         stop();
00479         return NSAPI_ERROR_NO_ADDRESS ;
00480     }
00481 
00482     if (write((uint8_t*)data, size) == 0) {
00483         stop();
00484         return NSAPI_ERROR_WOULD_BLOCK ;
00485     };
00486 
00487     if (endPacket() <= 0) {
00488         stop();
00489         return NSAPI_ERROR_WOULD_BLOCK ;
00490     }
00491 
00492     return NSAPI_ERROR_OK ;
00493 }
00494 
00495 /**
00496  * @brief
00497  * @note
00498  * @param
00499  * @retval
00500  */
00501 nsapi_size_or_error_t UdpSocket::recvfrom(SocketAddress* address, void* data, size_t size)
00502 {
00503     *address = _remote_addr;
00504 
00505     Timer   timer;
00506     int     success;
00507 
00508     timer.start();
00509     do {
00510         success = parsePacket();
00511     } while (!success && (timer.read_ms() < _timeout_ms));
00512 
00513     if (!success) {
00514         stop();
00515         return NSAPI_ERROR_WOULD_BLOCK ;
00516     }
00517 
00518     size_t      n;
00519     size_t      recv_count = 0;
00520     uint8_t*    pos = (uint8_t*)data;
00521 
00522     do {
00523         if (recv_count + available() <= size) {
00524             n = read(pos, available());
00525             pos += n;
00526             recv_count += n;
00527         }
00528         else {
00529             return NSAPI_ERROR_NO_MEMORY ;
00530         }
00531     } while ((available() > 0) && (recv_count < size));
00532 
00533     flush();
00534 
00535     return NSAPI_ERROR_OK ;
00536 }