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