|
|
|
|
|
|
#define NO_PIKE_SHORTHAND |
|
#include "global.h" |
#include "fdlib.h" |
#include "pike_netlib.h" |
#include "interpret.h" |
#include "svalue.h" |
#include "stralloc.h" |
#include "array.h" |
#include "object.h" |
#include "pike_macros.h" |
#include "backend.h" |
#include "fd_control.h" |
#include "threads.h" |
#include "program_id.h" |
#include "module_support.h" |
#include "time_stuff.h" |
|
#include "file_machine.h" |
#include "file.h" |
|
#ifdef HAVE_SYS_PARAM_H |
#include <sys/param.h> |
#endif |
#include <errno.h> |
#include <fcntl.h> |
#include <signal.h> |
#ifdef HAVE_SYS_WAIT_H |
#include <sys/wait.h> |
#endif |
#ifdef HAVE_SYS_SOCKET_H |
#include <sys/socket.h> |
#endif |
|
#ifdef HAVE_SYS_STREAM_H |
#include <sys/stream.h> |
|
|
#ifdef u |
#undef u |
#endif |
|
#endif |
|
#ifdef HAVE_SYS_PROTOSW_H |
#include <sys/protosw.h> |
#endif |
|
#ifdef HAVE_SYS_SOCKETVAR_H |
#include <sys/socketvar.h> |
#endif |
|
#ifdef HAVE_SYS_UN_H |
#include <sys/un.h> |
#endif |
|
#if !defined(SOL_TCP) && defined(IPPROTO_TCP) |
|
#define SOL_TCP IPPROTO_TCP |
#endif |
|
|
|
|
|
|
|
struct port |
{ |
struct fd_callback_box box; |
int my_errno; |
unsigned int immediate_cnt; |
struct svalue accept_callback; |
struct svalue id; |
}; |
|
#undef THIS |
#define THIS ((struct port *)(Pike_fp->current_storage)) |
|
static int got_port_event (struct fd_callback_box *box, int DEBUGUSED(event)) |
{ |
struct port *p = (struct port *) box; |
#ifdef PIKE_DEBUG |
#ifndef __NT__ |
if(!query_nonblocking(p->box.fd)) |
Pike_fatal("Port is in blocking mode in port accept callback!!!\n"); |
#endif |
if (event != PIKE_FD_READ) |
Pike_fatal ("Got unexpected event %d.\n", event); |
#endif |
|
p->my_errno = errno; |
p->immediate_cnt++; |
push_svalue (&p->id); |
apply_svalue(& p->accept_callback, 1); |
pop_stack(); |
return 0; |
} |
|
static void assign_accept_cb (struct port *p, struct svalue *cb) |
{ |
assign_svalue(& p->accept_callback, cb); |
if (UNSAFE_IS_ZERO (cb)) { |
if (p->box.backend) |
set_fd_callback_events (&p->box, 0, 0); |
set_nonblocking(p->box.fd,0); |
} |
else { |
if (!p->box.backend) |
INIT_FD_CALLBACK_BOX (&p->box, default_backend, p->box.ref_obj, |
p->box.fd, PIKE_BIT_FD_READ, got_port_event, 0); |
else |
set_fd_callback_events (&p->box, PIKE_BIT_FD_READ, 0); |
set_nonblocking(p->box.fd,1); |
} |
} |
|
static void do_close(struct port *p) |
{ |
retry: |
if(p->box.fd >= 0) |
{ |
if(fd_close(p->box.fd) < 0) |
if(errno == EINTR) { |
check_threads_etc(); |
goto retry; |
} |
change_fd_for_box (&p->box, -1); |
} |
} |
|
|
|
|
|
|
|
|
|
static void port_set_id(INT32 args) |
{ |
check_all_args("Port->set_id", args, BIT_MIXED, 0); |
assign_svalue(& THIS->id, Pike_sp-args); |
pop_n_elems(args-1); |
} |
|
|
|
|
|
|
|
|
|
static void port_query_id(INT32 args) |
{ |
pop_n_elems(args); |
assign_svalue_no_free(Pike_sp++,& THIS->id); |
} |
|
|
|
|
|
|
|
static void port_errno(INT32 args) |
{ |
pop_n_elems(args); |
push_int(THIS->my_errno); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void port_listen_fd(INT32 args) |
{ |
struct port *p = THIS; |
struct svalue *cb = NULL; |
int fd; |
do_close(p); |
|
get_all_args("listen_fd", args, "%d.%*", &fd, &cb); |
|
if(fd<0) |
{ |
errno = p->my_errno=EBADF; |
pop_n_elems(args); |
push_int(0); |
return; |
} |
|
if(fd_listen(fd, 16384) < 0) |
{ |
p->my_errno=errno; |
pop_n_elems(args); |
push_int(0); |
return; |
} |
|
change_fd_for_box (&p->box, fd); |
if(cb) assign_accept_cb (p, cb); |
p->my_errno=0; |
pop_n_elems(args); |
push_int(1); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void port_bind(INT32 args) |
{ |
struct port *p = THIS; |
PIKE_SOCKADDR addr; |
int addr_len,fd,tmp; |
|
do_close(p); |
|
if(args < 1) |
SIMPLE_TOO_FEW_ARGS_ERROR("bind", 1); |
|
if(TYPEOF(Pike_sp[-args]) != PIKE_T_INT && |
(TYPEOF(Pike_sp[-args]) != PIKE_T_STRING || |
Pike_sp[-args].u.string->size_shift)) |
SIMPLE_ARG_TYPE_ERROR("bind", 1, "int|string(8bit)"); |
|
addr_len = get_inet_addr(&addr, |
(args > 2 && TYPEOF(Pike_sp[2-args])==PIKE_T_STRING? |
Pike_sp[2-args].u.string->str : NULL), |
(TYPEOF(Pike_sp[-args]) == PIKE_T_STRING? |
Pike_sp[-args].u.string->str : NULL), |
(TYPEOF(Pike_sp[-args]) == PIKE_T_INT? |
Pike_sp[-args].u.integer : -1), 0); |
INVALIDATE_CURRENT_TIME(); |
|
fd=fd_socket(SOCKADDR_FAMILY(addr), SOCK_STREAM, 0); |
|
if(fd < 0) |
{ |
p->my_errno=errno; |
pop_n_elems(args); |
push_int(0); |
return; |
} |
#ifdef SO_REUSEPORT |
if( args > 3 && Pike_sp[3-args].u.integer ) |
{ |
int o=1; |
if(fd_setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *)&o, sizeof(int)) < 0) |
{ |
p->my_errno=errno; |
while (fd_close(fd) && errno == EINTR) {} |
errno = p->my_errno; |
pop_n_elems(args); |
push_int(0); |
return; |
} |
} |
#endif |
#ifndef __NT__ |
{ |
int o=1; |
if(fd_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&o, sizeof(int)) < 0) |
{ |
p->my_errno=errno; |
while (fd_close(fd) && errno == EINTR) {} |
errno = p->my_errno; |
pop_n_elems(args); |
push_int(0); |
return; |
} |
} |
#endif |
|
#if defined(IPV6_V6ONLY) && defined(IPPROTO_IPV6) |
if (SOCKADDR_FAMILY(addr) == AF_INET6) { |
|
|
|
|
int o = 0; |
fd_setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&o, sizeof(int)); |
} |
#endif |
|
my_set_close_on_exec(fd,1); |
|
THREADS_ALLOW_UID(); |
if( !(tmp=fd_bind(fd, (struct sockaddr *)&addr, addr_len) < 0) ) |
#ifdef TCP_FASTOPEN |
tmp = 256, |
setsockopt(fd,SOL_TCP, TCP_FASTOPEN, &tmp, sizeof(tmp)), |
#endif |
(tmp = fd_listen(fd, 16384) < 0); |
THREADS_DISALLOW_UID(); |
|
if(!Pike_fp->current_object->prog) |
{ |
if (fd >= 0) |
while (fd_close(fd) && errno == EINTR) {} |
Pike_error("Object destructed in Stdio.Port->bind()\n"); |
} |
|
if(tmp) |
{ |
p->my_errno=errno; |
while (fd_close(fd) && errno == EINTR) {} |
errno = p->my_errno; |
pop_n_elems(args); |
push_int(0); |
return; |
} |
|
change_fd_for_box (&p->box, fd); |
if(args > 1) assign_accept_cb (p, Pike_sp+1-args); |
p->my_errno=0; |
pop_n_elems(args); |
push_int(1); |
} |
|
|
#ifdef HAVE_SYS_UN_H |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void bind_unix(INT32 args) |
{ |
struct port *p = THIS; |
struct sockaddr_un *addr; |
struct pike_string *path; |
struct svalue *cb = NULL; |
int addr_len,fd,tmp; |
|
do_close(p); |
|
get_all_args("bind_unix", args, "%n.%*", &path, &cb); |
|
|
|
|
addr_len = sizeof(struct sockaddr_un) + path->len + 1 - |
sizeof(addr->sun_path); |
addr = xalloc(addr_len); |
|
strcpy(addr->sun_path, path->str); |
addr->sun_family = AF_UNIX; |
#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN |
|
addr->sun_len = path->len + 1; |
#endif |
|
fd=fd_socket(AF_UNIX, SOCK_STREAM, 0); |
|
if(fd < 0) |
{ |
free(addr); |
p->my_errno=errno; |
pop_n_elems(args); |
push_int(0); |
return; |
} |
|
#ifndef __NT__ |
{ |
int o=1; |
do { |
tmp = fd_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, |
(char *)&o, sizeof(int)); |
} while ((tmp < 0) && (errno == EINTR)); |
} |
#endif |
|
my_set_close_on_exec(fd,1); |
|
THREADS_ALLOW_UID(); |
do { |
tmp = fd_bind(fd, (struct sockaddr *)addr, addr_len); |
} while ((tmp < 0) && (errno == EINTR)); |
if (tmp >= 0) { |
do { |
tmp = fd_listen(fd, 16384); |
} while ((tmp < 0) && (errno == EINTR)); |
} |
THREADS_DISALLOW_UID(); |
|
free(addr); |
|
if(!Pike_fp->current_object->prog) |
{ |
if (fd >= 0) |
while (fd_close(fd) && errno == EINTR) {} |
Pike_error("Object destructed in Stdio.Port->bind_unix()\n"); |
} |
|
if(tmp < 0) |
{ |
p->my_errno=errno; |
while (fd_close(fd) && errno == EINTR) {} |
errno = p->my_errno; |
pop_n_elems(args); |
push_int(0); |
return; |
} |
|
change_fd_for_box (&p->box, fd); |
if (cb) assign_accept_cb (p, cb); |
p->my_errno=0; |
pop_n_elems(args); |
push_int(1); |
} |
|
#endif /* HAVE_SYS_UN_H */ |
|
|
|
|
|
static void port_close (INT32 UNUSED(args)) |
{ |
do_close (THIS); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void port_create(INT32 args) |
{ |
if(args) |
{ |
if(TYPEOF(Pike_sp[-args]) == PIKE_T_INT || |
(TYPEOF(Pike_sp[-args]) == PIKE_T_STRING && |
(Pike_sp[-args].u.string->len != 5 || |
strcmp("stdin",Pike_sp[-args].u.string->str)))) |
{ |
port_bind(args); |
return; |
}else{ |
struct port *p = THIS; |
|
if(TYPEOF(Pike_sp[-args]) != PIKE_T_STRING) |
SIMPLE_TOO_FEW_ARGS_ERROR("create", 1); |
|
|
|
do_close(p); |
change_fd_for_box (&p->box, 0); |
|
if(fd_listen(p->box.fd, 16384) < 0) |
{ |
p->my_errno=errno; |
}else{ |
if(args > 1) assign_accept_cb (p, Pike_sp+1-args); |
} |
} |
} |
} |
|
extern struct program *file_program; |
|
static int port_fd_factory_fun_num = -1; |
static void port_fd_factory(INT32 args) |
{ |
pop_n_elems(args); |
push_object(clone_object(file_program, 0)); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void port_accept(INT32 args) |
{ |
PIKE_SOCKADDR addr; |
struct port *this=THIS; |
int fd, err; |
ACCEPT_SIZE_T len=0; |
int one = 1; |
|
if(this->box.fd < 0) |
Pike_error("port->accept(): Port not open.\n"); |
|
|
THIS->box.revents = 0; |
|
THREADS_ALLOW(); |
len=sizeof(addr); |
do { |
fd=fd_accept(this->box.fd, (struct sockaddr *)&addr, &len); |
err = errno; |
} while (fd < 0 && err == EINTR); |
THREADS_DISALLOW(); |
INVALIDATE_CURRENT_TIME(); |
|
if(fd < 0) |
{ |
this->my_errno=errno = err; |
pop_n_elems(args); |
push_int(0); |
return; |
} |
|
|
while ((fd_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, |
(char *)&one, sizeof(int)) < 0) && |
(errno == EINTR)) |
one = 1; |
|
my_set_close_on_exec(fd,1); |
push_new_fd_object(port_fd_factory_fun_num, |
fd, FILE_READ | FILE_WRITE, SOCKET_CAPABILITIES); |
|
if (this->box.backend) { |
struct object *o = Pike_sp[-1].u.object; |
struct my_file *f = (struct my_file *) |
(o->storage + o->prog->inherits[SUBTYPEOF(Pike_sp[-1])].storage_offset); |
change_backend_for_box(&f->box, this->box.backend); |
} |
|
stack_pop_n_elems_keep_top(args); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void socket_query_address(INT32 args) |
{ |
PIKE_SOCKADDR addr; |
int i; |
char buffer[496]; |
ACCEPT_SIZE_T len; |
|
if(THIS->box.fd <0) |
Pike_error("Stdio.Port->query_address(): Socket not bound yet.\n"); |
|
len=sizeof(addr); |
i=fd_getsockname(THIS->box.fd,(struct sockaddr *)&addr,&len); |
pop_n_elems(args); |
if(i < 0 || len < (int)sizeof(addr.ipv4)) |
{ |
THIS->my_errno=errno; |
push_int(0); |
return; |
} |
|
#ifdef fd_inet_ntop |
if(!fd_inet_ntop(SOCKADDR_FAMILY(addr), SOCKADDR_IN_ADDR(addr), |
buffer, sizeof(buffer)-20)) |
{ |
THIS->my_errno = errno; |
push_int(0); |
return; |
} |
#else |
if(SOCKADDR_FAMILY(addr) == AF_INET) |
{ |
char *q = inet_ntoa(*SOCKADDR_IN_ADDR(addr)); |
strncpy(buffer,q,sizeof(buffer)-20); |
buffer[sizeof(buffer)-20]=0; |
}else{ |
#ifdef EAFNOSUPPORT |
THIS->my_errno = EAFNOSUPPORT; |
#else |
THIS->my_errno = EINVAL; |
#endif |
push_int(0); |
return; |
} |
#endif |
sprintf(buffer+strlen(buffer)," %d",(int)(ntohs(addr.ipv4.sin_port))); |
|
push_text(buffer); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
static void port_set_backend (INT32 args) |
{ |
struct port *p = THIS; |
struct Backend_struct *backend; |
|
if (!args) |
SIMPLE_TOO_FEW_ARGS_ERROR ("set_backend", 1); |
if (TYPEOF(Pike_sp[-args]) != PIKE_T_OBJECT) |
SIMPLE_ARG_TYPE_ERROR ("set_backend", 1, "object(Pike.Backend)"); |
backend = get_storage (Pike_sp[-args].u.object, Backend_program); |
if (!backend) |
SIMPLE_ARG_TYPE_ERROR ("set_backend", 1, "object(Pike.Backend)"); |
|
if (p->box.backend) |
change_backend_for_box (&p->box, backend); |
else |
INIT_FD_CALLBACK_BOX (&p->box, backend, p->box.ref_obj, |
p->box.fd, 0, got_port_event, 0); |
} |
|
|
|
|
|
|
|
|
static void port_query_backend (INT32 args) |
{ |
pop_n_elems (args); |
ref_push_object (get_backend_obj (THIS->box.backend ? THIS->box.backend : |
default_backend)); |
} |
|
int fd_from_portobject( struct object *p ) |
{ |
struct port *po = get_storage( p, port_program ); |
if(!po) return -1; |
return po->box.fd; |
} |
|
|
|
|
|
static void port_query_fd(INT32 UNUSED(args)) |
{ |
push_int(fd_from_portobject(Pike_fp->current_object)); |
} |
|
|
static void init_port_struct(struct object *o) |
{ |
INIT_FD_CALLBACK_BOX(&THIS->box, NULL, o, -1, 0, got_port_event, 0); |
THIS->my_errno=0; |
|
} |
|
static void exit_port_struct(struct object *UNUSED(o)) |
{ |
do_close(THIS); |
unhook_fd_callback_box (&THIS->box); |
|
} |
|
|
|
|
|
|
PMOD_EXPORT struct program *port_program = NULL; |
|
void exit_stdio_port(void) |
{ |
free_program( port_program ); |
} |
|
void init_stdio_port(void) |
{ |
ptrdiff_t offset; |
START_NEW_PROGRAM_ID (STDIO_PORT); |
offset = ADD_STORAGE(struct port); |
MAP_VARIABLE("_accept_callback", tMix, 0, |
offset + OFFSETOF(port, accept_callback), PIKE_T_MIXED); |
MAP_VARIABLE("_id", tMix, 0, |
offset + OFFSETOF(port, id), PIKE_T_MIXED); |
|
ADD_FUNCTION("bind", port_bind, |
tFunc(tOr(tInt,tStr) tOr(tVoid,tMix) tOr(tVoid,tStr) tOr(tVoid,tInt),tInt), 0); |
#ifdef HAVE_SYS_UN_H |
|
ADD_FUNCTION("bind_unix", bind_unix, |
tFunc(tStr tOr(tVoid,tMix),tInt), ID_OPTIONAL); |
#endif /* HAVE_SYS_UN_H */ |
ADD_FUNCTION("close",port_close,tFunc(tNone,tVoid),0); |
|
ADD_FUNCTION("listen_fd",port_listen_fd,tFunc(tInt tOr(tVoid,tMix),tInt),0); |
|
ADD_FUNCTION("set_id",port_set_id,tFunc(tMix,tMix),0); |
|
ADD_FUNCTION("query_id",port_query_id,tFunc(tNone,tMix),0); |
|
ADD_FUNCTION("query_address",socket_query_address,tFunc(tNone,tStr),0); |
|
ADD_FUNCTION("errno",port_errno,tFunc(tNone,tInt),0); |
|
port_fd_factory_fun_num = |
ADD_FUNCTION("fd_factory", port_fd_factory, tFunc(tNone,tObjIs_STDIO_FD), |
ID_STATIC); |
ADD_FUNCTION("accept",port_accept,tFunc(tNone,tObjIs_STDIO_FD),0); |
|
ADD_FUNCTION("create", port_create, |
tFunc(tOr3(tVoid,tStr,tInt) tOr(tVoid,tMix) tOr(tVoid,tStr), |
tVoid), 0); |
ADD_FUNCTION ("set_backend", port_set_backend, tFunc(tObj,tVoid), 0); |
ADD_FUNCTION ("query_backend", port_query_backend, tFunc(tVoid,tObj), 0); |
ADD_FUNCTION ("query_fd", port_query_fd, tFunc(tVoid,tInt), 0); |
|
#ifdef SO_REUSEPORT |
ADD_INT_CONSTANT( "SO_REUSEPORT_SUPPORT", SO_REUSEPORT, ID_OPTIONAL ); |
#endif |
#ifdef TCP_FASTOPEN |
ADD_INT_CONSTANT( "TCP_FASTOPEN_SUPPORT", TCP_FASTOPEN, ID_OPTIONAL ); |
#endif |
set_init_callback(init_port_struct); |
set_exit_callback(exit_port_struct); |
|
port_program = end_program(); |
add_program_constant( "_port", port_program, 0 ); |
|
} |
|
|