Commit 951f13516a50383cd462c9150e643cc9f9566267
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,43 +525,60 @@ Write output to filename. No character can be read. | ||
525 | name pipe @var{filename} | 525 | name pipe @var{filename} |
526 | @item COMn | 526 | @item COMn |
527 | [Windows only] Use host serial port @var{n} | 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 | If you plan to send characters back via netconsole or you want to stop | 536 | If you plan to send characters back via netconsole or you want to stop |
542 | and start qemu a lot of times, you should have qemu use the same | 537 | and start qemu a lot of times, you should have qemu use the same |
543 | source port each time by using something like @code{-serial | 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 | version of netcat which can listen to a TCP port and send and receive | 540 | version of netcat which can listen to a TCP port and send and receive |
546 | characters via udp. If you have a patched version of netcat which | 541 | characters via udp. If you have a patched version of netcat which |
547 | activates telnet remote echo and single char transfer, then you can | 542 | activates telnet remote echo and single char transfer, then you can |
548 | use the following options to step up a netcat redirector to allow | 543 | use the following options to step up a netcat redirector to allow |
549 | telnet on port 5555 to access the qemu port. | 544 | telnet on port 5555 to access the qemu port. |
550 | @table @code | 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 | @end table | 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 | @end table | 582 | @end table |
566 | 583 | ||
567 | @item -parallel dev | 584 | @item -parallel dev |
vl.c
@@ -2203,16 +2203,16 @@ static void udp_chr_add_read_handler(CharDriverState *chr, | @@ -2203,16 +2203,16 @@ static void udp_chr_add_read_handler(CharDriverState *chr, | ||
2203 | } | 2203 | } |
2204 | 2204 | ||
2205 | int parse_host_port(struct sockaddr_in *saddr, const char *str); | 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 | CharDriverState *qemu_chr_open_udp(const char *def) | 2210 | CharDriverState *qemu_chr_open_udp(const char *def) |
2208 | { | 2211 | { |
2209 | CharDriverState *chr = NULL; | 2212 | CharDriverState *chr = NULL; |
2210 | NetCharDriver *s = NULL; | 2213 | NetCharDriver *s = NULL; |
2211 | int fd = -1; | 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 | chr = qemu_mallocz(sizeof(CharDriverState)); | 2217 | chr = qemu_mallocz(sizeof(CharDriverState)); |
2218 | if (!chr) | 2218 | if (!chr) |
@@ -2227,58 +2227,12 @@ CharDriverState *qemu_chr_open_udp(const char *def) | @@ -2227,58 +2227,12 @@ CharDriverState *qemu_chr_open_udp(const char *def) | ||
2227 | goto return_err; | 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 | perror("bind"); | 2237 | perror("bind"); |
2284 | goto return_err; | 2238 | goto return_err; |
@@ -2312,6 +2266,7 @@ typedef struct { | @@ -2312,6 +2266,7 @@ typedef struct { | ||
2312 | int fd, listen_fd; | 2266 | int fd, listen_fd; |
2313 | int connected; | 2267 | int connected; |
2314 | int max_size; | 2268 | int max_size; |
2269 | + int do_telnetopt; | ||
2315 | } TCPCharDriver; | 2270 | } TCPCharDriver; |
2316 | 2271 | ||
2317 | static void tcp_chr_accept(void *opaque); | 2272 | static void tcp_chr_accept(void *opaque); |
@@ -2337,6 +2292,56 @@ static int tcp_chr_read_poll(void *opaque) | @@ -2337,6 +2292,56 @@ static int tcp_chr_read_poll(void *opaque) | ||
2337 | return s->max_size; | 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 | static void tcp_chr_read(void *opaque) | 2345 | static void tcp_chr_read(void *opaque) |
2341 | { | 2346 | { |
2342 | CharDriverState *chr = opaque; | 2347 | CharDriverState *chr = opaque; |
@@ -2360,7 +2365,10 @@ static void tcp_chr_read(void *opaque) | @@ -2360,7 +2365,10 @@ static void tcp_chr_read(void *opaque) | ||
2360 | closesocket(s->fd); | 2365 | closesocket(s->fd); |
2361 | s->fd = -1; | 2366 | s->fd = -1; |
2362 | } else if (size > 0) { | 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,6 +2393,21 @@ static void tcp_chr_connect(void *opaque) | ||
2385 | tcp_chr_read, NULL, chr); | 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 | static void tcp_chr_accept(void *opaque) | 2411 | static void tcp_chr_accept(void *opaque) |
2389 | { | 2412 | { |
2390 | CharDriverState *chr = opaque; | 2413 | CharDriverState *chr = opaque; |
@@ -2399,6 +2422,8 @@ static void tcp_chr_accept(void *opaque) | @@ -2399,6 +2422,8 @@ static void tcp_chr_accept(void *opaque) | ||
2399 | if (fd < 0 && errno != EINTR) { | 2422 | if (fd < 0 && errno != EINTR) { |
2400 | return; | 2423 | return; |
2401 | } else if (fd >= 0) { | 2424 | } else if (fd >= 0) { |
2425 | + if (s->do_telnetopt) | ||
2426 | + tcp_chr_telnet_init(fd); | ||
2402 | break; | 2427 | break; |
2403 | } | 2428 | } |
2404 | } | 2429 | } |
@@ -2419,16 +2444,34 @@ static void tcp_chr_close(CharDriverState *chr) | @@ -2419,16 +2444,34 @@ static void tcp_chr_close(CharDriverState *chr) | ||
2419 | } | 2444 | } |
2420 | 2445 | ||
2421 | static CharDriverState *qemu_chr_open_tcp(const char *host_str, | 2446 | static CharDriverState *qemu_chr_open_tcp(const char *host_str, |
2422 | - int is_listen) | 2447 | + int is_telnet) |
2423 | { | 2448 | { |
2424 | CharDriverState *chr = NULL; | 2449 | CharDriverState *chr = NULL; |
2425 | TCPCharDriver *s = NULL; | 2450 | TCPCharDriver *s = NULL; |
2426 | int fd = -1, ret, err, val; | 2451 | int fd = -1, ret, err, val; |
2452 | + int is_listen = 0; | ||
2453 | + int is_waitconnect = 1; | ||
2454 | + const char *ptr; | ||
2427 | struct sockaddr_in saddr; | 2455 | struct sockaddr_in saddr; |
2428 | 2456 | ||
2429 | if (parse_host_port(&saddr, host_str) < 0) | 2457 | if (parse_host_port(&saddr, host_str) < 0) |
2430 | goto fail; | 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 | chr = qemu_mallocz(sizeof(CharDriverState)); | 2475 | chr = qemu_mallocz(sizeof(CharDriverState)); |
2433 | if (!chr) | 2476 | if (!chr) |
2434 | goto fail; | 2477 | goto fail; |
@@ -2439,7 +2482,9 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str, | @@ -2439,7 +2482,9 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str, | ||
2439 | fd = socket(PF_INET, SOCK_STREAM, 0); | 2482 | fd = socket(PF_INET, SOCK_STREAM, 0); |
2440 | if (fd < 0) | 2483 | if (fd < 0) |
2441 | goto fail; | 2484 | goto fail; |
2442 | - socket_set_nonblock(fd); | 2485 | + |
2486 | + if (!is_waitconnect) | ||
2487 | + socket_set_nonblock(fd); | ||
2443 | 2488 | ||
2444 | s->connected = 0; | 2489 | s->connected = 0; |
2445 | s->fd = -1; | 2490 | s->fd = -1; |
@@ -2457,6 +2502,8 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str, | @@ -2457,6 +2502,8 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str, | ||
2457 | goto fail; | 2502 | goto fail; |
2458 | s->listen_fd = fd; | 2503 | s->listen_fd = fd; |
2459 | qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr); | 2504 | qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr); |
2505 | + if (is_telnet) | ||
2506 | + s->do_telnetopt = 1; | ||
2460 | } else { | 2507 | } else { |
2461 | for(;;) { | 2508 | for(;;) { |
2462 | ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr)); | 2509 | ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr)); |
@@ -2484,6 +2531,12 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str, | @@ -2484,6 +2531,12 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str, | ||
2484 | chr->chr_write = tcp_chr_write; | 2531 | chr->chr_write = tcp_chr_write; |
2485 | chr->chr_add_read_handler = tcp_chr_add_read_handler; | 2532 | chr->chr_add_read_handler = tcp_chr_add_read_handler; |
2486 | chr->chr_close = tcp_chr_close; | 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 | return chr; | 2540 | return chr; |
2488 | fail: | 2541 | fail: |
2489 | if (fd >= 0) | 2542 | if (fd >= 0) |
@@ -2505,7 +2558,7 @@ CharDriverState *qemu_chr_open(const char *filename) | @@ -2505,7 +2558,7 @@ CharDriverState *qemu_chr_open(const char *filename) | ||
2505 | if (strstart(filename, "tcp:", &p)) { | 2558 | if (strstart(filename, "tcp:", &p)) { |
2506 | return qemu_chr_open_tcp(p, 0); | 2559 | return qemu_chr_open_tcp(p, 0); |
2507 | } else | 2560 | } else |
2508 | - if (strstart(filename, "tcpl:", &p)) { | 2561 | + if (strstart(filename, "telnet:", &p)) { |
2509 | return qemu_chr_open_tcp(p, 1); | 2562 | return qemu_chr_open_tcp(p, 1); |
2510 | } else | 2563 | } else |
2511 | if (strstart(filename, "udp:", &p)) { | 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,6 +2671,45 @@ static int get_str_sep(char *buf, int buf_size, const char **pp, int sep) | ||
2618 | return 0; | 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 | int parse_host_port(struct sockaddr_in *saddr, const char *str) | 2713 | int parse_host_port(struct sockaddr_in *saddr, const char *str) |
2622 | { | 2714 | { |
2623 | char buf[512]; | 2715 | char buf[512]; |