Commit 951f13516a50383cd462c9150e643cc9f9566267

Authored by bellard
1 parent 1f6e24e7

telnet protocol and more consistent syntax (Jason Wessel)


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2030 c046a42c-6fe2-441c-8c8c-71466251a162
Showing 2 changed files with 191 additions and 82 deletions
qemu-doc.texi
... ... @@ -525,43 +525,60 @@ Write output to filename. No character can be read.
525 525 name pipe @var{filename}
526 526 @item COMn
527 527 [Windows only] Use host serial port @var{n}
528   -@item udp:remote_port
529   -UDP Net Console sent to locahost at remote_port
530   -@item udp:remote_host:remote_port
531   -UDP Net Console sent to remote_host at remote_port
532   -@item udp:src_port:remote_host:remote_port
533   -UDP Net Console sent from src_port to remote_host at the remote_port.
534   -
535   -The udp:* sub options are primary intended for netconsole. If you
536   -just want a simple readonly console you can use @code{netcat} or
537   -@code{nc}, by starting qemu with: @code{-serial udp:4555} and nc as:
538   -@code{nc -u -l -p 4555}. Any time qemu writes something to that port
539   -it will appear in the netconsole session.
  528 +@item udp:[remote_host]:remote_port[@@[src_ip]:src_port]
  529 +This implements UDP Net Console. When @var{remote_host} or @var{src_ip} are not specified they default to @code{0.0.0.0}. When not using a specifed @var{src_port} a random port is automatically chosen.
  530 +
  531 +If you just want a simple readonly console you can use @code{netcat} or
  532 +@code{nc}, by starting qemu with: @code{-serial udp::4555} and nc as:
  533 +@code{nc -u -l -p 4555}. Any time qemu writes something to that port it
  534 +will appear in the netconsole session.
540 535  
541 536 If you plan to send characters back via netconsole or you want to stop
542 537 and start qemu a lot of times, you should have qemu use the same
543 538 source port each time by using something like @code{-serial
544   -udp:4556:localhost:4555} to qemu. Another approach is to use a patched
  539 +udp::4555@@:4556} to qemu. Another approach is to use a patched
545 540 version of netcat which can listen to a TCP port and send and receive
546 541 characters via udp. If you have a patched version of netcat which
547 542 activates telnet remote echo and single char transfer, then you can
548 543 use the following options to step up a netcat redirector to allow
549 544 telnet on port 5555 to access the qemu port.
550 545 @table @code
551   -@item Qemu Options
552   --serial udp:4556:localhost:4555
553   -@item netcat options
554   --u -P 4555 -L localhost:4556 -t -p 5555 -I -T
  546 +@item Qemu Options:
  547 +-serial udp::4555@@:4556
  548 +@item netcat options:
  549 +-u -P 4555 -L 0.0.0.0:4556 -t -p 5555 -I -T
  550 +@item telnet options:
  551 +localhost 5555
  552 +@end table
  553 +
  554 +
  555 +@item tcp:[host]:port[,server][,nowait]
  556 +The TCP Net Console has two modes of operation. It can send the serial
  557 +I/O to a location or wait for a connection from a location. By default
  558 +the TCP Net Console is sent to @var{host} at the @var{port}. If you use
  559 +the @var{,server} option QEMU will wait for a client socket application
  560 +to connect to the port before continuing, unless the @code{,nowait}
  561 +option was specified. If @var{host} is omitted, 0.0.0.0 is assumed. Only
  562 +one TCP connection at a time is accepted. You can use @code{telnet} to
  563 +connect to the corresponding character device.
  564 +@table @code
  565 +@item Example to send tcp console to 192.168.0.2 port 4444
  566 +-serial tcp:192.168.0.2:4444
  567 +@item Example to listen and wait on port 4444 for connection
  568 +-serial tcp::4444,server
  569 +@item Example to not wait and listen on ip 192.168.0.100 port 4444
  570 +-serial tcp:192.168.0.100:4444,server,nowait
555 571 @end table
556 572  
  573 +@item telnet:host:port[,server][,nowait]
  574 +The telnet protocol is used instead of raw tcp sockets. The options
  575 +work the same as if you had specified @code{-serial tcp}. The
  576 +difference is that the port acts like a telnet server or client using
  577 +telnet option negotiation. This will also allow you to send the
  578 +MAGIC_SYSRQ sequence if you use a telnet that supports sending the break
  579 +sequence. Typically in unix telnet you do it with Control-] and then
  580 +type "send break" followed by pressing the enter key.
557 581  
558   -@item tcp:remote_host:remote_port
559   -TCP Net Console sent to remote_host at the remote_port
560   -@item tcpl:host:port
561   -TCP Net Console: wait for connection on @var{host} on the local port
562   -@var{port}. If host is omitted, 0.0.0.0 is assumed. Only one TCP
563   -connection at a time is accepted. You can use @code{telnet} to connect
564   -to the corresponding character device.
565 582 @end table
566 583  
567 584 @item -parallel dev
... ...
... ... @@ -2203,16 +2203,16 @@ static void udp_chr_add_read_handler(CharDriverState *chr,
2203 2203 }
2204 2204  
2205 2205 int parse_host_port(struct sockaddr_in *saddr, const char *str);
  2206 +int parse_host_src_port(struct sockaddr_in *haddr,
  2207 + struct sockaddr_in *saddr,
  2208 + const char *str);
2206 2209  
2207 2210 CharDriverState *qemu_chr_open_udp(const char *def)
2208 2211 {
2209 2212 CharDriverState *chr = NULL;
2210 2213 NetCharDriver *s = NULL;
2211 2214 int fd = -1;
2212   - int con_type;
2213   - struct sockaddr_in addr;
2214   - const char *p, *r;
2215   - int port;
  2215 + struct sockaddr_in saddr;
2216 2216  
2217 2217 chr = qemu_mallocz(sizeof(CharDriverState));
2218 2218 if (!chr)
... ... @@ -2227,58 +2227,12 @@ CharDriverState *qemu_chr_open_udp(const char *def)
2227 2227 goto return_err;
2228 2228 }
2229 2229  
2230   - /* There are three types of port definitions
2231   - * 1) udp:remote_port
2232   - * Juse use 0.0.0.0 for the IP and send to remote
2233   - * 2) udp:remote_host:port
2234   - * Use a IP and send traffic to remote
2235   - * 3) udp:local_port:remote_host:remote_port
2236   - * Use local_port as the originator + #2
2237   - */
2238   - con_type = 0;
2239   - p = def;
2240   - while ((p = strchr(p, ':'))) {
2241   - p++;
2242   - con_type++;
2243   - }
2244   -
2245   - p = def;
2246   - memset(&addr,0,sizeof(addr));
2247   - addr.sin_family = AF_INET;
2248   - addr.sin_addr.s_addr = htonl(INADDR_ANY);
2249   - s->daddr.sin_family = AF_INET;
2250   - s->daddr.sin_addr.s_addr = htonl(INADDR_ANY);
2251   -
2252   - switch (con_type) {
2253   - case 0:
2254   - port = strtol(p, (char **)&r, 0);
2255   - if (r == p) {
2256   - fprintf(stderr, "Error parsing port number\n");
2257   - goto return_err;
2258   - }
2259   - s->daddr.sin_port = htons((short)port);
2260   - break;
2261   - case 2:
2262   - port = strtol(p, (char **)&r, 0);
2263   - if (r == p) {
2264   - fprintf(stderr, "Error parsing port number\n");
2265   - goto return_err;
2266   - }
2267   - addr.sin_port = htons((short)port);
2268   - p = r + 1;
2269   - /* Fall through to case 1 now that we have the local port */
2270   - case 1:
2271   - if (parse_host_port(&s->daddr, p) < 0) {
2272   - fprintf(stderr, "Error parsing host name and port\n");
2273   - goto return_err;
2274   - }
2275   - break;
2276   - default:
2277   - fprintf(stderr, "Too many ':' characters\n");
2278   - goto return_err;
  2230 + if (parse_host_src_port(&s->daddr, &saddr, def) < 0) {
  2231 + printf("Could not parse: %s\n", def);
  2232 + goto return_err;
2279 2233 }
2280 2234  
2281   - if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
  2235 + if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
2282 2236 {
2283 2237 perror("bind");
2284 2238 goto return_err;
... ... @@ -2312,6 +2266,7 @@ typedef struct {
2312 2266 int fd, listen_fd;
2313 2267 int connected;
2314 2268 int max_size;
  2269 + int do_telnetopt;
2315 2270 } TCPCharDriver;
2316 2271  
2317 2272 static void tcp_chr_accept(void *opaque);
... ... @@ -2337,6 +2292,56 @@ static int tcp_chr_read_poll(void *opaque)
2337 2292 return s->max_size;
2338 2293 }
2339 2294  
  2295 +#define IAC 255
  2296 +#define IAC_BREAK 243
  2297 +static void tcp_chr_process_IAC_bytes(CharDriverState *chr,
  2298 + TCPCharDriver *s,
  2299 + char *buf, int *size)
  2300 +{
  2301 + /* Handle any telnet client's basic IAC options to satisfy char by
  2302 + * char mode with no echo. All IAC options will be removed from
  2303 + * the buf and the do_telnetopt variable will be used to track the
  2304 + * state of the width of the IAC information.
  2305 + *
  2306 + * IAC commands come in sets of 3 bytes with the exception of the
  2307 + * "IAC BREAK" command and the double IAC.
  2308 + */
  2309 +
  2310 + int i;
  2311 + int j = 0;
  2312 +
  2313 + for (i = 0; i < *size; i++) {
  2314 + if (s->do_telnetopt > 1) {
  2315 + if ((unsigned char)buf[i] == IAC && s->do_telnetopt == 2) {
  2316 + /* Double IAC means send an IAC */
  2317 + if (j != i)
  2318 + buf[j] = buf[i];
  2319 + j++;
  2320 + s->do_telnetopt = 1;
  2321 + } else {
  2322 + if ((unsigned char)buf[i] == IAC_BREAK && s->do_telnetopt == 2) {
  2323 + /* Handle IAC break commands by sending a serial break */
  2324 + chr->chr_event(s->fd_opaque, CHR_EVENT_BREAK);
  2325 + s->do_telnetopt++;
  2326 + }
  2327 + s->do_telnetopt++;
  2328 + }
  2329 + if (s->do_telnetopt >= 4) {
  2330 + s->do_telnetopt = 1;
  2331 + }
  2332 + } else {
  2333 + if ((unsigned char)buf[i] == IAC) {
  2334 + s->do_telnetopt = 2;
  2335 + } else {
  2336 + if (j != i)
  2337 + buf[j] = buf[i];
  2338 + j++;
  2339 + }
  2340 + }
  2341 + }
  2342 + *size = j;
  2343 +}
  2344 +
2340 2345 static void tcp_chr_read(void *opaque)
2341 2346 {
2342 2347 CharDriverState *chr = opaque;
... ... @@ -2360,7 +2365,10 @@ static void tcp_chr_read(void *opaque)
2360 2365 closesocket(s->fd);
2361 2366 s->fd = -1;
2362 2367 } else if (size > 0) {
2363   - s->fd_read(s->fd_opaque, buf, size);
  2368 + if (s->do_telnetopt)
  2369 + tcp_chr_process_IAC_bytes(chr, s, buf, &size);
  2370 + if (size > 0)
  2371 + s->fd_read(s->fd_opaque, buf, size);
2364 2372 }
2365 2373 }
2366 2374  
... ... @@ -2385,6 +2393,21 @@ static void tcp_chr_connect(void *opaque)
2385 2393 tcp_chr_read, NULL, chr);
2386 2394 }
2387 2395  
  2396 +#define IACSET(x,a,b,c) x[0] = a; x[1] = b; x[2] = c;
  2397 +static void tcp_chr_telnet_init(int fd)
  2398 +{
  2399 + char buf[3];
  2400 + /* Send the telnet negotion to put telnet in binary, no echo, single char mode */
  2401 + IACSET(buf, 0xff, 0xfb, 0x01); /* IAC WILL ECHO */
  2402 + send(fd, (char *)buf, 3, 0);
  2403 + IACSET(buf, 0xff, 0xfb, 0x03); /* IAC WILL Suppress go ahead */
  2404 + send(fd, (char *)buf, 3, 0);
  2405 + IACSET(buf, 0xff, 0xfb, 0x00); /* IAC WILL Binary */
  2406 + send(fd, (char *)buf, 3, 0);
  2407 + IACSET(buf, 0xff, 0xfd, 0x00); /* IAC DO Binary */
  2408 + send(fd, (char *)buf, 3, 0);
  2409 +}
  2410 +
2388 2411 static void tcp_chr_accept(void *opaque)
2389 2412 {
2390 2413 CharDriverState *chr = opaque;
... ... @@ -2399,6 +2422,8 @@ static void tcp_chr_accept(void *opaque)
2399 2422 if (fd < 0 && errno != EINTR) {
2400 2423 return;
2401 2424 } else if (fd >= 0) {
  2425 + if (s->do_telnetopt)
  2426 + tcp_chr_telnet_init(fd);
2402 2427 break;
2403 2428 }
2404 2429 }
... ... @@ -2419,16 +2444,34 @@ static void tcp_chr_close(CharDriverState *chr)
2419 2444 }
2420 2445  
2421 2446 static CharDriverState *qemu_chr_open_tcp(const char *host_str,
2422   - int is_listen)
  2447 + int is_telnet)
2423 2448 {
2424 2449 CharDriverState *chr = NULL;
2425 2450 TCPCharDriver *s = NULL;
2426 2451 int fd = -1, ret, err, val;
  2452 + int is_listen = 0;
  2453 + int is_waitconnect = 1;
  2454 + const char *ptr;
2427 2455 struct sockaddr_in saddr;
2428 2456  
2429 2457 if (parse_host_port(&saddr, host_str) < 0)
2430 2458 goto fail;
2431 2459  
  2460 + ptr = host_str;
  2461 + while((ptr = strchr(ptr,','))) {
  2462 + ptr++;
  2463 + if (!strncmp(ptr,"server",6)) {
  2464 + is_listen = 1;
  2465 + } else if (!strncmp(ptr,"nowait",6)) {
  2466 + is_waitconnect = 0;
  2467 + } else {
  2468 + printf("Unknown option: %s\n", ptr);
  2469 + goto fail;
  2470 + }
  2471 + }
  2472 + if (!is_listen)
  2473 + is_waitconnect = 0;
  2474 +
2432 2475 chr = qemu_mallocz(sizeof(CharDriverState));
2433 2476 if (!chr)
2434 2477 goto fail;
... ... @@ -2439,7 +2482,9 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str,
2439 2482 fd = socket(PF_INET, SOCK_STREAM, 0);
2440 2483 if (fd < 0)
2441 2484 goto fail;
2442   - socket_set_nonblock(fd);
  2485 +
  2486 + if (!is_waitconnect)
  2487 + socket_set_nonblock(fd);
2443 2488  
2444 2489 s->connected = 0;
2445 2490 s->fd = -1;
... ... @@ -2457,6 +2502,8 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str,
2457 2502 goto fail;
2458 2503 s->listen_fd = fd;
2459 2504 qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr);
  2505 + if (is_telnet)
  2506 + s->do_telnetopt = 1;
2460 2507 } else {
2461 2508 for(;;) {
2462 2509 ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr));
... ... @@ -2484,6 +2531,12 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str,
2484 2531 chr->chr_write = tcp_chr_write;
2485 2532 chr->chr_add_read_handler = tcp_chr_add_read_handler;
2486 2533 chr->chr_close = tcp_chr_close;
  2534 + if (is_listen && is_waitconnect) {
  2535 + printf("QEMU waiting for connection on: %s\n", host_str);
  2536 + tcp_chr_accept(chr);
  2537 + socket_set_nonblock(s->listen_fd);
  2538 + }
  2539 +
2487 2540 return chr;
2488 2541 fail:
2489 2542 if (fd >= 0)
... ... @@ -2505,7 +2558,7 @@ CharDriverState *qemu_chr_open(const char *filename)
2505 2558 if (strstart(filename, "tcp:", &p)) {
2506 2559 return qemu_chr_open_tcp(p, 0);
2507 2560 } else
2508   - if (strstart(filename, "tcpl:", &p)) {
  2561 + if (strstart(filename, "telnet:", &p)) {
2509 2562 return qemu_chr_open_tcp(p, 1);
2510 2563 } else
2511 2564 if (strstart(filename, "udp:", &p)) {
... ... @@ -2618,6 +2671,45 @@ static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
2618 2671 return 0;
2619 2672 }
2620 2673  
  2674 +int parse_host_src_port(struct sockaddr_in *haddr,
  2675 + struct sockaddr_in *saddr,
  2676 + const char *input_str)
  2677 +{
  2678 + char *str = strdup(input_str);
  2679 + char *host_str = str;
  2680 + char *src_str;
  2681 + char *ptr;
  2682 +
  2683 + /*
  2684 + * Chop off any extra arguments at the end of the string which
  2685 + * would start with a comma, then fill in the src port information
  2686 + * if it was provided else use the "any address" and "any port".
  2687 + */
  2688 + if ((ptr = strchr(str,',')))
  2689 + *ptr = '\0';
  2690 +
  2691 + if ((src_str = strchr(input_str,'@'))) {
  2692 + *src_str = '\0';
  2693 + src_str++;
  2694 + }
  2695 +
  2696 + if (parse_host_port(haddr, host_str) < 0)
  2697 + goto fail;
  2698 +
  2699 + if (!src_str || *src_str == '\0')
  2700 + src_str = ":0";
  2701 +
  2702 + if (parse_host_port(saddr, src_str) < 0)
  2703 + goto fail;
  2704 +
  2705 + free(str);
  2706 + return(0);
  2707 +
  2708 +fail:
  2709 + free(str);
  2710 + return -1;
  2711 +}
  2712 +
2621 2713 int parse_host_port(struct sockaddr_in *saddr, const char *str)
2622 2714 {
2623 2715 char buf[512];
... ...