67e3eee454
struct mbuf uses a C99 open char array to allow inlining data. Inlining this in another structure is however a GNU extension. The inlines used so far in struct Slirp were actually only needed as head of struct mbuf lists. This replaces these inline with mere struct quehead, and use casts as appropriate. Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
316 lines
8.8 KiB
C
316 lines
8.8 KiB
C
/*
|
|
* Copyright (c) 1995 Danny Gasparovski.
|
|
*
|
|
* Please read the file COPYRIGHT for the
|
|
* terms and conditions of the copyright.
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include <slirp.h>
|
|
#include <libslirp.h>
|
|
|
|
#include "monitor/monitor.h"
|
|
#include "qemu/error-report.h"
|
|
#include "qemu/main-loop.h"
|
|
|
|
#ifdef DEBUG
|
|
int slirp_debug = DBG_CALL|DBG_MISC|DBG_ERROR;
|
|
#endif
|
|
|
|
inline void
|
|
insque(void *a, void *b)
|
|
{
|
|
register struct quehead *element = (struct quehead *) a;
|
|
register struct quehead *head = (struct quehead *) b;
|
|
element->qh_link = head->qh_link;
|
|
head->qh_link = (struct quehead *)element;
|
|
element->qh_rlink = (struct quehead *)head;
|
|
((struct quehead *)(element->qh_link))->qh_rlink
|
|
= (struct quehead *)element;
|
|
}
|
|
|
|
inline void
|
|
remque(void *a)
|
|
{
|
|
register struct quehead *element = (struct quehead *) a;
|
|
((struct quehead *)(element->qh_link))->qh_rlink = element->qh_rlink;
|
|
((struct quehead *)(element->qh_rlink))->qh_link = element->qh_link;
|
|
element->qh_rlink = NULL;
|
|
}
|
|
|
|
int add_exec(struct ex_list **ex_ptr, int do_pty, char *exec,
|
|
struct in_addr addr, int port)
|
|
{
|
|
struct ex_list *tmp_ptr;
|
|
|
|
/* First, check if the port is "bound" */
|
|
for (tmp_ptr = *ex_ptr; tmp_ptr; tmp_ptr = tmp_ptr->ex_next) {
|
|
if (port == tmp_ptr->ex_fport &&
|
|
addr.s_addr == tmp_ptr->ex_addr.s_addr)
|
|
return -1;
|
|
}
|
|
|
|
tmp_ptr = *ex_ptr;
|
|
*ex_ptr = g_new(struct ex_list, 1);
|
|
(*ex_ptr)->ex_fport = port;
|
|
(*ex_ptr)->ex_addr = addr;
|
|
(*ex_ptr)->ex_pty = do_pty;
|
|
(*ex_ptr)->ex_exec = (do_pty == 3) ? exec : g_strdup(exec);
|
|
(*ex_ptr)->ex_next = tmp_ptr;
|
|
return 0;
|
|
}
|
|
|
|
#ifndef HAVE_STRERROR
|
|
|
|
/*
|
|
* For systems with no strerror
|
|
*/
|
|
|
|
extern int sys_nerr;
|
|
extern char *sys_errlist[];
|
|
|
|
char *
|
|
strerror(error)
|
|
int error;
|
|
{
|
|
if (error < sys_nerr)
|
|
return sys_errlist[error];
|
|
else
|
|
return "Unknown error.";
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
int
|
|
fork_exec(struct socket *so, const char *ex, int do_pty)
|
|
{
|
|
/* not implemented */
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
|
|
/*
|
|
* XXX This is ugly
|
|
* We create and bind a socket, then fork off to another
|
|
* process, which connects to this socket, after which we
|
|
* exec the wanted program. If something (strange) happens,
|
|
* the accept() call could block us forever.
|
|
*
|
|
* do_pty = 0 Fork/exec inetd style
|
|
* do_pty = 1 Fork/exec using slirp.telnetd
|
|
* do_ptr = 2 Fork/exec using pty
|
|
*/
|
|
int
|
|
fork_exec(struct socket *so, const char *ex, int do_pty)
|
|
{
|
|
int s;
|
|
struct sockaddr_in addr;
|
|
socklen_t addrlen = sizeof(addr);
|
|
int opt;
|
|
const char *argv[256];
|
|
/* don't want to clobber the original */
|
|
char *bptr;
|
|
const char *curarg;
|
|
int c, i, ret;
|
|
pid_t pid;
|
|
|
|
DEBUG_CALL("fork_exec");
|
|
DEBUG_ARG("so = %p", so);
|
|
DEBUG_ARG("ex = %p", ex);
|
|
DEBUG_ARG("do_pty = %x", do_pty);
|
|
|
|
if (do_pty == 2) {
|
|
return 0;
|
|
} else {
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = 0;
|
|
addr.sin_addr.s_addr = INADDR_ANY;
|
|
|
|
if ((s = qemu_socket(AF_INET, SOCK_STREAM, 0)) < 0 ||
|
|
bind(s, (struct sockaddr *)&addr, addrlen) < 0 ||
|
|
listen(s, 1) < 0) {
|
|
error_report("Error: inet socket: %s", strerror(errno));
|
|
closesocket(s);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
pid = fork();
|
|
switch(pid) {
|
|
case -1:
|
|
error_report("Error: fork failed: %s", strerror(errno));
|
|
close(s);
|
|
return 0;
|
|
|
|
case 0:
|
|
setsid();
|
|
|
|
/* Set the DISPLAY */
|
|
getsockname(s, (struct sockaddr *)&addr, &addrlen);
|
|
close(s);
|
|
/*
|
|
* Connect to the socket
|
|
* XXX If any of these fail, we're in trouble!
|
|
*/
|
|
s = qemu_socket(AF_INET, SOCK_STREAM, 0);
|
|
addr.sin_addr = loopback_addr;
|
|
do {
|
|
ret = connect(s, (struct sockaddr *)&addr, addrlen);
|
|
} while (ret < 0 && errno == EINTR);
|
|
|
|
dup2(s, 0);
|
|
dup2(s, 1);
|
|
dup2(s, 2);
|
|
for (s = getdtablesize() - 1; s >= 3; s--)
|
|
close(s);
|
|
|
|
i = 0;
|
|
bptr = g_strdup(ex); /* No need to free() this */
|
|
if (do_pty == 1) {
|
|
/* Setup "slirp.telnetd -x" */
|
|
argv[i++] = "slirp.telnetd";
|
|
argv[i++] = "-x";
|
|
argv[i++] = bptr;
|
|
} else
|
|
do {
|
|
/* Change the string into argv[] */
|
|
curarg = bptr;
|
|
while (*bptr != ' ' && *bptr != (char)0)
|
|
bptr++;
|
|
c = *bptr;
|
|
*bptr++ = (char)0;
|
|
argv[i++] = g_strdup(curarg);
|
|
} while (c);
|
|
|
|
argv[i] = NULL;
|
|
execvp(argv[0], (char **)argv);
|
|
|
|
/* Ooops, failed, let's tell the user why */
|
|
fprintf(stderr, "Error: execvp of %s failed: %s\n",
|
|
argv[0], strerror(errno));
|
|
close(0); close(1); close(2); /* XXX */
|
|
exit(1);
|
|
|
|
default:
|
|
qemu_add_child_watch(pid);
|
|
/*
|
|
* XXX this could block us...
|
|
* XXX Should set a timer here, and if accept() doesn't
|
|
* return after X seconds, declare it a failure
|
|
* The only reason this will block forever is if socket()
|
|
* of connect() fail in the child process
|
|
*/
|
|
do {
|
|
so->s = accept(s, (struct sockaddr *)&addr, &addrlen);
|
|
} while (so->s < 0 && errno == EINTR);
|
|
closesocket(s);
|
|
socket_set_fast_reuse(so->s);
|
|
opt = 1;
|
|
qemu_setsockopt(so->s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int));
|
|
qemu_set_nonblock(so->s);
|
|
|
|
/* Append the telnet options now */
|
|
if (so->so_m != NULL && do_pty == 1) {
|
|
sbappend(so, so->so_m);
|
|
so->so_m = NULL;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void slirp_connection_info(Slirp *slirp, Monitor *mon)
|
|
{
|
|
const char * const tcpstates[] = {
|
|
[TCPS_CLOSED] = "CLOSED",
|
|
[TCPS_LISTEN] = "LISTEN",
|
|
[TCPS_SYN_SENT] = "SYN_SENT",
|
|
[TCPS_SYN_RECEIVED] = "SYN_RCVD",
|
|
[TCPS_ESTABLISHED] = "ESTABLISHED",
|
|
[TCPS_CLOSE_WAIT] = "CLOSE_WAIT",
|
|
[TCPS_FIN_WAIT_1] = "FIN_WAIT_1",
|
|
[TCPS_CLOSING] = "CLOSING",
|
|
[TCPS_LAST_ACK] = "LAST_ACK",
|
|
[TCPS_FIN_WAIT_2] = "FIN_WAIT_2",
|
|
[TCPS_TIME_WAIT] = "TIME_WAIT",
|
|
};
|
|
struct in_addr dst_addr;
|
|
struct sockaddr_in src;
|
|
socklen_t src_len;
|
|
uint16_t dst_port;
|
|
struct socket *so;
|
|
const char *state;
|
|
char buf[20];
|
|
|
|
monitor_printf(mon, " Protocol[State] FD Source Address Port "
|
|
"Dest. Address Port RecvQ SendQ\n");
|
|
|
|
for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so->so_next) {
|
|
if (so->so_state & SS_HOSTFWD) {
|
|
state = "HOST_FORWARD";
|
|
} else if (so->so_tcpcb) {
|
|
state = tcpstates[so->so_tcpcb->t_state];
|
|
} else {
|
|
state = "NONE";
|
|
}
|
|
if (so->so_state & (SS_HOSTFWD | SS_INCOMING)) {
|
|
src_len = sizeof(src);
|
|
getsockname(so->s, (struct sockaddr *)&src, &src_len);
|
|
dst_addr = so->so_laddr;
|
|
dst_port = so->so_lport;
|
|
} else {
|
|
src.sin_addr = so->so_laddr;
|
|
src.sin_port = so->so_lport;
|
|
dst_addr = so->so_faddr;
|
|
dst_port = so->so_fport;
|
|
}
|
|
snprintf(buf, sizeof(buf), " TCP[%s]", state);
|
|
monitor_printf(mon, "%-19s %3d %15s %5d ", buf, so->s,
|
|
src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*",
|
|
ntohs(src.sin_port));
|
|
monitor_printf(mon, "%15s %5d %5d %5d\n",
|
|
inet_ntoa(dst_addr), ntohs(dst_port),
|
|
so->so_rcv.sb_cc, so->so_snd.sb_cc);
|
|
}
|
|
|
|
for (so = slirp->udb.so_next; so != &slirp->udb; so = so->so_next) {
|
|
if (so->so_state & SS_HOSTFWD) {
|
|
snprintf(buf, sizeof(buf), " UDP[HOST_FORWARD]");
|
|
src_len = sizeof(src);
|
|
getsockname(so->s, (struct sockaddr *)&src, &src_len);
|
|
dst_addr = so->so_laddr;
|
|
dst_port = so->so_lport;
|
|
} else {
|
|
snprintf(buf, sizeof(buf), " UDP[%d sec]",
|
|
(so->so_expire - curtime) / 1000);
|
|
src.sin_addr = so->so_laddr;
|
|
src.sin_port = so->so_lport;
|
|
dst_addr = so->so_faddr;
|
|
dst_port = so->so_fport;
|
|
}
|
|
monitor_printf(mon, "%-19s %3d %15s %5d ", buf, so->s,
|
|
src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*",
|
|
ntohs(src.sin_port));
|
|
monitor_printf(mon, "%15s %5d %5d %5d\n",
|
|
inet_ntoa(dst_addr), ntohs(dst_port),
|
|
so->so_rcv.sb_cc, so->so_snd.sb_cc);
|
|
}
|
|
|
|
for (so = slirp->icmp.so_next; so != &slirp->icmp; so = so->so_next) {
|
|
snprintf(buf, sizeof(buf), " ICMP[%d sec]",
|
|
(so->so_expire - curtime) / 1000);
|
|
src.sin_addr = so->so_laddr;
|
|
dst_addr = so->so_faddr;
|
|
monitor_printf(mon, "%-19s %3d %15s - ", buf, so->s,
|
|
src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*");
|
|
monitor_printf(mon, "%15s - %5d %5d\n", inet_ntoa(dst_addr),
|
|
so->so_rcv.sb_cc, so->so_snd.sb_cc);
|
|
}
|
|
}
|