/* * connect() * "connect" will attempt to open a connection on a foreign IP address and * foreign port address. This is achieved by specifying the foreign IP * address and foreign port number in the "servaddr". */ int W32_CALL connect (int s, const struct sockaddr *servaddr, int addrlen) { struct sockaddr_in *addr = (struct sockaddr_in*) servaddr; struct sockaddr_in6 *addr6 = (struct sockaddr_in6*) servaddr; struct Socket *socket = _socklist_find (s); volatile int rc, sa_len; BOOL is_ip6; SOCK_PROLOGUE (socket, "\nconnect:%d", s); is_ip6 = (socket->so_family == AF_INET6); sa_len = is_ip6 ? sizeof(*addr6) : sizeof(*addr); if (_sock_chk_sockaddr(socket, servaddr, addrlen) < 0) return (-1); if (socket->so_type == SOCK_STREAM) { if (socket->so_state & SS_ISCONNECTED) { SOCK_DEBUGF ((", EISCONN")); SOCK_ERRNO (EISCONN); return (-1); } if (socket->so_options & SO_ACCEPTCONN) { SOCK_DEBUGF ((", EOPNOTSUPP (listen sock)")); SOCK_ERRNO (EOPNOTSUPP); return (-1); } if (!is_ip6 && IN_MULTICAST(ntohl(addr->sin_addr.s_addr))) { SOCK_DEBUGF ((", EINVAL (mcast)")); SOCK_ERRNO (EINVAL); return (-1); } else if (is_ip6 && IN6_IS_ADDR_MULTICAST(&addr6->sin6_addr)) { SOCK_DEBUGF ((", EINVAL (mcast)")); SOCK_ERRNO (EINVAL); return (-1); } } if (socket->remote_addr) { if ((socket->so_type == SOCK_STREAM) && (socket->so_state & SS_NBIO)) return nblk_connect (socket); SOCK_DEBUGF ((", connect already done!")); SOCK_ERRNO (EISCONN); return (-1); } socket->remote_addr = (struct sockaddr_in*) SOCK_CALLOC (sa_len); if (!socket->remote_addr) { SOCK_DEBUGF ((", ENOMEM (rem)")); SOCK_ERRNO (ENOMEM); return (-1); } #if defined(USE_IPV6) if (is_ip6) { struct sockaddr_in6 *ra = (struct sockaddr_in6*)socket->remote_addr; ra->sin6_family = AF_INET6; ra->sin6_port = addr6->sin6_port; memcpy (&ra->sin6_addr, &addr6->sin6_addr, sizeof(ra->sin6_addr)); } else #endif { socket->remote_addr->sin_family = AF_INET; socket->remote_addr->sin_port = addr->sin_port; socket->remote_addr->sin_addr = addr->sin_addr; } if (!socket->local_addr) { SOCK_DEBUGF ((", auto-binding")); socket->local_addr = (struct sockaddr_in*) SOCK_CALLOC (sa_len); if (!socket->local_addr) { free (socket->remote_addr); socket->remote_addr = NULL; SOCK_DEBUGF ((", ENOMEM (loc)")); SOCK_ERRNO (ENOMEM); return (-1); } #if defined(USE_IPV6) if (is_ip6) { struct sockaddr_in6 *la = (struct sockaddr_in6*)socket->local_addr; la->sin6_family = AF_INET6; la->sin6_port = htons (find_free_port(0,TRUE)); memcpy (&la->sin6_addr, &in6addr_my_ip, sizeof(la->sin6_addr)); } else #endif { socket->local_addr->sin_family = AF_INET; socket->local_addr->sin_port = htons (find_free_port(0,TRUE)); socket->local_addr->sin_addr.s_addr = htonl (my_ip_addr); } } SOCK_DEBUGF ((", src/dest ports: %u/%u", ntohs(socket->local_addr->sin_port), ntohs(socket->remote_addr->sin_port))); /* Not safe to run sock_daemon() now */ _sock_crit_start(); /* Setup SIGINT handler now. */ if (_sock_sig_setup() < 0) { SOCK_ERRNO (EINTR); SOCK_DEBUGF ((", EINTR")); _sock_crit_stop(); return (-1); } switch (socket->so_type) { case SOCK_STREAM: rc = tcp_connect (socket); break; case SOCK_DGRAM: rc = udp_connect (socket); break; case SOCK_RAW: rc = raw_connect (socket); break; default: SOCK_ERRNO (EPROTONOSUPPORT); rc = -1; break; } _sock_sig_restore(); _sock_crit_stop(); return (rc); }
/* * transmit() flags: * MSG_DONTROUTE (not supported) * MSG_EOR Close sending side after data sent * MSG_TRUNC (not supported) * MSG_CTRUNC (not supported) * MSG_OOB (not supported) * MSG_WAITALL Wait till room in tx-buffer (not supported) */ static int transmit (const char *func, int s, const void *buf, int len, int flags, const struct sockaddr *to, int tolen) { Socket *socket = _socklist_find (s); int rc; SOCK_DEBUGF ((socket, "\n%s:%d, len=%d", func, s, len)); if (!socket) { if (_sock_dos_fd(s)) { SOCK_DEBUGF ((NULL, ", ENOTSOCK")); SOCK_ERR (ENOTSOCK); return (-1); } SOCK_DEBUGF ((NULL, ", EBADF")); SOCK_ERR (EBADF); return (-1); } if (socket->so_type == SOCK_STREAM || /* TCP-socket or */ (socket->so_state & SS_ISCONNECTED)) /* "connected" udp/raw */ { /* Note: SOCK_RAW doesn't really need a local address/port, but * makes the code more similar for all socket-types. * Disadvantage is that SOCK_RAW ties up a local port and a bit * more memory. */ if (!socket->local_addr) { SOCK_DEBUGF ((socket, ", no local_addr")); SOCK_ERR (ENOTCONN); return (-1); } if (!socket->remote_addr) { SOCK_DEBUGF ((socket, ", no remote_addr")); SOCK_ERR (ENOTCONN); return (-1); } if (socket->so_state & SS_CONN_REFUSED) { if (socket->so_error == ECONNRESET) /* set in tcp_sockreset() */ { SOCK_DEBUGF ((socket, ", ECONNRESET")); SOCK_ERR (ECONNRESET); } else { SOCK_DEBUGF ((socket, ", ECONNREFUSED")); SOCK_ERR (ECONNREFUSED); } return (-1); } } /* connectionless protocol setup */ if (socket->so_type == SOCK_DGRAM || socket->so_type == SOCK_RAW) { if (!to || tolen < sizeof(*to)) { SOCK_DEBUGF ((socket, ", no to-addr")); SOCK_ERR (EINVAL); return (-1); } if (setup_udp_raw(socket,to,tolen) < 0) return (-1); } VERIFY_RW (buf, len); /* Setup SIGINT handler now. */ if (_sock_sig_setup() < 0) { SOCK_ERR (EINTR); return (-1); } switch (socket->so_type) { case SOCK_DGRAM: rc = udp_transmit (socket, buf, len); break; case SOCK_STREAM: rc = tcp_transmit (socket, buf, len, flags); break; case SOCK_RAW: rc = ip_transmit (socket, buf, len); break; default: SOCK_DEBUGF ((socket, ", EPROTONOSUPPORT")); SOCK_ERR (EPROTONOSUPPORT); rc = -1; } _sock_sig_restore(); if (rc >= 0 && (flags & MSG_EOR)) msg_eor_close (socket); return (rc); }
int accept (int s, struct sockaddr *addr, int *addrlen) { Socket *clone, *socket; volatile DWORD timeout; volatile int newsock = -1; volatile int que_idx; volatile int maxconn; socket = _socklist_find (s); SOCK_PROLOGUE (socket, "\naccept:%d", s); if (!socket->local_addr) { SOCK_DEBUGF ((socket, ", not bound")); SOCK_ERR (ENOTCONN); return (-1); } if (socket->so_type != SOCK_STREAM) { SOCK_DEBUGF ((socket, ", EOPNOTSUPP")); SOCK_ERR (EOPNOTSUPP); return (-1); } if (!(socket->so_options & SO_ACCEPTCONN)) /* listen() not called */ { SOCK_DEBUGF ((socket, ", not SO_ACCEPTCONN")); SOCK_ERR (EINVAL); return (-1); } if (!(socket->so_state & (SS_ISLISTENING | SS_ISCONNECTING))) { SOCK_DEBUGF ((socket, ", not listening")); SOCK_ERR (ENOTCONN); return (-1); } if (addr && addrlen) { if (*addrlen < sizeof(*addr)) { SOCK_DEBUGF ((socket, ", EFAULT")); SOCK_ERR (EFAULT); return (-1); } VERIFY_RW (addr, *addrlen); } /* Get max possible TCBs on listen-queue. * Some (or all) may be NULL until a SYN comes in. */ maxconn = socket->backlog; if (maxconn < 1 || maxconn > SOMAXCONN) { SOCK_FATAL (("%s(%d): Illegal socket backlog %d", __FILE__, __LINE__, maxconn)); SOCK_ERR (EINVAL); return (-1); } if (socket->timeout) timeout = set_timeout (1000 * socket->timeout); else timeout = 0UL; if (_sock_sig_setup() < 0) { SOCK_ERR (EINTR); goto accept_fail; } /* Loop over all queue-slots and accept first connected TCB */ for (que_idx = 0; ; que_idx = (++que_idx % maxconn)) { tcp_Socket *sk = socket->listen_queue [que_idx]; tcp_tick (NULL); SOCK_YIELD(); /* No SYNs received yet. This shouldn't happen if we called 'accept()' * after 'select_s()' said that socket was readable. (At least one * connection on the listen-queue). */ if (sk) { /* This could happen if 'accept()' was called too long after connection * was established and then closed by peer. This could also happen if * someone did a portscan on us. I.e. he sent 'SYN', we replied with * 'SYN+ACK' and he never sent an 'ACK'. Thus we timeout in * 'tcp_Retransmitter()' and abort the TCB. * * Queue slot is in any case ready for another 'SYN' to come and be * handled by '_sock_append()'. */ if (sk->state >= tcp_StateLASTACK && sk->ip_type == 0) { SOCK_DEBUGF ((socket, ", aborted TCB (idx %d)", que_idx)); listen_free (socket, que_idx); continue; } /* !!to-do: Should maybe loop over all maxconn TCBs and accept the * one with oldest 'syn_timestamp'. */ if (tcp_established(sk)) { SOCK_DEBUGF ((socket, ", connected! (idx %d)", que_idx)); break; } } /* We've polled all listen-queue slots and none are connected. * Return fail if socket is non-blocking. */ if (que_idx == maxconn-1 && (socket->so_state & SS_NBIO)) { SOCK_DEBUGF ((socket, ", would block")); SOCK_ERR (EWOULDBLOCK); goto accept_fail; } if (chk_timeout(timeout)) { SOCK_DEBUGF ((socket, ", ETIMEDOUT")); SOCK_ERR (ETIMEDOUT); goto accept_fail; } } /* We're here only when above 'tcp_established()' succeeded. * Now duplicate 'socket' into a new listening socket 'clone' * with handle 'newsock'. */ _sock_enter_scope(); newsock = dup_bind (socket, &clone, que_idx); if (newsock < 0) goto accept_fail; if (alloc_addr(socket, clone) < 0) { SOCK_DEL_FD (newsock); goto accept_fail; } /* Clone is connected, but *not* listening/accepting. * Note: other 'so_state' bits from parent is unchanged. * e.g. clone may be non-blocking. */ clone->so_state |= SS_ISCONNECTED; clone->so_state &= ~(SS_ISLISTENING | SS_ISCONNECTING); clone->so_options &= ~SO_ACCEPTCONN; /* Prevent a PUSH on first segment sent. */ sock_noflush ((sock_type*)clone->tcp_sock); SOCK_DEBUGF ((clone, "\nremote %s (%d)", inet_ntoa (clone->remote_addr->sin_addr), ntohs (clone->remote_addr->sin_port))); if (addr && addrlen) { struct sockaddr_in *sa = (struct sockaddr_in*)addr; sa->sin_family = AF_INET; sa->sin_port = clone->remote_addr->sin_port; sa->sin_addr = clone->remote_addr->sin_addr; memset (sa->sin_zero, 0, sizeof(sa->sin_zero)); *addrlen = sizeof(*sa); } _sock_leave_scope(); _sock_sig_restore(); return (newsock); accept_fail: _sock_leave_scope(); _sock_sig_restore(); return (-1); }