📄 sflsock.c
字号:
/* ----------------------------------------------------------------<Prolog>-
Name: sflsock.c
Title: TCP/IP, UDP/IP socket functions
Package: Standard Function Library (SFL)
Written: 1996/02/03 iMatix SFL project team <sfl@imatix.com>
Revised: 2000/01/19
Notes: The BeOS port of sflsock is incomplete: at last testing,
BeOS 4.5 gave consistent errors on its select() function,
apparently due to timer interrupt signals.
Copyright: Copyright (c) 1996-2000 iMatix Corporation
License: This is free software; you can redistribute it and/or modify
it under the terms of the SFL License Agreement as provided
in the file LICENSE.TXT. This software is distributed in
the hope that it will be useful, but without any warranty.
------------------------------------------------------------------</Prolog>-*/
#include "prelude.h" /* Universal header file */
#include "sfllist.h" /* List-management functions */
#include "sflmem.h" /* Memory-allocation functions */
#include "sflsymb.h" /* Symbol-table functions */
#include "sfltok.h" /* Token-handling functions */
#include "sfluid.h" /* User/group functions */
#include "sflcons.h" /* Console i/o functions */
#include "sflfile.h" /* File handling functions */
#include "sflprint.h" /* snprintf functions */
#include "sflsock.h" /* Prototypes for functions */
/* Implementation notes
*
* These functions work on 16-bit Windows, 32-bit Windows, 32-bit UNIX,
* 64-bit UNIX, Digital OpenVMS. The size of a socket handle varies from
* 16 bits to 64 bits. All native socket functions define a socket handle
* as 'int'. However, we need a fixed-length external representation. So,
* we define a type, 'sock_t', which is a qbyte (32 bits). Outside this
* package, sockets are always a 'sock_t'. Internally, we always use an
* (SOCKET) cast when passing s sock_t to a system function like connect().
* If the system does not support sockets we fake them just a little.
*
* Modifications Oct 7 1998 for Unix by Grant McDorman <grant@isgtec.com> to
* allow running with the program suid root; it will run as the user until
* the socket must be opened; at that time, it will briefly switch to root
* and then return to the actual user id.
*/
/* Global variables */
int
ip_portbase = 0; /* Base for created services */
Bool
ip_nonblock = TRUE; /* Create non-blocking sockets */
qbyte
ip_passive = INADDR_ANY; /* IP address for passive connects */
int
ip_sockets = 0; /* Number of open sockets */
/* The connect_error_value holds the last recorded error cause after a */
/* connection attempt. */
static int
connect_error_value = 0;
char
*connect_errlist [] = { /* Corresponding error messages */
"No errors",
"System does not support sockets",
"Host is not known",
"Service or port not known",
"Protocol not known",
"Connection failed on socket()",
"Connection failed on connect()",
"Port is already used by another server",
"Connection failed on listen()"
};
/* Internal functions used to create passive and active connections */
#if (defined (DOES_SOCKETS))
static void prepare_socket (sock_t handle);
# if (defined (__WINDOWS__))
static int win_error (int rc);
# endif
#endif
/* ---------------------------------------------------------------------[<]-
Function: sock_init
Synopsis: Initialise the internet protocol. On most systems this is a
null call. On some systems this loads dynamic libraries. Returns 0
0 if everything was okay, else returns SOCKET_ERROR. You should call
sock_term() when your program ends.
---------------------------------------------------------------------[>]-*/
int
sock_init (void)
{
#if (defined (__WINDOWS__))
WORD
wVersionRequested; /* We really want Winsock 1.1 */
WSADATA
wsaData;
wVersionRequested = 0x0101; /* ... but we'll take 1.1 */
if (WSAStartup (wVersionRequested, &wsaData) == 0)
return (0);
else
return ((int) SOCKET_ERROR);
#elif (defined (__UTYPE_BEOS))
/* BeOS numbers sockets from 0 upwards, but this causes havoc with
* programs that expect a BSD-style numbering of 1 or higher. We
* force compatibility by creating (and wasting) one socket so that
* further socket handles are guaranteed >0.
*/
create_socket ("tcp");
return (0);
#elif (defined (DOES_SOCKETS))
return (0);
#elif (defined (FAKE_SOCKETS))
return (0);
#else
connect_error_value = IP_NOSOCKETS;
return ((int) SOCKET_ERROR); /* Sockets not supported */
#endif
}
/* ---------------------------------------------------------------------[<]-
Function: sock_term
Synopsis: Shuts-down the internet protocol. On most systems this is a
null call. On some systems this unloads dynamic libraries. Returns -1
if there was an error, or 0 if everything was okay. See sock_init().
---------------------------------------------------------------------[>]-*/
int
sock_term (void)
{
#if (defined (__WINDOWS__))
WSACleanup ();
#endif
return (0);
}
/* ---------------------------------------------------------------------[<]-
Function: passive_TCP
Synopsis: Creates a passive bound TCP socket for the specified service.
Returns socket number or INVALID_SOCKET. If it returns INVALID_SOCKET,
you can get the reason for the error by calling connect_error (). This
may be one of:
<TABLE>
IP_NOSOCKETS Sockets not supported on this system
IP_BADSERVICE Service cannot be converted to port number
IP_BADPROTOCOL Cannot understand protocol name
IP_SOCKETERROR Cannot create the passive socket
IP_BINDERROR Cannot bind to the port
IP_LISTENERROR Cannot listen to port
</TABLE>
---------------------------------------------------------------------[>]-*/
sock_t
passive_TCP (
const char *service, /* Service name or port as string */
int queue_length /* Queue length for listen() */
)
{
ASSERT (service && *service);
ASSERT (queue_length > 0);
return (passive_socket (service, "tcp", queue_length));
}
/* ---------------------------------------------------------------------[<]-
Function: passive_UDP
Synopsis: Creates a passive UDP socket for the specified service.
Returns socket number or INVALID_SOCKET. If it returns INVALID_SOCKET,
you can get the reason for the error by calling connect_error (). This
may be one of:
<TABLE>
IP_NOSOCKETS Sockets not supported on this system
IP_BADSERVICE Service cannot be converted to port number
IP_BADPROTOCOL Cannot understand protocol name
IP_SOCKETERROR Cannot create the passive socket
IP_BINDERROR Cannot bind to the port
</TABLE>
---------------------------------------------------------------------[>]-*/
sock_t
passive_UDP (
const char *service /* Service name or port as string */
)
{
ASSERT (service && *service);
return (passive_socket (service, "udp", 0));
}
/* ---------------------------------------------------------------------[<]-
Function: passive_socket
Synopsis:
Creates a passive TCP or UDP socket. This function allows a server
program to create a master socket, so that connections can be accepted.
Used by the passive_TCP and passive_UDP functions. Returns a socket
number or INVALID_SOCKET. If it returns INVALID_SOCKET, you can get the
reason for the error by calling connect_error (). This may be one of:
<TABLE>
IP_NOSOCKETS Sockets not supported on this system
IP_BADSERVICE Service cannot be converted to port number
IP_BADPROTOCOL Cannot understand protocol name
IP_SOCKETERROR Cannot create the passive socket
IP_BINDERROR Cannot bind to the port
IP_LISTENERROR Cannot listen to port
</TABLE>
By default, opens a socket on all available IP addresses. You can open
the socket on a specific address, by setting the global variable
ip_passive to the address (in network order). This variable is reset
to INADDR_ANY after each call to passive_socket or one of the functions
that calls it.
---------------------------------------------------------------------[>]-*/
sock_t
passive_socket (
const char *service, /* Service name or port as string */
const char *protocol, /* Protocol "tcp" or "udp" */
int queue_length /* Queue length for TCP sockets */
)
{
#if (defined (DOES_SOCKETS))
struct servent
*pse; /* Service information entry */
struct sockaddr_in
sin; /* Internet end-point address */
sock_t
handle; /* Socket from socket() call */
ASSERT (service && *service);
ASSERT (protocol && *protocol);
connect_error_value = IP_NOERROR; /* Assume no errors */
memset ((void *) &sin, 0, sizeof (sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = ip_passive;
ip_passive = INADDR_ANY; /* Reset passive address */
/* To allow privileged operations, if possible */
set_uid_root ();
/* Map service name to port number */
pse = getservbyname (service, protocol);
if (pse)
sin.sin_port = htons ((dbyte) (ntohs (pse-> s_port) + ip_portbase));
else
{
sin.sin_port = atoi (service);
if (sin.sin_port + ip_portbase > 0)
sin.sin_port = htons ((dbyte) (sin.sin_port + ip_portbase));
else
{
connect_error_value = IP_BADSERVICE;
set_uid_user ();
return (INVALID_SOCKET);
}
}
handle = create_socket (protocol);
if (handle == INVALID_SOCKET) /* Cannot create the socket */
{
set_uid_user ();
return (INVALID_SOCKET);
}
/* Bind the socket */
if (bind ((SOCKET) handle, (struct sockaddr *) &sin,
sizeof (sin)) == SOCKET_ERROR)
{
connect_error_value = IP_BINDERROR;
set_uid_user ();
return (INVALID_SOCKET); /* Cannot bind to port */
}
set_uid_user ();
/* Specify incoming queue length for stream socket */
if (streq (protocol, "tcp")
&& listen ((SOCKET) handle, queue_length) == SOCKET_ERROR)
{
connect_error_value = IP_LISTENERROR;
return (INVALID_SOCKET); /* Cannot listen on port */
}
return (handle);
#elif (defined (FAKE_SOCKETS))
return (1); /* Return dummy handle */
#else
connect_error_value = IP_NOSOCKETS;
return (INVALID_SOCKET); /* Sockets not supported */
#endif
}
/* ---------------------------------------------------------------------[<]-
Function: create_socket
Synopsis:
Creates a TCP or UDP socket. The socket is not connected. To use
with TCP services you must bind or connect the socket. You can use
the socket with UDP services - e.g. read_UDP () - immediately. Returns
a socket number or INVALID_SOCKET, in which case you can get the reason
for the error by calling connect_error (). This may be one of:
<TABLE>
IP_NOSOCKETS Sockets not supported on this system
IP_BADPROTOCOL Cannot understand protocol name
IP_SOCKETERROR Cannot create the socket
</TABLE>
---------------------------------------------------------------------[>]-*/
sock_t
create_socket (
const char *protocol /* Protocol "tcp" or "udp" */
)
{
#if (defined (DOES_SOCKETS))
struct protoent
*ppe; /* Protocol information entry */
int
# if (!defined (__WINDOWS__))
true_value = 1, /* Boolean value for setsockopt() */
# endif
sock_type; /* Type of socket we want */
sock_t
handle; /* Socket from socket() call */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -