268 lines
7.2 KiB
C
268 lines
7.2 KiB
C
/*
|
|
* This file is a contribution of David Tolpin (dvd@pizza.msk.su)
|
|
* It is an implementation of BSD-INET sockets and is known to run on
|
|
* Solaris 1 and Linux.
|
|
*
|
|
* (prepare-server-socket portnum)
|
|
* bound socket to a local address. Returns an object of type socket-handle
|
|
*
|
|
* (release-server-socket! handle)
|
|
* close server socket (created by prepare-server-socket
|
|
*
|
|
* (socket-handle? handle)
|
|
* returns truth if the handle is of type socket-handle, false otherwise
|
|
*
|
|
* (listen-socket! handle)
|
|
* listen for connection requests
|
|
*
|
|
* (accept-connection handle)
|
|
* returns a new socket in response to a detected connection request,
|
|
* the return value is a list of two ports,
|
|
* - (car sp) is opened for reading,
|
|
* - (cadr sp) - for writing
|
|
*
|
|
* (open-client-socket hostname portnum)
|
|
* connect to a socket on a remote machine, returns the same data structure
|
|
* as the function described above
|
|
*
|
|
* (shutdown-connection! skt)
|
|
* shutdown socket, the mode of shutting down is determined according to
|
|
* the mode of the port (read or write)
|
|
*/
|
|
|
|
#include "stk.h"
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <netdb.h>
|
|
|
|
PRIMITIVE prepare_server_socket(SCM portnum);
|
|
PRIMITIVE release_server_socket(SCM handle);
|
|
PRIMITIVE socket_handlep(SCM handle);
|
|
PRIMITIVE listen_socket(SCM handle);
|
|
PRIMITIVE accept_connection(SCM handle);
|
|
PRIMITIVE open_client_socket(SCM hostname, SCM portnum);
|
|
PRIMITIVE shutdown_connection(SCM skt);
|
|
|
|
/*
|
|
: stk_socket.c,v 1.4 1994/06/26 19:14:55 dvd Exp dvd $
|
|
*/
|
|
|
|
/*
|
|
: stk_socket.c,v $
|
|
* Revision 1.4 1994/06/26 19:14:55 dvd
|
|
* *** empty log message ***
|
|
*
|
|
* Revision 1.3 1994/06/26 18:55:27 dvd
|
|
* Verbose error reporting is added
|
|
*
|
|
*/
|
|
|
|
#ifdef __sun__
|
|
extern char *sys_errlist[];
|
|
#endif
|
|
|
|
struct socket_handle {
|
|
int portnum;
|
|
char *hostname;
|
|
int handle;
|
|
};
|
|
|
|
static int tc_sockhandle;
|
|
|
|
static void free_sockhandle(SCM handle);
|
|
static void mark_sockhandle(SCM handle);
|
|
static void displ_sockhandle(SCM x, FILE *f, int mode);
|
|
|
|
static extended_scheme_type sockhandle_type = {
|
|
"sockhandle", /* name */
|
|
0, /* is_procp */
|
|
mark_sockhandle, /* gc_mark_fct */
|
|
free_sockhandle, /* gc_free_fct */
|
|
NULL, /* apply_fct */
|
|
displ_sockhandle /* display_fct */
|
|
};
|
|
|
|
|
|
#define SOCKHANDLE(x) ((struct socket_handle*)(x->storage_as.extension.data))
|
|
#define LSOCKHANDLE(x) (x->storage_as.extension.data)
|
|
#define SOCKHANDLEP(x) (TYPEP(x,tc_sockhandle))
|
|
#define NSOCKHANDLEP(x) (NTYPEP(x,tc_sockhandle))
|
|
|
|
void mark_sockhandle(SCM handle)
|
|
{
|
|
}
|
|
|
|
void free_sockhandle(SCM handle)
|
|
{
|
|
struct socket_handle *sh;
|
|
sh = SOCKHANDLE(handle);
|
|
if(sh->hostname) free(sh->hostname);
|
|
close(sh->handle);
|
|
free(sh);
|
|
LSOCKHANDLE(handle) = NULL;
|
|
}
|
|
|
|
void displ_sockhandle(SCM handle, FILE *f, int mode)
|
|
{
|
|
struct socket_handle *sh;
|
|
sh = SOCKHANDLE(handle);
|
|
sprintf(tkbuffer, "#[socket-handle %s %i]", sh->hostname, sh->portnum);
|
|
Puts(tkbuffer,f);
|
|
}
|
|
|
|
|
|
static SCM makesp(int s, char *hn, int portnum)
|
|
{
|
|
int t;
|
|
int hnlen;
|
|
FILE *fs, *ft;
|
|
SCM zs, zt;
|
|
long flag;
|
|
|
|
flag = no_interrupt(1);
|
|
|
|
t = dup(s); /* duplicate handles so that we are able to access one socket channel */
|
|
/* via two scheme ports */
|
|
if(!((fs = fdopen(s, "r")) && (ft = fdopen(s, "w"))))
|
|
err("internal(makesp): cannot create ports", NIL);
|
|
NEWCELL(zs, tc_iport);
|
|
NEWCELL(zt, tc_oport);
|
|
zs->storage_as.port.f = fs; setbuf(fs, NULL); /* unbuffered input/output */
|
|
zt->storage_as.port.f = ft; setbuf(ft, NULL);
|
|
zs->storage_as.port.name = (char*)must_malloc((hnlen = strlen(hn))+16);
|
|
sprintf(zs->storage_as.port.name, "%s:%i(r)", hn, portnum);
|
|
zt->storage_as.port.name = (char*)must_malloc((hnlen = strlen(hn))+16);
|
|
sprintf(zt->storage_as.port.name, "%s:%i(w)", hn, portnum);
|
|
|
|
no_interrupt(flag);
|
|
return(cons(zs, cons(zt, NIL)));
|
|
}
|
|
|
|
PRIMITIVE prepare_server_socket(SCM portnum)
|
|
{
|
|
struct sockaddr_in sin;
|
|
int s;
|
|
long flag;
|
|
SCM ys;
|
|
|
|
if(NINTEGERP(portnum))
|
|
err("not a port number", portnum);
|
|
sin.sin_port = INTEGER(portnum);
|
|
sin.sin_addr.s_addr = INADDR_ANY;
|
|
if((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
|
err(sys_errlist[errno], portnum);
|
|
if(bind(s, (struct sockaddr*)&sin, sizeof sin) < 0)
|
|
switch(errno) {
|
|
case EADDRINUSE:
|
|
case EADDRNOTAVAIL: {
|
|
SCM errcode;
|
|
NEWCELL(errcode, tc_integer);
|
|
SET_INTEGER(errcode, errno);
|
|
return errcode;
|
|
}
|
|
break;
|
|
default: err(sys_errlist[errno], portnum);
|
|
}
|
|
/* now we're ready to create the object */
|
|
NEWCELL(ys, tc_sockhandle);
|
|
LSOCKHANDLE(ys) = (struct socket_handle*)must_malloc(sizeof (struct socket_handle));
|
|
SOCKHANDLE(ys)->portnum = sin.sin_port;
|
|
SOCKHANDLE(ys)->hostname = (char*)must_malloc(strlen("localhost")+1);
|
|
strcpy(SOCKHANDLE(ys)->hostname, "localhost");
|
|
SOCKHANDLE(ys)->handle = s;
|
|
|
|
return ys;
|
|
}
|
|
|
|
|
|
PRIMITIVE release_server_socket(SCM handle)
|
|
{
|
|
if(NSOCKHANDLEP(handle)) err("not a socket handle", handle);
|
|
close(SOCKHANDLE(handle)->handle);
|
|
return UNDEFINED;
|
|
}
|
|
|
|
PRIMITIVE socket_handlep(SCM handle)
|
|
{
|
|
return SOCKHANDLEP(handle)? truth: ntruth;
|
|
}
|
|
|
|
PRIMITIVE listen_socket(SCM handle)
|
|
{
|
|
if(NSOCKHANDLEP(handle))
|
|
err("not a socket handle", handle);
|
|
if(listen(SOCKHANDLE(handle)->handle, 5) < 0)
|
|
err(sys_errlist[errno], handle);
|
|
return UNDEFINED;
|
|
}
|
|
|
|
PRIMITIVE accept_connection(SCM handle)
|
|
{
|
|
int s;
|
|
|
|
if(NSOCKHANDLEP(handle))
|
|
err("not a socket handle", handle);
|
|
if((s = accept(SOCKHANDLE(handle)->handle, NULL, NULL)) < 0)
|
|
err(sys_errlist[errno], handle);
|
|
return makesp(s, SOCKHANDLE(handle)->hostname, SOCKHANDLE(handle)->portnum);
|
|
}
|
|
|
|
PRIMITIVE open_client_socket(SCM hostname, SCM portnum)
|
|
{
|
|
char *hn;
|
|
struct hostent *hp;
|
|
struct sockaddr_in server;
|
|
int s;
|
|
|
|
if(NSTRINGP(hostname)) err("bad hostname", hostname);
|
|
if(NINTEGERP(portnum)) err("bad port number", portnum);
|
|
hp = gethostbyname(hn = CHARS(hostname));
|
|
if(!hp) err("unknown or misspelled host name", hostname);
|
|
bzero((char*)&server,sizeof server);
|
|
bcopy(hp->h_addr,(char*)&server.sin_addr, hp->h_length);
|
|
server.sin_family = hp->h_addrtype;
|
|
server.sin_port = INTEGER(portnum);
|
|
if((s = socket(AF_INET,SOCK_STREAM,0)) < 0)
|
|
err(sys_errlist[errno], NIL);
|
|
if(connect(s, (struct sockaddr *)&server, sizeof server) < 0)
|
|
switch(errno) {
|
|
case EADDRINUSE:
|
|
case EADDRNOTAVAIL:
|
|
case ETIMEDOUT:
|
|
case ECONNREFUSED: {
|
|
SCM errcode;
|
|
NEWCELL(errcode, tc_integer);
|
|
SET_INTEGER(errcode, errno);
|
|
return errcode;
|
|
}
|
|
break;
|
|
default: err(sys_errlist[errno], NIL);
|
|
}
|
|
return makesp(s, hn, server.sin_port);
|
|
}
|
|
|
|
PRIMITIVE shutdown_connection(SCM skt)
|
|
{
|
|
if(NIPORTP(skt) && NOPORTP(skt))
|
|
err("not a port", skt);
|
|
if(shutdown(fileno(skt->storage_as.port.f), IPORTP(skt)?0:1) < 0)
|
|
err(sys_errlist[errno], NIL);
|
|
return UNDEFINED;
|
|
}
|
|
|
|
void init_socket(void)
|
|
{
|
|
tc_sockhandle = add_new_type(&sockhandle_type);
|
|
|
|
add_new_primitive("prepare-server-socket", tc_subr_1, prepare_server_socket);
|
|
add_new_primitive("release-server-socket!", tc_subr_1, release_server_socket);
|
|
add_new_primitive("socket-handle?", tc_subr_1, socket_handlep);
|
|
add_new_primitive("listen-socket!", tc_subr_1, listen_socket);
|
|
add_new_primitive("accept-connection", tc_subr_1, accept_connection);
|
|
add_new_primitive("open-client-socket", tc_subr_2, open_client_socket);
|
|
add_new_primitive("shutdown-connection!", tc_subr_1, shutdown_connection);
|
|
};
|
|
|