From 5e8ce2d49b607c96e4ab2f7e71657eed32a31a56 Mon Sep 17 00:00:00 2001
From: Joel Cunningham <joel.cunningham@me.com>
Date: Wed, 8 Apr 2015 10:25:07 -0500
Subject: [PATCH] Sockets: add sendmsg

Adds sendmsg implementation for TCP and UDP sockets. Control messages
are not supported at this point, but could be added in the future
---
 src/api/sockets.c          | 121 +++++++++++++++++++++++++++++++++++++++++++++
 src/include/lwip/sockets.h |  19 +++++++
 2 files changed, 140 insertions(+)

diff --git a/src/api/sockets.c b/src/api/sockets.c
index 413684d..ef6eb7b 100644
--- a/src/api/sockets.c
+++ b/src/api/sockets.c
@@ -928,6 +928,127 @@ lwip_send(int s, const void *data, size_t size, int flags)
 }
 
 int
+lwip_sendmsg(int s, const struct msghdr *msg, int flags)
+{
+  struct lwip_sock *sock;
+  const struct sockaddr_in *to_in;
+  struct netbuf *chain_buf;
+  struct netbuf *tail_buf;
+  u16_t remote_port;
+  int i;
+  int size;
+  err_t err;
+  u8_t write_flags;
+  size_t written;
+
+  size = 0;
+  err = ERR_OK;
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  LWIP_ERROR("lwip_sendmsg: invalid msghdr", msg != NULL,
+             sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+
+  LWIP_UNUSED_ARG(msg->msg_control);
+  LWIP_UNUSED_ARG(msg->msg_controllen);
+  LWIP_UNUSED_ARG(msg->msg_flags);
+  LWIP_ERROR("lwip_sendmsg: invalid msghdr iov", (msg->msg_iov != NULL && msg->msg_iovlen != 0),
+             sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+
+  if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+#if LWIP_TCP
+    write_flags = NETCONN_COPY |
+    ((flags & MSG_MORE)     ? NETCONN_MORE      : 0) |
+    ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0);
+
+    for(i = 0; i < msg->msg_iovlen; i++) {
+      written = 0;
+      err = netconn_write_partly(sock->conn, msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len, write_flags, &written);
+      if (err == ERR_OK) {
+        size += written;
+        /* check that the entire IO vector was accepected, if not return a partial write */
+        if (written != msg->msg_iov[i].iov_len)
+          break;
+      }
+      /* none of this IO vector was accepted, but previous was, return partial write and conceal ERR_WOULDBLOCK */
+      else if (err == ERR_WOULDBLOCK && size > 0) {
+        err = ERR_OK;
+        /* let ERR_WOULDBLOCK persist on the netconn since we are returning ERR_OK */
+        break;
+      } else {
+        size = -1;
+        break;
+      }
+    }
+    sock_set_errno(sock, err_to_errno(err));
+    return size;
+#else /* LWIP_TCP */
+    sock_set_errno(sock, err_to_errno(ERR_ARG));
+    return -1;
+#endif /* LWIP_TCP */
+  }
+  /* else, UDP and RAW NETCONNs */
+#if LWIP_UDP || LWIP_RAW
+
+  LWIP_UNUSED_ARG(flags);
+  LWIP_ERROR("lwip_sendmsg: invalid msghdr name", (((msg->msg_name == NULL) && (msg->msg_namelen == 0)) ||
+             IS_SOCK_ADDR_LEN_VALID(msg->msg_namelen)) ,
+             sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+  to_in = (const struct sockaddr_in *)msg->msg_name;
+
+  /* initialize chain buffer with destination */
+  chain_buf = netbuf_new();
+  if (!chain_buf) {
+    sock_set_errno(sock, err_to_errno(ERR_MEM));
+    return -1;
+  }
+  if (to_in) {
+    SOCKADDR_TO_IPADDR_PORT(to_in, &chain_buf->addr, remote_port);
+    netbuf_fromport(chain_buf) = remote_port;
+  }
+
+  /* create a chained netbuf from the IO vectors */
+  err = netbuf_ref(chain_buf, msg->msg_iov[0].iov_base, msg->msg_iov[0].iov_len);
+  if (err == ERR_OK) {
+    size = msg->msg_iov[0].iov_len;
+    for(i = 1; i < msg->msg_iovlen; i++) {
+      tail_buf = netbuf_new();
+      if (!tail_buf) {
+        err = ERR_MEM;
+        break;
+      } else {
+        err = netbuf_ref(tail_buf, msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len);
+        if (err == ERR_OK) {
+          netbuf_chain(chain_buf, tail_buf);
+          size += msg->msg_iov[i].iov_len;
+        } else {
+          netbuf_delete(tail_buf);
+          break;
+        }
+      }
+    }
+  }
+
+  if (err == ERR_OK) {
+    /* send the data */
+    err = netconn_send(sock->conn, chain_buf);
+  }
+
+  /* deallocated the buffer */
+  netbuf_delete(chain_buf);
+
+  sock_set_errno(sock, err_to_errno(err));
+  return (err == ERR_OK ? size : -1);
+#else /* LWIP_UDP || LWIP_RAW */
+  sock_set_errno(sock, err_to_errno(ERR_ARG));
+  return -1;
+#endif /* LWIP_UDP || LWIP_RAW */
+}
+
+int
 lwip_sendto(int s, const void *data, size_t size, int flags,
        const struct sockaddr *to, socklen_t tolen)
 {
diff --git a/src/include/lwip/sockets.h b/src/include/lwip/sockets.h
index 116d28a..bd800cc 100644
--- a/src/include/lwip/sockets.h
+++ b/src/include/lwip/sockets.h
@@ -136,6 +136,23 @@ struct lwip_setgetsockopt_data {
 };
 #endif /* !LWIP_TCPIP_CORE_LOCKING */
 
+#if !defined(iovec)
+struct iovec {
+  void  *iov_base;
+  size_t iov_len;
+};
+#endif
+
+struct msghdr {
+  void         *msg_name;
+  socklen_t     msg_namelen;
+  struct iovec *msg_iov;
+  int           msg_iovlen;
+  void         *msg_control;
+  socklen_t     msg_controllen;
+  int           msg_flags;
+};
+
 /* Socket protocol types (TCP/UDP/RAW) */
 #define SOCK_STREAM     1
 #define SOCK_DGRAM      2
@@ -429,6 +446,7 @@ int lwip_read(int s, void *mem, size_t len);
 int lwip_recvfrom(int s, void *mem, size_t len, int flags,
       struct sockaddr *from, socklen_t *fromlen);
 int lwip_send(int s, const void *dataptr, size_t size, int flags);
+int lwip_sendmsg(int s, const struct msghdr *message, int flags);
 int lwip_sendto(int s, const void *dataptr, size_t size, int flags,
     const struct sockaddr *to, socklen_t tolen);
 int lwip_socket(int domain, int type, int protocol);
@@ -452,6 +470,7 @@ int lwip_fcntl(int s, int cmd, int val);
 #define recv(a,b,c,d)         lwip_recv(a,b,c,d)
 #define recvfrom(a,b,c,d,e,f) lwip_recvfrom(a,b,c,d,e,f)
 #define send(a,b,c,d)         lwip_send(a,b,c,d)
+#define sendmsg(a,b,c)        lwip_sendmsg(a,b,c)
 #define sendto(a,b,c,d,e,f)   lwip_sendto(a,b,c,d,e,f)
 #define socket(a,b,c)         lwip_socket(a,b,c)
 #define select(a,b,c,d,e)     lwip_select(a,b,c,d,e)
-- 
1.9.5.msysgit.0

