Directory: | src/ |
---|---|
File: | src/net.c |
Date: | 2024-04-25 03:45:42 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 87 | 139 | 62.6% |
Branches: | 30 | 92 | 32.6% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Copyright (c) 2022 Egor Tensin <egor@tensin.name> | ||
3 | * This file is part of the "cimple" project. | ||
4 | * For details, see https://github.com/egor-tensin/cimple. | ||
5 | * Distributed under the MIT License. | ||
6 | */ | ||
7 | |||
8 | #include "net.h" | ||
9 | #include "buf.h" | ||
10 | #include "file.h" | ||
11 | #include "log.h" | ||
12 | |||
13 | #include <netdb.h> | ||
14 | #include <stdint.h> | ||
15 | #include <stdlib.h> | ||
16 | #include <string.h> | ||
17 | #include <sys/socket.h> | ||
18 | #include <sys/types.h> | ||
19 | #include <unistd.h> | ||
20 | |||
21 | #define gai_log_errno(ec) log_err("getaddrinfo: %s\n", gai_strerror(ec)) | ||
22 | |||
23 | 29 | int net_bind(const char *port) | |
24 | { | ||
25 | static const int flags = SOCK_CLOEXEC; | ||
26 | 29 | struct addrinfo *result = NULL, *it = NULL; | |
27 | struct addrinfo hints; | ||
28 | 29 | int socket_fd = -1, ret = 0; | |
29 | |||
30 | 29 | memset(&hints, 0, sizeof(hints)); | |
31 | 29 | hints.ai_family = AF_INET6; | |
32 | 29 | hints.ai_socktype = SOCK_STREAM; | |
33 | 29 | hints.ai_flags = AI_PASSIVE; | |
34 | |||
35 | 29 | ret = getaddrinfo(NULL, port, &hints, &result); | |
36 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (ret) { |
37 | ✗ | gai_log_errno(ret); | |
38 | ✗ | return -1; | |
39 | } | ||
40 | |||
41 |
1/2✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
|
29 | for (it = result; it; it = it->ai_next) { |
42 | 29 | socket_fd = socket(it->ai_family, it->ai_socktype | flags, it->ai_protocol); | |
43 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (socket_fd < 0) { |
44 | ✗ | log_errno("socket"); | |
45 | ✗ | continue; | |
46 | } | ||
47 | |||
48 | static const int yes = 1; | ||
49 | static const int no = 0; | ||
50 | |||
51 |
1/2✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
|
29 | if (it->ai_family == AF_INET6) { |
52 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 29 times.
|
29 | if (setsockopt(socket_fd, IPPROTO_IPV6, IPV6_V6ONLY, &no, sizeof(no)) < 0) { |
53 | ✗ | log_errno("setsockopt"); | |
54 | ✗ | goto close_socket; | |
55 | } | ||
56 | } | ||
57 | |||
58 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 29 times.
|
29 | if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { |
59 | ✗ | log_errno("setsockopt"); | |
60 | ✗ | goto close_socket; | |
61 | } | ||
62 | |||
63 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 29 times.
|
29 | if (bind(socket_fd, it->ai_addr, it->ai_addrlen) < 0) { |
64 | ✗ | log_errno("bind"); | |
65 | ✗ | goto close_socket; | |
66 | } | ||
67 | |||
68 | 29 | break; | |
69 | |||
70 | ✗ | close_socket: | |
71 | ✗ | net_close(socket_fd); | |
72 | } | ||
73 | |||
74 | 29 | freeaddrinfo(result); | |
75 | |||
76 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (!it) { |
77 | ✗ | log_err("Couldn't bind to port %s\n", port); | |
78 | ✗ | return -1; | |
79 | } | ||
80 | |||
81 | 29 | ret = listen(socket_fd, 4096); | |
82 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (ret < 0) { |
83 | ✗ | log_errno("listen"); | |
84 | ✗ | goto fail; | |
85 | } | ||
86 | |||
87 | 29 | return socket_fd; | |
88 | |||
89 | ✗ | fail: | |
90 | ✗ | net_close(socket_fd); | |
91 | |||
92 | ✗ | return ret; | |
93 | } | ||
94 | |||
95 | 27620 | int net_accept(int fd) | |
96 | { | ||
97 | static const int flags = SOCK_CLOEXEC; | ||
98 | 27620 | int ret = 0; | |
99 | |||
100 | 27620 | ret = accept4(fd, NULL, NULL, flags); | |
101 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 27620 times.
|
27620 | if (ret < 0) { |
102 | ✗ | log_errno("accept4"); | |
103 | ✗ | return ret; | |
104 | } | ||
105 | |||
106 | 27620 | return ret; | |
107 | } | ||
108 | |||
109 | 27620 | int net_connect(const char *host, const char *port) | |
110 | { | ||
111 | static const int flags = SOCK_CLOEXEC; | ||
112 | 27620 | struct addrinfo *result = NULL, *it = NULL; | |
113 | struct addrinfo hints; | ||
114 | 27620 | int socket_fd = -1, ret = 0; | |
115 | |||
116 | 27620 | memset(&hints, 0, sizeof(hints)); | |
117 | 27620 | hints.ai_family = AF_UNSPEC; | |
118 | 27620 | hints.ai_socktype = SOCK_STREAM; | |
119 | |||
120 | 27620 | ret = getaddrinfo(host, port, &hints, &result); | |
121 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 27620 times.
|
27620 | if (ret) { |
122 | ✗ | gai_log_errno(ret); | |
123 | ✗ | return -1; | |
124 | } | ||
125 | |||
126 |
1/2✓ Branch 0 taken 27620 times.
✗ Branch 1 not taken.
|
27620 | for (it = result; it; it = it->ai_next) { |
127 | 27620 | socket_fd = socket(it->ai_family, it->ai_socktype | flags, it->ai_protocol); | |
128 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 27620 times.
|
27620 | if (socket_fd < 0) { |
129 | ✗ | log_errno("socket"); | |
130 | ✗ | continue; | |
131 | } | ||
132 | |||
133 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 27620 times.
|
27620 | if (connect(socket_fd, it->ai_addr, it->ai_addrlen) < 0) { |
134 | ✗ | log_errno("connect"); | |
135 | ✗ | goto close_socket; | |
136 | } | ||
137 | |||
138 | 27620 | break; | |
139 | |||
140 | ✗ | close_socket: | |
141 | ✗ | net_close(socket_fd); | |
142 | } | ||
143 | |||
144 | 27620 | freeaddrinfo(result); | |
145 | |||
146 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 27620 times.
|
27620 | if (!it) { |
147 | ✗ | log_err("Couldn't connect to host %s, port %s\n", host, port); | |
148 | ✗ | return -1; | |
149 | } | ||
150 | |||
151 | 27620 | return socket_fd; | |
152 | } | ||
153 | |||
154 | 64503 | void net_close(int fd) | |
155 | { | ||
156 | 64503 | file_close(fd); | |
157 | 64503 | } | |
158 | |||
159 | 92012 | static ssize_t net_send_part(int fd, const void *buf, size_t size) | |
160 | { | ||
161 | static const int flags = MSG_NOSIGNAL; | ||
162 | |||
163 | 92012 | ssize_t ret = send(fd, buf, size, flags); | |
164 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 92012 times.
|
92012 | if (ret < 0) { |
165 | ✗ | log_errno("send"); | |
166 | ✗ | return -1; | |
167 | } | ||
168 | |||
169 | 92012 | return ret; | |
170 | } | ||
171 | |||
172 | 92012 | int net_send(int fd, const void *buf, size_t size) | |
173 | { | ||
174 | 92012 | size_t sent_total = 0; | |
175 | |||
176 |
2/2✓ Branch 0 taken 92012 times.
✓ Branch 1 taken 92012 times.
|
184024 | while (sent_total < size) { |
177 | ssize_t sent_now = | ||
178 | 92012 | net_send_part(fd, (const char *)buf + sent_total, size - sent_total); | |
179 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 92012 times.
|
92012 | if (sent_now < 0) |
180 | ✗ | return -1; | |
181 | 92012 | sent_total += sent_now; | |
182 | } | ||
183 | |||
184 | 92012 | return 0; | |
185 | } | ||
186 | |||
187 | 92012 | int net_recv(int fd, void *buf, size_t size) | |
188 | { | ||
189 | 92012 | ssize_t read_total = 0; | |
190 | |||
191 |
2/2✓ Branch 0 taken 109165 times.
✓ Branch 1 taken 92012 times.
|
201177 | while ((size_t)read_total < size) { |
192 | 109165 | ssize_t read_now = read(fd, (unsigned char *)buf + read_total, size - read_total); | |
193 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 109165 times.
|
109165 | if (!read_now) |
194 | ✗ | break; | |
195 | |||
196 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 109165 times.
|
109165 | if (read_now < 0) { |
197 | ✗ | log_errno("read"); | |
198 | ✗ | return -1; | |
199 | } | ||
200 | |||
201 | 109165 | read_total += read_now; | |
202 | } | ||
203 | |||
204 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 92012 times.
|
92012 | if ((size_t)read_total < size) { |
205 | ✗ | log_err("Received only %zd bytes out of %zu\n", read_total, size); | |
206 | ✗ | return -1; | |
207 | } | ||
208 | |||
209 | 92012 | return 0; | |
210 | } | ||
211 | |||
212 | 46006 | int net_send_buf(int fd, const struct buf *buf) | |
213 | { | ||
214 | 46006 | int ret = 0; | |
215 | |||
216 | 46006 | uint32_t size = htonl(buf_get_size(buf)); | |
217 | 46006 | ret = net_send(fd, &size, sizeof(size)); | |
218 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 46006 times.
|
46006 | if (ret < 0) |
219 | ✗ | return ret; | |
220 | |||
221 | 46006 | ret = net_send(fd, buf_get_data(buf), buf_get_size(buf)); | |
222 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 46006 times.
|
46006 | if (ret < 0) |
223 | ✗ | return ret; | |
224 | |||
225 | 46006 | return ret; | |
226 | } | ||
227 | |||
228 | 46006 | int net_recv_buf(int fd, struct buf **buf) | |
229 | { | ||
230 | 46006 | uint32_t size = 0; | |
231 | 46006 | int ret = 0; | |
232 | |||
233 | 46006 | ret = net_recv(fd, &size, sizeof(size)); | |
234 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 46006 times.
|
46006 | if (ret < 0) { |
235 | ✗ | log_err("Couldn't read buffer size\n"); | |
236 | ✗ | goto fail; | |
237 | } | ||
238 | 46006 | size = ntohl(size); | |
239 | |||
240 | 46006 | void *data = malloc(size); | |
241 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 46006 times.
|
46006 | if (!data) { |
242 | ✗ | log_errno("malloc"); | |
243 | ✗ | goto fail; | |
244 | } | ||
245 | |||
246 | 46006 | ret = net_recv(fd, data, size); | |
247 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 46006 times.
|
46006 | if (ret < 0) { |
248 | ✗ | log_err("Couldn't read buffer\n"); | |
249 | ✗ | goto free_data; | |
250 | } | ||
251 | |||
252 | 46006 | ret = buf_create(buf, data, size); | |
253 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 46006 times.
|
46006 | if (ret < 0) |
254 | ✗ | goto free_data; | |
255 | |||
256 | 46006 | return ret; | |
257 | |||
258 | ✗ | free_data: | |
259 | ✗ | free(data); | |
260 | |||
261 | ✗ | fail: | |
262 | ✗ | return -1; | |
263 | } | ||
264 |