1 | // CODYlib -*- mode:c++ -*- |
2 | // Copyright (C) 2020 Nathan Sidwell, nathan@acm.org |
3 | // License: Apache v2.0 |
4 | |
5 | // Cody |
6 | #include "internal.hh" |
7 | #if CODY_NETWORKING |
8 | // C |
9 | #include <cerrno> |
10 | #include <cstring> |
11 | // OS |
12 | #include <netdb.h> |
13 | #include <unistd.h> |
14 | #include <arpa/inet.h> |
15 | #include <netinet/in.h> |
16 | #include <sys/un.h> |
17 | |
18 | #ifndef AI_NUMERICSERV |
19 | #define AI_NUMERICSERV 0 |
20 | #endif |
21 | |
22 | // Server-side networking helpers |
23 | |
24 | namespace Cody { |
25 | |
26 | int ListenSocket (char const **e, sockaddr const *addr, socklen_t len, |
27 | unsigned backlog) |
28 | { |
29 | char const *errstr = nullptr; |
30 | |
31 | int fd = socket (domain: addr->sa_family, SOCK_STREAM, protocol: 0); |
32 | if (fd < 0) |
33 | { |
34 | errstr = "creating socket" ; |
35 | |
36 | fail:; |
37 | int err = errno; |
38 | if (e) |
39 | *e = errstr; |
40 | if (fd >= 0) |
41 | close (fd: fd); |
42 | errno = err; |
43 | return -1; |
44 | } |
45 | |
46 | if (bind (fd: fd, addr: addr, len: len) < 0) |
47 | { |
48 | errstr = "binding socket" ; |
49 | goto fail; |
50 | } |
51 | |
52 | if (listen (fd: fd, n: backlog ? backlog : 17) < 0) |
53 | { |
54 | errstr = "listening socket" ; |
55 | goto fail; |
56 | } |
57 | |
58 | return fd; |
59 | } |
60 | |
61 | int ListenLocal (char const **e, char const *name, unsigned backlog) |
62 | { |
63 | sockaddr_un addr; |
64 | size_t len = strlen (s: name); |
65 | |
66 | if (len >= sizeof (addr.sun_path)) |
67 | { |
68 | errno = ENAMETOOLONG; |
69 | return -1; |
70 | } |
71 | |
72 | memset (s: &addr, c: 0, offsetof (sockaddr_un, sun_path)); |
73 | addr.sun_family = AF_UNIX; |
74 | memcpy (dest: addr.sun_path, src: name, n: len + 1); |
75 | |
76 | return ListenSocket (e, addr: (sockaddr *)&addr, len: sizeof (addr), backlog); |
77 | } |
78 | |
79 | int ListenInet6 (char const **e, char const *name, int port, unsigned backlog) |
80 | { |
81 | addrinfo *addrs = nullptr; |
82 | int fd = -1; |
83 | char const *errstr = nullptr; |
84 | |
85 | fd = socket (AF_INET6, SOCK_STREAM, protocol: 0); |
86 | if (fd < 0) |
87 | { |
88 | errstr = "creating socket" ; |
89 | |
90 | fail:; |
91 | int err = errno; |
92 | if (e) |
93 | *e = errstr; |
94 | if (fd >= 0) |
95 | close (fd: fd); |
96 | if (addrs) |
97 | freeaddrinfo (ai: addrs); |
98 | errno = err; |
99 | return -1; |
100 | } |
101 | |
102 | addrinfo hints; |
103 | hints.ai_flags = AI_NUMERICSERV; |
104 | hints.ai_family = AF_INET6; |
105 | hints.ai_socktype = SOCK_STREAM; |
106 | hints.ai_protocol = 0; |
107 | hints.ai_addrlen = 0; |
108 | hints.ai_addr = nullptr; |
109 | hints.ai_canonname = nullptr; |
110 | hints.ai_next = nullptr; |
111 | |
112 | /* getaddrinfo requires a port number, but is quite happy to accept |
113 | invalid ones. So don't rely on it. */ |
114 | if (int err = getaddrinfo (name: name, service: "0" , req: &hints, pai: &addrs)) |
115 | { |
116 | errstr = gai_strerror (ecode: err); |
117 | // What's the best errno to set? |
118 | errno = 0; |
119 | goto fail; |
120 | } |
121 | |
122 | sockaddr_in6 addr; |
123 | memset (s: &addr, c: 0, n: sizeof (addr)); |
124 | addr.sin6_family = AF_INET6; |
125 | |
126 | for (struct addrinfo *next = addrs; next; next = next->ai_next) |
127 | if (next->ai_family == AF_INET6 |
128 | && next->ai_socktype == SOCK_STREAM) |
129 | { |
130 | sockaddr_in6 *in6 = (sockaddr_in6 *)next->ai_addr; |
131 | in6->sin6_port = htons (port); |
132 | if (ntohs (in6->sin6_port) != port) |
133 | errno = EINVAL; |
134 | else if (!bind (fd: fd, addr: next->ai_addr, len: next->ai_addrlen)) |
135 | goto listen; |
136 | } |
137 | |
138 | errstr = "binding socket" ; |
139 | goto fail; |
140 | |
141 | listen:; |
142 | freeaddrinfo (ai: addrs); |
143 | addrs = nullptr; |
144 | |
145 | if (listen (fd: fd, n: backlog ? backlog : 17) < 0) |
146 | { |
147 | errstr = "listening socket" ; |
148 | goto fail; |
149 | } |
150 | |
151 | return fd; |
152 | } |
153 | |
154 | } |
155 | #endif |
156 | |