/*

   This is the source code of the TCP/IP Support stuff
   of rtgmaster.library... originally it was included,
   as in rtgmaster.library V5 TCP/IP Support had to
   contain a LOT of Forbid()/Permit() calls, that slowed
   the thing down. (This was only needed for AmiTCP stuff
   inside a Shared Library). In the meanwhile, i got
   a reply from the coders of AmiTCP how to do this
   inside a shared library without a single Forbid()/Permit()
   call... so you could call this source obsolete...
   if you want to know how TCP/IP works NATIVE, you
   still can have a look at it :)  Of course this source
   is only usable from C, while the rtgmaster stuff is
   usable from ASM, too... i hope i did not forget
   any includes for the source version...

   THIS STUFF IS OBSOLETE, AS THE PROBLEMS THAT I HAD
   WITH TCP/IP SUPPORT AT THE BEGINNING, DISAPPEARED...
   IT IS THE CODE OF rtgmaster.library V6, AND DOES
   NOT SUPPORT UDP, CONTRARY TO rtgmaster.library V7 !!!

*/


// NOTE : Function Parameters are nearly the same, only
// that you have to provide a struct Library *SocketBase,
// and have to call SocketBase=OpenLibrary("bsdsocket.library",4);
// instead of providing SocketBase as parameters...

// Urgent note : If you want to use this code from different
// TASKS, provide the SocketBase as additional parameter "SBase" and
// give each function a local variable
// struct Library *SocketBase=SBase;
// Else you will get a LOT of AmiTCP error messages (this is the
// "famous" rtgmaster V6 speedfix. At the beginning i had a
// Forbid();/* Global */ SocketBase=SBase;Permit(); which
// of course is much slower than the local variable stuff...

#include <sys/types.h>
#include <exec/nodes.h>
#include <clib/socket_protos.h>
#include <pragmas/socket_pragmas.h>
#include <clib/netlib_protos.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <rtgmaster/rtgTCPIP.h>
#include <stdlib.h>
#include <stdio.h>
#include <clib/alib_protos.h>
#include <exec/memory.h>
#include <string.h>
#include <ctype.h>

// Note : Some of these includes might ACTUALLY
// be not needed... i hope i forgot no include :)

#define CREATE(result, type, number)  do {\
        if (!((result) = (type *) calloc ((number), sizeof(type))))\
         result=0;} while(0);

struct RTG_Socket *OpenServer(int port, int mode, int protocol)
{
 int opt,s;
 long handle;
 struct sockaddr_in sa;
 static struct RTG_Socket  rs;
 if ((s=socket(AF_INET,mode,protocol))<0) return(0);
#if defined(SO_SNDBUF)
  opt = (LARGE_BUFSIZE + GARBAGE_SPACE);
  if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *) &opt, sizeof(opt)) < 0) return(0);
#endif

#if defined(SO_REUSEADDR)
  opt = 1;
  if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt)) < 0) return(0);
#endif

#if defined(SO_LINGER)
  {
    struct linger ld;

    ld.l_onoff = 0;
    ld.l_linger = 0;
    if (setsockopt(s, SOL_SOCKET, SO_LINGER, (char *) &ld, sizeof(ld)) < 0) return(0);
  }
#endif

 sa.sin_family = AF_INET;
 sa.sin_port = htons(port);
 sa.sin_addr.s_addr = htonl(INADDR_ANY);
 if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0)
  {
    CloseSocket(s);
    return(0);
  }
 listen(s,5);
 rs.s=s;
 rs.num=1;
 rs.list=0;
 FD_ZERO(&(rs.input_set));
 FD_ZERO(&(rs.output_set));
 FD_ZERO(&(rs.exc_set));
 return(&rs);
}

void makeservaddr(struct sockaddr_in *address, char *host, int port);
void makeservaddr(struct sockaddr_in *address, char *host, int port)
{
 struct hostent *hi;
 address->sin_family=AF_INET;
 hi=gethostbyname(host);
 bcopy(hi->h_addr,&address->sin_addr,hi->h_length);
 address->sin_port=port;
}

struct RTG_Socket *OpenClient(char *host, int port, int mode, int protocol)
{
 struct sockaddr_in sa;
 struct sockaddr_in *sb;
 struct hostent *hi;
 long handle;
 static struct RTG_Socket rs;
 int s;
 if ((s=socket(AF_INET,mode,protocol))<0) return(0);
 makeservaddr(&sa,host,port);
 if (connect(s,&sa,sizeof(sa))<0) return(0);
 rs.s=s;
 rs.num=1;
 rs.list=0;
 return(&rs);
}

struct RTG_Socket *RunServer(struct RTG_Socket *s, struct RTG_Buff *in_buffer, struct RTG_Buff *out_buffer, int maxplayers)
{
 int maxdesc;
 int num=0;
 struct timeval null_time;
 struct RTG_Socket *d;
 int socknum=0;
 struct RTG_Socket *new_conn=0;
 if (maxplayers>12) maxplayers=12;
 null_time.tv_sec=0;
 null_time.tv_usec=0;
 if (s->list==0)
 {
  FD_ZERO(&(s->input_set));
  FD_SET(s->s,&(s->input_set));
  if (WaitSelect((s->s)+1, &(s->input_set), (fd_set *) 0, (fd_set *) 0, NULL,0) >= 0)
  {
   // New connection...
  }
 }
 FD_ZERO(&(s->input_set));
 FD_ZERO(&(s->output_set));
 FD_ZERO(&(s->exc_set));
 FD_SET(s->s,&(s->input_set));
 maxdesc=s->s;
 if (s->list!=0) for (d=s->list;d;d=d->list)
 {
  if ((d->s)>maxdesc) maxdesc=d->s;
  FD_SET(d->s,&(s->input_set));
  FD_SET(d->s,&(s->output_set));
  FD_SET(d->s,&(s->exc_set));
 }
 if (WaitSelect(maxdesc+1,&(s->input_set),&(s->output_set),&(s->exc_set),&null_time,0) >= 0)
 {
  // We have input...
 }
 if (FD_ISSET(s->s,&(s->input_set)))
 {
  // Allocate new descriptor
  int i;
  struct sockaddr_in peer;
  int desc;
  if ((desc = accept(s->s, 0,0))==-1)
  {
   return(0);
  }
  else
  {
   for (d=s->list;d;d=d->list)
    socknum++;
   if (socknum>=maxplayers)
   {
    CloseSocket(desc);
    return(0);
   }
   if (s->list!=0) for(d=s->list;(d->list);d=d->list);
   else d=s;
   if (s->num==maxplayers) return(0);
   CREATE(d->list,struct RTG_Socket,1);
   if ((d->list)==0) return(0);
   d=d->list;
   d->s=desc;
   d->num=1;
   d->list=0;
   new_conn=d;
   s->num++;
  }
 }
 if (s->list!=0) for (d=s->list;d;d=d->list)
 {
  if (FD_ISSET(d->s,&(s->exc_set)))
  {
   FD_CLR(d->s,&(s->input_set));
   FD_CLR(d->s,&(s->output_set));
   CloseSocket(d->s);
  }
 }
 if (s->list!=0) for (d=s->list;d;d=d->list)
 {
  if (FD_ISSET(d->s,&(s->input_set)))
  {

   int state;
   state=recv(d->s,in_buffer->sock[num],in_buffer->in_size,0);
   in_buffer->num[num]=d->s;
   num++;
   if (state<0) {num--;CloseSocket(d->s);in_buffer->num[num]=-1;}
   //Handle input, if it fails, CloseSocket !!!
   }
 }
 if (s->list!=0) for (d=s->list;d;d=d->list)
 {
  if (FD_ISSET(d->s,&(s->output_set)))
  {
   int state,f,g;
   g=-1;
   for (f=0;((f<maxplayers)&&(g==-1));f++)
    if (out_buffer->num[f]==d->s) g=f;
   state=send(d->s,&(out_buffer->sock[g]),out_buffer->out_size,0);
   if (state<0) CloseSocket(d->s);
   // Handle output, if it fails, CloseSocket !!!
  }
 }
 return(new_conn);
}

void CloseServer(struct RTG_Socket *s)
{
 if (s->s) CloseSocket(s->s);
 s->s=0;
}

void CloseClient(struct RTG_Socket *s)
{
 if (s->s) CloseSocket(s->s);
 s->s=0;
}

int RtgSend(struct RTG_Socket *s, char *message, int len)
{

 int test;
 test=(send(s->s,message,len,0));
 return(test);
}

int RtgRecv(struct RTG_Socket *s, char *message, int len)
{
 int test;
 test=(recv(s->s,message,len,0));
 return(test);
}

struct RTG_Socket *RtgAccept(struct RTG_Socket *s)
{
 static struct RTG_Socket rs;
 rs.s=accept(s->s,0,0);
 if (rs.s>=0)
 {
  rs.num=1;
  rs.list=0;
  return(&rs);
 }
 else return(0);
}

int RtgIoctl(struct RTG_Socket *s, long *arg)
{
 int test;
 test=(IoctlSocket(s->s,FIONBIO,arg));
 return(test);
}


