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