/* * Copyright (c) 2001, Swedish Institute of Computer Science. * 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 Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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. * * This file is part of the lwIP TCP/IP stack. * * Author: Adam Dunkels <adam@sics.se> * * $Id: sockets.c 54 2016-01-11 01:29:55Z reddawg $ */ #include <ubixos/types.h> #include "net/debug.h" #include "net/api.h" #include "net/sockets.h" #define NUM_SOCKETS 10 struct lwip_socket { struct netconn *conn; struct netbuf *lastdata; uInt16 lastoffset; }; static struct lwip_socket sockets[NUM_SOCKETS]; /*-----------------------------------------------------------------------------------*/ static struct lwip_socket * get_socket(int s) { struct lwip_socket *sock; if(s > NUM_SOCKETS) { /* errno = EBADF; */ return NULL; } sock = &sockets[s]; if(sock->conn == NULL) { /* errno = EBADF; */ return NULL; } return sock; } /*-----------------------------------------------------------------------------------*/ static int alloc_socket(struct netconn *newconn) { int i; /* allocate a new socket identifier */ for(i = 0; i < NUM_SOCKETS; ++i) { if(sockets[i].conn == NULL) { sockets[i].conn = newconn; sockets[i].lastdata = NULL; sockets[i].lastoffset = 0; return i; } } return -1; } /*-----------------------------------------------------------------------------------*/ int lwip_accept(int s, struct sockaddr *addr, int *addrlen) { struct lwip_socket *sock; struct netconn *newconn; struct ip_addr *naddr; uInt16 port; int newsock; sock = get_socket(s); if(sock == NULL) { return -1; } newconn = netconn_accept(sock->conn); /* get the IP address and port of the remote host */ netconn_peer(newconn, &naddr, &port); ((struct sockaddr_in *)addr)->sin_addr.s_addr = naddr->addr; ((struct sockaddr_in *)addr)->sin_port = port; newsock = alloc_socket(newconn); if(newsock == -1) { netconn_delete(newconn); /* errno = ENOBUFS; */ } return newsock; } /*-----------------------------------------------------------------------------------*/ int lwip_bind(int s, struct sockaddr *name, int namelen) { struct lwip_socket *sock; struct ip_addr remote_addr; uInt16 remote_port; err_t err; sock = get_socket(s); if(sock == NULL) { return -1; } remote_addr.addr = ((struct sockaddr_in *)name)->sin_addr.s_addr; remote_port = ((struct sockaddr_in *)name)->sin_port; err = netconn_bind(sock->conn, &remote_addr, ntohs(remote_port)); if(err != ERR_OK) { /* errno = ... */ return -1; } return 0; } /*-----------------------------------------------------------------------------------*/ int lwip_close(int s) { struct lwip_socket *sock; DEBUGF(SOCKETS_DEBUG, ("close: socket %d\n", s)); sock = get_socket(s); if(sock == NULL) { return -1; } netconn_delete(sock->conn); if(sock->lastdata != NULL) { netbuf_delete(sock->lastdata); } sock->lastdata = NULL; sock->lastoffset = 0; sock->conn = NULL; return 0; } /*-----------------------------------------------------------------------------------*/ int lwip_connect(int s, struct sockaddr *name, int namelen) { struct lwip_socket *sock; struct ip_addr remote_addr; uInt16 remote_port; err_t err; sock = get_socket(s); if(sock == NULL) { return -1; } remote_addr.addr = ((struct sockaddr_in *)name)->sin_addr.s_addr; remote_port = ((struct sockaddr_in *)name)->sin_port; err = netconn_connect(sock->conn, &remote_addr, ntohs(remote_port)); if(err != ERR_OK) { /* errno = ... */ return -1; } return 0; } /*-----------------------------------------------------------------------------------*/ int lwip_listen(int s, int backlog) { struct lwip_socket *sock; err_t err; sock = get_socket(s); if(sock == NULL) { return -1; } err = netconn_listen(sock->conn); if(err != ERR_OK) { /* errno = ... */ return -1; } return 0; } /*-----------------------------------------------------------------------------------*/ int lwip_recvfrom(int s, void *mem, int len, unsigned int flags, struct sockaddr *from, int *fromlen) { struct lwip_socket *sock; struct netbuf *buf; uInt16 buflen, copylen; struct ip_addr *addr; uInt16 port; sock = get_socket(s); if(sock == NULL) { return -1; } /* Check if there is data left from the last recv operation. */ if(sock->lastdata != NULL) { buf = sock->lastdata; } else { /* No data was left from the previous operation, so we try to get some from the network. */ buf = netconn_recv(sock->conn); if(buf == NULL) { /* We should really do some error checking here. */ return 0; } } buflen = netbuf_len(buf); buflen -= sock->lastoffset; if(len > buflen) { copylen = buflen; } else { copylen = len; } /* copy the contents of the received buffer into the supplied memory pointer mem */ netbuf_copy_partial(buf, mem, copylen, sock->lastoffset); /* If this is a TCP socket, check if there is data left in the buffer. If so, it should be saved in the sock structure for next time around. */ if(netconn_type(sock->conn) == NETCONN_TCP && buflen - copylen > 0) { sock->lastdata = buf; sock->lastoffset = buflen - copylen; } else { sock->lastdata = NULL; sock->lastoffset = 0; netbuf_delete(buf); } /* Check to see from where the data was. */ if(from != NULL && fromlen != NULL) { addr = netbuf_fromaddr(buf); port = htons(netbuf_fromport(buf)); ((struct sockaddr_in *)from)->sin_addr.s_addr = addr->addr; ((struct sockaddr_in *)from)->sin_port = port; *fromlen = sizeof(struct sockaddr_in); } /* if the length of the received data is larger than len, this data is discarded and we return len. otherwise we return the actual length of the received data */ if(len > copylen) { return copylen; } else { return len; } } /*-----------------------------------------------------------------------------------*/ int lwip_read(int s, void *mem, int len) { return lwip_recv(s, mem, len, 0); } /*-----------------------------------------------------------------------------------*/ int lwip_recv(int s, void *mem, int len, unsigned int flags) { return lwip_recvfrom(s, mem, len, flags, NULL, NULL); } /*-----------------------------------------------------------------------------------*/ int lwip_send(int s, void *data, int size, unsigned int flags) { struct lwip_socket *sock; struct netbuf *buf; err_t err; DEBUGF(SOCKETS_DEBUG, ("send: socket %d, size %d\n", s, size)); sock = get_socket(s); if(sock == NULL) { return -1; } switch(netconn_type(sock->conn)) { case NETCONN_UDP: /* create a buffer */ buf = netbuf_new(); if(buf == NULL) { /* errno = ENOBUFS; */ return -1; } /* make the buffer point to the data that should be sent */ netbuf_ref(buf, data, size); /* send the data */ err = netconn_send(sock->conn, buf); /* deallocated the buffer */ netbuf_delete(buf); break; case NETCONN_TCP: err = netconn_write(sock->conn, data, size, NETCONN_COPY); break; default: err = ERR_ARG; break; } if(err != ERR_OK) { /* errno = ... */ return -1; } return size; } /*-----------------------------------------------------------------------------------*/ int lwip_sendto(int s, void *data, int size, unsigned int flags, struct sockaddr *to, int tolen) { struct lwip_socket *sock; struct ip_addr remote_addr, *addr; uInt16 remote_port, port; int ret; sock = get_socket(s); if(sock == NULL) { return -1; } /* get the peer if currently connected */ netconn_peer(sock->conn, &addr, &port); remote_addr.addr = ((struct sockaddr_in *)to)->sin_addr.s_addr; remote_port = ((struct sockaddr_in *)to)->sin_port; netconn_connect(sock->conn, &remote_addr, remote_port); ret = lwip_send(s, data, size, flags); /* reset the remote address and port number of the connection */ netconn_connect(sock->conn, addr, port); return ret; } /*-----------------------------------------------------------------------------------*/ int lwip_socket(int domain, int type, int protocol) { struct netconn *conn; int i; /* create a netconn */ switch(type) { case SOCK_DGRAM: conn = netconn_new(NETCONN_UDP); break; case SOCK_STREAM: conn = netconn_new(NETCONN_TCP); break; default: /* errno = ... */ return -1; } if(conn == NULL) { DEBUGF(SOCKETS_DEBUG, ("socket: could not create netconn.\n")); /* errno = ENOBUFS; */ return -1; } i = alloc_socket(conn); if(i == -1) { /* errno = ENOBUFS; */ netconn_delete(conn); } return i; } /*-----------------------------------------------------------------------------------*/ int lwip_write(int s, void *data, int size) { struct lwip_socket *sock; err_t err; DEBUGF(SOCKETS_DEBUG, ("write: socket %d, size %d\n", s, size)); sock = get_socket(s); if(sock == NULL) { return -1; } switch(netconn_type(sock->conn)) { case NETCONN_UDP: return lwip_send(s, data, size, 0); case NETCONN_TCP: err = netconn_write(sock->conn, data, size, NETCONN_COPY); break; default: err = ERR_ARG; break; } if(err != ERR_OK) { /* errno = ... */ return -1; } return size; } /*-----------------------------------------------------------------------------------*/