1 /* SPDX-License-Identifier: BSD-3-Clause */
2 /*
3 * Copyright (c) 1995 Danny Gasparovski.
4 */
5
6 #include "slirp.h"
7 #ifdef G_OS_UNIX
8 #include <sys/un.h>
9 #endif
10
insque(void * a,void * b)11 inline void insque(void *a, void *b)
12 {
13 register struct quehead *element = (struct quehead *)a;
14 register struct quehead *head = (struct quehead *)b;
15 element->qh_link = head->qh_link;
16 head->qh_link = (struct quehead *)element;
17 element->qh_rlink = (struct quehead *)head;
18 ((struct quehead *)(element->qh_link))->qh_rlink =
19 (struct quehead *)element;
20 }
21
remque(void * a)22 inline void remque(void *a)
23 {
24 register struct quehead *element = (struct quehead *)a;
25 ((struct quehead *)(element->qh_link))->qh_rlink = element->qh_rlink;
26 ((struct quehead *)(element->qh_rlink))->qh_link = element->qh_link;
27 element->qh_rlink = NULL;
28 }
29
30 /* TODO: IPv6 */
add_guestfwd(struct gfwd_list ** ex_ptr,SlirpWriteCb write_cb,void * opaque,struct in_addr addr,int port)31 struct gfwd_list *add_guestfwd(struct gfwd_list **ex_ptr, SlirpWriteCb write_cb,
32 void *opaque, struct in_addr addr, int port)
33 {
34 struct gfwd_list *f = g_new0(struct gfwd_list, 1);
35
36 f->write_cb = write_cb;
37 f->opaque = opaque;
38 f->ex_fport = port;
39 f->ex_addr = addr;
40 f->ex_next = *ex_ptr;
41 *ex_ptr = f;
42
43 return f;
44 }
45
add_exec(struct gfwd_list ** ex_ptr,const char * cmdline,struct in_addr addr,int port)46 struct gfwd_list *add_exec(struct gfwd_list **ex_ptr, const char *cmdline,
47 struct in_addr addr, int port)
48 {
49 struct gfwd_list *f = add_guestfwd(ex_ptr, NULL, NULL, addr, port);
50
51 f->ex_exec = g_strdup(cmdline);
52
53 return f;
54 }
55
add_unix(struct gfwd_list ** ex_ptr,const char * unixsock,struct in_addr addr,int port)56 struct gfwd_list *add_unix(struct gfwd_list **ex_ptr, const char *unixsock,
57 struct in_addr addr, int port)
58 {
59 struct gfwd_list *f = add_guestfwd(ex_ptr, NULL, NULL, addr, port);
60
61 f->ex_unix = g_strdup(unixsock);
62
63 return f;
64 }
65
remove_guestfwd(struct gfwd_list ** ex_ptr,struct in_addr addr,int port)66 int remove_guestfwd(struct gfwd_list **ex_ptr, struct in_addr addr, int port)
67 {
68 for (; *ex_ptr != NULL; ex_ptr = &((*ex_ptr)->ex_next)) {
69 struct gfwd_list *f = *ex_ptr;
70 if (f->ex_addr.s_addr == addr.s_addr && f->ex_fport == port) {
71 *ex_ptr = f->ex_next;
72 g_free(f->ex_exec);
73 g_free(f);
74 return 0;
75 }
76 }
77 return -1;
78 }
79
slirp_socketpair_with_oob(int sv[2])80 static int slirp_socketpair_with_oob(int sv[2])
81 {
82 struct sockaddr_in addr = {
83 .sin_family = AF_INET,
84 .sin_port = 0,
85 .sin_addr.s_addr = INADDR_ANY,
86 };
87 socklen_t addrlen = sizeof(addr);
88 int ret, s;
89
90 sv[1] = -1;
91 s = slirp_socket(AF_INET, SOCK_STREAM, 0);
92 if (s < 0 || bind(s, (struct sockaddr *)&addr, addrlen) < 0 ||
93 listen(s, 1) < 0 ||
94 getsockname(s, (struct sockaddr *)&addr, &addrlen) < 0) {
95 goto err;
96 }
97
98 sv[1] = slirp_socket(AF_INET, SOCK_STREAM, 0);
99 if (sv[1] < 0) {
100 goto err;
101 }
102 /*
103 * This connect won't block because we've already listen()ed on
104 * the server end (even though we won't accept() the connection
105 * until later on).
106 */
107 do {
108 ret = connect(sv[1], (struct sockaddr *)&addr, addrlen);
109 } while (ret < 0 && errno == EINTR);
110 if (ret < 0) {
111 goto err;
112 }
113
114 do {
115 sv[0] = accept(s, (struct sockaddr *)&addr, &addrlen);
116 } while (sv[0] < 0 && errno == EINTR);
117 if (sv[0] < 0) {
118 goto err;
119 }
120
121 closesocket(s);
122 return 0;
123
124 err:
125 g_critical("slirp_socketpair(): %s", strerror(errno));
126 if (s >= 0) {
127 closesocket(s);
128 }
129 if (sv[1] >= 0) {
130 closesocket(sv[1]);
131 }
132 return -1;
133 }
134
fork_exec_child_setup(gpointer data)135 static void fork_exec_child_setup(gpointer data)
136 {
137 #ifndef _WIN32
138 setsid();
139
140 /* Unblock all signals and leave our exec()-ee to block what it wants */
141 sigset_t ss;
142 sigemptyset(&ss);
143 sigprocmask(SIG_SETMASK, &ss, NULL);
144
145 /* POSIX is obnoxious about SIGCHLD specifically across exec() */
146 signal(SIGCHLD, SIG_DFL);
147 #endif
148 }
149
150 #pragma GCC diagnostic push
151 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
152
153 #if !GLIB_CHECK_VERSION(2, 58, 0)
154 typedef struct SlirpGSpawnFds {
155 GSpawnChildSetupFunc child_setup;
156 gpointer user_data;
157 gint stdin_fd;
158 gint stdout_fd;
159 gint stderr_fd;
160 } SlirpGSpawnFds;
161
slirp_gspawn_fds_setup(gpointer user_data)162 static inline void slirp_gspawn_fds_setup(gpointer user_data)
163 {
164 SlirpGSpawnFds *q = (SlirpGSpawnFds *)user_data;
165
166 dup2(q->stdin_fd, 0);
167 dup2(q->stdout_fd, 1);
168 dup2(q->stderr_fd, 2);
169 q->child_setup(q->user_data);
170 }
171 #endif
172
173 static inline gboolean
g_spawn_async_with_fds_slirp(const gchar * working_directory,gchar ** argv,gchar ** envp,GSpawnFlags flags,GSpawnChildSetupFunc child_setup,gpointer user_data,GPid * child_pid,gint stdin_fd,gint stdout_fd,gint stderr_fd,GError ** error)174 g_spawn_async_with_fds_slirp(const gchar *working_directory, gchar **argv,
175 gchar **envp, GSpawnFlags flags,
176 GSpawnChildSetupFunc child_setup,
177 gpointer user_data, GPid *child_pid, gint stdin_fd,
178 gint stdout_fd, gint stderr_fd, GError **error)
179 {
180 #if GLIB_CHECK_VERSION(2, 58, 0)
181 return g_spawn_async_with_fds(working_directory, argv, envp, flags,
182 child_setup, user_data, child_pid, stdin_fd,
183 stdout_fd, stderr_fd, error);
184 #else
185 SlirpGSpawnFds setup = {
186 .child_setup = child_setup,
187 .user_data = user_data,
188 .stdin_fd = stdin_fd,
189 .stdout_fd = stdout_fd,
190 .stderr_fd = stderr_fd,
191 };
192
193 return g_spawn_async(working_directory, argv, envp, flags,
194 slirp_gspawn_fds_setup, &setup, child_pid, error);
195 #endif
196 }
197
198 #define g_spawn_async_with_fds(wd, argv, env, f, c, d, p, ifd, ofd, efd, err) \
199 g_spawn_async_with_fds_slirp(wd, argv, env, f, c, d, p, ifd, ofd, efd, err)
200
201 #pragma GCC diagnostic pop
202
fork_exec(struct socket * so,const char * ex)203 int fork_exec(struct socket *so, const char *ex)
204 {
205 GError *err = NULL;
206 gint argc = 0;
207 gchar **argv = NULL;
208 int opt, sp[2];
209
210 DEBUG_CALL("fork_exec");
211 DEBUG_ARG("so = %p", so);
212 DEBUG_ARG("ex = %p", ex);
213
214 if (slirp_socketpair_with_oob(sp) < 0) {
215 return 0;
216 }
217
218 if (!g_shell_parse_argv(ex, &argc, &argv, &err)) {
219 g_critical("fork_exec invalid command: %s\nerror: %s", ex, err->message);
220 g_error_free(err);
221 return 0;
222 }
223
224 g_spawn_async_with_fds(NULL /* cwd */, argv, NULL /* env */,
225 G_SPAWN_SEARCH_PATH, fork_exec_child_setup,
226 NULL /* data */, NULL /* child_pid */, sp[1], sp[1],
227 sp[1], &err);
228 g_strfreev(argv);
229
230 if (err) {
231 g_critical("fork_exec: %s", err->message);
232 g_error_free(err);
233 closesocket(sp[0]);
234 closesocket(sp[1]);
235 return 0;
236 }
237
238 so->s = sp[0];
239 closesocket(sp[1]);
240 slirp_socket_set_fast_reuse(so->s);
241 opt = 1;
242 setsockopt(so->s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int));
243 slirp_set_nonblock(so->s);
244 so->slirp->cb->register_poll_fd(so->s, so->slirp->opaque);
245 return 1;
246 }
247
open_unix(struct socket * so,const char * unixpath)248 int open_unix(struct socket *so, const char *unixpath)
249 {
250 #ifdef G_OS_UNIX
251 struct sockaddr_un sa;
252 int s;
253
254 DEBUG_CALL("open_unix");
255 DEBUG_ARG("so = %p", so);
256 DEBUG_ARG("unixpath = %s", unixpath);
257
258 memset(&sa, 0, sizeof(sa));
259 sa.sun_family = AF_UNIX;
260 if (g_strlcpy(sa.sun_path, unixpath, sizeof(sa.sun_path)) >= sizeof(sa.sun_path)) {
261 g_critical("Bad unix path: %s", unixpath);
262 return 0;
263 }
264
265 s = slirp_socket(PF_UNIX, SOCK_STREAM, 0);
266 if (s < 0) {
267 g_critical("open_unix(): %s", strerror(errno));
268 return 0;
269 }
270
271 if (connect(s, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
272 g_critical("open_unix(): %s", strerror(errno));
273 closesocket(s);
274 return 0;
275 }
276
277 so->s = s;
278 slirp_set_nonblock(so->s);
279 so->slirp->cb->register_poll_fd(so->s, so->slirp->opaque);
280
281 return 1;
282 #else
283 g_assert_not_reached();
284 #endif
285 }
286
slirp_connection_info(Slirp * slirp)287 char *slirp_connection_info(Slirp *slirp)
288 {
289 GString *str = g_string_new(NULL);
290 const char *const tcpstates[] = {
291 [TCPS_CLOSED] = "CLOSED", [TCPS_LISTEN] = "LISTEN",
292 [TCPS_SYN_SENT] = "SYN_SENT", [TCPS_SYN_RECEIVED] = "SYN_RCVD",
293 [TCPS_ESTABLISHED] = "ESTABLISHED", [TCPS_CLOSE_WAIT] = "CLOSE_WAIT",
294 [TCPS_FIN_WAIT_1] = "FIN_WAIT_1", [TCPS_CLOSING] = "CLOSING",
295 [TCPS_LAST_ACK] = "LAST_ACK", [TCPS_FIN_WAIT_2] = "FIN_WAIT_2",
296 [TCPS_TIME_WAIT] = "TIME_WAIT",
297 };
298 struct in_addr dst_addr;
299 struct sockaddr_in src;
300 socklen_t src_len;
301 uint16_t dst_port;
302 struct socket *so;
303 const char *state;
304 char buf[20];
305
306 g_string_append_printf(str,
307 " Protocol[State] FD Source Address Port "
308 "Dest. Address Port RecvQ SendQ\n");
309
310 /* TODO: IPv6 */
311
312 for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so->so_next) {
313 if (so->so_state & SS_HOSTFWD) {
314 state = "HOST_FORWARD";
315 } else if (so->so_tcpcb) {
316 state = tcpstates[so->so_tcpcb->t_state];
317 } else {
318 state = "NONE";
319 }
320 if (so->so_state & (SS_HOSTFWD | SS_INCOMING)) {
321 src_len = sizeof(src);
322 getsockname(so->s, (struct sockaddr *)&src, &src_len);
323 dst_addr = so->so_laddr;
324 dst_port = so->so_lport;
325 } else {
326 src.sin_addr = so->so_laddr;
327 src.sin_port = so->so_lport;
328 dst_addr = so->so_faddr;
329 dst_port = so->so_fport;
330 }
331 slirp_fmt0(buf, sizeof(buf), " TCP[%s]", state);
332 g_string_append_printf(str, "%-19s %3d %15s %5d ", buf, so->s,
333 src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) :
334 "*",
335 ntohs(src.sin_port));
336 g_string_append_printf(str, "%15s %5d %5d %5d\n", inet_ntoa(dst_addr),
337 ntohs(dst_port), so->so_rcv.sb_cc,
338 so->so_snd.sb_cc);
339 }
340
341 for (so = slirp->udb.so_next; so != &slirp->udb; so = so->so_next) {
342 if (so->so_state & SS_HOSTFWD) {
343 slirp_fmt0(buf, sizeof(buf), " UDP[HOST_FORWARD]");
344 src_len = sizeof(src);
345 getsockname(so->s, (struct sockaddr *)&src, &src_len);
346 dst_addr = so->so_laddr;
347 dst_port = so->so_lport;
348 } else {
349 slirp_fmt0(buf, sizeof(buf), " UDP[%d sec]",
350 (so->so_expire - curtime) / 1000);
351 src.sin_addr = so->so_laddr;
352 src.sin_port = so->so_lport;
353 dst_addr = so->so_faddr;
354 dst_port = so->so_fport;
355 }
356 g_string_append_printf(str, "%-19s %3d %15s %5d ", buf, so->s,
357 src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) :
358 "*",
359 ntohs(src.sin_port));
360 g_string_append_printf(str, "%15s %5d %5d %5d\n", inet_ntoa(dst_addr),
361 ntohs(dst_port), so->so_rcv.sb_cc,
362 so->so_snd.sb_cc);
363 }
364
365 for (so = slirp->icmp.so_next; so != &slirp->icmp; so = so->so_next) {
366 slirp_fmt0(buf, sizeof(buf), " ICMP[%d sec]",
367 (so->so_expire - curtime) / 1000);
368 src.sin_addr = so->so_laddr;
369 dst_addr = so->so_faddr;
370 g_string_append_printf(str, "%-19s %3d %15s - ", buf, so->s,
371 src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) :
372 "*");
373 g_string_append_printf(str, "%15s - %5d %5d\n", inet_ntoa(dst_addr),
374 so->so_rcv.sb_cc, so->so_snd.sb_cc);
375 }
376
377 return g_string_free(str, FALSE);
378 }
379
slirp_neighbor_info(Slirp * slirp)380 char *slirp_neighbor_info(Slirp *slirp)
381 {
382 GString *str = g_string_new(NULL);
383 ArpTable *arp_table = &slirp->arp_table;
384 NdpTable *ndp_table = &slirp->ndp_table;
385 char ip_addr[INET6_ADDRSTRLEN];
386 char eth_addr[ETH_ADDRSTRLEN];
387 const char *ip;
388
389 g_string_append_printf(str, " %5s %-17s %s\n",
390 "Table", "MacAddr", "IP Address");
391
392 for (int i = 0; i < ARP_TABLE_SIZE; ++i) {
393 struct in_addr addr;
394 addr.s_addr = arp_table->table[i].ar_sip;
395 if (!addr.s_addr) {
396 continue;
397 }
398 ip = inet_ntop(AF_INET, &addr, ip_addr, sizeof(ip_addr));
399 g_assert(ip != NULL);
400 g_string_append_printf(str, " %5s %-17s %s\n", "ARP",
401 slirp_ether_ntoa(arp_table->table[i].ar_sha,
402 eth_addr, sizeof(eth_addr)),
403 ip);
404 }
405
406 for (int i = 0; i < NDP_TABLE_SIZE; ++i) {
407 if (in6_zero(&ndp_table->table[i].ip_addr)) {
408 continue;
409 }
410 ip = inet_ntop(AF_INET6, &ndp_table->table[i].ip_addr, ip_addr,
411 sizeof(ip_addr));
412 g_assert(ip != NULL);
413 g_string_append_printf(str, " %5s %-17s %s\n", "NDP",
414 slirp_ether_ntoa(ndp_table->table[i].eth_addr,
415 eth_addr, sizeof(eth_addr)),
416 ip);
417 }
418
419 return g_string_free(str, FALSE);
420 }
421
slirp_bind_outbound(struct socket * so,unsigned short af)422 int slirp_bind_outbound(struct socket *so, unsigned short af)
423 {
424 int ret = 0;
425 struct sockaddr *addr = NULL;
426 int addr_size = 0;
427
428 if (af == AF_INET && so->slirp->outbound_addr != NULL) {
429 addr = (struct sockaddr *)so->slirp->outbound_addr;
430 addr_size = sizeof(struct sockaddr_in);
431 } else if (af == AF_INET6 && so->slirp->outbound_addr6 != NULL) {
432 addr = (struct sockaddr *)so->slirp->outbound_addr6;
433 addr_size = sizeof(struct sockaddr_in6);
434 }
435
436 if (addr != NULL) {
437 ret = bind(so->s, addr, addr_size);
438 }
439 return ret;
440 }
441