static int select_poll(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset) { int i, cnt, s; fd_set nr, nw; FD_ZERO(&nr); FD_ZERO(&nw); // Go through and look for FDs we need to service. cnt = 0; for (i=0; i<maxfdp1; i++) { if (readset && FD_ISSET(i, readset)) { // Convert to a socket # s = sock_for_fd(i); if (s < 0) continue; // Don't screw with things that are locked if (mutex_is_locked(fds[s].mutex)) continue; // If it's a listen socket, look for used connections. if (fds[s].conns) { if (fds[s].conncnt > 0) { FD_SET(i, &nr); cnt++; } } else { // Any data available? Or socket dead? if (fds[s].recv != 0) { FD_SET(i, &nr); cnt++; } } } if (writeset && FD_ISSET(i, writeset)) { // Convert to a socket # s = sock_for_fd(i); if (s < 0) continue; // Don't screw with things that are locked if (mutex_is_locked(fds[s].mutex)) continue; // Any buffer space available? Or socket dead? if (fds[s].send != 0) { FD_SET(i, &nw); cnt++; } } } if (cnt > 0) { if (readset) memcpy(readset, &nr, sizeof(fd_set)); if (writeset) memcpy(writeset, &nw, sizeof(fd_set)); } return cnt; }
int lwip_bind(int s, struct sockaddr *name, socklen_t namelen) { sockfd_t * fd; struct ip_addr ip; int port, rv = 0; s = sock_for_fd(s); if (s < 0) { errno = EBADF; return -1; } fd = fds + s; // Make sure it's an internet address we understand. if (namelen != sizeof(struct sockaddr_in)) { errno = ENAMETOOLONG; return -1; } // Get access mutex_lock(fd->mutex); // Copy it over memcpy(&fd->name, name, namelen); // Convert this to an lwIP-happy format ip.addr = ((struct sockaddr_in *)name)->sin_addr.s_addr; port = ((struct sockaddr_in *)name)->sin_port; // Are we TCP or UDP? switch (fd->type) { case SOCK_STREAM: fd->tcppcb = tcp_new(); tcp_arg(fd->tcppcb, (void *)s); tcp_recv(fd->tcppcb, recv_tcp); tcp_sent(fd->tcppcb, sent_tcp); tcp_poll(fd->tcppcb, poll_tcp, 4); // 4 == 4 TCP timer intervals tcp_err(fd->tcppcb, err_tcp); if (tcp_bind(fd->tcppcb, &ip, ntohs(port)) != ERR_OK) { if (tcp_close(fd->tcppcb) != ERR_OK) tcp_abort(fd->tcppcb); fd->tcppcb = NULL; errno = EINVAL; rv = -1; goto out; } break; case SOCK_DGRAM: fd->udppcb = udp_new(); udp_recv(fd->udppcb, recv_udp, (void *)s); udp_bind(fd->udppcb, &ip, ntohs(port)); break; default: assert( 0 ); errno = EINVAL; rv = -1; goto out; } out: mutex_unlock(fd->mutex); return rv; }
int lwip_listen(int s, int backlog) { struct tcp_pcb * npcb; sockfd_t * fd; int rv = 0, i; s = sock_for_fd(s); if (s < 0) { errno = EBADF; return -1; } fd = fds + s; // Get access mutex_lock(fd->mutex); // Is it the right type? if (fd->type != SOCK_STREAM) { errno = EOPNOTSUPP; rv = -1; goto out; } // Verify that someone has called bind() on this socket. if (!fd->tcppcb) { // XXX Is this the right error? errno = EOPNOTSUPP; rv = -1; goto out; } // Make sure we're not already listening. if (fd->conns) { errno = EINVAL; rv = -1; goto out; } // Setup a listen block. We get a new one back here. npcb = tcp_listen(fd->tcppcb); if (!npcb) { errno = EADDRINUSE; rv = -1; goto out; } // Set the new pcb fd->tcppcb = npcb; // Init the connection count fd->conncnt = 0; fd->connmax = backlog; fd->conns = malloc(backlog * sizeof(int)); for (i=0; i<backlog; i++) fd->conns[i] = -1; // Setup our accept callback tcp_accept(fd->tcppcb, accept_tcp); out: mutex_unlock(fd->mutex); return rv; }
int lwip_send(int s, const void *mem, int size, unsigned int flags) { s = sock_for_fd(s); if (s < 0) { errno = EBADF; return -1; } return send_common(s, mem, size, flags); }
int lwip_recv(int s, void *mem, int len, unsigned int flags) { s = sock_for_fd(s); if (s < 0) { errno = EBADF; return -1; } return recv_common(s, mem, len, flags, 1); }
static VALUE my_connect(VALUE klass, int io_wait, int domain, const void *addr, socklen_t addrlen) { int fd = my_socket(domain); if (connect(fd, addr, addrlen) < 0) { if (errno == EINPROGRESS) { VALUE io = sock_for_fd(klass, fd); if (io_wait) { errno = EAGAIN; (void)kgio_call_wait_writable(io); } return io; } close_fail(fd, "connect"); } return sock_for_fd(klass, fd); }
static VALUE my_connect(VALUE klass, int io_wait, int domain, void *addr, socklen_t addrlen) { int fd = socket(domain, MY_SOCK_STREAM, 0); if (fd == -1) { switch (errno) { case EMFILE: case ENFILE: #ifdef ENOBUFS case ENOBUFS: #endif /* ENOBUFS */ errno = 0; rb_gc(); fd = socket(domain, MY_SOCK_STREAM, 0); } if (fd == -1) rb_sys_fail("socket"); } #ifndef SOCK_NONBLOCK if (fcntl(fd, F_SETFL, O_RDWR | O_NONBLOCK) == -1) close_fail(fd, "fcntl(F_SETFL, O_RDWR | O_NONBLOCK)"); #endif /* SOCK_NONBLOCK */ if (connect(fd, addr, addrlen) == -1) { if (errno == EINPROGRESS) { VALUE io = sock_for_fd(klass, fd); if (io_wait) { errno = EAGAIN; (void)kgio_call_wait_writable(io); } return io; } close_fail(fd, "connect"); } return sock_for_fd(klass, fd); }
// This is a super hacky implementation of recvfrom (and below, sendto). // It assumes that send/recv are used on TCP only and recvfrom/sendto are // used on UDP only. This really needs to be fixed eventually. int lwip_recvfrom(int s, void *mem, int len, unsigned int flags, struct sockaddr *from, socklen_t *fromlen) { s = sock_for_fd(s); if (s < 0) { errno = EBADF; return -1; } // XXX from / fromlen aren't filled in. return recv_common(s, mem, len, flags, 0); }
static VALUE my_accept(struct accept_args *a, int force_nonblock) { int client_fd; VALUE client_io; int retried = 0; retry: client_fd = thread_accept(a, force_nonblock); if (client_fd == -1) { switch (errno) { case EAGAIN: if (force_nonblock) return Qnil; a->fd = my_fileno(a->accept_io); set_blocking_or_block(a->fd); #ifdef ECONNABORTED case ECONNABORTED: #endif /* ECONNABORTED */ #ifdef EPROTO case EPROTO: #endif /* EPROTO */ case EINTR: a->fd = my_fileno(a->accept_io); goto retry; case ENOMEM: case EMFILE: case ENFILE: #ifdef ENOBUFS case ENOBUFS: #endif /* ENOBUFS */ if (!retried) { retried = 1; errno = 0; rb_gc(); goto retry; } default: rb_sys_fail("accept"); } } client_io = sock_for_fd(a->accepted_class, client_fd); post_accept(a->accept_io, client_io); if (a->addr) in_addr_set(client_io, (struct sockaddr_storage *)a->addr, *a->addrlen); else rb_ivar_set(client_io, iv_kgio_addr, localhost); return client_io; }
int lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) { sockfd_t * fd; int rv = 0, i, ns; s = sock_for_fd(s); if (s < 0) { errno = EBADF; return -1; } fd = fds + s; // Make sure it's an internet address we understand. if (addrlen && *addrlen != sizeof(struct sockaddr_in)) { errno = ENAMETOOLONG; return -1; } // Get access mutex_lock(fd->mutex); // Is it the right type? if (fd->type != SOCK_STREAM) { errno = EINVAL; rv = -1; goto out; } // Loop until we've got one while (fd->conncnt <= 0) { // Make sure we're listen()'ing if (fd->conncnt < 0) { /// rv = -1; goto out; } // Wait for a connection // printf("lwip_accept(%d): waiting\n", s); cond_wait(fd->connect, fd->mutex); } // Ok, we've got a connection. Find the first available one // in the list and we'll take it. for (i=0; i<fd->connmax; i++) { if (fd->conns[i] >= 0) break; } if (i >= fd->connmax) { // This shouldn't happen... assert( 0 ); errno = EAGAIN; goto out; } ns = fd->conns[i]; fd->conns[i] = -1; fd->conncnt--; // Copy out the peer address as well if (addr) memcpy(addr, &fds[ns].name, sizeof(struct sockaddr_in)); if (addrlen) *addrlen = sizeof(struct sockaddr_in); // Ok, everything should be ready to go! Just need to make a VFS // fd for this socket. rv = fs_open_handle(&socketvfs, (void *)(ns + 1)); if (rv < 0) { close_common(ns); } out: mutex_unlock(fd->mutex); return rv; }
int lwip_connect(int s, struct sockaddr *name, socklen_t namelen) { sockfd_t * fd; struct ip_addr ip; int port, rv = 0; s = sock_for_fd(s); if (s < 0) { errno = EBADF; return -1; } fd = fds + s; // Make sure it's an internet address we understand. if (namelen != sizeof(struct sockaddr_in)) { errno = ENAMETOOLONG; return -1; } // Get access mutex_lock(fd->mutex); // Copy it over memcpy(&fd->name, name, namelen); // Convert this to an lwIP-happy format ip.addr = ((struct sockaddr_in *)name)->sin_addr.s_addr; port = ((struct sockaddr_in *)name)->sin_port; // Are we TCP or UDP? switch (fd->type) { case SOCK_STREAM: // This might have gotten made already, bind is valid on // outgoing sockets too. if (!fd->tcppcb) fd->tcppcb = tcp_new(); tcp_arg(fd->tcppcb, (void *)s); tcp_recv(fd->tcppcb, recv_tcp); tcp_sent(fd->tcppcb, sent_tcp); tcp_poll(fd->tcppcb, poll_tcp, 4); // 4 == 4 TCP timer intervals tcp_err(fd->tcppcb, err_tcp); if (tcp_connect(fd->tcppcb, &ip, ntohs(port), connect_tcp) != ERR_OK) { if (tcp_close(fd->tcppcb) != ERR_OK) tcp_abort(fd->tcppcb); fd->tcppcb = NULL; errno = EINVAL; rv = -1; goto out; } break; case SOCK_DGRAM: // This might have gotten made already, bind is valid on // outgoing sockets too. if (!fd->udppcb) fd->udppcb = udp_new(); udp_recv(fd->udppcb, recv_udp, (void *)s); udp_connect(fd->udppcb, &ip, ntohs(port)); break; default: assert( 0 ); errno = EINVAL; rv = -1; goto out; } // If we are doing a TCP connect, we need to wait for the results // of the operation. if (fd->type == SOCK_STREAM) { // Wait for the result fd->connerr = 10; while (fd->connerr > 0) { cond_wait(fd->connect, fd->mutex); } // Convert error codes switch (fd->connerr) { case ERR_OK: break; case ERR_MEM: case ERR_BUF: case ERR_VAL: case ERR_ARG: case ERR_IF: errno = EINVAL; rv = -1; goto out; case ERR_ABRT: case ERR_RST: case ERR_CLSD: case ERR_CONN: errno = ECONNREFUSED; rv = -1; goto out; case ERR_RTE: errno = ENETUNREACH; rv = -1; goto out; case ERR_USE: errno = EADDRINUSE; rv = -1; goto out; } if (fd->connerr == ERR_OK) { // Init our counters fd->recv = 0; fd->send = tcp_sndbuf(fd->tcppcb); } } out: mutex_unlock(fd->mutex); return rv; }
int lwip_sendto(int s, const void *dataptr, int size, unsigned int flags, struct sockaddr *to, socklen_t tolen) { sockfd_t * fd; struct ip_addr ip; struct pbuf * pbuf = NULL; int port, rv = 0; err_t err; if (flags != 0) { errno = EINVAL; return -1; } s = sock_for_fd(s); if (s < 0) { errno = EBADF; return -1; } fd = fds + s; // Get access mutex_lock(fd->mutex); // Make sure we're doing UDP here... we don't support sendto for TCP. if (fd->tcppcb) { errno = EINVAL; rv = -1; goto out; } // We might not have a udppcb still, if we just did socket(). if (!fd->udppcb) fd->udppcb = udp_new(); // Convert the address to an lwIP-happy format ip.addr = ((struct sockaddr_in *)to)->sin_addr.s_addr; port = ((struct sockaddr_in *)to)->sin_port; // Make sure we have the recv callback setup. fd->recv = 0; udp_recv(fd->udppcb, recv_udp, (void *)s); // Connect this to the specified destination. if ((err = udp_connect(fd->udppcb, &ip, ntohs(port))) != ERR_OK) { printf("connect: err is %d\n", err); // XXX Imprecise errno = ENOMEM; rv = -1; goto out; } // Make a pbuf for the data we want to send. pbuf = pbuf_alloc(PBUF_RAW, size, PBUF_POOL); if (pbuf == NULL) { printf("couldn't alloc pbuf of size %d\n", size); errno = ENOMEM; rv = -1; goto out; } { struct pbuf *q; const uint8 * src = (const uint8 *)dataptr; int i; for (q=pbuf; q; q=q->next) { //printf("putting %d bytes into pbuf @ %p\n", // q->len, q); for (i=0; i<q->len; i++) ((u8_t *)q->payload)[i] = *(src++); } } //memcpy(pbuf->payload, dataptr, size); // Send the data on the socket. if ((err = udp_send(fd->udppcb, pbuf)) != ERR_OK) { printf("send: err is %d\n", err); // XXX Imprecise errno = ENOMEM; rv = -1; goto out; } // Let go of the pbuf before returning. pbuf_free(pbuf); pbuf = NULL; out: if (pbuf) pbuf_free(pbuf); mutex_unlock(fd->mutex); return rv; }