summaryrefslogtreecommitdiff
path: root/vl.c
diff options
context:
space:
mode:
authorbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>2006-06-25 14:49:44 +0000
committerbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>2006-06-25 14:49:44 +0000
commit0bab00f30f798bd8ae4366a4516d2149174aa714 (patch)
treeef5aaa0243f45006230b86f2ff72bf4709280f2e /vl.c
parent3532fa7402cda16f7b95261b0339c58630051f0b (diff)
downloadqemu-0bab00f30f798bd8ae4366a4516d2149174aa714.tar.gz
UDP char device (initial patch by Jason Wessel) - TCP char device
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2007 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'vl.c')
-rw-r--r--vl.c381
1 files changed, 379 insertions, 2 deletions
diff --git a/vl.c b/vl.c
index 76cfe71e97..7d77234603 100644
--- a/vl.c
+++ b/vl.c
@@ -2130,6 +2130,373 @@ CharDriverState *qemu_chr_open_win_file_out(const char *file_out)
}
#endif
+/***********************************************************/
+/* UDP Net console */
+
+typedef struct {
+ IOCanRWHandler *fd_can_read;
+ IOReadHandler *fd_read;
+ void *fd_opaque;
+ int fd;
+ struct sockaddr_in daddr;
+ char buf[1024];
+ int bufcnt;
+ int bufptr;
+ int max_size;
+} NetCharDriver;
+
+static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ NetCharDriver *s = chr->opaque;
+
+ return sendto(s->fd, buf, len, 0,
+ (struct sockaddr *)&s->daddr, sizeof(struct sockaddr_in));
+}
+
+static int udp_chr_read_poll(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ NetCharDriver *s = chr->opaque;
+
+ s->max_size = s->fd_can_read(s->fd_opaque);
+
+ /* If there were any stray characters in the queue process them
+ * first
+ */
+ while (s->max_size > 0 && s->bufptr < s->bufcnt) {
+ s->fd_read(s->fd_opaque, &s->buf[s->bufptr], 1);
+ s->bufptr++;
+ s->max_size = s->fd_can_read(s->fd_opaque);
+ }
+ return s->max_size;
+}
+
+static void udp_chr_read(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ NetCharDriver *s = chr->opaque;
+
+ if (s->max_size == 0)
+ return;
+ s->bufcnt = recv(s->fd, s->buf, sizeof(s->buf), 0);
+ s->bufptr = s->bufcnt;
+ if (s->bufcnt <= 0)
+ return;
+
+ s->bufptr = 0;
+ while (s->max_size > 0 && s->bufptr < s->bufcnt) {
+ s->fd_read(s->fd_opaque, &s->buf[s->bufptr], 1);
+ s->bufptr++;
+ s->max_size = s->fd_can_read(s->fd_opaque);
+ }
+}
+
+static void udp_chr_add_read_handler(CharDriverState *chr,
+ IOCanRWHandler *fd_can_read,
+ IOReadHandler *fd_read, void *opaque)
+{
+ NetCharDriver *s = chr->opaque;
+
+ if (s->fd >= 0) {
+ s->fd_can_read = fd_can_read;
+ s->fd_read = fd_read;
+ s->fd_opaque = opaque;
+ qemu_set_fd_handler2(s->fd, udp_chr_read_poll,
+ udp_chr_read, NULL, chr);
+ }
+}
+
+int parse_host_port(struct sockaddr_in *saddr, const char *str);
+
+CharDriverState *qemu_chr_open_udp(const char *def)
+{
+ CharDriverState *chr = NULL;
+ NetCharDriver *s = NULL;
+ int fd = -1;
+ int con_type;
+ struct sockaddr_in addr;
+ const char *p, *r;
+ int port;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr)
+ goto return_err;
+ s = qemu_mallocz(sizeof(NetCharDriver));
+ if (!s)
+ goto return_err;
+
+ fd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ perror("socket(PF_INET, SOCK_DGRAM)");
+ goto return_err;
+ }
+
+ /* There are three types of port definitions
+ * 1) udp:remote_port
+ * Juse use 0.0.0.0 for the IP and send to remote
+ * 2) udp:remote_host:port
+ * Use a IP and send traffic to remote
+ * 3) udp:local_port:remote_host:remote_port
+ * Use local_port as the originator + #2
+ */
+ con_type = 0;
+ p = def;
+ while ((p = strchr(p, ':'))) {
+ p++;
+ con_type++;
+ }
+
+ p = def;
+ memset(&addr,0,sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ s->daddr.sin_family = AF_INET;
+ s->daddr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ switch (con_type) {
+ case 0:
+ port = strtol(p, (char **)&r, 0);
+ if (r == p) {
+ fprintf(stderr, "Error parsing port number\n");
+ goto return_err;
+ }
+ s->daddr.sin_port = htons((short)port);
+ break;
+ case 2:
+ port = strtol(p, (char **)&r, 0);
+ if (r == p) {
+ fprintf(stderr, "Error parsing port number\n");
+ goto return_err;
+ }
+ addr.sin_port = htons((short)port);
+ p = r + 1;
+ /* Fall through to case 1 now that we have the local port */
+ case 1:
+ if (parse_host_port(&s->daddr, p) < 0) {
+ fprintf(stderr, "Error parsing host name and port\n");
+ goto return_err;
+ }
+ break;
+ default:
+ fprintf(stderr, "Too many ':' characters\n");
+ goto return_err;
+ }
+
+ if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+ {
+ perror("bind");
+ goto return_err;
+ }
+
+ s->fd = fd;
+ s->bufcnt = 0;
+ s->bufptr = 0;
+ chr->opaque = s;
+ chr->chr_write = udp_chr_write;
+ chr->chr_add_read_handler = udp_chr_add_read_handler;
+ return chr;
+
+return_err:
+ if (chr)
+ free(chr);
+ if (s)
+ free(s);
+ if (fd >= 0)
+ closesocket(fd);
+ return NULL;
+}
+
+/***********************************************************/
+/* TCP Net console */
+
+typedef struct {
+ IOCanRWHandler *fd_can_read;
+ IOReadHandler *fd_read;
+ void *fd_opaque;
+ int fd, listen_fd;
+ int connected;
+ int max_size;
+} TCPCharDriver;
+
+static void tcp_chr_accept(void *opaque);
+
+static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ TCPCharDriver *s = chr->opaque;
+ if (s->connected) {
+ return send_all(s->fd, buf, len);
+ } else {
+ /* XXX: indicate an error ? */
+ return len;
+ }
+}
+
+static int tcp_chr_read_poll(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ TCPCharDriver *s = chr->opaque;
+ if (!s->connected)
+ return 0;
+ s->max_size = s->fd_can_read(s->fd_opaque);
+ return s->max_size;
+}
+
+static void tcp_chr_read(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ TCPCharDriver *s = chr->opaque;
+ uint8_t buf[1024];
+ int len, size;
+
+ if (!s->connected || s->max_size <= 0)
+ return;
+ len = sizeof(buf);
+ if (len > s->max_size)
+ len = s->max_size;
+ size = recv(s->fd, buf, len, 0);
+ if (size == 0) {
+ /* connection closed */
+ s->connected = 0;
+ if (s->listen_fd >= 0) {
+ qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr);
+ }
+ qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+ closesocket(s->fd);
+ s->fd = -1;
+ } else if (size > 0) {
+ s->fd_read(s->fd_opaque, buf, size);
+ }
+}
+
+static void tcp_chr_add_read_handler(CharDriverState *chr,
+ IOCanRWHandler *fd_can_read,
+ IOReadHandler *fd_read, void *opaque)
+{
+ TCPCharDriver *s = chr->opaque;
+
+ s->fd_can_read = fd_can_read;
+ s->fd_read = fd_read;
+ s->fd_opaque = opaque;
+}
+
+static void tcp_chr_connect(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ TCPCharDriver *s = chr->opaque;
+
+ s->connected = 1;
+ qemu_set_fd_handler2(s->fd, tcp_chr_read_poll,
+ tcp_chr_read, NULL, chr);
+}
+
+static void tcp_chr_accept(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ TCPCharDriver *s = chr->opaque;
+ struct sockaddr_in saddr;
+ socklen_t len;
+ int fd;
+
+ for(;;) {
+ len = sizeof(saddr);
+ fd = accept(s->listen_fd, (struct sockaddr *)&saddr, &len);
+ if (fd < 0 && errno != EINTR) {
+ return;
+ } else if (fd >= 0) {
+ break;
+ }
+ }
+ socket_set_nonblock(fd);
+ s->fd = fd;
+ qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL);
+ tcp_chr_connect(chr);
+}
+
+static void tcp_chr_close(CharDriverState *chr)
+{
+ TCPCharDriver *s = chr->opaque;
+ if (s->fd >= 0)
+ closesocket(s->fd);
+ if (s->listen_fd >= 0)
+ closesocket(s->listen_fd);
+ qemu_free(s);
+}
+
+static CharDriverState *qemu_chr_open_tcp(const char *host_str,
+ int is_listen)
+{
+ CharDriverState *chr = NULL;
+ TCPCharDriver *s = NULL;
+ int fd = -1, ret, err, val;
+ struct sockaddr_in saddr;
+
+ if (parse_host_port(&saddr, host_str) < 0)
+ goto fail;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr)
+ goto fail;
+ s = qemu_mallocz(sizeof(TCPCharDriver));
+ if (!s)
+ goto fail;
+
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0)
+ goto fail;
+ socket_set_nonblock(fd);
+
+ s->connected = 0;
+ s->fd = -1;
+ s->listen_fd = -1;
+ if (is_listen) {
+ /* allow fast reuse */
+ val = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val));
+
+ ret = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr));
+ if (ret < 0)
+ goto fail;
+ ret = listen(fd, 0);
+ if (ret < 0)
+ goto fail;
+ s->listen_fd = fd;
+ qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr);
+ } else {
+ for(;;) {
+ ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr));
+ if (ret < 0) {
+ err = socket_error();
+ if (err == EINTR || err == EWOULDBLOCK) {
+ } else if (err == EINPROGRESS) {
+ break;
+ } else {
+ goto fail;
+ }
+ } else {
+ s->connected = 1;
+ break;
+ }
+ }
+ s->fd = fd;
+ if (s->connected)
+ tcp_chr_connect(chr);
+ else
+ qemu_set_fd_handler(s->fd, NULL, tcp_chr_connect, chr);
+ }
+
+ chr->opaque = s;
+ chr->chr_write = tcp_chr_write;
+ chr->chr_add_read_handler = tcp_chr_add_read_handler;
+ chr->chr_close = tcp_chr_close;
+ return chr;
+ fail:
+ if (fd >= 0)
+ closesocket(fd);
+ qemu_free(s);
+ qemu_free(chr);
+ return NULL;
+}
+
CharDriverState *qemu_chr_open(const char *filename)
{
const char *p;
@@ -2139,6 +2506,15 @@ CharDriverState *qemu_chr_open(const char *filename)
} else if (!strcmp(filename, "null")) {
return qemu_chr_open_null();
} else
+ if (strstart(filename, "tcp:", &p)) {
+ return qemu_chr_open_tcp(p, 0);
+ } else
+ if (strstart(filename, "tcpl:", &p)) {
+ return qemu_chr_open_tcp(p, 1);
+ } else
+ if (strstart(filename, "udp:", &p)) {
+ return qemu_chr_open_udp(p);
+ } else
#ifndef _WIN32
if (strstart(filename, "file:", &p)) {
return qemu_chr_open_file_out(p);
@@ -2844,7 +3220,8 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr)
socket_set_nonblock(fd);
return fd;
fail:
- if (fd>=0) close(fd);
+ if (fd >= 0)
+ closesocket(fd);
return -1;
}
@@ -2972,7 +3349,7 @@ static void net_socket_accept(void *opaque)
}
s1 = net_socket_fd_init(s->vlan, fd, 1);
if (!s1) {
- close(fd);
+ closesocket(fd);
} else {
snprintf(s1->vc->info_str, sizeof(s1->vc->info_str),
"socket: connection from %s:%d",