From 485b70855defc45810d3f1f7398d003f4b7f4305 Mon Sep 17 00:00:00 2001 From: "Mirar (Pontus Hagland)" <pike@sort.mirar.org> Date: Thu, 15 Jul 1999 19:36:46 +0200 Subject: [PATCH] added Stdio.UDP (files.UDP) Rev: lib/modules/Stdio.pmod/module.pmod:1.57 Rev: src/modules/files/Makefile.in:1.10 Rev: src/modules/files/file.c:1.158 Rev: src/modules/files/udp.c:1.1 Rev: tutorial/tutorial.wmml:1.125 --- .gitattributes | 1 + lib/modules/Stdio.pmod/module.pmod | 33 +- src/modules/files/Makefile.in | 4 +- src/modules/files/file.c | 4 +- src/modules/files/udp.c | 473 +++++++++++++++++++++++++++++ tutorial/tutorial.wmml | 130 ++++++++ 6 files changed, 641 insertions(+), 4 deletions(-) create mode 100644 src/modules/files/udp.c diff --git a/.gitattributes b/.gitattributes index 11ef302f35..0db8bfeb7f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -385,6 +385,7 @@ testfont binary /src/modules/files/socket.c foreign_ident /src/modules/files/socktest.pike foreign_ident /src/modules/files/termios.c foreign_ident +/src/modules/files/udp.c foreign_ident /src/modules/math/Makefile.in foreign_ident /src/modules/math/configure.in foreign_ident /src/modules/math/math.c foreign_ident diff --git a/lib/modules/Stdio.pmod/module.pmod b/lib/modules/Stdio.pmod/module.pmod index de55ef7268..d6b6b954d4 100644 --- a/lib/modules/Stdio.pmod/module.pmod +++ b/lib/modules/Stdio.pmod/module.pmod @@ -1,4 +1,4 @@ -// $Id: module.pmod,v 1.56 1999/06/29 16:02:29 mast Exp $ +// $Id: module.pmod,v 1.57 1999/07/15 17:36:21 mirar Exp $ import String; @@ -1113,3 +1113,34 @@ object sendfile(array(string) headers, // Use nb_sendfile instead. return nb_sendfile(headers, from, offset, len, trailers, to, cb, @args); } + + +class UDP +{ + inherit files.UDP; + + private static array extra=0; + private static function callback=0; + + object set_nonblocking(mixed ...stuff) + { + if (stuff!=({})) + set_read_callback(@stuff); + return _set_nonblocking(); + } + + object set_read_callback(function f,mixed ...ext) + { + extra=ext; + callback=f; + _set_read_callback(_read_callback); + return this_object(); + } + + private static void _read_callback() + { + mapping i; + while (i=read()) + callback(i,@extra); + } +} diff --git a/src/modules/files/Makefile.in b/src/modules/files/Makefile.in index 10c39f5f8d..9a793bbd59 100644 --- a/src/modules/files/Makefile.in +++ b/src/modules/files/Makefile.in @@ -1,7 +1,7 @@ -# $Id: Makefile.in,v 1.9 1999/04/23 21:44:56 grubba Exp $ +# $Id: Makefile.in,v 1.10 1999/07/15 17:36:33 mirar Exp $ SRCDIR=@srcdir@ VPATH=@srcdir@:@srcdir@/../..:../.. -OBJS=file.o efuns.o socket.o termios.o sendfile.o +OBJS=file.o efuns.o socket.o termios.o sendfile.o udp.o MODULE_LDFLAGS=@LIBS@ MODULE_TESTS=local_tests diff --git a/src/modules/files/file.c b/src/modules/files/file.c index 4db3d4dfd0..bd294d7ab4 100644 --- a/src/modules/files/file.c +++ b/src/modules/files/file.c @@ -5,7 +5,7 @@ \*/ /**/ #include "global.h" -RCSID("$Id: file.c,v 1.157 1999/06/10 07:16:20 hubbe Exp $"); +RCSID("$Id: file.c,v 1.158 1999/07/15 17:36:34 mirar Exp $"); #include "fdlib.h" #include "interpret.h" #include "svalue.h" @@ -2599,6 +2599,7 @@ void pike_module_init(void) struct object *o; extern void port_setup_program(void); extern void init_sendfile(void); + extern void init_udp(void); int e; init_files_efuns(); @@ -2654,6 +2655,7 @@ void pike_module_init(void) port_setup_program(); init_sendfile(); + init_udp(); add_integer_constant("PROP_IPC",fd_INTERPROCESSABLE,0); add_integer_constant("PROP_NONBLOCK",fd_CAN_NONBLOCK,0); diff --git a/src/modules/files/udp.c b/src/modules/files/udp.c new file mode 100644 index 0000000000..69bac2a9d8 --- /dev/null +++ b/src/modules/files/udp.c @@ -0,0 +1,473 @@ +/* + * $Id: udp.c,v 1.1 1999/07/15 17:36:35 mirar Exp $ + */ + +#include "global.h" + +#include "file_machine.h" + +RCSID("$Id: udp.c,v 1.1 1999/07/15 17:36:35 mirar Exp $"); +#include "fdlib.h" +#include "interpret.h" +#include "svalue.h" +#include "stralloc.h" +#include "array.h" +#include "mapping.h" +#include "object.h" +#include "backend.h" +#include "fd_control.h" + +#include "error.h" +#include "signal_handler.h" +#include "pike_types.h" +#include "threads.h" + +#ifdef HAVE_SYS_TYPE_H +#include <sys/types.h> +#endif + +#include <sys/stat.h> +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#ifdef HAVE_WINSOCK_H +#include <winsock.h> +#endif + +#if ! defined(EWOULDBLOCK) && defined(WSAEWOULDBLOCK) +#define EWOULDBLOCK WSAEWOULDBLOCK +#endif +#if ! defined(EADDRINUSE) && defined(WSAEADDRINUSE) +#define EADDRINUSE WSAEADDRINUSE +#endif + + +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif + +#ifdef HAVE_ARPA_INET_H +#ifndef ARPA_INET_H +#include <arpa/inet.h> +#define ARPA_INET_H + +/* Stupid patch to avoid trouble with Linux includes... */ +#ifdef LITTLE_ENDIAN +#undef LITTLE_ENDIAN +#endif + +#endif +#endif + +#ifdef HAVE_SYS_PROTOSW_H +#include <sys/protosw.h> +#endif + +#ifdef HAVE_SYS_STREAM_H +#include <sys/stream.h> +#endif + +#ifdef HAVE_SYS_SOCKETVAR_H +#include <sys/socketvar.h> +#endif + +/* Fix warning on OSF/1 + * + * NOERROR is defined by both sys/stream.h (-1), and arpa/nameser.h (0), + * the latter is included by netdb.h. + */ +#ifdef NOERROR +#undef NOERROR +#endif /* NOERROR */ + +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif + +#include "dmalloc.h" + +struct udp_storage { + int fd; + int my_errno; + struct svalue read_callback; +}; + +#undef THIS +#define THIS ((struct udp_storage *)fp->current_storage) +#define THISOBJ (fp->current_object) +#define FD (THIS->fd) + +extern void get_inet_addr(struct sockaddr_in *addr,char *name); + +static void udp_bind(INT32 args) +{ + struct sockaddr_in addr; + int o; + int fd,tmp; + + + if(args < 1) error("Too few arguments to dumudp->bind()\n"); + + if(sp[-args].type != T_INT) + error("Bad argument 1 to dumudp->bind()\n"); + + if(FD != -1) + { + set_read_callback( FD, 0, 0 ); + fd_close(FD); + FD = -1; + } + + fd = fd_socket(AF_INET, SOCK_DGRAM, 0); + if(fd < 0) + { + pop_n_elems(args); + THIS->my_errno=errno; + error("UDP.bind: failed to create socket\n"); + } + + o=1; + if(fd_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&o, sizeof(int)) < 0) + { + fd_close(fd); + THIS->my_errno=errno; + error("UDP.bind: setsockopt failed\n"); + } + + MEMSET((char *)&addr,0,sizeof(struct sockaddr_in)); + + if(args > 1 && sp[1-args].type==T_STRING) { + get_inet_addr(&addr, sp[1-args].u.string->str); + } else { + addr.sin_addr.s_addr = htonl(INADDR_ANY); + } + + addr.sin_port = htons( ((u_short)sp[-args].u.integer) ); + addr.sin_family = AF_INET; + + THREADS_ALLOW_UID(); + + tmp=fd_bind(fd, (struct sockaddr *)&addr, sizeof(addr))<0; + + THREADS_DISALLOW_UID(); + + if(tmp) + { + fd_close(fd); + THIS->my_errno=errno; + error("UDP.bind: failed to bind to port %d\n",sp[-args].u.integer); + return; + } + + FD=fd; + pop_n_elems(args); + ref_push_object(THISOBJ); +} + +void udp_enable_broadcast(INT32 args) +{ +#ifdef SO_BROADCAST + int o; + pop_n_elems(args); + o = 1; + push_int(fd_setsockopt(FD, SOL_SOCKET, SO_BROADCAST, (char *)&o, sizeof(int))); +#else /* SO_BROADCAST */ + pop_n_elems(args); + push_int(0); +#endif /* SO_BROADCAST */ +} + +#define UDP_BUFFSIZE 65536 + +void udp_read(INT32 args) +{ + int flags = 0, res=0, fd; + struct sockaddr_in from; + char buffer[UDP_BUFFSIZE]; + ACCEPT_SIZE_T fromlen = sizeof(struct sockaddr_in); + + if(args) + { + if(sp[-args].u.integer & 1) { + flags |= MSG_OOB; + } + if(sp[-args].u.integer & 2) { +#ifdef MSG_PEEK + flags |= MSG_PEEK; +#else /* !MSG_PEEK */ + /* FIXME: What should we do here? */ +#endif /* MSG_PEEK */ + } + if(sp[-args].u.integer & ~3) { + error("Illegal 'flags' value passed to udp->read([int flags])\n"); + } + } + pop_n_elems(args); + fd = FD; + THREADS_ALLOW(); + while(((res = fd_recvfrom(fd, buffer, UDP_BUFFSIZE, flags, + (struct sockaddr *)&from, &fromlen))==-1) + &&(errno==EINTR)); + THREADS_DISALLOW(); + + if(res<0) + { + switch(errno) + { +#ifdef WSAEBADF + case WSAEBADF: +#endif + case EBADF: + set_read_callback( FD, 0, 0 ); + error("Socket closed\n"); +#ifdef ESTALE + case ESTALE: +#endif + case EIO: + set_read_callback( FD, 0, 0 ); + error("I/O error\n"); + case ENOMEM: +#ifdef ENOSR + case ENOSR: +#endif /* ENOSR */ + error("Out of memory\n"); +#ifdef ENOTSOCK + case ENOTSOCK: + fatal("reading from non-socket fd!!!\n"); +#endif + case EWOULDBLOCK: + push_int( 0 ); + return; + + default: + error("Socket read failed with errno %d.\n",errno); + } + } + /* Now comes the interresting part. + * make a nice mapping from this stuff.. + */ + push_text("ip"); + push_text( inet_ntoa( from.sin_addr ) ); + + push_text("port"); + push_int(ntohs(from.sin_port)); + + push_text("data"); + push_string( make_shared_binary_string(buffer, res) ); + f_aggregate_mapping( 6 ); +} + +void udp_sendto(INT32 args) +{ + int flags = 0, res=0, i, fd; + struct sockaddr_in to; + char *str; + INT32 len; + if(FD < 0) + error("UDP: not open\n"); + + if(args>3) + { + if(sp[3-args].u.integer & 1) { + flags |= MSG_OOB; + } + if(sp[3-args].u.integer & 2) { +#ifdef MSG_DONTROUTE + flags |= MSG_DONTROUTE; +#else /* !MSG_DONTROUTE */ + /* FIXME: What should we do here? */ +#endif /* MSG_DONTROUTE */ + } + if(sp[3-args].u.integer & ~3) { + error("Illegal 'flags' value passed to udp->send(string m,string t,int p,[int flags])\n"); + } + } + if(!args) + error("Illegal number of arguments to udp->sendto(string to" + ", string message, int port[, int flags])\n"); + + + if( sp[-args].type==T_STRING ) + get_inet_addr(&to, sp[-args].u.string->str); + else + error("Illegal type of argument to sendto, got non-string to-address.\n"); + + to.sin_port = htons( ((u_short)sp[1-args].u.integer) ); + + fd = FD; + str = sp[2-args].u.string->str; + len = sp[2-args].u.string->len; + THREADS_ALLOW(); + while(((res = fd_sendto( fd, str, len, flags, (struct sockaddr *)&to, + sizeof( struct sockaddr_in ))) == -1) && errno==EINTR); + THREADS_DISALLOW(); + + if(res<0) + { + switch(errno) + { +#ifdef EMSGSIZE + case EMSGSIZE: +#endif + error("Too big message\n"); + case EBADF: + set_read_callback( FD, 0, 0 ); + error("Socket closed\n"); + case ENOMEM: +#ifdef ENOSR + case ENOSR: +#endif /* ENOSR */ + error("Out of memory\n"); + case EINVAL: +#ifdef ENOTSOCK + case ENOTSOCK: + set_read_callback( FD, 0, 0 ); + error("Not a socket!!!\n"); +#endif + case EWOULDBLOCK: + error("Message would block.\n"); + } + } + pop_n_elems(args); + push_int( res ); +} + + +void zero_udp(struct object *ignored) +{ + MEMSET(THIS, 0, sizeof(struct udp_storage)); + THIS->read_callback.type=T_INT; + FD = -1; +} + +void exit_udp(struct object *ignored) +{ + if(FD != -1) + { + set_read_callback( FD, 0, 0 ); + fd_close(FD); + } + free_svalue(& THIS->read_callback ); +} + +#define THIS_DATA ((struct udp_storage *)data) + +static void udp_read_callback( int fd, void *data ) +{ + if(IS_ZERO(&THIS_DATA->read_callback)) + set_read_callback(THIS_DATA->fd, 0, 0); + else + apply_svalue(& THIS_DATA->read_callback, 0); + pop_stack(); + return; +} + +static void udp_set_read_callback(INT32 args) +{ + if(FD < 0) + error("File is not open.\n"); + + if(args != 1) + error("Wrong number of arguments to file->set_read_callback().\n"); + + if(IS_ZERO(& THIS->read_callback)) + assign_svalue(& THIS->read_callback, sp-1); + else + assign_svalue_no_free(& THIS->read_callback, sp-1); + + if(IS_ZERO(& THIS->read_callback)) + set_read_callback(FD, 0, 0); + else + set_read_callback(FD, udp_read_callback, THIS); + pop_n_elems(args); + ref_push_object(THISOBJ); +} + +static void udp_set_nonblocking(INT32 args) +{ + if (FD < 0) error("File not open.\n"); + + if (args) + { + udp_set_read_callback(args); + pop_stack(); + } + set_nonblocking(FD,1); + ref_push_object(THISOBJ); +} + +static void udp_set_blocking(INT32 args) +{ + if (FD < 0) error("File not open.\n"); + set_nonblocking(FD,0); + pop_n_elems(args); + ref_push_object(THISOBJ); +} + +static void udp_query_address(INT32 args) +{ + struct sockaddr_in addr; + int i; + char buffer[496],*q; + ACCEPT_SIZE_T len; + + if(THIS->fd <0) + error("socket->query_address(): Port not bound yet.\n"); + + len=sizeof(addr); + i=fd_getsockname(THIS->fd,(struct sockaddr *)&addr,&len); + pop_n_elems(args); + if(i < 0 || len < (int)sizeof(addr)) + { + push_int(0); + return; + } + + q=inet_ntoa(addr.sin_addr); + strncpy(buffer,q,sizeof(buffer)-20); + buffer[sizeof(buffer)-20]=0; + sprintf(buffer+strlen(buffer)," %d",(int)(ntohs(addr.sin_port))); + + push_string(make_shared_string(buffer)); +} + + +void init_udp(void) +{ + start_new_program(); + + ADD_STORAGE(struct udp_storage); + + ADD_FUNCTION("bind",udp_bind, + tFunc(tInt tOr(tVoid,tStr),tObj),0); + + ADD_FUNCTION("enable_broadcast", udp_enable_broadcast, + tFunc(tNone,tVoid), 0); + + ADD_FUNCTION("read",udp_read, + tFunc(tOr(tInt,tVoid),tMap(tStr,tOr(tInt,tStr))),0); + + ADD_FUNCTION("send",udp_sendto, + tFunc(tStr tInt tStr tOr(tVoid,tInt),tInt),0); + + ADD_FUNCTION("_set_nonblocking", udp_set_nonblocking, + tFunc(tOr(tFunc(tVoid,tVoid),tVoid),tObj), 0 ); + ADD_FUNCTION("_set_read_callback", udp_set_read_callback, + tFunc(tFunc(tVoid,tVoid),tObj), 0 ); + + ADD_FUNCTION("set_blocking",udp_set_blocking,tFunc(tVoid,tObj), 0 ); + ADD_FUNCTION("query_address",udp_query_address,tFunc(tNone,tStr),0); + + set_init_callback(zero_udp); + set_exit_callback(exit_udp); + + end_class("UDP",0); +} + diff --git a/tutorial/tutorial.wmml b/tutorial/tutorial.wmml index da15b2b6d2..f5cc21cd1a 100644 --- a/tutorial/tutorial.wmml +++ b/tutorial/tutorial.wmml @@ -4326,6 +4326,136 @@ of just <tt>write</tt> because they are the same function. </anchor> </anchor> + + +<section title="Stdio.UDP"> +<class name="UDP"> +<man_syntax> +Stdio.UDP(); +</man_syntax> +<man_description> +Stdio.UDP is the way of transceiving UDP from Pike. +</man_description> +<man_see> +Stdio.File +</man_see> + +<method name="bind"> +<man_syntax> + bind(int port); + bind(int port,string address); +</man_syntax> +<man_description> + binds a port for recieving or transmitting UDP +</man_description> +<man_returns> + the called object +</man_returns> +</method> + +<method name="enable_broadcast"> +<man_syntax> + enable_broadcast() +</man_syntax> +<man_description> + enable transmission of broadcasts. This is normally only avalable + to root users. +</man_description> +<man_returns> + 1 upon success, 0 otherwise +</man_returns> +</method> + +<method name="read"> +<man_syntax> + read() <br> + read(int flags) +</man_syntax> +<man_description> + read from the UDP socket. Flags is a bitfield, 1 for out of band + data and 2 for peek. +</man_description> +<man_returns> + mapping(string:int|string) in the form + <data_description type=mapping> + <elem name=data type="string">recieved data</elem> + <elem name=ip type="string">recieved from this ip</elem> + <elem name=port type="int">...and this port</elem> + </data_description> +</man_returns> +<man_see> + Stdio.UDP.set_nonblocking, Stdio.UDP.set_read_callback +</man_see> +</method> + +<method name="send"> +<man_syntax> + send(string to_addr,int to_port,string data)<br> + send(string to_addr,int to_port,string data,int flags) +</man_syntax> +<man_description> + +</man_description> +<man_returns> + number of bytes sent +</man_returns> +</method> + +<method name="set_nonblocking"> +<man_syntax> + set_nonblocking();<br> + set_blocking();<br> + set_nonblocking(function, mixed ...); +</man_syntax> +<man_description> + sets this object to be nonblocking or blocking. + + If set_nonblocking is given with argument, + these are passed to + <link to=Stdio.UDP.set_read_callback>set_read_callback</link>(). +</man_description> +<man_returns> + the called object +</man_returns> +</method> + +<method name="set_read_callback"> +<man_syntax> + set_read_callback(function(mapping(string:int|string), mixed...), mixed ... extra); +</man_syntax> +<man_description> + The called function is + recieving mapping similar to the return value of + <link to=Stdio.UDP.read>read</link>: + <data_description type=mapping> + <elem name=data type="string">recieved data</elem> + <elem name=ip type="string">recieved from this ip</elem> + <elem name=port type="int">...and this port</elem> + </data_description> +</man_description> +<man_returns> + the called object +</man_returns> +</method> + +<method name="query_address"> +<man_syntax> + query_address() +</man_syntax> +<man_description> + +</man_description> +<man_returns> + the current address in format "<ip> <port>". +</man_returns> +<man_see>Stdio.File.query_address</man_see> +</method> + +</class name="UDP"> +</section title="Stdio.UDP"> + + + <section title="Stdio.Terminfo"> <module name="Terminfo"> <function name=Stdio.Terminfo.getFallbackTerm title="get fallback terminal"> -- GitLab