/* | |
* Copyright (c) 1995 Danny Gasparovski. | |
* | |
* Please read the file COPYRIGHT for the | |
* terms and conditions of the copyright. | |
*/ | |
#include <slirp.h> | |
#include <libslirp.h> | |
#include "monitor/monitor.h" | |
#include "qemu/error-report.h" | |
#include "qemu/main-loop.h" | |
int slirp_debug = | |
#ifdef DEBUG | |
DBG_CALL|DBG_MISC|DBG_ERROR; | |
#else | |
0; | |
#endif | |
struct quehead { | |
struct quehead *qh_link; | |
struct quehead *qh_rlink; | |
}; | |
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; | |
} | |
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, const 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 = (struct ex_list *)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 = %lx", (long)so); | |
DEBUG_ARG("ex = %lx", (long)ex); | |
DEBUG_ARG("do_pty = %lx", (long)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 * const *)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) | |
{ | |
#if (TCPS_CLOSED != 0) || (TCPS_TIME_WAIT != 10) | |
#error unexpected TCPS symbol values | |
#endif | |
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); | |
} | |
} |