picolisp

Unnamed repository; edit this file to name it for gitweb.
git clone https://logand.com/git/picolisp.git/
Log | Files | Refs | README | LICENSE

net.c (5611B)


      1 /* 06feb13abu
      2  * (c) Software Lab. Alexander Burger
      3  */
      4 
      5 #include "pico.h"
      6 
      7 #include <netdb.h>
      8 #include <arpa/inet.h>
      9 #include <netinet/in.h>
     10 #include <sys/socket.h>
     11 
     12 static void ipErr(any ex, char *s) {
     13    err(ex, NULL, "IP %s error: %s", s, strerror(errno));
     14 }
     15 
     16 // (port ['T] 'cnt|(cnt . cnt) ['var]) -> cnt
     17 any doPort(any ex) {
     18    any x, y;
     19    int type, sd, n;
     20    unsigned short port;
     21    struct sockaddr_in6 addr;
     22 
     23    x = cdr(ex);
     24    type = SOCK_STREAM;
     25    if ((y = EVAL(car(x))) == T)
     26       type = SOCK_DGRAM,  x = cdr(x),  y = EVAL(car(x));
     27    if ((sd = socket(AF_INET6, type, 0)) < 0)
     28       ipErr(ex, "socket");
     29    closeOnExec(ex, sd);
     30    n = 0;
     31    if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &n, sizeof(n)) < 0)
     32       ipErr(ex, "IPV6_V6ONLY");
     33    memset(&addr, 0, sizeof(addr));
     34    addr.sin6_family = AF_INET6;
     35    addr.sin6_addr = in6addr_any;
     36    if (isNum(y)) {
     37       if ((port = (unsigned short)xCnt(ex,y)) != 0) {
     38          n = 1;
     39          if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) < 0)
     40             ipErr(ex, "SO_REUSEADDR");
     41       }
     42    }
     43    else if (isCell(y))
     44       port = (unsigned short)xCnt(ex,car(y));
     45    else
     46       argError(ex,y);
     47    for (;;) {
     48       addr.sin6_port = htons(port);
     49       if (bind(sd, (struct sockaddr*)&addr, sizeof(addr)) >= 0)
     50          break;
     51       if (!isCell(y)  ||  ++port > xCnt(ex,cdr(y)))
     52          close(sd),  ipErr(ex, "bind");
     53    }
     54    if (type == SOCK_STREAM  &&  listen(sd,5) < 0)
     55       close(sd),  ipErr(ex, "listen");
     56    if (!isNil(y = EVAL(cadr(x)))) {
     57       socklen_t len = sizeof(addr);
     58       if (getsockname(sd, (struct sockaddr*)&addr, &len) < 0)
     59          close(sd),  ipErr(ex, "getsockname");
     60       NeedVar(ex,y);
     61       CheckVar(ex,y);
     62       val(y) = boxCnt(ntohs(addr.sin6_port));
     63    }
     64    return boxCnt(sd);
     65 }
     66 
     67 static any tcpAccept(int sd) {
     68    int i, f, sd2;
     69    char s[INET6_ADDRSTRLEN];
     70    struct sockaddr_in6 addr;
     71 
     72    f = nonblocking(sd);
     73    i = 200; do {
     74       socklen_t len = sizeof(addr);
     75       if ((sd2 = accept(sd, (struct sockaddr*)&addr, &len)) >= 0) {
     76          fcntl(sd, F_SETFL, f);
     77 #ifndef __linux__
     78          fcntl(sd2, F_SETFL, 0);
     79 #endif
     80          inet_ntop(AF_INET6, &addr.sin6_addr, s, INET6_ADDRSTRLEN);
     81          val(Adr) = mkStr(s);
     82          initInFile(sd2,NULL), initOutFile(sd2);
     83          return boxCnt(sd2);
     84       }
     85       usleep(100000);  // 100 ms
     86    } while (errno == EAGAIN  &&  --i >= 0);
     87    fcntl(sd, F_SETFL, f);
     88    return NULL;
     89 }
     90 
     91 // (accept 'cnt) -> cnt | NIL
     92 any doAccept(any ex) {
     93    return tcpAccept((int)evCnt(ex, cdr(ex))) ?: Nil;
     94 }
     95 
     96 // (listen 'cnt1 ['cnt2]) -> cnt | NIL
     97 any doListen(any ex) {
     98    any x;
     99    int sd;
    100    long ms;
    101 
    102    sd = (int)evCnt(ex, x = cdr(ex));
    103    x = cdr(x);
    104    ms = isNil(x = EVAL(car(x)))? -1 : xCnt(ex,x);
    105    for (;;) {
    106       if (!waitFd(ex, sd, ms))
    107          return Nil;
    108       if (x = tcpAccept(sd))
    109          return x;
    110    }
    111 }
    112 
    113 // (host 'any) -> sym
    114 any doHost(any x) {
    115    x = evSym(cdr(x));
    116    {
    117       struct addrinfo *lst, *p;
    118       char host[NI_MAXHOST];
    119       char nm[bufSize(x)];
    120 
    121       bufString(x, nm);
    122       if (getaddrinfo(nm, NULL, NULL, &lst))
    123          return Nil;
    124       x = Nil;
    125       for (p = lst; p; p = p->ai_next) {
    126          if (getnameinfo(p->ai_addr, p->ai_addrlen, host, NI_MAXHOST, NULL, 0, NI_NAMEREQD) == 0 && host[0]) {
    127             x = mkStr(host);
    128             break;
    129          }
    130       }
    131       freeaddrinfo(lst);
    132       return x;
    133    }
    134 }
    135 
    136 static struct addrinfo *server(int type, any node, any service) {
    137    struct addrinfo hints, *lst;
    138    char nd[bufSize(node)], sv[bufSize(service)];
    139 
    140    memset(&hints, 0, sizeof(hints));
    141    hints.ai_family = AF_UNSPEC;
    142    hints.ai_socktype = type;
    143    bufString(node, nd),  bufString(service, sv);
    144    return getaddrinfo(nd, sv, &hints, &lst)? NULL : lst;
    145 }
    146 
    147 // (connect 'any1 'any2) -> cnt | NIL
    148 any doConnect(any ex) {
    149    struct addrinfo *lst, *p;
    150    any port;
    151    int sd;
    152    cell c1;
    153 
    154    Push(c1, evSym(cdr(ex)));
    155    port = evSym(cddr(ex));
    156    for (p = lst = server(SOCK_STREAM, Pop(c1), port); p; p = p->ai_next) {
    157       if ((sd = socket(p->ai_family, p->ai_socktype, 0)) >= 0) {
    158          if (connect(sd, p->ai_addr, p->ai_addrlen) == 0) {
    159             closeOnExec(ex, sd);
    160             initInFile(sd,NULL), initOutFile(sd);
    161             freeaddrinfo(lst);
    162             return boxCnt(sd);
    163          }
    164          close(sd);
    165       }
    166    }
    167    freeaddrinfo(lst);
    168    return Nil;
    169 }
    170 
    171 /*** UDP send/receive ***/
    172 #define UDPMAX 4096
    173 static byte *UdpBuf, *UdpPtr;
    174 
    175 static void putUdp(int c) {
    176    if (UdpPtr == UdpBuf + UDPMAX)
    177       err(NULL, NULL, "UDP overflow");
    178    *UdpPtr++ = c;
    179 }
    180 
    181 static int getUdp(void) {
    182    if (UdpPtr == UdpBuf + UDPMAX)
    183       return -1;
    184    return *UdpPtr++;
    185 }
    186 
    187 // (udp 'any1 'any2 'any3) -> any
    188 // (udp 'cnt) -> any
    189 any doUdp(any ex) {
    190    any x, y;
    191    cell c1;
    192    struct addrinfo *lst, *p;
    193    int sd;
    194    byte buf[UDPMAX];
    195 
    196    x = cdr(ex),  data(c1) = EVAL(car(x));
    197    if (!isCell(x = cdr(x))) {
    198       if (recv((int)xCnt(ex, data(c1)), buf, UDPMAX, 0) < 0)
    199          return Nil;
    200       getBin = getUdp,  UdpPtr = UdpBuf = buf;
    201       return binRead(ExtN) ?: Nil;
    202    }
    203    Save(c1);
    204    data(c1) = xSym(data(c1));
    205    y = evSym(x);
    206    drop(c1);
    207    if (lst = server(SOCK_DGRAM, data(c1), y)) {
    208       x = cdr(x),  x = EVAL(car(x));
    209       putBin = putUdp,  UdpPtr = UdpBuf = buf,  binPrint(ExtN, x);
    210       for (p = lst; p; p = p->ai_next) {
    211          if ((sd = socket(p->ai_family, p->ai_socktype, 0)) >= 0) {
    212             sendto(sd, buf, UdpPtr-buf, 0, p->ai_addr, p->ai_addrlen);
    213             close(sd);
    214             freeaddrinfo(lst);
    215             return x;
    216          }
    217       }
    218       freeaddrinfo(lst);
    219    }
    220    return Nil;
    221 }