/*- * Convert a network address from binary to printable numeric format. * This API is copied from INRIA's IPv6 implementation, but it is a * bit bogus in two ways: * * 1) There is no value in passing both an address family and * an address length; either one should imply the other, * or we should be passing sockaddrs instead. * 2) There should by contrast be /added/ a length for the buffer * that we pass in, so that programmers are spared the need to * manually calculate (read: ``guess'') the maximum length. * * Flash: the API is also the same in the NRL implementation, and seems to * be some sort of standard, so we appear to be stuck with both the bad * naming and the poor choice of arguments. */ char *addr2ascii (int af, const void *addrp, int len, char *buf) { static char staticbuf[64]; /* 64 for AF_LINK > 16 for AF_INET */ if (!buf) buf = staticbuf; switch (af) { case AF_INET: if (len != sizeof (struct in_addr)) { SOCK_ERR (ENAMETOOLONG); return (NULL); } strcpy (buf, inet_ntoa (*(const struct in_addr *) addrp)); break; case AF_LINK: if (len != sizeof (struct sockaddr_dl)) { SOCK_ERR (ENAMETOOLONG); return (NULL); } strcpy (buf, link_ntoa ((const struct sockaddr_dl *) addrp)); break; default: SOCK_ERR (EPROTONOSUPPORT); return (NULL); } return (buf); }
/* * Raise SIGPIPE if signal defined. * Return -1 with errno = EPIPE. */ int _sock_sig_epipe (void) { #if defined(SIGPIPE) raise (SIGPIPE); #endif SOCK_ERR (EPIPE); return (-1); }
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 writev_s (int s, const struct iovec *vector, size_t count) { char *buffer, *bp; size_t i, to_copy, bytes = 0; /* Find the total number of bytes to write */ for (i = 0; i < count; i++) bytes += vector[i].iov_len; if (bytes == 0) return (0); /* Allocate a temporary buffer to hold the data */ buffer = alloca (bytes); if (!buffer) { SOCK_ERR (ENOMEM); return (-1); } to_copy = bytes; bp = buffer; /* Copy the data into buffer. */ for (i = 0; i < count; ++i) { size_t copy = min (vector[i].iov_len, to_copy); memcpy (bp, vector[i].iov_base, copy); bp += copy; to_copy -= copy; if (to_copy == 0) break; } return transmit ("writev_s", s, (const void*)buffer, bytes, 0, NULL, 0); }
/* * Allocate and fill local/remote addresses for 'clone'. * Take local address from 'socket', and remote address from * TCB of clone. */ static int alloc_addr (Socket *socket, Socket *clone) { struct in_addr peer; clone->local_addr = SOCK_CALLOC (sizeof(*clone->local_addr)); clone->remote_addr = SOCK_CALLOC (sizeof(*clone->remote_addr)); if (!clone->local_addr || !clone->remote_addr) { SOCK_DEBUGF ((socket, ", ENOMEM")); SOCK_ERR (ENOMEM); return (-1); } peer.s_addr = htonl (clone->tcp_sock->hisaddr); clone->local_addr->sin_family = AF_INET; clone->local_addr->sin_port = socket->local_addr->sin_port; clone->local_addr->sin_addr = socket->local_addr->sin_addr; clone->remote_addr->sin_family = AF_INET; clone->remote_addr->sin_port = htons (clone->tcp_sock->hisport); clone->remote_addr->sin_addr = peer; return (0); }
/* * Raw IP transmitter */ static int ip_transmit (Socket *socket, const void *tx, int len) { eth_address eth; u_long dest; unsigned tx_len, tx_room; sock_type *sk = (sock_type*)socket->udp_sock; struct ip *ip = (struct ip*) tx; const BYTE *buf = (const BYTE*) tx; WORD flags = 0; DWORD offset; UINT h_len, o_len; tcp_tick (NULL); /* process other TCBs too */ tcp_Retransmitter (1); /* This should never happen */ if (ip && (socket->so_state & SS_NBIO) && sock_tbleft(sk) < (len + socket->send_lowat)) { SOCK_DEBUGF ((socket, ", EWOULDBLOCK")); SOCK_ERR (EWOULDBLOCK); return (-1); } if (ip) { offset = ntohs (ip->ip_off); flags = offset & ~IP_OFFMASK; offset = (offset & IP_OFFMASK) << 3; /* 0 <= ip_ofs <= 65536-8 */ } SOCK_DEBUGF ((socket, ", %s / Raw", inet_ntoa(socket->remote_addr->sin_addr))); if (ip && (socket->inp_flags & INP_HDRINCL)) { dest = ip->ip_dst.s_addr; tx_len = len; tx_room = mtu; } else { dest = socket->remote_addr->sin_addr.s_addr; tx_len = len + sizeof (*ip); tx_room = mtu + sizeof (*ip); } if (!dest || !_arp_resolve(ntohl(dest),ð,0)) { SOCK_DEBUGF ((socket, ", no route")); SOCK_ERR (EHOSTUNREACH); STAT (ipstats.ips_noroute++); return (-1); } #if defined(USE_FRAGMENTS) if (!(socket->inp_flags & INP_HDRINCL) && tx_len + socket->ip_opt_len > tx_room) { sk = (sock_type*)socket->raw_sock; if (flags & IP_DF) { SOCK_DEBUGF ((socket, ", EMSGSIZE")); SOCK_ERR (EMSGSIZE); STAT (ipstats.ips_toolong++); return (-1); } return SEND_IP_FRAGMENTS (sk, sk->raw.ip_type, dest, buf, len); } #else if (!(socket->inp_flags & INP_HDRINCL) && tx_len + socket->ip_opt_len > tx_room) { SOCK_DEBUGF ((socket, ", EMSGSIZE")); SOCK_ERR (EMSGSIZE); STAT (ipstats.ips_toolong++); return (-1); } #endif ip = (struct ip*) _eth_formatpacket (ð, IP_TYPE); if (socket->inp_flags & INP_HDRINCL) { memcpy (ip, buf, len); if (ip->ip_src.s_addr == 0) { ip->ip_src.s_addr = gethostid(); ip->ip_sum = 0; ip->ip_sum = ~checksum ((void*)ip, ip->ip_hl << 2); } if (ip->ip_sum == 0) ip->ip_sum = ~checksum ((void*)ip, ip->ip_hl << 2); } else { if (socket->ip_opt && socket->ip_opt_len > 0) { BYTE *data; o_len = min (socket->ip_opt_len, sizeof(socket->ip_opt->ip_opts)); h_len = sizeof(*ip) + o_len; data = (BYTE*)ip + h_len; memcpy (ip+1, &socket->ip_opt->ip_opts, o_len); memcpy (data, buf, len); tx_len += o_len; if (socket->ip_opt->ip_dst.s_addr) /* using source routing */ dest = socket->ip_opt->ip_dst.s_addr; } else { if (buf) memcpy (ip+1, buf, len); h_len = sizeof (*ip); } ip->ip_v = IPVERSION; ip->ip_hl = h_len >> 2; ip->ip_tos = socket->ip_tos; ip->ip_len = htons (tx_len); ip->ip_id = _get_ip_id(); ip->ip_off = 0; ip->ip_ttl = socket->ip_ttl; ip->ip_p = socket->so_proto; ip->ip_src.s_addr = gethostid(); ip->ip_dst.s_addr = dest; ip->ip_sum = 0; ip->ip_sum = ~checksum (ip, h_len); } DEBUG_TX (NULL, ip); if (!_eth_send(tx_len)) { SOCK_DEBUGF ((socket, ", ENETDOWN")); SOCK_ERR (ENETDOWN); return (-1); } if (buf) buf += tx_len; return (len); }
/* * UDP transmitter */ static int udp_transmit (Socket *socket, const void *buf, int len) { sock_type *sk = (sock_type*) socket->udp_sock; u_long dest = socket->remote_addr->sin_addr.s_addr; int tx_room, rc; int is_bcast, is_multi; if (!tcp_tick(sk)) { socket->so_state |= SS_CANTSENDMORE; SOCK_DEBUGF ((socket, ", ENOTCONN (can't send)")); /* !! or EPIPE */ SOCK_ERR (ENOTCONN); return (-1); } tcp_Retransmitter (1); if ((socket->so_state & SS_NBIO) && check_non_block_tx(socket,&len) < 0) { SOCK_DEBUGF ((socket, ", EWOULDBLOCK")); SOCK_ERR (EWOULDBLOCK); return (-1); } is_bcast = (dest == INADDR_BROADCAST || dest == INADDR_ANY); is_multi = IN_MULTICAST (ntohl(socket->remote_addr->sin_addr.s_addr)); SOCK_DEBUGF ((socket, ", %s (%d) / UDP %s", inet_ntoa(socket->remote_addr->sin_addr), ntohs(socket->remote_addr->sin_port), is_multi ? "(mc)" : "")); if (len == 0) /* 0-byte probe packet */ return ip_transmit (socket, NULL, 0); tx_room = sock_tbleft (sk); /* always MTU-28 */ /* Special tests for broadcast messages */ if (is_bcast) { if (len > tx_room) /* don't allow fragments */ { SOCK_DEBUGF ((socket, ", EMSGSIZE")); SOCK_ERR (EMSGSIZE); STAT (ipstats.ips_odropped++); return (-1); } if (_pktserial) /* Link-layer doesn't allow broadcast */ { SOCK_DEBUGF ((socket, ", EADDRNOTAVAIL")); SOCK_ERR (EADDRNOTAVAIL); STAT (ipstats.ips_odropped++); return (-1); } } /* set new TTL if setsockopt() used before sending to Class-D socket */ if (is_multi) sk->udp.ttl = socket->ip_ttl; #if defined(USE_FRAGMENTS) if ((long)len > USHRT_MAX - sizeof(udp_Header)) { SOCK_DEBUGF ((socket, ", EMSGSIZE")); SOCK_ERR (EMSGSIZE); STAT (ipstats.ips_toolong++); return (-1); } if (len > tx_room) return SEND_IP_FRAGMENTS (sk, UDP_PROTO, dest, buf, len); #endif rc = sock_write (sk, (BYTE*)buf, len); if (rc <= 0) /* error in udp_write() */ { SOCK_DEBUGF ((socket, ", ENETDOWN")); SOCK_ERR (ENETDOWN); return (-1); } return (rc); }
/* * TCP transmitter */ static int tcp_transmit (Socket *socket, const void *buf, int len, int flags) { sock_type *sk = (sock_type*)socket->tcp_sock; tcp_tick (sk); tcp_Retransmitter (1); if (sk->tcp.state < tcp_StateESTAB || sk->tcp.state >= tcp_StateLASTACK) { socket->so_state |= SS_CANTSENDMORE; SOCK_DEBUGF ((socket, ", ENOTCONN (%s)", /* !! or EPIPE */ (sk->tcp.locflags & LF_GOT_FIN) ? "got FIN" : "can't send")); SOCK_ERR (ENOTCONN); return (-1); } if (socket->so_state & SS_NBIO) { int in_len = len; if (check_non_block_tx(socket,&len) < 0) { SOCK_DEBUGF ((socket, ", EWOULDBLOCK")); SOCK_ERR (EWOULDBLOCK); return (-1); } if (in_len != len) SOCK_DEBUGF ((socket, " [%d]", len)); /* trace "len=x [y]" */ } SOCK_DEBUGF ((socket, ", %s (%d) / TCP", inet_ntoa(socket->remote_addr->sin_addr), ntohs(socket->remote_addr->sin_port))); #if 0 /* Must wait for room in send buffer */ if ((flags & MSG_WAITALL) || len > sock_tbleft(sk)) len = sock_write (sk, (BYTE*)buf, len); else len = sock_fastwrite (sk, (BYTE*)buf, len); #else /* This is more efficient. The above sock_fastwrite() would * effectively turn off Nagle's algorithm. */ ARGSUSED (flags); len = sock_write (sk, (BYTE*)buf, len); #endif if (len <= 0) /* error in tcp_write() */ { if (sk->tcp.state != tcp_StateESTAB) { SOCK_DEBUGF ((socket, ", ENOTCONN")); SOCK_ERR (ENOTCONN); /* maybe EPIPE? */ } else { SOCK_DEBUGF ((socket, ", ENETDOWN")); SOCK_ERR (ENETDOWN); } return (-1); } return (len); }
/* * 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); }