#include <sys/types.h>
#include <sys/sysent.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include "funcdef.h"
#include "../defs.h"

int _fastcall
tcpopen_socket(buf)
short _far *buf;
{
    int fd;
    struct sockaddr_in in;
    int on = 1;
    int type;
    struct net_addr _far *addr = (struct net_addr _far *)&buf[2];

    switch (buf[1]) {
	case NET_TYPE_TCP:
	    type = SOCK_STREAM;
	    break;
	case NET_TYPE_UDP:
	    type = SOCK_DGRAM;
	    break;
	default:
	    dfputs("unknown socket type\r\n");
	    buf[0] = -1;
	    buf[1] = 0;
	    buf[2] = NETERR_BADARGS;
	    return 6;
    }
    fd = socket(AF_INET, type, 0);
    if (fd < 0) {
	net_perror("socket");
	return fd;
    }

    if (ioctl(fd, FIONBIO, (fptr)&on) < 0) {
	net_perror("ioctl");
	sclose(fd);
	return -1;
    }
    if (type == SOCK_DGRAM) {
	if (setsoopt(fd, SOL_SOCKET, SO_BROADCAST, (fptr)&on, sizeof on) < 0)
	    net_perror("SO_BROADCAST");
    }
    (void)setsoopt(fd, SOL_SOCKET, SO_REUSEADDR, (fptr)&on, sizeof on);
    in.sin_family = AF_INET;
    in.sin_port = htons(addr->socket);
    in.sin_addr.s_addr = addr->host;
    memset(in.sin_zero, 0, sizeof in.sin_zero);

    if (bind(fd, (fptr)&in, sizeof in) < 0) {
	net_perror("bind");
	sclose(fd);
	return -1;
    }
    if (type == SOCK_STREAM)
	(void)listen(fd, 5);
    buf[0] = SD(fd);
    return sizeof (struct net_addr) + 2 * sizeof (short);
}

void
net_perror(str)
char _near *str;
{
    dfputs(str);
    dfputs(": Error ");
    printd(errno);
    dfputs("\r\n");
}

int _fastcall
tcpopen_accept(buf)
short _far *buf;
{
    struct net_addr _far *addr = (struct net_addr _far *)&buf[1];
    struct sockaddr_in in;
    int len;
    int ns;

    len = sizeof in;
    ns = accept(buf[1] | FD_SOCKET, (fptr)&in, &len);
    if (ns < 0)
	return ns;
    addr->host = in.sin_addr.s_addr;
    addr->socket = ntohs(in.sin_port);
    buf[0] = SD(ns);
    return sizeof (struct net_addr) + sizeof (short);
}

#include <netinet/if.h>

int _fastcall
tcpopen_info(buf)
short _far *buf;
{
    struct net_info _far *ni = (struct net_info _far *)&buf[1];
    struct ifconf ifc;
    char ifbuf[128];

    ifc.ifc_len = sizeof ifbuf;
    ifc.ifc_buf = ifbuf;
    if (ioctl(buf[1] | FD_SOCKET, SIOCGIFCONF, (fptr)&ifc) < 0)
	return -1;
    buf[0] = 0;
    ni->ip_address = ((struct sockaddr_in far *)&ifc.ifc_req->ifr_addr)->sin_addr.s_addr;
#ifdef notdef
    ni->ip_broadcast = ((struct sockaddr_in far *)&ifc.ifc_req->ifr_broadaddr)->sin_addr.s_addr;
dfputs("broadcast=");
printn((u_short)(ni->ip_broadcast >> 16));
printn((u_short)ni->ip_broadcast);
dfputs("\r\n");
#endif
    ni->ip_broadcast = 0xffffffff;
    return sizeof (struct net_info) + sizeof (short);
}
