/* -*- 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 #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SOCKET_H # include #endif #ifdef HAVE_SYS_SOCKET_H # include #endif #if defined(__NT__) # include # define USE_WINSOCK_SLTCP 1 #else # if defined(__MINGW32__) # define Win32_Winsock # include # define USE_WINSOCK_SLTCP 1 # endif #endif #ifdef USE_WINSOCK_SLTCP # define USE_WINSOCK_SLTCP 1 #else # include #endif #ifdef HAVE_SYS_UN_H # include /* for AF_UNIX sockets */ #endif #ifdef HAVE_NETINET_IN_H # include #endif #ifdef HAVE_ARPA_INET_H # include #endif #include 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) { }