Made htparser listening much more flexible.
[ashd.git] / src / plaintcp.c
1 /*
2     ashd - A Sane HTTP Daemon
3     Copyright (C) 2008  Fredrik Tolf <fredrik@dolda2000.com>
4
5     This program is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <stdio.h>
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
25 #include <errno.h>
26 #include <string.h>
27
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include <utils.h>
32 #include <req.h>
33 #include <mt.h>
34 #include <mtio.h>
35 #include <log.h>
36
37 #include "htparser.h"
38
39 struct tcpconn {
40     struct sockaddr_storage name;
41 };
42
43 static int listensock4(int port)
44 {
45     struct sockaddr_in name;
46     int fd;
47     int valbuf;
48     
49     memset(&name, 0, sizeof(name));
50     name.sin_family = AF_INET;
51     name.sin_port = htons(port);
52     if((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
53         return(-1);
54     valbuf = 1;
55     setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &valbuf, sizeof(valbuf));
56     if(bind(fd, (struct sockaddr *)&name, sizeof(name))) {
57         close(fd);
58         return(-1);
59     }
60     if(listen(fd, 16) < 0) {
61         close(fd);
62         return(-1);
63     }
64     return(fd);
65 }
66
67 static int listensock6(int port)
68 {
69     struct sockaddr_in6 name;
70     int fd;
71     int valbuf;
72     
73     memset(&name, 0, sizeof(name));
74     name.sin6_family = AF_INET6;
75     name.sin6_port = htons(port);
76     if((fd = socket(PF_INET6, SOCK_STREAM, 0)) < 0)
77         return(-1);
78     valbuf = 1;
79     setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &valbuf, sizeof(valbuf));
80     if(bind(fd, (struct sockaddr *)&name, sizeof(name))) {
81         close(fd);
82         return(-1);
83     }
84     if(listen(fd, 16) < 0) {
85         close(fd);
86         return(-1);
87     }
88     return(fd);
89 }
90
91 static int initreq(struct conn *conn, struct hthead *req)
92 {
93     struct tcpconn *tcp = conn->pdata;
94     char nmbuf[256];
95     
96     if(tcp->name.ss_family == AF_INET) {
97         headappheader(req, "X-Ash-Address", inet_ntop(AF_INET, &((struct sockaddr_in *)&tcp->name)->sin_addr, nmbuf, sizeof(nmbuf)));
98         headappheader(req, "X-Ash-Port", sprintf3("%i", ntohs(((struct sockaddr_in *)&tcp->name)->sin_port)));
99     } else if(tcp->name.ss_family == AF_INET6) {
100         headappheader(req, "X-Ash-Address", inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&tcp->name)->sin6_addr, nmbuf, sizeof(nmbuf)));
101         headappheader(req, "X-Ash-Port", sprintf3("%i", ntohs(((struct sockaddr_in6 *)&tcp->name)->sin6_port)));
102     }
103     return(0);
104 }
105
106 void servetcp(struct muth *muth, va_list args)
107 {
108     vavar(int, fd);
109     vavar(struct sockaddr_storage, name);
110     FILE *in;
111     struct conn *conn;
112     struct tcpconn *tcp;
113     
114     in = mtstdopen(fd, 1, 60, "r+");
115     omalloc(conn);
116     conn->pdata = omalloc(tcp);
117     conn->initreq = initreq;
118     tcp->name = name;
119     
120     serve(in, conn);
121     
122     free(tcp);
123     free(conn);
124 }
125
126 static void listenloop(struct muth *muth, va_list args)
127 {
128     vavar(int, ss);
129     int ns;
130     struct sockaddr_storage name;
131     socklen_t namelen;
132     
133     while(1) {
134         namelen = sizeof(name);
135         block(ss, EV_READ, 0);
136         ns = accept(ss, (struct sockaddr *)&name, &namelen);
137         if(ns < 0) {
138             flog(LOG_ERR, "accept: %s", strerror(errno));
139             goto out;
140         }
141         mustart(servetcp, ns, name);
142     }
143     
144 out:
145     close(ss);
146 }
147
148 void handleplain(int argc, char **argp, char **argv)
149 {
150     int port, fd;
151     int i;
152     
153     port = 80;
154     for(i = 0; i < argc; i++) {
155         if(!strcmp(argp[i], "help")) {
156             printf("plain handler parameters:\n");
157             printf("\tport=TCP-PORT (default is 80)\n");
158             exit(0);
159         } else if(!strcmp(argp[i], "port")) {
160             port = atoi(argv[i]);
161         } else {
162             flog(LOG_ERR, "unknown parameter `%s' to plain handler", argp[i]);
163             exit(1);
164         }
165     }
166     if((fd = listensock6(port)) < 0) {
167         flog(LOG_ERR, "could not listen on IPv6 (port %i): %s", port, strerror(errno));
168         exit(1);
169     }
170     mustart(listenloop, fd);
171     if((fd = listensock4(port)) < 0) {
172         if(errno != EADDRINUSE) {
173             flog(LOG_ERR, "could not listen on IPv4 (port %i): %s", port, strerror(errno));
174             exit(1);
175         }
176     } else {
177         mustart(listenloop, fd);
178     }
179 }