1 | /* SPDX-License-Identifier: BSD-3-Clause */ |
2 | /* |
3 | * Copyright (c) 1995 Danny Gasparovski. |
4 | */ |
5 | |
6 | #include "slirp.h" |
7 | |
8 | inline void insque(void *a, void *b) |
9 | { |
10 | register struct quehead *element = (struct quehead *)a; |
11 | register struct quehead *head = (struct quehead *)b; |
12 | element->qh_link = head->qh_link; |
13 | head->qh_link = (struct quehead *)element; |
14 | element->qh_rlink = (struct quehead *)head; |
15 | ((struct quehead *)(element->qh_link))->qh_rlink = |
16 | (struct quehead *)element; |
17 | } |
18 | |
19 | inline void remque(void *a) |
20 | { |
21 | register struct quehead *element = (struct quehead *)a; |
22 | ((struct quehead *)(element->qh_link))->qh_rlink = element->qh_rlink; |
23 | ((struct quehead *)(element->qh_rlink))->qh_link = element->qh_link; |
24 | element->qh_rlink = NULL; |
25 | } |
26 | |
27 | /* TODO: IPv6 */ |
28 | struct gfwd_list *add_guestfwd(struct gfwd_list **ex_ptr, SlirpWriteCb write_cb, |
29 | void *opaque, struct in_addr addr, int port) |
30 | { |
31 | struct gfwd_list *f = g_new0(struct gfwd_list, 1); |
32 | |
33 | f->write_cb = write_cb; |
34 | f->opaque = opaque; |
35 | f->ex_fport = port; |
36 | f->ex_addr = addr; |
37 | f->ex_next = *ex_ptr; |
38 | *ex_ptr = f; |
39 | |
40 | return f; |
41 | } |
42 | |
43 | struct gfwd_list *add_exec(struct gfwd_list **ex_ptr, const char *cmdline, |
44 | struct in_addr addr, int port) |
45 | { |
46 | struct gfwd_list *f = add_guestfwd(ex_ptr, NULL, NULL, addr, port); |
47 | |
48 | f->ex_exec = g_strdup(cmdline); |
49 | |
50 | return f; |
51 | } |
52 | |
53 | static int slirp_socketpair_with_oob(int sv[2]) |
54 | { |
55 | struct sockaddr_in addr = { |
56 | .sin_family = AF_INET, |
57 | .sin_port = 0, |
58 | .sin_addr.s_addr = INADDR_ANY, |
59 | }; |
60 | socklen_t addrlen = sizeof(addr); |
61 | int ret, s; |
62 | |
63 | sv[1] = -1; |
64 | s = slirp_socket(AF_INET, SOCK_STREAM, 0); |
65 | if (s < 0 || bind(s, (struct sockaddr *)&addr, addrlen) < 0 || |
66 | listen(s, 1) < 0 || |
67 | getsockname(s, (struct sockaddr *)&addr, &addrlen) < 0) { |
68 | goto err; |
69 | } |
70 | |
71 | sv[1] = slirp_socket(AF_INET, SOCK_STREAM, 0); |
72 | if (sv[1] < 0) { |
73 | goto err; |
74 | } |
75 | /* |
76 | * This connect won't block because we've already listen()ed on |
77 | * the server end (even though we won't accept() the connection |
78 | * until later on). |
79 | */ |
80 | do { |
81 | ret = connect(sv[1], (struct sockaddr *)&addr, addrlen); |
82 | } while (ret < 0 && errno == EINTR); |
83 | if (ret < 0) { |
84 | goto err; |
85 | } |
86 | |
87 | do { |
88 | sv[0] = accept(s, (struct sockaddr *)&addr, &addrlen); |
89 | } while (sv[0] < 0 && errno == EINTR); |
90 | if (sv[0] < 0) { |
91 | goto err; |
92 | } |
93 | |
94 | closesocket(s); |
95 | return 0; |
96 | |
97 | err: |
98 | g_critical("slirp_socketpair(): %s" , strerror(errno)); |
99 | if (s >= 0) { |
100 | closesocket(s); |
101 | } |
102 | if (sv[1] >= 0) { |
103 | closesocket(sv[1]); |
104 | } |
105 | return -1; |
106 | } |
107 | |
108 | static void fork_exec_child_setup(gpointer data) |
109 | { |
110 | #ifndef _WIN32 |
111 | setsid(); |
112 | #endif |
113 | } |
114 | |
115 | #pragma GCC diagnostic push |
116 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" |
117 | |
118 | #if !GLIB_CHECK_VERSION(2, 58, 0) |
119 | typedef struct SlirpGSpawnFds { |
120 | GSpawnChildSetupFunc child_setup; |
121 | gpointer user_data; |
122 | gint stdin_fd; |
123 | gint stdout_fd; |
124 | gint stderr_fd; |
125 | } SlirpGSpawnFds; |
126 | |
127 | static inline void slirp_gspawn_fds_setup(gpointer user_data) |
128 | { |
129 | SlirpGSpawnFds *q = (SlirpGSpawnFds *)user_data; |
130 | |
131 | dup2(q->stdin_fd, 0); |
132 | dup2(q->stdout_fd, 1); |
133 | dup2(q->stderr_fd, 2); |
134 | q->child_setup(q->user_data); |
135 | } |
136 | #endif |
137 | |
138 | static inline gboolean |
139 | g_spawn_async_with_fds_slirp(const gchar *working_directory, gchar **argv, |
140 | gchar **envp, GSpawnFlags flags, |
141 | GSpawnChildSetupFunc child_setup, |
142 | gpointer user_data, GPid *child_pid, gint stdin_fd, |
143 | gint stdout_fd, gint stderr_fd, GError **error) |
144 | { |
145 | #if GLIB_CHECK_VERSION(2, 58, 0) |
146 | return g_spawn_async_with_fds(working_directory, argv, envp, flags, |
147 | child_setup, user_data, child_pid, stdin_fd, |
148 | stdout_fd, stderr_fd, error); |
149 | #else |
150 | SlirpGSpawnFds setup = { |
151 | .child_setup = child_setup, |
152 | .user_data = user_data, |
153 | .stdin_fd = stdin_fd, |
154 | .stdout_fd = stdout_fd, |
155 | .stderr_fd = stderr_fd, |
156 | }; |
157 | |
158 | return g_spawn_async(working_directory, argv, envp, flags, |
159 | slirp_gspawn_fds_setup, &setup, child_pid, error); |
160 | #endif |
161 | } |
162 | |
163 | #define g_spawn_async_with_fds(wd, argv, env, f, c, d, p, ifd, ofd, efd, err) \ |
164 | g_spawn_async_with_fds_slirp(wd, argv, env, f, c, d, p, ifd, ofd, efd, err) |
165 | |
166 | #pragma GCC diagnostic pop |
167 | |
168 | int fork_exec(struct socket *so, const char *ex) |
169 | { |
170 | GError *err = NULL; |
171 | char **argv; |
172 | int opt, sp[2]; |
173 | |
174 | DEBUG_CALL("fork_exec" ); |
175 | DEBUG_ARG("so = %p" , so); |
176 | DEBUG_ARG("ex = %p" , ex); |
177 | |
178 | if (slirp_socketpair_with_oob(sp) < 0) { |
179 | return 0; |
180 | } |
181 | |
182 | argv = g_strsplit(ex, " " , -1); |
183 | g_spawn_async_with_fds(NULL /* cwd */, argv, NULL /* env */, |
184 | G_SPAWN_SEARCH_PATH, fork_exec_child_setup, |
185 | NULL /* data */, NULL /* child_pid */, sp[1], sp[1], |
186 | sp[1], &err); |
187 | g_strfreev(argv); |
188 | |
189 | if (err) { |
190 | g_critical("fork_exec: %s" , err->message); |
191 | g_error_free(err); |
192 | closesocket(sp[0]); |
193 | closesocket(sp[1]); |
194 | return 0; |
195 | } |
196 | |
197 | so->s = sp[0]; |
198 | closesocket(sp[1]); |
199 | slirp_socket_set_fast_reuse(so->s); |
200 | opt = 1; |
201 | setsockopt(so->s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); |
202 | slirp_set_nonblock(so->s); |
203 | so->slirp->cb->register_poll_fd(so->s, so->slirp->opaque); |
204 | return 1; |
205 | } |
206 | |
207 | char *slirp_connection_info(Slirp *slirp) |
208 | { |
209 | GString *str = g_string_new(NULL); |
210 | const char *const tcpstates[] = { |
211 | [TCPS_CLOSED] = "CLOSED" , [TCPS_LISTEN] = "LISTEN" , |
212 | [TCPS_SYN_SENT] = "SYN_SENT" , [TCPS_SYN_RECEIVED] = "SYN_RCVD" , |
213 | [TCPS_ESTABLISHED] = "ESTABLISHED" , [TCPS_CLOSE_WAIT] = "CLOSE_WAIT" , |
214 | [TCPS_FIN_WAIT_1] = "FIN_WAIT_1" , [TCPS_CLOSING] = "CLOSING" , |
215 | [TCPS_LAST_ACK] = "LAST_ACK" , [TCPS_FIN_WAIT_2] = "FIN_WAIT_2" , |
216 | [TCPS_TIME_WAIT] = "TIME_WAIT" , |
217 | }; |
218 | struct in_addr dst_addr; |
219 | struct sockaddr_in src; |
220 | socklen_t src_len; |
221 | uint16_t dst_port; |
222 | struct socket *so; |
223 | const char *state; |
224 | char buf[20]; |
225 | |
226 | g_string_append_printf(str, |
227 | " Protocol[State] FD Source Address Port " |
228 | "Dest. Address Port RecvQ SendQ\n" ); |
229 | |
230 | /* TODO: IPv6 */ |
231 | |
232 | for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so->so_next) { |
233 | if (so->so_state & SS_HOSTFWD) { |
234 | state = "HOST_FORWARD" ; |
235 | } else if (so->so_tcpcb) { |
236 | state = tcpstates[so->so_tcpcb->t_state]; |
237 | } else { |
238 | state = "NONE" ; |
239 | } |
240 | if (so->so_state & (SS_HOSTFWD | SS_INCOMING)) { |
241 | src_len = sizeof(src); |
242 | getsockname(so->s, (struct sockaddr *)&src, &src_len); |
243 | dst_addr = so->so_laddr; |
244 | dst_port = so->so_lport; |
245 | } else { |
246 | src.sin_addr = so->so_laddr; |
247 | src.sin_port = so->so_lport; |
248 | dst_addr = so->so_faddr; |
249 | dst_port = so->so_fport; |
250 | } |
251 | snprintf(buf, sizeof(buf), " TCP[%s]" , state); |
252 | g_string_append_printf(str, "%-19s %3d %15s %5d " , buf, so->s, |
253 | src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : |
254 | "*" , |
255 | ntohs(src.sin_port)); |
256 | g_string_append_printf(str, "%15s %5d %5d %5d\n" , inet_ntoa(dst_addr), |
257 | ntohs(dst_port), so->so_rcv.sb_cc, |
258 | so->so_snd.sb_cc); |
259 | } |
260 | |
261 | for (so = slirp->udb.so_next; so != &slirp->udb; so = so->so_next) { |
262 | if (so->so_state & SS_HOSTFWD) { |
263 | snprintf(buf, sizeof(buf), " UDP[HOST_FORWARD]" ); |
264 | src_len = sizeof(src); |
265 | getsockname(so->s, (struct sockaddr *)&src, &src_len); |
266 | dst_addr = so->so_laddr; |
267 | dst_port = so->so_lport; |
268 | } else { |
269 | snprintf(buf, sizeof(buf), " UDP[%d sec]" , |
270 | (so->so_expire - curtime) / 1000); |
271 | src.sin_addr = so->so_laddr; |
272 | src.sin_port = so->so_lport; |
273 | dst_addr = so->so_faddr; |
274 | dst_port = so->so_fport; |
275 | } |
276 | g_string_append_printf(str, "%-19s %3d %15s %5d " , buf, so->s, |
277 | src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : |
278 | "*" , |
279 | ntohs(src.sin_port)); |
280 | g_string_append_printf(str, "%15s %5d %5d %5d\n" , inet_ntoa(dst_addr), |
281 | ntohs(dst_port), so->so_rcv.sb_cc, |
282 | so->so_snd.sb_cc); |
283 | } |
284 | |
285 | for (so = slirp->icmp.so_next; so != &slirp->icmp; so = so->so_next) { |
286 | snprintf(buf, sizeof(buf), " ICMP[%d sec]" , |
287 | (so->so_expire - curtime) / 1000); |
288 | src.sin_addr = so->so_laddr; |
289 | dst_addr = so->so_faddr; |
290 | g_string_append_printf(str, "%-19s %3d %15s - " , buf, so->s, |
291 | src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : |
292 | "*" ); |
293 | g_string_append_printf(str, "%15s - %5d %5d\n" , inet_ntoa(dst_addr), |
294 | so->so_rcv.sb_cc, so->so_snd.sb_cc); |
295 | } |
296 | |
297 | return g_string_free(str, FALSE); |
298 | } |
299 | |