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 | |
8 | #if CODY_NETWORKING |
9 | // C |
10 | #include <cerrno> |
11 | #include <cstring> |
12 | // OS |
13 | #include <netdb.h> |
14 | #include <unistd.h> |
15 | #include <arpa/inet.h> |
16 | #include <netinet/in.h> |
17 | #include <sys/un.h> |
18 | |
19 | #ifndef AI_NUMERICSERV |
20 | #define AI_NUMERICSERV 0 |
21 | #endif |
22 | |
23 | // Client-side networking helpers |
24 | |
25 | namespace Cody { |
26 | |
27 | int OpenSocket (char const **e, sockaddr const *addr, socklen_t len) |
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 (connect (fd: fd, addr: addr, len: len) < 0) |
47 | { |
48 | errstr = "connecting socket" ; |
49 | goto fail; |
50 | } |
51 | |
52 | return fd; |
53 | } |
54 | |
55 | int OpenLocal (char const **e, char const *name) |
56 | { |
57 | sockaddr_un addr; |
58 | size_t len = strlen (s: name); |
59 | |
60 | if (len >= sizeof (addr.sun_path)) |
61 | { |
62 | errno = ENAMETOOLONG; |
63 | return -1; |
64 | } |
65 | |
66 | memset (s: &addr, c: 0, offsetof (sockaddr_un, sun_path)); |
67 | addr.sun_family = AF_UNIX; |
68 | memcpy (dest: addr.sun_path, src: name, n: len + 1); |
69 | return OpenSocket (e, addr: (sockaddr *)&addr, len: sizeof (addr)); |
70 | } |
71 | |
72 | int OpenInet6 (char const **e, char const *name, int port) |
73 | { |
74 | addrinfo *addrs = nullptr; |
75 | int fd = -1; |
76 | char const *errstr = nullptr; |
77 | |
78 | fd = socket (AF_INET6, SOCK_STREAM, protocol: 0); |
79 | if (fd < 0) |
80 | { |
81 | errstr = "socket" ; |
82 | |
83 | fail:; |
84 | int err = errno; |
85 | if (e) |
86 | *e = errstr; |
87 | if (fd >= 0) |
88 | close (fd: fd); |
89 | if (addrs) |
90 | freeaddrinfo (ai: addrs); |
91 | errno = err; |
92 | return -1; |
93 | } |
94 | |
95 | addrinfo hints; |
96 | hints.ai_flags = 0; |
97 | hints.ai_family = AF_INET6; |
98 | hints.ai_socktype = SOCK_STREAM; |
99 | hints.ai_protocol = 0; |
100 | hints.ai_addrlen = 0; |
101 | hints.ai_addr = nullptr; |
102 | hints.ai_canonname = nullptr; |
103 | hints.ai_next = nullptr; |
104 | |
105 | if (int err = getaddrinfo (name: name, service: nullptr, req: &hints, pai: &addrs)) |
106 | { |
107 | errstr = gai_strerror (ecode: err); |
108 | // What's the best errno to set? |
109 | errno = 0; |
110 | goto fail; |
111 | } |
112 | |
113 | sockaddr_in6 addr; |
114 | memset (s: &addr, c: 0, n: sizeof (addr)); |
115 | addr.sin6_family = AF_INET6; |
116 | |
117 | for (struct addrinfo *next = addrs; next; next = next->ai_next) |
118 | if (next->ai_family == AF_INET6 |
119 | && next->ai_socktype == SOCK_STREAM) |
120 | { |
121 | sockaddr_in6 *in6 = (sockaddr_in6 *)next->ai_addr; |
122 | in6->sin6_port = htons (port); |
123 | if (ntohs (in6->sin6_port) != port) |
124 | errno = EINVAL; |
125 | else if (!connect (fd: fd, addr: next->ai_addr, len: next->ai_addrlen)) |
126 | goto done; |
127 | } |
128 | errstr = "connecting" ; |
129 | goto fail; |
130 | |
131 | done:; |
132 | freeaddrinfo (ai: addrs); |
133 | |
134 | return fd; |
135 | } |
136 | |
137 | } |
138 | |
139 | #endif |
140 | |