/* net1.c, 19.09.99 */

/* (C)opyright (C) 1993-99  Martin Stover, Marburg, Germany
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "net.h"

#define MAX_SEND_TRIES     5
#define MAX_WAIT_MSEC   1000  /* 1 sec should be enough */

#if HAVE_TLI
void print_t_info(struct t_info *t)
{
  XDPRINTF((2,0, "T_INFO:addr %ld, options %ld, tsdu %ld, etsdu %ld",
	 t->addr, t->options,t->tsdu, t->etsdu));
  XDPRINTF((2,0, "connect %ld, discon %ld, servtype %ld",
	 t->connect, t->discon,t->servtype));
#if 0
   struct t_info {
         long addr;      /* size of protocol address                */
         long options;   /* size of protocol options                */
         long tsdu;      /* size of max transport service data unit */
         long etsdu;     /* size of max expedited tsdu              */
         long connect;   /* max data for connection primitives      */
         long discon;    /* max data for disconnect primitives      */
         long servtype;  /* provider service type                   */
#endif
}
#endif

char *xvisable_ipx_adr(ipxAddr_t *p, int modus)
{
static char str[200];
  if (p) {
    if (!modus) {
    sprintf(str,"net=%x:%x:%x:%x, node=%x:%x:%x:%x:%x:%x, sock=%02x:%02x",
       (int)p->net[0],  (int)p->net[1],  (int)p->net[2],  (int)p->net[3],
       (int)p->node[0], (int)p->node[1], (int)p->node[2], (int)p->node[3],
       (int)p->node[4], (int)p->node[5], (int)p->sock[0], (int)p->sock[1]);
    } else if (modus== 1) {
      sprintf(str,"%02X:%02X:%02X:%02X,%02x:%02x:%02x:%02x:%02x:%02x,%02x:%02x",
         (int)p->net[0],  (int)p->net[1],  (int)p->net[2],  (int)p->net[3],
         (int)p->node[0], (int)p->node[1], (int)p->node[2], (int)p->node[3],
         (int)p->node[4], (int)p->node[5], (int)p->sock[0], (int)p->sock[1]);
    } else if (modus== 2) {
      sprintf(str,"%02x%02x%02x%02x%02x%02x",
         (int)p->node[0], (int)p->node[1], (int)p->node[2], (int)p->node[3],
         (int)p->node[4], (int)p->node[5]);
    } else strcpy(str, "??");
  } else
    strcpy(str, "net=UNKOWN(NULLPOINTER)");
  return(str);
}

void print_ipx_addr(ipxAddr_t *p)
{
  XDPRINTF((2,0,"%s", visable_ipx_adr(p)));
}

void print_ud_data(struct t_unitdata *ud)
{
#if DO_DEBUG
  int packet_typ = *(int*)(ud->opt.buf);
  int data_len   = ud->udata.len;
  IPX_DATA *ipxdata = (IPX_DATA *)(ud->udata.buf);
  XDPRINTF((2,0,"DATA-LEN %d, PACKET-TYPE %d von: %s",
	data_len,  packet_typ, visable_ipx_adr((ipxAddr_t *)(ud->addr.buf))));
      /* hierin steht nun Addresse u. Node des Senders */
  if (packet_typ == PACKT_CORE) {
    XDPRINTF((2,0, "Query Type 0x%x, Server Type 0x%xd",
       GET_BE16(ipxdata->sqp.query_type),
       GET_BE16(ipxdata->sqp.server_type)));
  } else  if (data_len > sizeof(SIP)){
    SAP   *sap      = &(ipxdata->sap);
    SAPS  *saps     = &(ipxdata->sap.saps);
    XDPRINTF((2,0, "SAP-OPERATION %d", (int) GET_BE16(sap->sap_operation) ));
    while (data_len >= sizeof(SAPS)){
      XDPRINTF((2,0, "Name:%s:, typ:0x%x",
           visable_data(saps->server_name, MAX_SERVER_NAME),
	   GET_BE16(saps->server_type)));
      print_ipx_addr(&(saps->server_adr));
      saps++;
      data_len  -= sizeof(SAPS);
    }
  } else print_ipx_data(ipxdata);
#endif
}

void print_ipx_data(IPX_DATA *p)
{
  print_sip_data(&(p->sip));
}

void print_sip_data(SIP *sip)
{
   XDPRINTF((2,0,"Name:%s:,response_type:0x%x,server_typ:0x%x",
      visable_data(sip->server_name, MAX_SERVER_NAME),
      GET_BE16(sip->response_type), GET_BE16(sip->server_type)));
   print_ipx_addr(&(sip->server_adr));
}

void adr_to_ipx_addr(ipxAddr_t *p, char *s)
{
  int net0, net1, net2, net3;
  int node0, node1, node2, node3, node4, node5;
  int sock0, sock1;
  sscanf(s, "%x.%x.%x.%x:%x.%x.%x.%x.%x.%x:%x.%x",
	  &net0, &net1, &net2, &net3,
	  &node0, &node1, &node2, &node3, &node4, &node5,
	  &sock0, &sock1);
    p->net[0]  = net0;
    p->net[1]  = net1;
    p->net[2]  = net2;
    p->net[3]  = net3;
    p->node[0] = node0;
    p->node[1] = node1;
    p->node[2] = node2;
    p->node[3] = node3;
    p->node[4] = node4;
    p->node[5] = node5;
    p->sock[0] = sock0;
    p->sock[1] = sock1;
}

void ipx_addr_to_adr(char *s, ipxAddr_t *p)
{
  sprintf(s, "%x.%x.%x.%x:%x.%x.%x.%x.%x.%x:%x.%x",
	     (int)p->net[0] ,
	     (int)p->net[1] ,
	     (int)p->net[2] ,
	     (int)p->net[3] ,
	     (int)p->node[0],
	     (int)p->node[1],
	     (int)p->node[2],
	     (int)p->node[3],
	     (int)p->node[4],
	     (int)p->node[5],
	     (int)p->sock[0],
	     (int)p->sock[1]);
}

int open_ipx_socket(ipxAddr_t *addr, int sock_nr)
{
  struct t_bind   bind;
  ipxAddr_t       addr_buff;
  int fd=t_open("/dev/ipx", O_RDWR, NULL);
  if (fd < 0) {
    t_error("t_open !Ok");
    return(-1);
  }
  if (NULL == addr)
    addr = &addr_buff;
  memset(addr, 0, sizeof(ipxAddr_t));
  U16_TO_BE16(sock_nr, addr->sock);
  bind.addr.len    = sizeof(ipxAddr_t);
  bind.addr.maxlen = sizeof(ipxAddr_t);
  bind.addr.buf    = (char*)addr;
  bind.qlen        = 0;
  if (t_bind(fd, &bind, &bind) < 0){
    t_error("t_bind,open_ipx_socket");
    t_close(fd);
    return(-1);
  }
  return(fd);
}

int send_ipx_data(int fd, int pack_typ,
	              int data_len, char *data,
	              ipxAddr_t *to_addr, char *comment)
{
  int                fd_is_dyna=0;
  int                result=0;
  struct             t_unitdata ud;
  uint8              ipx_pack_typ = (uint8) pack_typ;
  ud.opt.len       = sizeof(ipx_pack_typ);
  ud.opt.maxlen    = sizeof(ipx_pack_typ);
  ud.opt.buf       = (char*)&ipx_pack_typ;
  ud.addr.len      = sizeof(ipxAddr_t);
  ud.addr.maxlen   = sizeof(ipxAddr_t);
  ud.udata.buf     = (char*)data;
  ud.udata.len     = data_len;
  ud.udata.maxlen  = data_len;
  ud.addr.buf      = (char*)to_addr;
  if (comment != NULL) XDPRINTF((2,0,"%s TO: ", comment));
  if (nw_debug > 1) print_ipx_addr(to_addr);
  if (fd < 0) {
    if ((fd=open_ipx_socket(NULL, 0)) < 0)
      return(-1);
    fd_is_dyna++;
  }
  if ((result=t_sndudata(fd, &ud)) < 0){
    if (nw_debug > 1) t_error("t_sndudata !OK");
  }
  if (fd_is_dyna) {
    t_unbind(fd);
    t_close(fd);
  }
  return(result);
}

int receive_ipx_data(int fd, int *pack_typ, IPX_DATA *d,
                     ipxAddr_t *fromaddr, int waitmsec)
{
  int result=-1;
  if (waitmsec) {
    struct pollfd  polls[1];
    polls[0].fd      = fd;
    polls[0].events  = POLLIN|POLLPRI;
    polls[0].revents = 0;
    if (1 == poll(polls, 1, waitmsec))
      waitmsec=0;
  }
  if (!waitmsec) {
    struct     t_unitdata ud;
    uint8      ipx_pack_typ;
    int        flags = 0;
    ud.opt.len       = sizeof(ipx_pack_typ);
    ud.opt.maxlen    = sizeof(ipx_pack_typ);
    ud.opt.buf       = (char*)&ipx_pack_typ; /* gets actual typ */

    ud.addr.len      = sizeof(ipxAddr_t);
    ud.addr.maxlen   = sizeof(ipxAddr_t);

    ud.addr.buf      = (char*)fromaddr;
    ud.udata.len     = IPX_MAX_DATA;
    ud.udata.maxlen  = IPX_MAX_DATA;
    ud.udata.buf     = (char*)d;
    if (t_rcvudata(fd, &ud, &flags) < 0){
      struct t_uderr uderr;
      ipxAddr_t  erradr;
      uint8      err_pack_typ;
      uderr.addr.len      = sizeof(ipxAddr_t);
      uderr.addr.maxlen   = sizeof(ipxAddr_t);
      uderr.addr.buf      = (char*)&erradr;
      uderr.opt.len       = sizeof(err_pack_typ);
      uderr.opt.maxlen    = sizeof(err_pack_typ);
      uderr.opt.buf       = (char*)&err_pack_typ; /* get actual typ */
      ud.addr.buf         = (char*)&fromaddr;
      t_rcvuderr(fd, &uderr);
      XDPRINTF((2, 0, "Error from %s, Code = 0x%lx", visable_ipx_adr(&erradr), uderr.error));
      if (nw_debug)
        t_error("t_rcvudata !OK");
      result = -1;
    } else {
      if (pack_typ)
        *pack_typ=(int)ipx_pack_typ;
      result=ud.udata.len;
    }
  }
  return(result);
}

int send_own_data(int fd, IPX_DATA *d, ipxAddr_t *toaddr)
/* returns < 0 if senderror or functionresultcode > = 0 */
{
static int lastsequence=0;
  int tries        =  0;
  int sendsize     =  d->owndata.d.size+sizeof(d->owndata.d.size)+
                         sizeof(d->owndata.h);
  d->owndata.h.type[0]   = 0xee;
  d->owndata.h.type[1]   = 0xee;
  d->owndata.h.sequence  = (uint8) ++lastsequence;
  d->owndata.h.reserved  = 0;


  while (tries++ < MAX_SEND_TRIES) {
    int result=send_ipx_data(fd, 17, sendsize, (char*)d,
                     toaddr, "send_own_data");
    while (result > -1) {
      int packet_typ;
      IPX_DATA   ipxd;
      ipxAddr_t  fromaddr;
      result=receive_ipx_data(fd, &packet_typ, &ipxd, &fromaddr,MAX_WAIT_MSEC);
      XDPRINTF((2, 0, "receive_ipx_data, result=%d, typ=0x%x%x, sequence=%d",
              result,
              (int)ipxd.ownreply.type[0],
              (int)ipxd.ownreply.type[1],
              (int)ipxd.ownreply.sequence ));
      if (sizeof(OWN_REPLY) == result
          && ipxd.ownreply.type[0]  == 0xef
          && ipxd.ownreply.type[1]  == 0xef
          && ipxd.ownreply.sequence == d->owndata.h.sequence)
            return((int)ipxd.ownreply.result);
    } /* while */
  } /* while */
  return(-1);
}

int send_own_reply(int fd, int result, int sequence, ipxAddr_t *toaddr)
{
  IPX_DATA   ipxd;
  IPX_DATA   *d=&ipxd;
  d->ownreply.type[0]   = 0xef;
  d->ownreply.type[1]   = 0xef;
  d->ownreply.sequence  = (uint8) sequence;
  d->ownreply.result    = result;
  return(send_ipx_data(fd, 17, sizeof(OWN_REPLY),
               (char*)d, toaddr, "send_own_reply"));
}

#if 0
int get_ipx_addr(ipxAddr_t *addr)
{
  int fd=t_open("/dev/ipx", O_RDWR, NULL);
  struct t_optmgmt optb;
  int result = -1;
  if (fd < 0) {
    t_error("t_open !Ok");
    return(-1);
  }
  optb.opt.maxlen = optb.opt.len = sizeof(ipxAddr_t);
  optb.opt.buf    = (char*)addr;
  optb.flags      = 0;
  result = t_optmgmt(fd, &optb, &optb);
  if (result < 0) t_error("t_optmgmt !Ok");
  else result=0;
  t_close(fd);
  return(result);
}
#else

int get_ipx_addr(ipxAddr_t *addr)
{
  int fd=t_open("/dev/ipx", O_RDWR, NULL);
  struct t_bind   bind;
  int result = -1;
  if (fd < 0) {
    t_error("t_open !Ok");
    return(-1);
  }
  bind.addr.len    = sizeof(ipxAddr_t);
  bind.addr.maxlen = sizeof(ipxAddr_t);
  bind.addr.buf    = (char*)addr;
  bind.qlen        = 0; /* ever */
  memset(addr, 0, sizeof(ipxAddr_t));
  if (t_bind(fd, &bind, &bind) < 0)
    t_error("tbind:get_ipx_addr");
  else {
    result=0;
    t_unbind(fd);
  }
  t_close(fd);
  return(result);
}
#endif

uint8  *station_fn=NULL;

int find_station_match(int entry, ipxAddr_t *addr)
{
  int matched = 0;
  if (station_fn && *station_fn) {
    FILE *f=fopen((char*)station_fn, "r");
    if (f) {
      uint8  buff[200];
      uint8  addrstring[100];
      int   what;
      ipx_addr_to_adr((char*)addrstring, addr);
      upstr(addrstring);
      while (0 != (what = get_ini_entry(f, 0, buff, sizeof(buff)))){
        if (what == entry) {
          uint8  *p = buff + strlen((char*)buff);
          while (p-- > buff && *p==32) *p='\0';
          upstr(buff);
          if (name_match(addrstring, buff)) {
            matched=1;
            break;
          }
        }
      }
      fclose(f);
    } else {
      XDPRINTF((3, 0, "find_station_match, cannot open '%s'",
           station_fn));
    }
  }
  XDPRINTF((3, 0, "find_station_match entry=%d, matched=%d, addr=%s",
          entry, matched, visable_ipx_adr(addr)));
  return(matched);
}




