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 }