Read This! | Picolisp | Picolisp Machine | Pil Sources | Pil Tutorials | Linux | BASH | C-Programmming | Javascipt | Python | Scheme | Operating Systems | AssemblyLanguage | Computer Security | Firewalls | Exploitation | Social Engineering | Metasploit | Emacs | vim | Pharo Smalltalk | Databases | Networking | Machine Learning | Git | Machine Learning | Algorithms | Open Data Science

net.c


/* 30jan15abu
 * (c) Software Lab. Alexander Burger
 */

#include "pico.h"

static void ipErr(any ex, char *s) {
   err(ex, NULL, "IP %s error: %s", s, strerror(errno));
}

// (port ['T] 'cnt|(cnt . cnt) ['var]) -> cnt
any doPort(any ex) {
   any x, y;
   int type, sd, n;
   unsigned short port;
   struct sockaddr_in6 addr;

   x = cdr(ex);
   type = SOCK_STREAM;
   if ((y = EVAL(car(x))) == T)
      type = SOCK_DGRAM,  x = cdr(x),  y = EVAL(car(x));
   if ((sd = socket(AF_INET6, type, 0)) < 0)
      ipErr(ex, "socket");
   closeOnExec(ex, sd);
   n = 0;
#ifndef __OpenBSD__
   if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &n, sizeof(n)) < 0)
      ipErr(ex, "IPV6_V6ONLY");
#endif
   memset(&addr, 0, sizeof(addr));
   addr.sin6_family = AF_INET6;
   addr.sin6_addr = in6addr_any;
   if (isNum(y)) {
      if ((port = (unsigned short)xCnt(ex,y)) != 0) {
         n = 1;
         if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) < 0)
            ipErr(ex, "SO_REUSEADDR");
      }
   }
   else if (isCell(y))
      port = (unsigned short)xCnt(ex,car(y));
   else
      argError(ex,y);
   for (;;) {
      addr.sin6_port = htons(port);
      if (bind(sd, (struct sockaddr*)&addr, sizeof(addr)) >= 0)
         break;
      if (!isCell(y)  ||  ++port > xCnt(ex,cdr(y)))
         close(sd),  ipErr(ex, "bind");
   }
   if (type == SOCK_STREAM  &&  listen(sd,5) < 0)
      close(sd),  ipErr(ex, "listen");
   if (!isNil(y = EVAL(cadr(x)))) {
      socklen_t len = sizeof(addr);
      if (getsockname(sd, (struct sockaddr*)&addr, &len) < 0)
         close(sd),  ipErr(ex, "getsockname");
      NeedVar(ex,y);
      CheckVar(ex,y);
      val(y) = boxCnt(ntohs(addr.sin6_port));
   }
   return boxCnt(sd);
}

static any tcpAccept(int sd) {
   int i, f, sd2;
   char s[INET6_ADDRSTRLEN];
   struct sockaddr_in6 addr;

   f = nonblocking(sd);
   i = 200; do {  // 20 s
      socklen_t len = sizeof(addr);
      if ((sd2 = accept(sd, (struct sockaddr*)&addr, &len)) >= 0) {
         fcntl(sd, F_SETFL, f);
#ifndef __linux__
         fcntl(sd2, F_SETFL, 0);
#endif
         inet_ntop(AF_INET6, &addr.sin6_addr, s, INET6_ADDRSTRLEN);
         val(Adr) = mkStr(s);
         initInFile(sd2,NULL), initOutFile(sd2);
         return boxCnt(sd2);
      }
      usleep(100000);  // 100 ms
   } while (errno == EAGAIN  &&  --i);
   fcntl(sd, F_SETFL, f);
   return NULL;
}

// (accept 'cnt) -> cnt | NIL
any doAccept(any ex) {
   return tcpAccept((int)evCnt(ex, cdr(ex))) ?: Nil;
}

// (listen 'cnt1 ['cnt2]) -> cnt | NIL
any doListen(any ex) {
   any x;
   int sd;
   long ms;

   sd = (int)evCnt(ex, x = cdr(ex));
   x = cdr(x);
   ms = isNil(x = EVAL(car(x)))? -1 : xCnt(ex,x);
   for (;;) {
      if (!waitFd(ex, sd, ms))
         return Nil;
      if (x = tcpAccept(sd))
         return x;
   }
}

// (host 'any) -> sym
any doHost(any x) {
   x = evSym(cdr(x));
   {
      struct addrinfo *lst, *p;
      char host[NI_MAXHOST];
      char nm[bufSize(x)];

      bufString(x, nm);
      if (getaddrinfo(nm, NULL, NULL, &lst))
         return Nil;
      x = Nil;
      for (p = lst; p; p = p->ai_next) {
         if (getnameinfo(p->ai_addr, p->ai_addrlen, host, NI_MAXHOST, NULL, 0, NI_NAMEREQD) == 0 && host[0]) {
            x = mkStr(host);
            break;
         }
      }
      freeaddrinfo(lst);
      return x;
   }
}

static struct addrinfo *server(int type, any node, any service) {
   struct addrinfo hints, *lst;
   char nd[bufSize(node)], sv[bufSize(service)];

   memset(&hints, 0, sizeof(hints));
   hints.ai_family = AF_UNSPEC;
   hints.ai_socktype = type;
   bufString(node, nd),  bufString(service, sv);
   return getaddrinfo(nd, sv, &hints, &lst)? NULL : lst;
}

// (connect 'any1 'any2) -> cnt | NIL
any doConnect(any ex) {
   struct addrinfo *lst, *p;
   any port;
   int sd;
   cell c1;

   Push(c1, evSym(cdr(ex)));
   port = evSym(cddr(ex));
   if (lst = server(SOCK_STREAM, Pop(c1), port)) {
      for (p = lst; p; p = p->ai_next) {
         if ((sd = socket(p->ai_family, p->ai_socktype, 0)) >= 0) {
            if (connect(sd, p->ai_addr, p->ai_addrlen) == 0) {
               closeOnExec(ex, sd);
               initInFile(sd,NULL), initOutFile(sd);
               freeaddrinfo(lst);
               return boxCnt(sd);
            }
            close(sd);
         }
      }
      freeaddrinfo(lst);
   }
   return Nil;
}

/*** UDP send/receive ***/
#define UDPMAX 4096
static byte *UdpBuf, *UdpPtr;

static void putUdp(int c) {
   if (UdpPtr == UdpBuf + UDPMAX)
      err(NULL, NULL, "UDP overflow");
   *UdpPtr++ = c;
}

static int getUdp(void) {
   if (UdpPtr == UdpBuf + UDPMAX)
      return -1;
   return *UdpPtr++;
}

// (udp 'any1 'any2 'any3) -> any
// (udp 'cnt) -> any
any doUdp(any ex) {
   any x, y;
   cell c1;
   struct addrinfo *lst, *p;
   int sd;
   byte buf[UDPMAX];

   x = cdr(ex),  data(c1) = EVAL(car(x));
   if (!isCell(x = cdr(x))) {
      if (recv((int)xCnt(ex, data(c1)), buf, UDPMAX, 0) < 0)
         return Nil;
      getBin = getUdp,  UdpPtr = UdpBuf = buf;
      return binRead(ExtN) ?: Nil;
   }
   Save(c1);
   data(c1) = xSym(data(c1));
   y = evSym(x);
   drop(c1);
   if (lst = server(SOCK_DGRAM, data(c1), y)) {
      x = cdr(x),  x = EVAL(car(x));
      putBin = putUdp,  UdpPtr = UdpBuf = buf,  binPrint(ExtN, x);
      for (p = lst; p; p = p->ai_next) {
         if ((sd = socket(p->ai_family, p->ai_socktype, 0)) >= 0) {
            sendto(sd, buf, UdpPtr-buf, 0, p->ai_addr, p->ai_addrlen);
            close(sd);
            freeaddrinfo(lst);
            return x;
         }
      }
      freeaddrinfo(lst);
   }
   return Nil;
}


http:///wiki/?netc

29jun17   admin