| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include <sys/socket.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <netinet/in.h> |
| #include <string.h> |
| #include <poll.h> |
| #include <assert.h> |
| #include <fcntl.h> |
| #include <sys/ioctl.h> |
| #include <sys/un.h> |
| #include <stdio.h> |
| #include <signal.h> |
| |
| #include "os/mynewt.h" |
| #include "mn_socket/mn_socket.h" |
| #include "mn_socket/mn_socket_ops.h" |
| #include "native_sockets/native_sock.h" |
| |
| #include "native_sock_priv.h" |
| |
| static struct native_sock { |
| struct mn_socket ns_sock; |
| int ns_fd; |
| unsigned int ns_connect:1; /* Non-blocking connect in progress. */ |
| unsigned int ns_poll:1; |
| unsigned int ns_listen:1; |
| uint8_t ns_type; |
| uint8_t ns_pf; |
| struct os_sem ns_sem; |
| STAILQ_HEAD(, os_mbuf_pkthdr) ns_rx; |
| struct os_mbuf *ns_tx; |
| } native_socks[MYNEWT_VAL(NATIVE_SOCKETS_MAX)]; |
| |
| static struct native_sock_state { |
| struct pollfd poll_fds[MYNEWT_VAL(NATIVE_SOCKETS_MAX)]; |
| int poll_fd_cnt; |
| struct os_mutex mtx; |
| struct os_task task; |
| } native_sock_state; |
| |
| static const struct mn_socket_ops native_sock_ops = { |
| .mso_create = native_sock_create, |
| .mso_close = native_sock_close, |
| |
| .mso_bind = native_sock_bind, |
| .mso_connect = native_sock_connect, |
| .mso_listen = native_sock_listen, |
| |
| .mso_sendto = native_sock_sendto, |
| .mso_recvfrom = native_sock_recvfrom, |
| |
| .mso_getsockopt = native_sock_getsockopt, |
| .mso_setsockopt = native_sock_setsockopt, |
| |
| .mso_getsockname = native_sock_getsockname, |
| .mso_getpeername = native_sock_getpeername, |
| |
| .mso_itf_getnext = native_sock_itf_getnext, |
| .mso_itf_addr_getnext = native_sock_itf_addr_getnext |
| }; |
| |
| static struct native_sock * |
| native_get_sock(void) |
| { |
| int i; |
| struct native_sock *ns; |
| |
| for (i = 0; i < MYNEWT_VAL(NATIVE_SOCKETS_MAX); i++) { |
| if (native_socks[i].ns_fd < 0) { |
| ns = &native_socks[i]; |
| ns->ns_poll = 0; |
| ns->ns_listen = 0; |
| return ns; |
| } |
| } |
| return NULL; |
| } |
| |
| static struct native_sock * |
| native_find_sock(int fd) |
| { |
| int i; |
| |
| for (i = 0; i < MYNEWT_VAL(NATIVE_SOCKETS_MAX); i++) { |
| if (native_socks[i].ns_fd == fd) { |
| return &native_socks[i]; |
| } |
| } |
| return NULL; |
| } |
| |
| static void |
| native_sock_poll_rebuild(struct native_sock_state *nss) |
| { |
| struct native_sock *ns; |
| int i; |
| int j; |
| |
| os_mutex_pend(&nss->mtx, OS_WAIT_FOREVER); |
| for (i = 0, j = 0; i < MYNEWT_VAL(NATIVE_SOCKETS_MAX); i++) { |
| ns = &native_socks[i]; |
| if (ns->ns_fd < 0) { |
| continue; |
| } |
| if (!ns->ns_poll) { |
| continue; |
| } |
| nss->poll_fds[j].fd = ns->ns_fd; |
| nss->poll_fds[j].events = POLLIN | POLLOUT; |
| nss->poll_fds[j].revents = 0; |
| j++; |
| } |
| nss->poll_fd_cnt = j; |
| os_mutex_release(&nss->mtx); |
| } |
| |
| int |
| native_sock_err_to_mn_err(int err) |
| { |
| switch (err) { |
| case 0: |
| return 0; |
| case EAGAIN: |
| case EINPROGRESS: |
| return MN_EAGAIN; |
| case ENOTCONN: |
| return MN_ENOTCONN; |
| case ETIMEDOUT: |
| return MN_ETIMEDOUT; |
| case ENOMEM: |
| return MN_ENOBUFS; |
| case EADDRINUSE: |
| return MN_EADDRINUSE; |
| case EADDRNOTAVAIL: |
| return MN_EADDRNOTAVAIL; |
| default: |
| return MN_EINVAL; |
| } |
| } |
| |
| static int |
| native_sock_mn_addr_to_addr(struct mn_sockaddr *ms, struct sockaddr *sa, |
| int *sa_len) |
| { |
| struct sockaddr_un *sun = (struct sockaddr_un *)sa; |
| struct sockaddr_in *sin = (struct sockaddr_in *)sa; |
| struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; |
| struct mn_sockaddr_un *msun = (struct mn_sockaddr_un *)ms; |
| struct mn_sockaddr_in *msin = (struct mn_sockaddr_in *)ms; |
| struct mn_sockaddr_in6 *msin6 = (struct mn_sockaddr_in6 *)ms; |
| |
| switch (ms->msa_family) { |
| case MN_AF_INET: |
| sin->sin_family = AF_INET; |
| #ifndef MN_LINUX |
| sin->sin_len = sizeof(*sin); |
| #endif |
| sin->sin_addr.s_addr = msin->msin_addr.s_addr; |
| sin->sin_port = msin->msin_port; |
| *sa_len = sizeof(*sin); |
| break; |
| case MN_AF_INET6: |
| sin6->sin6_family = AF_INET6; |
| #ifndef MN_LINUX |
| sin6->sin6_len = sizeof(*sin6); |
| #endif |
| sin6->sin6_port = msin6->msin6_port; |
| sin6->sin6_flowinfo = msin6->msin6_flowinfo; |
| memcpy(&sin6->sin6_addr, &msin6->msin6_addr, |
| sizeof(msin6->msin6_addr)); |
| sin6->sin6_scope_id = msin6->msin6_scope_id; |
| *sa_len = sizeof(*sin6); |
| break; |
| case MN_AF_LOCAL: |
| sun->sun_family = AF_LOCAL; |
| #ifndef MN_LINUX |
| sun->sun_len = sizeof(*sun); |
| #endif |
| sun->sun_path[0] = '\0'; |
| strncat(sun->sun_path, msun->msun_path, sizeof(sun->sun_path) - 1); |
| if (strcmp(sun->sun_path, msun->msun_path) != 0) { |
| /* Path too long. */ |
| return MN_EINVAL; |
| } |
| *sa_len = sizeof(*sun); |
| break; |
| default: |
| return MN_EPROTONOSUPPORT; |
| } |
| return 0; |
| } |
| |
| static int |
| native_sock_addr_to_mn_addr(struct sockaddr *sa, struct mn_sockaddr *ms) |
| { |
| struct mn_sockaddr_un *msun = (struct mn_sockaddr_un *)ms; |
| struct mn_sockaddr_in *msin = (struct mn_sockaddr_in *)ms; |
| struct mn_sockaddr_in6 *msin6 = (struct mn_sockaddr_in6 *)ms; |
| struct sockaddr_un *sun = (struct sockaddr_un *)sa; |
| struct sockaddr_in *sin = (struct sockaddr_in *)sa; |
| struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; |
| |
| switch (sa->sa_family) { |
| case AF_INET: |
| msin->msin_family = MN_AF_INET; |
| msin->msin_len = sizeof(*msin); |
| msin->msin_addr.s_addr = sin->sin_addr.s_addr; |
| msin->msin_port = sin->sin_port; |
| break; |
| case AF_INET6: |
| msin6->msin6_family = MN_AF_INET6; |
| msin6->msin6_len = sizeof(*msin6); |
| msin6->msin6_port = sin6->sin6_port; |
| msin6->msin6_flowinfo = sin6->sin6_flowinfo; |
| memcpy(&msin6->msin6_addr, &sin6->sin6_addr, |
| sizeof(msin6->msin6_addr)); |
| msin6->msin6_scope_id = sin6->sin6_scope_id; |
| break; |
| case AF_LOCAL: |
| msun->msun_family = MN_AF_LOCAL; |
| strncpy(msun->msun_path, sun->sun_path, sizeof msun->msun_path); |
| if (strcmp(msun->msun_path, sun->sun_path) != 0) { |
| /* Path too long. */ |
| return MN_EINVAL; |
| } |
| break; |
| default: |
| return MN_EPROTONOSUPPORT; |
| } |
| return 0; |
| } |
| |
| static void |
| native_sock_set_nonblocking(struct native_sock *ns) |
| { |
| int rc; |
| |
| rc = fcntl(ns->ns_fd, F_SETFL, fcntl(ns->ns_fd, F_GETFL, 0) | O_NONBLOCK); |
| assert(rc == 0); |
| } |
| |
| int |
| native_sock_create(struct mn_socket **sp, uint8_t domain, |
| uint8_t type, uint8_t proto) |
| { |
| struct native_sock_state *nss = &native_sock_state; |
| struct native_sock *ns; |
| int idx; |
| |
| switch (domain) { |
| case MN_PF_INET: |
| domain = PF_INET; |
| break; |
| case MN_PF_INET6: |
| domain = PF_INET6; |
| break; |
| case MN_PF_LOCAL: |
| domain = PF_LOCAL; |
| break; |
| default: |
| return MN_EPROTONOSUPPORT; |
| } |
| |
| switch (type) { |
| case MN_SOCK_DGRAM: |
| type = SOCK_DGRAM; |
| break; |
| case MN_SOCK_STREAM: |
| type = SOCK_STREAM; |
| break; |
| case 0: |
| break; |
| default: |
| return MN_EPROTONOSUPPORT; |
| } |
| |
| os_mutex_pend(&nss->mtx, OS_WAIT_FOREVER); |
| ns = native_get_sock(); |
| if (!ns) { |
| os_mutex_release(&nss->mtx); |
| return MN_ENOBUFS; |
| } |
| os_sem_init(&ns->ns_sem, 0); |
| idx = socket(domain, type, proto); |
| |
| ns->ns_fd = idx; |
| ns->ns_pf = domain; |
| ns->ns_type = type; |
| native_sock_set_nonblocking(ns); |
| |
| os_mutex_release(&nss->mtx); |
| if (idx < 0) { |
| return MN_ENOBUFS; |
| } |
| *sp = &ns->ns_sock; |
| return 0; |
| } |
| |
| int |
| native_sock_close(struct mn_socket *s) |
| { |
| struct native_sock_state *nss = &native_sock_state; |
| struct native_sock *ns = (struct native_sock *)s; |
| struct os_mbuf_pkthdr *m; |
| |
| os_mutex_pend(&nss->mtx, OS_WAIT_FOREVER); |
| close(ns->ns_fd); |
| ns->ns_fd = -1; |
| |
| /* |
| * When socket is closed, we must free all mbufs which might be |
| * queued in it. |
| */ |
| while ((m = STAILQ_FIRST(&ns->ns_rx))) { |
| STAILQ_REMOVE_HEAD(&ns->ns_rx, omp_next); |
| os_mbuf_free_chain(OS_MBUF_PKTHDR_TO_MBUF(m)); |
| } |
| os_mbuf_free_chain(ns->ns_tx); |
| native_sock_poll_rebuild(nss); |
| os_mutex_release(&nss->mtx); |
| return 0; |
| } |
| |
| int |
| native_sock_connect(struct mn_socket *s, struct mn_sockaddr *addr) |
| { |
| struct native_sock_state *nss = &native_sock_state; |
| struct native_sock *ns = (struct native_sock *)s; |
| struct sockaddr_storage ss; |
| struct sockaddr *sa = (struct sockaddr *)&ss; |
| int rc; |
| int sa_len; |
| int in_progress = 0; |
| |
| rc = native_sock_mn_addr_to_addr(addr, sa, &sa_len); |
| if (rc) { |
| return rc; |
| } |
| os_mutex_pend(&nss->mtx, OS_WAIT_FOREVER); |
| rc = connect(ns->ns_fd, sa, sa_len); |
| if (rc != 0) { |
| if (errno == EINPROGRESS) { |
| /* Non-blocking connect initiated. */ |
| in_progress = 1; |
| ns->ns_connect = 1; |
| } else { |
| rc = errno; |
| os_mutex_release(&nss->mtx); |
| return native_sock_err_to_mn_err(rc); |
| } |
| } |
| ns->ns_poll = 1; |
| native_sock_poll_rebuild(nss); |
| os_mutex_release(&nss->mtx); |
| |
| /* Indicate writability if connection fully established. */ |
| if (!in_progress) { |
| mn_socket_writable(s, 0); |
| } |
| |
| return 0; |
| } |
| |
| int |
| native_sock_bind(struct mn_socket *s, struct mn_sockaddr *addr) |
| { |
| struct native_sock_state *nss = &native_sock_state; |
| struct native_sock *ns = (struct native_sock *)s; |
| struct sockaddr_un ss; |
| struct sockaddr *sa = (struct sockaddr *)&ss; |
| int rc; |
| int sa_len; |
| int val = 1; |
| |
| rc = native_sock_mn_addr_to_addr(addr, sa, &sa_len); |
| if (rc) { |
| return rc; |
| } |
| |
| os_mutex_pend(&nss->mtx, OS_WAIT_FOREVER); |
| if (ns->ns_type == SOCK_STREAM) { |
| rc = setsockopt(ns->ns_fd, SOL_SOCKET, SO_REUSEADDR, &val, |
| sizeof(val)); |
| if (rc) { |
| goto err; |
| } |
| } |
| rc = ioctl(ns->ns_fd, FIONBIO, (char *)&val); |
| if (rc) { |
| goto err; |
| } |
| if (bind(ns->ns_fd, sa, sa_len)) { |
| goto err; |
| } |
| if (ns->ns_type == SOCK_DGRAM) { |
| ns->ns_poll = 1; |
| native_sock_poll_rebuild(nss); |
| } |
| os_mutex_release(&nss->mtx); |
| return 0; |
| err: |
| rc = errno; |
| os_mutex_release(&nss->mtx); |
| return native_sock_err_to_mn_err(rc); |
| } |
| |
| int |
| native_sock_listen(struct mn_socket *s, uint8_t qlen) |
| { |
| struct native_sock_state *nss = &native_sock_state; |
| struct native_sock *ns = (struct native_sock *)s; |
| int rc; |
| |
| os_mutex_pend(&nss->mtx, OS_WAIT_FOREVER); |
| if (listen(ns->ns_fd, qlen)) { |
| rc = errno; |
| os_mutex_release(&nss->mtx); |
| return native_sock_err_to_mn_err(rc); |
| } |
| ns->ns_poll = 1; |
| ns->ns_listen = 1; |
| native_sock_poll_rebuild(nss); |
| os_mutex_release(&nss->mtx); |
| return 0; |
| } |
| |
| /* |
| * TX routine for stream sockets (TCP). The data to send is pointed |
| * by ns_tx. |
| * Keep sending mbufs until socket says that it can't take anymore. |
| * then wait for send event notification before continuing. |
| */ |
| static int |
| native_sock_stream_tx(struct native_sock *ns, int notify) |
| { |
| struct native_sock_state *nss = &native_sock_state; |
| struct os_mbuf *m; |
| struct os_mbuf *n; |
| int rc; |
| |
| rc = 0; |
| |
| os_mutex_pend(&nss->mtx, OS_TIMEOUT_NEVER); |
| while (ns->ns_tx && rc == 0) { |
| m = ns->ns_tx; |
| n = SLIST_NEXT(m, om_next); |
| |
| errno = 0; |
| rc = write(ns->ns_fd, m->om_data, m->om_len); |
| if (rc == m->om_len) { |
| /* Complete write. */ |
| ns->ns_tx = n; |
| os_mbuf_free(m); |
| rc = 0; |
| } else if (rc != -1) { |
| /* Partial write. */ |
| os_mbuf_adj(m, m->om_len - rc); |
| rc = 0; |
| } else { |
| /* Error. */ |
| rc = errno; |
| if (rc == EAGAIN) { |
| rc = 0; |
| } else { |
| /* |
| * Socket had an error. User should close it. |
| */ |
| os_mbuf_free_chain(ns->ns_tx); |
| ns->ns_tx = NULL; |
| rc = native_sock_err_to_mn_err(rc); |
| } |
| break; |
| } |
| } |
| os_mutex_release(&nss->mtx); |
| if (notify) { |
| mn_socket_writable(&ns->ns_sock, rc); |
| } |
| return rc; |
| } |
| |
| static int |
| native_sock_set_tx_buf(struct native_sock *ns, struct os_mbuf *om) |
| { |
| struct native_sock_state *nss = &native_sock_state; |
| int rc; |
| |
| os_mutex_pend(&nss->mtx, OS_WAIT_FOREVER); |
| if (ns->ns_tx) { |
| rc = MN_EAGAIN; |
| } else { |
| ns->ns_tx = om; |
| rc = 0; |
| } |
| os_mutex_release(&nss->mtx); |
| |
| return rc; |
| } |
| |
| int |
| native_sock_sendto(struct mn_socket *s, struct os_mbuf *m, |
| struct mn_sockaddr *addr) |
| { |
| struct native_sock *ns = (struct native_sock *)s; |
| struct sockaddr_storage ss; |
| struct sockaddr *sa = (struct sockaddr *)&ss; |
| uint8_t tmpbuf[MYNEWT_VAL(NATIVE_SOCKETS_MAX_UDP)]; |
| struct os_mbuf *o; |
| int sa_len; |
| int off; |
| int rc; |
| |
| if (ns->ns_type == SOCK_DGRAM) { |
| rc = native_sock_mn_addr_to_addr(addr, sa, &sa_len); |
| if (rc) { |
| return rc; |
| } |
| off = 0; |
| for (o = m; o; o = SLIST_NEXT(o, om_next)) { |
| if (off + o->om_len > sizeof(tmpbuf)) { |
| return MN_ENOBUFS; |
| } |
| os_mbuf_copydata(o, 0, o->om_len, &tmpbuf[off]); |
| off += o->om_len; |
| } |
| rc = sendto(ns->ns_fd, tmpbuf, off, 0, sa, sa_len); |
| if (rc != off) { |
| return native_sock_err_to_mn_err(errno); |
| } |
| os_mbuf_free_chain(m); |
| return 0; |
| } else { |
| rc = native_sock_set_tx_buf(ns, m); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| rc = native_sock_stream_tx(ns, 0); |
| return rc; |
| } |
| } |
| |
| int |
| native_sock_recvfrom(struct mn_socket *s, struct os_mbuf **mp, |
| struct mn_sockaddr *addr) |
| { |
| struct native_sock *ns = (struct native_sock *)s; |
| struct sockaddr_storage ss; |
| struct sockaddr *sa = (struct sockaddr *)&ss; |
| uint8_t tmpbuf[MYNEWT_VAL(NATIVE_SOCKETS_MAX_UDP)]; |
| struct os_mbuf *m; |
| socklen_t slen; |
| int rc; |
| |
| slen = sizeof(ss); |
| if (ns->ns_type == SOCK_DGRAM) { |
| rc = recvfrom(ns->ns_fd, tmpbuf, sizeof(tmpbuf), 0, sa, &slen); |
| } else { |
| rc = getpeername(ns->ns_fd, sa, &slen); |
| if (rc == 0) { |
| rc = read(ns->ns_fd, tmpbuf, sizeof(tmpbuf)); |
| } |
| } |
| if (rc < 0) { |
| return native_sock_err_to_mn_err(errno); |
| } |
| if (ns->ns_type == SOCK_STREAM && rc == 0) { |
| ns->ns_poll = 0; |
| native_sock_poll_rebuild(&native_sock_state); |
| return MN_ECONNABORTED; |
| } |
| |
| m = os_msys_get_pkthdr(rc, 0); |
| if (!m) { |
| return MN_ENOBUFS; |
| } |
| os_mbuf_copyinto(m, 0, tmpbuf, rc); |
| *mp = m; |
| if (addr) { |
| native_sock_addr_to_mn_addr(sa, addr); |
| } |
| return 0; |
| } |
| |
| int |
| native_sock_getsockopt(struct mn_socket *s, uint8_t level, uint8_t name, |
| void *val) |
| { |
| return MN_EPROTONOSUPPORT; |
| } |
| |
| int |
| native_sock_setsockopt(struct mn_socket *s, uint8_t level, uint8_t name, |
| void *val) |
| { |
| struct native_sock *ns = (struct native_sock *)s; |
| int rc; |
| uint32_t val32; |
| struct ip_mreq ip_mreq; |
| struct ipv6_mreq ipv6_mreq; |
| void *opt; |
| int opt_len; |
| struct mn_mreq *mreq; |
| |
| if (level == MN_SO_LEVEL) { |
| switch (name) { |
| case MN_MCAST_JOIN_GROUP: |
| case MN_MCAST_LEAVE_GROUP: |
| mreq = val; |
| if (mreq->mm_family == MN_AF_INET) { |
| memset(&ip_mreq, 0, sizeof(ip_mreq)); |
| if (native_sock_itf_addr(mreq->mm_idx, |
| &ip_mreq.imr_interface.s_addr)) { |
| return MN_EADDRNOTAVAIL; |
| } |
| ip_mreq.imr_multiaddr.s_addr = mreq->mm_addr.v4.s_addr; |
| level = IPPROTO_IP; |
| if (name == MN_MCAST_JOIN_GROUP) { |
| name = IP_ADD_MEMBERSHIP; |
| } else { |
| name = IP_DROP_MEMBERSHIP; |
| } |
| opt = &ip_mreq; |
| opt_len = sizeof(ip_mreq); |
| } else { |
| memset(&ipv6_mreq, 0, sizeof(ipv6_mreq)); |
| ipv6_mreq.ipv6mr_interface = mreq->mm_idx; |
| memcpy(&ipv6_mreq.ipv6mr_multiaddr, &mreq->mm_addr, |
| sizeof(struct in6_addr)); |
| level = IPPROTO_IPV6; |
| if (name == MN_MCAST_JOIN_GROUP) { |
| name = IPV6_JOIN_GROUP; |
| } else { |
| name = IPV6_LEAVE_GROUP; |
| } |
| opt = &ipv6_mreq; |
| opt_len = sizeof(ipv6_mreq); |
| } |
| |
| rc = setsockopt(ns->ns_fd, level, name, opt, opt_len); |
| if (rc) { |
| return native_sock_err_to_mn_err(errno); |
| } |
| return 0; |
| case MN_MCAST_IF: |
| if (ns->ns_pf == AF_INET) { |
| level = IPPROTO_IP; |
| name = IP_MULTICAST_IF; |
| rc = native_sock_itf_addr(*(int *)val, &val32); |
| if (rc) { |
| return rc; |
| } |
| } else { |
| level = IPPROTO_IPV6; |
| name = IPV6_MULTICAST_IF; |
| val32 = *(uint32_t *)val; |
| } |
| rc = setsockopt(ns->ns_fd, level, name, &val32, sizeof(val32)); |
| if (rc) { |
| return native_sock_err_to_mn_err(errno); |
| } |
| return 0; |
| |
| case MN_REUSEADDR: |
| name = SO_REUSEADDR; |
| val32 = *(uint32_t *)val; |
| rc = setsockopt(ns->ns_fd, level, name, &val32, sizeof(val32)); |
| if (rc) { |
| return native_sock_err_to_mn_err(errno); |
| } |
| return 0; |
| } |
| } |
| return MN_EPROTONOSUPPORT; |
| } |
| |
| int |
| native_sock_getsockname(struct mn_socket *s, struct mn_sockaddr *addr) |
| { |
| struct native_sock *ns = (struct native_sock *)s; |
| struct sockaddr_storage ss; |
| struct sockaddr *sa = (struct sockaddr *)&ss; |
| socklen_t len; |
| int rc; |
| |
| len = sizeof(struct sockaddr_storage); |
| rc = getsockname(ns->ns_fd, sa, &len); |
| if (rc) { |
| return native_sock_err_to_mn_err(errno); |
| } |
| rc = native_sock_addr_to_mn_addr(sa, addr); |
| if (rc) { |
| return rc; |
| } |
| return 0; |
| } |
| |
| int |
| native_sock_getpeername(struct mn_socket *s, struct mn_sockaddr *addr) |
| { |
| struct native_sock *ns = (struct native_sock *)s; |
| struct sockaddr_storage ss; |
| struct sockaddr *sa = (struct sockaddr *)&ss; |
| socklen_t len; |
| int rc; |
| |
| len = sizeof(struct sockaddr_storage); |
| rc = getpeername(ns->ns_fd, sa, &len); |
| if (rc) { |
| return native_sock_err_to_mn_err(errno); |
| } |
| rc = native_sock_addr_to_mn_addr(sa, addr); |
| if (rc) { |
| return rc; |
| } |
| return 0; |
| } |
| |
| /* |
| * XXX should do this task with SIGIO as well. |
| */ |
| static void |
| socket_task(void *arg) |
| { |
| struct native_sock_state *nss = arg; |
| struct native_sock *ns, *new_ns; |
| struct sockaddr_storage ss; |
| struct sockaddr *sa = (struct sockaddr *)&ss; |
| int revents; |
| int i; |
| socklen_t slen; |
| int sock_err; |
| int rc; |
| |
| os_mutex_pend(&nss->mtx, OS_WAIT_FOREVER); |
| while (1) { |
| os_mutex_release(&nss->mtx); |
| os_time_delay(os_time_ms_to_ticks32(MYNEWT_VAL(NATIVE_SOCKETS_POLL_INTERVAL_MS))); |
| os_mutex_pend(&nss->mtx, OS_WAIT_FOREVER); |
| if (nss->poll_fd_cnt) { |
| rc = poll(nss->poll_fds, nss->poll_fd_cnt, 0); |
| } else { |
| rc = 0; |
| } |
| if (rc == 0) { |
| continue; |
| } |
| for (i = 0; i < nss->poll_fd_cnt; i++) { |
| if (nss->poll_fds[i].revents == 0) { |
| continue; |
| } |
| |
| revents = nss->poll_fds[i].revents; |
| nss->poll_fds[i].revents = 0; |
| |
| ns = native_find_sock(nss->poll_fds[i].fd); |
| assert(ns); |
| |
| if (revents & POLLIN) { |
| if (ns->ns_listen) { |
| new_ns = native_get_sock(); |
| if (!new_ns) { |
| continue; |
| } |
| slen = sizeof(ss); |
| new_ns->ns_fd = accept(ns->ns_fd, sa, &slen); |
| if (new_ns->ns_fd < 0) { |
| continue; |
| } |
| new_ns->ns_type = ns->ns_type; |
| new_ns->ns_sock.ms_ops = &native_sock_ops; |
| native_sock_set_nonblocking(new_ns); |
| |
| os_mutex_release(&nss->mtx); |
| if (mn_socket_newconn(&ns->ns_sock, &new_ns->ns_sock)) { |
| /* |
| * should close |
| */ |
| } |
| os_mutex_pend(&nss->mtx, OS_WAIT_FOREVER); |
| new_ns->ns_poll = 1; |
| native_sock_poll_rebuild(nss); |
| } else { |
| mn_socket_readable(&ns->ns_sock, 0); |
| } |
| } |
| |
| if (revents & POLLOUT) { |
| if (ns->ns_connect) { |
| /* |
| * The connection attempt has completed. Report whether it |
| * succeeded. |
| */ |
| ns->ns_connect = 0; |
| |
| slen = sizeof(sock_err); |
| rc = getsockopt(ns->ns_fd, SOL_SOCKET, SO_ERROR, |
| &sock_err, &slen); |
| if (rc != 0) { |
| rc = native_sock_err_to_mn_err(errno); |
| } else if (sock_err != 0) { |
| rc = native_sock_err_to_mn_err(sock_err); |
| } |
| mn_socket_writable(&ns->ns_sock, rc); |
| } else if (ns->ns_type == SOCK_STREAM && ns->ns_tx) { |
| native_sock_stream_tx(ns, 1); |
| } |
| } |
| } |
| } |
| } |
| |
| int |
| native_sock_init(void) |
| { |
| struct native_sock_state *nss = &native_sock_state; |
| int i; |
| os_stack_t *sp; |
| |
| /* Ensure this function only gets called by sysinit. */ |
| SYSINIT_ASSERT_ACTIVE(); |
| |
| for (i = 0; i < MYNEWT_VAL(NATIVE_SOCKETS_MAX); i++) { |
| native_socks[i].ns_fd = -1; |
| STAILQ_INIT(&native_socks[i].ns_rx); |
| } |
| sp = malloc(sizeof(os_stack_t) * MYNEWT_VAL(NATIVE_SOCKETS_STACK_SZ)); |
| if (!sp) { |
| return -1; |
| } |
| os_mutex_init(&nss->mtx); |
| i = os_task_init(&nss->task, "socket", socket_task, &native_sock_state, |
| MYNEWT_VAL(NATIVE_SOCKETS_PRIO), OS_WAIT_FOREVER, sp, |
| MYNEWT_VAL(NATIVE_SOCKETS_STACK_SZ)); |
| if (i) { |
| return -1; |
| } |
| i = mn_socket_ops_reg(&native_sock_ops); |
| if (i) { |
| return -1; |
| } |
| |
| signal(SIGPIPE, SIG_IGN); |
| |
| return 0; |
| } |