📄 socket.c
字号:
/*
*
* BSD sockets functionality for Waterloo TCP/IP
*
* Version
*
* 0.5 : Dec 18, 1997 : G. Vanem - created
*/
#include "socket.h"
#include "pcdbug.h"
#if defined(USE_BSD_FUNC)
#ifdef __DJGPP__
#include <sys/resource.h>
#include <sys/fsext.h>
#include <dos.h>
#include <unistd.h>
#endif
#ifdef USE_LIBPCAP
#include "w32pcap.h"
#endif
static int sk_block = 0; /* sock_daemon() semaphore */
static int sk_last = SK_FIRST; /* highest socket number */
static Socket *sk_list = NULL;
static BOOL sk_init = 0;
#if 0 /* !!to-do */
#define SOCK_HASH_SIZE (MAX_SOCKETS / 32)
static Socket *sk_hashes [SOCK_HASH_SIZE];
#endif
#define FREE_SK(x) do { \
if (x) { \
free (x); \
x = NULL; \
} \
} while (0)
/*
* Memory allocation; print some info if allocation fails
*/
void *_sock_calloc (const char *file, unsigned line, size_t size)
{
void *ptr;
#if defined(__WATCOM386__) && 0 /* find DOS4GW bug! */
int rc = _heapset (0xCC);
if (rc != _HEAPOK && rc != _HEAPEMPTY)
SOCK_FATAL (("%s (%u) Fatal: heap corrupt\r\n", file, line));
#endif
#if defined(USE_FORTIFY) || defined(USE_BSD_FORTIFY)
ptr = Fortify_calloc (size, 1, file, line);
#else
ptr = calloc (size, 1);
#endif
if (!ptr)
{
#if defined(__WATCOM386__) && 0 /* find DOS4GW bug! */
struct _heapinfo hi;
_heapwalk (&hi);
#endif
SOCK_FATAL (("%s (%u) Fatal: Allocation failed\r\n", file, line));
}
#if !defined(USE_BSD_FATAL) && !defined(USE_FORTIFY) && !defined(USE_BSD_FORTIFY)
ARGSUSED (file);
ARGSUSED (line);
#endif
return (ptr);
}
#if defined(USE_BSD_FORTIFY) /* to detect leaks done here */
#undef SOCK_CALLOC
#define SOCK_CALLOC(x) Fortify_calloc (x, 1, __FILE__, __LINE__)
#endif
/*
* `inuse[]' has a non-zero bit for each socket-descriptor in use.
* There can be max `MAX_SOCKETS' allocated at any time. Dead stream
* sockets will be unlinked by `sock_daemon()' in due time.
*
* Non-djgpp targets:
* Allocate a new descriptor (handle) by searching through `inuse' for
* the first zero bit. Update `sk_last' as needed.
*
* djgpp target:
* Allocate a descriptior from the "File System Extension" layer.
* `sk_last' is not used (initialised to MAX_SOCKETS).
*/
static fd_set inuse [NUM_SOCK_FDSETS];
static int sock_get_fd (void)
{
#if defined(__DJGPP__) && defined(USE_FSEXT)
extern int _fsext_demux (__FSEXT_Fnumber func, /* in fsext.c */
int *rv, va_list _args);
int s = __FSEXT_alloc_fd (_fsext_demux);
if (s < 0)
{
SOCK_FATAL (("%s (%u) Fatal: FSEXT_alloc_fd() failed\r\n",
__FILE__, __LINE__));
return (-1);
}
if (FD_ISSET(s,&inuse[0]))
{
SOCK_FATAL (("%s (%u) Fatal: Reusing existing socket\n",
__FILE__, __LINE__));
return (-1);
}
#else
int s;
for (s = SK_FIRST; s < sk_last; s++)
if (!FD_ISSET(s,&inuse[0]) && /* not marked as in-use */
!_socklist_find(s)) /* don't use a dying socket */
break;
#endif /* __DJGPP__ && USE_FSEXT */
if (s < MAX_SOCKETS)
{
if (s == sk_last)
sk_last++;
FD_SET (s, &inuse[0]);
return (s);
}
/* No vacant bits in 'inuse' array. djgpp (and DOS) could theoretically
* return a file-handle > 'MAX_SOCKETS-1'.
*/
return (-1);
}
/*
* _sock_dos_fd -
* Return TRUE if `s' is a valid DOS handle.
* Used to differentiate EBADF from ENOTSOCK.
*
* Note: for non-djgpp targets 's' may have same value as a
* DOS-handle. This function should only be used when 's'
* isn't found in 'sk_list'.
*/
int _sock_dos_fd (int s)
{
if (s >= fileno(stdin) && s <= fileno(stderr)) /* 0..2 (redirected) */
return (1);
if (s > fileno(stderr) && isatty(s))
return (1);
return (0);
}
/*
* Setup a bigger receive buffer, the default in Wattcp
* is only 2k.
* Note: If calloc() fails, sock_setbuf() reverts to default
* 2kB socket buffer.
* to-do: allow user to define size using SO_RCVBUF/SO_SNDBUF
* before calling connect().
*/
static __inline void set_rcv_buf (sock_type *p)
{
int len = DEFAULT_RCV_WIN;
sock_setbuf (p, calloc(len,1), len);
}
/*
* Free receive buffer associated with udp/tcp sockets
*/
static __inline void free_rcv_buf (sock_type *p)
{
if (p->udp.rdata != &p->udp.rddata[0])
{
free (p->udp.rdata);
p->udp.rdata = &p->udp.rddata[0];
p->udp.rdatalen = 0;
}
}
/*
* sk_list_del
* Deletes the list element associated with a socket.
* Return pointer to next node.
* Return NULL if no next or sock not found.
*/
static __inline Socket *sk_list_del (int s)
{
Socket *sock, *next, *last;
for (sock = last = sk_list; sock; last = sock, sock = sock->next)
{
if (sock->fd != s)
continue;
if (sock == sk_list)
sk_list = sock->next;
else last->next = sock->next;
next = sock->next;
free (sock);
return (next);
}
return (NULL);
}
/*
* Traverse socket-list to find other SOCK_STREAM sockets
* besides 'this' which are also listening.
* Unhook '_tcp_syn_hook' if none found.
*/
static __inline void unset_tcp_syn_hook (Socket *this)
{
Socket *sock;
int num = 0;
for (sock = sk_list; sock; sock = sock->next)
if (sock->so_type == SOCK_STREAM &&
(sock->so_options & SO_ACCEPTCONN) &&
sock != this)
num++;
if (num == 0)
_tcp_syn_hook = NULL;
}
/*
* Traverse socket-list to find other SOCK_RAW sockets
* besides 'this'. Unhook '_raw_ip_hook' if none found.
*/
static __inline void unset_raw_ip_hook (Socket *this)
{
Socket *sock;
int num = 0;
for (sock = sk_list; sock; sock = sock->next)
if (sock->so_type == SOCK_RAW && sock != this)
num++;
if (num == 0)
_raw_ip_hook = NULL;
}
/*
* _sock_del_fd
* Delete the socket from `inuse' array and all memory associated
* with it. Also unlink it from the socket list (sk_list).
* Return pointer to next node in list or NULL if none/error.
*/
Socket * _sock_del_fd (const char *file, unsigned line, int s)
{
Socket *sock, *next = NULL;
sock_type *sk;
SOCK_DEBUGF ((NULL, "\n _sock_del_fd:%d", s));
if (s < SK_FIRST || s >= sk_last || !FD_ISSET(s,&inuse[0]))
{
SOCK_FATAL (("%s (%u) Fatal: socket %d not inuse\r\n", file, line, s));
return (NULL);
}
sock = _socklist_find (s);
if (!sock)
{
SOCK_FATAL (("%s (%u) Fatal: socket %d not in list\r\n", file, line, s));
goto not_inuse;
}
if (sock->cookie != SAFETYTCP) /* Aaarg! marker destroyed */
{
SOCK_FATAL (("%s (%u) fatal: socket %d (%p) overwritten\r\n",
file, line, s, sock));
goto not_inuse;
}
switch (sock->so_type)
{
case SOCK_STREAM:
sk = (sock_type*) sock->tcp_sock;
if (sk)
{
reuse_localport (sk->tcp.myport); /* clear 'lport_inuse' bit now */
sock_abort (sk);
free_rcv_buf (sk);
}
FREE_SK (sock->tcp_sock);
unset_tcp_syn_hook (sock);
break;
case SOCK_DGRAM:
sk = (sock_type*) sock->udp_sock;
if (sk)
{
reuse_localport (sk->udp.myport);
sock_abort (sk);
free_rcv_buf (sk);
}
FREE_SK (sock->udp_sock);
break;
case SOCK_RAW:
sock->raw_sock->ip_type = 0;
sock->raw_sock->next = NULL;
FREE_SK (sock->raw_sock);
unset_raw_ip_hook (sock);
break;
default:
SOCK_DEBUGF ((NULL, "\n _sock_del_fd(%d): unknown type %d",
s, sock->so_type));
break;
}
FREE_SK (sock->local_addr);
FREE_SK (sock->remote_addr);
FREE_SK (sock->ip_opt);
FREE_SK (sock->bcast_pool);
#if defined(USE_FSEXT) && defined(__DJGPP__)
/* Free the socket from File-System Extension system.
* Free the duplicated handle from DOS's System File Table.
*/
__FSEXT_set_function (s, NULL);
_close (s);
#endif
next = sk_list_del (s); /* delete socket from linked list */
not_inuse:
if (s == sk_last-1)
sk_last--;
FD_CLR (s, &inuse[0]);
#if !defined(USE_DEBUG)
ARGSUSED (file);
ARGSUSED (line);
#endif
return (next);
}
#ifdef NOT_USED
/*
* sock_find_fd
* Finds the 'fd' associated with pointer 'socket'.
* Return -1 if not found.
*/
static int sock_find_fd (const Socket *socket)
{
Socket *sock;
for (sock = sk_list; sock; sock = sock->next)
if (sock == socket)
return (sock->fd);
return (-1);
}
/*
* sock_find_udp
* Finds the 'Socket' associated with udp-socket 'udp'.
* Return NULL if not found.
*/
static Socket *sock_find_udp (const udp_Socket *udp)
{
Socket *sock;
for (sock = sk_list; sock; sock = sock->next)
if (sock->udp_sock == udp)
return (sock);
return (NULL);
}
#endif
/*
* sock_find_tcp
* Finds the 'Socket' associated with tcp-socket 'tcp'.
* Return NULL if not found.
*/
static void *sock_find_tcp (const tcp_Socket *tcp)
{
Socket *sock;
for (sock = sk_list; sock; sock = sock->next)
if (sock->tcp_sock == tcp)
return (void*)sock;
return (NULL);
}
/*
* sock_raw_recv - Called from _ip_handler() via `_raw_ip_hook'.
* IP-header is already checked in _ip_handler().
* Finds all 'Socket' associated with raw IP-packet 'ip'.
* Enqueue to 'sock->raw_sock'.
* Return >=1 if 'ip' is consumed, 0 otherwise.
*
* Fix-me: This routine will steal all packets destined for
* SOCK_STREAM/SOCK_DGRAM sockets if those sockets are
* allocated after the SOCK_RAW socket (behind in sk_list).
*/
static int sock_raw_recv (const in_Header *ip)
{
Socket *sock;
int num_enqueued = 0;
int num_dropped = 0;
int hlen = in_GetHdrLen (ip);
DWORD dst = ntohl (ip->destination);
size_t len = ntohs (ip->length);
/* Jumbo packets won't match any raw-sockets
*/
if (len > sizeof(sock->raw_sock->data))
return (0);
/* Not addressed to us or not (limited) broadcast
*/
if (!is_local_addr(dst) && !is_ip_brdcast(ip))
return (0);
for (sock = sk_list; sock; sock = sock->next)
{
#if 0 /* !! to-do */
if (sock->so_type == SOCK_RAW && sock->so_proto == IPPROTO_RAW)
; /* socket matches every IP-protocol, enqueue */
#endif
if (ip->proto == IPPROTO_TCP && sock->so_type == SOCK_STREAM)
return (0);
if (ip->proto == IPPROTO_UDP && sock->so_type == SOCK_DGRAM)
return (0);
if (sock->so_type != SOCK_RAW ||
(ip->proto != sock->so_proto && sock->so_proto != IPPROTO_IP))
continue;
/* !!to-do: follow the 'sock->raw_sock->next' pointer to first
* vacant buffer.
* assumes sock->raw_sock is non-NULL
*/
if (sock->raw_sock->used)
{
num_dropped++;
SOCK_DEBUGF ((sock, "\n socket %d dropped IP, proto %d",
sock->fd, ip->proto));
}
else
{
/* Copy IP-header to raw_sock.ip
*/
memcpy (&sock->raw_sock->ip, ip, sizeof(*ip));
/* Copy any IP-options
*/
if (hlen > sizeof(*ip) && sock->ip_opt)
{
int olen = min (sock->ip_opt_len, hlen - sizeof(*ip));
memcpy (&sock->ip_opt, ip+1, olen);
}
/* Copy rest of IP-packet
*/
memcpy (&sock->raw_sock->data, (BYTE*)ip+hlen, len);
sock->raw_sock->used = TRUE;
num_enqueued++;
}
}
if (num_enqueued > 0) /* both enqueued and dropped is possible */
STAT (ipstats.ips_delivered++);
if (num_dropped > 0)
STAT (ipstats.ips_idropped++);
#ifdef USE_DEBUG
if (num_dropped > 0 || num_enqueued > 0)
DEBUG_RX (NULL, ip);
#endif
return (num_enqueued);
}
/*
* tcp_sock_daemon -
* Called by sock_daemon() to handle SOCK_STREAM sockets.
*
* Unlink the socket from the linked list if application has
* read all data and tcp_state has become CLOSED and the linger
* period has expired.
*
*/
static Socket *tcp_sock_daemon (Socket *sock, tcp_Socket *tcp)
{
Socket *next = sock->next;
int s = sock->fd;
int state = tcp->state;
if ((sock->so_options & SO_KEEPALIVE) && chk_timeout(sock->keepalive))
{
sock_keepalive ((sock_type*)tcp);
if (tcp_keepalive)
sock->keepalive = set_timeout (1000 * tcp_keepalive);
else sock->keepalive = 0;
}
if (state == tcp_StateSYNSENT) /* opening active tcp session */
{
sock->so_state |= SS_ISCONNECTING;
}
else if (state == tcp_StateESTAB) /* established tcp session */
{
sock->so_state |= SS_ISCONNECTED;
sock->so_state &= ~SS_ISCONNECTING;
sock->so_state &= ~SS_ISDISCONNECTING;
}
else if (state >= tcp_StateTIMEWT) /* dying tcp session */
{
sock_type *sk = (sock_type*)tcp;
int closing = sock->so_state & (SS_ISDISCONNECTING | SS_CANTSENDMORE);
sock->so_state &= ~(SS_ISCONNECTED | SS_ISCONNECTING);
if (sock->close_time && (sock->so_state & SS_CANTRCVMORE))
{
/* Flush any remaining Rx data received after shutdown(0) called.
*/
sock_fastread (sk, NULL, -1);
}
if (closing && tcp->ip_type == 0) /* fully closed, refused or aborted */
{
int expired = 0;
if (!sock_rbused(sk))
{
free_rcv_buf (sk); /* free memory not needed anymore */
FREE_SK (sock->ip_opt);
}
if (sock->close_time) /* close_s() called */
expired = (time(NULL) - sock->close_time >= sock->linger_time);
/* If linger-period expired and fully closed, delete the TCB
*/
if (expired && state == tcp_StateCLOSED)
{
SOCK_DEBUGF ((sock, "\n tcp_sock_daemon del:%d, lport %d",
s, tcp->myport));
next = SOCK_DEL_FD (s);
}
}
}
return (next);
}
/*
* Called by sock_daemon() for SOCK_DGRAM sockets.
*
* Unlink the socket from the linked list if application have read all
* data and if "state" is disconnecting.
*
* Note: Setting 'SS_ISDISCONNECTING' is really a mis-nomer, but
* should indicate socket is closed/aborted with Rx-data remaining.
*/
static Socket *udp_sock_daemon (Socket *sock, udp_Socket *udp)
{
Socket *next = sock->next;
if ((sock->so_state & (SS_ISDISCONNECTING | SS_CANTSENDMORE)) &&
(udp->rdatalen == 0 || udp->ip_type == 0))
{
SOCK_DEBUGF ((sock, "\n udp_sock_daemon del:%d", sock->fd));
next = SOCK_DEL_FD (sock->fd);
}
return (next);
}
/*
* Called from tcp_tick(), but not more than once every 55msec
*/
static void sock_daemon (void)
{
Socket *sock, *next = NULL;
/* If we're in a critical region (e.g. select_s()) where we don't
* want our socket-list to change, do this later.
*/
if (sk_block)
return;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -