/* * Duplicate a SOCK_STREAM 'socket' to 'newconn'. Doesn't set * local/remote addresses. Transfer TCB from listen-queue[idx] of * 'socket' to TCB of 'clone'. */ static int dup_bind (Socket *sock, Socket **newconn, int idx) { int fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); if (fd >= 0) { Socket *clone = _socklist_find (fd); /* child gets state from parent */ clone->timeout = sock->timeout; clone->close_time = sock->close_time; clone->keepalive = sock->keepalive; clone->ip_tos = sock->ip_tos; clone->ip_ttl = sock->ip_ttl; clone->so_state = sock->so_state; clone->so_options = sock->so_options; /* TCB for clone is from listen-queue[idx]; free tcp_sock from * socket(). Reuse listen-queue slot for another SYN. */ free (clone->tcp_sock); clone->tcp_sock = sock->listen_queue[idx]; sock->listen_queue [idx] = NULL; sock->syn_timestamp[idx] = 0; *newconn = clone; } return (fd); }
int sock_fputs (const char *text, FILE *stream) { int s = fileno (stream); Socket *sock = _socklist_find (s); SOCK_PROLOGUE (sock, "\nsock_fputs:%d", s); return write_s (s, text, strlen(text)); }
int W32_CALL shutdown (int s, int how) { Socket *socket = _socklist_find (s); #if defined(USE_DEBUG) static char fmt[] = "\nshutdown:%d/??"; static char rrw[] = "r w rw"; how &= 3; fmt [sizeof(fmt)-3] = rrw [2*how]; fmt [sizeof(fmt)-2] = rrw [2*how+1]; #endif SOCK_PROLOGUE (socket, fmt, s); switch (how) { case SHUT_RD: socket->so_error = ESHUTDOWN; socket->so_state |= SS_CANTRCVMORE; /* socket->so_options &= ~SO_ACCEPTCONN; */ /** \todo For tcp, should send RST if we get * incoming data. Don't send ICMP error. */ return (0); case SHUT_WR: socket->so_error = ESHUTDOWN; socket->so_state |= SS_CANTSENDMORE; socket->so_state &= ~SS_ISLISTENING; /* socket->so_options &= ~SO_ACCEPTCONN; */ /** \todo For tcp, should send FIN when all Tx data * has been ack'ed. * close_s(s) should be same as shutdown(s,SHUT_WR) */ return (0); case SHUT_RDWR: socket->so_error = ESHUTDOWN; socket->so_state |= (SS_CANTRCVMORE | SS_CANTSENDMORE); socket->so_state &= ~SS_ISLISTENING; /* socket->so_options &= ~SO_ACCEPTCONN; */ return (0); } SOCK_ERRNO (EINVAL); return (-1); }
int W32_CALL getsockname (int s, struct sockaddr *name, int *namelen) { Socket *socket = _socklist_find (s); int sa_len; SOCK_PROLOGUE (socket, "\ngetsockname:%d", s); sa_len = (socket->so_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); if (!name || !namelen || (*namelen < sa_len)) { SOCK_DEBUGF ((", EINVAL")); SOCK_ERRNO (EINVAL); if (namelen) *namelen = 0; return (-1); } if (!socket->local_addr) { SOCK_DEBUGF ((", EINVAL")); SOCK_ERRNO (EINVAL); /* according to HP/UX manpage */ return (-1); } VERIFY_RW (name, *namelen); *namelen = sa_len; memcpy (name, socket->local_addr, sa_len); #if defined(USE_IPV6) if (socket->so_family == AF_INET6) { const struct sockaddr_in6 *la = (const struct sockaddr_in6*)socket->local_addr; SOCK_DEBUGF ((", %s (%d)", _inet6_ntoa(&la->sin6_addr), ntohs(la->sin6_port))); ARGSUSED (la); } else #endif { const struct sockaddr_in *la = socket->local_addr; SOCK_DEBUGF ((", %s (%d)", inet_ntoa(la->sin_addr), ntohs(la->sin_port))); ARGSUSED (la); } return (0); }
int shutdown (int s, int how) { Socket *socket = _socklist_find (s); #if defined(USE_DEBUG) static char fmt[] = "\nshutdown:%d/??"; if (how == SHUT_RD) strcpy (fmt+sizeof(fmt)-3, "r "); else if (how == SHUT_WR) strcpy (fmt+sizeof(fmt)-3, "w "); else if (how == SHUT_RDWR) strcpy (fmt+sizeof(fmt)-3, "rw"); #endif SOCK_PROLOGUE (socket, fmt, s); #if 0 /* if not connected, let close_s() do it */ if (!(socket->so_state & SS_ISCONNECTED)) return close_s (s); #endif switch (how) { case SHUT_RD: socket->so_state |= SS_CANTRCVMORE; socket->so_options &= ~SO_ACCEPTCONN; return (0); case SHUT_WR: socket->so_state |= SS_CANTSENDMORE; socket->so_state &= ~SS_ISLISTENING; socket->so_options &= ~SO_ACCEPTCONN; return (0); // return close_s (s); case SHUT_RDWR: socket->so_state |= SS_CANTRCVMORE; socket->so_state |= SS_CANTSENDMORE; socket->so_state &= ~SS_ISLISTENING; return close_s (s); } SOCK_ERR (EINVAL); return (-1); }
int W32_CALL getpeername (int s, struct sockaddr *name, int *namelen) { Socket *socket = _socklist_find (s); int sa_len; SOCK_PROLOGUE (socket, "\ngetpeername:%d", s); sa_len = (socket->so_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); if (!name || !namelen || (*namelen < sa_len)) { SOCK_DEBUGF ((", EINVAL")); SOCK_ERRNO (EINVAL); if (namelen) *namelen = 0; return (-1); } if (!(socket->so_state & SS_ISCONNECTED)) { SOCK_DEBUGF ((", ENOTCONN")); SOCK_ERRNO (ENOTCONN); return (-1); } VERIFY_RW (name, *namelen); *namelen = sa_len; memcpy (name, socket->remote_addr, *namelen); #if defined(USE_IPV6) if (socket->so_family == AF_INET6) { const struct sockaddr_in6 *ra = (const struct sockaddr_in6*)socket->remote_addr; SOCK_DEBUGF ((", %s (%d)", _inet6_ntoa(&ra->sin6_addr), ntohs(ra->sin6_port))); ARGSUSED (ra); } else #endif { const struct sockaddr_in *ra = socket->remote_addr; SOCK_DEBUGF ((", %s (%d)", inet_ntoa(ra->sin_addr), ntohs(ra->sin_port))); ARGSUSED (ra); } return (0); }
int sock_fgets (char *buf, int max, FILE *stream) { int rc, s = fileno (stream); Socket *sock = _socklist_find (s); sock_type *sk = NULL; SOCK_PROLOGUE (sock, "\nsock_fgets:%d", s); if (sock->tcp_sock) sk = (sock_type*) sock->tcp_sock; else if (sock->udp_sock) sk = (sock_type*) sock->udp_sock; rc = sk ? sock_gets (sk, (BYTE*)buf, max) : -1; SOCK_DEBUGF ((", rc %d", rc)); 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); }
/* * 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); }
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); }