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