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