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 #endif
140 }
141 
142 #pragma GCC diagnostic push
143 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
144 
145 #if !GLIB_CHECK_VERSION(2, 58, 0)
146 typedef struct SlirpGSpawnFds {
147     GSpawnChildSetupFunc child_setup;
148     gpointer user_data;
149     gint stdin_fd;
150     gint stdout_fd;
151     gint stderr_fd;
152 } SlirpGSpawnFds;
153 
slirp_gspawn_fds_setup(gpointer user_data)154 static inline void slirp_gspawn_fds_setup(gpointer user_data)
155 {
156     SlirpGSpawnFds *q = (SlirpGSpawnFds *)user_data;
157 
158     dup2(q->stdin_fd, 0);
159     dup2(q->stdout_fd, 1);
160     dup2(q->stderr_fd, 2);
161     q->child_setup(q->user_data);
162 }
163 #endif
164 
165 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)166 g_spawn_async_with_fds_slirp(const gchar *working_directory, gchar **argv,
167                              gchar **envp, GSpawnFlags flags,
168                              GSpawnChildSetupFunc child_setup,
169                              gpointer user_data, GPid *child_pid, gint stdin_fd,
170                              gint stdout_fd, gint stderr_fd, GError **error)
171 {
172 #if GLIB_CHECK_VERSION(2, 58, 0)
173     return g_spawn_async_with_fds(working_directory, argv, envp, flags,
174                                   child_setup, user_data, child_pid, stdin_fd,
175                                   stdout_fd, stderr_fd, error);
176 #else
177     SlirpGSpawnFds setup = {
178         .child_setup = child_setup,
179         .user_data = user_data,
180         .stdin_fd = stdin_fd,
181         .stdout_fd = stdout_fd,
182         .stderr_fd = stderr_fd,
183     };
184 
185     return g_spawn_async(working_directory, argv, envp, flags,
186                          slirp_gspawn_fds_setup, &setup, child_pid, error);
187 #endif
188 }
189 
190 #define g_spawn_async_with_fds(wd, argv, env, f, c, d, p, ifd, ofd, efd, err) \
191     g_spawn_async_with_fds_slirp(wd, argv, env, f, c, d, p, ifd, ofd, efd, err)
192 
193 #pragma GCC diagnostic pop
194 
fork_exec(struct socket * so,const char * ex)195 int fork_exec(struct socket *so, const char *ex)
196 {
197     GError *err = NULL;
198     gint argc = 0;
199     gchar **argv = NULL;
200     int opt, sp[2];
201 
202     DEBUG_CALL("fork_exec");
203     DEBUG_ARG("so = %p", so);
204     DEBUG_ARG("ex = %p", ex);
205 
206     if (slirp_socketpair_with_oob(sp) < 0) {
207         return 0;
208     }
209 
210     if (!g_shell_parse_argv(ex, &argc, &argv, &err)) {
211         g_critical("fork_exec invalid command: %s\nerror: %s", ex, err->message);
212         g_error_free(err);
213         return 0;
214     }
215 
216     g_spawn_async_with_fds(NULL /* cwd */, argv, NULL /* env */,
217                            G_SPAWN_SEARCH_PATH, fork_exec_child_setup,
218                            NULL /* data */, NULL /* child_pid */, sp[1], sp[1],
219                            sp[1], &err);
220     g_strfreev(argv);
221 
222     if (err) {
223         g_critical("fork_exec: %s", err->message);
224         g_error_free(err);
225         closesocket(sp[0]);
226         closesocket(sp[1]);
227         return 0;
228     }
229 
230     so->s = sp[0];
231     closesocket(sp[1]);
232     slirp_socket_set_fast_reuse(so->s);
233     opt = 1;
234     setsockopt(so->s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int));
235     slirp_set_nonblock(so->s);
236     so->slirp->cb->register_poll_fd(so->s, so->slirp->opaque);
237     return 1;
238 }
239 
open_unix(struct socket * so,const char * unixpath)240 int open_unix(struct socket *so, const char *unixpath)
241 {
242 #ifdef G_OS_UNIX
243     struct sockaddr_un sa;
244     int s;
245 
246     DEBUG_CALL("open_unix");
247     DEBUG_ARG("so = %p", so);
248     DEBUG_ARG("unixpath = %s", unixpath);
249 
250     memset(&sa, 0, sizeof(sa));
251     sa.sun_family = AF_UNIX;
252     if (g_strlcpy(sa.sun_path, unixpath, sizeof(sa.sun_path)) >= sizeof(sa.sun_path)) {
253         g_critical("Bad unix path: %s", unixpath);
254         return 0;
255     }
256 
257     s = slirp_socket(PF_UNIX, SOCK_STREAM, 0);
258     if (s < 0) {
259         g_critical("open_unix(): %s", strerror(errno));
260         return 0;
261     }
262 
263     if (connect(s, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
264         g_critical("open_unix(): %s", strerror(errno));
265         closesocket(s);
266         return 0;
267     }
268 
269     so->s = s;
270     slirp_set_nonblock(so->s);
271     so->slirp->cb->register_poll_fd(so->s, so->slirp->opaque);
272 
273     return 1;
274 #else
275     g_assert_not_reached();
276 #endif
277 }
278 
slirp_connection_info(Slirp * slirp)279 char *slirp_connection_info(Slirp *slirp)
280 {
281     GString *str = g_string_new(NULL);
282     const char *const tcpstates[] = {
283         [TCPS_CLOSED] = "CLOSED",           [TCPS_LISTEN] = "LISTEN",
284         [TCPS_SYN_SENT] = "SYN_SENT",       [TCPS_SYN_RECEIVED] = "SYN_RCVD",
285         [TCPS_ESTABLISHED] = "ESTABLISHED", [TCPS_CLOSE_WAIT] = "CLOSE_WAIT",
286         [TCPS_FIN_WAIT_1] = "FIN_WAIT_1",   [TCPS_CLOSING] = "CLOSING",
287         [TCPS_LAST_ACK] = "LAST_ACK",       [TCPS_FIN_WAIT_2] = "FIN_WAIT_2",
288         [TCPS_TIME_WAIT] = "TIME_WAIT",
289     };
290     struct in_addr dst_addr;
291     struct sockaddr_in src;
292     socklen_t src_len;
293     uint16_t dst_port;
294     struct socket *so;
295     const char *state;
296     char buf[20];
297 
298     g_string_append_printf(str,
299                            "  Protocol[State]    FD  Source Address  Port   "
300                            "Dest. Address  Port RecvQ SendQ\n");
301 
302     /* TODO: IPv6 */
303 
304     for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so->so_next) {
305         if (so->so_state & SS_HOSTFWD) {
306             state = "HOST_FORWARD";
307         } else if (so->so_tcpcb) {
308             state = tcpstates[so->so_tcpcb->t_state];
309         } else {
310             state = "NONE";
311         }
312         if (so->so_state & (SS_HOSTFWD | SS_INCOMING)) {
313             src_len = sizeof(src);
314             getsockname(so->s, (struct sockaddr *)&src, &src_len);
315             dst_addr = so->so_laddr;
316             dst_port = so->so_lport;
317         } else {
318             src.sin_addr = so->so_laddr;
319             src.sin_port = so->so_lport;
320             dst_addr = so->so_faddr;
321             dst_port = so->so_fport;
322         }
323         slirp_fmt0(buf, sizeof(buf), "  TCP[%s]", state);
324         g_string_append_printf(str, "%-19s %3d %15s %5d ", buf, so->s,
325                                src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) :
326                                                      "*",
327                                ntohs(src.sin_port));
328         g_string_append_printf(str, "%15s %5d %5d %5d\n", inet_ntoa(dst_addr),
329                                ntohs(dst_port), so->so_rcv.sb_cc,
330                                so->so_snd.sb_cc);
331     }
332 
333     for (so = slirp->udb.so_next; so != &slirp->udb; so = so->so_next) {
334         if (so->so_state & SS_HOSTFWD) {
335             slirp_fmt0(buf, sizeof(buf), "  UDP[HOST_FORWARD]");
336             src_len = sizeof(src);
337             getsockname(so->s, (struct sockaddr *)&src, &src_len);
338             dst_addr = so->so_laddr;
339             dst_port = so->so_lport;
340         } else {
341             slirp_fmt0(buf, sizeof(buf), "  UDP[%d sec]",
342                        (so->so_expire - curtime) / 1000);
343             src.sin_addr = so->so_laddr;
344             src.sin_port = so->so_lport;
345             dst_addr = so->so_faddr;
346             dst_port = so->so_fport;
347         }
348         g_string_append_printf(str, "%-19s %3d %15s %5d ", buf, so->s,
349                                src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) :
350                                                      "*",
351                                ntohs(src.sin_port));
352         g_string_append_printf(str, "%15s %5d %5d %5d\n", inet_ntoa(dst_addr),
353                                ntohs(dst_port), so->so_rcv.sb_cc,
354                                so->so_snd.sb_cc);
355     }
356 
357     for (so = slirp->icmp.so_next; so != &slirp->icmp; so = so->so_next) {
358         slirp_fmt0(buf, sizeof(buf), "  ICMP[%d sec]",
359                    (so->so_expire - curtime) / 1000);
360         src.sin_addr = so->so_laddr;
361         dst_addr = so->so_faddr;
362         g_string_append_printf(str, "%-19s %3d %15s  -    ", buf, so->s,
363                                src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) :
364                                                      "*");
365         g_string_append_printf(str, "%15s  -    %5d %5d\n", inet_ntoa(dst_addr),
366                                so->so_rcv.sb_cc, so->so_snd.sb_cc);
367     }
368 
369     return g_string_free(str, FALSE);
370 }
371 
slirp_bind_outbound(struct socket * so,unsigned short af)372 int slirp_bind_outbound(struct socket *so, unsigned short af)
373 {
374     int ret = 0;
375     struct sockaddr *addr = NULL;
376     int addr_size = 0;
377 
378     if (af == AF_INET && so->slirp->outbound_addr != NULL) {
379         addr = (struct sockaddr *)so->slirp->outbound_addr;
380         addr_size = sizeof(struct sockaddr_in);
381     } else if (af == AF_INET6 && so->slirp->outbound_addr6 != NULL) {
382         addr = (struct sockaddr *)so->slirp->outbound_addr6;
383         addr_size = sizeof(struct sockaddr_in6);
384     }
385 
386     if (addr != NULL) {
387         ret = bind(so->s, addr, addr_size);
388     }
389     return ret;
390 }