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