/**************************************************************************** * net/tcp/tcp_accept.c * * 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. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #ifdef CONFIG_NET_TCP #include #include #include #include #include #include #include #include #include "socket/socket.h" #include "tcp/tcp.h" /**************************************************************************** * Private Types ****************************************************************************/ struct accept_s { sem_t acpt_sem; /* Wait for driver event */ FAR struct sockaddr *acpt_addr; /* Return connection address */ FAR socklen_t *acpt_addrlen; /* Return length of address */ FAR struct tcp_conn_s *acpt_newconn; /* The accepted connection */ int acpt_result; /* The result of the wait */ }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: accept_tcpsender * * Description: * Get the sender's address from the UDP packet * * Input Parameters: * listener - The connection structure of the listener * conn - The newly accepted TCP connection * pstate - the recvfrom state structure * * Returned Value: * None * * Assumptions: * The network is locked * ****************************************************************************/ static inline void accept_tcpsender(FAR struct tcp_conn_s *listener, FAR struct tcp_conn_s *conn, FAR struct sockaddr *addr, socklen_t *addrlen) { if (addr) { /* If an address is provided, then the length must also be provided. */ DEBUGASSERT(addrlen); #ifdef CONFIG_NET_IPv4 #ifdef CONFIG_NET_IPv6 /* If both IPv4 and IPv6 support are enabled, then we will need to * select which one to use when obtaining the sender's IP address. */ if (listener->domain == PF_INET) #endif /* CONFIG_NET_IPv6 */ { FAR struct sockaddr_in *inaddr = (FAR struct sockaddr_in *)addr; inaddr->sin_family = AF_INET; inaddr->sin_port = conn->rport; net_ipv4addr_copy(inaddr->sin_addr.s_addr, conn->u.ipv4.raddr); memset(inaddr->sin_zero, 0, sizeof(inaddr->sin_zero)); *addrlen = sizeof(struct sockaddr_in); } #endif /* CONFIG_NET_IPv4 */ #ifdef CONFIG_NET_IPv6 #ifdef CONFIG_NET_IPv4 /* Otherwise, this the IPv6 address is needed */ else #endif /* CONFIG_NET_IPv4 */ { FAR struct sockaddr_in6 *inaddr = (FAR struct sockaddr_in6 *)addr; #if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6) DEBUGASSERT(listener->domain == PF_INET6); #endif inaddr->sin6_family = AF_INET6; inaddr->sin6_port = conn->rport; net_ipv6addr_copy(inaddr->sin6_addr.s6_addr, conn->u.ipv6.raddr); *addrlen = sizeof(struct sockaddr_in6); } #endif /* CONFIG_NET_IPv6 */ } } /**************************************************************************** * Name: accept_eventhandler * * Description: * Receive event callbacks when connections occur * * Input Parameters: * listener The connection structure of the listener * conn The connection structure that was just accepted * * Returned Value: * None * * Assumptions: * The network is locked * ****************************************************************************/ static int accept_eventhandler(FAR struct tcp_conn_s *listener, FAR struct tcp_conn_s *conn) { struct accept_s *pstate = (struct accept_s *)listener->accept_private; int ret = -EINVAL; if (pstate) { /* Get the connection address */ accept_tcpsender(listener, conn, pstate->acpt_addr, pstate->acpt_addrlen); /* Save the connection structure */ pstate->acpt_newconn = conn; pstate->acpt_result = OK; /* There should be a reference of one on the new connection */ DEBUGASSERT(conn->crefs == 1); /* Wake-up the waiting caller thread */ nxsem_post(&pstate->acpt_sem); /* Stop any further callbacks */ listener->accept_private = NULL; listener->accept = NULL; ret = OK; } return ret; } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: tcp_accept * * Description: * This function implements accept() for TCP/IP sockets. See the * description of accept() for further information. * * Input Parameters: * psock The listening TCP socket structure * addr Receives the address of the connecting client * addrlen Input: allocated size of 'addr', Return: returned size of * 'addr' * newconn The new, accepted TCP connection structure * * Returned Value: * Returns zero (OK) on success or a negated errno value on failure. * See the description of accept of the possible errno values in the * description of accept(). * * Assumptions: * Network is locked. * ****************************************************************************/ int psock_tcp_accept(FAR struct socket *psock, FAR struct sockaddr *addr, FAR socklen_t *addrlen, FAR void **newconn) { FAR struct tcp_conn_s *conn; struct accept_s state; int ret; /* Check the backlog to see if there is a connection already pending for * this listener. */ conn = psock->s_conn; #ifdef CONFIG_NET_TCPBACKLOG state.acpt_newconn = tcp_backlogremove(conn); if (state.acpt_newconn) { /* Yes... get the address of the connected client */ ninfo("Pending conn=%p\n", state.acpt_newconn); accept_tcpsender(conn, state.acpt_newconn, addr, addrlen); } /* In general, this implementation will not support non-blocking socket * operations... except in a few cases: Here for TCP accept with * backlog enabled. If this socket is configured as non-blocking then * return EAGAIN if there is no pending connection in the backlog. */ else if (_SS_ISNONBLOCK(conn->sconn.s_flags)) { return -EAGAIN; } else #endif { /* Perform the TCP accept operation */ /* Initialize the state structure. This is done with the network * locked because we don't want anything to happen until we are * ready. */ state.acpt_addr = addr; state.acpt_addrlen = addrlen; state.acpt_newconn = NULL; state.acpt_result = OK; nxsem_init(&state.acpt_sem, 0, 0); /* Set up the callback in the connection */ conn->accept_private = (FAR void *)&state; conn->accept = accept_eventhandler; /* Wait for the send to complete or an error to occur: NOTES: * net_sem_wait will also terminate if a signal is received. */ ret = net_sem_wait(&state.acpt_sem); /* Make sure that no further events are processed */ conn->accept_private = NULL; conn->accept = NULL; nxsem_destroy(&state.acpt_sem); /* Check for a errors. Errors are signalled by negative errno values * for the send length. */ if (state.acpt_result != 0) { DEBUGASSERT(state.acpt_result > 0); return -state.acpt_result; } /* If net_sem_wait failed, then we were probably reawakened by a * signal. In this case, net_sem_wait will have returned negated * errno appropriately. */ if (ret < 0) { return ret; } } *newconn = (FAR void *)state.acpt_newconn; return OK; } #endif /* CONFIG_NET_TCP */