add changelog for 1.13
[beanstalkd.git] / net.c
bloba9d2056f6b7e3f05dd7e59c7edc67c550ec7cd34
1 #include "dat.h"
2 #include <netdb.h>
3 #include <stdio.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <string.h>
7 #include <errno.h>
8 #include <sys/socket.h>
9 #include <sys/un.h>
10 #include <sys/stat.h>
11 #include <netinet/in.h>
12 #include <netinet/tcp.h>
14 #ifdef HAVE_LIBSYSTEMD
15 #include <systemd/sd-daemon.h>
16 #endif
18 static int
19 set_nonblocking(int fd)
21 int flags, r;
23 flags = fcntl(fd, F_GETFL, 0);
24 if (flags < 0) {
25 twarn("getting flags");
26 return -1;
28 r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
29 if (r == -1) {
30 twarn("setting O_NONBLOCK");
31 return -1;
33 return 0;
36 static int
37 make_inet_socket(char *host, char *port)
39 int fd = -1, flags, r;
40 struct linger linger = {0, 0};
41 struct addrinfo *airoot, *ai, hints;
43 memset(&hints, 0, sizeof(hints));
44 hints.ai_family = AF_UNSPEC;
45 hints.ai_socktype = SOCK_STREAM;
46 hints.ai_flags = AI_PASSIVE;
47 r = getaddrinfo(host, port, &hints, &airoot);
48 if (r != 0) {
49 twarnx("getaddrinfo(): %s", gai_strerror(r));
50 return -1;
53 for (ai = airoot; ai; ai = ai->ai_next) {
54 fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
55 if (fd == -1) {
56 twarn("socket()");
57 continue;
60 r = set_nonblocking(fd);
61 if (r == -1) {
62 close(fd);
63 continue;
66 flags = 1;
67 r = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof flags);
68 if (r == -1) {
69 twarn("setting SO_REUSEADDR on fd %d", fd);
70 close(fd);
71 continue;
73 r = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof flags);
74 if (r == -1) {
75 twarn("setting SO_KEEPALIVE on fd %d", fd);
76 close(fd);
77 continue;
79 r = setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger, sizeof linger);
80 if (r == -1) {
81 twarn("setting SO_LINGER on fd %d", fd);
82 close(fd);
83 continue;
85 r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof flags);
86 if (r == -1) {
87 twarn("setting TCP_NODELAY on fd %d", fd);
88 close(fd);
89 continue;
92 if (host == NULL && ai->ai_family == AF_INET6) {
93 flags = 0;
94 r = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flags, sizeof(flags));
95 if (r == -1) {
96 twarn("setting IPV6_V6ONLY on fd %d", fd);
97 close(fd);
98 continue;
102 r = bind(fd, ai->ai_addr, ai->ai_addrlen);
103 if (r == -1) {
104 twarn("bind()");
105 close(fd);
106 continue;
108 if (verbose) {
109 char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV], *h = host, *p = port;
110 struct sockaddr_in addr;
111 socklen_t addrlen;
113 addrlen = sizeof(addr);
114 r = getsockname(fd, (struct sockaddr *) &addr, &addrlen);
115 if (!r) {
116 r = getnameinfo((struct sockaddr *) &addr, addrlen,
117 hbuf, sizeof(hbuf),
118 pbuf, sizeof(pbuf),
119 NI_NUMERICHOST|NI_NUMERICSERV);
120 if (!r) {
121 h = hbuf;
122 p = pbuf;
125 if (ai->ai_family == AF_INET6) {
126 printf("bind %d [%s]:%s\n", fd, h, p);
127 } else {
128 printf("bind %d %s:%s\n", fd, h, p);
132 r = listen(fd, 1024);
133 if (r == -1) {
134 twarn("listen()");
135 close(fd);
136 continue;
139 break;
142 freeaddrinfo(airoot);
144 if(ai == NULL)
145 fd = -1;
147 return fd;
150 static int
151 make_unix_socket(char *path)
153 int fd = -1, r;
154 struct stat st;
155 struct sockaddr_un addr;
156 const size_t maxlen = sizeof(addr.sun_path) - 1; // Reserve the last position for '\0'
158 memset(&addr, 0, sizeof(struct sockaddr_un));
159 addr.sun_family = AF_UNIX;
160 if (strlen(path) > maxlen) {
161 warnx("socket path %s is too long (%ld characters), where maximum allowed is %ld",
162 path, strlen(path), maxlen);
163 return -1;
165 strncpy(addr.sun_path, path, maxlen);
167 r = stat(path, &st);
168 if (r == 0) {
169 if (S_ISSOCK(st.st_mode)) {
170 warnx("removing existing local socket to replace it");
171 r = unlink(path);
172 if (r == -1) {
173 twarn("unlink");
174 return -1;
176 } else {
177 twarnx("another file already exists in the given path");
178 return -1;
182 fd = socket(AF_UNIX, SOCK_STREAM, 0);
183 if (fd == -1) {
184 twarn("socket()");
185 return -1;
188 r = set_nonblocking(fd);
189 if (r == -1) {
190 close(fd);
191 return -1;
194 r = bind(fd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
195 if (r == -1) {
196 twarn("bind()");
197 close(fd);
198 return -1;
200 if (verbose) {
201 printf("bind %d %s\n", fd, path);
204 r = listen(fd, 1024);
205 if (r == -1) {
206 twarn("listen()");
207 close(fd);
208 return -1;
211 return fd;
215 make_server_socket(char *host, char *port)
217 #ifdef HAVE_LIBSYSTEMD
218 int fd = -1, r;
220 /* See if we got a listen fd from systemd. If so, all socket options etc
221 * are already set, so we check that the fd is a TCP or UNIX listen socket
222 * and return. */
223 r = sd_listen_fds(1);
224 if (r < 0) {
225 twarn("sd_listen_fds");
226 return -1;
228 if (r > 0) {
229 if (r > 1) {
230 twarnx("inherited more than one listen socket;"
231 " ignoring all but the first");
233 fd = SD_LISTEN_FDS_START;
234 r = sd_is_socket_inet(fd, 0, SOCK_STREAM, 1, 0);
235 if (r < 0) {
236 twarn("sd_is_socket_inet");
237 errno = -r;
238 return -1;
240 if (r == 0) {
241 r = sd_is_socket_unix(fd, SOCK_STREAM, 1, NULL, 0);
242 if (r < 0) {
243 twarn("sd_is_socket_unix");
244 errno = -r;
245 return -1;
247 if (r == 0) {
248 twarnx("inherited fd is not a TCP or UNIX listening socket");
249 return -1;
252 return fd;
254 #endif
256 if (host && !strncmp(host, "unix:", 5)) {
257 return make_unix_socket(&host[5]);
258 } else {
259 return make_inet_socket(host, port);