indent -kr -i8 -l132 on everything in webcit-ng
[citadel.git] / webcit-ng / tcp_sockets.c
1 /*
2  * TCP sockets layer
3  *
4  * Copyright (c) 1987-2018 by the citadel.org team
5  *
6  * This program is open source software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 3.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14
15 #include "webcit.h"
16
17 /*
18  * lingering_close() a`la Apache. see
19  * http://httpd.apache.org/docs/2.0/misc/fin_wait_2.html for rationale
20  */
21 int lingering_close(int fd)
22 {
23         char buf[SIZ];
24         int i;
25         fd_set set;
26         struct timeval tv, start;
27
28         gettimeofday(&start, NULL);
29         if (fd == -1)
30                 return -1;
31         shutdown(fd, 1);
32         do {
33                 do {
34                         gettimeofday(&tv, NULL);
35                         tv.tv_sec = SLEEPING - (tv.tv_sec - start.tv_sec);
36                         tv.tv_usec = start.tv_usec - tv.tv_usec;
37                         if (tv.tv_usec < 0) {
38                                 tv.tv_sec--;
39                                 tv.tv_usec += 1000000;
40                         }
41                         FD_ZERO(&set);
42                         FD_SET(fd, &set);
43                         i = select(fd + 1, &set, NULL, NULL, &tv);
44                 } while (i == -1 && errno == EINTR);
45
46                 if (i <= 0)
47                         break;
48
49                 i = read(fd, buf, sizeof buf);
50         } while (i != 0 && (i != -1 || errno == EINTR));
51
52         return close(fd);
53 }
54
55
56 /* 
57  * This is a generic function to set up a master socket for listening on
58  * a TCP port.  The server shuts down if the bind fails.  (IPv4/IPv6 version)
59  *
60  * ip_addr      IP address to bind
61  * port_number  port number to bind
62  * queue_len    number of incoming connections to allow in the queue
63  */
64 int webcit_tcp_server(const char *ip_addr, int port_number, int queue_len)
65 {
66         const char *ipv4broadcast = "0.0.0.0";
67         int IsDefault = 0;
68         struct protoent *p;
69         struct sockaddr_in6 sin6;
70         struct sockaddr_in sin4;
71         int s, i, b;
72         int ip_version = 6;
73
74       retry:
75         memset(&sin6, 0, sizeof(sin6));
76         memset(&sin4, 0, sizeof(sin4));
77         sin6.sin6_family = AF_INET6;
78         sin4.sin_family = AF_INET;
79
80         if ((ip_addr == NULL)   /* any IPv6 */
81             ||(IsEmptyStr(ip_addr))
82             || (!strcmp(ip_addr, "*"))
83             ) {
84                 IsDefault = 1;
85                 ip_version = 6;
86                 sin6.sin6_addr = in6addr_any;
87         } else if (!strcmp(ip_addr, "0.0.0.0")) {       /* any IPv4 */
88                 ip_version = 4;
89                 sin4.sin_addr.s_addr = INADDR_ANY;
90         } else if ((strchr(ip_addr, '.')) && (!strchr(ip_addr, ':'))) { /* specific IPv4 */
91                 ip_version = 4;
92                 if (inet_pton(AF_INET, ip_addr, &sin4.sin_addr) <= 0) {
93                         syslog(LOG_WARNING, "Error binding to [%s] : %s\n", ip_addr, strerror(errno));
94                         return (-1);
95                 }
96         } else {                /* specific IPv6 */
97
98                 ip_version = 6;
99                 if (inet_pton(AF_INET6, ip_addr, &sin6.sin6_addr) <= 0) {
100                         syslog(LOG_WARNING, "Error binding to [%s] : %s\n", ip_addr, strerror(errno));
101                         return (-1);
102                 }
103         }
104
105         if (port_number == 0) {
106                 syslog(LOG_WARNING, "Cannot start: no port number specified.\n");
107                 return (-1);
108         }
109         sin6.sin6_port = htons((u_short) port_number);
110         sin4.sin_port = htons((u_short) port_number);
111
112         p = getprotobyname("tcp");
113
114         s = socket(((ip_version == 6) ? PF_INET6 : PF_INET), SOCK_STREAM, (p->p_proto));
115         if (s < 0) {
116                 if (IsDefault && (errno == EAFNOSUPPORT)) {
117                         s = 0;
118                         ip_addr = ipv4broadcast;
119                         goto retry;
120                 }
121                 syslog(LOG_WARNING, "Can't create a listening socket: %s\n", strerror(errno));
122                 return (-1);
123         }
124         /* Set some socket options that make sense. */
125         i = 1;
126         setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
127
128         if (ip_version == 6) {
129                 b = bind(s, (struct sockaddr *) &sin6, sizeof(sin6));
130         } else {
131                 b = bind(s, (struct sockaddr *) &sin4, sizeof(sin4));
132         }
133
134         if (b < 0) {
135                 syslog(LOG_ERR, "Can't bind: %s\n", strerror(errno));
136                 close(s);
137                 return (-1);
138         }
139
140         if (listen(s, queue_len) < 0) {
141                 syslog(LOG_ERR, "Can't listen: %s\n", strerror(errno));
142                 close(s);
143                 return (-1);
144         }
145         return (s);
146 }
147
148
149 /*
150  * Create a Unix domain socket and listen on it
151  * sockpath - file name of the unix domain socket
152  * queue_len - Number of incoming connections to allow in the queue
153  */
154 int webcit_uds_server(char *sockpath, int queue_len)
155 {
156         struct sockaddr_un addr;
157         int s;
158         int i;
159         int actual_queue_len;
160
161         actual_queue_len = queue_len;
162         if (actual_queue_len < 5)
163                 actual_queue_len = 5;
164
165         i = unlink(sockpath);
166         if ((i != 0) && (errno != ENOENT)) {
167                 syslog(LOG_WARNING, "webcit: can't unlink %s: %s", sockpath, strerror(errno));
168                 return (-1);
169         }
170
171         memset(&addr, 0, sizeof(addr));
172         addr.sun_family = AF_UNIX;
173         safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
174
175         s = socket(AF_UNIX, SOCK_STREAM, 0);
176         if (s < 0) {
177                 syslog(LOG_WARNING, "webcit: Can't create a unix domain socket: %s", strerror(errno));
178                 return (-1);
179         }
180
181         if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
182                 syslog(LOG_WARNING, "webcit: Can't bind: %s", strerror(errno));
183                 close(s);
184                 return (-1);
185         }
186
187         if (listen(s, actual_queue_len) < 0) {
188                 syslog(LOG_WARNING, "webcit: Can't listen: %s", strerror(errno));
189                 close(s);
190                 return (-1);
191         }
192
193         chmod(sockpath, 0777);
194         return (s);
195 }