1601 строка
36 KiB
C
1601 строка
36 KiB
C
/* -*- mode: C; mode: fold; -*- */
|
|
/*
|
|
Copyright (C) 2006-2017,2018 John E. Davis
|
|
*
|
|
This file is part of the S-Lang Library.
|
|
*
|
|
The S-Lang Library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any later version.
|
|
*
|
|
The S-Lang Library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
*
|
|
You should have received a copy of the GNU General Public License
|
|
along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
|
USA.
|
|
*/
|
|
|
|
/* This file was derived from the code in SLtcp.c distributed with slsh */
|
|
|
|
/*{{{ Include Files */
|
|
|
|
#define _BSD_SOURCE 1 /* to get struct ip_mreq */
|
|
#define _DEFAULT_SOURCE 1
|
|
#include "config.h"
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include <stdarg.h>
|
|
|
|
#include <setjmp.h>
|
|
#include <signal.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <time.h>
|
|
#ifdef HAVE_SYS_TIME_H
|
|
# include <sys/time.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_SOCKET_H
|
|
# include <socket.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_SOCKET_H
|
|
# include <sys/socket.h>
|
|
#endif
|
|
|
|
#if defined(__NT__)
|
|
# include <winsock.h>
|
|
# define USE_WINSOCK_SLTCP 1
|
|
#else
|
|
# if defined(__MINGW32__)
|
|
# define Win32_Winsock
|
|
# include <windows.h>
|
|
# define USE_WINSOCK_SLTCP 1
|
|
# endif
|
|
#endif
|
|
|
|
#ifdef USE_WINSOCK_SLTCP
|
|
# define USE_WINSOCK_SLTCP 1
|
|
#else
|
|
# include <netdb.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_UN_H
|
|
# include <sys/un.h> /* for AF_UNIX sockets */
|
|
#endif
|
|
|
|
#ifdef HAVE_NETINET_IN_H
|
|
# include <netinet/in.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_ARPA_INET_H
|
|
# include <arpa/inet.h>
|
|
#endif
|
|
|
|
#include <slang.h>
|
|
SLANG_MODULE(socket);
|
|
|
|
#ifndef h_errno
|
|
extern int h_errno;
|
|
#endif
|
|
|
|
/*}}}*/
|
|
|
|
static int SocketError = -1;
|
|
static int SocketHerrnoError = -1;
|
|
static int Socket_Type_Id = -1;
|
|
|
|
typedef struct Socket_Type Socket_Type;
|
|
typedef struct
|
|
{
|
|
int domain;
|
|
int (*connect)(Socket_Type *, int);
|
|
int (*bind)(Socket_Type *, int);
|
|
#define MAX_ACCEPT_REF_ARGS 4
|
|
Socket_Type *(*accept)(Socket_Type *, unsigned int, SLang_Ref_Type **);
|
|
void (*free_socket_data)(Socket_Type *);
|
|
}
|
|
Domain_Methods_Type;
|
|
|
|
struct Socket_Type
|
|
{
|
|
int fd; /* socket descriptor */
|
|
Domain_Methods_Type *methods;
|
|
VOID_STAR socket_data;
|
|
int domain;
|
|
int type;
|
|
int protocol;
|
|
};
|
|
|
|
static Socket_Type *create_socket (int, int, int, int);
|
|
static void free_socket (Socket_Type *);
|
|
static int close_socket (int);
|
|
|
|
/*{{{ Generic Routines */
|
|
|
|
static int Module_H_Errno = 0;
|
|
|
|
static SLFUTURE_CONST char *herror_to_string (int h)
|
|
{
|
|
#ifdef HOST_NOT_FOUND
|
|
if (h == HOST_NOT_FOUND)
|
|
return "The specified host is unknown";
|
|
#endif
|
|
|
|
#ifdef NO_ADDRESS
|
|
if (h == NO_ADDRESS)
|
|
return "The requested name is valid but does not have an IP address";
|
|
#endif
|
|
|
|
#ifdef NO_DATA
|
|
if (h == NO_DATA)
|
|
return "The requested name is valid but does not have an IP address";
|
|
#endif
|
|
|
|
#ifdef NO_RECOVERY
|
|
if (h == NO_RECOVERY)
|
|
return "A non-recoverable name server error occurred";
|
|
#endif
|
|
|
|
#ifdef TRY_AGAIN
|
|
if (h == TRY_AGAIN)
|
|
return "A temporary error occurred on an authoritative name server. Try again later";
|
|
#endif
|
|
|
|
return "Unknown h_error";
|
|
}
|
|
|
|
static void throw_herror (SLFUTURE_CONST char *what, int h)
|
|
{
|
|
Module_H_Errno = h;
|
|
SLang_verror (SocketHerrnoError, "%s: %s", what, herror_to_string(h));
|
|
}
|
|
|
|
static void throw_errno_error (SLFUTURE_CONST char *what, int e)
|
|
{
|
|
SLerrno_set_errno (e);
|
|
SLang_verror (SocketError, "%s: %s", what, SLerrno_strerror (e));
|
|
}
|
|
|
|
static int perform_connect (int fd, struct sockaddr *addr, unsigned int len, int throw_err)
|
|
{
|
|
while (-1 == connect (fd, addr, len))
|
|
{
|
|
#ifdef EINTR
|
|
if (errno == EINTR)
|
|
{
|
|
if (-1 == SLang_handle_interrupt ())
|
|
return -1;
|
|
continue;
|
|
}
|
|
#endif
|
|
/* The manpage indicates EAGAIN will be returned if no free ports exist.
|
|
* So allow the caller to handle that.
|
|
*/
|
|
if (throw_err)
|
|
throw_errno_error ("connect", errno);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int perform_bind (int fd, struct sockaddr *addr, unsigned int len)
|
|
{
|
|
while (-1 == bind (fd, addr, len))
|
|
{
|
|
#ifdef EINTR
|
|
if (errno == EINTR)
|
|
{
|
|
if (-1 == SLang_handle_interrupt ())
|
|
return -1;
|
|
continue;
|
|
}
|
|
#endif
|
|
/* The manpage indicates EAGAIN will be returned if no free ports exist.
|
|
* So allow the caller to handle that.
|
|
*/
|
|
throw_errno_error ("bind", errno);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static Socket_Type *perform_accept (Socket_Type *s, struct sockaddr *addr, unsigned int *lenp)
|
|
{
|
|
socklen_t addr_len;
|
|
Socket_Type *s1;
|
|
int fd1;
|
|
|
|
addr_len = *lenp;
|
|
while (-1 == (fd1 = accept (s->fd, addr, &addr_len)))
|
|
{
|
|
#ifdef EINTR
|
|
if (errno == EINTR)
|
|
{
|
|
if (-1 == SLang_handle_interrupt ())
|
|
return NULL;
|
|
continue;
|
|
}
|
|
#endif
|
|
throw_errno_error ("accept", errno);
|
|
return NULL;
|
|
}
|
|
*lenp = (unsigned int) addr_len;
|
|
if (NULL == (s1 = create_socket (fd1, s->domain, s->type, s->protocol)))
|
|
(void) close_socket (fd1);
|
|
|
|
return s1;
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
/* Domain Methods */
|
|
#if defined(PF_UNIX) && defined(AF_UNIX) /*{{{ */
|
|
|
|
static void free_af_unix (Socket_Type *s)
|
|
{
|
|
char *file = (char *) s->socket_data;
|
|
if (file == NULL)
|
|
return;
|
|
|
|
(void) unlink (file);
|
|
SLang_free_slstring (file);
|
|
s->socket_data = NULL;
|
|
}
|
|
|
|
static int connect_af_unix (Socket_Type *s, int nargs)
|
|
{
|
|
struct sockaddr_un addr;
|
|
char *file;
|
|
|
|
if (nargs != 1)
|
|
{
|
|
SLang_verror (SL_NumArgs_Error, "This socket expects a filename");
|
|
return -1;
|
|
}
|
|
if (-1 == SLang_pop_slstring (&file))
|
|
return -1;
|
|
|
|
if (strlen (file) >= sizeof(addr.sun_path))
|
|
{
|
|
SLang_verror (SL_InvalidParm_Error, "filename too long for PF_UNIX socket");
|
|
SLang_free_slstring (file);
|
|
return -1;
|
|
}
|
|
|
|
memset ((char *)&addr, 0, sizeof (struct sockaddr_un));
|
|
addr.sun_family = AF_UNIX;
|
|
strcpy (addr.sun_path, file); /* \0 terminated */
|
|
|
|
SLang_free_slstring (file);
|
|
return perform_connect (s->fd, (struct sockaddr *)&addr, sizeof (addr), 1);
|
|
}
|
|
|
|
static int bind_af_unix (Socket_Type *s, int nargs)
|
|
{
|
|
struct sockaddr_un addr;
|
|
char *file;
|
|
|
|
if (nargs != 1)
|
|
{
|
|
SLang_verror (SL_NumArgs_Error, "This socket expects a filename");
|
|
return -1;
|
|
}
|
|
if (-1 == SLang_pop_slstring (&file))
|
|
return -1;
|
|
|
|
if (strlen (file) >= sizeof(addr.sun_path))
|
|
{
|
|
SLang_verror (SL_InvalidParm_Error, "filename too long for PF_UNIX socket");
|
|
SLang_free_slstring (file);
|
|
return -1;
|
|
}
|
|
|
|
memset ((char *)&addr, 0, sizeof (struct sockaddr_un));
|
|
addr.sun_family = AF_UNIX;
|
|
strcpy (addr.sun_path, file); /* \0 terminated */
|
|
|
|
(void) unlink (file);
|
|
s->socket_data = (VOID_STAR) file;
|
|
return perform_bind (s->fd, (struct sockaddr *)&addr, sizeof (addr));
|
|
}
|
|
|
|
static Socket_Type *accept_af_unix (Socket_Type *s, unsigned int nrefs, SLang_Ref_Type **refs)
|
|
{
|
|
struct sockaddr_un addr;
|
|
Socket_Type *s1;
|
|
unsigned int addr_len;
|
|
|
|
(void) refs;
|
|
if (nrefs != 0)
|
|
{
|
|
SLang_verror (SL_NotImplemented_Error, "accept: reference args not supported for PF_UNIX sockets");
|
|
return NULL;
|
|
}
|
|
addr_len = sizeof (struct sockaddr_un);
|
|
s1 = perform_accept (s, (struct sockaddr *)&addr, &addr_len);
|
|
return s1;
|
|
}
|
|
|
|
#endif
|
|
/*}}}*/
|
|
|
|
#if defined(PF_INET) && defined(AF_INET) /*{{{*/
|
|
static int pop_host_port (SLFUTURE_CONST char *what, int nargs, char **hostp, int *portp)
|
|
{
|
|
char *host;
|
|
int port;
|
|
|
|
if (nargs != 2)
|
|
{
|
|
SLang_verror (SL_NumArgs_Error, "%s on an PF_INET socket requires a hostname and portnumber", what);
|
|
return -1;
|
|
}
|
|
|
|
*hostp = NULL;
|
|
if ((-1 == SLang_pop_int (&port))
|
|
|| (-1 == SLang_pop_slstring (&host)))
|
|
return -1;
|
|
|
|
*hostp = host;
|
|
*portp = port;
|
|
return 0;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
int h_addrtype; /* AF_INET or AF_INET6 */
|
|
int h_length; /* length of address */
|
|
unsigned int num; /* num elements of h_addr_list */
|
|
char **h_addr_list; /* Array of num of these */
|
|
}
|
|
Host_Addr_Info_Type;
|
|
|
|
static void free_host_addr_info (Host_Addr_Info_Type *hinfo)
|
|
{
|
|
if (hinfo == NULL)
|
|
return;
|
|
if (hinfo->h_addr_list != NULL)
|
|
SLfree ((char *)hinfo->h_addr_list);
|
|
SLfree ((char *) hinfo);
|
|
}
|
|
|
|
static Host_Addr_Info_Type *alloc_host_addr_info (unsigned int num, int h_length)
|
|
{
|
|
Host_Addr_Info_Type *hinfo;
|
|
size_t nbytes;
|
|
char *buf;
|
|
unsigned int i;
|
|
|
|
hinfo = (Host_Addr_Info_Type *) SLcalloc (1, sizeof (Host_Addr_Info_Type));
|
|
if (hinfo == NULL)
|
|
return NULL;
|
|
|
|
/* We need memory to hold num (char *) addresses + num*h_length bytes */
|
|
nbytes = num * sizeof(char *) + num * h_length;
|
|
if (NULL == (buf = (char *)SLmalloc (nbytes)))
|
|
{
|
|
SLfree ((char *)hinfo);
|
|
return NULL;
|
|
}
|
|
hinfo->h_addr_list = (char **)buf;
|
|
buf += num*sizeof(char *);
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
hinfo->h_addr_list[i] = buf;
|
|
buf += h_length;
|
|
}
|
|
hinfo->num = num;
|
|
hinfo->h_length = h_length;
|
|
|
|
return hinfo;
|
|
}
|
|
|
|
/* glibc removed the h_addr compat macro, which messes up the logic below. */
|
|
# ifndef h_addr
|
|
# ifdef __GNUC_PREREQ
|
|
# if __GNUC_PREREQ(2,8)
|
|
# define h_addr "unused" /* define it, but do not use it */
|
|
# endif
|
|
# endif
|
|
# endif
|
|
|
|
static Host_Addr_Info_Type *get_host_addr_info (char *host)
|
|
{
|
|
in_addr_t addr;
|
|
Host_Addr_Info_Type *hinfo;
|
|
struct hostent *hp;
|
|
unsigned int max_retries;
|
|
# ifndef h_addr
|
|
char *fake_h_addr_list[2];
|
|
# endif
|
|
char **h_addr_list;
|
|
unsigned int i, num;
|
|
|
|
# ifndef INADDR_NONE
|
|
# define INADDR_NONE ((in_addr_t)(-1))
|
|
# endif
|
|
if ((isdigit ((unsigned char)*host))
|
|
&& (INADDR_NONE != (addr = inet_addr (host))))
|
|
{
|
|
/* Numerical address */
|
|
if (NULL == (hinfo = alloc_host_addr_info (1, sizeof(in_addr_t))))
|
|
return NULL;
|
|
hinfo->h_addrtype = AF_INET;
|
|
memcpy (hinfo->h_addr_list[0], (char *)&addr, sizeof(in_addr_t));
|
|
return hinfo;
|
|
}
|
|
|
|
max_retries = 3;
|
|
while (NULL == (hp = gethostbyname (host)))
|
|
{
|
|
# ifdef TRY_AGAIN
|
|
max_retries--;
|
|
if (max_retries && (h_errno == TRY_AGAIN))
|
|
{
|
|
sleep (1);
|
|
continue;
|
|
}
|
|
# endif
|
|
throw_herror ("gethostbyname", h_errno);
|
|
return NULL;
|
|
}
|
|
# ifndef h_addr
|
|
/* Older interface. There is only one address, so fake a list */
|
|
h_addr_list = fake_h_addr_list;
|
|
h_addr_list [0] = hp->h_addr;
|
|
h_addr_list [1] = NULL;
|
|
# else
|
|
h_addr_list = hp->h_addr_list;
|
|
# endif
|
|
|
|
/* Now count the number of addresses */
|
|
num = 0;
|
|
while (h_addr_list[num] != NULL)
|
|
num++;
|
|
|
|
if (num == 0)
|
|
{
|
|
# ifdef NO_DATA
|
|
throw_herror ("gethostbyname", NO_DATA);
|
|
# else
|
|
throw_herror ("gethostbyname", NO_ADDRESS);
|
|
# endif
|
|
return NULL;
|
|
}
|
|
|
|
if (NULL == (hinfo = alloc_host_addr_info (num, hp->h_length)))
|
|
return NULL;
|
|
|
|
hinfo->h_addrtype = hp->h_addrtype;
|
|
|
|
for (i = 0; i < num; i++)
|
|
memcpy (hinfo->h_addr_list[i], h_addr_list[i], hp->h_length);
|
|
|
|
return hinfo;
|
|
}
|
|
|
|
static int connect_af_inet (Socket_Type *s, int nargs)
|
|
{
|
|
struct sockaddr_in s_in;
|
|
int port;
|
|
char *host;
|
|
Host_Addr_Info_Type *hinfo;
|
|
unsigned int i;
|
|
|
|
if (-1 == pop_host_port ("connect", nargs, &host, &port))
|
|
return -1;
|
|
|
|
if (NULL == (hinfo = get_host_addr_info (host)))
|
|
{
|
|
SLang_free_slstring (host);
|
|
return -1;
|
|
}
|
|
|
|
if (hinfo->h_addrtype != AF_INET)
|
|
{
|
|
# ifdef AF_INET6
|
|
if (hinfo->h_addrtype == AF_INET6)
|
|
SLang_verror (SL_NOT_IMPLEMENTED, "AF_INET6 not implemented");
|
|
else
|
|
# endif
|
|
SLang_verror (SocketError, "Unknown socket family for host %s", host);
|
|
SLang_free_slstring (host);
|
|
free_host_addr_info (hinfo);
|
|
return -1;
|
|
}
|
|
|
|
memset ((char *) &s_in, 0, sizeof(s_in));
|
|
s_in.sin_family = hinfo->h_addrtype;
|
|
s_in.sin_port = htons((unsigned short) port);
|
|
|
|
for (i = 0; i < hinfo->num; i++)
|
|
{
|
|
memcpy ((char *) &s_in.sin_addr, hinfo->h_addr_list[i], hinfo->h_length);
|
|
if (-1 == perform_connect (s->fd, (struct sockaddr *)&s_in, sizeof (s_in), 0))
|
|
continue;
|
|
|
|
free_host_addr_info (hinfo);
|
|
SLang_free_slstring (host);
|
|
return 0;
|
|
}
|
|
throw_errno_error ("connect", errno);
|
|
free_host_addr_info (hinfo);
|
|
SLang_free_slstring (host);
|
|
return -1;
|
|
}
|
|
|
|
static int bind_af_inet (Socket_Type *s, int nargs)
|
|
{
|
|
struct sockaddr_in s_in;
|
|
char *host;
|
|
int port;
|
|
int status;
|
|
Host_Addr_Info_Type *hinfo;
|
|
|
|
if (-1 == pop_host_port ("connect", nargs, &host, &port))
|
|
return -1;
|
|
|
|
if (NULL == (hinfo = get_host_addr_info (host)))
|
|
{
|
|
SLang_free_slstring (host);
|
|
return -1;
|
|
}
|
|
|
|
if (hinfo->h_addrtype != AF_INET)
|
|
{
|
|
# ifdef AF_INET6
|
|
if (hinfo->h_addrtype == AF_INET6)
|
|
SLang_verror (SL_NOT_IMPLEMENTED, "AF_INET6 not implemented");
|
|
else
|
|
# endif
|
|
SLang_verror (SocketError, "Unknown socket family for host %s", host);
|
|
SLang_free_slstring (host);
|
|
free_host_addr_info (hinfo);
|
|
return -1;
|
|
}
|
|
|
|
memset ((char *) &s_in, 0, sizeof(s_in));
|
|
s_in.sin_family = hinfo->h_addrtype;
|
|
s_in.sin_port = htons((unsigned short) port);
|
|
|
|
memcpy ((char *) &s_in.sin_addr, hinfo->h_addr_list[0], hinfo->h_length);
|
|
|
|
status = perform_bind (s->fd, (struct sockaddr *)&s_in, sizeof (s_in));
|
|
|
|
free_host_addr_info (hinfo);
|
|
SLang_free_slstring (host);
|
|
return status;
|
|
}
|
|
|
|
/* Usage: s1 = accept (s [,&host,&port]); */
|
|
static Socket_Type *accept_af_inet (Socket_Type *s, unsigned int nrefs, SLang_Ref_Type **refs)
|
|
{
|
|
struct sockaddr_in s_in;
|
|
Socket_Type *s1;
|
|
unsigned int addr_len;
|
|
|
|
if ((nrefs != 0) && (nrefs != 2))
|
|
{
|
|
SLang_verror (SL_NumArgs_Error, "accept (sock [,&host,&port])");
|
|
return NULL;
|
|
}
|
|
|
|
addr_len = sizeof (struct sockaddr_in);
|
|
s1 = perform_accept (s, (struct sockaddr *)&s_in, &addr_len);
|
|
|
|
if ((s1 == NULL) || (nrefs == 0))
|
|
return s1;
|
|
|
|
if (nrefs == 2)
|
|
{
|
|
char *host;
|
|
char host_ip[32]; /* aaa.bbb.ccc.ddd */
|
|
unsigned char *bytes = (unsigned char *)&s_in.sin_addr;
|
|
int port = ntohs (s_in.sin_port);
|
|
sprintf (host_ip, "%d.%d.%d.%d",
|
|
(int)bytes[0],(int)bytes[1],(int)bytes[2],(int)bytes[3]);
|
|
|
|
if (NULL == (host = SLang_create_slstring (host_ip)))
|
|
{
|
|
free_socket (s1);
|
|
return NULL;
|
|
}
|
|
if (-1 == SLang_assign_to_ref (refs[0], SLANG_STRING_TYPE, (VOID_STAR)&host))
|
|
{
|
|
SLang_free_slstring (host);
|
|
free_socket (s1);
|
|
return NULL;
|
|
}
|
|
SLang_free_slstring (host);
|
|
if (-1 == SLang_assign_to_ref (refs[1], SLANG_INT_TYPE, &port))
|
|
{
|
|
free_socket (s1);
|
|
return NULL;
|
|
}
|
|
}
|
|
return s1;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*}}}*/
|
|
|
|
static Domain_Methods_Type Domain_Methods_Table [] =
|
|
{
|
|
#if defined(PF_UNIX) && defined(AF_UNIX)
|
|
{PF_UNIX, connect_af_unix, bind_af_unix, accept_af_unix, free_af_unix},
|
|
#endif
|
|
#if defined(PF_INET) && defined(AF_INET)
|
|
{PF_INET, connect_af_inet, bind_af_inet, accept_af_inet, NULL},
|
|
#endif
|
|
{0, NULL, NULL, NULL, NULL}
|
|
};
|
|
|
|
static Domain_Methods_Type *lookup_domain_methods (int domain)
|
|
{
|
|
Domain_Methods_Type *a = Domain_Methods_Table;
|
|
unsigned int i, n;
|
|
|
|
n = sizeof (Domain_Methods_Table)/sizeof(Domain_Methods_Type);
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
if (a->domain == domain)
|
|
return a;
|
|
a++;
|
|
}
|
|
|
|
SLang_verror (SocketError, "Unsupported socket domain: %d", domain);
|
|
return NULL;
|
|
}
|
|
|
|
static int close_socket (int fd)
|
|
{
|
|
/* Do not call close again to avoid undefined behavior */
|
|
if (-1 == close (fd))
|
|
{
|
|
#ifdef EINTR
|
|
if (errno == EINTR)
|
|
{
|
|
if (-1 == SLang_handle_interrupt ())
|
|
return -1;
|
|
}
|
|
#endif
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void free_socket (Socket_Type *s)
|
|
{
|
|
if (s == NULL)
|
|
return;
|
|
|
|
if ((s->methods != NULL) && (s->methods->free_socket_data != NULL))
|
|
(*s->methods->free_socket_data)(s);
|
|
|
|
if (s->fd != -1)
|
|
close_socket (s->fd);
|
|
|
|
SLfree ((char *) s);
|
|
}
|
|
|
|
static Socket_Type *create_socket (int fd, int domain, int type, int protocol)
|
|
{
|
|
Socket_Type *s;
|
|
Domain_Methods_Type *methods;
|
|
|
|
if (NULL == (methods = lookup_domain_methods (domain)))
|
|
return NULL;
|
|
|
|
s = (Socket_Type *)SLmalloc (sizeof (Socket_Type));
|
|
if (s == NULL)
|
|
return s;
|
|
memset ((char *)s, 0, sizeof (Socket_Type));
|
|
|
|
s->fd = fd;
|
|
s->domain = domain;
|
|
s->protocol = protocol;
|
|
s->type = type;
|
|
s->methods = methods;
|
|
|
|
return s;
|
|
}
|
|
|
|
static int close_socket_callback (VOID_STAR cd)
|
|
{
|
|
Socket_Type *s;
|
|
|
|
s = (Socket_Type *) cd;
|
|
if (s->fd == -1)
|
|
{
|
|
#ifdef EBADF
|
|
errno = EBADF;
|
|
#endif
|
|
return -1;
|
|
}
|
|
if (-1 == close (s->fd))
|
|
return -1;
|
|
|
|
s->fd = -1;
|
|
return 0;
|
|
}
|
|
|
|
static void free_socket_callback (VOID_STAR cd)
|
|
{
|
|
free_socket ((Socket_Type *)cd);
|
|
}
|
|
|
|
static SLFile_FD_Type *socket_to_fd (Socket_Type *s)
|
|
{
|
|
SLFile_FD_Type *f;
|
|
if (NULL == (f = SLfile_create_fd ("*socket*", s->fd)))
|
|
return NULL;
|
|
|
|
(void) SLfile_set_clientdata (f, free_socket_callback, (VOID_STAR)s, Socket_Type_Id);
|
|
(void) SLfile_set_close_method (f, close_socket_callback);
|
|
return f;
|
|
}
|
|
|
|
static Socket_Type *socket_from_fd (SLFile_FD_Type *f)
|
|
{
|
|
Socket_Type *s;
|
|
if (-1 == SLfile_get_clientdata (f, Socket_Type_Id, (VOID_STAR *)&s))
|
|
{
|
|
SLang_verror (SL_TypeMismatch_Error, "File descriptor does not represent a socket");
|
|
return NULL;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
/* This function frees the socket before returning */
|
|
static int push_socket (Socket_Type *s)
|
|
{
|
|
SLFile_FD_Type *f;
|
|
int status;
|
|
|
|
if (s == NULL)
|
|
return SLang_push_null ();
|
|
|
|
if (NULL == (f = socket_to_fd (s)))
|
|
{
|
|
free_socket (s);
|
|
return -1;
|
|
}
|
|
|
|
status = SLfile_push_fd (f);
|
|
SLfile_free_fd (f);
|
|
return status;
|
|
}
|
|
|
|
static Socket_Type *pop_socket (SLFile_FD_Type **fp)
|
|
{
|
|
SLFile_FD_Type *f;
|
|
Socket_Type *s;
|
|
|
|
if (-1 == SLfile_pop_fd (&f))
|
|
{
|
|
*fp = NULL;
|
|
return NULL;
|
|
}
|
|
if (NULL == (s = socket_from_fd (f)))
|
|
{
|
|
SLfile_free_fd (f);
|
|
return NULL;
|
|
}
|
|
*fp = f;
|
|
return s;
|
|
}
|
|
|
|
static void socket_intrin (int *domain, int *type, int *protocol)
|
|
{
|
|
Socket_Type *s;
|
|
int fd;
|
|
|
|
if (NULL == lookup_domain_methods (*domain))
|
|
return;
|
|
|
|
fd = socket (*domain, *type, *protocol);
|
|
if (fd == -1)
|
|
{
|
|
throw_errno_error ("socket", errno);
|
|
return;
|
|
}
|
|
|
|
if (NULL == (s = create_socket (fd, *domain, *type, *protocol)))
|
|
{
|
|
close_socket (fd);
|
|
return;
|
|
}
|
|
|
|
(void) push_socket (s); /* frees it upon error */
|
|
return;
|
|
}
|
|
|
|
#ifdef HAVE_SOCKETPAIR
|
|
static void socketpair_intrin (int *domain, int *type, int *protocol)
|
|
{
|
|
Socket_Type *s;
|
|
int fds[2];
|
|
|
|
if (NULL == lookup_domain_methods (*domain))
|
|
return;
|
|
|
|
if (-1 == socketpair (*domain, *type, *protocol, fds))
|
|
{
|
|
throw_errno_error ("socketpair", errno);
|
|
return;
|
|
}
|
|
|
|
if (NULL == (s = create_socket (fds[0], *domain, *type, *protocol)))
|
|
{
|
|
close_socket (fds[0]);
|
|
close_socket (fds[1]);
|
|
return;
|
|
}
|
|
if (-1 == push_socket (s)) /* frees upon error */
|
|
{
|
|
close_socket (fds[1]);
|
|
return;
|
|
}
|
|
if (NULL == (s = create_socket (fds[1], *domain, *type, *protocol)))
|
|
{
|
|
close_socket (fds[1]);
|
|
return;
|
|
}
|
|
(void) push_socket (s); /* frees it upon error */
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
static void connect_intrin (void)
|
|
{
|
|
Socket_Type *s;
|
|
SLFile_FD_Type *f;
|
|
int nargs = SLang_Num_Function_Args;
|
|
Domain_Methods_Type *methods;
|
|
|
|
if (-1 == SLroll_stack (-nargs))
|
|
return;
|
|
|
|
if (NULL == (s = pop_socket (&f)))
|
|
return;
|
|
nargs--;
|
|
|
|
methods = s->methods;
|
|
(void) (*methods->connect)(s, nargs);
|
|
SLfile_free_fd (f);
|
|
}
|
|
|
|
static void bind_intrin (void)
|
|
{
|
|
Socket_Type *s;
|
|
SLFile_FD_Type *f;
|
|
int nargs = SLang_Num_Function_Args;
|
|
Domain_Methods_Type *methods;
|
|
|
|
if (-1 == SLroll_stack (-nargs))
|
|
return;
|
|
|
|
if (NULL == (s = pop_socket (&f)))
|
|
return;
|
|
nargs--;
|
|
|
|
methods = s->methods;
|
|
(void)(*methods->bind)(s, nargs);
|
|
SLfile_free_fd (f);
|
|
}
|
|
|
|
static void listen_intrin (SLFile_FD_Type *f, int *np)
|
|
{
|
|
Socket_Type *s;
|
|
|
|
if (NULL == (s = socket_from_fd (f)))
|
|
return;
|
|
|
|
if (0 == listen (s->fd, *np))
|
|
return;
|
|
|
|
throw_errno_error ("listen", errno);
|
|
}
|
|
|
|
static void accept_intrin (void)
|
|
{
|
|
SLFile_FD_Type *f;
|
|
Socket_Type *s, *s1;
|
|
Domain_Methods_Type *methods;
|
|
int nargs = SLang_Num_Function_Args;
|
|
SLang_Ref_Type *refs[MAX_ACCEPT_REF_ARGS];
|
|
int i;
|
|
|
|
if (nargs <= 0)
|
|
{
|
|
SLang_verror (SL_Usage_Error, "s1 = accept (s [,&v...])");
|
|
return;
|
|
}
|
|
|
|
if (-1 == SLroll_stack (-nargs))
|
|
return;
|
|
|
|
if (NULL == (s = pop_socket (&f)))
|
|
return;
|
|
nargs--;
|
|
|
|
if (nargs > MAX_ACCEPT_REF_ARGS)
|
|
{
|
|
SLang_verror (SL_NumArgs_Error, "accept: too many reference args");
|
|
SLfile_free_fd (f);
|
|
}
|
|
memset ((char *)refs, 0, sizeof (refs));
|
|
|
|
i = nargs;
|
|
while (i != 0)
|
|
{
|
|
i--;
|
|
if (-1 == SLang_pop_ref (refs+i))
|
|
goto free_return;
|
|
}
|
|
|
|
methods = s->methods;
|
|
if (NULL != (s1 = (*methods->accept)(s, nargs, refs)))
|
|
(void) push_socket (s1); /* frees it upon error */
|
|
|
|
/* drop */
|
|
|
|
free_return:
|
|
for (i = 0; i < nargs; i++)
|
|
{
|
|
if (refs[i] != NULL)
|
|
SLang_free_ref (refs[i]);
|
|
}
|
|
SLfile_free_fd (f);
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
int optname;
|
|
int (*setopt)(Socket_Type *, int, int);
|
|
int (*getopt)(Socket_Type *, int, int);
|
|
}
|
|
SockOpt_Type;
|
|
|
|
static int do_setsockopt (int fd, int level, int optname, void *val, socklen_t len)
|
|
{
|
|
if (-1 == setsockopt (fd, level, optname, val, len))
|
|
{
|
|
throw_errno_error ("setsockopt", errno);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int do_getsockopt (int fd, int level, int optname, void *val, socklen_t *lenp)
|
|
{
|
|
if (-1 == getsockopt (fd, level, optname, val, lenp))
|
|
{
|
|
throw_errno_error ("getsockopt", errno);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int set_int_sockopt (Socket_Type *s, int level, int optname)
|
|
{
|
|
int val;
|
|
|
|
if (-1 == SLang_pop_int (&val))
|
|
return -1;
|
|
|
|
return do_setsockopt (s->fd, level, optname, (void *)&val, sizeof(int));
|
|
}
|
|
|
|
static int get_int_sockopt (Socket_Type *s, int level, int optname)
|
|
{
|
|
int val;
|
|
socklen_t len;
|
|
|
|
len = sizeof (int);
|
|
if (-1 == do_getsockopt (s->fd, level, optname, (void *)&val, &len))
|
|
return -1;
|
|
|
|
return SLang_push_int (val);
|
|
}
|
|
|
|
static int set_str_sockopt (Socket_Type *s, int level, int optname)
|
|
{
|
|
char *val;
|
|
socklen_t len;
|
|
int ret;
|
|
|
|
if (-1 == SLang_pop_slstring (&val))
|
|
return -1;
|
|
len = strlen (val); len++;
|
|
ret = do_setsockopt (s->fd, level, optname, (void *)val, len);
|
|
SLang_free_slstring (val);
|
|
return ret;
|
|
}
|
|
static int get_str_sockopt (Socket_Type *s, int level, int optname)
|
|
{
|
|
char buf[1024];
|
|
socklen_t len = sizeof (buf)-1;
|
|
|
|
if (-1 == do_getsockopt (s->fd, level, optname, (void *)buf, &len))
|
|
return -1;
|
|
|
|
buf[len] = 0;
|
|
return SLang_push_string (buf);
|
|
}
|
|
|
|
static int set_struct_sockopt (Socket_Type *s, int level, int optname,
|
|
SLang_CStruct_Field_Type *cs, VOID_STAR v,
|
|
socklen_t len)
|
|
{
|
|
int ret;
|
|
|
|
if (-1 == SLang_pop_cstruct (v, cs))
|
|
return -1;
|
|
|
|
ret = do_setsockopt (s->fd, level, optname, v, len);
|
|
SLang_free_cstruct (v, cs);
|
|
return ret;
|
|
}
|
|
|
|
static int get_struct_sockopt (Socket_Type *s, int level, int optname,
|
|
SLang_CStruct_Field_Type *cs, VOID_STAR v,
|
|
socklen_t len)
|
|
{
|
|
if (-1 == do_getsockopt (s->fd, level, optname, v, &len))
|
|
return -1;
|
|
|
|
return SLang_push_cstruct (v, cs);
|
|
}
|
|
|
|
static SLang_CStruct_Field_Type TV_Struct [] =
|
|
{
|
|
MAKE_CSTRUCT_INT_FIELD(struct timeval, tv_sec, "tv_sec", 0),
|
|
MAKE_CSTRUCT_INT_FIELD(struct timeval, tv_sec, "tv_usec", 0),
|
|
SLANG_END_CSTRUCT_TABLE
|
|
};
|
|
|
|
static int set_timeval_sockopt (Socket_Type *s, int level, int optname)
|
|
{
|
|
struct timeval tv;
|
|
return set_struct_sockopt (s, level, optname, TV_Struct, (VOID_STAR)&tv, sizeof(struct timeval));
|
|
}
|
|
|
|
static int get_timeval_sockopt (Socket_Type *s, int level, int optname)
|
|
{
|
|
struct timeval tv;
|
|
return get_struct_sockopt (s, level, optname, TV_Struct, (VOID_STAR)&tv, sizeof(struct timeval));
|
|
}
|
|
|
|
#if defined(SO_LINGER)
|
|
static SLang_CStruct_Field_Type Linger_Struct [] =
|
|
{
|
|
MAKE_CSTRUCT_INT_FIELD(struct linger, l_onoff, "l_onoff", 0),
|
|
MAKE_CSTRUCT_INT_FIELD(struct linger, l_linger, "l_linger", 0),
|
|
SLANG_END_CSTRUCT_TABLE
|
|
};
|
|
|
|
static int set_linger_sockopt (Socket_Type *s, int level, int optname)
|
|
{
|
|
struct linger lg;
|
|
return set_struct_sockopt (s, level, optname, Linger_Struct, (VOID_STAR)&lg, sizeof(struct linger));
|
|
}
|
|
|
|
static int get_linger_sockopt (Socket_Type *s, int level, int optname)
|
|
{
|
|
struct linger lg;
|
|
return get_struct_sockopt (s, level, optname, Linger_Struct, (VOID_STAR)&lg, sizeof(struct linger));
|
|
}
|
|
#endif
|
|
|
|
#ifdef SOL_SOCKET
|
|
static SockOpt_Type SO_Option_Table[] =
|
|
{
|
|
# ifdef SO_KEEPALIVE
|
|
{SO_KEEPALIVE, set_int_sockopt, get_int_sockopt},
|
|
# endif
|
|
# ifdef SO_OOBINLINE
|
|
{SO_OOBINLINE, set_int_sockopt, get_int_sockopt},
|
|
# endif
|
|
# ifdef SO_RCVLOWAT
|
|
{SO_RCVLOWAT, set_int_sockopt, get_int_sockopt},
|
|
# endif
|
|
# ifdef SO_SNDLOWAT
|
|
{SO_SNDLOWAT, set_int_sockopt, get_int_sockopt},
|
|
# endif
|
|
# ifdef SO_BSDCOMPAT
|
|
{SO_BSDCOMPAT, set_int_sockopt, get_int_sockopt},
|
|
# endif
|
|
# ifdef SO_PASSCRED
|
|
{SO_PASSCRED, set_int_sockopt, get_int_sockopt},
|
|
# endif
|
|
# ifdef SO_BINDTODEVICE
|
|
{SO_BINDTODEVICE, set_str_sockopt, get_str_sockopt},
|
|
# endif
|
|
# ifdef SO_DEBUG
|
|
{SO_DEBUG, set_int_sockopt, get_int_sockopt},
|
|
# endif
|
|
# ifdef SO_REUSEADDR
|
|
{SO_REUSEADDR, set_int_sockopt, get_int_sockopt},
|
|
# endif
|
|
# ifdef SO_TYPE
|
|
{SO_TYPE, set_int_sockopt, get_int_sockopt},
|
|
# endif
|
|
# ifdef SO_ACCEPTCONN
|
|
{SO_ACCEPTCONN, set_int_sockopt, get_int_sockopt},
|
|
# endif
|
|
# ifdef SO_DONTROUTE
|
|
{SO_DONTROUTE, set_int_sockopt, get_int_sockopt},
|
|
# endif
|
|
# ifdef SO_BROADCAST
|
|
{SO_BROADCAST, set_int_sockopt, get_int_sockopt},
|
|
# endif
|
|
# ifdef SO_SNDBUF
|
|
{SO_SNDBUF, set_int_sockopt, get_int_sockopt},
|
|
# endif
|
|
# ifdef SO_RCVBUF
|
|
{SO_RCVBUF, set_int_sockopt, get_int_sockopt},
|
|
# endif
|
|
# ifdef SO_PRIORITY
|
|
{SO_PRIORITY, set_int_sockopt, get_int_sockopt},
|
|
# endif
|
|
# ifdef SO_ERROR
|
|
{SO_ERROR, NULL, get_int_sockopt},
|
|
# endif
|
|
# ifdef SO_PEERCRED
|
|
/* {SO_PEERCRED, NULL, get_peercred_sockopt}, */
|
|
# endif
|
|
# ifdef SO_RCVTIMEO
|
|
{SO_RCVTIMEO, set_timeval_sockopt, get_timeval_sockopt},
|
|
# endif
|
|
# ifdef SO_SNDTIMEO
|
|
{SO_SNDTIMEO, set_timeval_sockopt, get_timeval_sockopt},
|
|
# endif
|
|
# ifdef SO_LINGER
|
|
{SO_LINGER, set_linger_sockopt, get_linger_sockopt},
|
|
# endif
|
|
|
|
{-1, NULL, NULL}
|
|
};
|
|
#endif /* SOL_SOCKET */
|
|
|
|
#if defined(IP_ADD_MEMBERSHIP) /* either add or drop same args */
|
|
static int set_multicast_sockopt (Socket_Type *s, int level, int option)
|
|
{
|
|
struct ip_mreq group;
|
|
char *multi;
|
|
char *local = NULL;
|
|
Host_Addr_Info_Type *multi_info = NULL;
|
|
Host_Addr_Info_Type *local_info = NULL;
|
|
int ret = -1;
|
|
|
|
if (-1 == SLang_pop_slstring(&multi))
|
|
return -1;
|
|
|
|
if (5 == SLang_Num_Function_Args)
|
|
{
|
|
if (-1 == SLang_pop_slstring(&local))
|
|
{
|
|
SLang_free_slstring (multi);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (NULL == (multi_info = get_host_addr_info (multi)))
|
|
goto free_and_return;
|
|
|
|
if (local != NULL)
|
|
{
|
|
if (NULL == (local_info = get_host_addr_info (local)))
|
|
goto free_and_return;
|
|
|
|
memcpy ((char *) &group.imr_interface.s_addr, local_info->h_addr_list[0], local_info->h_length);
|
|
}
|
|
else
|
|
{
|
|
group.imr_interface.s_addr = INADDR_ANY;
|
|
}
|
|
memcpy ((char *) &group.imr_multiaddr.s_addr, multi_info->h_addr_list[0], multi_info->h_length);
|
|
|
|
ret = do_setsockopt (s->fd, level, option, (void *)&group, sizeof(group));
|
|
|
|
free_and_return:
|
|
|
|
SLang_free_slstring(multi);
|
|
if (NULL != local)
|
|
SLang_free_slstring(local);
|
|
free_host_addr_info (multi_info);
|
|
if (NULL != local_info)
|
|
free_host_addr_info (local_info);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#if defined(IP_MULTICAST_IF)
|
|
static int set_multicast_if_sockopt (Socket_Type *s, int level, int option)
|
|
{
|
|
struct in_addr iface;
|
|
char *local;
|
|
Host_Addr_Info_Type *local_info;
|
|
|
|
if (-1 == SLang_pop_slstring(&local))
|
|
return -1;
|
|
|
|
if (NULL == (local_info = get_host_addr_info (local)))
|
|
{
|
|
SLang_free_slstring (local);
|
|
return -1;
|
|
}
|
|
memcpy ((char *) &iface.s_addr, local_info->h_addr_list[0], local_info->h_length);
|
|
|
|
SLang_free_slstring(local);
|
|
free_host_addr_info (local_info);
|
|
|
|
return do_setsockopt (s->fd, level, option, (void *)&iface, sizeof(iface));
|
|
}
|
|
#endif
|
|
|
|
#ifdef SOL_IP
|
|
static SockOpt_Type IP_Option_Table[] =
|
|
{
|
|
# ifdef IP_OPTIONS
|
|
/* {IP_OPTIONS, NULL, NULL}, */
|
|
# endif
|
|
# ifdef IP_PKTINFO
|
|
/* {IP_PKTINFO, NULL, NULL}, */
|
|
# endif
|
|
# ifdef IP_RECVTOS
|
|
/* {IP_RECVTOS, NULL, NULL}, */
|
|
# endif
|
|
# ifdef IP_RECVTTL
|
|
/* {IP_RECVTTL, NULL, NULL}, */
|
|
# endif
|
|
# ifdef IP_RECVOPTS
|
|
/* {IP_RECVOPTS, NULL, NULL}, */
|
|
# endif
|
|
# ifdef IP_TOS
|
|
/* {IP_TOS, set_int_sockopt, get_int_sockopt}, */
|
|
# endif
|
|
# ifdef IP_TTL
|
|
{IP_TTL, set_int_sockopt, get_int_sockopt},
|
|
# endif
|
|
# ifdef IP_HDRINCL
|
|
{IP_HDRINCL, set_int_sockopt, get_int_sockopt},
|
|
# endif
|
|
# ifdef IP_RECVERR
|
|
/* {IP_RECVERR, set_int_sockopt, get_int_sockopt}, */
|
|
# endif
|
|
# ifdef IP_MTU_DISCOVER
|
|
/* {IP_MTU_DISCOVER, set_int_sockopt, get_int_sockopt}, */
|
|
# endif
|
|
# ifdef IP_MTU
|
|
{IP_MTU_DISCOVER, NULL, get_int_sockopt},
|
|
# endif
|
|
# ifdef IP_ROUTER_ALERT
|
|
{IP_ROUTER_ALERT, set_int_sockopt, get_int_sockopt},
|
|
# endif
|
|
# ifdef IP_MULTICAST_TTL
|
|
{IP_MULTICAST_TTL, set_int_sockopt, get_int_sockopt},
|
|
# endif
|
|
# ifdef IP_MULTICAST_LOOP
|
|
{IP_MULTICAST_LOOP, set_int_sockopt, get_int_sockopt},
|
|
# endif
|
|
# ifdef IP_ADD_MEMBERSHIP
|
|
{IP_ADD_MEMBERSHIP, set_multicast_sockopt, NULL},
|
|
# endif
|
|
# ifdef IP_DROP_MEMBERSHIP
|
|
{IP_DROP_MEMBERSHIP, set_multicast_sockopt, NULL},
|
|
# endif
|
|
# ifdef IP_MULTICAST_IF
|
|
{IP_MULTICAST_IF, set_multicast_if_sockopt, NULL},
|
|
# endif
|
|
|
|
{-1, NULL, NULL}
|
|
};
|
|
#endif /* SOL_IP */
|
|
/* Usage: get/setsockopt (socket, level, optname, value) */
|
|
static void getset_sockopt (int set)
|
|
{
|
|
Socket_Type *s;
|
|
SLFile_FD_Type *f;
|
|
int level, optname;
|
|
SockOpt_Type *table;
|
|
|
|
if (-1 == SLreverse_stack (SLang_Num_Function_Args))
|
|
return;
|
|
|
|
if (NULL == (s = pop_socket (&f)))
|
|
return;
|
|
|
|
if ((-1 == SLang_pop_int (&level))
|
|
|| (-1 == SLang_pop_int (&optname)))
|
|
{
|
|
SLfile_free_fd (f);
|
|
return;
|
|
}
|
|
|
|
switch (level)
|
|
{
|
|
#ifdef SOL_SOCKET
|
|
case SOL_SOCKET: table = SO_Option_Table; break;
|
|
#endif
|
|
#ifdef SOL_IP
|
|
case SOL_IP: table = IP_Option_Table; break;
|
|
#endif
|
|
default:
|
|
SLang_verror (SL_NotImplemented_Error, "get/setsockopt level %d is not supported", level);
|
|
goto free_return;
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
if (table->optname == optname)
|
|
{
|
|
int (*func)(Socket_Type *, int, int);
|
|
if (set)
|
|
func = table->setopt;
|
|
else
|
|
func = table->getopt;
|
|
if (func == NULL)
|
|
goto not_implemented_error;
|
|
|
|
(void)(*func)(s, level, optname);
|
|
break;
|
|
}
|
|
if (table->optname == -1)
|
|
goto free_return;
|
|
|
|
table++;
|
|
}
|
|
|
|
/* drop */
|
|
free_return:
|
|
SLfile_free_fd (f);
|
|
return;
|
|
|
|
not_implemented_error:
|
|
SLang_verror (SL_NotImplemented_Error, "get/setsockopt option %d is not supported at level %d", optname, level);
|
|
SLfile_free_fd (f);
|
|
}
|
|
|
|
static void setsockopt_intrin (void)
|
|
{
|
|
getset_sockopt (1);
|
|
}
|
|
static void getsockopt_intrin (void)
|
|
{
|
|
getset_sockopt (0);
|
|
}
|
|
|
|
#define I SLANG_INT_TYPE
|
|
#define V SLANG_VOID_TYPE
|
|
#define F SLANG_FILE_FD_TYPE
|
|
static SLang_Intrin_Fun_Type Module_Intrinsics [] =
|
|
{
|
|
MAKE_INTRINSIC_3("socket", socket_intrin, V, I, I, I),
|
|
#ifdef HAVE_SOCKETPAIR
|
|
MAKE_INTRINSIC_3("socketpair", socketpair_intrin, V, I, I, I),
|
|
#endif
|
|
MAKE_INTRINSIC_0("connect", connect_intrin, V),
|
|
MAKE_INTRINSIC_0("bind", bind_intrin, V),
|
|
MAKE_INTRINSIC_2("listen", listen_intrin, V, F, I),
|
|
MAKE_INTRINSIC_0("accept", accept_intrin, V),
|
|
MAKE_INTRINSIC_0("getsockopt", getsockopt_intrin, V),
|
|
MAKE_INTRINSIC_0("setsockopt", setsockopt_intrin, V),
|
|
SLANG_END_INTRIN_FUN_TABLE
|
|
};
|
|
#undef F
|
|
#undef V
|
|
#undef I
|
|
|
|
static SLang_IConstant_Type Module_IConstants [] =
|
|
{
|
|
#ifdef SOCK_STREAM
|
|
MAKE_ICONSTANT("SOCK_STREAM", SOCK_STREAM),
|
|
#endif
|
|
#ifdef SOCK_DGRAM
|
|
MAKE_ICONSTANT("SOCK_DGRAM", SOCK_DGRAM),
|
|
#endif
|
|
#ifdef SOCK_RAW
|
|
MAKE_ICONSTANT("SOCK_RAW", SOCK_RAW),
|
|
#endif
|
|
#ifdef SOCK_RDM
|
|
MAKE_ICONSTANT("SOCK_RDM", SOCK_RDM),
|
|
#endif
|
|
#ifdef SOCK_SEQPACKET
|
|
MAKE_ICONSTANT("SOCK_SEQPACKET", SOCK_SEQPACKET),
|
|
#endif
|
|
#ifdef SOCK_PACKET
|
|
MAKE_ICONSTANT("SOCK_PACKET", SOCK_PACKET),
|
|
#endif
|
|
|
|
/* Domains */
|
|
#ifdef PF_UNIX
|
|
MAKE_ICONSTANT("PF_UNIX", PF_UNIX),
|
|
#endif
|
|
#ifdef PF_INET
|
|
MAKE_ICONSTANT("PF_INET", PF_INET),
|
|
#endif
|
|
#ifdef AF_UNIX
|
|
MAKE_ICONSTANT("AF_UNIX", AF_UNIX),
|
|
#endif
|
|
#ifdef AF_INET
|
|
MAKE_ICONSTANT("AF_INET", AF_INET),
|
|
#endif
|
|
|
|
#ifdef SOL_SOCKET
|
|
MAKE_ICONSTANT("SOL_SOCKET", SOL_SOCKET),
|
|
# ifdef SO_KEEPALIVE
|
|
MAKE_ICONSTANT("SO_KEEPALIVE", SO_KEEPALIVE),
|
|
# endif
|
|
# ifdef SO_OOBINLINE
|
|
MAKE_ICONSTANT("SO_OOBINLINE", SO_OOBINLINE),
|
|
# endif
|
|
# ifdef SO_RCVLOWAT
|
|
MAKE_ICONSTANT("SO_RCVLOWAT", SO_RCVLOWAT),
|
|
# endif
|
|
# ifdef SO_SNDLOWAT
|
|
MAKE_ICONSTANT("SO_SNDLOWAT", SO_SNDLOWAT),
|
|
# endif
|
|
# ifdef SO_BSDCOMPAT
|
|
MAKE_ICONSTANT("SO_BSDCOMPAT", SO_BSDCOMPAT),
|
|
# endif
|
|
# ifdef SO_PASSCRED
|
|
MAKE_ICONSTANT("SO_PASSCRED", SO_PASSCRED),
|
|
# endif
|
|
# ifdef SO_BINDTODEVICE
|
|
MAKE_ICONSTANT("SO_BINDTODEVICE", SO_BINDTODEVICE),
|
|
# endif
|
|
# ifdef SO_DEBUG
|
|
MAKE_ICONSTANT("SO_DEBUG", SO_DEBUG),
|
|
# endif
|
|
# ifdef SO_REUSEADDR
|
|
MAKE_ICONSTANT("SO_REUSEADDR", SO_REUSEADDR),
|
|
# endif
|
|
# ifdef SO_TYPE
|
|
MAKE_ICONSTANT("SO_TYPE", SO_TYPE),
|
|
# endif
|
|
# ifdef SO_ACCEPTCONN
|
|
MAKE_ICONSTANT("SO_ACCEPTCONN", SO_ACCEPTCONN),
|
|
# endif
|
|
# ifdef SO_DONTROUTE
|
|
MAKE_ICONSTANT("SO_DONTROUTE", SO_DONTROUTE),
|
|
# endif
|
|
# ifdef SO_BROADCAST
|
|
MAKE_ICONSTANT("SO_BROADCAST", SO_BROADCAST),
|
|
# endif
|
|
# ifdef SO_SNDBUF
|
|
MAKE_ICONSTANT("SO_SNDBUF", SO_SNDBUF),
|
|
# endif
|
|
# ifdef SO_RCVBUF
|
|
MAKE_ICONSTANT("SO_RCVBUF", SO_RCVBUF),
|
|
# endif
|
|
# ifdef SO_PRIORITY
|
|
MAKE_ICONSTANT("SO_PRIORITY", SO_PRIORITY),
|
|
# endif
|
|
# ifdef SO_ERROR
|
|
MAKE_ICONSTANT("SO_ERROR", SO_ERROR),
|
|
# endif
|
|
# ifdef SO_PEERCRED
|
|
MAKE_ICONSTANT("SO_PEERCRED", SO_PEERCRED),
|
|
# endif
|
|
# ifdef SO_RCVTIMEO
|
|
MAKE_ICONSTANT("SO_RCVTIMEO", SO_RCVTIMEO),
|
|
# endif
|
|
# ifdef SO_SNDTIMEO
|
|
MAKE_ICONSTANT("SO_SNDTIMEO", SO_SNDTIMEO),
|
|
# endif
|
|
# ifdef SO_LINGER
|
|
MAKE_ICONSTANT("SO_LINGER", SO_LINGER),
|
|
# endif
|
|
#endif /* SOL_SOCKET */
|
|
|
|
#ifdef SOL_IP
|
|
MAKE_ICONSTANT("SOL_IP", SOL_IP),
|
|
# ifdef IP_RECVTTL
|
|
MAKE_ICONSTANT("IP_RECVTTL", IP_RECVTTL),
|
|
# endif
|
|
# ifdef IP_RECVOPTS
|
|
MAKE_ICONSTANT("IP_RECVOPTS", IP_RECVOPTS),
|
|
# endif
|
|
# ifdef IP_RECVOPTS
|
|
MAKE_ICONSTANT("IP_RECVOPTS", IP_RECVOPTS),
|
|
# endif
|
|
# ifdef IP_TOS
|
|
MAKE_ICONSTANT("IP_TOS", IP_TOS),
|
|
# endif
|
|
# ifdef IP_TTL
|
|
MAKE_ICONSTANT("IP_TTL", IP_TTL),
|
|
# endif
|
|
# ifdef IP_HDRINCL
|
|
MAKE_ICONSTANT("IP_HDRINCL", IP_HDRINCL),
|
|
# endif
|
|
# ifdef IP_RECVERR
|
|
MAKE_ICONSTANT("IP_RECVERR", IP_RECVERR),
|
|
# endif
|
|
# ifdef IP_MTU_DISCOVER
|
|
MAKE_ICONSTANT("IP_MTU_DISCOVER", IP_MTU_DISCOVER),
|
|
# endif
|
|
# ifdef IP_ROUTER_ALERT
|
|
MAKE_ICONSTANT("IP_ROUTER_ALERT", IP_ROUTER_ALERT),
|
|
# endif
|
|
# ifdef IP_MULTICAST_TTL
|
|
MAKE_ICONSTANT("IP_MULTICAST_TTL", IP_MULTICAST_TTL),
|
|
# endif
|
|
# ifdef IP_MULTICAST_LOOP
|
|
MAKE_ICONSTANT("IP_MULTICAST_LOOP", IP_MULTICAST_LOOP),
|
|
# endif
|
|
# ifdef IP_ADD_MEMBERSHIP
|
|
MAKE_ICONSTANT("IP_ADD_MEMBERSHIP", IP_ADD_MEMBERSHIP),
|
|
# endif
|
|
# ifdef IP_DROP_MEMBERSHIP
|
|
MAKE_ICONSTANT("IP_DROP_MEMBERSHIP", IP_DROP_MEMBERSHIP),
|
|
# endif
|
|
# ifdef IP_MULTICAST_IF
|
|
MAKE_ICONSTANT("IP_MULTICAST_IF", IP_MULTICAST_IF),
|
|
# endif
|
|
# ifdef IP_OPTIONS
|
|
MAKE_ICONSTANT("IP_OPTIONS", IP_OPTIONS),
|
|
# endif
|
|
# ifdef IP_PKTINFO
|
|
MAKE_ICONSTANT("IP_PKTINFO", IP_PKTINFO),
|
|
# endif
|
|
# ifdef IP_RECVTOS
|
|
MAKE_ICONSTANT("IP_RECVTOS", IP_RECVTOS),
|
|
# endif
|
|
# ifdef IPPROTO_IP
|
|
MAKE_ICONSTANT("IPPROTO_IP", IPPROTO_IP),
|
|
# endif
|
|
#endif /* SOL_IP */
|
|
|
|
SLANG_END_ICONST_TABLE
|
|
};
|
|
|
|
int init_socket_module_ns (char *ns_name)
|
|
{
|
|
SLang_NameSpace_Type *ns;
|
|
|
|
if (SocketError == -1)
|
|
{
|
|
if (-1 == (SocketError = SLerr_new_exception (SL_RunTime_Error, "SocketError", "Socket Error")))
|
|
return -1;
|
|
if (-1 == (SocketHerrnoError = SLerr_new_exception (SocketError, "SocketHError", "Socket h_errno Error")))
|
|
return -1;
|
|
}
|
|
if (Socket_Type_Id == -1)
|
|
{
|
|
(void) SLfile_create_clientdata_id (&Socket_Type_Id);
|
|
}
|
|
|
|
if (NULL == (ns = SLns_create_namespace (ns_name)))
|
|
return -1;
|
|
|
|
if ((-1 == SLns_add_intrin_fun_table (ns, Module_Intrinsics, NULL))
|
|
|| (-1 == SLns_add_iconstant_table (ns, Module_IConstants, NULL)))
|
|
return -1;
|
|
|
|
if (-1 == SLns_add_intrinsic_variable(ns, "h_errno", (VOID_STAR)&Module_H_Errno, SLANG_INT_TYPE, 1))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void deinit_socket_module (void)
|
|
{
|
|
}
|