/*
 * $Source: $
 * $Revision: $
 * $Date: $
 * $State: $
 * $Author: $
 *
 *
 * $Log: $
 *
 */

/*
 *  Copyright (C) 1992 by the Massachusetts Institute of Technology.
 *  All rights reserved.
 *
 *  For copying and distribution information, please see the file
 *  COPYRIGHT.
 */
/*
 *
 * net_stuff.c () -- Provides a generic network interface on top of whatever
 *	       	     specific package we use.
 *
 */

/*
 *
 *    Here are the routines that make up this interface:
 *
 *    tcp_open_stream(dest_ip, dest_port, streamp)
 *
 *	Opens up a stream to the specific ip address, and port number.
 *	
 *    tcp_close_stream(stream)
 *
 *	Closes a stream that was opened previously.
 *
 *    tcp_send_data (stream, addr, len)
 *	Sends data over the tcp stream.  addr points to the data, for
 *	len bytes.
 *
 *    tcp_get_data (stream, addr, max_len, real_lenp)
 *	Gets data from a tcp connection.
 *
 *    udp_send_dgram (src_port, dest_ip, dest_port, addr, len)
 *	Sends a datagram on a udp port.
 *
 *    udp_get_dgram (src_port, &dest_ip, &dest_port, addr, max_len, real_lenp)
 *	Gets a datagram on a udp port.
 *
 *    resolve_name(in_name, cname, in_addrp)
 *	Resolves the name in_name, putting the address in in_addrp.
 *	If cname is non_null, puts the canonical name there.
 *
 */

#include	<types.h>

#include	<files.h>
#include	<devices.h>
#include	<desk.h>

#include	<stdio.h>

#include "mtypes.h"
#include "net_stuff.h"
#include "AddressXlation.h"

char *malloc();

static short mactcp_driver = 0;
static int open_driver();
static int wait_for();
static pascal void my_asr(StreamPtr tcp_stream, enum TCPEventCode event, Ptr udp, enum TCPTerminationReason term_reason,
    struct ICMPReport *icmp_msg);

static TCPiopb open_pb,close_pb,send_pb,get_pb;

int
tcp_open_stream(dest_ip, dest_port, sp)
ip_addr dest_ip;
ip_port dest_port;
stream *sp;
{
     char *buf;
     int result;

     if (mactcp_driver == 0) {
	  result = open_driver();
	  if (result != 0)
	       return(result);
     }

     bzero(&open_pb, sizeof (open_pb));

     buf = malloc(STREAM_SIZE);
     if (buf == NULL)
	  mpanic("Out of memory");

     /* Create the TCP stream */
     open_pb.ioCRefNum = mactcp_driver;
     open_pb.csCode = TCPCreate;
     open_pb.csParam.create.rcvBuff = buf;
     open_pb.csParam.create.rcvBuffLen = STREAM_SIZE;
     open_pb.csParam.create.notifyProc = NULL;
     open_pb.csParam.create.userDataPtr = 0;

     PBControl((ParmBlkPtr) &open_pb, false);

     if (open_pb.ioResult != 0) {
	  free(buf);
	  return(open_pb.ioResult);
     }

     *sp = open_pb.tcpStream;

     bzero(&open_pb, sizeof(open_pb));
     open_pb.ioCRefNum = mactcp_driver;
     open_pb.csCode = TCPActiveOpen;
     open_pb.tcpStream = *sp;
     open_pb.csParam.open.ulpTimeoutValue = 60;
     open_pb.csParam.open.ulpTimeoutAction = 1;		/* Abort on timeout */
     open_pb.csParam.open.validityFlags = 0xC0;
     open_pb.csParam.open.remoteHost = dest_ip;
     open_pb.csParam.open.remotePort = dest_port;
     open_pb.csParam.open.localPort = 0;

     PBControl((ParmBlkPtr) &open_pb, true);
     result = wait_for(&open_pb.ioResult);
     if (result) goto abort;

     return(0);

abort:
     bzero(&open_pb, sizeof(open_pb));
     open_pb.ioCRefNum = mactcp_driver;
     open_pb.csCode = TCPRelease;
     open_pb.tcpStream = *sp;
     
     PBControl((ParmBlkPtr) &open_pb, false);
     free(buf);
     *sp = NULL;
     return(result);
}

static pascal void 
my_asr(tcp_stream, event, udp, term_reason, icmp_msg)
StreamPtr tcp_stream;
enum TCPEventCode event;
Ptr udp;
enum TCPTerminationReason term_reason;
struct ICMPReport *icmp_msg;
{
#pragma unused(tcp_stream)
#pragma unused(event)
#pragma unused(udp)
#pragma unused(term_reason)
#pragma unused(icmp_msg)
}

static int
open_driver()
{
     int result;

     result = OpenDriver("\p.IPP", &mactcp_driver);
     return(result);
}

static int wait_for(resultp)
short *resultp;
{
     for (;;) {
	  if (*resultp != 1)
	       return(*resultp);
	  if (user_abort())
	       return(USER_ABORTED);
	  SystemTask();
     }
}

int
tcp_close_stream(s)
stream s;
{
     int result;

     if (mactcp_driver == 0) {
	  result = open_driver();
	  if (result != 0)
	       return(result);
     }

     bzero(&close_pb, sizeof(close_pb));
     close_pb.ioCRefNum = mactcp_driver;
     close_pb.csCode = TCPClose;
     close_pb.tcpStream = s;
     close_pb.csParam.close.ulpTimeoutValue = 30;
     close_pb.csParam.close.ulpTimeoutAction = 0;		/* Abort on timeout */
     close_pb.csParam.close.validityFlags = 0xC0;

     PBControl((ParmBlkPtr) &close_pb, true);
     result = wait_for(&close_pb.ioResult);

     bzero(&close_pb, sizeof(close_pb));
     close_pb.ioCRefNum = mactcp_driver;
     close_pb.csCode = TCPRelease;
     close_pb.tcpStream = s;

     PBControl((ParmBlkPtr) &close_pb, false);
     if (close_pb.ioResult == 0)
	  free(close_pb.csParam.create.rcvBuff);

     if (result == 0)
	  result = close_pb.ioResult;

     return(result);
}

tcp_send_data(s, addr, len)
stream s;
char *addr;
int len;
{
     int result;
     wdsEntry wds[2];

     if (mactcp_driver == 0) {
	  result = open_driver();
	  if (result != 0)
	       return(result);
     }

     wds[0].length = len;
     wds[0].ptr = addr;
     wds[1].length = 0;

     bzero(&send_pb, sizeof (send_pb));
     send_pb.ioCRefNum = mactcp_driver;
     send_pb.csCode = TCPSend;
     send_pb.tcpStream = s;
     send_pb.csParam.send.ulpTimeoutValue = 60;
     send_pb.csParam.send.ulpTimeoutAction = 0;
     send_pb.csParam.send.validityFlags = 0xC0;
     send_pb.csParam.send.wdsPtr = (Ptr) wds;

     PBControl((ParmBlkPtr) &send_pb, true);
     result = wait_for(&send_pb.ioResult);

     return(result);
}

tcp_get_data(s, addr, max_len, real_lenp)
stream s;
char *addr;
int max_len;
int *real_lenp;
{
     int result;

     *real_lenp = 0;
     if (mactcp_driver == 0) {
	  result = open_driver();
	  if (result != 0)
	       return(result);
     }

     bzero(&get_pb, sizeof (get_pb));
     get_pb.ioCRefNum = mactcp_driver;
     get_pb.csCode = TCPRcv;
     get_pb.tcpStream = s;
     get_pb.csParam.receive.commandTimeoutValue = 2;
     get_pb.csParam.receive.rcvBuff = addr;
     get_pb.csParam.receive.rcvBuffLen = max_len;

     PBControl((ParmBlkPtr) &get_pb, true);
     result = wait_for(&get_pb.ioResult);

     if (result == 0)
	  *real_lenp = get_pb.csParam.receive.rcvBuffLen;
  
     return(result);
}

static int resolve_inited = 0;
static short resolve_rtn;
static pascal void resolve_proc(struct hostInfo *hostInfoPtr, char *userDataPtr);
static struct hostInfo dhost;

int
resolve_name(in_name, cname, in_addrp)
const char *in_name;
char *cname;
ip_addr *in_addrp;
{
     int result;
     
     if (!resolve_inited) {
	  result = OpenResolver("\phosts");
	  if (result != 0)
	       return(result);
	  resolve_inited = true;
     }

     resolve_rtn = 1;
     StrToAddr(in_name, &dhost, resolve_proc, (char *) &resolve_rtn);
     if (dhost.rtnCode == cacheFault) {
	  dhost.rtnCode = wait_for(&resolve_rtn);
     }

     if (dhost.rtnCode != 0)
	  return(dhost.rtnCode);

     *in_addrp = dhost.addr[0];
     if (cname)
	  bcopy(dhost.cname,cname,dhost.cname[0]);
     return(0);
}

static pascal void
resolve_proc(hostInfoPtr, userDataPtr)
struct hostInfo *hostInfoPtr;
char *userDataPtr;
{
     *((short *) userDataPtr) = hostInfoPtr -> rtnCode;
}
