Goto sanos source index

//
// sockets.c
//
// BSD socket interface
//
// Copyright (C) 2002 Michael Ringgaard. 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 project 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 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.
// 

#include <net/net.h>

extern struct sockops tcpops;
extern struct sockops udpops;
extern struct sockops rawops;

struct sockops *sockops[SOCKTYPE_NUM];

void socket_init() {
  sockops[SOCKTYPE_TCP] = &tcpops;
  sockops[SOCKTYPE_UDP] = &udpops;
  sockops[SOCKTYPE_RAW] = &rawops;
}

void cancel_socket_request(struct sockreq *req) {
  if (req->next) req->next->prev = req->prev;
  if (req->prev) req->prev->next = req->next;
  if (req->socket) {
    if (req == req->socket->waithead) req->socket->waithead = req->next;
    if (req == req->socket->waittail) req->socket->waittail = req->prev;
  }
}

void release_socket_request(struct sockreq *req, int rc) {
  cancel_socket_request(req);
  req->rc = rc;
  mark_thread_ready(req->thread, 1, 2);
}

static void socket_timeout(void *arg) {
  struct sockreq *req = arg;

  if (req->thread->state == THREAD_STATE_READY) return;
  release_socket_request(req, req->rc > 0 ? req->rc : -ETIMEOUT);
}

err_t submit_socket_request(struct socket *s, struct sockreq *req, int type, struct msghdr *msg, unsigned int timeout) {
  struct timer timer;
  int rc;

  if (timeout == 0) return -ETIMEOUT;

  req->socket = s;
  req->thread = self();
  req->type = type;
  req->msg = msg;
  req->rc = 0;

  req->next = NULL;
  req->prev = s->waittail;
  if (s->waittail) s->waittail->next = req;
  s->waittail = req;
  if (!s->waithead) s->waithead = req;

  if (timeout != INFINITE) {
    init_timer(&timer, socket_timeout, req);
    timer.expires = ticks + timeout / MSECS_PER_TICK;
    add_timer(&timer);
  }

  rc = enter_alertable_wait(THREAD_WAIT_SOCKET);
  if (rc < 0) {
    cancel_socket_request(req);
    req->rc = rc;
  }

  if (timeout != INFINITE) del_timer(&timer);

  return req->rc;
}

int accept(struct socket *s, struct sockaddr *addr, int *addrlen, struct socket **retval) {
  return sockops[s->type]->accept(s, addr, addrlen, retval);
}

int bind(struct socket *s, struct sockaddr *name, int namelen) {
  return sockops[s->type]->bind(s, name, namelen);
}

int closesocket(struct socket *s) {
  int rc;

  rc = sockops[s->type]->close(s);
  detach_ioobject(&s->iob);
  return rc;
}

int connect(struct socket *s, struct sockaddr *name, int namelen) {
  return sockops[s->type]->connect(s, name, namelen);
}

int getpeername(struct socket *s, struct sockaddr *name, int *namelen) {
  return sockops[s->type]->getpeername(s, name, namelen);
}

int getsockname(struct socket *s, struct sockaddr *name, int *namelen) {
  return sockops[s->type]->getsockname(s, name, namelen);
}

int getsockopt(struct socket *s, int level, int optname, void *optval, int *optlen) {
  return sockops[s->type]->getsockopt(s, level, optname, optval, optlen);
}

int ioctlsocket(struct socket *s, int cmd, void *data, size_t size) {
  if (cmd == SIOIFLIST) {
    return netif_ioctl_list(data, size);
  } else if (cmd == SIOIFCFG) {
    return netif_ioctl_cfg(data, size);
  } else {
    return sockops[s->type]->ioctl(s, cmd, data, size);
  }
}

int listen(struct socket *s, int backlog) {
  return sockops[s->type]->listen(s, backlog);
}

int recv(struct socket *s, void *data, int size, unsigned int flags) {
  struct msghdr msg;
  struct iovec iov;
  int rc;

  if (!data) return -EFAULT;
  if (size < 0) return -EINVAL;

  msg.msg_name = NULL;
  msg.msg_namelen = 0;
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;
  iov.iov_base = data;
  iov.iov_len = size;

  rc = sockops[s->type]->recvmsg(s, &msg, flags);

  return rc;
}

int recvmsg(struct socket *s, struct msghdr *msg, unsigned int flags) {
  struct msghdr m;
  int rc;

  if (!msg) return -EINVAL;
  m.msg_name = msg->msg_name;
  m.msg_namelen = msg->msg_namelen;
  m.msg_iov = dup_iovec(msg->msg_iov, msg->msg_iovlen);
  m.msg_iovlen = msg->msg_iovlen;
  if (!m.msg_iov) return -ENOMEM;

  rc = sockops[s->type]->recvmsg(s, &m, flags);
  msg->msg_namelen = m.msg_namelen;
  kfree(m.msg_iov);

  return rc;
}

int recvv(struct socket *s, struct iovec *iov, int count) {
  struct msghdr msg;
  int rc;

  msg.msg_name = NULL;
  msg.msg_namelen = 0;
  msg.msg_iov = dup_iovec(iov, count);
  msg.msg_iovlen = count;
  if (!msg.msg_iov) return -ENOMEM;

  rc = sockops[s->type]->recvmsg(s, &msg, 0);
  kfree(msg.msg_iov);

  return rc;
}

int recvfrom(struct socket *s, void *data, int size, unsigned int flags, struct sockaddr *from, int *fromlen) {
  struct msghdr msg;
  struct iovec iov;
  int rc;

  if (!data) return -EFAULT;
  if (size < 0) return -EINVAL;

  msg.msg_name = from;
  msg.msg_namelen = fromlen ? *fromlen : 0;
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;
  iov.iov_base = data;
  iov.iov_len = size;

  rc = sockops[s->type]->recvmsg(s, &msg, flags);
  if (fromlen) *fromlen = msg.msg_namelen;

  return rc;
}

int send(struct socket *s, void *data, int size, unsigned int flags) {
  struct msghdr msg;
  struct iovec iov;
  int rc;

  if (!data) return -EFAULT;
  if (size < 0) return -EINVAL;

  msg.msg_name = NULL;
  msg.msg_namelen = 0;
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;
  iov.iov_base = data;
  iov.iov_len = size;

  rc = sockops[s->type]->sendmsg(s, &msg, flags);

  return rc;
}

int sendmsg(struct socket *s, struct msghdr *msg, unsigned int flags) {
  struct msghdr m;
  int rc;

  if (!msg) return -EINVAL;
  m.msg_name = msg->msg_name;
  m.msg_namelen = msg->msg_namelen;
  m.msg_iov = dup_iovec(msg->msg_iov, msg->msg_iovlen);
  m.msg_iovlen = msg->msg_iovlen;
  if (!m.msg_iov) return -ENOMEM;

  rc = sockops[s->type]->sendmsg(s, &m, flags);
  kfree(m.msg_iov);

  return rc;
}

int sendto(struct socket *s, void *data, int size, unsigned int flags, struct sockaddr *to, int tolen) {
  struct msghdr msg;
  struct iovec iov;
  int rc;

  if (!data) return -EFAULT;
  if (size < 0) return -EINVAL;

  msg.msg_name = to;
  msg.msg_namelen = tolen;
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;
  iov.iov_base = data;
  iov.iov_len = size;

  rc = sockops[s->type]->sendmsg(s, &msg, flags);

  return rc;
}

int sendv(struct socket *s, struct iovec *iov, int count) {
  struct msghdr msg;
  int rc;

  msg.msg_name = NULL;
  msg.msg_namelen = 0;
  msg.msg_iov = dup_iovec(iov, count);
  msg.msg_iovlen = count;
  if (!msg.msg_iov) return -ENOMEM;

  rc = sockops[s->type]->sendmsg(s, &msg, 0);
  kfree(msg.msg_iov);

  return rc;
}

int setsockopt(struct socket *s, int level, int optname, const void *optval, int optlen) {
  return sockops[s->type]->setsockopt(s, level, optname, optval, optlen);
}

int shutdown(struct socket *s, int how) {
  return sockops[s->type]->shutdown(s, how);
}

int socket(int domain, int type, int protocol, struct socket **retval) {
  struct socket *s;
  int socktype;
  int rc;

  if (domain != AF_INET) return -EAFNOSUPPORT;
  switch (type) {
    case SOCK_STREAM:
      if (protocol == IPPROTO_IP) {
        socktype = SOCKTYPE_TCP;
      } else if (protocol == IPPROTO_TCP) {
        socktype = SOCKTYPE_TCP;
      } else {
        return -EPROTONOSUPPORT;
      }
      break;

    case SOCK_DGRAM:
      if (protocol == IPPROTO_IP) {
        socktype = SOCKTYPE_UDP;
      } else if (protocol == IPPROTO_UDP) {
        socktype = SOCKTYPE_UDP;
      } else {
        return -EPROTONOSUPPORT;
      }
      break;

    case SOCK_RAW:
      socktype = SOCKTYPE_RAW;
      break;

    default:
      return -EPROTONOSUPPORT;
  }

  s = (struct socket *) kmalloc(sizeof(struct socket));
  if (!s) return -ENOMEM;
  memset(s, 0, sizeof(struct socket));

  init_ioobject(&s->iob, OBJECT_SOCKET);
  s->type = socktype;
  s->state = SOCKSTATE_UNBOUND;
  s->sndtimeo = INFINITE;
  s->rcvtimeo = INFINITE;

  rc = sockops[socktype]->socket(s, domain, type, protocol);
  if (rc < 0) return rc;

  *retval = s;
  return 0;
}