doc: Documented htpipe.
[ashd.git] / src / plaintcp.c
index 72a3959..56fc81c 100644 (file)
@@ -22,6 +22,7 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <fcntl.h>
 #include <errno.h>
 #include <string.h>
 
 
 #include "htparser.h"
 
+struct tcpport {
+    int fd;
+    int sport;
+};
+
 struct tcpconn {
     struct sockaddr_storage name;
+    struct tcpport *port;
+    int fd;
 };
 
-static int listensock4(int port)
+int listensock4(int port)
 {
     struct sockaddr_in name;
     int fd;
@@ -57,14 +65,15 @@ static int listensock4(int port)
        close(fd);
        return(-1);
     }
-    if(listen(fd, 16) < 0) {
+    if(listen(fd, 128) < 0) {
        close(fd);
        return(-1);
     }
+    fcntl(fd, F_SETFD, FD_CLOEXEC);
     return(fd);
 }
 
-static int listensock6(int port)
+int listensock6(int port)
 {
     struct sockaddr_in6 name;
     int fd;
@@ -81,25 +90,58 @@ static int listensock6(int port)
        close(fd);
        return(-1);
     }
-    if(listen(fd, 16) < 0) {
+    if(listen(fd, 128) < 0) {
        close(fd);
        return(-1);
     }
+    fcntl(fd, F_SETFD, FD_CLOEXEC);
     return(fd);
 }
 
+char *formathaddress(struct sockaddr *name, socklen_t namelen)
+{
+    static char buf[128];
+    struct sockaddr_in *v4;
+    struct sockaddr_in6 *v6;
+
+    switch(name->sa_family) {
+    case AF_INET:
+       v4 = (struct sockaddr_in *)name;
+       if(!inet_ntop(AF_INET, &v4->sin_addr, buf, sizeof(buf)))
+           return(NULL);
+       return(buf);
+    case AF_INET6:
+       v6 = (struct sockaddr_in6 *)name;
+       if(IN6_IS_ADDR_V4MAPPED(&v6->sin6_addr)) {
+           if(!inet_ntop(AF_INET, ((char *)&v6->sin6_addr) + 12, buf, sizeof(buf)))
+               return(NULL);
+       } else {
+           if(!inet_ntop(AF_INET6, &v6->sin6_addr, buf, sizeof(buf)))
+               return(NULL);
+       }
+       return(buf);
+    default:
+       errno = EPFNOSUPPORT;
+       return(NULL);
+    }
+}
+
 static int initreq(struct conn *conn, struct hthead *req)
 {
     struct tcpconn *tcp = conn->pdata;
-    char nmbuf[256];
+    struct sockaddr_storage sa;
+    socklen_t salen;
     
-    if(tcp->name.ss_family == AF_INET) {
-       headappheader(req, "X-Ash-Address", inet_ntop(AF_INET, &((struct sockaddr_in *)&tcp->name)->sin_addr, nmbuf, sizeof(nmbuf)));
+    headappheader(req, "X-Ash-Address", formathaddress((struct sockaddr *)&tcp->name, sizeof(sa)));
+    if(tcp->name.ss_family == AF_INET)
        headappheader(req, "X-Ash-Port", sprintf3("%i", ntohs(((struct sockaddr_in *)&tcp->name)->sin_port)));
-    } else if(tcp->name.ss_family == AF_INET6) {
-       headappheader(req, "X-Ash-Address", inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&tcp->name)->sin6_addr, nmbuf, sizeof(nmbuf)));
+    else if(tcp->name.ss_family == AF_INET6)
        headappheader(req, "X-Ash-Port", sprintf3("%i", ntohs(((struct sockaddr_in6 *)&tcp->name)->sin6_port)));
-    }
+    salen = sizeof(sa);
+    if(!getsockname(tcp->fd, (struct sockaddr *)&sa, &salen))
+       headappheader(req, "X-Ash-Server-Address", formathaddress((struct sockaddr *)&sa, sizeof(sa)));
+    headappheader(req, "X-Ash-Server-Port", sprintf3("%i", tcp->port->sport));
+    headappheader(req, "X-Ash-Protocol", "http");
     return(0);
 }
 
@@ -107,54 +149,72 @@ void servetcp(struct muth *muth, va_list args)
 {
     vavar(int, fd);
     vavar(struct sockaddr_storage, name);
-    FILE *in;
-    struct conn *conn;
-    struct tcpconn *tcp;
-    
-    in = mtstdopen(fd, 1, 60, "r+");
-    omalloc(conn);
-    conn->pdata = omalloc(tcp);
-    conn->initreq = initreq;
-    tcp->name = name;
+    vavar(struct tcpport *, stcp);
+    struct bufio *in;
+    struct conn conn;
+    struct tcpconn tcp;
     
-    serve(in, conn);
-    
-    free(tcp);
-    free(conn);
+    memset(&conn, 0, sizeof(conn));
+    memset(&tcp, 0, sizeof(tcp));
+    in = mtbioopen(fd, 1, 60, "r+", NULL);
+    conn.pdata = &tcp;
+    conn.initreq = initreq;
+    tcp.fd = fd;
+    tcp.name = name;
+    tcp.port = stcp;
+    serve(in, fd, &conn);
 }
 
 static void listenloop(struct muth *muth, va_list args)
 {
-    vavar(int, ss);
-    int ns;
+    vavar(struct tcpport *, tcp);
+    int i, ns, n;
     struct sockaddr_storage name;
     socklen_t namelen;
     
+    fcntl(tcp->fd, F_SETFL, fcntl(tcp->fd, F_GETFL) | O_NONBLOCK);
     while(1) {
        namelen = sizeof(name);
-       block(ss, EV_READ, 0);
-       ns = accept(ss, (struct sockaddr *)&name, &namelen);
-       if(ns < 0) {
-           flog(LOG_ERR, "accept: %s", strerror(errno));
+       if(block(tcp->fd, EV_READ, 0) == 0)
            goto out;
+       n = 0;
+       while(1) {
+           ns = accept(tcp->fd, (struct sockaddr *)&name, &namelen);
+           if(ns < 0) {
+               if(errno == EAGAIN)
+                   break;
+               if(errno == ECONNABORTED)
+                   continue;
+               flog(LOG_ERR, "accept: %s", strerror(errno));
+               goto out;
+           }
+           mustart(servetcp, ns, name, tcp);
+           if(++n >= 100)
+               break;
        }
-       mustart(servetcp, ns, name);
     }
     
 out:
-    close(ss);
+    close(tcp->fd);
+    free(tcp);
+    for(i = 0; i < listeners.d; i++) {
+       if(listeners.b[i] == muth)
+           bufdel(listeners, i);
+    }
 }
 
 void handleplain(int argc, char **argp, char **argv)
 {
     int port, fd;
     int i;
+    struct tcpport *tcp;
     
     port = 80;
     for(i = 0; i < argc; i++) {
        if(!strcmp(argp[i], "help")) {
            printf("plain handler parameters:\n");
-           printf("\tport=TCP-PORT (default is 80)\n");
+           printf("\tport=TCP-PORT   [80]\n");
+           printf("\t\tThe TCP port to listen on.\n");
            exit(0);
        } else if(!strcmp(argp[i], "port")) {
            port = atoi(argv[i]);
@@ -167,13 +227,19 @@ void handleplain(int argc, char **argp, char **argv)
        flog(LOG_ERR, "could not listen on IPv6 (port %i): %s", port, strerror(errno));
        exit(1);
     }
-    mustart(listenloop, fd);
+    omalloc(tcp);
+    tcp->fd = fd;
+    tcp->sport = port;
+    bufadd(listeners, mustart(listenloop, tcp));
     if((fd = listensock4(port)) < 0) {
        if(errno != EADDRINUSE) {
            flog(LOG_ERR, "could not listen on IPv4 (port %i): %s", port, strerror(errno));
            exit(1);
        }
     } else {
-       mustart(listenloop, fd);
+       omalloc(tcp);
+       tcp->fd = fd;
+       tcp->sport = port;
+       bufadd(listeners, mustart(listenloop, tcp));
     }
 }